summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/usb/host
Initial import
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig790
-rw-r--r--drivers/usb/host/Makefile77
-rw-r--r--drivers/usb/host/bcma-hcd.c333
-rw-r--r--drivers/usb/host/ehci-atmel.c256
-rw-r--r--drivers/usb/host/ehci-dbg.c1084
-rw-r--r--drivers/usb/host/ehci-exynos.c349
-rw-r--r--drivers/usb/host/ehci-fsl.c714
-rw-r--r--drivers/usb/host/ehci-fsl.h65
-rw-r--r--drivers/usb/host/ehci-grlib.c192
-rw-r--r--drivers/usb/host/ehci-hcd.c1400
-rw-r--r--drivers/usb/host/ehci-hub.c1321
-rw-r--r--drivers/usb/host/ehci-mem.c246
-rw-r--r--drivers/usb/host/ehci-msm.c233
-rw-r--r--drivers/usb/host/ehci-mv.c321
-rw-r--r--drivers/usb/host/ehci-mxc.c225
-rw-r--r--drivers/usb/host/ehci-omap.c327
-rw-r--r--drivers/usb/host/ehci-orion.c347
-rw-r--r--drivers/usb/host/ehci-pci.c435
-rw-r--r--drivers/usb/host/ehci-platform.c442
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c329
-rw-r--r--drivers/usb/host/ehci-ppc-of.c239
-rw-r--r--drivers/usb/host/ehci-ps3.c266
-rw-r--r--drivers/usb/host/ehci-q.c1476
-rw-r--r--drivers/usb/host/ehci-sched.c2517
-rw-r--r--drivers/usb/host/ehci-sead3.c185
-rw-r--r--drivers/usb/host/ehci-sh.c195
-rw-r--r--drivers/usb/host/ehci-spear.c190
-rw-r--r--drivers/usb/host/ehci-st.c375
-rw-r--r--drivers/usb/host/ehci-sysfs.c187
-rw-r--r--drivers/usb/host/ehci-tegra.c621
-rw-r--r--drivers/usb/host/ehci-tilegx.c216
-rw-r--r--drivers/usb/host/ehci-timer.c433
-rw-r--r--drivers/usb/host/ehci-w90x900.c148
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c241
-rw-r--r--drivers/usb/host/ehci.h880
-rw-r--r--drivers/usb/host/fhci-dbg.c135
-rw-r--r--drivers/usb/host/fhci-hcd.c836
-rw-r--r--drivers/usb/host/fhci-hub.c340
-rw-r--r--drivers/usb/host/fhci-mem.c114
-rw-r--r--drivers/usb/host/fhci-q.c285
-rw-r--r--drivers/usb/host/fhci-sched.c898
-rw-r--r--drivers/usb/host/fhci-tds.c626
-rw-r--r--drivers/usb/host/fhci.h594
-rw-r--r--drivers/usb/host/fotg210-hcd.c5951
-rw-r--r--drivers/usb/host/fotg210.h686
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c339
-rw-r--r--drivers/usb/host/fusbh200-hcd.c5893
-rw-r--r--drivers/usb/host/fusbh200.h675
-rw-r--r--drivers/usb/host/hwa-hc.c889
-rw-r--r--drivers/usb/host/imx21-dbg.c531
-rw-r--r--drivers/usb/host/imx21-hcd.c1946
-rw-r--r--drivers/usb/host/imx21-hcd.h444
-rw-r--r--drivers/usb/host/isp116x-hcd.c1716
-rw-r--r--drivers/usb/host/isp116x.h595
-rw-r--r--drivers/usb/host/isp1362-hcd.c2788
-rw-r--r--drivers/usb/host/isp1362.h1014
-rw-r--r--drivers/usb/host/max3421-hcd.c1955
-rw-r--r--drivers/usb/host/ohci-at91.c712
-rw-r--r--drivers/usb/host/ohci-da8xx.c438
-rw-r--r--drivers/usb/host/ohci-dbg.c809
-rw-r--r--drivers/usb/host/ohci-exynos.c311
-rw-r--r--drivers/usb/host/ohci-hcd.c1384
-rw-r--r--drivers/usb/host/ohci-hub.c796
-rw-r--r--drivers/usb/host/ohci-jz4740.c245
-rw-r--r--drivers/usb/host/ohci-mem.c139
-rw-r--r--drivers/usb/host/ohci-nxp.c350
-rw-r--r--drivers/usb/host/ohci-omap.c513
-rw-r--r--drivers/usb/host/ohci-omap3.c211
-rw-r--r--drivers/usb/host/ohci-pci.c315
-rw-r--r--drivers/usb/host/ohci-platform.c416
-rw-r--r--drivers/usb/host/ohci-ppc-of.c235
-rw-r--r--drivers/usb/host/ohci-ps3.c251
-rw-r--r--drivers/usb/host/ohci-pxa27x.c639
-rw-r--r--drivers/usb/host/ohci-q.c1229
-rw-r--r--drivers/usb/host/ohci-s3c2410.c502
-rw-r--r--drivers/usb/host/ohci-sa1111.c265
-rw-r--r--drivers/usb/host/ohci-sm501.c271
-rw-r--r--drivers/usb/host/ohci-spear.c203
-rw-r--r--drivers/usb/host/ohci-st.c349
-rw-r--r--drivers/usb/host/ohci-tilegx.c205
-rw-r--r--drivers/usb/host/ohci-tmio.c372
-rw-r--r--drivers/usb/host/ohci.h744
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c3928
-rw-r--r--drivers/usb/host/oxu210hp.h447
-rw-r--r--drivers/usb/host/pci-quirks.c1097
-rw-r--r--drivers/usb/host/pci-quirks.h26
-rw-r--r--drivers/usb/host/r8a66597-hcd.c2541
-rw-r--r--drivers/usb/host/r8a66597.h345
-rw-r--r--drivers/usb/host/sl811-hcd.c1816
-rw-r--r--drivers/usb/host/sl811.h249
-rw-r--r--drivers/usb/host/sl811_cs.c202
-rw-r--r--drivers/usb/host/ssb-hcd.c278
-rw-r--r--drivers/usb/host/u132-hcd.c3260
-rw-r--r--drivers/usb/host/uhci-debug.c655
-rw-r--r--drivers/usb/host/uhci-grlib.c193
-rw-r--r--drivers/usb/host/uhci-hcd.c945
-rw-r--r--drivers/usb/host/uhci-hcd.h660
-rw-r--r--drivers/usb/host/uhci-hub.c420
-rw-r--r--drivers/usb/host/uhci-pci.c306
-rw-r--r--drivers/usb/host/uhci-platform.c152
-rw-r--r--drivers/usb/host/uhci-q.c1794
-rw-r--r--drivers/usb/host/whci/Kbuild12
-rw-r--r--drivers/usb/host/whci/asl.c389
-rw-r--r--drivers/usb/host/whci/debug.c200
-rw-r--r--drivers/usb/host/whci/hcd.c366
-rw-r--r--drivers/usb/host/whci/hw.c104
-rw-r--r--drivers/usb/host/whci/init.c189
-rw-r--r--drivers/usb/host/whci/int.c93
-rw-r--r--drivers/usb/host/whci/pzl.c417
-rw-r--r--drivers/usb/host/whci/qset.c835
-rw-r--r--drivers/usb/host/whci/whcd.h215
-rw-r--r--drivers/usb/host/whci/whci-hc.h414
-rw-r--r--drivers/usb/host/whci/wusb.c221
-rw-r--r--drivers/usb/host/xhci-dbg.c597
-rw-r--r--drivers/usb/host/xhci-ext-caps.h156
-rw-r--r--drivers/usb/host/xhci-hub.c1265
-rw-r--r--drivers/usb/host/xhci-mem.c2548
-rw-r--r--drivers/usb/host/xhci-mvebu.c72
-rw-r--r--drivers/usb/host/xhci-mvebu.h21
-rw-r--r--drivers/usb/host/xhci-pci.c398
-rw-r--r--drivers/usb/host/xhci-plat.c287
-rw-r--r--drivers/usb/host/xhci-rcar.c148
-rw-r--r--drivers/usb/host/xhci-rcar.h27
-rw-r--r--drivers/usb/host/xhci-ring.c3962
-rw-r--r--drivers/usb/host/xhci-trace.c17
-rw-r--r--drivers/usb/host/xhci-trace.h158
-rw-r--r--drivers/usb/host/xhci.c5033
-rw-r--r--drivers/usb/host/xhci.h1878
128 files changed, 98145 insertions, 0 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
new file mode 100644
index 000000000..197a6a3e6
--- /dev/null
+++ b/drivers/usb/host/Kconfig
@@ -0,0 +1,790 @@
+#
+# USB Host Controller Drivers
+#
+comment "USB Host Controller Drivers"
+
+config USB_C67X00_HCD
+ tristate "Cypress C67x00 HCD support"
+ help
+ The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
+ host/peripheral/OTG USB controllers.
+
+ Enable this option to support this chip in host controller mode.
+ If unsure, say N.
+
+ 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"
+ ---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.
+
+if USB_XHCI_HCD
+
+config USB_XHCI_PCI
+ tristate
+ depends on PCI
+ default y
+
+config USB_XHCI_PLATFORM
+ tristate
+
+config USB_XHCI_MVEBU
+ tristate "xHCI support for Marvell Armada 375/38x"
+ select USB_XHCI_PLATFORM
+ depends on ARCH_MVEBU || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Marvell Armada 375/38x ARM SOCs.
+
+config USB_XHCI_RCAR
+ tristate "xHCI support for Renesas R-Car SoCs"
+ select USB_XHCI_PLATFORM
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Renesas R-Car ARM SoCs.
+
+endif # USB_XHCI_HCD
+
+config USB_EHCI_HCD
+ tristate "EHCI HCD (USB 2.0) support"
+ ---help---
+ The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
+ "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
+ If your USB host controller supports USB 2.0, you will likely want to
+ configure this Host Controller Driver.
+
+ EHCI controllers are packaged with "companion" host controllers (OHCI
+ or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports
+ will connect to EHCI if the device is high speed, otherwise they
+ connect to a companion controller. If you configure EHCI, you should
+ probably configure the OHCI (for NEC and some other vendors) USB Host
+ Controller Driver or UHCI (for Via motherboards) Host Controller
+ Driver too.
+
+ You may want to read <file:Documentation/usb/ehci.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ehci-hcd.
+
+config USB_EHCI_ROOT_HUB_TT
+ bool "Root Hub Transaction Translators"
+ depends on USB_EHCI_HCD
+ ---help---
+ Some EHCI chips have vendor-specific extensions to integrate
+ transaction translators, so that no OHCI or UHCI companion
+ controller is needed. It's safe to say "y" even if your
+ controller doesn't support this feature.
+
+ This supports the EHCI implementation that's originally
+ from ARC, and has since changed hands a few times.
+
+config USB_EHCI_TT_NEWSCHED
+ bool "Improved Transaction Translator scheduling"
+ depends on USB_EHCI_HCD
+ default y
+ ---help---
+ This changes the periodic scheduling code to fill more of the low
+ and full speed bandwidth available from the Transaction Translator
+ (TT) in USB 2.0 hubs. Without this, only one transfer will be
+ issued in each microframe, significantly reducing the number of
+ periodic low/fullspeed transfers possible.
+
+ If you have multiple periodic low/fullspeed devices connected to a
+ highspeed USB hub which is connected to a highspeed USB Host
+ Controller, and some of those devices will not work correctly
+ (possibly due to "ENOSPC" or "-28" errors), say Y. Conversely, if
+ you have only one such device and it doesn't work, you could try
+ saying N.
+
+ If unsure, say Y.
+
+config USB_FSL_MPH_DR_OF
+ tristate
+
+if USB_EHCI_HCD
+
+config USB_EHCI_PCI
+ tristate
+ depends on PCI
+ default y
+
+config USB_EHCI_HCD_PMC_MSP
+ tristate "EHCI support for on-chip PMC MSP71xx USB controller"
+ depends on MSP_HAS_USB
+ default n
+ select USB_EHCI_BIG_ENDIAN_DESC
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ ---help---
+ Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's.
+ If unsure, say N.
+
+config XPS_USB_HCD_XILINX
+ bool "Use Xilinx usb host EHCI controller core"
+ depends on (PPC32 || MICROBLAZE)
+ select USB_EHCI_BIG_ENDIAN_DESC
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ ---help---
+ Xilinx xps USB host controller core is EHCI compliant and has
+ transaction translator built-in. It can be configured to either
+ support both high speed and full speed devices, or high speed
+ devices only.
+
+config USB_EHCI_FSL
+ bool "Support for Freescale PPC on-chip EHCI USB controller"
+ depends on FSL_SOC
+ select USB_EHCI_ROOT_HUB_TT
+ select USB_FSL_MPH_DR_OF if OF
+ ---help---
+ Variation of ARC USB block used in some Freescale chips.
+
+config USB_EHCI_MXC
+ tristate "Support for Freescale i.MX on-chip EHCI USB controller"
+ depends on ARCH_MXC
+ select USB_EHCI_ROOT_HUB_TT
+ ---help---
+ Variation of ARC USB block used in some Freescale chips.
+
+config USB_EHCI_HCD_OMAP
+ tristate "EHCI support for OMAP3 and later chips"
+ depends on ARCH_OMAP
+ select NOP_USB_XCEIV
+ default y
+ ---help---
+ Enables support for the on-chip EHCI controller on
+ OMAP3 and later chips.
+
+config USB_EHCI_HCD_ORION
+ tristate "Support for Marvell EBU on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && PLAT_ORION
+ default y
+ ---help---
+ Enables support for the on-chip EHCI controller on Marvell's
+ embedded ARM SoCs, including Orion, Kirkwood, Dove, Armada XP,
+ Armada 370. This is different from the EHCI implementation
+ on Marvell's mobile PXA and MMP SoC, see "EHCI support for
+ Marvell PXA/MMP USB controller" for those.
+
+config USB_EHCI_HCD_SPEAR
+ tristate "Support for ST SPEAr on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && PLAT_SPEAR
+ default y
+ ---help---
+ Enables support for the on-chip EHCI controller on
+ ST SPEAr chips.
+
+config USB_EHCI_HCD_STI
+ tristate "Support for ST STiHxxx on-chip EHCI USB controller"
+ depends on ARCH_STI && OF
+ select GENERIC_PHY
+ select USB_EHCI_HCD_PLATFORM
+ help
+ Enable support for the on-chip EHCI controller found on
+ STMicroelectronics consumer electronics SoC's.
+
+config USB_EHCI_HCD_AT91
+ tristate "Support for Atmel on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && ARCH_AT91
+ default y
+ ---help---
+ Enables support for the on-chip EHCI controller on
+ Atmel chips.
+
+config USB_EHCI_MSM
+ tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller"
+ depends on ARCH_QCOM
+ select USB_EHCI_ROOT_HUB_TT
+ ---help---
+ Enables support for the USB Host controller present on the
+ Qualcomm chipsets. Root Hub has inbuilt TT.
+ This driver depends on OTG driver for PHY initialization,
+ clock management, powering up VBUS, and power management.
+ This driver is not supported on boards like trout which
+ has an external PHY.
+
+config USB_EHCI_TEGRA
+ tristate "NVIDIA Tegra HCD support"
+ depends on ARCH_TEGRA
+ select USB_EHCI_ROOT_HUB_TT
+ select USB_PHY
+ help
+ This driver enables support for the internal USB Host Controllers
+ found in NVIDIA Tegra SoCs. The controllers are EHCI compliant.
+
+config USB_EHCI_HCD_PPC_OF
+ bool "EHCI support for PPC USB controller on OF platform bus"
+ depends on PPC
+ default y
+ ---help---
+ Enables support for the USB controller present on the PowerPC
+ OpenFirmware platform bus.
+
+config USB_EHCI_SH
+ bool "EHCI support for SuperH USB controller"
+ depends on SUPERH
+ ---help---
+ Enables support for the on-chip EHCI controller on the SuperH.
+ If you use the PCI EHCI controller, this option is not necessary.
+
+config USB_EHCI_EXYNOS
+ tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
+ depends on ARCH_S5PV210 || ARCH_EXYNOS
+ help
+ Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
+
+config USB_EHCI_MV
+ bool "EHCI support for Marvell PXA/MMP USB controller"
+ depends on (ARCH_PXA || ARCH_MMP)
+ select USB_EHCI_ROOT_HUB_TT
+ ---help---
+ Enables support for Marvell (including PXA and MMP series) on-chip
+ USB SPH and OTG controller. SPH is a single port host, and it can
+ only be EHCI host. OTG is controller that can switch to host mode.
+ Note that this driver will not work on Marvell's other EHCI
+ controller used by the EBU-type SoCs including Orion, Kirkwood,
+ Dova, Armada 370 and Armada XP. See "Support for Marvell EBU
+ on-chip EHCI USB controller" for those.
+
+config USB_W90X900_EHCI
+ tristate "W90X900(W90P910) EHCI support"
+ depends on ARCH_W90X900
+ ---help---
+ Enables support for the W90X900 USB controller
+
+config USB_CNS3XXX_EHCI
+ bool "Cavium CNS3XXX EHCI Module (DEPRECATED)"
+ depends on ARCH_CNS3XXX
+ select USB_EHCI_HCD_PLATFORM
+ ---help---
+ This option is deprecated now and the driver was removed, use
+ USB_EHCI_HCD_PLATFORM instead.
+
+ Enable support for the CNS3XXX SOC's on-chip EHCI controller.
+ It is needed for high-speed (480Mbit/sec) USB 2.0 device
+ support.
+
+config USB_EHCI_ATH79
+ bool "EHCI support for AR7XXX/AR9XXX SoCs (DEPRECATED)"
+ depends on (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X)
+ select USB_EHCI_ROOT_HUB_TT
+ select USB_EHCI_HCD_PLATFORM
+ default y
+ ---help---
+ This option is deprecated now and the driver was removed, use
+ USB_EHCI_HCD_PLATFORM instead.
+
+ Enables support for the built-in EHCI controller present
+ on the Atheros AR7XXX/AR9XXX SoCs.
+
+config USB_EHCI_HCD_PLATFORM
+ tristate "Generic EHCI driver for a platform device"
+ default n
+ ---help---
+ Adds an EHCI host driver for a generic platform device, which
+ provides a memory space and an irq.
+
+ If unsure, say N.
+
+config USB_OCTEON_EHCI
+ bool "Octeon on-chip EHCI support (DEPRECATED)"
+ depends on CAVIUM_OCTEON_SOC
+ default n
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ select USB_EHCI_HCD_PLATFORM
+ help
+ This option is deprecated now and the driver was removed, use
+ USB_EHCI_HCD_PLATFORM instead.
+
+ Enable support for the Octeon II SOC's on-chip EHCI
+ controller. It is needed for high-speed (480Mbit/sec)
+ USB 2.0 device support. All CN6XXX based chips with USB are
+ supported.
+
+endif # USB_EHCI_HCD
+
+config USB_OXU210HP_HCD
+ tristate "OXU210HP HCD support"
+ ---help---
+ The OXU210HP is an USB host/OTG/device controller. Enable this
+ option if your board has this chip. If unsure, say N.
+
+ This driver does not support isochronous transfers and doesn't
+ implement OTG nor USB device controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called oxu210hp-hcd.
+
+config USB_ISP116X_HCD
+ tristate "ISP116X HCD support"
+ ---help---
+ The ISP1160 and ISP1161 chips are USB host controllers. Enable this
+ option if your board has this chip. If unsure, say N.
+
+ This driver does not support isochronous transfers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isp116x-hcd.
+
+config USB_ISP1362_HCD
+ tristate "ISP1362 HCD support"
+ ---help---
+ Supports the Philips ISP1362 chip as a host controller
+
+ This driver does not support isochronous transfers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isp1362-hcd.
+
+config USB_FUSBH200_HCD
+ tristate "FUSBH200 HCD support"
+ depends on USB
+ ---help---
+ Faraday FUSBH200 is designed to meet USB2.0 EHCI specification
+ with minor modification.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fusbh200-hcd.
+
+config USB_FOTG210_HCD
+ tristate "FOTG210 HCD support"
+ depends on USB
+ ---help---
+ Faraday FOTG210 is an OTG controller which can be configured as
+ an USB2.0 host. It is designed to meet USB2.0 EHCI specification
+ with minor modification.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fotg210-hcd.
+
+config USB_MAX3421_HCD
+ tristate "MAX3421 HCD (USB-over-SPI) support"
+ depends on USB && SPI
+ ---help---
+ The Maxim MAX3421E chip supports standard USB 2.0-compliant
+ full-speed devices either in host or peripheral mode. This
+ driver supports the host-mode of the MAX3421E only.
+
+ To compile this driver as a module, choose M here: the module will
+ be called max3421-hcd.
+
+config USB_OHCI_HCD
+ tristate "OHCI HCD (USB 1.1) support"
+ ---help---
+ The Open Host Controller Interface (OHCI) is a standard for accessing
+ USB 1.1 host controller hardware. It does more in hardware than Intel's
+ UHCI specification. If your USB host controller follows the OHCI spec,
+ say Y. On most non-x86 systems, and on x86 hardware that's not using a
+ USB controller from Intel or VIA, this is appropriate. If your host
+ controller doesn't use PCI, this is probably appropriate. For a PCI
+ based system where you're not sure, the "lspci -v" entry will list the
+ right "prog-if" for your USB controller(s): EHCI, OHCI, or UHCI.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ohci-hcd.
+
+if USB_OHCI_HCD
+
+config USB_OHCI_HCD_OMAP1
+ tristate "OHCI support for OMAP1/2 chips"
+ depends on ARCH_OMAP1
+ depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
+ default y
+ ---help---
+ Enables support for the OHCI controller on OMAP1/2 chips.
+
+config USB_OHCI_HCD_SPEAR
+ tristate "Support for ST SPEAr on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && PLAT_SPEAR
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ ST SPEAr chips.
+
+config USB_OHCI_HCD_STI
+ tristate "Support for ST STiHxxx on-chip OHCI USB controller"
+ depends on ARCH_STI && OF
+ select GENERIC_PHY
+ select USB_OHCI_HCD_PLATFORM
+ help
+ Enable support for the on-chip OHCI controller found on
+ STMicroelectronics consumer electronics SoC's.
+
+config USB_OHCI_HCD_S3C2410
+ tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
+ depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX)
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ S3C24xx/S3C64xx chips.
+
+config USB_OHCI_HCD_LPC32XX
+ tristate "Support for LPC on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && ARCH_LPC32XX
+ depends on USB_ISP1301
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ NXP chips.
+
+config USB_OHCI_HCD_PXA27X
+ tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && (PXA27x || PXA3xx)
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ PXA27x/PXA3xx chips.
+
+config USB_OHCI_HCD_AT91
+ tristate "Support for Atmel on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && ARCH_AT91
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ Atmel chips.
+
+config USB_OHCI_HCD_OMAP3
+ tristate "OHCI support for OMAP3 and later chips"
+ depends on (ARCH_OMAP3 || ARCH_OMAP4)
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ OMAP3 and later chips.
+
+config USB_OHCI_HCD_DAVINCI
+ bool "OHCI support for TI DaVinci DA8xx"
+ depends on ARCH_DAVINCI_DA8XX
+ depends on USB_OHCI_HCD=y
+ default y
+ help
+ Enables support for the DaVinci DA8xx integrated OHCI
+ controller. This driver cannot currently be a loadable
+ module because it lacks a proper PHY abstraction.
+
+config USB_OHCI_ATH79
+ bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs (DEPRECATED)"
+ depends on (SOC_AR71XX || SOC_AR724X)
+ select USB_OHCI_HCD_PLATFORM
+ default y
+ help
+ This option is deprecated now and the driver was removed, use
+ USB_OHCI_HCD_PLATFORM instead.
+
+ Enables support for the built-in OHCI controller present on the
+ Atheros AR71XX/AR7240 SoCs.
+
+config USB_OHCI_HCD_PPC_OF_BE
+ bool "OHCI support for OF platform bus (big endian)"
+ depends on PPC
+ select USB_OHCI_BIG_ENDIAN_DESC
+ select USB_OHCI_BIG_ENDIAN_MMIO
+ ---help---
+ Enables support for big-endian USB controllers present on the
+ OpenFirmware platform bus.
+
+config USB_OHCI_HCD_PPC_OF_LE
+ bool "OHCI support for OF platform bus (little endian)"
+ depends on PPC
+ select USB_OHCI_LITTLE_ENDIAN
+ ---help---
+ Enables support for little-endian USB controllers present on the
+ OpenFirmware platform bus.
+
+config USB_OHCI_HCD_PPC_OF
+ bool
+ depends on PPC
+ default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE
+
+config USB_OHCI_HCD_PCI
+ tristate "OHCI support for PCI-bus USB controllers"
+ depends on PCI
+ default y
+ select USB_OHCI_LITTLE_ENDIAN
+ ---help---
+ Enables support for PCI-bus plug-in USB controller cards.
+ If unsure, say Y.
+
+config USB_OHCI_HCD_SSB
+ bool "OHCI support for Broadcom SSB OHCI core (DEPRECATED)"
+ depends on (SSB = y || SSB = USB_OHCI_HCD)
+ select USB_HCD_SSB
+ select USB_OHCI_HCD_PLATFORM
+ default n
+ ---help---
+ This option is deprecated now and the driver was removed, use
+ USB_HCD_SSB and USB_OHCI_HCD_PLATFORM instead.
+
+ Support for the Sonics Silicon Backplane (SSB) attached
+ Broadcom USB OHCI core.
+
+ This device is present in some embedded devices with
+ Broadcom based SSB bus.
+
+ If unsure, say N.
+
+config USB_OHCI_SH
+ bool "OHCI support for SuperH USB controller (DEPRECATED)"
+ depends on SUPERH
+ select USB_OHCI_HCD_PLATFORM
+ ---help---
+ This option is deprecated now and the driver was removed, use
+ USB_OHCI_HCD_PLATFORM instead.
+
+ Enables support for the on-chip OHCI controller on the SuperH.
+ If you use the PCI OHCI controller, this option is not necessary.
+
+config USB_OHCI_EXYNOS
+ tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
+ depends on ARCH_S5PV210 || ARCH_EXYNOS
+ help
+ Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
+
+config USB_CNS3XXX_OHCI
+ bool "Cavium CNS3XXX OHCI Module (DEPRECATED)"
+ depends on ARCH_CNS3XXX
+ select USB_OHCI_HCD_PLATFORM
+ ---help---
+ This option is deprecated now and the driver was removed, use
+ USB_OHCI_HCD_PLATFORM instead.
+
+ Enable support for the CNS3XXX SOC's on-chip OHCI controller.
+ It is needed for low-speed USB 1.0 device support.
+
+config USB_OHCI_HCD_PLATFORM
+ tristate "Generic OHCI driver for a platform device"
+ default n
+ ---help---
+ Adds an OHCI host driver for a generic platform device, which
+ provides a memory space and an irq.
+
+ If unsure, say N.
+
+config USB_OCTEON_OHCI
+ bool "Octeon on-chip OHCI support (DEPRECATED)"
+ depends on CAVIUM_OCTEON_SOC
+ default USB_OCTEON_EHCI
+ select USB_OHCI_BIG_ENDIAN_MMIO
+ select USB_OHCI_LITTLE_ENDIAN
+ select USB_OHCI_HCD_PLATFORM
+ help
+ This option is deprecated now and the driver was removed, use
+ USB_OHCI_HCD_PLATFORM instead.
+
+ Enable support for the Octeon II SOC's on-chip OHCI
+ controller. It is needed for low-speed USB 1.0 device
+ support. All CN6XXX based chips with USB are supported.
+
+endif # USB_OHCI_HCD
+
+config USB_UHCI_HCD
+ tristate "UHCI HCD (most Intel and VIA) support"
+ depends on PCI || USB_UHCI_SUPPORT_NON_PCI_HC
+ ---help---
+ The Universal Host Controller Interface is a standard by Intel for
+ accessing the USB hardware in the PC (which is also called the USB
+ host controller). If your USB host controller conforms to this
+ standard, you may want to say Y, but see below. All recent boards
+ with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
+ i810, i820) conform to this standard. Also all VIA PCI chipsets
+ (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
+ 133) and LEON/GRLIB SoCs with the GRUSBHC controller.
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uhci-hcd.
+
+config USB_UHCI_SUPPORT_NON_PCI_HC
+ bool
+ default y if (SPARC_LEON || USB_UHCI_PLATFORM)
+
+config USB_UHCI_PLATFORM
+ bool
+ default y if ARCH_VT8500
+
+config USB_UHCI_BIG_ENDIAN_MMIO
+ bool
+ default y if SPARC_LEON
+
+config USB_UHCI_BIG_ENDIAN_DESC
+ bool
+ default y if SPARC_LEON
+
+config USB_FHCI_HCD
+ tristate "Freescale QE USB Host Controller support"
+ depends on OF_GPIO && QE_GPIO && QUICC_ENGINE
+ select FSL_GTM
+ select QE_USB
+ help
+ This driver enables support for Freescale QE USB Host Controller
+ (as found on MPC8360 and MPC8323 processors), the driver supports
+ Full and Low Speed USB.
+
+config FHCI_DEBUG
+ bool "Freescale QE USB Host Controller debug support"
+ depends on USB_FHCI_HCD && DEBUG_FS
+ help
+ Say "y" to see some FHCI debug information and statistics
+ through debugfs.
+
+config USB_U132_HCD
+ tristate "Elan U132 Adapter Host Controller"
+ depends on USB_FTDI_ELAN
+ help
+ The U132 adapter is a USB to CardBus adapter specifically designed
+ for PC cards that contain an OHCI host controller. Typical PC cards
+ are the Orange Mobile 3G Option GlobeTrotter Fusion card. The U132
+ adapter will *NOT* work with PC cards that do not contain an OHCI
+ controller.
+
+ For those PC cards that contain multiple OHCI controllers only the
+ first one is used.
+
+ The driver consists of two modules, the "ftdi-elan" module is a
+ USB client driver that interfaces to the FTDI chip within ELAN's
+ USB-to-PCMCIA adapter, and this "u132-hcd" module is a USB host
+ controller driver that talks to the OHCI controller within the
+ CardBus cards that are inserted in the U132 adapter.
+
+ This driver has been tested with a CardBus OHCI USB adapter, and
+ worked with a USB PEN Drive inserted into the first USB port of
+ the PCCARD. A rather pointless thing to do, but useful for testing.
+
+ It is safe to say M here.
+
+ See also <http://www.elandigitalsystems.com/support/ufaq/u132linux.php>
+
+config USB_SL811_HCD
+ tristate "SL811HS HCD support"
+ help
+ The SL811HS is a single-port USB controller that supports either
+ host side or peripheral side roles. Enable this option if your
+ board has this chip, and you want to use it as a host controller.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sl811-hcd.
+
+config USB_SL811_HCD_ISO
+ bool "partial ISO support"
+ depends on USB_SL811_HCD
+ help
+ The driver doesn't support iso_frame_desc (yet), but for some simple
+ devices that just queue one ISO frame per URB, then ISO transfers
+ "should" work using the normal urb status fields.
+
+ If unsure, say N.
+
+config USB_SL811_CS
+ tristate "CF/PCMCIA support for SL811HS HCD"
+ depends on USB_SL811_HCD && PCMCIA
+ help
+ Wraps a PCMCIA driver around the SL811HS HCD, supporting the RATOC
+ REX-CFU1U CF card (often used with PDAs). If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called "sl811_cs".
+
+config USB_R8A66597_HCD
+ tristate "R8A66597 HCD support"
+ help
+ The R8A66597 is a USB 2.0 host and peripheral controller.
+
+ Enable this option if your board has this chip, and you want
+ to use it as a host controller. If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called r8a66597-hcd.
+
+config USB_RENESAS_USBHS_HCD
+ tristate "Renesas USBHS HCD support"
+ depends on USB_RENESAS_USBHS
+ help
+ The Renesas USBHS is a USB 2.0 host and peripheral controller.
+
+ Enable this option if your board has this chip, and you want
+ to use it as a host controller. If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called renesas-usbhs.
+
+config USB_WHCI_HCD
+ tristate "Wireless USB Host Controller Interface (WHCI) driver"
+ depends on PCI && USB && UWB
+ select USB_WUSB
+ select UWB_WHCI
+ help
+ A driver for PCI-based Wireless USB Host Controllers that are
+ compliant with the WHCI specification.
+
+ To compile this driver a module, choose M here: the module
+ will be called "whci-hcd".
+
+config USB_HWA_HCD
+ tristate "Host Wire Adapter (HWA) driver"
+ depends on USB && UWB
+ select USB_WUSB
+ select UWB_HWA
+ help
+ This driver enables you to connect Wireless USB devices to
+ your system using a Host Wire Adaptor USB dongle. This is an
+ UWB Radio Controller and WUSB Host Controller connected to
+ your machine via USB (specified in WUSB1.0).
+
+ To compile this driver a module, choose M here: the module
+ will be called "hwa-hc".
+
+config USB_IMX21_HCD
+ tristate "i.MX21 HCD support"
+ depends on ARM && ARCH_MXC
+ help
+ This driver enables support for the on-chip USB host in the
+ i.MX21 processor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called "imx21-hcd".
+
+config USB_HCD_BCMA
+ tristate "BCMA usb host driver"
+ depends on BCMA
+ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
+ help
+ Enable support for the EHCI and OCHI host controller on an bcma bus.
+ It converts the bcma driver into two platform device drivers
+ for ehci and ohci.
+
+ If unsure, say N.
+
+config USB_HCD_SSB
+ tristate "SSB usb host driver"
+ depends on SSB
+ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
+ help
+ Enable support for the EHCI and OCHI host controller on an bcma bus.
+ It converts the bcma driver into two platform device drivers
+ for ehci and ohci.
+
+ If unsure, say N.
+
+config USB_HCD_TEST_MODE
+ bool "HCD test mode support"
+ ---help---
+ Say 'Y' to enable additional software test modes that may be
+ supported by the host controller drivers.
+
+ One such test mode is the Embedded High-speed Host Electrical Test
+ (EHSET) for EHCI host controller hardware, specifically the "Single
+ Step Set Feature" test. Typically this will be enabled for On-the-Go
+ or embedded hosts that need to undergo USB-IF compliance testing with
+ the aid of special testing hardware. In the future, this may expand
+ to include other tests that require support from a HCD driver.
+
+ This option is of interest only to developers who need to validate
+ their USB hardware designs. It is not needed for normal use. If
+ unsure, say N.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
new file mode 100644
index 000000000..65b0b6a58
--- /dev/null
+++ b/drivers/usb/host/Makefile
@@ -0,0 +1,77 @@
+#
+# Makefile for USB Host Controller Drivers
+#
+
+# tell define_trace.h where to find the xhci trace header
+CFLAGS_xhci-trace.o := -I$(src)
+
+fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o
+fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
+
+fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
+
+xhci-hcd-y := xhci.o xhci-mem.o
+xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
+xhci-hcd-y += xhci-trace.o
+
+xhci-plat-hcd-y := xhci-plat.o
+ifneq ($(CONFIG_USB_XHCI_MVEBU), )
+ xhci-plat-hcd-y += xhci-mvebu.o
+endif
+ifneq ($(CONFIG_USB_XHCI_RCAR), )
+ xhci-plat-hcd-y += xhci-rcar.o
+endif
+
+obj-$(CONFIG_USB_WHCI_HCD) += whci/
+
+obj-$(CONFIG_PCI) += pci-quirks.o
+
+obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
+obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
+
+obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
+obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
+obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o
+obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
+obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o
+obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o
+obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o
+obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o
+obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
+obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
+obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o
+obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
+obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
+
+obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
+obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
+obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
+
+obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
+obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
+obj-$(CONFIG_USB_OHCI_HCD_PLATFORM) += ohci-platform.o
+obj-$(CONFIG_USB_OHCI_EXYNOS) += ohci-exynos.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP1) += ohci-omap.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP3) += ohci-omap3.o
+obj-$(CONFIG_USB_OHCI_HCD_SPEAR) += ohci-spear.o
+obj-$(CONFIG_USB_OHCI_HCD_STI) += ohci-st.o
+obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o
+obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o
+obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o
+obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o
+
+obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
+obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
+obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.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
+obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
+obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
+obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
+obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
+obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
+obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
+obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o
+obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
+obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
new file mode 100644
index 000000000..526cfab41
--- /dev/null
+++ b/drivers/usb/host/bcma-hcd.c
@@ -0,0 +1,333 @@
+/*
+ * Broadcom specific Advanced Microcontroller Bus
+ * Broadcom USB-core driver (BCMA bus glue)
+ *
+ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/usb/ohci_pdriver.h>
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Common USB driver for BCMA Bus");
+MODULE_LICENSE("GPL");
+
+struct bcma_hcd_device {
+ struct platform_device *ehci_dev;
+ struct platform_device *ohci_dev;
+};
+
+/* Wait for bitmask in a register to get set or cleared.
+ * timeout is in units of ten-microseconds.
+ */
+static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask,
+ int timeout)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < timeout; i++) {
+ val = bcma_read32(dev, reg);
+ if ((val & bitmask) == bitmask)
+ return 0;
+ udelay(10);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void bcma_hcd_4716wa(struct bcma_device *dev)
+{
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+ /* Work around for 4716 failures. */
+ if (dev->bus->chipinfo.id == 0x4716) {
+ u32 tmp;
+
+ tmp = bcma_cpu_clock(&dev->bus->drv_mips);
+ if (tmp >= 480000000)
+ tmp = 0x1846b; /* set CDR to 0x11(fast) */
+ else if (tmp == 453000000)
+ tmp = 0x1046b; /* set CDR to 0x10(slow) */
+ else
+ tmp = 0;
+
+ /* Change Shim mdio control reg to fix host not acking at
+ * high frequencies
+ */
+ if (tmp) {
+ bcma_write32(dev, 0x524, 0x1); /* write sel to enable */
+ udelay(500);
+
+ bcma_write32(dev, 0x524, tmp);
+ udelay(500);
+ bcma_write32(dev, 0x524, 0x4ab);
+ udelay(500);
+ bcma_read32(dev, 0x528);
+ bcma_write32(dev, 0x528, 0x80000000);
+ }
+ }
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+}
+
+/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
+static void bcma_hcd_init_chip(struct bcma_device *dev)
+{
+ u32 tmp;
+
+ /*
+ * USB 2.0 special considerations:
+ *
+ * 1. Since the core supports both OHCI and EHCI functions, it must
+ * only be reset once.
+ *
+ * 2. In addition to the standard SI reset sequence, the Host Control
+ * Register must be programmed to bring the USB core and various
+ * phy components out of reset.
+ */
+ if (!bcma_core_is_enabled(dev)) {
+ bcma_core_enable(dev, 0);
+ mdelay(10);
+ if (dev->id.rev >= 5) {
+ /* Enable Misc PLL */
+ tmp = bcma_read32(dev, 0x1e0);
+ tmp |= 0x100;
+ bcma_write32(dev, 0x1e0, tmp);
+ if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100))
+ printk(KERN_EMERG "Failed to enable misc PPL!\n");
+
+ /* Take out of resets */
+ bcma_write32(dev, 0x200, 0x4ff);
+ udelay(25);
+ bcma_write32(dev, 0x200, 0x6ff);
+ udelay(25);
+
+ /* Make sure digital and AFE are locked in USB PHY */
+ bcma_write32(dev, 0x524, 0x6b);
+ udelay(50);
+ tmp = bcma_read32(dev, 0x524);
+ udelay(50);
+ bcma_write32(dev, 0x524, 0xab);
+ udelay(50);
+ tmp = bcma_read32(dev, 0x524);
+ udelay(50);
+ bcma_write32(dev, 0x524, 0x2b);
+ udelay(50);
+ tmp = bcma_read32(dev, 0x524);
+ udelay(50);
+ bcma_write32(dev, 0x524, 0x10ab);
+ udelay(50);
+ tmp = bcma_read32(dev, 0x524);
+
+ if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) {
+ tmp = bcma_read32(dev, 0x528);
+ printk(KERN_EMERG
+ "USB20H mdio_rddata 0x%08x\n", tmp);
+ }
+ bcma_write32(dev, 0x528, 0x80000000);
+ tmp = bcma_read32(dev, 0x314);
+ udelay(265);
+ bcma_write32(dev, 0x200, 0x7ff);
+ udelay(10);
+
+ /* Take USB and HSIC out of non-driving modes */
+ bcma_write32(dev, 0x510, 0);
+ } else {
+ bcma_write32(dev, 0x200, 0x7ff);
+
+ udelay(1);
+ }
+
+ bcma_hcd_4716wa(dev);
+ }
+}
+
+static const struct usb_ehci_pdata ehci_pdata = {
+};
+
+static const struct usb_ohci_pdata ohci_pdata = {
+};
+
+static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr)
+{
+ struct platform_device *hci_dev;
+ struct resource hci_res[2];
+ int ret = -ENOMEM;
+
+ memset(hci_res, 0, sizeof(hci_res));
+
+ hci_res[0].start = addr;
+ hci_res[0].end = hci_res[0].start + 0x1000 - 1;
+ hci_res[0].flags = IORESOURCE_MEM;
+
+ hci_res[1].start = dev->irq;
+ hci_res[1].flags = IORESOURCE_IRQ;
+
+ hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
+ "ehci-platform" , 0);
+ if (!hci_dev)
+ return NULL;
+
+ hci_dev->dev.parent = &dev->dev;
+ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
+
+ ret = platform_device_add_resources(hci_dev, hci_res,
+ ARRAY_SIZE(hci_res));
+ if (ret)
+ goto err_alloc;
+ if (ohci)
+ ret = platform_device_add_data(hci_dev, &ohci_pdata,
+ sizeof(ohci_pdata));
+ else
+ ret = platform_device_add_data(hci_dev, &ehci_pdata,
+ sizeof(ehci_pdata));
+ if (ret)
+ goto err_alloc;
+ ret = platform_device_add(hci_dev);
+ if (ret)
+ goto err_alloc;
+
+ return hci_dev;
+
+err_alloc:
+ platform_device_put(hci_dev);
+ return ERR_PTR(ret);
+}
+
+static int bcma_hcd_probe(struct bcma_device *dev)
+{
+ int err;
+ u16 chipid_top;
+ u32 ohci_addr;
+ struct bcma_hcd_device *usb_dev;
+ struct bcma_chipinfo *chipinfo;
+
+ chipinfo = &dev->bus->chipinfo;
+ /* USBcores are only connected on embedded devices. */
+ chipid_top = (chipinfo->id & 0xFF00);
+ if (chipid_top != 0x4700 && chipid_top != 0x5300)
+ return -ENODEV;
+
+ /* TODO: Probably need checks here; is the core connected? */
+
+ if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
+ return -EOPNOTSUPP;
+
+ usb_dev = kzalloc(sizeof(struct bcma_hcd_device), GFP_KERNEL);
+ if (!usb_dev)
+ return -ENOMEM;
+
+ bcma_hcd_init_chip(dev);
+
+ /* In AI chips EHCI is addrspace 0, OHCI is 1 */
+ ohci_addr = dev->addr_s[0];
+ if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749)
+ && chipinfo->rev == 0)
+ ohci_addr = 0x18009000;
+
+ usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr);
+ if (IS_ERR(usb_dev->ohci_dev)) {
+ err = PTR_ERR(usb_dev->ohci_dev);
+ goto err_free_usb_dev;
+ }
+
+ usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr);
+ if (IS_ERR(usb_dev->ehci_dev)) {
+ err = PTR_ERR(usb_dev->ehci_dev);
+ goto err_unregister_ohci_dev;
+ }
+
+ bcma_set_drvdata(dev, usb_dev);
+ return 0;
+
+err_unregister_ohci_dev:
+ platform_device_unregister(usb_dev->ohci_dev);
+err_free_usb_dev:
+ kfree(usb_dev);
+ return err;
+}
+
+static void bcma_hcd_remove(struct bcma_device *dev)
+{
+ struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
+ struct platform_device *ohci_dev = usb_dev->ohci_dev;
+ struct platform_device *ehci_dev = usb_dev->ehci_dev;
+
+ if (ohci_dev)
+ platform_device_unregister(ohci_dev);
+ if (ehci_dev)
+ platform_device_unregister(ehci_dev);
+
+ bcma_core_disable(dev, 0);
+}
+
+static void bcma_hcd_shutdown(struct bcma_device *dev)
+{
+ bcma_core_disable(dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static int bcma_hcd_suspend(struct bcma_device *dev)
+{
+ bcma_core_disable(dev, 0);
+
+ return 0;
+}
+
+static int bcma_hcd_resume(struct bcma_device *dev)
+{
+ bcma_core_enable(dev, 0);
+
+ return 0;
+}
+
+#else /* !CONFIG_PM */
+#define bcma_hcd_suspend NULL
+#define bcma_hcd_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct bcma_device_id bcma_hcd_table[] = {
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS),
+ {},
+};
+MODULE_DEVICE_TABLE(bcma, bcma_hcd_table);
+
+static struct bcma_driver bcma_hcd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = bcma_hcd_table,
+ .probe = bcma_hcd_probe,
+ .remove = bcma_hcd_remove,
+ .shutdown = bcma_hcd_shutdown,
+ .suspend = bcma_hcd_suspend,
+ .resume = bcma_hcd_resume,
+};
+
+static int __init bcma_hcd_init(void)
+{
+ return bcma_driver_register(&bcma_hcd_driver);
+}
+module_init(bcma_hcd_init);
+
+static void __exit bcma_hcd_exit(void)
+{
+ bcma_driver_unregister(&bcma_hcd_driver);
+}
+module_exit(bcma_hcd_exit);
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 000000000..be0964a80
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,256 @@
+/*
+ * Driver for EHCI UHP on Atmel chips
+ *
+ * Copyright (C) 2009 Atmel Corporation,
+ * Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+#define DRIVER_DESC "EHCI Atmel driver"
+
+static const char hcd_name[] = "ehci-atmel";
+
+/* interface and function clocks */
+#define hcd_to_atmel_ehci_priv(h) \
+ ((struct atmel_ehci_priv *)hcd_to_ehci(h)->priv)
+
+struct atmel_ehci_priv {
+ struct clk *iclk;
+ struct clk *uclk;
+ bool clocked;
+};
+
+static struct hc_driver __read_mostly ehci_atmel_hc_driver;
+
+static const struct ehci_driver_overrides ehci_atmel_drv_overrides __initconst = {
+ .extra_priv_size = sizeof(struct atmel_ehci_priv),
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void atmel_start_clock(struct atmel_ehci_priv *atmel_ehci)
+{
+ if (atmel_ehci->clocked)
+ return;
+
+ clk_prepare_enable(atmel_ehci->uclk);
+ clk_prepare_enable(atmel_ehci->iclk);
+ atmel_ehci->clocked = true;
+}
+
+static void atmel_stop_clock(struct atmel_ehci_priv *atmel_ehci)
+{
+ if (!atmel_ehci->clocked)
+ return;
+
+ clk_disable_unprepare(atmel_ehci->iclk);
+ clk_disable_unprepare(atmel_ehci->uclk);
+ atmel_ehci->clocked = false;
+}
+
+static void atmel_start_ehci(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+ dev_dbg(&pdev->dev, "start\n");
+ atmel_start_clock(atmel_ehci);
+}
+
+static void atmel_stop_ehci(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+ dev_dbg(&pdev->dev, "stop\n");
+ atmel_stop_clock(atmel_ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_atmel_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ const struct hc_driver *driver = &ehci_atmel_hc_driver;
+ struct resource *res;
+ struct ehci_hcd *ehci;
+ struct atmel_ehci_priv *atmel_ehci;
+ int irq;
+ int retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Atmel-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval)
+ goto fail_create_hcd;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+ atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto fail_request_resource;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ atmel_ehci->iclk = devm_clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(atmel_ehci->iclk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = -ENOENT;
+ goto fail_request_resource;
+ }
+
+ atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk");
+ if (IS_ERR(atmel_ehci->uclk)) {
+ dev_err(&pdev->dev, "failed to get uclk\n");
+ retval = PTR_ERR(atmel_ehci->uclk);
+ goto fail_request_resource;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ /* registers start at offset 0x0 */
+ ehci->caps = hcd->regs;
+
+ atmel_start_ehci(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto fail_add_hcd;
+ device_wakeup_enable(hcd->self.controller);
+
+ return retval;
+
+fail_add_hcd:
+ atmel_stop_ehci(pdev);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), retval);
+
+ return retval;
+}
+
+static int ehci_atmel_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ atmel_stop_ehci(pdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ehci_atmel_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+ int ret;
+
+ ret = ehci_suspend(hcd, false);
+ if (ret)
+ return ret;
+
+ atmel_stop_clock(atmel_ehci);
+ return 0;
+}
+
+static int ehci_atmel_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+ atmel_start_clock(atmel_ehci);
+ return ehci_resume(hcd, false);
+}
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_ehci_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g45-ehci" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
+#endif
+
+static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend,
+ ehci_atmel_drv_resume);
+
+static struct platform_driver ehci_atmel_driver = {
+ .probe = ehci_atmel_drv_probe,
+ .remove = ehci_atmel_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "atmel-ehci",
+ .pm = &ehci_atmel_pm_ops,
+ .of_match_table = of_match_ptr(atmel_ehci_dt_ids),
+ },
+};
+
+static int __init ehci_atmel_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ehci_init_driver(&ehci_atmel_hc_driver, &ehci_atmel_drv_overrides);
+ return platform_driver_register(&ehci_atmel_driver);
+}
+module_init(ehci_atmel_init);
+
+static void __exit ehci_atmel_cleanup(void)
+{
+ platform_driver_unregister(&ehci_atmel_driver);
+}
+module_exit(ehci_atmel_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:atmel-ehci");
+MODULE_AUTHOR("Nicolas Ferre");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
new file mode 100644
index 000000000..524cbf26d
--- /dev/null
+++ b/drivers/usb/host/ehci-dbg.c
@@ -0,0 +1,1084 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+/* check the values in the HCSPARAMS register
+ * (host controller _Structural_ parameters)
+ * see EHCI spec, Table 2-4 for each value
+ */
+static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+{
+ u32 params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ ehci_dbg (ehci,
+ "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
+ label, params,
+ HCS_DEBUG_PORT (params),
+ HCS_INDICATOR (params) ? " ind" : "",
+ HCS_N_CC (params),
+ HCS_N_PCC (params),
+ HCS_PORTROUTED (params) ? "" : " ordered",
+ HCS_PPC (params) ? "" : " !ppc",
+ HCS_N_PORTS (params)
+ );
+ /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
+ if (HCS_PORTROUTED (params)) {
+ int i;
+ char buf [46], tmp [7], byte;
+
+ buf[0] = 0;
+ for (i = 0; i < HCS_N_PORTS (params); i++) {
+ // FIXME MIPS won't readb() ...
+ byte = readb (&ehci->caps->portroute[(i>>1)]);
+ sprintf(tmp, "%d ",
+ ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
+ strcat(buf, tmp);
+ }
+ ehci_dbg (ehci, "%s portroute %s\n",
+ label, buf);
+ }
+}
+#else
+
+static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+/* check the values in the HCCPARAMS register
+ * (host controller _Capability_ parameters)
+ * see EHCI Spec, Table 2-5 for each value
+ * */
+static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
+{
+ u32 params = ehci_readl(ehci, &ehci->caps->hcc_params);
+
+ if (HCC_ISOC_CACHE (params)) {
+ ehci_dbg (ehci,
+ "%s hcc_params %04x caching frame %s%s%s\n",
+ label, params,
+ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+ HCC_CANPARK(params) ? " park" : "",
+ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
+ } else {
+ ehci_dbg (ehci,
+ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
+ label,
+ params,
+ HCC_ISOC_THRES(params),
+ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+ HCC_CANPARK(params) ? " park" : "",
+ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "",
+ HCC_LPM(params) ? " LPM" : "",
+ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
+ HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
+ HCC_32FRAME_PERIODIC_LIST(params) ?
+ " 32 periodic list" : "");
+ }
+}
+#else
+
+static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+static void __maybe_unused
+dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ hc32_to_cpup(ehci, &qtd->hw_next),
+ hc32_to_cpup(ehci, &qtd->hw_alt_next),
+ hc32_to_cpup(ehci, &qtd->hw_token),
+ hc32_to_cpup(ehci, &qtd->hw_buf [0]));
+ if (qtd->hw_buf [1])
+ ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ hc32_to_cpup(ehci, &qtd->hw_buf[1]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[2]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[3]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[4]));
+}
+
+static void __maybe_unused
+dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qh_hw *hw = qh->hw;
+
+ ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
+ dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
+}
+
+static void __maybe_unused
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+ ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
+ label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next),
+ itd->urb);
+ ehci_dbg (ehci,
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(ehci, itd->hw_transaction[0]),
+ hc32_to_cpu(ehci, itd->hw_transaction[1]),
+ hc32_to_cpu(ehci, itd->hw_transaction[2]),
+ hc32_to_cpu(ehci, itd->hw_transaction[3]),
+ hc32_to_cpu(ehci, itd->hw_transaction[4]),
+ hc32_to_cpu(ehci, itd->hw_transaction[5]),
+ hc32_to_cpu(ehci, itd->hw_transaction[6]),
+ hc32_to_cpu(ehci, itd->hw_transaction[7]));
+ ehci_dbg (ehci,
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(ehci, itd->hw_bufp[0]),
+ hc32_to_cpu(ehci, itd->hw_bufp[1]),
+ hc32_to_cpu(ehci, itd->hw_bufp[2]),
+ hc32_to_cpu(ehci, itd->hw_bufp[3]),
+ hc32_to_cpu(ehci, itd->hw_bufp[4]),
+ hc32_to_cpu(ehci, itd->hw_bufp[5]),
+ hc32_to_cpu(ehci, itd->hw_bufp[6]));
+ ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
+ itd->index[0], itd->index[1], itd->index[2],
+ itd->index[3], itd->index[4], itd->index[5],
+ itd->index[6], itd->index[7]);
+}
+
+static void __maybe_unused
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+{
+ ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
+ label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next),
+ sitd->urb);
+ ehci_dbg (ehci,
+ " addr %08x sched %04x result %08x buf %08x %08x\n",
+ hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
+ hc32_to_cpu(ehci, sitd->hw_uframe),
+ hc32_to_cpu(ehci, sitd->hw_results),
+ hc32_to_cpu(ehci, sitd->hw_buf[0]),
+ hc32_to_cpu(ehci, sitd->hw_buf[1]));
+}
+
+static int __maybe_unused
+dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
+{
+ return scnprintf (buf, len,
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", status,
+ (status & STS_PPCE_MASK) ? " PPCE" : "",
+ (status & STS_ASS) ? " Async" : "",
+ (status & STS_PSS) ? " Periodic" : "",
+ (status & STS_RECL) ? " Recl" : "",
+ (status & STS_HALT) ? " Halt" : "",
+ (status & STS_IAA) ? " IAA" : "",
+ (status & STS_FATAL) ? " FATAL" : "",
+ (status & STS_FLR) ? " FLR" : "",
+ (status & STS_PCD) ? " PCD" : "",
+ (status & STS_ERR) ? " ERR" : "",
+ (status & STS_INT) ? " INT" : ""
+ );
+}
+
+static int __maybe_unused
+dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
+{
+ return scnprintf (buf, len,
+ "%s%sintrenable %02x%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", enable,
+ (enable & STS_PPCE_MASK) ? " PPCE" : "",
+ (enable & STS_IAA) ? " IAA" : "",
+ (enable & STS_FATAL) ? " FATAL" : "",
+ (enable & STS_FLR) ? " FLR" : "",
+ (enable & STS_PCD) ? " PCD" : "",
+ (enable & STS_ERR) ? " ERR" : "",
+ (enable & STS_INT) ? " INT" : ""
+ );
+}
+
+static const char *const fls_strings [] =
+ { "1024", "512", "256", "??" };
+
+static int
+dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
+{
+ return scnprintf (buf, len,
+ "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
+ "period=%s%s %s",
+ label, label [0] ? " " : "", command,
+ (command & CMD_HIRD) ? " HIRD" : "",
+ (command & CMD_PPCEE) ? " PPCEE" : "",
+ (command & CMD_FSP) ? " FSP" : "",
+ (command & CMD_ASPE) ? " ASPE" : "",
+ (command & CMD_PSPE) ? " PSPE" : "",
+ (command & CMD_PARK) ? " park" : "(park)",
+ CMD_PARK_CNT (command),
+ (command >> 16) & 0x3f,
+ (command & CMD_LRESET) ? " LReset" : "",
+ (command & CMD_IAAD) ? " IAAD" : "",
+ (command & CMD_ASE) ? " Async" : "",
+ (command & CMD_PSE) ? " Periodic" : "",
+ fls_strings [(command >> 2) & 0x3],
+ (command & CMD_RESET) ? " Reset" : "",
+ (command & CMD_RUN) ? "RUN" : "HALT"
+ );
+}
+
+static int
+dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
+{
+ char *sig;
+
+ /* signaling state */
+ switch (status & (3 << 10)) {
+ case 0 << 10: sig = "se0"; break;
+ case 1 << 10: sig = "k"; break; /* low speed */
+ case 2 << 10: sig = "j"; break;
+ default: sig = "?"; break;
+ }
+
+ return scnprintf (buf, len,
+ "%s%sport:%d status %06x %d %s%s%s%s%s%s "
+ "sig=%s%s%s%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", port, status,
+ status>>25,/*device address */
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
+ " ACK" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
+ " NYET" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
+ " STALL" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
+ " ERR" : "",
+ (status & PORT_POWER) ? " POWER" : "",
+ (status & PORT_OWNER) ? " OWNER" : "",
+ sig,
+ (status & PORT_LPM) ? " LPM" : "",
+ (status & PORT_RESET) ? " RESET" : "",
+ (status & PORT_SUSPEND) ? " SUSPEND" : "",
+ (status & PORT_RESUME) ? " RESUME" : "",
+ (status & PORT_OCC) ? " OCC" : "",
+ (status & PORT_OC) ? " OC" : "",
+ (status & PORT_PEC) ? " PEC" : "",
+ (status & PORT_PE) ? " PE" : "",
+ (status & PORT_CSC) ? " CSC" : "",
+ (status & PORT_CONNECT) ? " CONNECT" : "");
+}
+
+#else
+static inline void __maybe_unused
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{}
+
+static inline int __maybe_unused
+dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
+{ return 0; }
+
+static inline int __maybe_unused
+dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
+{ return 0; }
+
+static inline int __maybe_unused
+dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
+{ return 0; }
+
+static inline int __maybe_unused
+dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
+{ return 0; }
+
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(ehci, label, status) { \
+ char _buf [80]; \
+ dbg_status_buf (_buf, sizeof _buf, label, status); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_cmd(ehci, label, command) { \
+ char _buf [80]; \
+ dbg_command_buf (_buf, sizeof _buf, label, command); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_port(ehci, label, port, status) { \
+ char _buf [80]; \
+ dbg_port_buf (_buf, sizeof _buf, label, port, status); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILES
+
+static inline void create_debug_files (struct ehci_hcd *bus) { }
+static inline void remove_debug_files (struct ehci_hcd *bus) { }
+
+#else
+
+/* troubleshooting help: expose state in debugfs */
+
+static int debug_async_open(struct inode *, struct file *);
+static int debug_bandwidth_open(struct inode *, struct file *);
+static int debug_periodic_open(struct inode *, struct file *);
+static int debug_registers_open(struct inode *, struct file *);
+
+static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
+static int debug_close(struct inode *, struct file *);
+
+static const struct file_operations debug_async_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_async_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_bandwidth_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_bandwidth_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_periodic_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_periodic_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_registers_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_registers_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+
+static struct dentry *ehci_debug_root;
+
+struct debug_buffer {
+ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
+ struct usb_bus *bus;
+ struct mutex mutex; /* protect filling of buffer */
+ size_t count; /* number of characters filled into buffer */
+ char *output_buf;
+ size_t alloc_size;
+};
+
+#define speed_char(info1) ({ char tmp; \
+ switch (info1 & (3 << 12)) { \
+ case QH_FULL_SPEED: tmp = 'f'; break; \
+ case QH_LOW_SPEED: tmp = 'l'; break; \
+ case QH_HIGH_SPEED: tmp = 'h'; break; \
+ default: tmp = '?'; break; \
+ } tmp; })
+
+static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
+{
+ __u32 v = hc32_to_cpu(ehci, token);
+
+ if (v & QTD_STS_ACTIVE)
+ return '*';
+ if (v & QTD_STS_HALT)
+ return '-';
+ if (!IS_SHORT_READ (v))
+ return ' ';
+ /* tries to advance through hw_alt_next */
+ return '/';
+}
+
+static void qh_lines (
+ struct ehci_hcd *ehci,
+ struct ehci_qh *qh,
+ char **nextp,
+ unsigned *sizep
+)
+{
+ u32 scratch;
+ u32 hw_curr;
+ struct list_head *entry;
+ struct ehci_qtd *td;
+ unsigned temp;
+ unsigned size = *sizep;
+ char *next = *nextp;
+ char mark;
+ __le32 list_end = EHCI_LIST_END(ehci);
+ struct ehci_qh_hw *hw = qh->hw;
+
+ if (hw->hw_qtd_next == list_end) /* NEC does this */
+ mark = '@';
+ else
+ mark = token_mark(ehci, hw->hw_token);
+ if (mark == '/') { /* qh_alt_next controls qh advance? */
+ if ((hw->hw_alt_next & QTD_MASK(ehci))
+ == ehci->async->hw->hw_alt_next)
+ mark = '#'; /* blocked */
+ else if (hw->hw_alt_next == list_end)
+ mark = '.'; /* use hw_qtd_next */
+ /* else alt_next points to some other qtd */
+ }
+ scratch = hc32_to_cpup(ehci, &hw->hw_info1);
+ hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
+ temp = scnprintf (next, size,
+ "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+ qh, scratch & 0x007f,
+ speed_char (scratch),
+ (scratch >> 8) & 0x000f,
+ scratch, hc32_to_cpup(ehci, &hw->hw_info2),
+ hc32_to_cpup(ehci, &hw->hw_token), mark,
+ (cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
+ ? "data1" : "data0",
+ (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
+ size -= temp;
+ next += temp;
+
+ /* hc may be modifying the list as we read it ... */
+ list_for_each (entry, &qh->qtd_list) {
+ td = list_entry (entry, struct ehci_qtd, qtd_list);
+ scratch = hc32_to_cpup(ehci, &td->hw_token);
+ mark = ' ';
+ if (hw_curr == td->qtd_dma)
+ mark = '*';
+ else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
+ mark = '+';
+ else if (QTD_LENGTH (scratch)) {
+ if (td->hw_alt_next == ehci->async->hw->hw_alt_next)
+ mark = '#';
+ else if (td->hw_alt_next != list_end)
+ mark = '/';
+ }
+ temp = snprintf (next, size,
+ "\n\t%p%c%s len=%d %08x urb %p",
+ td, mark, ({ char *tmp;
+ switch ((scratch>>8)&0x03) {
+ case 0: tmp = "out"; break;
+ case 1: tmp = "in"; break;
+ case 2: tmp = "setup"; break;
+ default: tmp = "?"; break;
+ } tmp;}),
+ (scratch >> 16) & 0x7fff,
+ scratch,
+ td->urb);
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+ if (temp == size)
+ goto done;
+ }
+
+ temp = snprintf (next, size, "\n");
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+
+done:
+ *sizep = size;
+ *nextp = next;
+}
+
+static ssize_t fill_async_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ struct ehci_qh *qh;
+
+ hcd = bus_to_hcd(buf->bus);
+ ehci = hcd_to_ehci (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ *next = 0;
+
+ /* dumps a snapshot of the async schedule.
+ * usually empty except for long-term bulk reads, or head.
+ * one QH per line, and TDs we know about
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+ qh_lines (ehci, qh, &next, &size);
+ if (!list_empty(&ehci->async_unlink) && size > 0) {
+ temp = scnprintf(next, size, "\nunlink =\n");
+ size -= temp;
+ next += temp;
+
+ list_for_each_entry(qh, &ehci->async_unlink, unlink_node) {
+ if (size <= 0)
+ break;
+ qh_lines(ehci, qh, &next, &size);
+ }
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ return strlen(buf->output_buf);
+}
+
+static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
+{
+ struct ehci_hcd *ehci;
+ struct ehci_tt *tt;
+ struct ehci_per_sched *ps;
+ unsigned temp, size;
+ char *next;
+ unsigned i;
+ u8 *bw;
+ u16 *bf;
+ u8 budget[EHCI_BANDWIDTH_SIZE];
+
+ ehci = hcd_to_ehci(bus_to_hcd(buf->bus));
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ *next = 0;
+
+ spin_lock_irq(&ehci->lock);
+
+ /* Dump the HS bandwidth table */
+ temp = scnprintf(next, size,
+ "HS bandwidth allocation (us per microframe)\n");
+ size -= temp;
+ next += temp;
+ for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+ bw = &ehci->bandwidth[i];
+ temp = scnprintf(next, size,
+ "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+ i, bw[0], bw[1], bw[2], bw[3],
+ bw[4], bw[5], bw[6], bw[7]);
+ size -= temp;
+ next += temp;
+ }
+
+ /* Dump all the FS/LS tables */
+ list_for_each_entry(tt, &ehci->tt_list, tt_list) {
+ temp = scnprintf(next, size,
+ "\nTT %s port %d FS/LS bandwidth allocation (us per frame)\n",
+ dev_name(&tt->usb_tt->hub->dev),
+ tt->tt_port + !!tt->usb_tt->multi);
+ size -= temp;
+ next += temp;
+
+ bf = tt->bandwidth;
+ temp = scnprintf(next, size,
+ " %5u%5u%5u%5u%5u%5u%5u%5u\n",
+ bf[0], bf[1], bf[2], bf[3],
+ bf[4], bf[5], bf[6], bf[7]);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf(next, size,
+ "FS/LS budget (us per microframe)\n");
+ size -= temp;
+ next += temp;
+ compute_tt_budget(budget, tt);
+ for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+ bw = &budget[i];
+ temp = scnprintf(next, size,
+ "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+ i, bw[0], bw[1], bw[2], bw[3],
+ bw[4], bw[5], bw[6], bw[7]);
+ size -= temp;
+ next += temp;
+ }
+ list_for_each_entry(ps, &tt->ps_list, ps_list) {
+ temp = scnprintf(next, size,
+ "%s ep %02x: %4u @ %2u.%u+%u mask %04x\n",
+ dev_name(&ps->udev->dev),
+ ps->ep->desc.bEndpointAddress,
+ ps->tt_usecs,
+ ps->bw_phase, ps->phase_uf,
+ ps->bw_period, ps->cs_mask);
+ size -= temp;
+ next += temp;
+ }
+ }
+ spin_unlock_irq(&ehci->lock);
+
+ return next - buf->output_buf;
+}
+
+#define DBG_SCHED_LIMIT 64
+static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ union ehci_shadow p, *seen;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+ __hc32 tag;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ hcd = bus_to_hcd(buf->bus);
+ ehci = hcd_to_ehci (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule.
+ * iso changes, interrupt usually doesn't.
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < ehci->periodic_size; i++) {
+ p = ehci->pshadow [i];
+ if (likely (!p.ptr))
+ continue;
+ tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
+
+ temp = scnprintf (next, size, "%4d: ", i);
+ size -= temp;
+ next += temp;
+
+ do {
+ struct ehci_qh_hw *hw;
+
+ switch (hc32_to_cpu(ehci, tag)) {
+ case Q_TYPE_QH:
+ hw = p.qh->hw;
+ temp = scnprintf (next, size, " qh%d-%04x/%p",
+ p.qh->ps.period,
+ hc32_to_cpup(ehci,
+ &hw->hw_info2)
+ /* uframe masks */
+ & (QH_CMASK | QH_SMASK),
+ p.qh);
+ size -= temp;
+ next += temp;
+ /* don't repeat what follows this qh */
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp].ptr != p.ptr)
+ continue;
+ if (p.qh->qh_next.ptr) {
+ temp = scnprintf (next, size,
+ " ...");
+ size -= temp;
+ next += temp;
+ }
+ break;
+ }
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 scratch = hc32_to_cpup(ehci,
+ &hw->hw_info1);
+ struct ehci_qtd *qtd;
+ char *type = "";
+
+ /* count tds, get ep direction */
+ temp = 0;
+ list_for_each_entry (qtd,
+ &p.qh->qtd_list,
+ qtd_list) {
+ temp++;
+ switch (0x03 & (hc32_to_cpu(
+ ehci,
+ qtd->hw_token) >> 8)) {
+ case 0: type = "out"; continue;
+ case 1: type = "in"; continue;
+ }
+ }
+
+ temp = scnprintf (next, size,
+ " (%c%d ep%d%s "
+ "[%d/%d] q%d p%d)",
+ speed_char (scratch),
+ scratch & 0x007f,
+ (scratch >> 8) & 0x000f, type,
+ p.qh->ps.usecs,
+ p.qh->ps.c_usecs,
+ temp,
+ 0x7ff & (scratch >> 16));
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++].qh = p.qh;
+ } else
+ temp = 0;
+ tag = Q_NEXT_TYPE(ehci, hw->hw_next);
+ p = p.qh->qh_next;
+ break;
+ case Q_TYPE_FSTN:
+ temp = scnprintf (next, size,
+ " fstn-%8x/%p", p.fstn->hw_prev,
+ p.fstn);
+ tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next);
+ p = p.fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ temp = scnprintf (next, size,
+ " itd/%p", p.itd);
+ tag = Q_NEXT_TYPE(ehci, p.itd->hw_next);
+ p = p.itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ temp = scnprintf (next, size,
+ " sitd%d-%04x/%p",
+ p.sitd->stream->ps.period,
+ hc32_to_cpup(ehci, &p.sitd->hw_uframe)
+ & 0x0000ffff,
+ p.sitd);
+ tag = Q_NEXT_TYPE(ehci, p.sitd->hw_next);
+ p = p.sitd->sitd_next;
+ break;
+ }
+ size -= temp;
+ next += temp;
+ } while (p.ptr);
+
+ temp = scnprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ kfree (seen);
+
+ return buf->alloc_size - size;
+}
+#undef DBG_SCHED_LIMIT
+
+static const char *rh_state_string(struct ehci_hcd *ehci)
+{
+ switch (ehci->rh_state) {
+ case EHCI_RH_HALTED:
+ return "halted";
+ case EHCI_RH_SUSPENDED:
+ return "suspended";
+ case EHCI_RH_RUNNING:
+ return "running";
+ case EHCI_RH_STOPPING:
+ return "stopping";
+ }
+ return "?";
+}
+
+static ssize_t fill_registers_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ unsigned temp, size, i;
+ char *next, scratch [80];
+ static char fmt [] = "%*s\n";
+ static char label [] = "";
+
+ hcd = bus_to_hcd(buf->bus);
+ ehci = hcd_to_ehci (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ size = scnprintf (next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "SUSPENDED (no register access)\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc);
+ goto done;
+ }
+
+ /* Capability Registers */
+ i = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+ temp = scnprintf (next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "EHCI %x.%02x, rh state %s\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc,
+ i >> 8, i & 0x0ff, rh_state_string(ehci));
+ size -= temp;
+ next += temp;
+
+#ifdef CONFIG_PCI
+ /* EHCI 0.96 and later may have "extended capabilities" */
+ if (dev_is_pci(hcd->self.controller)) {
+ struct pci_dev *pdev;
+ u32 offset, cap, cap2;
+ unsigned count = 256/4;
+
+ pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
+ offset = HCC_EXT_CAPS(ehci_readl(ehci,
+ &ehci->caps->hcc_params));
+ while (offset && count--) {
+ pci_read_config_dword (pdev, offset, &cap);
+ switch (cap & 0xff) {
+ case 1:
+ temp = scnprintf (next, size,
+ "ownership %08x%s%s\n", cap,
+ (cap & (1 << 24)) ? " linux" : "",
+ (cap & (1 << 16)) ? " firmware" : "");
+ size -= temp;
+ next += temp;
+
+ offset += 4;
+ pci_read_config_dword (pdev, offset, &cap2);
+ temp = scnprintf (next, size,
+ "SMI sts/enable 0x%08x\n", cap2);
+ size -= temp;
+ next += temp;
+ break;
+ case 0: /* illegal reserved capability */
+ cap = 0;
+ /* FALLTHROUGH */
+ default: /* unknown */
+ break;
+ }
+ temp = (cap >> 8) & 0xff;
+ }
+ }
+#endif
+
+ // FIXME interpret both types of params
+ i = ehci_readl(ehci, &ehci->caps->hcs_params);
+ temp = scnprintf (next, size, "structural params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ i = ehci_readl(ehci, &ehci->caps->hcc_params);
+ temp = scnprintf (next, size, "capability params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ /* Operational Registers */
+ temp = dbg_status_buf (scratch, sizeof scratch, label,
+ ehci_readl(ehci, &ehci->regs->status));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_command_buf (scratch, sizeof scratch, label,
+ ehci_readl(ehci, &ehci->regs->command));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_intr_buf (scratch, sizeof scratch, label,
+ ehci_readl(ehci, &ehci->regs->intr_enable));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "uframe %04x\n",
+ ehci_read_frame_index(ehci));
+ size -= temp;
+ next += temp;
+
+ for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
+ temp = dbg_port_buf (scratch, sizeof scratch, label, i,
+ ehci_readl(ehci,
+ &ehci->regs->port_status[i - 1]));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+ if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
+ temp = scnprintf (next, size,
+ " debug control %08x\n",
+ ehci_readl(ehci,
+ &ehci->debug->control));
+ size -= temp;
+ next += temp;
+ }
+ }
+
+ if (!list_empty(&ehci->async_unlink)) {
+ temp = scnprintf(next, size, "async unlink qh %p\n",
+ list_first_entry(&ehci->async_unlink,
+ struct ehci_qh, unlink_node));
+ size -= temp;
+ next += temp;
+ }
+
+#ifdef EHCI_STATS
+ temp = scnprintf (next, size,
+ "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+ ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
+ ehci->stats.lost_iaa);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "complete %ld unlink %ld\n",
+ ehci->stats.complete, ehci->stats.unlink);
+ size -= temp;
+ next += temp;
+#endif
+
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ return buf->alloc_size - size;
+}
+
+static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
+ ssize_t (*fill_func)(struct debug_buffer *))
+{
+ struct debug_buffer *buf;
+
+ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+
+ if (buf) {
+ buf->bus = bus;
+ buf->fill_func = fill_func;
+ mutex_init(&buf->mutex);
+ buf->alloc_size = PAGE_SIZE;
+ }
+
+ return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+ int ret = 0;
+
+ if (!buf->output_buf)
+ buf->output_buf = vmalloc(buf->alloc_size);
+
+ if (!buf->output_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = buf->fill_func(buf);
+
+ if (ret >= 0) {
+ buf->count = ret;
+ ret = 0;
+ }
+
+out:
+ return ret;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+ size_t len, loff_t *offset)
+{
+ struct debug_buffer *buf = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&buf->mutex);
+ if (buf->count == 0) {
+ ret = fill_buffer(buf);
+ if (ret != 0) {
+ mutex_unlock(&buf->mutex);
+ goto out;
+ }
+ }
+ mutex_unlock(&buf->mutex);
+
+ ret = simple_read_from_buffer(user_buf, len, offset,
+ buf->output_buf, buf->count);
+
+out:
+ return ret;
+
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf = file->private_data;
+
+ if (buf) {
+ vfree(buf->output_buf);
+ kfree(buf);
+ }
+
+ return 0;
+}
+
+static int debug_async_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_bandwidth_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_bandwidth_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_periodic_open(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf;
+ buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
+ file->private_data = buf;
+ return 0;
+}
+
+static int debug_registers_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_registers_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static inline void create_debug_files (struct ehci_hcd *ehci)
+{
+ struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
+
+ ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root);
+ if (!ehci->debug_dir)
+ return;
+
+ if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
+ &debug_async_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
+ &debug_bandwidth_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
+ &debug_periodic_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
+ &debug_registers_fops))
+ goto file_error;
+
+ return;
+
+file_error:
+ debugfs_remove_recursive(ehci->debug_dir);
+}
+
+static inline void remove_debug_files (struct ehci_hcd *ehci)
+{
+ debugfs_remove_recursive(ehci->debug_dir);
+}
+
+#endif /* STUB_DEBUG_FILES */
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
new file mode 100644
index 000000000..df538fd10
--- /dev/null
+++ b/drivers/usb/host/ehci-exynos.c
@@ -0,0 +1,349 @@
+/*
+ * SAMSUNG EXYNOS USB HOST EHCI Controller
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+#define DRIVER_DESC "EHCI EXYNOS driver"
+
+#define EHCI_INSNREG00(base) (base + 0x90)
+#define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25)
+#define EHCI_INSNREG00_ENA_INCR8 (0x1 << 24)
+#define EHCI_INSNREG00_ENA_INCR4 (0x1 << 23)
+#define EHCI_INSNREG00_ENA_INCRX_ALIGN (0x1 << 22)
+#define EHCI_INSNREG00_ENABLE_DMA_BURST \
+ (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
+ EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
+
+static const char hcd_name[] = "ehci-exynos";
+static struct hc_driver __read_mostly exynos_ehci_hc_driver;
+
+#define PHY_NUMBER 3
+
+struct exynos_ehci_hcd {
+ struct clk *clk;
+ struct phy *phy[PHY_NUMBER];
+};
+
+#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
+
+static int exynos_ehci_get_phy(struct device *dev,
+ struct exynos_ehci_hcd *exynos_ehci)
+{
+ struct device_node *child;
+ struct phy *phy;
+ int phy_number;
+ int ret;
+
+ /* Get PHYs for the controller */
+ for_each_available_child_of_node(dev->of_node, child) {
+ ret = of_property_read_u32(child, "reg", &phy_number);
+ if (ret) {
+ dev_err(dev, "Failed to parse device tree\n");
+ of_node_put(child);
+ return ret;
+ }
+
+ if (phy_number >= PHY_NUMBER) {
+ dev_err(dev, "Invalid number of PHYs\n");
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ phy = devm_of_phy_get(dev, child, NULL);
+ exynos_ehci->phy[phy_number] = phy;
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else if (ret != -ENOSYS && ret != -ENODEV) {
+ dev_err(dev,
+ "Error retrieving usb2 phy: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int exynos_ehci_phy_enable(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+ int i;
+ int ret = 0;
+
+ for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
+ if (!IS_ERR(exynos_ehci->phy[i]))
+ ret = phy_power_on(exynos_ehci->phy[i]);
+ if (ret)
+ for (i--; i >= 0; i--)
+ if (!IS_ERR(exynos_ehci->phy[i]))
+ phy_power_off(exynos_ehci->phy[i]);
+
+ return ret;
+}
+
+static void exynos_ehci_phy_disable(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+ int i;
+
+ for (i = 0; i < PHY_NUMBER; i++)
+ if (!IS_ERR(exynos_ehci->phy[i]))
+ phy_power_off(exynos_ehci->phy[i]);
+}
+
+static void exynos_setup_vbus_gpio(struct device *dev)
+{
+ int err;
+ int gpio;
+
+ if (!dev->of_node)
+ return;
+
+ gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0);
+ if (!gpio_is_valid(gpio))
+ return;
+
+ err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
+ "ehci_vbus_gpio");
+ if (err)
+ dev_err(dev, "can't request ehci vbus gpio %d", gpio);
+}
+
+static int exynos_ehci_probe(struct platform_device *pdev)
+{
+ struct exynos_ehci_hcd *exynos_ehci;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource *res;
+ int irq;
+ int err;
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we move to full device tree support this will vanish off.
+ */
+ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ exynos_setup_vbus_gpio(&pdev->dev);
+
+ hcd = usb_create_hcd(&exynos_ehci_hc_driver,
+ &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ return -ENOMEM;
+ }
+ exynos_ehci = to_exynos_ehci(hcd);
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "samsung,exynos5440-ehci"))
+ goto skip_phy;
+
+ err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
+ if (err)
+ goto fail_clk;
+
+skip_phy:
+
+ exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
+
+ if (IS_ERR(exynos_ehci->clk)) {
+ dev_err(&pdev->dev, "Failed to get usbhost clock\n");
+ err = PTR_ERR(exynos_ehci->clk);
+ goto fail_clk;
+ }
+
+ err = clk_prepare_enable(exynos_ehci->clk);
+ if (err)
+ goto fail_clk;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto fail_io;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "Failed to get IRQ\n");
+ err = -ENODEV;
+ goto fail_io;
+ }
+
+ err = exynos_ehci_phy_enable(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to enable USB phy\n");
+ goto fail_io;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+
+ /* DMA burst Enable */
+ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add USB HCD\n");
+ goto fail_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ platform_set_drvdata(pdev, hcd);
+
+ return 0;
+
+fail_add_hcd:
+ exynos_ehci_phy_disable(&pdev->dev);
+fail_io:
+ clk_disable_unprepare(exynos_ehci->clk);
+fail_clk:
+ usb_put_hcd(hcd);
+ return err;
+}
+
+static int exynos_ehci_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+
+ usb_remove_hcd(hcd);
+
+ exynos_ehci_phy_disable(&pdev->dev);
+
+ clk_disable_unprepare(exynos_ehci->clk);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_ehci_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+
+ bool do_wakeup = device_may_wakeup(dev);
+ int rc;
+
+ rc = ehci_suspend(hcd, do_wakeup);
+ if (rc)
+ return rc;
+
+ exynos_ehci_phy_disable(dev);
+
+ clk_disable_unprepare(exynos_ehci->clk);
+
+ return rc;
+}
+
+static int exynos_ehci_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
+ int ret;
+
+ clk_prepare_enable(exynos_ehci->clk);
+
+ ret = exynos_ehci_phy_enable(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable USB phy\n");
+ clk_disable_unprepare(exynos_ehci->clk);
+ return ret;
+ }
+
+ /* DMA burst Enable */
+ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
+
+ ehci_resume(hcd, false);
+ return 0;
+}
+#else
+#define exynos_ehci_suspend NULL
+#define exynos_ehci_resume NULL
+#endif
+
+static const struct dev_pm_ops exynos_ehci_pm_ops = {
+ .suspend = exynos_ehci_suspend,
+ .resume = exynos_ehci_resume,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_ehci_match[] = {
+ { .compatible = "samsung,exynos4210-ehci" },
+ { .compatible = "samsung,exynos5440-ehci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ehci_match);
+#endif
+
+static struct platform_driver exynos_ehci_driver = {
+ .probe = exynos_ehci_probe,
+ .remove = exynos_ehci_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "exynos-ehci",
+ .pm = &exynos_ehci_pm_ops,
+ .of_match_table = of_match_ptr(exynos_ehci_match),
+ }
+};
+static const struct ehci_driver_overrides exynos_overrides __initdata = {
+ .extra_priv_size = sizeof(struct exynos_ehci_hcd),
+};
+
+static int __init ehci_exynos_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides);
+ return platform_driver_register(&exynos_ehci_driver);
+}
+module_init(ehci_exynos_init);
+
+static void __exit ehci_exynos_cleanup(void)
+{
+ platform_driver_unregister(&exynos_ehci_driver);
+}
+module_exit(ehci_exynos_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:exynos-ehci");
+MODULE_AUTHOR("Jingoo Han");
+MODULE_AUTHOR("Joonyoung Shim");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
new file mode 100644
index 000000000..ab4eee3df
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright 2005-2009 MontaVista Software, Inc.
+ * Copyright 2008,2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
+ * by Hunter Wu.
+ * Power Management support by Dave Liu <daveliu@freescale.com>,
+ * Jerry Huang <Chang-Ming.Huang@freescale.com> and
+ * Anton Vorontsov <avorontsov@ru.mvista.com>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+
+#include "ehci-fsl.h"
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_fsl_probe - initialize FSL-based HCDs
+ * @drvier: Driver to be used for this HCD
+ * @pdev: USB Host Controller being probed
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller.
+ *
+ */
+static int usb_hcd_fsl_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ pr_debug("initializing FSL-SOC USB Controller\n");
+
+ /* Need platform data for setup */
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "No platform data for %s.\n", dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+
+ /*
+ * This is a host mode driver, verify that we're supposed to be
+ * in host mode.
+ */
+ if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_MPH_HOST) ||
+ (pdata->operating_mode == FSL_USB2_DR_OTG))) {
+ dev_err(&pdev->dev,
+ "Non Host Mode configured for %s. Wrong driver linked.\n",
+ dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err2;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ pdata->regs = hcd->regs;
+
+ if (pdata->power_budget)
+ hcd->power_budget = pdata->power_budget;
+
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->init && pdata->init(pdev)) {
+ retval = -ENODEV;
+ goto err2;
+ }
+
+ /* Enable USB controller, 83xx or 8536 */
+ if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6)
+ setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
+
+ /* Don't need to set host mode here. It will be done by tdi_reset() */
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval != 0)
+ goto err2;
+ device_wakeup_enable(hcd->self.controller);
+
+#ifdef CONFIG_USB_OTG
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n",
+ hcd, ehci, hcd->usb_phy);
+
+ if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
+ retval = otg_set_host(hcd->usb_phy->otg,
+ &ehci_to_hcd(ehci)->self);
+ if (retval) {
+ usb_put_phy(hcd->usb_phy);
+ goto err2;
+ }
+ } else {
+ dev_err(&pdev->dev, "can't find phy\n");
+ retval = -ENODEV;
+ goto err2;
+ }
+ }
+#endif
+ return retval;
+
+ err2:
+ usb_put_hcd(hcd);
+ err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
+ if (pdata->exit)
+ pdata->exit(pdev);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_fsl_probe().
+ *
+ */
+static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
+ otg_set_host(hcd->usb_phy->otg, NULL);
+ usb_put_phy(hcd->usb_phy);
+ }
+
+ usb_remove_hcd(hcd);
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, disable clock, etc.
+ */
+ if (pdata->exit)
+ pdata->exit(pdev);
+ usb_put_hcd(hcd);
+}
+
+static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
+ enum fsl_usb2_phy_modes phy_mode,
+ unsigned int port_offset)
+{
+ u32 portsc;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ void __iomem *non_ehci = hcd->regs;
+ struct device *dev = hcd->self.controller;
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev);
+
+ if (pdata->controller_ver < 0) {
+ dev_warn(hcd->self.controller, "Could not get controller version\n");
+ return -ENODEV;
+ }
+
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
+ portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
+
+ switch (phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ if (pdata->have_sysif_regs && pdata->controller_ver) {
+ /* controller version 1.6 or above */
+ clrbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
+ setbits32(non_ehci + FSL_SOC_USB_CTRL,
+ ULPI_PHY_CLK_SEL | USB_CTRL_USB_EN);
+ }
+ portsc |= PORT_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portsc |= PORT_PTS_SERIAL;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portsc |= PORT_PTS_PTW;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ if (pdata->have_sysif_regs && pdata->controller_ver) {
+ /* controller version 1.6 or above */
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
+ mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to
+ become stable - 10ms*/
+ }
+ /* enable UTMI PHY */
+ if (pdata->have_sysif_regs)
+ setbits32(non_ehci + FSL_SOC_USB_CTRL,
+ CTRL_UTMI_PHY_EN);
+ portsc |= PORT_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_NONE:
+ break;
+ }
+
+ if (pdata->have_sysif_regs &&
+ pdata->controller_ver > FSL_USB_VER_1_6 &&
+ (phy_mode == FSL_USB2_PHY_ULPI)) {
+ /* check PHY_CLK_VALID to get phy clk valid */
+ if (!(spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
+ PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0) ||
+ in_be32(non_ehci + FSL_SOC_USB_PRICTRL))) {
+ dev_warn(hcd->self.controller, "USB PHY clock invalid\n");
+ return -EINVAL;
+ }
+ }
+
+ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
+
+ if (phy_mode != FSL_USB2_PHY_ULPI && pdata->have_sysif_regs)
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN);
+
+ return 0;
+}
+
+static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
+{
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
+ struct fsl_usb2_platform_data *pdata;
+ void __iomem *non_ehci = hcd->regs;
+
+ pdata = dev_get_platdata(hcd->self.controller);
+
+ if (pdata->have_sysif_regs) {
+ /*
+ * Turn on cache snooping hardware, since some PowerPC platforms
+ * wholly rely on hardware to deal with cache coherent
+ */
+
+ /* Setup Snooping for all the 4GB space */
+ /* SNOOP1 starts from 0x0, size 2G */
+ out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB);
+ /* SNOOP2 starts from 0x80000000, size 2G */
+ out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
+ }
+
+ if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_DR_OTG))
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
+
+ if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
+ unsigned int chip, rev, svr;
+
+ svr = mfspr(SPRN_SVR);
+ chip = svr >> 16;
+ rev = (svr >> 4) & 0xf;
+
+ /* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */
+ if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055))
+ ehci->has_fsl_port_bug = 1;
+
+ if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
+
+ if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1))
+ return -EINVAL;
+ }
+
+ if (pdata->have_sysif_regs) {
+#ifdef CONFIG_FSL_SOC_BOOKE
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
+#else
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+#endif
+ out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+ }
+
+ return 0;
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_fsl_reinit(struct ehci_hcd *ehci)
+{
+ if (ehci_fsl_usb_setup(ehci))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_fsl_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+ struct fsl_usb2_platform_data *pdata;
+ struct device *dev;
+
+ dev = hcd->self.controller;
+ pdata = dev_get_platdata(hcd->self.controller);
+ ehci->big_endian_desc = pdata->big_endian_desc;
+ ehci->big_endian_mmio = pdata->big_endian_mmio;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+
+#ifdef CONFIG_PPC_83xx
+ /*
+ * Deal with MPC834X that need port power to be cycled after the power
+ * fault condition is removed. Otherwise the state machine does not
+ * reflect PORTSC[CSC] correctly.
+ */
+ ehci->need_oc_pp_cycle = 1;
+#endif
+
+ hcd->has_tt = 1;
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ if (of_device_is_compatible(dev->parent->of_node,
+ "fsl,mpc5121-usb2-dr")) {
+ /*
+ * set SBUSCFG:AHBBRST so that control msgs don't
+ * fail when doing heavy PATA writes.
+ */
+ ehci_writel(ehci, SBUSCFG_INCR8,
+ hcd->regs + FSL_SOC_USB_SBUSCFG);
+ }
+
+ retval = ehci_fsl_reinit(ehci);
+ return retval;
+}
+
+struct ehci_fsl {
+ struct ehci_hcd ehci;
+
+#ifdef CONFIG_PM
+ /* Saved USB PHY settings, need to restore after deep sleep. */
+ u32 usb_ctrl;
+#endif
+};
+
+#ifdef CONFIG_PM
+
+#ifdef CONFIG_PPC_MPC512x
+static int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev);
+ u32 tmp;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE);
+ mode &= USBMODE_CM_MASK;
+ tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */
+
+ dev_dbg(dev, "suspend=%d already_suspended=%d "
+ "mode=%d usbcmd %08x\n", pdata->suspended,
+ pdata->already_suspended, mode, tmp);
+#endif
+
+ /*
+ * If the controller is already suspended, then this must be a
+ * PM suspend. Remember this fact, so that we will leave the
+ * controller suspended at PM resume time.
+ */
+ if (pdata->suspended) {
+ dev_dbg(dev, "already suspended, leaving early\n");
+ pdata->already_suspended = 1;
+ return 0;
+ }
+
+ dev_dbg(dev, "suspending...\n");
+
+ ehci->rh_state = EHCI_RH_SUSPENDED;
+ dev->power.power_state = PMSG_SUSPEND;
+
+ /* ignore non-host interrupts */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ /* stop the controller */
+ tmp = ehci_readl(ehci, &ehci->regs->command);
+ tmp &= ~CMD_RUN;
+ ehci_writel(ehci, tmp, &ehci->regs->command);
+
+ /* save EHCI registers */
+ pdata->pm_command = ehci_readl(ehci, &ehci->regs->command);
+ pdata->pm_command &= ~CMD_RUN;
+ pdata->pm_status = ehci_readl(ehci, &ehci->regs->status);
+ pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable);
+ pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index);
+ pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment);
+ pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list);
+ pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next);
+ pdata->pm_configured_flag =
+ ehci_readl(ehci, &ehci->regs->configured_flag);
+ pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ pdata->pm_usbgenctrl = ehci_readl(ehci,
+ hcd->regs + FSL_SOC_USB_USBGENCTRL);
+
+ /* clear the W1C bits */
+ pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS);
+
+ pdata->suspended = 1;
+
+ /* clear PP to cut power to the port */
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ tmp &= ~PORT_POWER;
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+
+ return 0;
+}
+
+static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev);
+ u32 tmp;
+
+ dev_dbg(dev, "suspend=%d already_suspended=%d\n",
+ pdata->suspended, pdata->already_suspended);
+
+ /*
+ * If the controller was already suspended at suspend time,
+ * then don't resume it now.
+ */
+ if (pdata->already_suspended) {
+ dev_dbg(dev, "already suspended, leaving early\n");
+ pdata->already_suspended = 0;
+ return 0;
+ }
+
+ if (!pdata->suspended) {
+ dev_dbg(dev, "not suspended, leaving early\n");
+ return 0;
+ }
+
+ pdata->suspended = 0;
+
+ dev_dbg(dev, "resuming...\n");
+
+ /* set host mode */
+ tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0);
+ ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE);
+
+ ehci_writel(ehci, pdata->pm_usbgenctrl,
+ hcd->regs + FSL_SOC_USB_USBGENCTRL);
+ ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE,
+ hcd->regs + FSL_SOC_USB_ISIPHYCTRL);
+
+ ehci_writel(ehci, SBUSCFG_INCR8, hcd->regs + FSL_SOC_USB_SBUSCFG);
+
+ /* restore EHCI registers */
+ ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
+ ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
+ ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index);
+ ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment);
+ ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list);
+ ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next);
+ ehci_writel(ehci, pdata->pm_configured_flag,
+ &ehci->regs->configured_flag);
+ ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
+
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ ehci->rh_state = EHCI_RH_RUNNING;
+ dev->power.power_state = PMSG_ON;
+
+ tmp = ehci_readl(ehci, &ehci->regs->command);
+ tmp |= CMD_RUN;
+ ehci_writel(ehci, tmp, &ehci->regs->command);
+
+ usb_hcd_resume_root_hub(hcd);
+
+ return 0;
+}
+#else
+static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_PPC_MPC512x */
+
+static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ return container_of(ehci, struct ehci_fsl, ehci);
+}
+
+static int ehci_fsl_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+ void __iomem *non_ehci = hcd->regs;
+
+ if (of_device_is_compatible(dev->parent->of_node,
+ "fsl,mpc5121-usb2-dr")) {
+ return ehci_fsl_mpc512x_drv_suspend(dev);
+ }
+
+ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
+ device_may_wakeup(dev));
+ if (!fsl_deep_sleep())
+ return 0;
+
+ ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+ return 0;
+}
+
+static int ehci_fsl_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ void __iomem *non_ehci = hcd->regs;
+
+ if (of_device_is_compatible(dev->parent->of_node,
+ "fsl,mpc5121-usb2-dr")) {
+ return ehci_fsl_mpc512x_drv_resume(dev);
+ }
+
+ ehci_prepare_ports_for_controller_resume(ehci);
+ if (!fsl_deep_sleep())
+ return 0;
+
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
+ /* Restore USB PHY settings and enable the controller. */
+ out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl);
+
+ ehci_reset(ehci);
+ ehci_fsl_reinit(ehci);
+
+ return 0;
+}
+
+static int ehci_fsl_drv_restore(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_root_hub_lost_power(hcd->self.root_hub);
+ return 0;
+}
+
+static struct dev_pm_ops ehci_fsl_pm_ops = {
+ .suspend = ehci_fsl_drv_suspend,
+ .resume = ehci_fsl_drv_resume,
+ .restore = ehci_fsl_drv_restore,
+};
+
+#define EHCI_FSL_PM_OPS (&ehci_fsl_pm_ops)
+#else
+#define EHCI_FSL_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_USB_OTG
+static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 status;
+
+ if (!port)
+ return -EINVAL;
+
+ port--;
+
+ /* start port reset before HNP protocol time out */
+ status = readl(&ehci->regs->port_status[port]);
+ if (!(status & PORT_CONNECT))
+ return -ENODEV;
+
+ /* hub_wq will finish the reset later */
+ if (ehci_is_TDI(ehci)) {
+ writel(PORT_RESET |
+ (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)),
+ &ehci->regs->port_status[port]);
+ } else {
+ writel(PORT_RESET, &ehci->regs->port_status[port]);
+ }
+
+ return 0;
+}
+#else
+#define ehci_start_port_reset NULL
+#endif /* CONFIG_USB_OTG */
+
+
+static const struct hc_driver ehci_fsl_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_fsl),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_fsl_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .start_port_reset = ehci_start_port_reset,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_fsl_drv_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* FIXME we only want one one probe() not two */
+ return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
+}
+
+static int ehci_fsl_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ /* FIXME we only want one one remove() not two */
+ usb_hcd_fsl_remove(hcd, pdev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:fsl-ehci");
+
+static struct platform_driver ehci_fsl_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "fsl-ehci",
+ .pm = EHCI_FSL_PM_OPS,
+ },
+};
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
new file mode 100644
index 000000000..dbd292e9f
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2005-2010,2012 Freescale Semiconductor, Inc.
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _EHCI_FSL_H
+#define _EHCI_FSL_H
+
+/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_SBUSCFG 0x90
+#define SBUSCFG_INCR8 0x02 /* INCR8, specified */
+#define FSL_SOC_USB_ULPIVP 0x170
+#define FSL_SOC_USB_PORTSC1 0x184
+#define PORT_PTS_MSK (3<<30)
+#define PORT_PTS_UTMI (0<<30)
+#define PORT_PTS_ULPI (2<<30)
+#define PORT_PTS_SERIAL (3<<30)
+#define PORT_PTS_PTW (1<<28)
+#define FSL_SOC_USB_PORTSC2 0x188
+#define FSL_SOC_USB_USBMODE 0x1a8
+#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */
+#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */
+#define USBMODE_ES (1 << 2) /* (Big) Endian Select */
+
+#define FSL_SOC_USB_USBGENCTRL 0x200
+#define USBGENCTRL_PPP (1 << 3)
+#define USBGENCTRL_PFP (1 << 2)
+#define FSL_SOC_USB_ISIPHYCTRL 0x204
+#define ISIPHYCTRL_PXE (1)
+#define ISIPHYCTRL_PHYE (1 << 4)
+
+#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
+#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
+#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
+#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
+#define CTRL_UTMI_PHY_EN (1<<9)
+#define CTRL_PHY_CLK_VALID (1 << 17)
+#define SNOOP_SIZE_2GB 0x1e
+
+/* control Register Bit Masks */
+#define ULPI_INT_EN (1<<0)
+#define WU_INT_EN (1<<1)
+#define USB_CTRL_USB_EN (1<<2)
+#define LINE_STATE_FILTER__EN (1<<3)
+#define KEEP_OTG_ON (1<<4)
+#define OTG_PORT (1<<5)
+#define PLL_RESET (1<<8)
+#define UTMI_PHY_EN (1<<9)
+#define ULPI_PHY_CLK_SEL (1<<10)
+#define PHY_CLK_VALID (1<<17)
+#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
new file mode 100644
index 000000000..21650044b
--- /dev/null
+++ b/drivers/usb/host/ehci-grlib.c
@@ -0,0 +1,192 @@
+/*
+ * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller
+ *
+ * GRUSBHC is typically found on LEON/GRLIB SoCs
+ *
+ * (c) Jan Andersson <jan@gaisler.com>
+ *
+ * Based on ehci-ppc-of.c which is:
+ * (c) Valentine Barshak <vbarshak@ru.mvista.com>
+ * and in turn based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
+ * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/signal.h>
+
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */
+
+static const struct hc_driver ehci_grlib_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "GRLIB GRUSBHC EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+
+static int ehci_hcd_grlib_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci = NULL;
+ struct resource res;
+ u32 hc_capbase;
+ int irq;
+ int rv;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ /* usb_create_hcd requires dma_mask != NULL */
+ op->dev.dma_mask = &op->dev.coherent_dma_mask;
+ hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev,
+ "GRUSBHC EHCI USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = resource_size(&res);
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
+ __FILE__);
+ rv = -EBUSY;
+ goto err_irq;
+ }
+
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_ioremap;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+
+ ehci->caps = hcd->regs;
+
+ /* determine endianness of this implementation */
+ hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase);
+ if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) {
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+ ehci->big_endian_capbase = 1;
+ }
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv)
+ goto err_ioremap;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_ioremap:
+ irq_dispose_mapping(irq);
+err_irq:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+
+static int ehci_hcd_grlib_remove(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ irq_dispose_mapping(hcd->irq);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+
+static const struct of_device_id ehci_hcd_grlib_of_match[] = {
+ {
+ .name = "GAISLER_EHCI",
+ },
+ {
+ .name = "01_026",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match);
+
+
+static struct platform_driver ehci_grlib_driver = {
+ .probe = ehci_hcd_grlib_probe,
+ .remove = ehci_hcd_grlib_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "grlib-ehci",
+ .of_match_table = ehci_hcd_grlib_of_match,
+ },
+};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
new file mode 100644
index 000000000..f4d88dfb2
--- /dev/null
+++ b/drivers/usb/host/ehci-hcd.c
@@ -0,0 +1,1400 @@
+/*
+ * Enhanced Host Controller Interface (EHCI) driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * Copyright (c) 2000-2004 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#if defined(CONFIG_PPC_PS3)
+#include <asm/firmware.h>
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hc_driver implementation ... experimental, incomplete.
+ * Based on the final 1.0 register interface specification.
+ *
+ * USB 2.0 shows up in upcoming www.pcmcia.org technology.
+ * First was PCMCIA, like ISA; then CardBus, which is PCI.
+ * Next comes "CardBay", using USB 2.0 signals.
+ *
+ * Contains additional contributions by Brad Hards, Rory Bolt, and others.
+ * Special thanks to Intel and VIA for providing host controllers to
+ * test this driver on, and Cypress (including In-System Design) for
+ * providing early devices for those host controllers to talk to!
+ */
+
+#define DRIVER_AUTHOR "David Brownell"
+#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
+
+static const char hcd_name [] = "ehci_hcd";
+
+
+#undef EHCI_URB_TRACE
+
+/* magic numbers that can affect system performance */
+#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
+#define EHCI_TUNE_RL_TT 0
+#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define EHCI_TUNE_MULT_TT 1
+/*
+ * Some drivers think it's safe to schedule isochronous transfers more than
+ * 256 ms into the future (partly as a result of an old bug in the scheduling
+ * code). In an attempt to avoid trouble, we will use a minimum scheduling
+ * length of 512 frames instead of 256.
+ */
+#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
+
+/* Initial IRQ latency: faster than hw default */
+static int log2_irq_thresh = 0; // 0 to 6
+module_param (log2_irq_thresh, int, S_IRUGO);
+MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* initial park setting: slower than hw default */
+static unsigned park = 0;
+module_param (park, uint, S_IRUGO);
+MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
+
+/* for flakey hardware, ignore overcurrent indicators */
+static bool ignore_oc = 0;
+module_param (ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci.h"
+#include "pci-quirks.h"
+
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+ struct ehci_tt *tt);
+
+/*
+ * The MosChip MCS9990 controller updates its microframe counter
+ * a little before the frame counter, and occasionally we will read
+ * the invalid intermediate value. Avoid problems by checking the
+ * microframe number (the low-order 3 bits); if they are 0 then
+ * re-read the register to get the correct value.
+ */
+static unsigned ehci_moschip_read_frame_index(struct ehci_hcd *ehci)
+{
+ unsigned uf;
+
+ uf = ehci_readl(ehci, &ehci->regs->frame_index);
+ if (unlikely((uf & 7) == 0))
+ uf = ehci_readl(ehci, &ehci->regs->frame_index);
+ return uf;
+}
+
+static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci)
+{
+ if (ehci->frame_index_bug)
+ return ehci_moschip_read_frame_index(ehci);
+ return ehci_readl(ehci, &ehci->regs->frame_index);
+}
+
+#include "ehci-dbg.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * ehci_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).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown: shutting down the bridge before the devices using it.
+ */
+int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
+ u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = ehci_readl(ehci, ptr);
+ if (result == ~(u32)0) /* card removed */
+ return -ENODEV;
+ result &= mask;
+ if (result == done)
+ return 0;
+ udelay (1);
+ usec--;
+ } while (usec > 0);
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(ehci_handshake);
+
+/* check TDI/ARC silicon is in host mode */
+static int tdi_in_host_mode (struct ehci_hcd *ehci)
+{
+ u32 tmp;
+
+ tmp = ehci_readl(ehci, &ehci->regs->usbmode);
+ return (tmp & 3) == USBMODE_CM_HC;
+}
+
+/*
+ * Force HC to halt state from unknown (EHCI spec section 2.3).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int ehci_halt (struct ehci_hcd *ehci)
+{
+ u32 temp;
+
+ spin_lock_irq(&ehci->lock);
+
+ /* disable any irqs left enabled by previous code */
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+
+ if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) {
+ spin_unlock_irq(&ehci->lock);
+ return 0;
+ }
+
+ /*
+ * This routine gets called during probe before ehci->command
+ * has been initialized, so we can't rely on its value.
+ */
+ ehci->command &= ~CMD_RUN;
+ temp = ehci_readl(ehci, &ehci->regs->command);
+ temp &= ~(CMD_RUN | CMD_IAAD);
+ ehci_writel(ehci, temp, &ehci->regs->command);
+
+ spin_unlock_irq(&ehci->lock);
+ synchronize_irq(ehci_to_hcd(ehci)->irq);
+
+ return ehci_handshake(ehci, &ehci->regs->status,
+ STS_HALT, STS_HALT, 16 * 125);
+}
+
+/* put TDI/ARC silicon into EHCI mode */
+static void tdi_reset (struct ehci_hcd *ehci)
+{
+ u32 tmp;
+
+ tmp = ehci_readl(ehci, &ehci->regs->usbmode);
+ tmp |= USBMODE_CM_HC;
+ /* The default byte access to MMR space is LE after
+ * controller reset. Set the required endian mode
+ * for transfer buffers to match the host microprocessor
+ */
+ if (ehci_big_endian_mmio(ehci))
+ tmp |= USBMODE_BE;
+ ehci_writel(ehci, tmp, &ehci->regs->usbmode);
+}
+
+/*
+ * Reset a non-running (STS_HALT == 1) controller.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int ehci_reset (struct ehci_hcd *ehci)
+{
+ int retval;
+ u32 command = ehci_readl(ehci, &ehci->regs->command);
+
+ /* If the EHCI debug controller is active, special care must be
+ * taken before and after a host controller reset */
+ if (ehci->debug && !dbgp_reset_prep(ehci_to_hcd(ehci)))
+ ehci->debug = NULL;
+
+ command |= CMD_RESET;
+ dbg_cmd (ehci, "reset", command);
+ ehci_writel(ehci, command, &ehci->regs->command);
+ ehci->rh_state = EHCI_RH_HALTED;
+ ehci->next_statechange = jiffies;
+ retval = ehci_handshake(ehci, &ehci->regs->command,
+ CMD_RESET, 0, 250 * 1000);
+
+ if (ehci->has_hostpc) {
+ ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
+ &ehci->regs->usbmode_ex);
+ ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning);
+ }
+ if (retval)
+ return retval;
+
+ if (ehci_is_TDI(ehci))
+ tdi_reset (ehci);
+
+ if (ehci->debug)
+ dbgp_external_startup(ehci_to_hcd(ehci));
+
+ ehci->port_c_suspend = ehci->suspended_ports =
+ ehci->resuming_ports = 0;
+ return retval;
+}
+
+/*
+ * Idle the controller (turn off the schedules).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void ehci_quiesce (struct ehci_hcd *ehci)
+{
+ u32 temp;
+
+ if (ehci->rh_state != EHCI_RH_RUNNING)
+ return;
+
+ /* wait for any schedule enables/disables to take effect */
+ temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
+ ehci_handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp,
+ 16 * 125);
+
+ /* then disable anything that's still active */
+ spin_lock_irq(&ehci->lock);
+ ehci->command &= ~(CMD_ASE | CMD_PSE);
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ spin_unlock_irq(&ehci->lock);
+
+ /* hardware can take 16 microframes to turn off ... */
+ ehci_handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0,
+ 16 * 125);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void end_unlink_async(struct ehci_hcd *ehci);
+static void unlink_empty_async(struct ehci_hcd *ehci);
+static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
+static void ehci_work(struct ehci_hcd *ehci);
+static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
+static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
+static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable);
+
+#include "ehci-timer.c"
+#include "ehci-hub.c"
+#include "ehci-mem.c"
+#include "ehci-q.c"
+#include "ehci-sched.c"
+#include "ehci-sysfs.c"
+
+/*-------------------------------------------------------------------------*/
+
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
+{
+ int port = HCS_N_PORTS(ehci->hcs_params);
+
+ while (port--) {
+ ehci_writel(ehci, PORT_RWC_BITS,
+ &ehci->regs->port_status[port]);
+ spin_unlock_irq(&ehci->lock);
+ ehci_port_power(ehci, port, false);
+ spin_lock_irq(&ehci->lock);
+ }
+}
+
+/*
+ * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void ehci_silence_controller(struct ehci_hcd *ehci)
+{
+ ehci_halt(ehci);
+
+ spin_lock_irq(&ehci->lock);
+ ehci->rh_state = EHCI_RH_HALTED;
+ ehci_turn_off_all_ports(ehci);
+
+ /* make BIOS/etc use companion controller during reboot */
+ ehci_writel(ehci, 0, &ehci->regs->configured_flag);
+
+ /* unblock posted writes */
+ ehci_readl(ehci, &ehci->regs->configured_flag);
+ spin_unlock_irq(&ehci->lock);
+}
+
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
+ * This forcibly disables dma and IRQs, helping kexec and other cases
+ * where the next system software may expect clean state.
+ */
+static void ehci_shutdown(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ spin_lock_irq(&ehci->lock);
+ ehci->shutdown = true;
+ ehci->rh_state = EHCI_RH_STOPPING;
+ ehci->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&ehci->lock);
+
+ ehci_silence_controller(ehci);
+
+ hrtimer_cancel(&ehci->hrtimer);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * ehci_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping ehci->lock.
+ */
+static void ehci_work (struct ehci_hcd *ehci)
+{
+ /* another CPU may drop ehci->lock during a schedule scan while
+ * it reports urb completions. this flag guards against bogus
+ * attempts at re-entrant schedule scanning.
+ */
+ if (ehci->scanning) {
+ ehci->need_rescan = true;
+ return;
+ }
+ ehci->scanning = true;
+
+ rescan:
+ ehci->need_rescan = false;
+ if (ehci->async_count)
+ scan_async(ehci);
+ if (ehci->intr_count > 0)
+ scan_intr(ehci);
+ if (ehci->isoc_count > 0)
+ scan_isoc(ehci);
+ if (ehci->need_rescan)
+ goto rescan;
+ ehci->scanning = false;
+
+ /* the IO watchdog guards against hardware or driver bugs that
+ * misplace IRQs, and should let us run completely without IRQs.
+ * such lossage has been observed on both VT6202 and VT8235.
+ */
+ turn_on_io_watchdog(ehci);
+}
+
+/*
+ * Called when the ehci_hcd module is removed.
+ */
+static void ehci_stop (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+
+ ehci_dbg (ehci, "stop\n");
+
+ /* no more interrupts ... */
+
+ spin_lock_irq(&ehci->lock);
+ ehci->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&ehci->lock);
+
+ ehci_quiesce(ehci);
+ ehci_silence_controller(ehci);
+ ehci_reset (ehci);
+
+ hrtimer_cancel(&ehci->hrtimer);
+ remove_sysfs_files(ehci);
+ remove_debug_files (ehci);
+
+ /* root hub is shut down separately (first, when possible) */
+ spin_lock_irq (&ehci->lock);
+ end_free_itds(ehci);
+ spin_unlock_irq (&ehci->lock);
+ ehci_mem_cleanup (ehci);
+
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_dev_put();
+
+ dbg_status (ehci, "ehci_stop completed",
+ ehci_readl(ehci, &ehci->regs->status));
+}
+
+/* one-time init, only for memory state */
+static int ehci_init(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 temp;
+ int retval;
+ u32 hcc_params;
+ struct ehci_qh_hw *hw;
+
+ spin_lock_init(&ehci->lock);
+
+ /*
+ * keep io watchdog by default, those good HCDs could turn off it later
+ */
+ ehci->need_io_watchdog = 1;
+
+ hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ ehci->hrtimer.function = ehci_hrtimer_func;
+ ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
+
+ hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
+
+ /*
+ * by default set standard 80% (== 100 usec/uframe) max periodic
+ * bandwidth as required by USB 2.0
+ */
+ ehci->uframe_periodic_max = 100;
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ ehci->periodic_size = DEFAULT_I_TDPS;
+ INIT_LIST_HEAD(&ehci->async_unlink);
+ INIT_LIST_HEAD(&ehci->async_idle);
+ INIT_LIST_HEAD(&ehci->intr_unlink_wait);
+ INIT_LIST_HEAD(&ehci->intr_unlink);
+ INIT_LIST_HEAD(&ehci->intr_qh_list);
+ INIT_LIST_HEAD(&ehci->cached_itd_list);
+ INIT_LIST_HEAD(&ehci->cached_sitd_list);
+ INIT_LIST_HEAD(&ehci->tt_list);
+
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ switch (EHCI_TUNE_FLS) {
+ case 0: ehci->periodic_size = 1024; break;
+ case 1: ehci->periodic_size = 512; break;
+ case 2: ehci->periodic_size = 256; break;
+ default: BUG();
+ }
+ }
+ if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
+ return retval;
+
+ /* controllers may cache some of the periodic schedule ... */
+ if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
+ ehci->i_thresh = 0;
+ else // N microframes cached
+ ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
+
+ /*
+ * dedicate a qh for the async ring head, since we couldn't unlink
+ * a 'real' qh without stopping the async schedule [4.8]. use it
+ * as the 'reclamation list head' too.
+ * its dummy is used in hw_alt_next of many tds, to prevent the qh
+ * from automatically advancing to the next td after short reads.
+ */
+ ehci->async->qh_next.qh = NULL;
+ hw = ehci->async->hw;
+ hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
+ hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
+#if defined(CONFIG_PPC_PS3)
+ hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE);
+#endif
+ hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+ hw->hw_qtd_next = EHCI_LIST_END(ehci);
+ ehci->async->qh_state = QH_STATE_LINKED;
+ hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
+
+ /* clear interrupt enables, set irq latency */
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) {
+ ehci->has_ppcd = 1;
+ ehci_dbg(ehci, "enable per-port change event\n");
+ temp |= CMD_PPCEE;
+ }
+ if (HCC_CANPARK(hcc_params)) {
+ /* HW default park == 3, on hardware that supports it (like
+ * NVidia and ALI silicon), maximizes throughput on the async
+ * schedule by avoiding QH fetches between transfers.
+ *
+ * With fast usb storage devices and NForce2, "park" seems to
+ * make problems: throughput reduction (!), data errors...
+ */
+ if (park) {
+ park = min(park, (unsigned) 3);
+ temp |= CMD_PARK;
+ temp |= park << 8;
+ }
+ ehci_dbg(ehci, "park %d\n", park);
+ }
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (EHCI_TUNE_FLS << 2);
+ }
+ ehci->command = temp;
+
+ /* Accept arbitrarily long scatter-gather lists */
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
+ return 0;
+}
+
+/* start HC running; it's halted, ehci_init() has been run (once) */
+static int ehci_run (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ u32 hcc_params;
+
+ hcd->uses_new_polling = 1;
+
+ /* EHCI spec section 4.1 */
+
+ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+ ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+ /*
+ * hcc_params controls whether ehci->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * pci_pool consistent memory always uses segment zero.
+ * streaming mappings for I/O buffers, like pci_map_single(),
+ * can return segments above 4GB, if the device allows.
+ *
+ * NOTE: the dma mask is visible through dma_supported(), so
+ * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+ * Scsi_Host.highmem_io, and so forth. It's readonly to all
+ * host side drivers though.
+ */
+ hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
+ if (HCC_64BIT_ADDR(hcc_params)) {
+ ehci_writel(ehci, 0, &ehci->regs->segment);
+#if 0
+// this is deeply broken on almost all architectures
+ if (!dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)))
+ ehci_info(ehci, "enabled 64bit DMA\n");
+#endif
+ }
+
+
+ // Philips, Intel, and maybe others need CMD_RUN before the
+ // root hub will detect new devices (why?); NEC doesn't
+ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+ ehci->command |= CMD_RUN;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ dbg_cmd (ehci, "init", ehci->command);
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub. (Except where one is integrated,
+ * and there's no companion controller unless maybe for USB OTG.)
+ *
+ * Turning on the CF flag will transfer ownership of all ports
+ * from the companions to the EHCI controller. If any of the
+ * companions are in the middle of a port reset at the time, it
+ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem
+ * guarantees that no resets are in progress. After we set CF,
+ * a short delay lets the hardware catch up; new resets shouldn't
+ * be started before the port switching actions could complete.
+ */
+ down_write(&ehci_cf_port_reset_rwsem);
+ ehci->rh_state = EHCI_RH_RUNNING;
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+ msleep(5);
+ up_write(&ehci_cf_port_reset_rwsem);
+ ehci->last_periodic_enable = ktime_get_real();
+
+ temp = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+ ehci_info (ehci,
+ "USB %x.%x started, EHCI %x.%02x%s\n",
+ ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
+ temp >> 8, temp & 0xff,
+ ignore_oc ? ", overcurrent ignored" : "");
+
+ ehci_writel(ehci, INTR_MASK,
+ &ehci->regs->intr_enable); /* Turn On Interrupts */
+
+ /* GRR this is run-once init(), being done every time the HC starts.
+ * So long as they're part of class devices, we can't do it init()
+ * since the class device isn't created that early.
+ */
+ create_debug_files(ehci);
+ create_sysfs_files(ehci);
+
+ return 0;
+}
+
+int ehci_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci->regs = (void __iomem *)ehci->caps +
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ ehci->sbrn = HCD_USB2;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ ehci_reset(ehci);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ehci_setup);
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t ehci_irq (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 status, masked_status, pcd_status = 0, cmd;
+ int bh;
+ unsigned long flags;
+
+ /*
+ * For threadirqs option we use spin_lock_irqsave() variant to prevent
+ * deadlock with ehci hrtimer callback, because hrtimer callbacks run
+ * in interrupt context even when threadirqs is specified. We can go
+ * back to spin_lock() variant when hrtimer callbacks become threaded.
+ */
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ status = ehci_readl(ehci, &ehci->regs->status);
+
+ /* e.g. cardbus physical eject */
+ if (status == ~(u32) 0) {
+ ehci_dbg (ehci, "device removed\n");
+ goto dead;
+ }
+
+ /*
+ * We don't use STS_FLR, but some controllers don't like it to
+ * remain on, so mask it out along with the other status bits.
+ */
+ masked_status = status & (INTR_MASK | STS_FLR);
+
+ /* Shared IRQ? */
+ if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* clear (just) interrupts */
+ ehci_writel(ehci, masked_status, &ehci->regs->status);
+ cmd = ehci_readl(ehci, &ehci->regs->command);
+ bh = 0;
+
+ /* normal [4.15.1.2] or error [4.15.1.1] completion */
+ if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
+ if (likely ((status & STS_ERR) == 0))
+ COUNT (ehci->stats.normal);
+ else
+ COUNT (ehci->stats.error);
+ bh = 1;
+ }
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+
+ /* Turn off the IAA watchdog */
+ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG);
+
+ /*
+ * Mild optimization: Allow another IAAD to reset the
+ * hrtimer, if one occurs before the next expiration.
+ * In theory we could always cancel the hrtimer, but
+ * tests show that about half the time it will be reset
+ * for some other event anyway.
+ */
+ if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG)
+ ++ehci->next_hrtimer_event;
+
+ /* guard against (alleged) silicon errata */
+ if (cmd & CMD_IAAD)
+ ehci_dbg(ehci, "IAA with IAAD still set?\n");
+ if (ehci->iaa_in_progress)
+ COUNT(ehci->stats.iaa);
+ end_unlink_async(ehci);
+ }
+
+ /* remote wakeup [4.3.1] */
+ if (status & STS_PCD) {
+ unsigned i = HCS_N_PORTS (ehci->hcs_params);
+ u32 ppcd = ~0;
+
+ /* kick root hub later */
+ pcd_status = status;
+
+ /* resume root hub? */
+ if (ehci->rh_state == EHCI_RH_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = status >> 16;
+
+ while (i--) {
+ int pstatus;
+
+ /* leverage per-port change bits feature */
+ if (!(ppcd & (1 << i)))
+ continue;
+ pstatus = ehci_readl(ehci,
+ &ehci->regs->port_status[i]);
+
+ if (pstatus & PORT_OWNER)
+ continue;
+ if (!(test_bit(i, &ehci->suspended_ports) &&
+ ((pstatus & PORT_RESUME) ||
+ !(pstatus & PORT_SUSPEND)) &&
+ (pstatus & PORT_PE) &&
+ ehci->reset_done[i] == 0))
+ continue;
+
+ /* start USB_RESUME_TIMEOUT msec resume signaling from
+ * this port, and make hub_wq collect
+ * PORT_STAT_C_SUSPEND to stop that signaling.
+ */
+ ehci->reset_done[i] = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ set_bit(i, &ehci->resuming_ports);
+ ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+ usb_hcd_start_port_resume(&hcd->self, i);
+ mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
+ }
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely ((status & STS_FATAL) != 0)) {
+ ehci_err(ehci, "fatal error\n");
+ dbg_cmd(ehci, "fatal", cmd);
+ dbg_status(ehci, "fatal", status);
+dead:
+ usb_hc_died(hcd);
+
+ /* Don't let the controller do anything more */
+ ehci->shutdown = true;
+ ehci->rh_state = EHCI_RH_STOPPING;
+ ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ ehci_handle_controller_death(ehci);
+
+ /* Handle completions when the controller stops */
+ bh = 0;
+ }
+
+ if (bh)
+ ehci_work (ehci);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ if (pcd_status)
+ usb_hcd_poll_rh_status(hcd);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd.self.controller.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int ehci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct list_head qtd_list;
+
+ INIT_LIST_HEAD (&qtd_list);
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_CONTROL:
+ /* qh_completions() code doesn't handle all the fault cases
+ * in multi-TD control transfers. Even 1KB is rare anyway.
+ */
+ if (urb->transfer_buffer_length > (16 * 1024))
+ return -EMSGSIZE;
+ /* FALLTHROUGH */
+ /* case PIPE_BULK: */
+ default:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return submit_async(ehci, urb, &qtd_list, mem_flags);
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit(ehci, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+ if (urb->dev->speed == USB_SPEED_HIGH)
+ return itd_submit (ehci, urb, mem_flags);
+ else
+ return sitd_submit (ehci, urb, mem_flags);
+ }
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct ehci_qh *qh;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ /*
+ * We don't expedite dequeue for isochronous URBs.
+ * Just wait until they complete normally or their
+ * time slot expires.
+ */
+ } else {
+ qh = (struct ehci_qh *) urb->hcpriv;
+ qh->exception = 1;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)
+ start_unlink_intr(ehci, qh);
+ else
+ start_unlink_async(ehci, qh);
+ break;
+ case QH_STATE_COMPLETING:
+ qh->dequeue_during_giveback = 1;
+ break;
+ case QH_STATE_UNLINK:
+ case QH_STATE_UNLINK_WAIT:
+ /* already started */
+ break;
+ case QH_STATE_IDLE:
+ /* QH might be waiting for a Clear-TT-Buffer */
+ qh_completions(ehci, qh);
+ break;
+ }
+ }
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void
+ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ unsigned long flags;
+ struct ehci_qh *qh;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+rescan:
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh)
+ goto done;
+
+ /* endpoints can be iso streams. for now, we don't
+ * accelerate iso completions ... so spin a while.
+ */
+ if (qh->hw == NULL) {
+ struct ehci_iso_stream *stream = ep->hcpriv;
+
+ if (!list_empty(&stream->td_list))
+ goto idle_timeout;
+
+ /* BUG_ON(!list_empty(&stream->free_list)); */
+ reserve_release_iso_bandwidth(ehci, stream, -1);
+ kfree(stream);
+ goto done;
+ }
+
+ qh->exception = 1;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ WARN_ON(!list_empty(&qh->qtd_list));
+ if (usb_endpoint_type(&ep->desc) != USB_ENDPOINT_XFER_INT)
+ start_unlink_async(ehci, qh);
+ else
+ start_unlink_intr(ehci, qh);
+ /* FALL THROUGH */
+ case QH_STATE_COMPLETING: /* already in unlinking */
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+ case QH_STATE_UNLINK_WAIT:
+idle_timeout:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ case QH_STATE_IDLE: /* fully unlinked */
+ if (qh->clearing_tt)
+ goto idle_timeout;
+ if (list_empty (&qh->qtd_list)) {
+ if (qh->ps.bw_uperiod)
+ reserve_release_intr_bandwidth(ehci, qh, -1);
+ qh_destroy(ehci, qh);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ ehci_err (ehci, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty (&qh->qtd_list) ? "" : "(has tds)");
+ break;
+ }
+ done:
+ ep->hcpriv = NULL;
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+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);
+ int epnum = usb_endpoint_num(&ep->desc);
+ int is_out = usb_endpoint_dir_out(&ep->desc);
+ unsigned long flags;
+
+ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
+ return;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ 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 {
+ /* The toggle value in the QH can't be updated
+ * while the QH is active. Unlink it now;
+ * re-linking will call qh_refresh().
+ */
+ usb_settoggle(qh->ps.udev, epnum, is_out, 0);
+ qh->exception = 1;
+ if (eptype == USB_ENDPOINT_XFER_BULK)
+ start_unlink_async(ehci, qh);
+ else
+ start_unlink_intr(ehci, qh);
+ }
+ }
+ spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static int ehci_get_frame (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Device addition and removal */
+
+static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ spin_lock_irq(&ehci->lock);
+ drop_tt(udev);
+ spin_unlock_irq(&ehci->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+/* These routines handle the generic parts of controller suspend/resume */
+
+int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ if (time_before(jiffies, ehci->next_statechange))
+ msleep(10);
+
+ /*
+ * Root hub was already suspended. Disable IRQ emission and
+ * mark HW unaccessible. The PM and USB cores make sure that
+ * the root hub is either suspended or stopped.
+ */
+ ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
+
+ spin_lock_irq(&ehci->lock);
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ (void) ehci_readl(ehci, &ehci->regs->intr_enable);
+
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ spin_unlock_irq(&ehci->lock);
+
+ synchronize_irq(hcd->irq);
+
+ /* Check for race with a wakeup request */
+ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ ehci_resume(hcd, false);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ehci_suspend);
+
+/* Returns 0 if power was preserved, 1 if power was lost */
+int ehci_resume(struct usb_hcd *hcd, bool force_reset)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ if (time_before(jiffies, ehci->next_statechange))
+ msleep(100);
+
+ /* Mark hardware accessible again as we are back to full power by now */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ if (ehci->shutdown)
+ return 0; /* Controller is dead */
+
+ /*
+ * If CF is still set and reset isn't forced
+ * then we maintained suspend power.
+ * Just undo the effect of ehci_suspend().
+ */
+ if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
+ !force_reset) {
+ int mask = INTR_MASK;
+
+ ehci_prepare_ports_for_controller_resume(ehci);
+
+ spin_lock_irq(&ehci->lock);
+ if (ehci->shutdown)
+ goto skip;
+
+ if (!hcd->self.root_hub->do_remote_wakeup)
+ mask &= ~STS_PCD;
+ ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+ ehci_readl(ehci, &ehci->regs->intr_enable);
+ skip:
+ spin_unlock_irq(&ehci->lock);
+ return 0;
+ }
+
+ /*
+ * Else reset, to cope with power loss or resume from hibernation
+ * having let the firmware kick in during reboot.
+ */
+ usb_root_hub_lost_power(hcd->self.root_hub);
+ (void) ehci_halt(ehci);
+ (void) ehci_reset(ehci);
+
+ spin_lock_irq(&ehci->lock);
+ if (ehci->shutdown)
+ goto skip;
+
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+ ehci->rh_state = EHCI_RH_SUSPENDED;
+ spin_unlock_irq(&ehci->lock);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(ehci_resume);
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic structure: This gets copied for platform drivers so that
+ * individual entries can be overridden as needed.
+ */
+
+static const struct hc_driver ehci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ /*
+ * device support
+ */
+ .free_dev = ehci_remove_device,
+};
+
+void ehci_init_driver(struct hc_driver *drv,
+ const struct ehci_driver_overrides *over)
+{
+ /* Copy the generic table to drv and then apply the overrides */
+ *drv = ehci_hc_driver;
+
+ if (over) {
+ drv->hcd_priv_size += over->extra_priv_size;
+ if (over->reset)
+ drv->reset = over->reset;
+ if (over->port_power)
+ drv->port_power = over->port_power;
+ }
+}
+EXPORT_SYMBOL_GPL(ehci_init_driver);
+
+/*-------------------------------------------------------------------------*/
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_LICENSE ("GPL");
+
+#ifdef CONFIG_USB_EHCI_FSL
+#include "ehci-fsl.c"
+#define PLATFORM_DRIVER ehci_fsl_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_SH
+#include "ehci-sh.c"
+#define PLATFORM_DRIVER ehci_hcd_sh_driver
+#endif
+
+#ifdef CONFIG_PPC_PS3
+#include "ehci-ps3.c"
+#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PPC_OF
+#include "ehci-ppc-of.c"
+#define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver
+#endif
+
+#ifdef CONFIG_XPS_USB_HCD_XILINX
+#include "ehci-xilinx-of.c"
+#define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
+#endif
+
+#ifdef CONFIG_TILE_USB
+#include "ehci-tilegx.c"
+#define PLATFORM_DRIVER ehci_hcd_tilegx_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
+#include "ehci-pmcmsp.c"
+#define PLATFORM_DRIVER ehci_hcd_msp_driver
+#endif
+
+#ifdef CONFIG_SPARC_LEON
+#include "ehci-grlib.c"
+#define PLATFORM_DRIVER ehci_grlib_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_MV
+#include "ehci-mv.c"
+#define PLATFORM_DRIVER ehci_mv_driver
+#endif
+
+#ifdef CONFIG_MIPS_SEAD3
+#include "ehci-sead3.c"
+#define PLATFORM_DRIVER ehci_hcd_sead3_driver
+#endif
+
+static int __init ehci_hcd_init(void)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
+ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
+ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
+ printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
+ " before uhci_hcd and ohci_hcd, not after\n");
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ hcd_name,
+ sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
+ sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root);
+ if (!ehci_debug_root) {
+ retval = -ENOENT;
+ goto err_debug;
+ }
+#endif
+
+#ifdef PLATFORM_DRIVER
+ retval = platform_driver_register(&PLATFORM_DRIVER);
+ if (retval < 0)
+ goto clean0;
+#endif
+
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0)
+ goto clean2;
+#endif
+
+#ifdef OF_PLATFORM_DRIVER
+ retval = platform_driver_register(&OF_PLATFORM_DRIVER);
+ if (retval < 0)
+ goto clean3;
+#endif
+
+#ifdef XILINX_OF_PLATFORM_DRIVER
+ retval = platform_driver_register(&XILINX_OF_PLATFORM_DRIVER);
+ if (retval < 0)
+ goto clean4;
+#endif
+ return retval;
+
+#ifdef XILINX_OF_PLATFORM_DRIVER
+ /* platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER); */
+clean4:
+#endif
+#ifdef OF_PLATFORM_DRIVER
+ platform_driver_unregister(&OF_PLATFORM_DRIVER);
+clean3:
+#endif
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+clean2:
+#endif
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+clean0:
+#endif
+#ifdef CONFIG_DYNAMIC_DEBUG
+ debugfs_remove(ehci_debug_root);
+ ehci_debug_root = NULL;
+err_debug:
+#endif
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ return retval;
+}
+module_init(ehci_hcd_init);
+
+static void __exit ehci_hcd_cleanup(void)
+{
+#ifdef XILINX_OF_PLATFORM_DRIVER
+ platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
+#endif
+#ifdef OF_PLATFORM_DRIVER
+ platform_driver_unregister(&OF_PLATFORM_DRIVER);
+#endif
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+#endif
+#ifdef CONFIG_DYNAMIC_DEBUG
+ debugfs_remove(ehci_debug_root);
+#endif
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+}
+module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
new file mode 100644
index 000000000..69208447d
--- /dev/null
+++ b/drivers/usb/host/ehci-hub.c
@@ -0,0 +1,1321 @@
+/*
+ * Copyright (C) 2001-2004 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/*-------------------------------------------------------------------------*/
+#include <linux/usb/otg.h>
+
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+
+#ifdef CONFIG_PM
+
+static int persist_enabled_on_companion(struct usb_device *udev, void *unused)
+{
+ return !udev->maxchild && udev->persist_enabled &&
+ udev->bus->root_hub->speed < USB_SPEED_HIGH;
+}
+
+/* After a power loss, ports that were owned by the companion must be
+ * reset so that the companion can still own them.
+ */
+static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
+{
+ u32 __iomem *reg;
+ u32 status;
+ int port;
+ __le32 buf;
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
+
+ if (!ehci->owned_ports)
+ return;
+
+ /*
+ * USB 1.1 devices are mostly HIDs, which don't need to persist across
+ * suspends. If we ensure that none of our companion's devices have
+ * persist_enabled (by looking through all USB 1.1 buses in the system),
+ * we can skip this and avoid slowing resume down. Devices without
+ * persist will just get reenumerated shortly after resume anyway.
+ */
+ if (!usb_for_each_dev(NULL, persist_enabled_on_companion))
+ return;
+
+ /* Make sure the ports are powered */
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ if (test_bit(port, &ehci->owned_ports)) {
+ reg = &ehci->regs->port_status[port];
+ status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+ if (!(status & PORT_POWER))
+ ehci_port_power(ehci, port, true);
+ }
+ }
+
+ /* Give the connections some time to appear */
+ msleep(20);
+
+ spin_lock_irq(&ehci->lock);
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ if (test_bit(port, &ehci->owned_ports)) {
+ reg = &ehci->regs->port_status[port];
+ status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+
+ /* Port already owned by companion? */
+ if (status & PORT_OWNER)
+ clear_bit(port, &ehci->owned_ports);
+ else if (test_bit(port, &ehci->companion_ports))
+ ehci_writel(ehci, status & ~PORT_PE, reg);
+ else {
+ spin_unlock_irq(&ehci->lock);
+ ehci_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_RESET, port + 1,
+ NULL, 0);
+ spin_lock_irq(&ehci->lock);
+ }
+ }
+ }
+ spin_unlock_irq(&ehci->lock);
+
+ if (!ehci->owned_ports)
+ return;
+ msleep(90); /* Wait for resets to complete */
+
+ spin_lock_irq(&ehci->lock);
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ if (test_bit(port, &ehci->owned_ports)) {
+ spin_unlock_irq(&ehci->lock);
+ ehci_hub_control(hcd, GetPortStatus,
+ 0, port + 1,
+ (char *) &buf, sizeof(buf));
+ spin_lock_irq(&ehci->lock);
+
+ /* The companion should now own the port,
+ * but if something went wrong the port must not
+ * remain enabled.
+ */
+ reg = &ehci->regs->port_status[port];
+ status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+ if (status & PORT_OWNER)
+ ehci_writel(ehci, status | PORT_CSC, reg);
+ else {
+ ehci_dbg(ehci, "failed handover port %d: %x\n",
+ port + 1, status);
+ ehci_writel(ehci, status & ~PORT_PE, reg);
+ }
+ }
+ }
+
+ ehci->owned_ports = 0;
+ spin_unlock_irq(&ehci->lock);
+}
+
+static int ehci_port_change(struct ehci_hcd *ehci)
+{
+ int i = HCS_N_PORTS(ehci->hcs_params);
+
+ /* First check if the controller indicates a change event */
+
+ if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)
+ return 1;
+
+ /*
+ * Not all controllers appear to update this while going from D3 to D0,
+ * so check the individual port status registers as well
+ */
+
+ while (i--)
+ if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC)
+ return 1;
+
+ return 0;
+}
+
+static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
+ bool suspending, bool do_wakeup)
+{
+ int port;
+ u32 temp;
+
+ /* If remote wakeup is enabled for the root hub but disabled
+ * for the controller, we must adjust all the port wakeup flags
+ * when the controller is suspended or resumed. In all other
+ * cases they don't need to be changed.
+ */
+ if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
+ return;
+
+ spin_lock_irq(&ehci->lock);
+
+ /* clear phy low-power mode before changing wakeup flags */
+ if (ehci->has_tdi_phy_lpm) {
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
+
+ temp = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
+ }
+ spin_unlock_irq(&ehci->lock);
+ msleep(5);
+ spin_lock_irq(&ehci->lock);
+ }
+
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *reg = &ehci->regs->port_status[port];
+ u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+ u32 t2 = t1 & ~PORT_WAKE_BITS;
+
+ /* If we are suspending the controller, clear the flags.
+ * If we are resuming the controller, set the wakeup flags.
+ */
+ if (!suspending) {
+ if (t1 & PORT_CONNECT)
+ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+ else
+ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
+ }
+ ehci_writel(ehci, t2, reg);
+ }
+
+ /* enter phy low-power mode again */
+ if (ehci->has_tdi_phy_lpm) {
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
+
+ temp = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
+ }
+ }
+
+ /* Does the root hub have a port wakeup pending? */
+ if (!suspending && ehci_port_change(ehci))
+ usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
+
+ spin_unlock_irq(&ehci->lock);
+}
+
+static int ehci_bus_suspend (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ int port;
+ int mask;
+ int changed;
+ bool fs_idle_delay;
+
+ ehci_dbg(ehci, "suspend root hub\n");
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep(5);
+
+ /* stop the schedules */
+ ehci_quiesce(ehci);
+
+ spin_lock_irq (&ehci->lock);
+ if (ehci->rh_state < EHCI_RH_RUNNING)
+ goto done;
+
+ /* Once the controller is stopped, port resumes that are already
+ * in progress won't complete. Hence if remote wakeup is enabled
+ * for the root hub and any ports are in the middle of a resume or
+ * remote wakeup, we must fail the suspend.
+ */
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ if (ehci->resuming_ports) {
+ spin_unlock_irq(&ehci->lock);
+ ehci_dbg(ehci, "suspend failed because a port is resuming\n");
+ return -EBUSY;
+ }
+ }
+
+ /* Unlike other USB host controller types, EHCI doesn't have
+ * any notion of "global" or bus-wide suspend. The driver has
+ * to manually suspend all the active unsuspended ports, and
+ * then manually resume them in the bus_resume() routine.
+ */
+ ehci->bus_suspended = 0;
+ ehci->owned_ports = 0;
+ changed = 0;
+ fs_idle_delay = false;
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *reg = &ehci->regs->port_status [port];
+ u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+ u32 t2 = t1 & ~PORT_WAKE_BITS;
+
+ /* keep track of which ports we suspend */
+ if (t1 & PORT_OWNER)
+ set_bit(port, &ehci->owned_ports);
+ else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
+ t2 |= PORT_SUSPEND;
+ set_bit(port, &ehci->bus_suspended);
+ }
+
+ /* enable remote wakeup on all ports, if told to do so */
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ /* only enable appropriate wake bits, otherwise the
+ * hardware can not go phy low power mode. If a race
+ * condition happens here(connection change during bits
+ * set), the port change detection will finally fix it.
+ */
+ if (t1 & PORT_CONNECT)
+ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+ else
+ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
+ }
+
+ if (t1 != t2) {
+ /*
+ * On some controllers, Wake-On-Disconnect will
+ * generate false wakeup signals until the bus
+ * switches over to full-speed idle. For their
+ * sake, add a delay if we need one.
+ */
+ if ((t2 & PORT_WKDISC_E) &&
+ ehci_port_speed(ehci, t2) ==
+ USB_PORT_STAT_HIGH_SPEED)
+ fs_idle_delay = true;
+ ehci_writel(ehci, t2, reg);
+ changed = 1;
+ }
+ }
+ spin_unlock_irq(&ehci->lock);
+
+ if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) {
+ /*
+ * Wait for HCD to enter low-power mode or for the bus
+ * to switch to full-speed idle.
+ */
+ usleep_range(5000, 5500);
+ }
+
+ if (changed && ehci->has_tdi_phy_lpm) {
+ spin_lock_irq(&ehci->lock);
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
+ u32 t3;
+
+ t3 = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
+ t3 = ehci_readl(ehci, hostpc_reg);
+ ehci_dbg(ehci, "Port %d phy low-power mode %s\n",
+ port, (t3 & HOSTPC_PHCD) ?
+ "succeeded" : "failed");
+ }
+ spin_unlock_irq(&ehci->lock);
+ }
+
+ /* Apparently some devices need a >= 1-uframe delay here */
+ if (ehci->bus_suspended)
+ udelay(150);
+
+ /* turn off now-idle HC */
+ ehci_halt (ehci);
+
+ spin_lock_irq(&ehci->lock);
+ if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD))
+ ehci_handle_controller_death(ehci);
+ if (ehci->rh_state != EHCI_RH_RUNNING)
+ goto done;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
+
+ end_unlink_async(ehci);
+ unlink_empty_async_suspended(ehci);
+ ehci_handle_start_intr_unlinks(ehci);
+ ehci_handle_intr_unlinks(ehci);
+ end_free_itds(ehci);
+
+ /* allow remote wakeup */
+ mask = INTR_MASK;
+ if (!hcd->self.root_hub->do_remote_wakeup)
+ mask &= ~STS_PCD;
+ ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+ ehci_readl(ehci, &ehci->regs->intr_enable);
+
+ done:
+ ehci->next_statechange = jiffies + msecs_to_jiffies(10);
+ ehci->enabled_hrtimer_events = 0;
+ ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
+ spin_unlock_irq (&ehci->lock);
+
+ hrtimer_cancel(&ehci->hrtimer);
+ return 0;
+}
+
+
+/* caller has locked the root hub, and should reset/reinit on error */
+static int ehci_bus_resume (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ u32 power_okay;
+ int i;
+ unsigned long resume_needed = 0;
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep(5);
+ spin_lock_irq (&ehci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown)
+ goto shutdown;
+
+ if (unlikely(ehci->debug)) {
+ if (!dbgp_reset_prep(hcd))
+ ehci->debug = NULL;
+ else
+ dbgp_external_startup(hcd);
+ }
+
+ /* Ideally and we've got a real resume here, and no port's power
+ * was lost. (For PCI, that means Vaux was maintained.) But we
+ * could instead be restoring a swsusp snapshot -- so that BIOS was
+ * the last user of the controller, not reset/pm hardware keeping
+ * state we gave to it.
+ */
+ power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
+ ehci_dbg(ehci, "resume root hub%s\n",
+ power_okay ? "" : " after power loss");
+
+ /* at least some APM implementations will try to deliver
+ * IRQs right away, so delay them until we're ready.
+ */
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+
+ /* re-init operational registers */
+ ehci_writel(ehci, 0, &ehci->regs->segment);
+ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+ ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);
+
+ /* restore CMD_RUN, framelist size, and irq threshold */
+ ehci->command |= CMD_RUN;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ ehci->rh_state = EHCI_RH_RUNNING;
+
+ /*
+ * According to Bugzilla #8190, the port status for some controllers
+ * will be wrong without a delay. At their wrong status, the port
+ * is enabled, but not suspended neither resumed.
+ */
+ i = HCS_N_PORTS(ehci->hcs_params);
+ while (i--) {
+ temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
+ if ((temp & PORT_PE) &&
+ !(temp & (PORT_SUSPEND | PORT_RESUME))) {
+ ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp);
+ spin_unlock_irq(&ehci->lock);
+ msleep(8);
+ spin_lock_irq(&ehci->lock);
+ break;
+ }
+ }
+
+ if (ehci->shutdown)
+ goto shutdown;
+
+ /* clear phy low-power mode before resume */
+ if (ehci->bus_suspended && ehci->has_tdi_phy_lpm) {
+ i = HCS_N_PORTS(ehci->hcs_params);
+ while (i--) {
+ if (test_bit(i, &ehci->bus_suspended)) {
+ u32 __iomem *hostpc_reg =
+ &ehci->regs->hostpc[i];
+
+ temp = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp & ~HOSTPC_PHCD,
+ hostpc_reg);
+ }
+ }
+ spin_unlock_irq(&ehci->lock);
+ msleep(5);
+ spin_lock_irq(&ehci->lock);
+ if (ehci->shutdown)
+ goto shutdown;
+ }
+
+ /* manually resume the ports we suspended during bus_suspend() */
+ i = HCS_N_PORTS (ehci->hcs_params);
+ while (i--) {
+ temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
+ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+ if (test_bit(i, &ehci->bus_suspended) &&
+ (temp & PORT_SUSPEND)) {
+ temp |= PORT_RESUME;
+ set_bit(i, &resume_needed);
+ }
+ ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
+ }
+
+ /*
+ * msleep for USB_RESUME_TIMEOUT ms only if code is trying to resume
+ * port
+ */
+ if (resume_needed) {
+ spin_unlock_irq(&ehci->lock);
+ msleep(USB_RESUME_TIMEOUT);
+ spin_lock_irq(&ehci->lock);
+ if (ehci->shutdown)
+ goto shutdown;
+ }
+
+ i = HCS_N_PORTS (ehci->hcs_params);
+ while (i--) {
+ temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
+ if (test_bit(i, &resume_needed)) {
+ temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
+ ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
+ }
+ }
+
+ ehci->next_statechange = jiffies + msecs_to_jiffies(5);
+ spin_unlock_irq(&ehci->lock);
+
+ ehci_handover_companion_ports(ehci);
+
+ /* Now we can safely re-enable irqs */
+ spin_lock_irq(&ehci->lock);
+ if (ehci->shutdown)
+ goto shutdown;
+ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+ (void) ehci_readl(ehci, &ehci->regs->intr_enable);
+ spin_unlock_irq(&ehci->lock);
+
+ return 0;
+
+ shutdown:
+ spin_unlock_irq(&ehci->lock);
+ return -ESHUTDOWN;
+}
+
+#else
+
+#define ehci_bus_suspend NULL
+#define ehci_bus_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sets the owner of a port
+ */
+static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
+{
+ u32 __iomem *status_reg;
+ u32 port_status;
+ int try;
+
+ status_reg = &ehci->regs->port_status[portnum];
+
+ /*
+ * The controller won't set the OWNER bit if the port is
+ * enabled, so this loop will sometimes require at least two
+ * iterations: one to disable the port and one to set OWNER.
+ */
+ for (try = 4; try > 0; --try) {
+ spin_lock_irq(&ehci->lock);
+ port_status = ehci_readl(ehci, status_reg);
+ if ((port_status & PORT_OWNER) == new_owner
+ || (port_status & (PORT_OWNER | PORT_CONNECT))
+ == 0)
+ try = 0;
+ else {
+ port_status ^= PORT_OWNER;
+ port_status &= ~(PORT_PE | PORT_RWC_BITS);
+ ehci_writel(ehci, port_status, status_reg);
+ }
+ spin_unlock_irq(&ehci->lock);
+ if (try > 1)
+ msleep(5);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete (
+ struct ehci_hcd *ehci,
+ int index,
+ u32 __iomem *status_reg,
+ int port_status
+) {
+ if (!(port_status & PORT_CONNECT))
+ return port_status;
+
+ /* if reset finished and it's still not enabled -- handoff */
+ if (!(port_status & PORT_PE)) {
+
+ /* with integrated TT, there's nobody to hand it to! */
+ if (ehci_is_TDI(ehci)) {
+ ehci_dbg (ehci,
+ "Failed to enable port %d on root hub TT\n",
+ index+1);
+ return port_status;
+ }
+
+ ehci_dbg (ehci, "port %d full speed --> companion\n",
+ index + 1);
+
+ // what happens if HCS_N_CC(params) == 0 ?
+ port_status |= PORT_OWNER;
+ port_status &= ~PORT_RWC_BITS;
+ ehci_writel(ehci, port_status, status_reg);
+
+ /* ensure 440EPX ohci controller state is operational */
+ if (ehci->has_amcc_usb23)
+ set_ohci_hcfs(ehci, 1);
+ } else {
+ ehci_dbg(ehci, "port %d reset complete, port enabled\n",
+ index + 1);
+ /* ensure 440EPx ohci controller state is suspended */
+ if (ehci->has_amcc_usb23)
+ set_ohci_hcfs(ehci, 0);
+ }
+
+ return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp, status;
+ u32 mask;
+ int ports, i, retval = 1;
+ unsigned long flags;
+ u32 ppcd = ~0;
+
+ /* init status to no-changes */
+ buf [0] = 0;
+ ports = HCS_N_PORTS (ehci->hcs_params);
+ if (ports > 7) {
+ buf [1] = 0;
+ retval++;
+ }
+
+ /* Inform the core about resumes-in-progress by returning
+ * a non-zero value even if there are no status changes.
+ */
+ status = ehci->resuming_ports;
+
+ /* Some boards (mostly VIA?) report bogus overcurrent indications,
+ * causing massive log spam unless we completely ignore them. It
+ * may be relevant that VIA VT8235 controllers, where PORT_POWER is
+ * always set, seem to clear PORT_OCC and PORT_CSC when writing to
+ * PORT_POWER; that's surprising, but maybe within-spec.
+ */
+ if (!ignore_oc)
+ mask = PORT_CSC | PORT_PEC | PORT_OCC;
+ else
+ mask = PORT_CSC | PORT_PEC;
+ // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
+
+ for (i = 0; i < ports; i++) {
+ /* leverage per-port change bits feature */
+ if (ppcd & (1 << i))
+ temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
+ else
+ temp = 0;
+
+ /*
+ * Return status information even for ports with OWNER set.
+ * Otherwise hub_wq wouldn't see the disconnect event when a
+ * high-speed device is switched over to the companion
+ * controller by the user.
+ */
+
+ if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
+ || (ehci->reset_done[i] && time_after_eq(
+ jiffies, ehci->reset_done[i]))) {
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
+ status = STS_PCD;
+ }
+ }
+
+ /* If a resume is in progress, make sure it can finish */
+ if (ehci->resuming_ports)
+ mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25));
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ehci_hub_descriptor (
+ struct ehci_hcd *ehci,
+ struct usb_hub_descriptor *desc
+) {
+ int ports = HCS_N_PORTS (ehci->hcs_params);
+ u16 temp;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
+
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ if (HCS_PPC (ehci->hcs_params))
+ temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */
+ else
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
+#if 0
+// re-enable when we support USB_PORT_FEAT_INDICATOR below.
+ if (HCS_INDICATOR (ehci->hcs_params))
+ temp |= HUB_CHAR_PORTIND; /* per-port indicators (LEDs) */
+#endif
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_HCD_TEST_MODE
+
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
+
+static void usb_ehset_completion(struct urb *urb)
+{
+ struct completion *done = urb->context;
+
+ complete(done);
+}
+static int submit_single_step_set_feature(
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int is_setup
+);
+
+/*
+ * Allocate and initialize a control URB. This request will be used by the
+ * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
+ * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
+ * Return NULL if failed.
+ */
+static struct urb *request_single_step_set_feature_urb(
+ struct usb_device *udev,
+ void *dr,
+ void *buf,
+ struct completion *done
+) {
+ struct urb *urb;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_host_endpoint *ep;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return NULL;
+
+ urb->pipe = usb_rcvctrlpipe(udev, 0);
+ ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
+ [usb_pipeendpoint(urb->pipe)];
+ if (!ep) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ urb->ep = ep;
+ urb->dev = udev;
+ urb->setup_packet = (void *)dr;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+ urb->complete = usb_ehset_completion;
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->transfer_flags = URB_DIR_IN;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ urb->setup_dma = dma_map_single(
+ hcd->self.controller,
+ urb->setup_packet,
+ sizeof(struct usb_ctrlrequest),
+ DMA_TO_DEVICE);
+ urb->transfer_dma = dma_map_single(
+ hcd->self.controller,
+ urb->transfer_buffer,
+ urb->transfer_buffer_length,
+ DMA_FROM_DEVICE);
+ urb->context = done;
+ return urb;
+}
+
+static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ int retval = -ENOMEM;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ struct usb_device *udev;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct usb_device_descriptor *buf;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ /* Obtain udev of the rhub's child port */
+ udev = usb_hub_find_child(hcd->self.root_hub, port);
+ if (!udev) {
+ ehci_err(ehci, "No device attached to the RootHub\n");
+ return -ENODEV;
+ }
+ buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!dr) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* Fill Setup packet for GetDescriptor */
+ dr->bRequestType = USB_DIR_IN;
+ dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+ dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+ urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
+ if (!urb)
+ goto cleanup;
+
+ /* Submit just the SETUP stage */
+ retval = submit_single_step_set_feature(hcd, urb, 1);
+ if (retval)
+ goto out1;
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
+ goto out1;
+ }
+ msleep(15 * 1000);
+
+ /* Complete remaining DATA and STATUS stages using the same URB */
+ urb->status = -EINPROGRESS;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ retval = submit_single_step_set_feature(hcd, urb, 0);
+ if (!retval && !wait_for_completion_timeout(&done,
+ msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
+ }
+out1:
+ usb_free_urb(urb);
+cleanup:
+ kfree(dr);
+ kfree(buf);
+ return retval;
+}
+#endif /* CONFIG_USB_HCD_TEST_MODE */
+/*-------------------------------------------------------------------------*/
+
+int ehci_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ int ports = HCS_N_PORTS (ehci->hcs_params);
+ u32 __iomem *status_reg = &ehci->regs->port_status[
+ (wIndex & 0xff) - 1];
+ u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];
+ u32 temp, temp1, status;
+ unsigned long flags;
+ int retval = 0;
+ unsigned selector;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = ehci_readl(ehci, status_reg);
+ temp &= ~PORT_RWC_BITS;
+
+ /*
+ * Even if OWNER is set, so the port is owned by the
+ * companion controller, hub_wq needs to be able to clear
+ * the port-change status bits (especially
+ * USB_PORT_STAT_C_CONNECTION).
+ */
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ ehci_writel(ehci, temp & ~PORT_PE, status_reg);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ ehci_writel(ehci, temp | PORT_PEC, status_reg);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (temp & PORT_RESET)
+ goto error;
+ if (ehci->no_selective_suspend)
+ break;
+#ifdef CONFIG_USB_OTG
+ if ((hcd->self.otg_port == (wIndex + 1))
+ && hcd->self.b_hnp_enable) {
+ otg_start_hnp(hcd->usb_phy->otg);
+ break;
+ }
+#endif
+ if (!(temp & PORT_SUSPEND))
+ break;
+ if ((temp & PORT_PE) == 0)
+ goto error;
+
+ /* clear phy low-power mode before resume */
+ if (ehci->has_tdi_phy_lpm) {
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
+ hostpc_reg);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ msleep(5);/* wait to leave low-power mode */
+ spin_lock_irqsave(&ehci->lock, flags);
+ }
+ /* resume signaling for 20 msec */
+ temp &= ~PORT_WAKE_BITS;
+ ehci_writel(ehci, temp | PORT_RESUME, status_reg);
+ ehci->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ set_bit(wIndex, &ehci->resuming_ports);
+ usb_hcd_start_port_resume(&hcd->self, wIndex);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ clear_bit(wIndex, &ehci->port_c_suspend);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC(ehci->hcs_params)) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ ehci_port_power(ehci, wIndex, false);
+ spin_lock_irqsave(&ehci->lock, flags);
+ }
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ ehci_writel(ehci, temp | PORT_CSC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ ehci_writel(ehci, temp | PORT_OCC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */
+ break;
+ case GetHubDescriptor:
+ ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset (buf, 0, 4);
+ //cpu_to_le32s ((u32 *) buf);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ status = 0;
+ temp = ehci_readl(ehci, status_reg);
+
+ // wPortChange bits
+ if (temp & PORT_CSC)
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
+ if (temp & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+
+ if ((temp & PORT_OCC) && !ignore_oc){
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+
+ /*
+ * Hubs should disable port power on over-current.
+ * However, not all EHCI implementations do this
+ * automatically, even if they _do_ support per-port
+ * power switching; they're allowed to just limit the
+ * current. hub_wq will turn the power back on.
+ */
+ if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
+ && HCS_PPC(ehci->hcs_params)) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ ehci_port_power(ehci, wIndex, false);
+ spin_lock_irqsave(&ehci->lock, flags);
+ temp = ehci_readl(ehci, status_reg);
+ }
+ }
+
+ /* no reset or resume pending */
+ if (!ehci->reset_done[wIndex]) {
+
+ /* Remote Wakeup received? */
+ if (temp & PORT_RESUME) {
+ /* resume signaling for 20 msec */
+ ehci->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ usb_hcd_start_port_resume(&hcd->self, wIndex);
+ set_bit(wIndex, &ehci->resuming_ports);
+ /* check the port again */
+ mod_timer(&ehci_to_hcd(ehci)->rh_timer,
+ ehci->reset_done[wIndex]);
+ }
+
+ /* reset or resume not yet complete */
+ } else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) {
+ ; /* wait until it is complete */
+
+ /* resume completed */
+ } else if (test_bit(wIndex, &ehci->resuming_ports)) {
+ clear_bit(wIndex, &ehci->suspended_ports);
+ set_bit(wIndex, &ehci->port_c_suspend);
+ ehci->reset_done[wIndex] = 0;
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
+
+ /* stop resume signaling */
+ temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
+ ehci_writel(ehci, temp, status_reg);
+ clear_bit(wIndex, &ehci->resuming_ports);
+ retval = ehci_handshake(ehci, status_reg,
+ PORT_RESUME, 0, 2000 /* 2msec */);
+ if (retval != 0) {
+ ehci_err(ehci, "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp = ehci_readl(ehci, status_reg);
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ } else {
+ status |= USB_PORT_STAT_C_RESET << 16;
+ ehci->reset_done [wIndex] = 0;
+
+ /* force reset to complete */
+ ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
+ status_reg);
+ /* REVISIT: some hardware needs 550+ usec to clear
+ * this bit; seems too long to spin routinely...
+ */
+ retval = ehci_handshake(ehci, status_reg,
+ PORT_RESET, 0, 1000);
+ if (retval != 0) {
+ ehci_err (ehci, "port %d reset error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+
+ /* see what we found out */
+ temp = check_reset_complete (ehci, wIndex, status_reg,
+ ehci_readl(ehci, status_reg));
+ }
+
+ /* transfer dedicated ports to the companion hc */
+ if ((temp & PORT_CONNECT) &&
+ test_bit(wIndex, &ehci->companion_ports)) {
+ temp &= ~PORT_RWC_BITS;
+ temp |= PORT_OWNER;
+ ehci_writel(ehci, temp, status_reg);
+ ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
+ temp = ehci_readl(ehci, status_reg);
+ }
+
+ /*
+ * Even if OWNER is set, there's no harm letting hub_wq
+ * see the wPortStatus values (they should all be 0 except
+ * for PORT_POWER anyway).
+ */
+
+ if (temp & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ // status may be from integrated TT
+ if (ehci->has_hostpc) {
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ status |= ehci_port_speed(ehci, temp1);
+ } else
+ status |= ehci_port_speed(ehci, temp);
+ }
+ if (temp & PORT_PE)
+ status |= USB_PORT_STAT_ENABLE;
+
+ /* maybe the port was unsuspended without our knowledge */
+ if (temp & (PORT_SUSPEND|PORT_RESUME)) {
+ status |= USB_PORT_STAT_SUSPEND;
+ } else if (test_bit(wIndex, &ehci->suspended_ports)) {
+ clear_bit(wIndex, &ehci->suspended_ports);
+ clear_bit(wIndex, &ehci->resuming_ports);
+ ehci->reset_done[wIndex] = 0;
+ if (temp & PORT_PE)
+ set_bit(wIndex, &ehci->port_c_suspend);
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
+ }
+
+ if (temp & PORT_OC)
+ status |= USB_PORT_STAT_OVERCURRENT;
+ if (temp & PORT_RESET)
+ status |= USB_PORT_STAT_RESET;
+ if (temp & PORT_POWER)
+ status |= USB_PORT_STAT_POWER;
+ if (test_bit(wIndex, &ehci->port_c_suspend))
+ status |= USB_PORT_STAT_C_SUSPEND << 16;
+
+ if (status & ~0xffff) /* only if wPortChange is interesting */
+ dbg_port(ehci, "GetStatus", wIndex + 1, temp);
+ put_unaligned_le32(status, buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ selector = wIndex >> 8;
+ wIndex &= 0xff;
+ if (unlikely(ehci->debug)) {
+ /* If the debug port is active any port
+ * feature requests should get denied */
+ if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) &&
+ (readl(&ehci->debug->control) & DBGP_ENABLED)) {
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = ehci_readl(ehci, status_reg);
+ if (temp & PORT_OWNER)
+ break;
+
+ temp &= ~PORT_RWC_BITS;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (ehci->no_selective_suspend)
+ break;
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_RESET) != 0)
+ goto error;
+
+ /* After above check the port must be connected.
+ * Set appropriate bit thus could put phy into low power
+ * mode if we have tdi_phy_lpm feature
+ */
+ temp &= ~PORT_WKCONN_E;
+ temp |= PORT_WKDISC_E | PORT_WKOC_E;
+ ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+ if (ehci->has_tdi_phy_lpm) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ msleep(5);/* 5ms for HCD enter low pwr mode */
+ spin_lock_irqsave(&ehci->lock, flags);
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp1 | HOSTPC_PHCD,
+ hostpc_reg);
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
+ wIndex, (temp1 & HOSTPC_PHCD) ?
+ "succeeded" : "failed");
+ }
+ set_bit(wIndex, &ehci->suspended_ports);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC(ehci->hcs_params)) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ ehci_port_power(ehci, wIndex, true);
+ spin_lock_irqsave(&ehci->lock, flags);
+ }
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (temp & (PORT_SUSPEND|PORT_RESUME))
+ goto error;
+ /* line status bits may report this as low speed,
+ * which can be fine if this root hub has a
+ * transaction translator built in.
+ */
+ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+ && !ehci_is_TDI(ehci)
+ && PORT_USB11 (temp)) {
+ ehci_dbg (ehci,
+ "port %d low speed --> companion\n",
+ wIndex + 1);
+ temp |= PORT_OWNER;
+ } else {
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ ehci->reset_done [wIndex] = jiffies
+ + msecs_to_jiffies (50);
+ }
+ ehci_writel(ehci, temp, status_reg);
+ break;
+
+ /* For downstream facing ports (these): one hub port is put
+ * into test mode according to USB2 11.24.2.13, then the hub
+ * must be reset (which for root hub now means rmmod+modprobe,
+ * or else system reboot). See EHCI 2.3.9 and 4.14 for info
+ * about the EHCI-specific stuff.
+ */
+ case USB_PORT_FEAT_TEST:
+#ifdef CONFIG_USB_HCD_TEST_MODE
+ if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ retval = ehset_single_step_set_feature(hcd,
+ wIndex + 1);
+ spin_lock_irqsave(&ehci->lock, flags);
+ break;
+ }
+#endif
+ if (!selector || selector > 5)
+ goto error;
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ ehci_quiesce(ehci);
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ /* Put all enabled ports into suspend */
+ while (ports--) {
+ u32 __iomem *sreg =
+ &ehci->regs->port_status[ports];
+
+ temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
+ if (temp & PORT_PE)
+ ehci_writel(ehci, temp | PORT_SUSPEND,
+ sreg);
+ }
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ ehci_halt(ehci);
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ temp = ehci_readl(ehci, status_reg);
+ temp |= selector << 16;
+ ehci_writel(ehci, temp, status_reg);
+ break;
+
+ default:
+ goto error;
+ }
+ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+error_exit:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(ehci_hub_control);
+
+static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ if (ehci_is_TDI(ehci))
+ return;
+ set_owner(ehci, --portnum, PORT_OWNER);
+}
+
+static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 __iomem *reg;
+
+ if (ehci_is_TDI(ehci))
+ return 0;
+ reg = &ehci->regs->port_status[portnum - 1];
+ return ehci_readl(ehci, reg) & PORT_OWNER;
+}
+
+static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable)
+{
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
+ u32 __iomem *status_reg = &ehci->regs->port_status[portnum];
+ u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
+
+ if (enable)
+ ehci_writel(ehci, temp | PORT_POWER, status_reg);
+ else
+ ehci_writel(ehci, temp & ~PORT_POWER, status_reg);
+
+ if (hcd->driver->port_power)
+ hcd->driver->port_power(hcd, portnum, enable);
+
+ return 0;
+}
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
new file mode 100644
index 000000000..b6205fac3
--- /dev/null
+++ b/drivers/usb/host/ehci-mem.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
+ * No memory seen by this driver is pageable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
+ dma_addr_t dma)
+{
+ memset (qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END(ehci);
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
+ INIT_LIST_HEAD (&qtd->qtd_list);
+}
+
+static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, gfp_t flags)
+{
+ struct ehci_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
+ if (qtd != NULL) {
+ ehci_qtd_init(ehci, qtd, dma);
+ }
+ return qtd;
+}
+
+static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static void qh_destroy(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+ ehci_dbg (ehci, "unused qh not empty!\n");
+ BUG ();
+ }
+ if (qh->dummy)
+ ehci_qtd_free (ehci, qh->dummy);
+ dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+ kfree(qh);
+}
+
+static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
+{
+ struct ehci_qh *qh;
+ dma_addr_t dma;
+
+ qh = kzalloc(sizeof *qh, GFP_ATOMIC);
+ if (!qh)
+ goto done;
+ qh->hw = (struct ehci_qh_hw *)
+ dma_pool_alloc(ehci->qh_pool, flags, &dma);
+ if (!qh->hw)
+ goto fail;
+ memset(qh->hw, 0, sizeof *qh->hw);
+ qh->qh_dma = dma;
+ // INIT_LIST_HEAD (&qh->qh_list);
+ INIT_LIST_HEAD (&qh->qtd_list);
+ INIT_LIST_HEAD(&qh->unlink_node);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = ehci_qtd_alloc (ehci, flags);
+ if (qh->dummy == NULL) {
+ ehci_dbg (ehci, "no dummy td\n");
+ goto fail1;
+ }
+done:
+ return qh;
+fail1:
+ dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+fail:
+ kfree(qh);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void ehci_mem_cleanup (struct ehci_hcd *ehci)
+{
+ if (ehci->async)
+ qh_destroy(ehci, ehci->async);
+ ehci->async = NULL;
+
+ if (ehci->dummy)
+ qh_destroy(ehci, ehci->dummy);
+ ehci->dummy = NULL;
+
+ /* DMA consistent memory and pools */
+ if (ehci->qtd_pool)
+ dma_pool_destroy (ehci->qtd_pool);
+ ehci->qtd_pool = NULL;
+
+ if (ehci->qh_pool) {
+ dma_pool_destroy (ehci->qh_pool);
+ ehci->qh_pool = NULL;
+ }
+
+ if (ehci->itd_pool)
+ dma_pool_destroy (ehci->itd_pool);
+ ehci->itd_pool = NULL;
+
+ if (ehci->sitd_pool)
+ dma_pool_destroy (ehci->sitd_pool);
+ ehci->sitd_pool = NULL;
+
+ if (ehci->periodic)
+ dma_free_coherent (ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof (u32),
+ ehci->periodic, ehci->periodic_dma);
+ ehci->periodic = NULL;
+
+ /* shadow periodic table */
+ kfree(ehci->pshadow);
+ ehci->pshadow = NULL;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
+{
+ int i;
+
+ /* QTDs for control/bulk/intr transfers */
+ ehci->qtd_pool = dma_pool_create ("ehci_qtd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->qtd_pool) {
+ goto fail;
+ }
+
+ /* QHs for control/bulk/intr transfers */
+ ehci->qh_pool = dma_pool_create ("ehci_qh",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof(struct ehci_qh_hw),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->qh_pool) {
+ goto fail;
+ }
+ ehci->async = ehci_qh_alloc (ehci, flags);
+ if (!ehci->async) {
+ goto fail;
+ }
+
+ /* ITD for high speed ISO transfers */
+ ehci->itd_pool = dma_pool_create ("ehci_itd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_itd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->itd_pool) {
+ goto fail;
+ }
+
+ /* SITD for full/low speed split ISO transfers */
+ ehci->sitd_pool = dma_pool_create ("ehci_sitd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_sitd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->sitd_pool) {
+ goto fail;
+ }
+
+ /* Hardware periodic table */
+ ehci->periodic = (__le32 *)
+ dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof(__le32),
+ &ehci->periodic_dma, flags);
+ if (ehci->periodic == NULL) {
+ goto fail;
+ }
+
+ if (ehci->use_dummy_qh) {
+ struct ehci_qh_hw *hw;
+ ehci->dummy = ehci_qh_alloc(ehci, flags);
+ if (!ehci->dummy)
+ goto fail;
+
+ hw = ehci->dummy->hw;
+ hw->hw_next = EHCI_LIST_END(ehci);
+ hw->hw_qtd_next = EHCI_LIST_END(ehci);
+ hw->hw_alt_next = EHCI_LIST_END(ehci);
+ ehci->dummy->hw = hw;
+
+ for (i = 0; i < ehci->periodic_size; i++)
+ ehci->periodic[i] = cpu_to_hc32(ehci,
+ ehci->dummy->qh_dma);
+ } else {
+ for (i = 0; i < ehci->periodic_size; i++)
+ ehci->periodic[i] = EHCI_LIST_END(ehci);
+ }
+
+ /* software shadow of hardware table */
+ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
+ if (ehci->pshadow != NULL)
+ return 0;
+
+fail:
+ ehci_dbg (ehci, "couldn't init memory\n");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+}
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
new file mode 100644
index 000000000..275c92e53
--- /dev/null
+++ b/drivers/usb/host/ehci-msm.c
@@ -0,0 +1,233 @@
+/* ehci-msm.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+#define MSM_USB_BASE (hcd->regs)
+
+#define DRIVER_DESC "Qualcomm On-Chip EHCI Host Controller"
+
+static const char hcd_name[] = "ehci-msm";
+static struct hc_driver __read_mostly msm_hc_driver;
+
+static int ehci_msm_reset(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci->caps = USB_CAPLENGTH;
+ hcd->has_tt = 1;
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ /* bursts of unspecified length. */
+ writel(0, USB_AHBBURST);
+ /* Use the AHB transactor */
+ writel(0, USB_AHBMODE);
+ /* Disable streaming mode and select host mode */
+ writel(0x13, USB_USBMODE);
+
+ return 0;
+}
+
+static int ehci_msm_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ struct usb_phy *phy;
+ int ret;
+
+ dev_dbg(&pdev->dev, "ehci_msm proble\n");
+
+ hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ return -ENOMEM;
+ }
+
+ hcd->irq = platform_get_irq(pdev, 0);
+ if (hcd->irq < 0) {
+ dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+ ret = hcd->irq;
+ goto put_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get memory resource\n");
+ ret = -ENODEV;
+ goto put_hcd;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto put_hcd;
+ }
+
+ /*
+ * OTG driver takes care of PHY initialization, clock management,
+ * powering up VBUS, mapping of registers address space and power
+ * management.
+ */
+ if (pdev->dev.of_node)
+ phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
+ else
+ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
+ if (IS_ERR(phy)) {
+ dev_err(&pdev->dev, "unable to find transceiver\n");
+ ret = -EPROBE_DEFER;
+ goto put_hcd;
+ }
+
+ ret = otg_set_host(phy->otg, &hcd->self);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register with transceiver\n");
+ goto put_hcd;
+ }
+
+ hcd->usb_phy = phy;
+ device_init_wakeup(&pdev->dev, 1);
+ /*
+ * OTG device parent of HCD takes care of putting
+ * hardware into low power mode.
+ */
+ pm_runtime_no_callbacks(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ /* FIXME: need to call usb_add_hcd() here? */
+
+ return 0;
+
+put_hcd:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int ehci_msm_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ otg_set_host(hcd->usb_phy->otg, NULL);
+
+ /* FIXME: need to call usb_remove_hcd() here? */
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ehci_msm_pm_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ bool do_wakeup = device_may_wakeup(dev);
+
+ dev_dbg(dev, "ehci-msm PM suspend\n");
+
+ return ehci_suspend(hcd, do_wakeup);
+}
+
+static int ehci_msm_pm_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "ehci-msm PM resume\n");
+ ehci_resume(hcd, false);
+
+ return 0;
+}
+#else
+#define ehci_msm_pm_suspend NULL
+#define ehci_msm_pm_resume NULL
+#endif
+
+static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
+ .suspend = ehci_msm_pm_suspend,
+ .resume = ehci_msm_pm_resume,
+};
+
+static const struct of_device_id msm_ehci_dt_match[] = {
+ { .compatible = "qcom,ehci-host", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_ehci_dt_match);
+
+static struct platform_driver ehci_msm_driver = {
+ .probe = ehci_msm_probe,
+ .remove = ehci_msm_remove,
+ .driver = {
+ .name = "msm_hsusb_host",
+ .pm = &ehci_msm_dev_pm_ops,
+ .of_match_table = msm_ehci_dt_match,
+ },
+};
+
+static const struct ehci_driver_overrides msm_overrides __initdata = {
+ .reset = ehci_msm_reset,
+};
+
+static int __init ehci_msm_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ehci_init_driver(&msm_hc_driver, &msm_overrides);
+ return platform_driver_register(&ehci_msm_driver);
+}
+module_init(ehci_msm_init);
+
+static void __exit ehci_msm_cleanup(void)
+{
+ platform_driver_unregister(&ehci_msm_driver);
+}
+module_exit(ehci_msm_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:msm-ehci");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
new file mode 100644
index 000000000..849806a75
--- /dev/null
+++ b/drivers/usb/host/ehci-mv.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ * Neil Zhang <zhangwm@marvell.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_data/mv_usb.h>
+
+#define CAPLENGTH_MASK (0xff)
+
+struct ehci_hcd_mv {
+ struct usb_hcd *hcd;
+
+ /* Which mode does this ehci running OTG/Host ? */
+ int mode;
+
+ void __iomem *phy_regs;
+ void __iomem *cap_regs;
+ void __iomem *op_regs;
+
+ struct usb_phy *otg;
+
+ struct mv_usb_platform_data *pdata;
+
+ struct clk *clk;
+};
+
+static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
+{
+ clk_prepare_enable(ehci_mv->clk);
+}
+
+static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
+{
+ clk_disable_unprepare(ehci_mv->clk);
+}
+
+static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
+{
+ int retval;
+
+ ehci_clock_enable(ehci_mv);
+ if (ehci_mv->pdata->phy_init) {
+ retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
+ if (retval)
+ return retval;
+ }
+
+ return 0;
+}
+
+static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
+{
+ if (ehci_mv->pdata->phy_deinit)
+ ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
+ ehci_clock_disable(ehci_mv);
+}
+
+static int mv_ehci_reset(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
+ int retval;
+
+ if (ehci_mv == NULL) {
+ dev_err(dev, "Can not find private ehci data\n");
+ return -ENODEV;
+ }
+
+ hcd->has_tt = 1;
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ dev_err(dev, "ehci_setup failed %d\n", retval);
+
+ return retval;
+}
+
+static const struct hc_driver mv_ehci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Marvell EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = mv_ehci_reset,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+};
+
+static int mv_ehci_probe(struct platform_device *pdev)
+{
+ struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct ehci_hcd_mv *ehci_mv;
+ struct resource *r;
+ int retval = -ENODEV;
+ u32 offset;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
+ if (!hcd)
+ return -ENOMEM;
+
+ ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL);
+ if (ehci_mv == NULL) {
+ retval = -ENOMEM;
+ goto err_put_hcd;
+ }
+
+ platform_set_drvdata(pdev, ehci_mv);
+ ehci_mv->pdata = pdata;
+ ehci_mv->hcd = hcd;
+
+ ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ehci_mv->clk)) {
+ dev_err(&pdev->dev, "error getting clock\n");
+ retval = PTR_ERR(ehci_mv->clk);
+ goto err_put_hcd;
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
+ ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(ehci_mv->phy_regs)) {
+ retval = PTR_ERR(ehci_mv->phy_regs);
+ goto err_put_hcd;
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
+ ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(ehci_mv->cap_regs)) {
+ retval = PTR_ERR(ehci_mv->cap_regs);
+ goto err_put_hcd;
+ }
+
+ retval = mv_ehci_enable(ehci_mv);
+ if (retval) {
+ dev_err(&pdev->dev, "init phy error %d\n", retval);
+ goto err_put_hcd;
+ }
+
+ offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
+ ehci_mv->op_regs =
+ (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
+
+ hcd->rsrc_start = r->start;
+ hcd->rsrc_len = resource_size(r);
+ hcd->regs = ehci_mv->op_regs;
+
+ hcd->irq = platform_get_irq(pdev, 0);
+ if (!hcd->irq) {
+ dev_err(&pdev->dev, "Cannot get irq.");
+ retval = -ENODEV;
+ goto err_disable_clk;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
+
+ ehci_mv->mode = pdata->mode;
+ if (ehci_mv->mode == MV_USB_MODE_OTG) {
+ ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(ehci_mv->otg)) {
+ retval = PTR_ERR(ehci_mv->otg);
+
+ if (retval == -ENXIO)
+ dev_info(&pdev->dev, "MV_USB_MODE_OTG "
+ "must have CONFIG_USB_PHY enabled\n");
+ else
+ dev_err(&pdev->dev,
+ "unable to find transceiver\n");
+ goto err_disable_clk;
+ }
+
+ retval = otg_set_host(ehci_mv->otg->otg, &hcd->self);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "unable to register with transceiver\n");
+ retval = -ENODEV;
+ goto err_disable_clk;
+ }
+ /* otg will enable clock before use as host */
+ mv_ehci_disable(ehci_mv);
+ } else {
+ if (pdata->set_vbus)
+ pdata->set_vbus(1);
+
+ retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "failed to add hcd with err %d\n", retval);
+ goto err_set_vbus;
+ }
+ device_wakeup_enable(hcd->self.controller);
+ }
+
+ if (pdata->private_init)
+ pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
+
+ dev_info(&pdev->dev,
+ "successful find EHCI device with regs 0x%p irq %d"
+ " working in %s mode\n", hcd->regs, hcd->irq,
+ ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
+
+ return 0;
+
+err_set_vbus:
+ if (pdata->set_vbus)
+ pdata->set_vbus(0);
+err_disable_clk:
+ mv_ehci_disable(ehci_mv);
+err_put_hcd:
+ usb_put_hcd(hcd);
+
+ return retval;
+}
+
+static int mv_ehci_remove(struct platform_device *pdev)
+{
+ struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_mv->hcd;
+
+ if (hcd->rh_registered)
+ usb_remove_hcd(hcd);
+
+ if (!IS_ERR_OR_NULL(ehci_mv->otg))
+ otg_set_host(ehci_mv->otg->otg, NULL);
+
+ if (ehci_mv->mode == MV_USB_MODE_HOST) {
+ if (ehci_mv->pdata->set_vbus)
+ ehci_mv->pdata->set_vbus(0);
+
+ mv_ehci_disable(ehci_mv);
+ }
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+MODULE_ALIAS("mv-ehci");
+
+static const struct platform_device_id ehci_id_table[] = {
+ {"pxa-u2oehci", PXA_U2OEHCI},
+ {"pxa-sph", PXA_SPH},
+ {"mmp3-hsic", MMP3_HSIC},
+ {"mmp3-fsic", MMP3_FSIC},
+ {},
+};
+
+static void mv_ehci_shutdown(struct platform_device *pdev)
+{
+ struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_mv->hcd;
+
+ if (!hcd->rh_registered)
+ return;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver ehci_mv_driver = {
+ .probe = mv_ehci_probe,
+ .remove = mv_ehci_remove,
+ .shutdown = mv_ehci_shutdown,
+ .driver = {
+ .name = "mv-ehci",
+ .bus = &platform_bus_type,
+ },
+ .id_table = ehci_id_table,
+};
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
new file mode 100644
index 000000000..c7a9b31ee
--- /dev/null
+++ b/drivers/usb/host/ehci-mxc.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_data/usb-ehci-mxc.h>
+#include "ehci.h"
+
+#define DRIVER_DESC "Freescale On-Chip EHCI Host driver"
+
+static const char hcd_name[] = "ehci-mxc";
+
+#define ULPI_VIEWPORT_OFFSET 0x170
+
+struct ehci_mxc_priv {
+ struct clk *usbclk, *ahbclk, *phyclk;
+};
+
+static struct hc_driver __read_mostly ehci_mxc_hc_driver;
+
+static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = {
+ .extra_priv_size = sizeof(struct ehci_mxc_priv),
+};
+
+static int ehci_mxc_drv_probe(struct platform_device *pdev)
+{
+ struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq, ret;
+ struct ehci_mxc_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct ehci_hcd *ehci;
+
+ if (!pdata) {
+ dev_err(dev, "No platform data given, bailing out.\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+
+ hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto err_alloc;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ hcd->has_tt = 1;
+ ehci = hcd_to_ehci(hcd);
+ priv = (struct ehci_mxc_priv *) ehci->priv;
+
+ /* enable clocks */
+ priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(priv->usbclk)) {
+ ret = PTR_ERR(priv->usbclk);
+ goto err_alloc;
+ }
+ clk_prepare_enable(priv->usbclk);
+
+ priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(priv->ahbclk)) {
+ ret = PTR_ERR(priv->ahbclk);
+ goto err_clk_ahb;
+ }
+ clk_prepare_enable(priv->ahbclk);
+
+ /* "dr" device has its own clock on i.MX51 */
+ priv->phyclk = devm_clk_get(&pdev->dev, "phy");
+ if (IS_ERR(priv->phyclk))
+ priv->phyclk = NULL;
+ if (priv->phyclk)
+ clk_prepare_enable(priv->phyclk);
+
+
+ /* call platform specific init function */
+ if (pdata->init) {
+ ret = pdata->init(pdev);
+ if (ret) {
+ dev_err(dev, "platform init failed\n");
+ goto err_init;
+ }
+ /* platforms need some time to settle changed IO settings */
+ mdelay(10);
+ }
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ /* set up the PORTSCx register */
+ ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
+
+ /* is this really needed? */
+ msleep(10);
+
+ /* Initialize the transceiver */
+ if (pdata->otg) {
+ pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
+ ret = usb_phy_init(pdata->otg);
+ if (ret) {
+ dev_err(dev, "unable to init transceiver, probably missing\n");
+ ret = -ENODEV;
+ goto err_add;
+ }
+ ret = otg_set_vbus(pdata->otg->otg, 1);
+ if (ret) {
+ dev_err(dev, "unable to enable vbus on transceiver\n");
+ goto err_add;
+ }
+ }
+
+ platform_set_drvdata(pdev, hcd);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto err_add;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_add:
+ if (pdata && pdata->exit)
+ pdata->exit(pdev);
+err_init:
+ if (priv->phyclk)
+ clk_disable_unprepare(priv->phyclk);
+
+ clk_disable_unprepare(priv->ahbclk);
+err_clk_ahb:
+ clk_disable_unprepare(priv->usbclk);
+err_alloc:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int ehci_mxc_drv_remove(struct platform_device *pdev)
+{
+ struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata && pdata->exit)
+ pdata->exit(pdev);
+
+ if (pdata && pdata->otg)
+ usb_phy_shutdown(pdata->otg);
+
+ clk_disable_unprepare(priv->usbclk);
+ clk_disable_unprepare(priv->ahbclk);
+
+ if (priv->phyclk)
+ clk_disable_unprepare(priv->phyclk);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+MODULE_ALIAS("platform:mxc-ehci");
+
+static struct platform_driver ehci_mxc_driver = {
+ .probe = ehci_mxc_drv_probe,
+ .remove = ehci_mxc_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "mxc-ehci",
+ },
+};
+
+static int __init ehci_mxc_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides);
+ return platform_driver_register(&ehci_mxc_driver);
+}
+module_init(ehci_mxc_init);
+
+static void __exit ehci_mxc_cleanup(void)
+{
+ platform_driver_unregister(&ehci_mxc_driver);
+}
+module_exit(ehci_mxc_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sascha Hauer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
new file mode 100644
index 000000000..a24720beb
--- /dev/null
+++ b/drivers/usb/host/ehci-omap.c
@@ -0,0 +1,327 @@
+/*
+ * ehci-omap.c - driver for USBHOST on OMAP3/4 processors
+ *
+ * Bus Glue for the EHCI controllers in OMAP3/4
+ * Tested on several OMAP3 boards, and OMAP4 Pandaboard
+ *
+ * Copyright (C) 2007-2013 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ * Author: Anand Gadiyar <gadiyar@ti.com>
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/ulpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+
+#include "ehci.h"
+
+#include <linux/platform_data/usb-omap.h>
+
+/* EHCI Register Set */
+#define EHCI_INSNREG04 (0xA0)
+#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5)
+#define EHCI_INSNREG05_ULPI (0xA4)
+#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31
+#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24
+#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22
+#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16
+#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
+#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
+
+#define DRIVER_DESC "OMAP-EHCI Host Controller driver"
+
+static const char hcd_name[] = "ehci-omap";
+
+/*-------------------------------------------------------------------------*/
+
+struct omap_hcd {
+ struct usb_phy *phy[OMAP3_HS_USB_PORTS]; /* one PHY for each port */
+ int nports;
+};
+
+static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
+{
+ __raw_writel(val, base + reg);
+}
+
+static inline u32 ehci_read(void __iomem *base, u32 reg)
+{
+ return __raw_readl(base + reg);
+}
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+static struct hc_driver __read_mostly ehci_omap_hc_driver;
+
+static const struct ehci_driver_overrides ehci_omap_overrides __initdata = {
+ .extra_priv_size = sizeof(struct omap_hcd),
+};
+
+/**
+ * ehci_hcd_omap_probe - initialize TI-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int ehci_hcd_omap_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
+ struct resource *res;
+ struct usb_hcd *hcd;
+ void __iomem *regs;
+ int ret;
+ int irq;
+ int i;
+ struct omap_hcd *omap;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (!dev->parent) {
+ dev_err(dev, "Missing parent device\n");
+ return -ENODEV;
+ }
+
+ /* For DT boot, get platform data from parent. i.e. usbhshost */
+ if (dev->of_node) {
+ pdata = dev_get_platdata(dev->parent);
+ dev->platform_data = pdata;
+ }
+
+ if (!pdata) {
+ dev_err(dev, "Missing platform data\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "EHCI irq failed\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ ret = -ENODEV;
+ hcd = usb_create_hcd(&ehci_omap_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "Failed to create HCD\n");
+ return -ENOMEM;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+ hcd_to_ehci(hcd)->caps = regs;
+
+ omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv;
+ omap->nports = pdata->nports;
+
+ platform_set_drvdata(pdev, hcd);
+
+ /* get the PHY devices if needed */
+ for (i = 0 ; i < omap->nports ; i++) {
+ struct usb_phy *phy;
+
+ /* get the PHY device */
+ if (dev->of_node)
+ phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
+ else
+ phy = devm_usb_get_phy_dev(dev, i);
+ if (IS_ERR(phy)) {
+ /* Don't bail out if PHY is not absolutely necessary */
+ if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY)
+ continue;
+
+ ret = PTR_ERR(phy);
+ dev_err(dev, "Can't get PHY device for port %d: %d\n",
+ i, ret);
+ goto err_phy;
+ }
+
+ omap->phy[i] = phy;
+
+ if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_PHY) {
+ usb_phy_init(omap->phy[i]);
+ /* bring PHY out of suspend */
+ usb_phy_set_suspend(omap->phy[i], 0);
+ }
+ }
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ /*
+ * An undocumented "feature" in the OMAP3 EHCI controller,
+ * causes suspended ports to be taken out of suspend when
+ * the USBCMD.Run/Stop bit is cleared (for example when
+ * we do ehci_bus_suspend).
+ * This breaks suspend-resume if the root-hub is allowed
+ * to suspend. Writing 1 to this undocumented register bit
+ * disables this feature and restores normal behavior.
+ */
+ ehci_write(regs, EHCI_INSNREG04,
+ EHCI_INSNREG04_DISABLE_UNSUSPEND);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret) {
+ dev_err(dev, "failed to add hcd with err %d\n", ret);
+ goto err_pm_runtime;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ /*
+ * Bring PHYs out of reset for non PHY modes.
+ * Even though HSIC mode is a PHY-less mode, the reset
+ * line exists between the chips and can be modelled
+ * as a PHY device for reset control.
+ */
+ for (i = 0; i < omap->nports; i++) {
+ if (!omap->phy[i] ||
+ pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_PHY)
+ continue;
+
+ usb_phy_init(omap->phy[i]);
+ /* bring PHY out of suspend */
+ usb_phy_set_suspend(omap->phy[i], 0);
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_put_sync(dev);
+
+err_phy:
+ for (i = 0; i < omap->nports; i++) {
+ if (omap->phy[i])
+ usb_phy_shutdown(omap->phy[i]);
+ }
+
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+
+/**
+ * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static int ehci_hcd_omap_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct omap_hcd *omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv;
+ int i;
+
+ usb_remove_hcd(hcd);
+
+ for (i = 0; i < omap->nports; i++) {
+ if (omap->phy[i])
+ usb_phy_shutdown(omap->phy[i]);
+ }
+
+ usb_put_hcd(hcd);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static const struct of_device_id omap_ehci_dt_ids[] = {
+ { .compatible = "ti,ehci-omap" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids);
+
+static struct platform_driver ehci_hcd_omap_driver = {
+ .probe = ehci_hcd_omap_probe,
+ .remove = ehci_hcd_omap_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ /*.suspend = ehci_hcd_omap_suspend, */
+ /*.resume = ehci_hcd_omap_resume, */
+ .driver = {
+ .name = hcd_name,
+ .of_match_table = omap_ehci_dt_ids,
+ }
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init ehci_omap_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_omap_hc_driver, &ehci_omap_overrides);
+ return platform_driver_register(&ehci_hcd_omap_driver);
+}
+module_init(ehci_omap_init);
+
+static void __exit ehci_omap_cleanup(void)
+{
+ platform_driver_unregister(&ehci_hcd_omap_driver);
+}
+module_exit(ehci_omap_cleanup);
+
+MODULE_ALIAS("platform:ehci-omap");
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
new file mode 100644
index 000000000..bfcbb9aa8
--- /dev/null
+++ b/drivers/usb/host/ehci-orion.c
@@ -0,0 +1,347 @@
+/*
+ * drivers/usb/host/ehci-orion.c
+ *
+ * Tzachi Perelstein <tzachi@marvell.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mbus.h>
+#include <linux/clk.h>
+#include <linux/platform_data/usb-ehci-orion.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+#include "ehci.h"
+
+#define rdl(off) readl_relaxed(hcd->regs + (off))
+#define wrl(off, val) writel_relaxed((val), hcd->regs + (off))
+
+#define USB_CMD 0x140
+#define USB_CMD_RUN BIT(0)
+#define USB_CMD_RESET BIT(1)
+#define USB_MODE 0x1a8
+#define USB_MODE_MASK GENMASK(1, 0)
+#define USB_MODE_DEVICE 0x2
+#define USB_MODE_HOST 0x3
+#define USB_MODE_SDIS BIT(4)
+#define USB_CAUSE 0x310
+#define USB_MASK 0x314
+#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4))
+#define USB_WINDOW_BASE(i) (0x324 + ((i) << 4))
+#define USB_IPG 0x360
+#define USB_PHY_PWR_CTRL 0x400
+#define USB_PHY_TX_CTRL 0x420
+#define USB_PHY_RX_CTRL 0x430
+#define USB_PHY_IVREF_CTRL 0x440
+#define USB_PHY_TST_GRP_CTRL 0x450
+
+#define DRIVER_DESC "EHCI orion driver"
+
+#define hcd_to_orion_priv(h) ((struct orion_ehci_hcd *)hcd_to_ehci(h)->priv)
+
+struct orion_ehci_hcd {
+ struct clk *clk;
+ struct phy *phy;
+};
+
+static const char hcd_name[] = "ehci-orion";
+
+static struct hc_driver __read_mostly ehci_orion_hc_driver;
+
+/*
+ * Implement Orion USB controller specification guidelines
+ */
+static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)
+{
+ /* The below GLs are according to the Orion Errata document */
+ /*
+ * Clear interrupt cause and mask
+ */
+ wrl(USB_CAUSE, 0);
+ wrl(USB_MASK, 0);
+
+ /*
+ * Reset controller
+ */
+ wrl(USB_CMD, rdl(USB_CMD) | USB_CMD_RESET);
+ while (rdl(USB_CMD) & USB_CMD_RESET);
+
+ /*
+ * GL# USB-10: Set IPG for non start of frame packets
+ * Bits[14:8]=0xc
+ */
+ wrl(USB_IPG, (rdl(USB_IPG) & ~0x7f00) | 0xc00);
+
+ /*
+ * GL# USB-9: USB 2.0 Power Control
+ * BG_VSEL[7:6]=0x1
+ */
+ wrl(USB_PHY_PWR_CTRL, (rdl(USB_PHY_PWR_CTRL) & ~0xc0)| 0x40);
+
+ /*
+ * GL# USB-1: USB PHY Tx Control - force calibration to '8'
+ * TXDATA_BLOCK_EN[21]=0x1, EXT_RCAL_EN[13]=0x1, IMP_CAL[6:3]=0x8
+ */
+ wrl(USB_PHY_TX_CTRL, (rdl(USB_PHY_TX_CTRL) & ~0x78) | 0x202040);
+
+ /*
+ * GL# USB-3 GL# USB-9: USB PHY Rx Control
+ * RXDATA_BLOCK_LENGHT[31:30]=0x3, EDGE_DET_SEL[27:26]=0,
+ * CDR_FASTLOCK_EN[21]=0, DISCON_THRESHOLD[9:8]=0, SQ_THRESH[7:4]=0x1
+ */
+ wrl(USB_PHY_RX_CTRL, (rdl(USB_PHY_RX_CTRL) & ~0xc2003f0) | 0xc0000010);
+
+ /*
+ * GL# USB-3 GL# USB-9: USB PHY IVREF Control
+ * PLLVDD12[1:0]=0x2, RXVDD[5:4]=0x3, Reserved[19]=0
+ */
+ wrl(USB_PHY_IVREF_CTRL, (rdl(USB_PHY_IVREF_CTRL) & ~0x80003 ) | 0x32);
+
+ /*
+ * GL# USB-3 GL# USB-9: USB PHY Test Group Control
+ * REG_FIFO_SQ_RST[15]=0
+ */
+ wrl(USB_PHY_TST_GRP_CTRL, rdl(USB_PHY_TST_GRP_CTRL) & ~0x8000);
+
+ /*
+ * Stop and reset controller
+ */
+ wrl(USB_CMD, rdl(USB_CMD) & ~USB_CMD_RUN);
+ wrl(USB_CMD, rdl(USB_CMD) | USB_CMD_RESET);
+ while (rdl(USB_CMD) & USB_CMD_RESET);
+
+ /*
+ * GL# USB-5 Streaming disable REG_USB_MODE[4]=1
+ * TBD: This need to be done after each reset!
+ * GL# USB-4 Setup USB Host mode
+ */
+ wrl(USB_MODE, USB_MODE_SDIS | USB_MODE_HOST);
+}
+
+static void
+ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
+ const struct mbus_dram_target_info *dram)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ wrl(USB_WINDOW_CTRL(i), 0);
+ wrl(USB_WINDOW_BASE(i), 0);
+ }
+
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ wrl(USB_WINDOW_CTRL(i), ((cs->size - 1) & 0xffff0000) |
+ (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1);
+ wrl(USB_WINDOW_BASE(i), cs->base);
+ }
+}
+
+static const struct ehci_driver_overrides orion_overrides __initconst = {
+ .extra_priv_size = sizeof(struct orion_ehci_hcd),
+};
+
+static int ehci_orion_drv_probe(struct platform_device *pdev)
+{
+ struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev);
+ const struct mbus_dram_target_info *dram;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ void __iomem *regs;
+ int irq, err;
+ enum orion_ehci_phy_ver phy_version;
+ struct orion_ehci_hcd *priv;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Orion-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ err = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask
+ * set. Since shared usb code relies on it, set it here for
+ * now. Once we have dma capability bindings this can go away.
+ */
+ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ goto err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs)) {
+ err = PTR_ERR(regs);
+ goto err;
+ }
+
+ hcd = usb_create_hcd(&ehci_orion_hc_driver,
+ &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs + 0x100;
+ hcd->has_tt = 1;
+
+ priv = hcd_to_orion_priv(hcd);
+ /*
+ * Not all platforms can gate the clock, so it is not an error if
+ * the clock does not exists.
+ */
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+
+ priv->phy = devm_phy_optional_get(&pdev->dev, "usb");
+ if (IS_ERR(priv->phy)) {
+ err = PTR_ERR(priv->phy);
+ goto err_phy_get;
+ } else {
+ err = phy_init(priv->phy);
+ if (err)
+ goto err_phy_init;
+
+ err = phy_power_on(priv->phy);
+ if (err)
+ goto err_phy_power_on;
+ }
+
+ /*
+ * (Re-)program MBUS remapping windows if we are asked to.
+ */
+ dram = mv_mbus_dram_info();
+ if (dram)
+ ehci_orion_conf_mbus_windows(hcd, dram);
+
+ /*
+ * setup Orion USB controller.
+ */
+ if (pdev->dev.of_node)
+ phy_version = EHCI_PHY_NA;
+ else
+ phy_version = pd->phy_version;
+
+ switch (phy_version) {
+ case EHCI_PHY_NA: /* dont change USB phy settings */
+ break;
+ case EHCI_PHY_ORION:
+ orion_usb_phy_v1_setup(hcd);
+ break;
+ case EHCI_PHY_DD:
+ case EHCI_PHY_KW:
+ default:
+ dev_warn(&pdev->dev, "USB phy version isn't supported.\n");
+ }
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_add_hcd;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_add_hcd:
+ if (!IS_ERR(priv->phy))
+ phy_power_off(priv->phy);
+err_phy_power_on:
+ if (!IS_ERR(priv->phy))
+ phy_exit(priv->phy);
+err_phy_init:
+err_phy_get:
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ usb_put_hcd(hcd);
+err:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), err);
+
+ return err;
+}
+
+static int ehci_orion_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd);
+
+ usb_remove_hcd(hcd);
+
+ if (!IS_ERR(priv->phy)) {
+ phy_power_off(priv->phy);
+ phy_exit(priv->phy);
+ }
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id ehci_orion_dt_ids[] = {
+ { .compatible = "marvell,orion-ehci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids);
+
+static struct platform_driver ehci_orion_driver = {
+ .probe = ehci_orion_drv_probe,
+ .remove = ehci_orion_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "orion-ehci",
+ .of_match_table = ehci_orion_dt_ids,
+ },
+};
+
+static int __init ehci_orion_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_orion_hc_driver, &orion_overrides);
+ return platform_driver_register(&ehci_orion_driver);
+}
+module_init(ehci_orion_init);
+
+static void __exit ehci_orion_cleanup(void)
+{
+ platform_driver_unregister(&ehci_orion_driver);
+}
+module_exit(ehci_orion_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:orion-ehci");
+MODULE_AUTHOR("Tzachi Perelstein");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
new file mode 100644
index 000000000..2a5d2fd76
--- /dev/null
+++ b/drivers/usb/host/ehci-pci.c
@@ -0,0 +1,435 @@
+/*
+ * EHCI HCD (Host Controller Driver) PCI Bus Glue.
+ *
+ * Copyright (c) 2000-2004 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+#include "pci-quirks.h"
+
+#define DRIVER_DESC "EHCI PCI platform driver"
+
+static const char hcd_name[] = "ehci-pci";
+
+/* defined here to avoid adding to pci_ids.h for single instance use */
+#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
+
+/*-------------------------------------------------------------------------*/
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939
+static inline bool is_intel_quark_x1000(struct pci_dev *pdev)
+{
+ return pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC;
+}
+
+/*
+ * This is the list of PCI IDs for the devices that have EHCI USB class and
+ * specific drivers for that. One of the example is a ChipIdea device installed
+ * on some Intel MID platforms.
+ */
+static const struct pci_device_id bypass_pci_id_table[] = {
+ /* ChipIdea on Intel MID platform */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), },
+ {}
+};
+
+static inline bool is_bypassed_id(struct pci_dev *pdev)
+{
+ return !!pci_match_id(bypass_pci_id_table, pdev);
+}
+
+/*
+ * 0x84 is the offset of in/out threshold register,
+ * and it is the same offset as the register of 'hostpc'.
+ */
+#define intel_quark_x1000_insnreg01 hostpc
+
+/* Maximum usable threshold value is 0x7f dwords for both IN and OUT */
+#define INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD 0x007f007f
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
+{
+ int retval;
+
+ /* we expect static quirk code to handle the "extended capabilities"
+ * (currently just BIOS handoff) allowed starting with EHCI 0.96
+ */
+
+ /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
+ retval = pci_set_mwi(pdev);
+ if (!retval)
+ ehci_dbg(ehci, "MWI active\n");
+
+ /* Reset the threshold limit */
+ if (is_intel_quark_x1000(pdev)) {
+ /*
+ * For the Intel QUARK X1000, raise the I/O threshold to the
+ * maximum usable value in order to improve performance.
+ */
+ ehci_writel(ehci, INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD,
+ ehci->regs->intel_quark_x1000_insnreg01);
+ }
+
+ return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_pci_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ u32 temp;
+ int retval;
+
+ ehci->caps = hcd->regs;
+
+ /*
+ * ehci_init() causes memory for DMA transfers to be
+ * allocated. Thus, any vendor-specific workarounds based on
+ * limiting the type of memory used for DMA transfers must
+ * happen before ehci_setup() is called.
+ *
+ * Most other workarounds can be done either before or after
+ * init and reset; they are located here too.
+ */
+ switch (pdev->vendor) {
+ case PCI_VENDOR_ID_TOSHIBA_2:
+ /* celleb's companion chip */
+ if (pdev->device == 0x01b5) {
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+ ehci->big_endian_mmio = 1;
+#else
+ ehci_warn(ehci,
+ "unsupported big endian Toshiba quirk\n");
+#endif
+ }
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ /* NVidia reports that certain chips don't handle
+ * QH, ITD, or SITD addresses above 2GB. (But TD,
+ * data buffer, and periodic schedule are normal.)
+ */
+ switch (pdev->device) {
+ case 0x003c: /* MCP04 */
+ case 0x005b: /* CK804 */
+ case 0x00d8: /* CK8 */
+ case 0x00e8: /* CK8S */
+ if (pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(31)) < 0)
+ ehci_warn(ehci, "can't enable NVidia "
+ "workaround for >2GB RAM\n");
+ break;
+
+ /* Some NForce2 chips have problems with selective suspend;
+ * fixed in newer silicon.
+ */
+ case 0x0068:
+ if (pdev->revision < 0xa4)
+ ehci->no_selective_suspend = 1;
+ break;
+ }
+ break;
+ case PCI_VENDOR_ID_INTEL:
+ if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB)
+ hcd->has_tt = 1;
+ break;
+ case PCI_VENDOR_ID_TDI:
+ if (pdev->device == PCI_DEVICE_ID_TDI_EHCI)
+ hcd->has_tt = 1;
+ break;
+ case PCI_VENDOR_ID_AMD:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
+ /* AMD8111 EHCI doesn't work, according to AMD errata */
+ if (pdev->device == 0x7463) {
+ ehci_info(ehci, "ignoring AMD8111 (errata)\n");
+ retval = -EIO;
+ goto done;
+ }
+
+ /*
+ * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
+ * read/write memory space which does not belong to it when
+ * there is NULL pointer with T-bit set to 1 in the frame list
+ * table. To avoid the issue, the frame list link pointer
+ * should always contain a valid pointer to a inactive qh.
+ */
+ if (pdev->device == 0x7808) {
+ ehci->use_dummy_qh = 1;
+ ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
+ }
+ break;
+ case PCI_VENDOR_ID_VIA:
+ if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x60) {
+ u8 tmp;
+
+ /* The VT6212 defaults to a 1 usec EHCI sleep time which
+ * hogs the PCI bus *badly*. Setting bit 5 of 0x4B makes
+ * that sleep time use the conventional 10 usec.
+ */
+ pci_read_config_byte(pdev, 0x4b, &tmp);
+ if (tmp & 0x20)
+ break;
+ pci_write_config_byte(pdev, 0x4b, tmp | 0x20);
+ }
+ break;
+ case PCI_VENDOR_ID_ATI:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
+
+ /*
+ * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
+ * read/write memory space which does not belong to it when
+ * there is NULL pointer with T-bit set to 1 in the frame list
+ * table. To avoid the issue, the frame list link pointer
+ * should always contain a valid pointer to a inactive qh.
+ */
+ if (pdev->device == 0x4396) {
+ ehci->use_dummy_qh = 1;
+ ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
+ }
+ /* SB600 and old version of SB700 have a bug in EHCI controller,
+ * which causes usb devices lose response in some cases.
+ */
+ if ((pdev->device == 0x4386 || pdev->device == 0x4396) &&
+ usb_amd_hang_symptom_quirk()) {
+ u8 tmp;
+ ehci_info(ehci, "applying AMD SB600/SB700 USB freeze workaround\n");
+ pci_read_config_byte(pdev, 0x53, &tmp);
+ pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
+ }
+ break;
+ case PCI_VENDOR_ID_NETMOS:
+ /* MosChip frame-index-register bug */
+ ehci_info(ehci, "applying MosChip frame-index workaround\n");
+ ehci->frame_index_bug = 1;
+ break;
+ }
+
+ /* optional debug port, normally in the first BAR */
+ temp = pci_find_capability(pdev, PCI_CAP_ID_DBG);
+ if (temp) {
+ pci_read_config_dword(pdev, temp, &temp);
+ temp >>= 16;
+ if (((temp >> 13) & 7) == 1) {
+ u32 hcs_params = ehci_readl(ehci,
+ &ehci->caps->hcs_params);
+
+ temp &= 0x1fff;
+ ehci->debug = hcd->regs + temp;
+ temp = ehci_readl(ehci, &ehci->debug->control);
+ ehci_info(ehci, "debug port %d%s\n",
+ HCS_DEBUG_PORT(hcs_params),
+ (temp & DBGP_ENABLED) ? " IN USE" : "");
+ if (!(temp & DBGP_ENABLED))
+ ehci->debug = NULL;
+ }
+ }
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ /* These workarounds need to be applied after ehci_setup() */
+ switch (pdev->vendor) {
+ case PCI_VENDOR_ID_NEC:
+ ehci->need_io_watchdog = 0;
+ break;
+ case PCI_VENDOR_ID_INTEL:
+ ehci->need_io_watchdog = 0;
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ switch (pdev->device) {
+ /* MCP89 chips on the MacBookAir3,1 give EPROTO when
+ * fetching device descriptors unless LPM is disabled.
+ * There are also intermittent problems enumerating
+ * devices with PPCD enabled.
+ */
+ case 0x0d9d:
+ ehci_info(ehci, "disable ppcd for nvidia mcp89\n");
+ ehci->has_ppcd = 0;
+ ehci->command &= ~CMD_PPCEE;
+ break;
+ }
+ break;
+ }
+
+ /* at least the Genesys GL880S needs fixup here */
+ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
+ temp &= 0x0f;
+ if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) {
+ ehci_dbg(ehci, "bogus port configuration: "
+ "cc=%d x pcc=%d < ports=%d\n",
+ HCS_N_CC(ehci->hcs_params),
+ HCS_N_PCC(ehci->hcs_params),
+ HCS_N_PORTS(ehci->hcs_params));
+
+ switch (pdev->vendor) {
+ case 0x17a0: /* GENESYS */
+ /* GL880S: should be PORTS=2 */
+ temp |= (ehci->hcs_params & ~0xf);
+ ehci->hcs_params = temp;
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ /* NF4: should be PCC=10 */
+ break;
+ }
+ }
+
+ /* Serial Bus Release Number is at PCI 0x60 offset */
+ if (pdev->vendor == PCI_VENDOR_ID_STMICRO
+ && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST)
+ ; /* ConneXT has no sbrn register */
+ else
+ pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
+
+ /* Keep this around for a while just in case some EHCI
+ * implementation uses legacy PCI PM support. This test
+ * can be removed on 17 Dec 2009 if the dev_warn() hasn't
+ * been triggered by then.
+ */
+ if (!device_can_wakeup(&pdev->dev)) {
+ u16 port_wake;
+
+ pci_read_config_word(pdev, 0x62, &port_wake);
+ if (port_wake & 0x0001) {
+ dev_warn(&pdev->dev, "Enabling legacy PCI PM\n");
+ device_set_wakeup_capable(&pdev->dev, 1);
+ }
+ }
+
+#ifdef CONFIG_PM
+ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev))
+ ehci_warn(ehci, "selective suspend/wakeup unavailable\n");
+#endif
+
+ retval = ehci_pci_reinit(ehci, pdev);
+done:
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+/* These routines rely on the PCI bus glue
+ * to handle powerdown and wakeup, and currently also on
+ * transceivers that don't need any software attention to set up
+ * the right sort of wakeup.
+ * Also they depend on separate root hub suspend/resume.
+ */
+
+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);
+
+ if (ehci_resume(hcd, hibernated) != 0)
+ (void) ehci_pci_reinit(ehci, pdev);
+ return 0;
+}
+
+#else
+
+#define ehci_suspend NULL
+#define ehci_pci_resume NULL
+#endif /* CONFIG_PM */
+
+static struct hc_driver __read_mostly ehci_pci_hc_driver;
+
+static const struct ehci_driver_overrides pci_overrides __initconst = {
+ .reset = ehci_pci_setup,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ if (is_bypassed_id(pdev))
+ return -ENODEV;
+ return usb_hcd_pci_probe(pdev, id);
+}
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id pci_ids [] = { {
+ /* handle any USB 2.0 EHCI controller */
+ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
+ .driver_data = (unsigned long) &ehci_pci_hc_driver,
+ }, {
+ PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST),
+ .driver_data = (unsigned long) &ehci_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 ehci_pci_driver = {
+ .name = (char *) hcd_name,
+ .id_table = pci_ids,
+
+ .probe = ehci_pci_probe,
+ .remove = usb_hcd_pci_remove,
+ .shutdown = usb_hcd_pci_shutdown,
+
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &usb_hcd_pci_pm_ops
+ },
+#endif
+};
+
+static int __init ehci_pci_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_pci_hc_driver, &pci_overrides);
+
+ /* Entries for the PCI suspend/resume callbacks are special */
+ ehci_pci_hc_driver.pci_suspend = ehci_suspend;
+ ehci_pci_hc_driver.pci_resume = ehci_pci_resume;
+
+ return pci_register_driver(&ehci_pci_driver);
+}
+module_init(ehci_pci_init);
+
+static void __exit ehci_pci_cleanup(void)
+{
+ pci_unregister_driver(&ehci_pci_driver);
+}
+module_exit(ehci_pci_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_AUTHOR("Alan Stern");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
new file mode 100644
index 000000000..d8a75a51d
--- /dev/null
+++ b/drivers/usb/host/ehci-platform.c
@@ -0,0 +1,442 @@
+/*
+ * Generic platform ehci driver
+ *
+ * Copyright 2007 Steven Brown <sbrown@cortland.com>
+ * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Derived from the ohci-ssb driver
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ *
+ * Derived from the EHCI-PCI driver
+ * Copyright (c) 2000-2004 by David Brownell
+ *
+ * Derived from the ohci-pci driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ehci_pdriver.h>
+
+#include "ehci.h"
+
+#define DRIVER_DESC "EHCI generic platform driver"
+#define EHCI_MAX_CLKS 3
+#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
+
+struct ehci_platform_priv {
+ struct clk *clks[EHCI_MAX_CLKS];
+ struct reset_control *rst;
+ struct phy **phys;
+ int num_phys;
+};
+
+static const char hcd_name[] = "ehci-platform";
+
+static int ehci_platform_reset(struct usb_hcd *hcd)
+{
+ struct platform_device *pdev = to_platform_device(hcd->self.controller);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ hcd->has_tt = pdata->has_tt;
+ ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
+
+ if (pdata->pre_setup) {
+ retval = pdata->pre_setup(hcd);
+ if (retval < 0)
+ return retval;
+ }
+
+ ehci->caps = hcd->regs + pdata->caps_offset;
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ if (pdata->no_io_watchdog)
+ ehci->need_io_watchdog = 0;
+ return 0;
+}
+
+static int ehci_platform_power_on(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk, ret, phy_num;
+
+ for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
+ ret = clk_prepare_enable(priv->clks[clk]);
+ if (ret)
+ goto err_disable_clks;
+ }
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ ret = phy_init(priv->phys[phy_num]);
+ if (ret)
+ goto err_exit_phy;
+ ret = phy_power_on(priv->phys[phy_num]);
+ if (ret) {
+ phy_exit(priv->phys[phy_num]);
+ goto err_exit_phy;
+ }
+ }
+ }
+
+ return 0;
+
+err_exit_phy:
+ while (--phy_num >= 0) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(priv->clks[clk]);
+
+ return ret;
+}
+
+static void ehci_platform_power_off(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk, phy_num;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
+
+ for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
+ if (priv->clks[clk])
+ clk_disable_unprepare(priv->clks[clk]);
+}
+
+static struct hc_driver __read_mostly ehci_platform_hc_driver;
+
+static const struct ehci_driver_overrides platform_overrides __initconst = {
+ .reset = ehci_platform_reset,
+ .extra_priv_size = sizeof(struct ehci_platform_priv),
+};
+
+static struct usb_ehci_pdata ehci_platform_defaults = {
+ .power_on = ehci_platform_power_on,
+ .power_suspend = ehci_platform_power_off,
+ .power_off = ehci_platform_power_off,
+};
+
+static int ehci_platform_probe(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_mem;
+ struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct ehci_platform_priv *priv;
+ struct ehci_hcd *ehci;
+ const char *phy_name;
+ int err, irq, phy_num, clk = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Use reasonable defaults so platforms don't have to provide these
+ * with DT probing on ARM.
+ */
+ if (!pdata)
+ pdata = &ehci_platform_defaults;
+
+ err = dma_coerce_mask_and_coherent(&dev->dev,
+ pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ dev_err(&dev->dev, "no irq provided");
+ return irq;
+ }
+
+ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(dev, hcd);
+ dev->dev.platform_data = pdata;
+ priv = hcd_to_ehci_priv(hcd);
+ ehci = hcd_to_ehci(hcd);
+
+ if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
+ if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+ ehci->big_endian_mmio = 1;
+
+ if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+ ehci->big_endian_desc = 1;
+
+ if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+ ehci->big_endian_mmio = ehci->big_endian_desc = 1;
+
+ if (of_property_read_bool(dev->dev.of_node,
+ "needs-reset-on-resume"))
+ pdata->reset_on_resume = 1;
+
+ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
+ "phys", "#phy-cells");
+ priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
+
+ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
+ sizeof(struct phy *), GFP_KERNEL);
+ if (!priv->phys)
+ return -ENOMEM;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ err = of_property_read_string_index(
+ dev->dev.of_node,
+ "phy-names", phy_num,
+ &phy_name);
+
+ if (err < 0) {
+ if (priv->num_phys > 1) {
+ dev_err(&dev->dev, "phy-names not provided");
+ goto err_put_hcd;
+ } else
+ phy_name = "usb";
+ }
+
+ priv->phys[phy_num] = devm_phy_get(&dev->dev,
+ phy_name);
+ if (IS_ERR(priv->phys[phy_num])) {
+ err = PTR_ERR(priv->phys[phy_num]);
+ if ((priv->num_phys > 1) ||
+ (err == -EPROBE_DEFER))
+ goto err_put_hcd;
+ priv->phys[phy_num] = NULL;
+ }
+ }
+
+ for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
+ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+ if (IS_ERR(priv->clks[clk])) {
+ err = PTR_ERR(priv->clks[clk]);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->clks[clk] = NULL;
+ break;
+ }
+ }
+ }
+
+ priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ err = PTR_ERR(priv->rst);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->rst = NULL;
+ } else {
+ err = reset_control_deassert(priv->rst);
+ if (err)
+ goto err_put_clks;
+ }
+
+ if (pdata->big_endian_desc)
+ ehci->big_endian_desc = 1;
+ if (pdata->big_endian_mmio)
+ ehci->big_endian_mmio = 1;
+
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+ if (ehci->big_endian_mmio) {
+ dev_err(&dev->dev,
+ "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n");
+ err = -EINVAL;
+ goto err_reset;
+ }
+#endif
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+ if (ehci->big_endian_desc) {
+ dev_err(&dev->dev,
+ "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n");
+ err = -EINVAL;
+ goto err_reset;
+ }
+#endif
+
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ goto err_reset;
+ }
+
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto err_power;
+ }
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_power;
+
+ device_wakeup_enable(hcd->self.controller);
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+err_reset:
+ if (priv->rst)
+ reset_control_assert(priv->rst);
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(priv->clks[clk]);
+err_put_hcd:
+ if (pdata == &ehci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ usb_put_hcd(hcd);
+
+ return err;
+}
+
+static int ehci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
+ if (priv->rst)
+ reset_control_assert(priv->rst);
+
+ for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
+ clk_put(priv->clks[clk]);
+
+ usb_put_hcd(hcd);
+
+ if (pdata == &ehci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_platform_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = ehci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ return ret;
+}
+
+static int ehci_platform_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
+
+ ehci_resume(hcd, pdata->reset_on_resume);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id vt8500_ehci_ids[] = {
+ { .compatible = "via,vt8500-ehci", },
+ { .compatible = "wm,prizm-ehci", },
+ { .compatible = "generic-ehci", },
+ { .compatible = "cavium,octeon-6335-ehci", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
+
+static const struct platform_device_id ehci_platform_table[] = {
+ { "ehci-platform", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ehci_platform_table);
+
+static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend,
+ ehci_platform_resume);
+
+static struct platform_driver ehci_platform_driver = {
+ .id_table = ehci_platform_table,
+ .probe = ehci_platform_probe,
+ .remove = ehci_platform_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ehci-platform",
+ .pm = &ehci_platform_pm_ops,
+ .of_match_table = vt8500_ehci_ids,
+ }
+};
+
+static int __init ehci_platform_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
+ return platform_driver_register(&ehci_platform_driver);
+}
+module_init(ehci_platform_init);
+
+static void __exit ehci_platform_cleanup(void)
+{
+ platform_driver_unregister(&ehci_platform_driver);
+}
+module_exit(ehci_platform_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_AUTHOR("Alan Stern");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c
new file mode 100644
index 000000000..342816a7f
--- /dev/null
+++ b/drivers/usb/host/ehci-pmcmsp.c
@@ -0,0 +1,329 @@
+/*
+ * PMC MSP EHCI (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2006-2010 PMC-Sierra Inc
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+/* includes */
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/usb.h>
+#include <msp_usb.h>
+
+/* stream disable*/
+#define USB_CTRL_MODE_STREAM_DISABLE 0x10
+
+/* threshold */
+#define USB_CTRL_FIFO_THRESH 0x00300000
+
+/* register offset for usb_mode */
+#define USB_EHCI_REG_USB_MODE 0x68
+
+/* register offset for usb fifo */
+#define USB_EHCI_REG_USB_FIFO 0x24
+
+/* register offset for usb status */
+#define USB_EHCI_REG_USB_STATUS 0x44
+
+/* serial/parallel transceiver */
+#define USB_EHCI_REG_BIT_STAT_STS (1<<29)
+
+/* TWI USB0 host device pin */
+#define MSP_PIN_USB0_HOST_DEV 49
+
+/* TWI USB1 host device pin */
+#define MSP_PIN_USB1_HOST_DEV 50
+
+
+static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
+{
+ u8 *base;
+ u8 *statreg;
+ u8 *fiforeg;
+ u32 val;
+ struct ehci_regs *reg_base = ehci->regs;
+
+ /* get register base */
+ base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
+ statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
+ fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
+
+ /* Disable controller mode stream */
+ val = ehci_readl(ehci, (u32 *)base);
+ ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
+ (u32 *)base);
+
+ /* clear STS to select parallel transceiver interface */
+ val = ehci_readl(ehci, (u32 *)statreg);
+ val = val & ~USB_EHCI_REG_BIT_STAT_STS;
+ ehci_writel(ehci, val, (u32 *)statreg);
+
+ /* write to set the proper fifo threshold */
+ ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
+
+ /* set TWI GPIO USB_HOST_DEV pin high */
+ gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_msp_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+ ehci->caps = hcd->regs;
+ hcd->has_tt = 1;
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ usb_hcd_tdi_set_mode(ehci);
+
+ return retval;
+}
+
+
+/* configure so an HC device and id are always provided
+ * always called with process context; sleeping is OK
+ */
+
+static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
+{
+ struct resource *res;
+ struct platform_device *pdev = &dev->dev;
+ u32 res_len;
+ int retval;
+
+ /* MAB register space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL)
+ return -ENOMEM;
+ res_len = resource_size(res);
+ if (!request_mem_region(res->start, res_len, "mab regs"))
+ return -EBUSY;
+
+ dev->mab_regs = ioremap_nocache(res->start, res_len);
+ if (dev->mab_regs == NULL) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ /* MSP USB register space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (res == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+ res_len = resource_size(res);
+ if (!request_mem_region(res->start, res_len, "usbid regs")) {
+ retval = -EBUSY;
+ goto err2;
+ }
+ dev->usbid_regs = ioremap_nocache(res->start, res_len);
+ if (dev->usbid_regs == NULL) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ return 0;
+err3:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ res_len = resource_size(res);
+ release_mem_region(res->start, res_len);
+err2:
+ iounmap(dev->mab_regs);
+err1:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ res_len = resource_size(res);
+ release_mem_region(res->start, res_len);
+ dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
+ return retval;
+}
+
+/**
+ * usb_hcd_msp_probe - initialize PMC MSP-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_msp_probe(const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ struct ehci_hcd *ehci ;
+
+ hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_debug("No IOMEM resource info for %s.\n", dev->name);
+ retval = -ENOMEM;
+ goto err1;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
+ retval = -EBUSY;
+ goto err1;
+ }
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ /* Map non-EHCI register spaces */
+ retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
+ if (retval != 0)
+ goto err3;
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+
+ retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
+ if (retval == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+ }
+
+ usb_remove_hcd(hcd);
+err3:
+ iounmap(hcd->regs);
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+
+ return retval;
+}
+
+
+
+/**
+ * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_msp_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * may be called without controller electrically present
+ * may be called with controller, bus, and devices active
+ */
+void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+static const struct hc_driver ehci_msp_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PMC MSP EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_msp_setup,
+ .shutdown = ehci_shutdown,
+ .start = ehci_run,
+ .stop = ehci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ pr_debug("In ehci_hcd_msp_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
+
+ ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
+
+ return ret;
+}
+
+static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_msp_remove(hcd, pdev);
+
+ /* free TWI GPIO USB_HOST_DEV pin */
+ gpio_free(MSP_PIN_USB0_HOST_DEV);
+
+ return 0;
+}
+
+MODULE_ALIAS("pmcmsp-ehci");
+
+static struct platform_driver ehci_hcd_msp_driver = {
+ .probe = ehci_hcd_msp_drv_probe,
+ .remove = ehci_hcd_msp_drv_remove,
+ .driver = {
+ .name = "pmcmsp-ehci",
+ },
+};
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
new file mode 100644
index 000000000..1a10c8d54
--- /dev/null
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -0,0 +1,239 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus
+ * Tested on AMCC PPC 440EPx
+ *
+ * Valentine Barshak <vbarshak@ru.mvista.com>
+ *
+ * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
+ * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/err.h>
+#include <linux/signal.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+
+static const struct hc_driver ehci_ppc_of_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OF EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+
+/*
+ * 440EPx Errata USBH_3
+ * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
+ */
+#define PPC440EPX_EHCI0_INSREG_BMT (0x1 << 0)
+static int
+ppc44x_enable_bmt(struct device_node *dn)
+{
+ __iomem u32 *insreg_virt;
+
+ insreg_virt = of_iomap(dn, 1);
+ if (!insreg_virt)
+ return -EINVAL;
+
+ out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT);
+
+ iounmap(insreg_virt);
+ return 0;
+}
+
+
+static int ehci_hcd_ppc_of_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci = NULL;
+ struct resource res;
+ int irq;
+ int rv;
+
+ struct device_node *np;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = resource_size(&res);
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
+ __FILE__);
+ rv = -EBUSY;
+ goto err_irq;
+ }
+
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_ioremap;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
+ if (np != NULL) {
+ /* claim we really affected by usb23 erratum */
+ if (!of_address_to_resource(np, 0, &res))
+ ehci->ohci_hcctrl_reg =
+ devm_ioremap(&op->dev,
+ res.start + OHCI_HCCTRL_OFFSET,
+ OHCI_HCCTRL_LEN);
+ else
+ pr_debug("%s: no ohci offset in fdt\n", __FILE__);
+ if (!ehci->ohci_hcctrl_reg) {
+ pr_debug("%s: ioremap for ohci hcctrl failed\n", __FILE__);
+ } else {
+ ehci->has_amcc_usb23 = 1;
+ }
+ }
+
+ if (of_get_property(dn, "big-endian", NULL)) {
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+ }
+ if (of_get_property(dn, "big-endian-regs", NULL))
+ ehci->big_endian_mmio = 1;
+ if (of_get_property(dn, "big-endian-desc", NULL))
+ ehci->big_endian_desc = 1;
+
+ ehci->caps = hcd->regs;
+
+ if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
+ rv = ppc44x_enable_bmt(dn);
+ ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n",
+ rv ? "NOT ": "");
+ }
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv)
+ goto err_ioremap;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_ioremap:
+ irq_dispose_mapping(irq);
+err_irq:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+
+static int ehci_hcd_ppc_of_remove(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ struct device_node *np;
+ struct resource res;
+
+ dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ irq_dispose_mapping(hcd->irq);
+
+ /* use request_mem_region to test if the ohci driver is loaded. if so
+ * ensure the ohci core is operational.
+ */
+ if (ehci->has_amcc_usb23) {
+ np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
+ if (np != NULL) {
+ if (!of_address_to_resource(np, 0, &res))
+ if (!request_mem_region(res.start,
+ 0x4, hcd_name))
+ set_ohci_hcfs(ehci, 1);
+ else
+ release_mem_region(res.start, 0x4);
+ else
+ pr_debug("%s: no ohci offset in fdt\n", __FILE__);
+ of_node_put(np);
+ }
+ }
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+
+static const struct of_device_id ehci_hcd_ppc_of_match[] = {
+ {
+ .compatible = "usb-ehci",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
+
+
+static struct platform_driver ehci_hcd_ppc_of_driver = {
+ .probe = ehci_hcd_ppc_of_probe,
+ .remove = ehci_hcd_ppc_of_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ppc-of-ehci",
+ .of_match_table = ehci_hcd_ppc_of_match,
+ },
+};
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
new file mode 100644
index 000000000..7934ff9b3
--- /dev/null
+++ b/drivers/usb/host/ehci-ps3.c
@@ -0,0 +1,266 @@
+/*
+ * PS3 EHCI Host Controller driver
+ *
+ * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright 2006 Sony Corp.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/firmware.h>
+#include <asm/ps3.h>
+
+static void ps3_ehci_setup_insnreg(struct ehci_hcd *ehci)
+{
+ /* PS3 HC internal setup register offsets. */
+
+ enum ps3_ehci_hc_insnreg {
+ ps3_ehci_hc_insnreg01 = 0x084,
+ ps3_ehci_hc_insnreg02 = 0x088,
+ ps3_ehci_hc_insnreg03 = 0x08c,
+ };
+
+ /* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its
+ * internal INSNREGXX setup regs back to the chip default values
+ * on Host Controller Reset (CMD_RESET) or Light Host Controller
+ * Reset (CMD_LRESET). The work-around for this is for the HC
+ * driver to re-initialise these regs when ever the HC is reset.
+ */
+
+ /* Set burst transfer counts to 256 out, 32 in. */
+
+ writel_be(0x01000020, (void __iomem *)ehci->regs +
+ ps3_ehci_hc_insnreg01);
+
+ /* Enable burst transfer counts. */
+
+ writel_be(0x00000001, (void __iomem *)ehci->regs +
+ ps3_ehci_hc_insnreg03);
+}
+
+static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
+{
+ int result;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci->big_endian_mmio = 1;
+ ehci->caps = hcd->regs;
+
+ result = ehci_setup(hcd);
+ if (result)
+ return result;
+
+ ps3_ehci_setup_insnreg(ehci);
+
+ return result;
+}
+
+static const struct hc_driver ps3_ehci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PS3 EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .reset = ps3_ehci_hc_reset,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+ .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,
+#if defined(CONFIG_PM)
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
+{
+ int result;
+ struct usb_hcd *hcd;
+ unsigned int virq;
+ static u64 dummy_mask = DMA_BIT_MASK(32);
+
+ if (usb_disabled()) {
+ result = -ENODEV;
+ goto fail_start;
+ }
+
+ result = ps3_open_hv_device(dev);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
+ __func__, __LINE__);
+ goto fail_open;
+ }
+
+ result = ps3_dma_region_create(dev->d_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+ "(%d)\n", __func__, __LINE__, result);
+ BUG_ON("check region type");
+ goto fail_dma_region;
+ }
+
+ result = ps3_mmio_region_create(dev->m_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
+ __func__, __LINE__);
+ result = -EPERM;
+ goto fail_mmio_region;
+ }
+
+ dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
+ __LINE__, dev->m_region->lpar_addr);
+
+ result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n",
+ __func__, __LINE__, virq);
+ result = -EPERM;
+ goto fail_irq;
+ }
+
+ dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
+
+ hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core));
+
+ if (!hcd) {
+ dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
+ __LINE__);
+ result = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ hcd->rsrc_start = dev->m_region->lpar_addr;
+ hcd->rsrc_len = dev->m_region->len;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+ dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+ __func__, __LINE__);
+
+ hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
+
+ if (!hcd->regs) {
+ dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__,
+ __LINE__);
+ result = -EPERM;
+ goto fail_ioremap;
+ }
+
+ dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->rsrc_start);
+ dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->rsrc_len);
+ dev_dbg(&dev->core, "%s:%d: hcd->regs %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
+ (unsigned long)virq);
+
+ ps3_system_bus_set_drvdata(dev, hcd);
+
+ result = usb_add_hcd(hcd, virq, 0);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
+ __func__, __LINE__, result);
+ goto fail_add_hcd;
+ }
+
+ device_wakeup_enable(hcd->self.controller);
+ return result;
+
+fail_add_hcd:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ ps3_io_irq_destroy(virq);
+fail_irq:
+ ps3_free_mmio_region(dev->m_region);
+fail_mmio_region:
+ ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+ ps3_close_hv_device(dev);
+fail_open:
+fail_start:
+ return result;
+}
+
+static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
+{
+ unsigned int tmp;
+ struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
+
+ BUG_ON(!hcd);
+
+ dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+ tmp = hcd->irq;
+
+ usb_remove_hcd(hcd);
+
+ ps3_system_bus_set_drvdata(dev, NULL);
+
+ BUG_ON(!hcd->regs);
+ iounmap(hcd->regs);
+
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ ps3_io_irq_destroy(tmp);
+ ps3_free_mmio_region(dev->m_region);
+
+ ps3_dma_region_free(dev->d_region);
+ ps3_close_hv_device(dev);
+
+ return 0;
+}
+
+static int __init ps3_ehci_driver_register(struct ps3_system_bus_driver *drv)
+{
+ return firmware_has_feature(FW_FEATURE_PS3_LV1)
+ ? ps3_system_bus_driver_register(drv)
+ : 0;
+}
+
+static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI);
+
+static struct ps3_system_bus_driver ps3_ehci_driver = {
+ .core.name = "ps3-ehci-driver",
+ .core.owner = THIS_MODULE,
+ .match_id = PS3_MATCH_ID_EHCI,
+ .probe = ps3_ehci_probe,
+ .remove = ps3_ehci_remove,
+ .shutdown = ps3_ehci_remove,
+};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
new file mode 100644
index 000000000..54f5332f8
--- /dev/null
+++ b/drivers/usb/host/ehci-q.c
@@ -0,0 +1,1476 @@
+/*
+ * Copyright (C) 2001-2004 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hardware queue manipulation ... the core. QH/QTD manipulation.
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number). We use one QH per endpoint, queue
+ * multiple urbs (all three types) per endpoint. URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge. That's in "ehci-sched.c".
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
+ size_t len, int token, int maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr);
+ qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely (len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ addr = buf;
+ qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
+ qtd->hw_buf_hi[i] = cpu_to_hc32(ehci,
+ (u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ struct ehci_qh_hw *hw = qh->hw;
+
+ /* writes to an active overlay are unsafe */
+ WARN_ON(qh->qh_state != QH_STATE_IDLE);
+
+ hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ hw->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 (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) {
+ unsigned is_out, epnum;
+
+ is_out = qh->is_out;
+ epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
+ if (unlikely(!usb_gettoggle(qh->ps.udev, epnum, is_out))) {
+ hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+ usb_settoggle(qh->ps.udev, epnum, is_out, 1);
+ }
+ }
+
+ hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* if it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void
+qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list);
+
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (qh->hw->hw_token & ACTIVE_BIT(ehci))
+ qh->hw->hw_qtd_next = qtd->hw_next;
+ else
+ qh_update(ehci, qh, qtd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct ehci_qh *qh = ep->hcpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ qh->clearing_tt = 0;
+ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
+ && ehci->rh_state == EHCI_RH_RUNNING)
+ qh_link_async(ehci, qh);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh,
+ struct urb *urb, u32 token)
+{
+
+ /* If an async split transaction gets an error or is unlinked,
+ * the TT buffer may be left in an indeterminate state. We
+ * have to clear the TT buffer.
+ *
+ * Note: this routine is never called for Isochronous transfers.
+ */
+ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
+#ifdef CONFIG_DYNAMIC_DEBUG
+ struct usb_device *tt = urb->dev->tt->hub;
+ dev_dbg(&tt->dev,
+ "clear tt buffer port %d, a%d ep%d t%08x\n",
+ urb->dev->ttport, urb->dev->devnum,
+ usb_pipeendpoint(urb->pipe), token);
+#endif /* CONFIG_DYNAMIC_DEBUG */
+ if (!ehci_is_TDI(ehci)
+ || urb->dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub) {
+ if (usb_hub_clear_tt_buffer(urb) == 0)
+ qh->clearing_tt = 1;
+ } else {
+
+ /* REVISIT ARC-derived cores don't clear the root
+ * hub TT buffer in this way...
+ */
+ }
+ }
+}
+
+static int qtd_copy_status (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ size_t length,
+ u32 token
+)
+{
+ int status = -EINPROGRESS;
+
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely (QTD_PID (token) != 2))
+ urb->actual_length += length - QTD_LENGTH (token);
+
+ /* don't modify error codes */
+ if (unlikely(urb->unlinked))
+ return status;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely (IS_SHORT_READ (token)))
+ status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ status = -EOVERFLOW;
+ /* CERR nonzero + halt --> stall */
+ } else if (QTD_CERR(token)) {
+ status = -EPIPE;
+
+ /* In theory, more than one of the following bits can be set
+ * since they are sticky and the transaction is retried.
+ * Which to test first is rather arbitrary.
+ */
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ status = -EPROTO;
+ } else if (token & QTD_STS_DBE) {
+ status = (QTD_PID (token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ } else if (token & QTD_STS_XACT) {
+ /* timeout, bad CRC, wrong PID, etc */
+ ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out");
+ status = -EPROTO;
+ } else { /* unknown */
+ status = -EPROTO;
+ }
+ }
+
+ return status;
+}
+
+static void
+ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
+{
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ /* ... update hc-wide periodic stats */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
+ }
+
+ if (unlikely(urb->unlinked)) {
+ COUNT(ehci->stats.unlink);
+ } else {
+ /* report non-error and short read status as zero */
+ if (status == -EINPROGRESS || status == -EREMOTEIO)
+ status = 0;
+ COUNT(ehci->stats.complete);
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ status,
+ urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
+}
+
+static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns nonzero if the caller should
+ * unlink qh.
+ */
+static unsigned
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qtd *last, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int last_status;
+ int stopped;
+ u8 state;
+ struct ehci_qh_hw *hw = qh->hw;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ *
+ * It's a bug for qh->qh_state to be anything other than
+ * QH_STATE_IDLE, unless our caller is scan_async() or
+ * scan_intr().
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ rescan:
+ last = NULL;
+ last_status = -EINPROGRESS;
+ qh->dequeue_during_giveback = 0;
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe (entry, tmp, &qh->qtd_list) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ u32 token = 0;
+
+ qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely (last->urb != urb)) {
+ ehci_urb_done(ehci, last->urb, last_status);
+ last_status = -EINPROGRESS;
+ }
+ ehci_qtd_free (ehci, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb ();
+ token = hc32_to_cpu(ehci, qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ retry_xacterr:
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ /* Report Data Buffer Error: non-fatal but useful */
+ if (token & QTD_STS_DBE)
+ ehci_dbg(ehci,
+ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ urb,
+ usb_endpoint_num(&urb->ep->desc),
+ usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd,
+ qh);
+
+ /* on STALL, error, and short reads this urb must
+ * complete and all its qtds must be recycled.
+ */
+ if ((token & QTD_STS_HALT) != 0) {
+
+ /* retry transaction errors until we
+ * reach the software xacterr limit
+ */
+ if ((token & QTD_STS_XACT) &&
+ QTD_CERR(token) == 0 &&
+ ++qh->xacterrs < QH_XACTERR_MAX &&
+ !urb->unlinked) {
+ ehci_dbg(ehci,
+ "detected XactErr len %zu/%zu retry %d\n",
+ qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs);
+
+ /* reset the token in the qtd and the
+ * qh overlay (which still contains
+ * the qtd) so that we pick up from
+ * where we left off
+ */
+ token &= ~QTD_STS_HALT;
+ token |= QTD_STS_ACTIVE |
+ (EHCI_TUNE_CERR << 10);
+ qtd->hw_token = cpu_to_hc32(ehci,
+ token);
+ wmb();
+ hw->hw_token = cpu_to_hc32(ehci,
+ token);
+ goto retry_xacterr;
+ }
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ *
+ * other short reads won't stop the queue, including
+ * control transfers (status stage handles that) or
+ * most other single-qtd reads ... the queue stops if
+ * URB_SHORT_NOT_OK was set so the driver submitting
+ * the urbs could clean it up.
+ */
+ } else if (IS_SHORT_READ (token)
+ && !(qtd->hw_alt_next
+ & EHCI_LIST_END(ehci))) {
+ stopped = 1;
+ }
+
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely (!stopped
+ && ehci->rh_state >= EHCI_RH_RUNNING)) {
+ break;
+
+ /* scan the whole queue for unlinks whenever it stops */
+ } else {
+ stopped = 1;
+
+ /* cancel everything if we halt, suspend, etc */
+ if (ehci->rh_state < EHCI_RH_RUNNING)
+ last_status = -ESHUTDOWN;
+
+ /* this qtd is active; skip it unless a previous qtd
+ * for its urb faulted, or its urb was canceled.
+ */
+ else if (last_status == -EINPROGRESS && !urb->unlinked)
+ continue;
+
+ /*
+ * If this was the active qtd when the qh was unlinked
+ * and the overlay's token is active, then the overlay
+ * hasn't been written back to the qtd yet so use its
+ * token instead of the qtd's. After the qtd is
+ * processed and removed, the overlay won't be valid
+ * any more.
+ */
+ if (state == QH_STATE_IDLE &&
+ qh->qtd_list.next == &qtd->qtd_list &&
+ (hw->hw_token & ACTIVE_BIT(ehci))) {
+ token = hc32_to_cpu(ehci, hw->hw_token);
+ hw->hw_token &= ~ACTIVE_BIT(ehci);
+
+ /* An unlink may leave an incomplete
+ * async transaction in the TT buffer.
+ * We have to clear it.
+ */
+ ehci_clear_tt_buffer(ehci, qh, urb, token);
+ }
+ }
+
+ /* unless we already know the urb's status, collect qtd status
+ * and update count of bytes transferred. in common short read
+ * cases with only one data qtd (including control transfers),
+ * queue processing won't halt. but with two or more qtds (for
+ * example, with a 32 KB transfer), when the first qtd gets a
+ * short read the second must be removed by hand.
+ */
+ if (last_status == -EINPROGRESS) {
+ last_status = qtd_copy_status(ehci, urb,
+ qtd->length, token);
+ if (last_status == -EREMOTEIO
+ && (qtd->hw_alt_next
+ & EHCI_LIST_END(ehci)))
+ last_status = -EINPROGRESS;
+
+ /* As part of low/full-speed endpoint-halt processing
+ * we must clear the TT buffer (11.17.5).
+ */
+ if (unlikely(last_status != -EINPROGRESS &&
+ last_status != -EREMOTEIO)) {
+ /* The TT's in some hubs malfunction when they
+ * receive this request following a STALL (they
+ * stop sending isochronous packets). Since a
+ * STALL can't leave the TT buffer in a busy
+ * state (if you believe Figures 11-48 - 11-51
+ * in the USB 2.0 spec), we won't clear the TT
+ * buffer in this case. Strictly speaking this
+ * is a violation of the spec.
+ */
+ if (last_status != -EPIPE)
+ ehci_clear_tt_buffer(ehci, qh, urb,
+ token);
+ }
+ }
+
+ /* if we're removing something not at the queue head,
+ * patch the hardware queue pointer.
+ */
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry (qtd->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+
+ /* remove qtd; it's recycled after possible urb completion */
+ list_del (&qtd->qtd_list);
+ last = qtd;
+
+ /* reinit the xacterr counter for the next qtd */
+ qh->xacterrs = 0;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely (last != NULL)) {
+ ehci_urb_done(ehci, last->urb, last_status);
+ ehci_qtd_free (ehci, last);
+ }
+
+ /* Do we need to rescan for URBs dequeued during a giveback? */
+ if (unlikely(qh->dequeue_during_giveback)) {
+ /* If the QH is already unlinked, do the rescan now. */
+ if (state == QH_STATE_IDLE)
+ goto rescan;
+
+ /* Otherwise the caller must unlink the QH. */
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ *
+ * We won't refresh a QH that's linked (after the HC
+ * stopped the queue). That avoids a race:
+ * - HC reads first part of QH;
+ * - CPU updates that first part and the token;
+ * - HC reads rest of that QH, including token
+ * Result: HC gets an inconsistent image, and then
+ * DMAs to/from the wrong memory (corrupting it).
+ *
+ * That should be rare for interrupt transfers,
+ * except maybe high bandwidth ...
+ */
+ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci))
+ qh->exception = 1;
+
+ /* Let the caller know if the QH needs to be unlinked. */
+ return qh->exception;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+// ... and packet size, for any kind of endpoint descriptor
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list
+) {
+ struct list_head *entry, *temp;
+
+ list_for_each_safe (entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+ list_del (&qtd->qtd_list);
+ ehci_qtd_free (ehci, qtd);
+ }
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *head,
+ gfp_t flags
+) {
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, this_sg_len, maxpacket;
+ int is_input;
+ u32 token;
+ int i;
+ struct scatterlist *sg;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ return NULL;
+ list_add_tail (&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein (urb->pipe);
+ if (usb_pipecontrol (urb->pipe)) {
+ /* SETUP pid */
+ qtd_fill(ehci, qtd, urb->setup_dma,
+ sizeof (struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ i = urb->num_mapped_sgs;
+ if (len > 0 && i > 0) {
+ sg = urb->sg;
+ buf = sg_dma_address(sg);
+
+ /* urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ } else {
+ sg = NULL;
+ buf = urb->transfer_dma;
+ this_sg_len = len;
+ }
+
+ if (is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+
+ this_qtd_len = qtd_fill(ehci, qtd, buf, this_sg_len, token,
+ maxpacket);
+ this_sg_len -= this_qtd_len;
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+
+ /*
+ * short reads advance to a "magic" dummy instead of the next
+ * qtd ... that forces the queue to stop, for manual cleanup.
+ * (this will usually be overridden later.)
+ */
+ if (is_input)
+ qtd->hw_alt_next = ehci->async->hw->hw_alt_next;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely(this_sg_len <= 0)) {
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ buf = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ }
+
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * unless the caller requires manual cleanup after short reads,
+ * have the alt_next mechanism keep the queue running after the
+ * last data qtd (the only one, for control and most other cases).
+ */
+ if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol (urb->pipe)))
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * other OUT ones may need a terminating short packet
+ * (zero length).
+ */
+ if (likely (urb->transfer_buffer_length != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol (urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipeout(urb->pipe)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill(ehci, qtd, 0, 0, token, 0);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
+ return head;
+
+cleanup:
+ qtd_list_free (ehci, urb, head);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// Would be best to create all qh's from config descriptors,
+// when each interface/altsetting is established. Unlink
+// any previous qh and cancel its urbs first; endpoints are
+// implicitly reset then (data toggle too).
+// That'd mean updating how usbcore talks to HCDs. (2.7?)
+
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *
+qh_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ gfp_t flags
+) {
+ struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+ struct usb_tt *tt = urb->dev->tt;
+ struct ehci_qh_hw *hw;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint (urb->pipe) << 8;
+ info1 |= usb_pipedevice (urb->pipe) << 0;
+
+ is_input = usb_pipein (urb->pipe);
+ type = usb_pipetype (urb->pipe);
+ maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+
+ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth
+ * acts like up to 3KB, but is built from smaller packets.
+ */
+ if (max_packet(maxp) > 1024) {
+ ehci_dbg(ehci, "bogus qh maxpacket %d\n", max_packet(maxp));
+ goto done;
+ }
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ unsigned tmp;
+
+ qh->ps.usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+ is_input, 0,
+ hb_mult(maxp) * max_packet(maxp)));
+ qh->ps.phase = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->ps.c_usecs = 0;
+ qh->gap_uf = 0;
+
+ if (urb->interval > 1 && urb->interval < 8) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ urb->interval = 1;
+ } else if (urb->interval > ehci->periodic_size << 3) {
+ urb->interval = ehci->periodic_size << 3;
+ }
+ qh->ps.period = urb->interval >> 3;
+
+ /* period for bandwidth allocation */
+ tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+ 1 << (urb->ep->desc.bInterval - 1));
+
+ /* Allow urb->interval to override */
+ qh->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+ qh->ps.bw_period = qh->ps.bw_uperiod >> 3;
+ } else {
+ int think_time;
+
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { // SPLIT, gap, CSPLIT+DATA
+ qh->ps.c_usecs = qh->ps.usecs + HS_USECS(0);
+ qh->ps.usecs = HS_USECS(1);
+ } else { // SPLIT+DATA, gap, CSPLIT
+ qh->ps.usecs += HS_USECS(1);
+ qh->ps.c_usecs = HS_USECS(0);
+ }
+
+ think_time = tt ? tt->think_time : 0;
+ qh->ps.tt_usecs = NS_TO_US(think_time +
+ usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, max_packet (maxp)));
+ if (urb->interval > ehci->periodic_size)
+ urb->interval = ehci->periodic_size;
+ qh->ps.period = urb->interval;
+
+ /* period for bandwidth allocation */
+ tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+ urb->ep->desc.bInterval);
+ tmp = rounddown_pow_of_two(tmp);
+
+ /* Allow urb->interval to override */
+ qh->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+ qh->ps.bw_uperiod = qh->ps.bw_period << 3;
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->ps.udev = urb->dev;
+ qh->ps.ep = urb->ep;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= QH_LOW_SPEED;
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= QH_CONTROL_EP; /* for TT */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (ehci_has_fsl_portno_bug(ehci))
+ info2 |= (urb->dev->ttport-1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
+
+ /* set the address of the TT; for TDI's integrated
+ * root hub tt, leave it zeroed.
+ */
+ if (tt && tt->hub != ehci_to_hcd(ehci)->self.root_hub)
+ info2 |= tt->hub->devnum << 16;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= QH_HIGH_SPEED;
+ if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ /* The USB spec says that high speed bulk endpoints
+ * always use 512 byte maxpacket. But some device
+ * vendors decided to ignore that, and MSFT is happy
+ * to help them do so. So now people expect to use
+ * such nonconformant devices with Linux too; sigh.
+ */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet (maxp) << 16;
+ info2 |= hb_mult (maxp) << 30;
+ }
+ break;
+ default:
+ ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev,
+ urb->dev->speed);
+done:
+ qh_destroy(ehci, qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear */
+ qh->qh_state = QH_STATE_IDLE;
+ hw = qh->hw;
+ hw->hw_info1 = cpu_to_hc32(ehci, info1);
+ hw->hw_info2 = cpu_to_hc32(ehci, info2);
+ qh->is_out = !is_input;
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_async(struct ehci_hcd *ehci)
+{
+ if (ehci->async_count++)
+ return;
+
+ /* Stop waiting to turn off the async schedule */
+ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
+
+ /* Don't start the schedule until ASS is 0 */
+ ehci_poll_ASS(ehci);
+ turn_on_io_watchdog(ehci);
+}
+
+static void disable_async(struct ehci_hcd *ehci)
+{
+ if (--ehci->async_count)
+ return;
+
+ /* The async schedule and unlink lists are supposed to be empty */
+ WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) ||
+ !list_empty(&ehci->async_idle));
+
+ /* Don't turn off the schedule until ASS is 1 */
+ ehci_poll_ASS(ehci);
+}
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ __hc32 dma = QH_NEXT(ehci, qh->qh_dma);
+ struct ehci_qh *head;
+
+ /* Don't link a QH if there's a Clear-TT-Buffer pending */
+ if (unlikely(qh->clearing_tt))
+ return;
+
+ WARN_ON(qh->qh_state != QH_STATE_IDLE);
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ qh_refresh(ehci, qh);
+
+ /* splice right after start */
+ head = ehci->async;
+ qh->qh_next = head->qh_next;
+ qh->hw->hw_next = head->hw->hw_next;
+ wmb ();
+
+ head->qh_next.qh = qh;
+ head->hw->hw_next = dma;
+
+ qh->qh_state = QH_STATE_LINKED;
+ qh->xacterrs = 0;
+ qh->exception = 0;
+ /* qtd completions reported later by interrupt */
+
+ enable_async(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int epnum,
+ void **ptr
+)
+{
+ struct ehci_qh *qh = NULL;
+ __hc32 qh_addr_mask = cpu_to_hc32(ehci, 0x7f);
+
+ qh = (struct ehci_qh *) *ptr;
+ if (unlikely (qh == NULL)) {
+ /* can't sleep here, we have ehci->lock... */
+ qh = qh_make (ehci, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely (qh != NULL)) {
+ struct ehci_qtd *qtd;
+
+ if (unlikely (list_empty (qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry (qtd_list->next, struct ehci_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely (epnum == 0)) {
+
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice (urb->pipe) == 0)
+ qh->hw->hw_info1 &= ~qh_addr_mask;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely (qtd != NULL)) {
+ struct ehci_qtd *dummy;
+ dma_addr_t dma;
+ __hc32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT(ehci);
+
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del (&qtd->qtd_list);
+ list_add (&dummy->qtd_list, qtd_list);
+ list_splice_tail(qtd_list, &qh->qtd_list);
+
+ ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry (qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ qtd->hw_next = QTD_NEXT(ehci, dma);
+
+ /* let the hc process these next qtds */
+ wmb ();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh;
+ }
+ }
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = NULL;
+ int rc;
+
+ epnum = urb->ep->desc.bEndpointAddress;
+
+#ifdef EHCI_URB_TRACE
+ {
+ struct ehci_qtd *qtd;
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ ehci_dbg(ehci,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, urb->ep->hcpriv);
+ }
+#endif
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+ rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
+ if (unlikely(rc))
+ goto done;
+
+ qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async(ehci, qh);
+ done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (unlikely (qh == NULL))
+ qtd_list_free (ehci, urb, qtd_list);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_HCD_TEST_MODE
+/*
+ * This function creates the qtds and submits them for the
+ * SINGLE_STEP_SET_FEATURE Test.
+ * This is done in two parts: first SETUP req for GetDesc is sent then
+ * 15 seconds later, the IN stage for GetDesc starts to req data from dev
+ *
+ * is_setup : i/p arguement decides which of the two stage needs to be
+ * performed; TRUE - SETUP and FALSE - IN+STATUS
+ * Returns 0 if success
+ */
+static int submit_single_step_set_feature(
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int is_setup
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct list_head qtd_list;
+ struct list_head *head;
+
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, maxpacket;
+ u32 token;
+
+ INIT_LIST_HEAD(&qtd_list);
+ head = &qtd_list;
+
+ /* URBs map to sequences of QTDs: one logical transaction */
+ qtd = ehci_qtd_alloc(ehci, GFP_KERNEL);
+ if (unlikely(!qtd))
+ return -1;
+ list_add_tail(&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+
+ len = urb->transfer_buffer_length;
+ /*
+ * Check if the request is to perform just the SETUP stage (getDesc)
+ * as in SINGLE_STEP_SET_FEATURE test, DATA stage (IN) happens
+ * 15 secs after the setup
+ */
+ if (is_setup) {
+ /* SETUP pid */
+ qtd_fill(ehci, qtd, urb->setup_dma,
+ sizeof(struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+
+ submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
+ return 0; /*Return now; we shall come back after 15 seconds*/
+ }
+
+ /*
+ * IN: data transfer stage: buffer setup : start the IN txn phase for
+ * the get_Desc SETUP which was sent 15seconds back
+ */
+ token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/
+ buf = urb->transfer_dma;
+
+ token |= (1 /* "in" */ << 8); /*This is IN stage*/
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0));
+
+ qtd_fill(ehci, qtd, buf, len, token, maxpacket);
+
+ /*
+ * Our IN phase shall always be a short read; so keep the queue running
+ * and let it advance to the next qtd which zero length OUT status
+ */
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
+
+ /* STATUS stage for GetDesc control request */
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(ehci, GFP_ATOMIC);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* dont fill any data in such packets */
+ qtd_fill(ehci, qtd, 0, 0, token, 0);
+
+ /* by default, enable interrupt on urb completion */
+ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
+
+ submit_async(ehci, urb, &qtd_list, GFP_KERNEL);
+
+ return 0;
+
+cleanup:
+ qtd_list_free(ehci, urb, head);
+ return -1;
+}
+#endif /* CONFIG_USB_HCD_TEST_MODE */
+
+/*-------------------------------------------------------------------------*/
+
+static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qh *prev;
+
+ /* Add to the end of the list of QHs waiting for the next IAAD */
+ qh->qh_state = QH_STATE_UNLINK_WAIT;
+ list_add_tail(&qh->unlink_node, &ehci->async_unlink);
+
+ /* Unlink it from the schedule */
+ prev = ehci->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw->hw_next = qh->hw->hw_next;
+ prev->qh_next = qh->qh_next;
+ if (ehci->qh_scan_next == qh)
+ ehci->qh_scan_next = qh->qh_next.qh;
+}
+
+static void start_iaa_cycle(struct ehci_hcd *ehci)
+{
+ /* Do nothing if an IAA cycle is already running */
+ if (ehci->iaa_in_progress)
+ return;
+ ehci->iaa_in_progress = true;
+
+ /* If the controller isn't running, we don't have to wait for it */
+ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
+ end_unlink_async(ehci);
+
+ /* Otherwise start a new IAA cycle */
+ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
+
+ /* Make sure the unlinks are all visible to the hardware */
+ wmb();
+
+ ehci_writel(ehci, ehci->command | CMD_IAAD,
+ &ehci->regs->command);
+ ehci_readl(ehci, &ehci->regs->command);
+ ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
+ }
+}
+
+/* the async qh for the qtds being unlinked are now gone from the HC */
+
+static void end_unlink_async(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ bool early_exit;
+
+ if (ehci->has_synopsys_hc_bug)
+ ehci_writel(ehci, (u32) ehci->async->qh_dma,
+ &ehci->regs->async_next);
+
+ /* The current IAA cycle has ended */
+ ehci->iaa_in_progress = false;
+
+ if (list_empty(&ehci->async_unlink))
+ return;
+ qh = list_first_entry(&ehci->async_unlink, struct ehci_qh,
+ unlink_node); /* QH whose IAA cycle just ended */
+
+ /*
+ * If async_unlinking is set then this routine is already running,
+ * either on the stack or on another CPU.
+ */
+ early_exit = ehci->async_unlinking;
+
+ /* If the controller isn't running, process all the waiting QHs */
+ if (ehci->rh_state < EHCI_RH_RUNNING)
+ list_splice_tail_init(&ehci->async_unlink, &ehci->async_idle);
+
+ /*
+ * Intel (?) bug: The HC can write back the overlay region even
+ * after the IAA interrupt occurs. In self-defense, always go
+ * through two IAA cycles for each QH.
+ */
+ else if (qh->qh_state == QH_STATE_UNLINK_WAIT) {
+ qh->qh_state = QH_STATE_UNLINK;
+ early_exit = true;
+ }
+
+ /* Otherwise process only the first waiting QH (NVIDIA bug?) */
+ else
+ list_move_tail(&qh->unlink_node, &ehci->async_idle);
+
+ /* Start a new IAA cycle if any QHs are waiting for it */
+ if (!list_empty(&ehci->async_unlink))
+ start_iaa_cycle(ehci);
+
+ /*
+ * Don't allow nesting or concurrent calls,
+ * or wait for the second IAA cycle for the next QH.
+ */
+ if (early_exit)
+ return;
+
+ /* Process the idle QHs */
+ ehci->async_unlinking = true;
+ while (!list_empty(&ehci->async_idle)) {
+ qh = list_first_entry(&ehci->async_idle, struct ehci_qh,
+ unlink_node);
+ list_del(&qh->unlink_node);
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+
+ if (!list_empty(&qh->qtd_list))
+ qh_completions(ehci, qh);
+ if (!list_empty(&qh->qtd_list) &&
+ ehci->rh_state == EHCI_RH_RUNNING)
+ qh_link_async(ehci, qh);
+ disable_async(ehci);
+ }
+ ehci->async_unlinking = false;
+}
+
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void unlink_empty_async(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ struct ehci_qh *qh_to_unlink = NULL;
+ int count = 0;
+
+ /* Find the last async QH which has been empty for a timer cycle */
+ for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) {
+ if (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED) {
+ ++count;
+ if (qh->unlink_cycle != ehci->async_unlink_cycle)
+ qh_to_unlink = qh;
+ }
+ }
+
+ /* If nothing else is being unlinked, unlink the last empty QH */
+ if (list_empty(&ehci->async_unlink) && qh_to_unlink) {
+ start_unlink_async(ehci, qh_to_unlink);
+ --count;
+ }
+
+ /* Other QHs will be handled later */
+ if (count > 0) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
+ ++ehci->async_unlink_cycle;
+ }
+}
+
+/* The root hub is suspended; unlink all the async QHs */
+static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+
+ while (ehci->async->qh_next.qh) {
+ qh = ehci->async->qh_next.qh;
+ WARN_ON(!list_empty(&qh->qtd_list));
+ single_unlink_async(ehci, qh);
+ }
+ start_iaa_cycle(ehci);
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
+
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ /* If the QH isn't linked then there's nothing we can do. */
+ if (qh->qh_state != QH_STATE_LINKED)
+ return;
+
+ single_unlink_async(ehci, qh);
+ start_iaa_cycle(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ bool check_unlinks_later = false;
+
+ ehci->qh_scan_next = ehci->async->qh_next.qh;
+ while (ehci->qh_scan_next) {
+ qh = ehci->qh_scan_next;
+ ehci->qh_scan_next = qh->qh_next.qh;
+
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why ehci->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then ehci->qh_scan_next is adjusted
+ * in single_unlink_async().
+ */
+ temp = qh_completions(ehci, qh);
+ if (unlikely(temp)) {
+ start_unlink_async(ehci, qh);
+ } else if (list_empty(&qh->qtd_list)
+ && qh->qh_state == QH_STATE_LINKED) {
+ qh->unlink_cycle = ehci->async_unlink_cycle;
+ check_unlinks_later = true;
+ }
+ }
+ }
+
+ /*
+ * Unlink empty entries, reducing DMA usage as well
+ * as HCD schedule-scanning costs. Delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ */
+ if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
+ !(ehci->enabled_hrtimer_events &
+ BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
+ ++ehci->async_unlink_cycle;
+ }
+}
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
new file mode 100644
index 000000000..f9a332775
--- /dev/null
+++ b/drivers/usb/host/ehci-sched.c
@@ -0,0 +1,2517 @@
+/*
+ * Copyright (c) 2001-2004 by David Brownell
+ * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI scheduled transaction support: interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ *
+ * Note that for interrupt transfers, the QH/QTD manipulation is shared
+ * with the "asynchronous" transaction support (control/bulk transfers).
+ * The only real difference is in how interrupt transfers are scheduled.
+ *
+ * For ISO, we make an "iso_stream" head to serve the same role as a QH.
+ * It keeps track of every ITD (or SITD) that's linked, and holds enough
+ * pre-calculated schedule data to make appending to the queue be quick.
+ */
+
+static int ehci_get_frame (struct usb_hcd *hcd);
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd/sitd
+ * @tag: hardware tag for type of this record
+ */
+static union ehci_shadow *
+periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
+ __hc32 tag)
+{
+ switch (hc32_to_cpu(ehci, tag)) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+ case Q_TYPE_ITD:
+ return &periodic->itd->itd_next;
+ // case Q_TYPE_SITD:
+ default:
+ return &periodic->sitd->sitd_next;
+ }
+}
+
+static __hc32 *
+shadow_next_periodic(struct ehci_hcd *ehci, union ehci_shadow *periodic,
+ __hc32 tag)
+{
+ switch (hc32_to_cpu(ehci, tag)) {
+ /* our ehci_shadow.qh is actually software part */
+ case Q_TYPE_QH:
+ return &periodic->qh->hw->hw_next;
+ /* others are hw parts */
+ default:
+ return periodic->hw_next;
+ }
+}
+
+/* caller must hold ehci->lock */
+static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
+{
+ union ehci_shadow *prev_p = &ehci->pshadow[frame];
+ __hc32 *hw_p = &ehci->periodic[frame];
+ union ehci_shadow here = *prev_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow(ehci, prev_p,
+ Q_NEXT_TYPE(ehci, *hw_p));
+ hw_p = shadow_next_periodic(ehci, &here,
+ Q_NEXT_TYPE(ehci, *hw_p));
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr)
+ return;
+
+ /* update shadow and hardware lists ... the old "next" pointers
+ * from ptr may still be in use, the caller updates them.
+ */
+ *prev_p = *periodic_next_shadow(ehci, &here,
+ Q_NEXT_TYPE(ehci, *hw_p));
+
+ if (!ehci->use_dummy_qh ||
+ *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p))
+ != EHCI_LIST_END(ehci))
+ *hw_p = *shadow_next_periodic(ehci, &here,
+ Q_NEXT_TYPE(ehci, *hw_p));
+ else
+ *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Bandwidth and TT management */
+
+/* Find the TT data structure for this device; create it if necessary */
+static struct ehci_tt *find_tt(struct usb_device *udev)
+{
+ struct usb_tt *utt = udev->tt;
+ struct ehci_tt *tt, **tt_index, **ptt;
+ unsigned port;
+ bool allocated_index = false;
+
+ if (!utt)
+ return NULL; /* Not below a TT */
+
+ /*
+ * Find/create our data structure.
+ * For hubs with a single TT, we get it directly.
+ * For hubs with multiple TTs, there's an extra level of pointers.
+ */
+ tt_index = NULL;
+ if (utt->multi) {
+ tt_index = utt->hcpriv;
+ if (!tt_index) { /* Create the index array */
+ tt_index = kzalloc(utt->hub->maxchild *
+ sizeof(*tt_index), GFP_ATOMIC);
+ if (!tt_index)
+ return ERR_PTR(-ENOMEM);
+ utt->hcpriv = tt_index;
+ allocated_index = true;
+ }
+ port = udev->ttport - 1;
+ ptt = &tt_index[port];
+ } else {
+ port = 0;
+ ptt = (struct ehci_tt **) &utt->hcpriv;
+ }
+
+ tt = *ptt;
+ if (!tt) { /* Create the ehci_tt */
+ struct ehci_hcd *ehci =
+ hcd_to_ehci(bus_to_hcd(udev->bus));
+
+ tt = kzalloc(sizeof(*tt), GFP_ATOMIC);
+ if (!tt) {
+ if (allocated_index) {
+ utt->hcpriv = NULL;
+ kfree(tt_index);
+ }
+ return ERR_PTR(-ENOMEM);
+ }
+ list_add_tail(&tt->tt_list, &ehci->tt_list);
+ INIT_LIST_HEAD(&tt->ps_list);
+ tt->usb_tt = utt;
+ tt->tt_port = port;
+ *ptt = tt;
+ }
+
+ return tt;
+}
+
+/* Release the TT above udev, if it's not in use */
+static void drop_tt(struct usb_device *udev)
+{
+ struct usb_tt *utt = udev->tt;
+ struct ehci_tt *tt, **tt_index, **ptt;
+ int cnt, i;
+
+ if (!utt || !utt->hcpriv)
+ return; /* Not below a TT, or never allocated */
+
+ cnt = 0;
+ if (utt->multi) {
+ tt_index = utt->hcpriv;
+ ptt = &tt_index[udev->ttport - 1];
+
+ /* How many entries are left in tt_index? */
+ for (i = 0; i < utt->hub->maxchild; ++i)
+ cnt += !!tt_index[i];
+ } else {
+ tt_index = NULL;
+ ptt = (struct ehci_tt **) &utt->hcpriv;
+ }
+
+ tt = *ptt;
+ if (!tt || !list_empty(&tt->ps_list))
+ return; /* never allocated, or still in use */
+
+ list_del(&tt->tt_list);
+ *ptt = NULL;
+ kfree(tt);
+ if (cnt == 1) {
+ utt->hcpriv = NULL;
+ kfree(tt_index);
+ }
+}
+
+static void bandwidth_dbg(struct ehci_hcd *ehci, int sign, char *type,
+ struct ehci_per_sched *ps)
+{
+ dev_dbg(&ps->udev->dev,
+ "ep %02x: %s %s @ %u+%u (%u.%u+%u) [%u/%u us] mask %04x\n",
+ ps->ep->desc.bEndpointAddress,
+ (sign >= 0 ? "reserve" : "release"), type,
+ (ps->bw_phase << 3) + ps->phase_uf, ps->bw_uperiod,
+ ps->phase, ps->phase_uf, ps->period,
+ ps->usecs, ps->c_usecs, ps->cs_mask);
+}
+
+static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci,
+ struct ehci_qh *qh, int sign)
+{
+ unsigned start_uf;
+ unsigned i, j, m;
+ int usecs = qh->ps.usecs;
+ int c_usecs = qh->ps.c_usecs;
+ int tt_usecs = qh->ps.tt_usecs;
+ struct ehci_tt *tt;
+
+ if (qh->ps.phase == NO_FRAME) /* Bandwidth wasn't reserved */
+ return;
+ start_uf = qh->ps.bw_phase << 3;
+
+ bandwidth_dbg(ehci, sign, "intr", &qh->ps);
+
+ if (sign < 0) { /* Release bandwidth */
+ usecs = -usecs;
+ c_usecs = -c_usecs;
+ tt_usecs = -tt_usecs;
+ }
+
+ /* Entire transaction (high speed) or start-split (full/low speed) */
+ for (i = start_uf + qh->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+ i += qh->ps.bw_uperiod)
+ ehci->bandwidth[i] += usecs;
+
+ /* Complete-split (full/low speed) */
+ if (qh->ps.c_usecs) {
+ /* NOTE: adjustments needed for FSTN */
+ for (i = start_uf; i < EHCI_BANDWIDTH_SIZE;
+ i += qh->ps.bw_uperiod) {
+ for ((j = 2, m = 1 << (j+8)); j < 8; (++j, m <<= 1)) {
+ if (qh->ps.cs_mask & m)
+ ehci->bandwidth[i+j] += c_usecs;
+ }
+ }
+ }
+
+ /* FS/LS bus bandwidth */
+ if (tt_usecs) {
+ tt = find_tt(qh->ps.udev);
+ if (sign > 0)
+ list_add_tail(&qh->ps.ps_list, &tt->ps_list);
+ else
+ list_del(&qh->ps.ps_list);
+
+ for (i = start_uf >> 3; i < EHCI_BANDWIDTH_FRAMES;
+ i += qh->ps.bw_period)
+ tt->bandwidth[i] += tt_usecs;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+ struct ehci_tt *tt)
+{
+ struct ehci_per_sched *ps;
+ unsigned uframe, uf, x;
+ u8 *budget_line;
+
+ if (!tt)
+ return;
+ memset(budget_table, 0, EHCI_BANDWIDTH_SIZE);
+
+ /* Add up the contributions from all the endpoints using this TT */
+ list_for_each_entry(ps, &tt->ps_list, ps_list) {
+ for (uframe = ps->bw_phase << 3; uframe < EHCI_BANDWIDTH_SIZE;
+ uframe += ps->bw_uperiod) {
+ budget_line = &budget_table[uframe];
+ x = ps->tt_usecs;
+
+ /* propagate the time forward */
+ for (uf = ps->phase_uf; uf < 8; ++uf) {
+ x += budget_line[uf];
+
+ /* Each microframe lasts 125 us */
+ if (x <= 125) {
+ budget_line[uf] = x;
+ break;
+ } else {
+ budget_line[uf] = 125;
+ x -= 125;
+ }
+ }
+ }
+ }
+}
+
+static int __maybe_unused same_tt(struct usb_device *dev1,
+ struct usb_device *dev2)
+{
+ if (!dev1->tt || !dev2->tt)
+ return 0;
+ if (dev1->tt != dev2->tt)
+ return 0;
+ if (dev1->tt->multi)
+ return dev1->ttport == dev2->ttport;
+ else
+ return 1;
+}
+
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+
+/* Which uframe does the low/fullspeed transfer start in?
+ *
+ * The parameter is the mask of ssplits in "H-frame" terms
+ * and this returns the transfer start uframe in "B-frame" terms,
+ * which allows both to match, e.g. a ssplit in "H-frame" uframe 0
+ * will cause a transfer in "B-frame" uframe 0. "B-frames" lag
+ * "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7.
+ */
+static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
+{
+ unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask);
+ if (!smask) {
+ ehci_err(ehci, "invalid empty smask!\n");
+ /* uframe 7 can't have bw so this will indicate failure */
+ return 7;
+ }
+ return ffs(smask) - 1;
+}
+
+static const unsigned char
+max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
+
+/* carryover low/fullspeed bandwidth that crosses uframe boundries */
+static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
+{
+ int i;
+ for (i=0; i<7; i++) {
+ if (max_tt_usecs[i] < tt_usecs[i]) {
+ tt_usecs[i+1] += tt_usecs[i] - max_tt_usecs[i];
+ tt_usecs[i] = max_tt_usecs[i];
+ }
+ }
+}
+
+/*
+ * Return true if the device's tt's downstream bus is available for a
+ * periodic transfer of the specified length (usecs), starting at the
+ * specified frame/uframe. Note that (as summarized in section 11.19
+ * of the usb 2.0 spec) TTs can buffer multiple transactions for each
+ * uframe.
+ *
+ * The uframe parameter is when the fullspeed/lowspeed transfer
+ * should be executed in "B-frame" terms, which is the same as the
+ * highspeed ssplit's uframe (which is in "H-frame" terms). For example
+ * a ssplit in "H-frame" 0 causes a transfer in "B-frame" 0.
+ * See the EHCI spec sec 4.5 and fig 4.7.
+ *
+ * This checks if the full/lowspeed bus, at the specified starting uframe,
+ * has the specified bandwidth available, according to rules listed
+ * in USB 2.0 spec section 11.18.1 fig 11-60.
+ *
+ * This does not check if the transfer would exceed the max ssplit
+ * limit of 16, specified in USB 2.0 spec section 11.18.4 requirement #4,
+ * since proper scheduling limits ssplits to less than 16 per uframe.
+ */
+static int tt_available (
+ struct ehci_hcd *ehci,
+ struct ehci_per_sched *ps,
+ struct ehci_tt *tt,
+ unsigned frame,
+ unsigned uframe
+)
+{
+ unsigned period = ps->bw_period;
+ unsigned usecs = ps->tt_usecs;
+
+ if ((period == 0) || (uframe >= 7)) /* error */
+ return 0;
+
+ for (frame &= period - 1; frame < EHCI_BANDWIDTH_FRAMES;
+ frame += period) {
+ unsigned i, uf;
+ unsigned short tt_usecs[8];
+
+ if (tt->bandwidth[frame] + usecs > 900)
+ return 0;
+
+ uf = frame << 3;
+ for (i = 0; i < 8; (++i, ++uf))
+ tt_usecs[i] = ehci->tt_budget[uf];
+
+ if (max_tt_usecs[uframe] <= tt_usecs[uframe])
+ return 0;
+
+ /* special case for isoc transfers larger than 125us:
+ * the first and each subsequent fully used uframe
+ * must be empty, so as to not illegally delay
+ * already scheduled transactions
+ */
+ if (125 < usecs) {
+ int ufs = (usecs / 125);
+
+ for (i = uframe; i < (uframe + ufs) && i < 8; i++)
+ if (0 < tt_usecs[i])
+ return 0;
+ }
+
+ tt_usecs[uframe] += usecs;
+
+ carryover_tt_bandwidth(tt_usecs);
+
+ /* fail if the carryover pushed bw past the last uframe's limit */
+ if (max_tt_usecs[7] < tt_usecs[7])
+ return 0;
+ }
+
+ return 1;
+}
+
+#else
+
+/* return true iff the device's transaction translator is available
+ * for a periodic transfer starting at the specified frame, using
+ * all the uframes in the mask.
+ */
+static int tt_no_collision (
+ struct ehci_hcd *ehci,
+ unsigned period,
+ struct usb_device *dev,
+ unsigned frame,
+ u32 uf_mask
+)
+{
+ if (period == 0) /* error */
+ return 0;
+
+ /* note bandwidth wastage: split never follows csplit
+ * (different dev or endpoint) until the next uframe.
+ * calling convention doesn't make that distinction.
+ */
+ for (; frame < ehci->periodic_size; frame += period) {
+ union ehci_shadow here;
+ __hc32 type;
+ struct ehci_qh_hw *hw;
+
+ here = ehci->pshadow [frame];
+ type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
+ while (here.ptr) {
+ switch (hc32_to_cpu(ehci, type)) {
+ case Q_TYPE_ITD:
+ type = Q_NEXT_TYPE(ehci, here.itd->hw_next);
+ here = here.itd->itd_next;
+ continue;
+ case Q_TYPE_QH:
+ hw = here.qh->hw;
+ if (same_tt(dev, here.qh->ps.udev)) {
+ u32 mask;
+
+ mask = hc32_to_cpu(ehci,
+ hw->hw_info2);
+ /* "knows" no gap is needed */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE(ehci, hw->hw_next);
+ here = here.qh->qh_next;
+ continue;
+ case Q_TYPE_SITD:
+ if (same_tt (dev, here.sitd->urb->dev)) {
+ u16 mask;
+
+ mask = hc32_to_cpu(ehci, here.sitd
+ ->hw_uframe);
+ /* FIXME assumes no gap for IN! */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE(ehci, here.sitd->hw_next);
+ here = here.sitd->sitd_next;
+ continue;
+ // case Q_TYPE_FSTN:
+ default:
+ ehci_dbg (ehci,
+ "periodic frame %d bogus type %d\n",
+ frame, type);
+ }
+
+ /* collision or error */
+ return 0;
+ }
+ }
+
+ /* no collision */
+ return 1;
+}
+
+#endif /* CONFIG_USB_EHCI_TT_NEWSCHED */
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_periodic(struct ehci_hcd *ehci)
+{
+ if (ehci->periodic_count++)
+ return;
+
+ /* Stop waiting to turn off the periodic schedule */
+ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
+
+ /* Don't start the schedule until PSS is 0 */
+ ehci_poll_PSS(ehci);
+ turn_on_io_watchdog(ehci);
+}
+
+static void disable_periodic(struct ehci_hcd *ehci)
+{
+ if (--ehci->periodic_count)
+ return;
+
+ /* Don't turn off the schedule until PSS is 1 */
+ ehci_poll_PSS(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* periodic schedule slots have iso tds (normal or split) first, then a
+ * sparse tree for active interrupt transfers.
+ *
+ * this just links in a qh; caller guarantees uframe masks are set right.
+ * no FSTN support (yet; ehci 0.96+)
+ */
+static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period = qh->ps.period;
+
+ dev_dbg(&qh->ps.udev->dev,
+ "link qh%d-%04x/%p start %d [%d/%d us]\n",
+ period, hc32_to_cpup(ehci, &qh->hw->hw_info2)
+ & (QH_CMASK | QH_SMASK),
+ qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
+
+ /* high bandwidth, or otherwise every microframe */
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->ps.phase; i < ehci->periodic_size; i += period) {
+ union ehci_shadow *prev = &ehci->pshadow[i];
+ __hc32 *hw_p = &ehci->periodic[i];
+ union ehci_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip the iso nodes at list head */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(ehci, *hw_p);
+ if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(ehci, prev, type);
+ hw_p = shadow_next_periodic(ehci, &here, type);
+ here = *prev;
+ }
+
+ /* sorting each branch by period (slow-->fast)
+ * enables sharing interior tree nodes
+ */
+ while (here.ptr && qh != here.qh) {
+ if (qh->ps.period > here.qh->ps.period)
+ break;
+ prev = &here.qh->qh_next;
+ hw_p = &here.qh->hw->hw_next;
+ here = *prev;
+ }
+ /* link in this qh, unless some earlier pass did that */
+ if (qh != here.qh) {
+ qh->qh_next = here;
+ if (here.qh)
+ qh->hw->hw_next = *hw_p;
+ wmb ();
+ prev->qh = qh;
+ *hw_p = QH_NEXT (ehci, qh->qh_dma);
+ }
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ qh->xacterrs = 0;
+ qh->exception = 0;
+
+ /* update per-qh bandwidth for debugfs */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->ps.bw_period
+ ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+ : (qh->ps.usecs * 8);
+
+ list_add(&qh->intr_node, &ehci->intr_qh_list);
+
+ /* maybe enable periodic schedule processing */
+ ++ehci->intr_count;
+ enable_periodic(ehci);
+}
+
+static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period;
+
+ /*
+ * If qh is for a low/full-speed device, simply unlinking it
+ * could interfere with an ongoing split transaction. To unlink
+ * it safely would require setting the QH_INACTIVATE bit and
+ * waiting at least one frame, as described in EHCI 4.12.2.5.
+ *
+ * We won't bother with any of this. Instead, we assume that the
+ * only reason for unlinking an interrupt QH while the current URB
+ * is still active is to dequeue all the URBs (flush the whole
+ * endpoint queue).
+ *
+ * If rebalancing the periodic schedule is ever implemented, this
+ * approach will no longer be valid.
+ */
+
+ /* high bandwidth, or otherwise part of every microframe */
+ period = qh->ps.period ? : 1;
+
+ for (i = qh->ps.phase; i < ehci->periodic_size; i += period)
+ periodic_unlink (ehci, i, qh);
+
+ /* update per-qh bandwidth for debugfs */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->ps.bw_period
+ ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+ : (qh->ps.usecs * 8);
+
+ dev_dbg(&qh->ps.udev->dev,
+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
+ qh->ps.period,
+ hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
+ qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
+
+ /* qh->qh_next still "live" to HC */
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = NULL;
+
+ if (ehci->qh_scan_next == qh)
+ ehci->qh_scan_next = list_entry(qh->intr_node.next,
+ struct ehci_qh, intr_node);
+ list_del(&qh->intr_node);
+}
+
+static void cancel_unlink_wait_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ if (qh->qh_state != QH_STATE_LINKED ||
+ list_empty(&qh->unlink_node))
+ return;
+
+ list_del_init(&qh->unlink_node);
+
+ /*
+ * TODO: disable the event of EHCI_HRTIMER_START_UNLINK_INTR for
+ * avoiding unnecessary CPU wakeup
+ */
+}
+
+static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ /* If the QH isn't linked then there's nothing we can do. */
+ if (qh->qh_state != QH_STATE_LINKED)
+ return;
+
+ /* if the qh is waiting for unlink, cancel it now */
+ cancel_unlink_wait_intr(ehci, qh);
+
+ qh_unlink_periodic (ehci, qh);
+
+ /* Make sure the unlinks are visible before starting the timer */
+ wmb();
+
+ /*
+ * The EHCI spec doesn't say how long it takes the controller to
+ * stop accessing an unlinked interrupt QH. The timer delay is
+ * 9 uframes; presumably that will be long enough.
+ */
+ qh->unlink_cycle = ehci->intr_unlink_cycle;
+
+ /* New entries go at the end of the intr_unlink list */
+ list_add_tail(&qh->unlink_node, &ehci->intr_unlink);
+
+ if (ehci->intr_unlinking)
+ ; /* Avoid recursive calls */
+ else if (ehci->rh_state < EHCI_RH_RUNNING)
+ ehci_handle_intr_unlinks(ehci);
+ else if (ehci->intr_unlink.next == &qh->unlink_node) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
+ ++ehci->intr_unlink_cycle;
+ }
+}
+
+/*
+ * It is common only one intr URB is scheduled on one qh, and
+ * given complete() is run in tasklet context, introduce a bit
+ * delay to avoid unlink qh too early.
+ */
+static void start_unlink_intr_wait(struct ehci_hcd *ehci,
+ struct ehci_qh *qh)
+{
+ qh->unlink_cycle = ehci->intr_unlink_wait_cycle;
+
+ /* New entries go at the end of the intr_unlink_wait list */
+ list_add_tail(&qh->unlink_node, &ehci->intr_unlink_wait);
+
+ if (ehci->rh_state < EHCI_RH_RUNNING)
+ ehci_handle_start_intr_unlinks(ehci);
+ else if (ehci->intr_unlink_wait.next == &qh->unlink_node) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true);
+ ++ehci->intr_unlink_wait_cycle;
+ }
+}
+
+static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qh_hw *hw = qh->hw;
+ int rc;
+
+ qh->qh_state = QH_STATE_IDLE;
+ hw->hw_next = EHCI_LIST_END(ehci);
+
+ if (!list_empty(&qh->qtd_list))
+ qh_completions(ehci, qh);
+
+ /* reschedule QH iff another request is queued */
+ if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
+ rc = qh_schedule(ehci, qh);
+ if (rc == 0) {
+ qh_refresh(ehci, qh);
+ qh_link_periodic(ehci, qh);
+ }
+
+ /* An error here likely indicates handshake failure
+ * or no space left in the schedule. Neither fault
+ * should happen often ...
+ *
+ * FIXME kill the now-dysfunctional queued urbs
+ */
+ else {
+ ehci_err(ehci, "can't reschedule qh %p, err %d\n",
+ qh, rc);
+ }
+ }
+
+ /* maybe turn off periodic schedule */
+ --ehci->intr_count;
+ disable_periodic(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_period (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ unsigned uframe,
+ unsigned uperiod,
+ unsigned usecs
+) {
+ /* complete split running into next frame?
+ * given FSTN support, we could sometimes check...
+ */
+ if (uframe >= 8)
+ return 0;
+
+ /* convert "usecs we need" to "max already claimed" */
+ usecs = ehci->uframe_periodic_max - usecs;
+
+ for (uframe += frame << 3; uframe < EHCI_BANDWIDTH_SIZE;
+ uframe += uperiod) {
+ if (ehci->bandwidth[uframe] > usecs)
+ return 0;
+ }
+
+ // success!
+ return 1;
+}
+
+static int check_intr_schedule (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ unsigned uframe,
+ struct ehci_qh *qh,
+ unsigned *c_maskp,
+ struct ehci_tt *tt
+)
+{
+ int retval = -ENOSPC;
+ u8 mask = 0;
+
+ if (qh->ps.c_usecs && uframe >= 6) /* FSTN territory? */
+ goto done;
+
+ if (!check_period(ehci, frame, uframe, qh->ps.bw_uperiod, qh->ps.usecs))
+ goto done;
+ if (!qh->ps.c_usecs) {
+ retval = 0;
+ *c_maskp = 0;
+ goto done;
+ }
+
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+ if (tt_available(ehci, &qh->ps, tt, frame, uframe)) {
+ unsigned i;
+
+ /* TODO : this may need FSTN for SSPLIT in uframe 5. */
+ for (i = uframe+2; i < 8 && i <= uframe+4; i++)
+ if (!check_period(ehci, frame, i,
+ qh->ps.bw_uperiod, qh->ps.c_usecs))
+ goto done;
+ else
+ mask |= 1 << i;
+
+ retval = 0;
+
+ *c_maskp = mask;
+ }
+#else
+ /* Make sure this tt's buffer is also available for CSPLITs.
+ * We pessimize a bit; probably the typical full speed case
+ * doesn't need the second CSPLIT.
+ *
+ * NOTE: both SPLIT and CSPLIT could be checked in just
+ * one smart pass...
+ */
+ mask = 0x03 << (uframe + qh->gap_uf);
+ *c_maskp = mask;
+
+ mask |= 1 << uframe;
+ if (tt_no_collision(ehci, qh->ps.bw_period, qh->ps.udev, frame, mask)) {
+ if (!check_period(ehci, frame, uframe + qh->gap_uf + 1,
+ qh->ps.bw_uperiod, qh->ps.c_usecs))
+ goto done;
+ if (!check_period(ehci, frame, uframe + qh->gap_uf,
+ qh->ps.bw_uperiod, qh->ps.c_usecs))
+ goto done;
+ retval = 0;
+ }
+#endif
+done:
+ return retval;
+}
+
+/* "first fit" scheduling policy used the first time through,
+ * or when the previous schedule slot can't be re-used.
+ */
+static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int status = 0;
+ unsigned uframe;
+ unsigned c_mask;
+ struct ehci_qh_hw *hw = qh->hw;
+ struct ehci_tt *tt;
+
+ hw->hw_next = EHCI_LIST_END(ehci);
+
+ /* reuse the previous schedule slots, if we can */
+ if (qh->ps.phase != NO_FRAME) {
+ ehci_dbg(ehci, "reused qh %p schedule\n", qh);
+ return 0;
+ }
+
+ uframe = 0;
+ c_mask = 0;
+ tt = find_tt(qh->ps.udev);
+ if (IS_ERR(tt)) {
+ status = PTR_ERR(tt);
+ goto done;
+ }
+ compute_tt_budget(ehci->tt_budget, tt);
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ /* "normal" case, uframing flexible except with splits */
+ if (qh->ps.bw_period) {
+ int i;
+ unsigned frame;
+
+ for (i = qh->ps.bw_period; i > 0; --i) {
+ frame = ++ehci->random_frame & (qh->ps.bw_period - 1);
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule(ehci,
+ frame, uframe, qh, &c_mask, tt);
+ if (status == 0)
+ goto got_it;
+ }
+ }
+
+ /* qh->ps.bw_period == 0 means every uframe */
+ } else {
+ status = check_intr_schedule(ehci, 0, 0, qh, &c_mask, tt);
+ }
+ if (status)
+ goto done;
+
+ got_it:
+ qh->ps.phase = (qh->ps.period ? ehci->random_frame &
+ (qh->ps.period - 1) : 0);
+ qh->ps.bw_phase = qh->ps.phase & (qh->ps.bw_period - 1);
+ qh->ps.phase_uf = uframe;
+ qh->ps.cs_mask = qh->ps.period ?
+ (c_mask << 8) | (1 << uframe) :
+ QH_SMASK;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
+ hw->hw_info2 |= cpu_to_hc32(ehci, qh->ps.cs_mask);
+ reserve_release_intr_bandwidth(ehci, qh, 1);
+
+done:
+ return status;
+}
+
+static int intr_submit (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ unsigned epnum;
+ unsigned long flags;
+ struct ehci_qh *qh;
+ int status;
+ struct list_head empty;
+
+ /* get endpoint and transfer/schedule data */
+ epnum = urb->ep->desc.bEndpointAddress;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD (&empty);
+ qh = qh_append_tds(ehci, urb, &empty, epnum, &urb->ep->hcpriv);
+ if (qh == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ if (qh->qh_state == QH_STATE_IDLE) {
+ if ((status = qh_schedule (ehci, qh)) != 0)
+ goto done;
+ }
+
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ BUG_ON (qh == NULL);
+
+ /* stuff into the periodic schedule */
+ if (qh->qh_state == QH_STATE_IDLE) {
+ qh_refresh(ehci, qh);
+ qh_link_periodic(ehci, qh);
+ } else {
+ /* cancel unlink wait for the qh */
+ cancel_unlink_wait_intr(ehci, qh);
+ }
+
+ /* ... update usbfs periodic stats */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++;
+
+done:
+ if (unlikely(status))
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+done_not_linked:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (status)
+ qtd_list_free (ehci, urb, qtd_list);
+
+ return status;
+}
+
+static void scan_intr(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+
+ list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list,
+ intr_node) {
+
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why ehci->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then ehci->qh_scan_next is adjusted
+ * in qh_unlink_periodic().
+ */
+ temp = qh_completions(ehci, qh);
+ if (unlikely(temp))
+ start_unlink_intr(ehci, qh);
+ else if (unlikely(list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED))
+ start_unlink_intr_wait(ehci, qh);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ehci_iso_stream ops work with both ITD and SITD */
+
+static struct ehci_iso_stream *
+iso_stream_alloc (gfp_t mem_flags)
+{
+ struct ehci_iso_stream *stream;
+
+ stream = kzalloc(sizeof *stream, mem_flags);
+ if (likely (stream != NULL)) {
+ INIT_LIST_HEAD(&stream->td_list);
+ INIT_LIST_HEAD(&stream->free_list);
+ stream->next_uframe = NO_FRAME;
+ stream->ps.phase = NO_FRAME;
+ }
+ return stream;
+}
+
+static void
+iso_stream_init (
+ struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream,
+ struct urb *urb
+)
+{
+ static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
+
+ struct usb_device *dev = urb->dev;
+ u32 buf1;
+ unsigned epnum, maxp;
+ int is_input;
+ unsigned tmp;
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's wMaxPacket field
+ */
+ epnum = usb_pipeendpoint(urb->pipe);
+ is_input = usb_pipein(urb->pipe) ? USB_DIR_IN : 0;
+ maxp = usb_endpoint_maxp(&urb->ep->desc);
+ if (is_input) {
+ buf1 = (1 << 11);
+ } else {
+ buf1 = 0;
+ }
+
+ /* knows about ITD vs SITD */
+ if (dev->speed == USB_SPEED_HIGH) {
+ unsigned multi = hb_mult(maxp);
+
+ stream->highspeed = 1;
+
+ maxp = max_packet(maxp);
+ buf1 |= maxp;
+ maxp *= multi;
+
+ stream->buf0 = cpu_to_hc32(ehci, (epnum << 8) | dev->devnum);
+ stream->buf1 = cpu_to_hc32(ehci, buf1);
+ stream->buf2 = cpu_to_hc32(ehci, multi);
+
+ /* usbfs wants to report the average usecs per frame tied up
+ * when transfers on this endpoint are scheduled ...
+ */
+ stream->ps.usecs = HS_USECS_ISO(maxp);
+
+ /* period for bandwidth allocation */
+ tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+ 1 << (urb->ep->desc.bInterval - 1));
+
+ /* Allow urb->interval to override */
+ stream->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+
+ stream->uperiod = urb->interval;
+ stream->ps.period = urb->interval >> 3;
+ stream->bandwidth = stream->ps.usecs * 8 /
+ stream->ps.bw_uperiod;
+
+ } else {
+ u32 addr;
+ int think_time;
+ int hs_transfers;
+
+ addr = dev->ttport << 24;
+ if (!ehci_is_TDI(ehci)
+ || (dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub))
+ addr |= dev->tt->hub->devnum << 16;
+ addr |= epnum << 8;
+ addr |= dev->devnum;
+ stream->ps.usecs = HS_USECS_ISO(maxp);
+ think_time = dev->tt ? dev->tt->think_time : 0;
+ stream->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time(
+ dev->speed, is_input, 1, maxp));
+ hs_transfers = max (1u, (maxp + 187) / 188);
+ if (is_input) {
+ u32 tmp;
+
+ addr |= 1 << 31;
+ stream->ps.c_usecs = stream->ps.usecs;
+ stream->ps.usecs = HS_USECS_ISO(1);
+ stream->ps.cs_mask = 1;
+
+ /* c-mask as specified in USB 2.0 11.18.4 3.c */
+ tmp = (1 << (hs_transfers + 2)) - 1;
+ stream->ps.cs_mask |= tmp << (8 + 2);
+ } else
+ stream->ps.cs_mask = smask_out[hs_transfers - 1];
+
+ /* period for bandwidth allocation */
+ tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+ 1 << (urb->ep->desc.bInterval - 1));
+
+ /* Allow urb->interval to override */
+ stream->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+ stream->ps.bw_uperiod = stream->ps.bw_period << 3;
+
+ stream->ps.period = urb->interval;
+ stream->uperiod = urb->interval << 3;
+ stream->bandwidth = (stream->ps.usecs + stream->ps.c_usecs) /
+ stream->ps.bw_period;
+
+ /* stream->splits gets created from cs_mask later */
+ stream->address = cpu_to_hc32(ehci, addr);
+ }
+
+ stream->ps.udev = dev;
+ stream->ps.ep = urb->ep;
+
+ stream->bEndpointAddress = is_input | epnum;
+ stream->maxp = maxp;
+}
+
+static struct ehci_iso_stream *
+iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
+{
+ unsigned epnum;
+ struct ehci_iso_stream *stream;
+ struct usb_host_endpoint *ep;
+ unsigned long flags;
+
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein(urb->pipe))
+ ep = urb->dev->ep_in[epnum];
+ else
+ ep = urb->dev->ep_out[epnum];
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ stream = ep->hcpriv;
+
+ if (unlikely (stream == NULL)) {
+ stream = iso_stream_alloc(GFP_ATOMIC);
+ if (likely (stream != NULL)) {
+ ep->hcpriv = stream;
+ iso_stream_init(ehci, stream, urb);
+ }
+
+ /* if dev->ep [epnum] is a QH, hw is set */
+ } else if (unlikely (stream->hw != NULL)) {
+ ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n",
+ urb->dev->devpath, epnum,
+ usb_pipein(urb->pipe) ? "in" : "out");
+ stream = NULL;
+ }
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return stream;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ehci_iso_sched ops can be ITD-only or SITD-only */
+
+static struct ehci_iso_sched *
+iso_sched_alloc (unsigned packets, gfp_t mem_flags)
+{
+ struct ehci_iso_sched *iso_sched;
+ int size = sizeof *iso_sched;
+
+ size += packets * sizeof (struct ehci_iso_packet);
+ iso_sched = kzalloc(size, mem_flags);
+ if (likely (iso_sched != NULL)) {
+ INIT_LIST_HEAD (&iso_sched->td_list);
+ }
+ return iso_sched;
+}
+
+static inline void
+itd_sched_init(
+ struct ehci_hcd *ehci,
+ struct ehci_iso_sched *iso_sched,
+ struct ehci_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many uframes are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->uperiod;
+
+ /* figure out per-uframe itd fields that we'll need later
+ * when we fit new itds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct ehci_iso_packet *uframe = &iso_sched->packet [i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc [i].length;
+ buf = dma + urb->iso_frame_desc [i].offset;
+
+ trans = EHCI_ISOC_ACTIVE;
+ trans |= buf & 0x0fff;
+ if (unlikely (((i + 1) == urb->number_of_packets))
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= EHCI_ITD_IOC;
+ trans |= length << 16;
+ uframe->transaction = cpu_to_hc32(ehci, trans);
+
+ /* might need to cross a buffer page within a uframe */
+ uframe->bufp = (buf & ~(u64)0x0fff);
+ buf += length;
+ if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
+ uframe->cross = 1;
+ }
+}
+
+static void
+iso_sched_free (
+ struct ehci_iso_stream *stream,
+ struct ehci_iso_sched *iso_sched
+)
+{
+ if (!iso_sched)
+ return;
+ // caller must hold ehci->lock!
+ list_splice (&iso_sched->td_list, &stream->free_list);
+ kfree (iso_sched);
+}
+
+static int
+itd_urb_transaction (
+ struct ehci_iso_stream *stream,
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ gfp_t mem_flags
+)
+{
+ struct ehci_itd *itd;
+ dma_addr_t itd_dma;
+ int i;
+ unsigned num_itds;
+ struct ehci_iso_sched *sched;
+ unsigned long flags;
+
+ sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+ if (unlikely (sched == NULL))
+ return -ENOMEM;
+
+ itd_sched_init(ehci, sched, stream, urb);
+
+ if (urb->interval < 8)
+ num_itds = 1 + (sched->span + 7) / 8;
+ else
+ num_itds = urb->number_of_packets;
+
+ /* allocate/init ITDs */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < num_itds; i++) {
+
+ /*
+ * Use iTDs from the free list, but not iTDs that may
+ * still be in use by the hardware.
+ */
+ if (likely(!list_empty(&stream->free_list))) {
+ itd = list_first_entry(&stream->free_list,
+ struct ehci_itd, itd_list);
+ if (itd->frame == ehci->now_frame)
+ goto alloc_itd;
+ list_del (&itd->itd_list);
+ itd_dma = itd->itd_dma;
+ } else {
+ alloc_itd:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
+ &itd_dma);
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (!itd) {
+ iso_sched_free(stream, sched);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return -ENOMEM;
+ }
+ }
+
+ memset (itd, 0, sizeof *itd);
+ itd->itd_dma = itd_dma;
+ itd->frame = NO_FRAME;
+ list_add (&itd->itd_list, &sched->td_list);
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = sched;
+ urb->error_count = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream, int sign)
+{
+ unsigned uframe;
+ unsigned i, j;
+ unsigned s_mask, c_mask, m;
+ int usecs = stream->ps.usecs;
+ int c_usecs = stream->ps.c_usecs;
+ int tt_usecs = stream->ps.tt_usecs;
+ struct ehci_tt *tt;
+
+ if (stream->ps.phase == NO_FRAME) /* Bandwidth wasn't reserved */
+ return;
+ uframe = stream->ps.bw_phase << 3;
+
+ bandwidth_dbg(ehci, sign, "iso", &stream->ps);
+
+ if (sign < 0) { /* Release bandwidth */
+ usecs = -usecs;
+ c_usecs = -c_usecs;
+ tt_usecs = -tt_usecs;
+ }
+
+ if (!stream->splits) { /* High speed */
+ for (i = uframe + stream->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+ i += stream->ps.bw_uperiod)
+ ehci->bandwidth[i] += usecs;
+
+ } else { /* Full speed */
+ s_mask = stream->ps.cs_mask;
+ c_mask = s_mask >> 8;
+
+ /* NOTE: adjustment needed for frame overflow */
+ for (i = uframe; i < EHCI_BANDWIDTH_SIZE;
+ i += stream->ps.bw_uperiod) {
+ for ((j = stream->ps.phase_uf, m = 1 << j); j < 8;
+ (++j, m <<= 1)) {
+ if (s_mask & m)
+ ehci->bandwidth[i+j] += usecs;
+ else if (c_mask & m)
+ ehci->bandwidth[i+j] += c_usecs;
+ }
+ }
+
+ tt = find_tt(stream->ps.udev);
+ if (sign > 0)
+ list_add_tail(&stream->ps.ps_list, &tt->ps_list);
+ else
+ list_del(&stream->ps.ps_list);
+
+ for (i = uframe >> 3; i < EHCI_BANDWIDTH_FRAMES;
+ i += stream->ps.bw_period)
+ tt->bandwidth[i] += tt_usecs;
+ }
+}
+
+static inline int
+itd_slot_ok (
+ struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream,
+ unsigned uframe
+)
+{
+ unsigned usecs;
+
+ /* convert "usecs we need" to "max already claimed" */
+ usecs = ehci->uframe_periodic_max - stream->ps.usecs;
+
+ for (uframe &= stream->ps.bw_uperiod - 1; uframe < EHCI_BANDWIDTH_SIZE;
+ uframe += stream->ps.bw_uperiod) {
+ if (ehci->bandwidth[uframe] > usecs)
+ return 0;
+ }
+ return 1;
+}
+
+static inline int
+sitd_slot_ok (
+ struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream,
+ unsigned uframe,
+ struct ehci_iso_sched *sched,
+ struct ehci_tt *tt
+)
+{
+ unsigned mask, tmp;
+ unsigned frame, uf;
+
+ mask = stream->ps.cs_mask << (uframe & 7);
+
+ /* for OUT, don't wrap SSPLIT into H-microframe 7 */
+ if (((stream->ps.cs_mask & 0xff) << (uframe & 7)) >= (1 << 7))
+ return 0;
+
+ /* for IN, don't wrap CSPLIT into the next frame */
+ if (mask & ~0xffff)
+ return 0;
+
+ /* check bandwidth */
+ uframe &= stream->ps.bw_uperiod - 1;
+ frame = uframe >> 3;
+
+#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
+ /* The tt's fullspeed bus bandwidth must be available.
+ * tt_available scheduling guarantees 10+% for control/bulk.
+ */
+ uf = uframe & 7;
+ if (!tt_available(ehci, &stream->ps, tt, frame, uf))
+ return 0;
+#else
+ /* tt must be idle for start(s), any gap, and csplit.
+ * assume scheduling slop leaves 10+% for control/bulk.
+ */
+ if (!tt_no_collision(ehci, stream->ps.bw_period,
+ stream->ps.udev, frame, mask))
+ return 0;
+#endif
+
+ do {
+ unsigned max_used;
+ unsigned i;
+
+ /* check starts (OUT uses more than one) */
+ uf = uframe;
+ max_used = ehci->uframe_periodic_max - stream->ps.usecs;
+ for (tmp = stream->ps.cs_mask & 0xff; tmp; tmp >>= 1, uf++) {
+ if (ehci->bandwidth[uf] > max_used)
+ return 0;
+ }
+
+ /* for IN, check CSPLIT */
+ if (stream->ps.c_usecs) {
+ max_used = ehci->uframe_periodic_max -
+ stream->ps.c_usecs;
+ uf = uframe & ~7;
+ tmp = 1 << (2+8);
+ for (i = (uframe & 7) + 2; i < 8; (++i, tmp <<= 1)) {
+ if ((stream->ps.cs_mask & tmp) == 0)
+ continue;
+ if (ehci->bandwidth[uf+i] > max_used)
+ return 0;
+ }
+ }
+
+ uframe += stream->ps.bw_uperiod;
+ } while (uframe < EHCI_BANDWIDTH_SIZE);
+
+ stream->ps.cs_mask <<= uframe & 7;
+ stream->splits = cpu_to_hc32(ehci, stream->ps.cs_mask);
+ return 1;
+}
+
+/*
+ * This scheduler plans almost as far into the future as it has actual
+ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
+ * "as small as possible" to be cache-friendlier.) That limits the size
+ * transfers you can stream reliably; avoid more than 64 msec per urb.
+ * Also avoid queue depths of less than ehci's worst irq latency (affected
+ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
+ * and other factors); or more than about 230 msec total (for portability,
+ * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
+ */
+
+static int
+iso_stream_schedule (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct ehci_iso_stream *stream
+)
+{
+ u32 now, base, next, start, period, span, now2;
+ u32 wrap = 0, skip = 0;
+ int status = 0;
+ unsigned mod = ehci->periodic_size << 3;
+ struct ehci_iso_sched *sched = urb->hcpriv;
+ bool empty = list_empty(&stream->td_list);
+ bool new_stream = false;
+
+ period = stream->uperiod;
+ span = sched->span;
+ if (!stream->highspeed)
+ span <<= 3;
+
+ /* Start a new isochronous stream? */
+ if (unlikely(empty && !hcd_periodic_completion_in_progress(
+ ehci_to_hcd(ehci), urb->ep))) {
+
+ /* Schedule the endpoint */
+ if (stream->ps.phase == NO_FRAME) {
+ int done = 0;
+ struct ehci_tt *tt = find_tt(stream->ps.udev);
+
+ if (IS_ERR(tt)) {
+ status = PTR_ERR(tt);
+ goto fail;
+ }
+ compute_tt_budget(ehci->tt_budget, tt);
+
+ start = ((-(++ehci->random_frame)) << 3) & (period - 1);
+
+ /* find a uframe slot with enough bandwidth.
+ * Early uframes are more precious because full-speed
+ * iso IN transfers can't use late uframes,
+ * and therefore they should be allocated last.
+ */
+ next = start;
+ start += period;
+ do {
+ start--;
+ /* check schedule: enough space? */
+ if (stream->highspeed) {
+ if (itd_slot_ok(ehci, stream, start))
+ done = 1;
+ } else {
+ if ((start % 8) >= 6)
+ continue;
+ if (sitd_slot_ok(ehci, stream, start,
+ sched, tt))
+ done = 1;
+ }
+ } while (start > next && !done);
+
+ /* no room in the schedule */
+ if (!done) {
+ ehci_dbg(ehci, "iso sched full %p", urb);
+ status = -ENOSPC;
+ goto fail;
+ }
+ stream->ps.phase = (start >> 3) &
+ (stream->ps.period - 1);
+ stream->ps.bw_phase = stream->ps.phase &
+ (stream->ps.bw_period - 1);
+ stream->ps.phase_uf = start & 7;
+ reserve_release_iso_bandwidth(ehci, stream, 1);
+ }
+
+ /* New stream is already scheduled; use the upcoming slot */
+ else {
+ start = (stream->ps.phase << 3) + stream->ps.phase_uf;
+ }
+
+ stream->next_uframe = start;
+ new_stream = true;
+ }
+
+ now = ehci_read_frame_index(ehci) & (mod - 1);
+
+ /* Take the isochronous scheduling threshold into account */
+ if (ehci->i_thresh)
+ next = now + ehci->i_thresh; /* uframe cache */
+ else
+ next = (now + 2 + 7) & ~0x07; /* full frame cache */
+
+ /* If needed, initialize last_iso_frame so that this URB will be seen */
+ if (ehci->isoc_count == 0)
+ ehci->last_iso_frame = now >> 3;
+
+ /*
+ * Use ehci->last_iso_frame as the base. There can't be any
+ * TDs scheduled for earlier than that.
+ */
+ base = ehci->last_iso_frame << 3;
+ next = (next - base) & (mod - 1);
+ start = (stream->next_uframe - base) & (mod - 1);
+
+ if (unlikely(new_stream))
+ goto do_ASAP;
+
+ /*
+ * Typical case: reuse current schedule, stream may still be active.
+ * Hopefully there are no gaps from the host falling behind
+ * (irq delays etc). If there are, the behavior depends on
+ * whether URB_ISO_ASAP is set.
+ */
+ now2 = (now - base) & (mod - 1);
+
+ /* Is the schedule about to wrap around? */
+ if (unlikely(!empty && start < period)) {
+ ehci_dbg(ehci, "request %p would overflow (%u-%u < %u mod %u)\n",
+ urb, stream->next_uframe, base, period, mod);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ /* Is the next packet scheduled after the base time? */
+ if (likely(!empty || start <= now2 + period)) {
+
+ /* URB_ISO_ASAP: make sure that start >= next */
+ if (unlikely(start < next &&
+ (urb->transfer_flags & URB_ISO_ASAP)))
+ goto do_ASAP;
+
+ /* Otherwise use start, if it's not in the past */
+ if (likely(start >= now2))
+ goto use_start;
+
+ /* Otherwise we got an underrun while the queue was empty */
+ } else {
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ goto do_ASAP;
+ wrap = mod;
+ now2 += mod;
+ }
+
+ /* How many uframes and packets do we need to skip? */
+ skip = (now2 - start + period - 1) & -period;
+ if (skip >= span) { /* Entirely in the past? */
+ ehci_dbg(ehci, "iso underrun %p (%u+%u < %u) [%u]\n",
+ urb, start + base, span - period, now2 + base,
+ base);
+
+ /* Try to keep the last TD intact for scanning later */
+ skip = span - period;
+
+ /* Will it come before the current scan position? */
+ if (empty) {
+ skip = span; /* Skip the entire URB */
+ status = 1; /* and give it back immediately */
+ iso_sched_free(stream, sched);
+ sched = NULL;
+ }
+ }
+ urb->error_count = skip / period;
+ if (sched)
+ sched->first_packet = urb->error_count;
+ goto use_start;
+
+ do_ASAP:
+ /* Use the first slot after "next" */
+ start = next + ((start - next) & (period - 1));
+
+ use_start:
+ /* Tried to schedule too far into the future? */
+ if (unlikely(start + span - period >= mod + wrap)) {
+ ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n",
+ urb, start, span - period, mod + wrap);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ start += base;
+ stream->next_uframe = (start + skip) & (mod - 1);
+
+ /* report high speed start in uframes; full speed, in frames */
+ urb->start_frame = start & (mod - 1);
+ if (!stream->highspeed)
+ urb->start_frame >>= 3;
+ return status;
+
+ fail:
+ iso_sched_free(stream, sched);
+ urb->hcpriv = NULL;
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream,
+ struct ehci_itd *itd)
+{
+ int i;
+
+ /* it's been recently zeroed */
+ itd->hw_next = EHCI_LIST_END(ehci);
+ itd->hw_bufp [0] = stream->buf0;
+ itd->hw_bufp [1] = stream->buf1;
+ itd->hw_bufp [2] = stream->buf2;
+
+ for (i = 0; i < 8; i++)
+ itd->index[i] = -1;
+
+ /* All other fields are filled when scheduling */
+}
+
+static inline void
+itd_patch(
+ struct ehci_hcd *ehci,
+ struct ehci_itd *itd,
+ struct ehci_iso_sched *iso_sched,
+ unsigned index,
+ u16 uframe
+)
+{
+ struct ehci_iso_packet *uf = &iso_sched->packet [index];
+ unsigned pg = itd->pg;
+
+ // BUG_ON (pg == 6 && uf->cross);
+
+ uframe &= 0x07;
+ itd->index [uframe] = index;
+
+ itd->hw_transaction[uframe] = uf->transaction;
+ itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12);
+ itd->hw_bufp[pg] |= cpu_to_hc32(ehci, uf->bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32));
+
+ /* iso_frame_desc[].offset must be strictly increasing */
+ if (unlikely (uf->cross)) {
+ u64 bufp = uf->bufp + 4096;
+
+ itd->pg = ++pg;
+ itd->hw_bufp[pg] |= cpu_to_hc32(ehci, bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(bufp >> 32));
+ }
+}
+
+static inline void
+itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
+{
+ union ehci_shadow *prev = &ehci->pshadow[frame];
+ __hc32 *hw_p = &ehci->periodic[frame];
+ union ehci_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip any iso nodes which might belong to previous microframes */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(ehci, *hw_p);
+ if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(ehci, prev, type);
+ hw_p = shadow_next_periodic(ehci, &here, type);
+ here = *prev;
+ }
+
+ itd->itd_next = here;
+ itd->hw_next = *hw_p;
+ prev->itd = itd;
+ itd->frame = frame;
+ wmb ();
+ *hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
+}
+
+/* fit urb's itds into the selected schedule slot; activate as needed */
+static void itd_link_urb(
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned mod,
+ struct ehci_iso_stream *stream
+)
+{
+ int packet;
+ unsigned next_uframe, uframe, frame;
+ struct ehci_iso_sched *iso_sched = urb->hcpriv;
+ struct ehci_itd *itd;
+
+ next_uframe = stream->next_uframe & (mod - 1);
+
+ if (unlikely (list_empty(&stream->td_list)))
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ += stream->bandwidth;
+
+ if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
+ }
+
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
+
+ /* fill iTDs uframe by uframe */
+ for (packet = iso_sched->first_packet, itd = NULL;
+ packet < urb->number_of_packets;) {
+ if (itd == NULL) {
+ /* ASSERT: we have all necessary itds */
+ // BUG_ON (list_empty (&iso_sched->td_list));
+
+ /* ASSERT: no itds for this endpoint in this uframe */
+
+ itd = list_entry (iso_sched->td_list.next,
+ struct ehci_itd, itd_list);
+ list_move_tail (&itd->itd_list, &stream->td_list);
+ itd->stream = stream;
+ itd->urb = urb;
+ itd_init (ehci, stream, itd);
+ }
+
+ uframe = next_uframe & 0x07;
+ frame = next_uframe >> 3;
+
+ itd_patch(ehci, itd, iso_sched, packet, uframe);
+
+ next_uframe += stream->uperiod;
+ next_uframe &= mod - 1;
+ packet++;
+
+ /* link completed itds into the schedule */
+ if (((next_uframe >> 3) != frame)
+ || packet == urb->number_of_packets) {
+ itd_link(ehci, frame & (ehci->periodic_size - 1), itd);
+ itd = NULL;
+ }
+ }
+ stream->next_uframe = next_uframe;
+
+ /* don't need that schedule data any more */
+ iso_sched_free (stream, iso_sched);
+ urb->hcpriv = stream;
+
+ ++ehci->isoc_count;
+ enable_periodic(ehci);
+}
+
+#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+/* Process and recycle a completed ITD. Return true iff its urb completed,
+ * and hence its completion callback probably added things to the hardware
+ * schedule.
+ *
+ * Note that we carefully avoid recycling this descriptor until after any
+ * completion callback runs, so that it won't be reused quickly. That is,
+ * assuming (a) no more than two urbs per frame on this endpoint, and also
+ * (b) only this endpoint's completions submit URBs. It seems some silicon
+ * corrupts things if you reuse completed descriptors very quickly...
+ */
+static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+ struct urb *urb = itd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ unsigned uframe;
+ int urb_index = -1;
+ struct ehci_iso_stream *stream = itd->stream;
+ struct usb_device *dev;
+ bool retval = false;
+
+ /* for each uframe with a packet */
+ for (uframe = 0; uframe < 8; uframe++) {
+ if (likely (itd->index[uframe] == -1))
+ continue;
+ urb_index = itd->index[uframe];
+ desc = &urb->iso_frame_desc [urb_index];
+
+ t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
+ itd->hw_transaction [uframe] = 0;
+
+ /* report transfer status */
+ if (unlikely (t & ISO_ERRS)) {
+ urb->error_count++;
+ if (t & EHCI_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & EHCI_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & EHCI_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+
+ /* HC need not update length with this error */
+ if (!(t & EHCI_ISOC_BABBLE)) {
+ desc->actual_length = EHCI_ITD_LENGTH(t);
+ urb->actual_length += desc->actual_length;
+ }
+ } else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
+ desc->status = 0;
+ desc->actual_length = EHCI_ITD_LENGTH(t);
+ urb->actual_length += desc->actual_length;
+ } else {
+ /* URB was too late */
+ urb->error_count++;
+ }
+ }
+
+ /* handle completion now? */
+ if (likely ((urb_index + 1) != urb->number_of_packets))
+ goto done;
+
+ /* ASSERT: it's really the last itd for this urb
+ list_for_each_entry (itd, &stream->td_list, itd_list)
+ BUG_ON (itd->urb == urb);
+ */
+
+ /* give urb back to the driver; completion often (re)submits */
+ dev = urb->dev;
+ ehci_urb_done(ehci, urb, 0);
+ retval = true;
+ urb = NULL;
+
+ --ehci->isoc_count;
+ disable_periodic(ehci);
+
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+ if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
+ }
+
+ if (unlikely(list_is_singular(&stream->td_list)))
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ -= stream->bandwidth;
+
+done:
+ itd->urb = NULL;
+
+ /* Add to the end of the free list for later reuse */
+ list_move_tail(&itd->itd_list, &stream->free_list);
+
+ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */
+ if (list_empty(&stream->td_list)) {
+ list_splice_tail_init(&stream->free_list,
+ &ehci->cached_itd_list);
+ start_free_itds(ehci);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct ehci_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find (ehci, urb);
+ if (unlikely (stream == NULL)) {
+ ehci_dbg (ehci, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (unlikely(urb->interval != stream->uperiod)) {
+ ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+ stream->uperiod, urb->interval);
+ goto done;
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length,
+ urb->number_of_packets, urb->interval,
+ stream);
+#endif
+
+ /* allocate ITDs w/o locking anything */
+ status = itd_urb_transaction (stream, ehci, urb, mem_flags);
+ if (unlikely (status < 0)) {
+ ehci_dbg (ehci, "can't init itds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+ status = iso_stream_schedule(ehci, urb, stream);
+ if (likely(status == 0)) {
+ itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+ } else if (status > 0) {
+ status = 0;
+ ehci_urb_done(ehci, urb, 0);
+ } else {
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ }
+ done_not_linked:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ done:
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Split ISO TDs" ... used for USB 1.1 devices going through the
+ * TTs in USB 2.0 hubs. These need microframe scheduling.
+ */
+
+static inline void
+sitd_sched_init(
+ struct ehci_hcd *ehci,
+ struct ehci_iso_sched *iso_sched,
+ struct ehci_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many frames are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->ps.period;
+
+ /* figure out per-frame sitd fields that we'll need later
+ * when we fit new sitds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct ehci_iso_packet *packet = &iso_sched->packet [i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc [i].length & 0x03ff;
+ buf = dma + urb->iso_frame_desc [i].offset;
+
+ trans = SITD_STS_ACTIVE;
+ if (((i + 1) == urb->number_of_packets)
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= SITD_IOC;
+ trans |= length << 16;
+ packet->transaction = cpu_to_hc32(ehci, trans);
+
+ /* might need to cross a buffer page within a td */
+ packet->bufp = buf;
+ packet->buf1 = (buf + length) & ~0x0fff;
+ if (packet->buf1 != (buf & ~(u64)0x0fff))
+ packet->cross = 1;
+
+ /* OUT uses multiple start-splits */
+ if (stream->bEndpointAddress & USB_DIR_IN)
+ continue;
+ length = (length + 187) / 188;
+ if (length > 1) /* BEGIN vs ALL */
+ length |= 1 << 3;
+ packet->buf1 |= length;
+ }
+}
+
+static int
+sitd_urb_transaction (
+ struct ehci_iso_stream *stream,
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ gfp_t mem_flags
+)
+{
+ struct ehci_sitd *sitd;
+ dma_addr_t sitd_dma;
+ int i;
+ struct ehci_iso_sched *iso_sched;
+ unsigned long flags;
+
+ iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+ if (iso_sched == NULL)
+ return -ENOMEM;
+
+ sitd_sched_init(ehci, iso_sched, stream, urb);
+
+ /* allocate/init sITDs */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+ /* NOTE: for now, we don't try to handle wraparound cases
+ * for IN (using sitd->hw_backpointer, like a FSTN), which
+ * means we never need two sitds for full speed packets.
+ */
+
+ /*
+ * Use siTDs from the free list, but not siTDs that may
+ * still be in use by the hardware.
+ */
+ if (likely(!list_empty(&stream->free_list))) {
+ sitd = list_first_entry(&stream->free_list,
+ struct ehci_sitd, sitd_list);
+ if (sitd->frame == ehci->now_frame)
+ goto alloc_sitd;
+ list_del (&sitd->sitd_list);
+ sitd_dma = sitd->sitd_dma;
+ } else {
+ alloc_sitd:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
+ &sitd_dma);
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (!sitd) {
+ iso_sched_free(stream, iso_sched);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return -ENOMEM;
+ }
+ }
+
+ memset (sitd, 0, sizeof *sitd);
+ sitd->sitd_dma = sitd_dma;
+ sitd->frame = NO_FRAME;
+ list_add (&sitd->sitd_list, &iso_sched->td_list);
+ }
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = iso_sched;
+ urb->error_count = 0;
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+sitd_patch(
+ struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream,
+ struct ehci_sitd *sitd,
+ struct ehci_iso_sched *iso_sched,
+ unsigned index
+)
+{
+ struct ehci_iso_packet *uf = &iso_sched->packet [index];
+ u64 bufp = uf->bufp;
+
+ sitd->hw_next = EHCI_LIST_END(ehci);
+ sitd->hw_fullspeed_ep = stream->address;
+ sitd->hw_uframe = stream->splits;
+ sitd->hw_results = uf->transaction;
+ sitd->hw_backpointer = EHCI_LIST_END(ehci);
+
+ bufp = uf->bufp;
+ sitd->hw_buf[0] = cpu_to_hc32(ehci, bufp);
+ sitd->hw_buf_hi[0] = cpu_to_hc32(ehci, bufp >> 32);
+
+ sitd->hw_buf[1] = cpu_to_hc32(ehci, uf->buf1);
+ if (uf->cross)
+ bufp += 4096;
+ sitd->hw_buf_hi[1] = cpu_to_hc32(ehci, bufp >> 32);
+ sitd->index = index;
+}
+
+static inline void
+sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
+{
+ /* note: sitd ordering could matter (CSPLIT then SSPLIT) */
+ sitd->sitd_next = ehci->pshadow [frame];
+ sitd->hw_next = ehci->periodic [frame];
+ ehci->pshadow [frame].sitd = sitd;
+ sitd->frame = frame;
+ wmb ();
+ ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD);
+}
+
+/* fit urb's sitds into the selected schedule slot; activate as needed */
+static void sitd_link_urb(
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned mod,
+ struct ehci_iso_stream *stream
+)
+{
+ int packet;
+ unsigned next_uframe;
+ struct ehci_iso_sched *sched = urb->hcpriv;
+ struct ehci_sitd *sitd;
+
+ next_uframe = stream->next_uframe;
+
+ if (list_empty(&stream->td_list))
+ /* usbfs ignores TT bandwidth */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ += stream->bandwidth;
+
+ if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
+ }
+
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
+
+ /* fill sITDs frame by frame */
+ for (packet = sched->first_packet, sitd = NULL;
+ packet < urb->number_of_packets;
+ packet++) {
+
+ /* ASSERT: we have all necessary sitds */
+ BUG_ON (list_empty (&sched->td_list));
+
+ /* ASSERT: no itds for this endpoint in this frame */
+
+ sitd = list_entry (sched->td_list.next,
+ struct ehci_sitd, sitd_list);
+ list_move_tail (&sitd->sitd_list, &stream->td_list);
+ sitd->stream = stream;
+ sitd->urb = urb;
+
+ sitd_patch(ehci, stream, sitd, sched, packet);
+ sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
+ sitd);
+
+ next_uframe += stream->uperiod;
+ }
+ stream->next_uframe = next_uframe & (mod - 1);
+
+ /* don't need that schedule data any more */
+ iso_sched_free (stream, sched);
+ urb->hcpriv = stream;
+
+ ++ehci->isoc_count;
+ enable_periodic(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
+ | SITD_STS_XACT | SITD_STS_MMF)
+
+/* Process and recycle a completed SITD. Return true iff its urb completed,
+ * and hence its completion callback probably added things to the hardware
+ * schedule.
+ *
+ * Note that we carefully avoid recycling this descriptor until after any
+ * completion callback runs, so that it won't be reused quickly. That is,
+ * assuming (a) no more than two urbs per frame on this endpoint, and also
+ * (b) only this endpoint's completions submit URBs. It seems some silicon
+ * corrupts things if you reuse completed descriptors very quickly...
+ */
+static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+{
+ struct urb *urb = sitd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ int urb_index = -1;
+ struct ehci_iso_stream *stream = sitd->stream;
+ struct usb_device *dev;
+ bool retval = false;
+
+ urb_index = sitd->index;
+ desc = &urb->iso_frame_desc [urb_index];
+ t = hc32_to_cpup(ehci, &sitd->hw_results);
+
+ /* report transfer status */
+ if (unlikely(t & SITD_ERRS)) {
+ urb->error_count++;
+ if (t & SITD_STS_DBE)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & SITD_STS_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* XACT, MMF, etc */
+ desc->status = -EPROTO;
+ } else if (unlikely(t & SITD_STS_ACTIVE)) {
+ /* URB was too late */
+ urb->error_count++;
+ } else {
+ desc->status = 0;
+ desc->actual_length = desc->length - SITD_LENGTH(t);
+ urb->actual_length += desc->actual_length;
+ }
+
+ /* handle completion now? */
+ if ((urb_index + 1) != urb->number_of_packets)
+ goto done;
+
+ /* ASSERT: it's really the last sitd for this urb
+ list_for_each_entry (sitd, &stream->td_list, sitd_list)
+ BUG_ON (sitd->urb == urb);
+ */
+
+ /* give urb back to the driver; completion often (re)submits */
+ dev = urb->dev;
+ ehci_urb_done(ehci, urb, 0);
+ retval = true;
+ urb = NULL;
+
+ --ehci->isoc_count;
+ disable_periodic(ehci);
+
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+ if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
+ }
+
+ if (list_is_singular(&stream->td_list))
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ -= stream->bandwidth;
+
+done:
+ sitd->urb = NULL;
+
+ /* Add to the end of the free list for later reuse */
+ list_move_tail(&sitd->sitd_list, &stream->free_list);
+
+ /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */
+ if (list_empty(&stream->td_list)) {
+ list_splice_tail_init(&stream->free_list,
+ &ehci->cached_sitd_list);
+ start_free_itds(ehci);
+ }
+
+ return retval;
+}
+
+
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct ehci_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find (ehci, urb);
+ if (stream == NULL) {
+ ehci_dbg (ehci, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (urb->interval != stream->ps.period) {
+ ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+ stream->ps.period, urb->interval);
+ goto done;
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "submit %p dev%s ep%d%s-iso len %d\n",
+ urb, urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length);
+#endif
+
+ /* allocate SITDs */
+ status = sitd_urb_transaction (stream, ehci, urb, mem_flags);
+ if (status < 0) {
+ ehci_dbg (ehci, "can't init sitds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+ status = iso_stream_schedule(ehci, urb, stream);
+ if (likely(status == 0)) {
+ sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+ } else if (status > 0) {
+ status = 0;
+ ehci_urb_done(ehci, urb, 0);
+ } else {
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ }
+ done_not_linked:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ done:
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_isoc(struct ehci_hcd *ehci)
+{
+ unsigned uf, now_frame, frame;
+ unsigned fmask = ehci->periodic_size - 1;
+ bool modified, live;
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
+ * Touches as few pages as possible: cache-friendly.
+ */
+ if (ehci->rh_state >= EHCI_RH_RUNNING) {
+ uf = ehci_read_frame_index(ehci);
+ now_frame = (uf >> 3) & fmask;
+ live = true;
+ } else {
+ now_frame = (ehci->last_iso_frame - 1) & fmask;
+ live = false;
+ }
+ ehci->now_frame = now_frame;
+
+ frame = ehci->last_iso_frame;
+ for (;;) {
+ union ehci_shadow q, *q_p;
+ __hc32 type, *hw_p;
+
+restart:
+ /* scan each element in frame's queue for completions */
+ q_p = &ehci->pshadow [frame];
+ hw_p = &ehci->periodic [frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE(ehci, *hw_p);
+ modified = false;
+
+ while (q.ptr != NULL) {
+ switch (hc32_to_cpu(ehci, type)) {
+ case Q_TYPE_ITD:
+ /* If this ITD is still active, leave it for
+ * later processing ... check the next entry.
+ * No need to check for activity unless the
+ * frame is current.
+ */
+ if (frame == now_frame && live) {
+ rmb();
+ for (uf = 0; uf < 8; uf++) {
+ if (q.itd->hw_transaction[uf] &
+ ITD_ACTIVE(ehci))
+ break;
+ }
+ if (uf < 8) {
+ q_p = &q.itd->itd_next;
+ hw_p = &q.itd->hw_next;
+ type = Q_NEXT_TYPE(ehci,
+ q.itd->hw_next);
+ q = *q_p;
+ break;
+ }
+ }
+
+ /* Take finished ITDs out of the schedule
+ * and process them: recycle, maybe report
+ * URB completion. HC won't cache the
+ * pointer for much longer, if at all.
+ */
+ *q_p = q.itd->itd_next;
+ if (!ehci->use_dummy_qh ||
+ q.itd->hw_next != EHCI_LIST_END(ehci))
+ *hw_p = q.itd->hw_next;
+ else
+ *hw_p = cpu_to_hc32(ehci,
+ ehci->dummy->qh_dma);
+ type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
+ wmb();
+ modified = itd_complete (ehci, q.itd);
+ q = *q_p;
+ break;
+ case Q_TYPE_SITD:
+ /* If this SITD is still active, leave it for
+ * later processing ... check the next entry.
+ * No need to check for activity unless the
+ * frame is current.
+ */
+ if (((frame == now_frame) ||
+ (((frame + 1) & fmask) == now_frame))
+ && live
+ && (q.sitd->hw_results &
+ SITD_ACTIVE(ehci))) {
+
+ q_p = &q.sitd->sitd_next;
+ hw_p = &q.sitd->hw_next;
+ type = Q_NEXT_TYPE(ehci,
+ q.sitd->hw_next);
+ q = *q_p;
+ break;
+ }
+
+ /* Take finished SITDs out of the schedule
+ * and process them: recycle, maybe report
+ * URB completion.
+ */
+ *q_p = q.sitd->sitd_next;
+ if (!ehci->use_dummy_qh ||
+ q.sitd->hw_next != EHCI_LIST_END(ehci))
+ *hw_p = q.sitd->hw_next;
+ else
+ *hw_p = cpu_to_hc32(ehci,
+ ehci->dummy->qh_dma);
+ type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
+ wmb();
+ modified = sitd_complete (ehci, q.sitd);
+ q = *q_p;
+ break;
+ default:
+ ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
+ type, frame, q.ptr);
+ // BUG ();
+ /* FALL THROUGH */
+ case Q_TYPE_QH:
+ case Q_TYPE_FSTN:
+ /* End of the iTDs and siTDs */
+ q.ptr = NULL;
+ break;
+ }
+
+ /* assume completion callbacks modify the queue */
+ if (unlikely(modified && ehci->isoc_count > 0))
+ goto restart;
+ }
+
+ /* Stop when we have reached the current frame */
+ if (frame == now_frame)
+ break;
+
+ /* The last frame may still have active siTDs */
+ ehci->last_iso_frame = frame;
+ frame = (frame + 1) & fmask;
+ }
+}
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
new file mode 100644
index 000000000..3d86cc2ff
--- /dev/null
+++ b/drivers/usb/host/ehci-sead3.c
@@ -0,0 +1,185 @@
+/*
+ * MIPS CI13320A EHCI Host Controller driver
+ * Based on "ehci-au1xxx.c" by K.Boge <karsten.boge@amd.com>
+ *
+ * Copyright (C) 2012 MIPS Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+static int ehci_sead3_setup(struct usb_hcd *hcd)
+{
+ int ret;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci->caps = hcd->regs + 0x100;
+
+#ifdef __BIG_ENDIAN
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+#endif
+
+ ret = ehci_setup(hcd);
+ if (ret)
+ return ret;
+
+ ehci->need_io_watchdog = 0;
+
+ /* Set burst length to 16 words. */
+ ehci_writel(ehci, 0x1010, &ehci->regs->reserved1[1]);
+
+ return ret;
+}
+
+const struct hc_driver ehci_sead3_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SEAD-3 EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ *
+ */
+ .reset = ehci_sead3_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (pdev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[1] is not IORESOURCE_IRQ");
+ return -ENOMEM;
+ }
+ hcd = usb_create_hcd(&ehci_sead3_hc_driver, &pdev->dev, "SEAD-3");
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto err1;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ /* Root hub has integrated TT. */
+ hcd->has_tt = 1;
+
+ ret = usb_add_hcd(hcd, pdev->resource[1].start,
+ IRQF_SHARED);
+ if (ret == 0) {
+ platform_set_drvdata(pdev, hcd);
+ device_wakeup_enable(hcd->self.controller);
+ return ret;
+ }
+
+err1:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ehci_hcd_sead3_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ bool do_wakeup = device_may_wakeup(dev);
+
+ return ehci_suspend(hcd, do_wakeup);
+}
+
+static int ehci_hcd_sead3_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ ehci_resume(hcd, false);
+ return 0;
+}
+
+static const struct dev_pm_ops sead3_ehci_pmops = {
+ .suspend = ehci_hcd_sead3_drv_suspend,
+ .resume = ehci_hcd_sead3_drv_resume,
+};
+
+#define SEAD3_EHCI_PMOPS (&sead3_ehci_pmops)
+
+#else
+#define SEAD3_EHCI_PMOPS NULL
+#endif
+
+static struct platform_driver ehci_hcd_sead3_driver = {
+ .probe = ehci_hcd_sead3_drv_probe,
+ .remove = ehci_hcd_sead3_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "sead3-ehci",
+ .pm = SEAD3_EHCI_PMOPS,
+ }
+};
+
+MODULE_ALIAS("platform:sead3-ehci");
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
new file mode 100644
index 000000000..5caf88d67
--- /dev/null
+++ b/drivers/usb/host/ehci-sh.c
@@ -0,0 +1,195 @@
+/*
+ * SuperH EHCI host controller driver
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * Based on ohci-sh.c and ehci-atmel.c.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/platform_data/ehci-sh.h>
+
+struct ehci_sh_priv {
+ struct clk *iclk, *fclk;
+ struct usb_hcd *hcd;
+};
+
+static int ehci_sh_reset(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci->caps = hcd->regs;
+
+ return ehci_setup(hcd);
+}
+
+static const struct hc_driver ehci_sh_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SuperH EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_sh_reset,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_hcd_sh_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct ehci_sh_priv *priv;
+ struct ehci_sh_platdata *pdata;
+ struct usb_hcd *hcd;
+ int irq, ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ ret = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ /* initialize hcd */
+ hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
+ GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto fail_request_resource;
+ }
+
+ /* These are optional, we don't care if they fail */
+ priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
+ if (IS_ERR(priv->fclk))
+ priv->fclk = NULL;
+
+ priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
+ if (IS_ERR(priv->iclk))
+ priv->iclk = NULL;
+
+ clk_enable(priv->fclk);
+ clk_enable(priv->iclk);
+
+ if (pdata && pdata->phy_init)
+ pdata->phy_init();
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to add hcd");
+ goto fail_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ priv->hcd = hcd;
+ platform_set_drvdata(pdev, priv);
+
+ return ret;
+
+fail_add_hcd:
+ clk_disable(priv->iclk);
+ clk_disable(priv->fclk);
+
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
+
+ return ret;
+}
+
+static int ehci_hcd_sh_remove(struct platform_device *pdev)
+{
+ struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = priv->hcd;
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ clk_disable(priv->fclk);
+ clk_disable(priv->iclk);
+
+ return 0;
+}
+
+static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
+{
+ struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = priv->hcd;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver ehci_hcd_sh_driver = {
+ .probe = ehci_hcd_sh_probe,
+ .remove = ehci_hcd_sh_remove,
+ .shutdown = ehci_hcd_sh_shutdown,
+ .driver = {
+ .name = "sh_ehci",
+ },
+};
+
+MODULE_ALIAS("platform:sh_ehci");
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
new file mode 100644
index 000000000..34e14746b
--- /dev/null
+++ b/drivers/usb/host/ehci-spear.c
@@ -0,0 +1,190 @@
+/*
+* Driver for EHCI HCD on SPEAr SOC
+*
+* Copyright (C) 2010 ST Micro Electronics,
+* Deepak Sikri <deepak.sikri@st.com>
+*
+* Based on various ehci-*.c drivers
+*
+* This file is subject to the terms and conditions of the GNU General Public
+* License. See the file COPYING in the main directory of this archive for
+* more details.
+*/
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+#define DRIVER_DESC "EHCI SPEAr driver"
+
+static const char hcd_name[] = "SPEAr-ehci";
+
+struct spear_ehci {
+ struct clk *clk;
+};
+
+#define to_spear_ehci(hcd) (struct spear_ehci *)(hcd_to_ehci(hcd)->priv)
+
+static struct hc_driver __read_mostly ehci_spear_hc_driver;
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_spear_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ bool do_wakeup = device_may_wakeup(dev);
+
+ return ehci_suspend(hcd, do_wakeup);
+}
+
+static int ehci_spear_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ ehci_resume(hcd, false);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
+ ehci_spear_drv_resume);
+
+static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd ;
+ struct spear_ehci *sehci;
+ struct resource *res;
+ struct clk *usbh_clk;
+ const struct hc_driver *driver = &ehci_spear_hc_driver;
+ int irq, retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = irq;
+ goto fail;
+ }
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval)
+ goto fail;
+
+ usbh_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usbh_clk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = PTR_ERR(usbh_clk);
+ goto fail;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err_put_hcd;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ sehci = to_spear_ehci(hcd);
+ sehci->clk = usbh_clk;
+
+ /* registers start at offset 0x0 */
+ hcd_to_ehci(hcd)->caps = hcd->regs;
+
+ clk_prepare_enable(sehci->clk);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto err_stop_ehci;
+
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+
+err_stop_ehci:
+ clk_disable_unprepare(sehci->clk);
+err_put_hcd:
+ usb_put_hcd(hcd);
+fail:
+ dev_err(&pdev->dev, "init fail, %d\n", retval);
+
+ return retval ;
+}
+
+static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct spear_ehci *sehci = to_spear_ehci(hcd);
+
+ usb_remove_hcd(hcd);
+
+ if (sehci->clk)
+ clk_disable_unprepare(sehci->clk);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id spear_ehci_id_table[] = {
+ { .compatible = "st,spear600-ehci", },
+ { },
+};
+
+static struct platform_driver spear_ehci_hcd_driver = {
+ .probe = spear_ehci_hcd_drv_probe,
+ .remove = spear_ehci_hcd_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "spear-ehci",
+ .bus = &platform_bus_type,
+ .pm = &ehci_spear_pm_ops,
+ .of_match_table = spear_ehci_id_table,
+ }
+};
+
+static const struct ehci_driver_overrides spear_overrides __initdata = {
+ .extra_priv_size = sizeof(struct spear_ehci),
+};
+
+static int __init ehci_spear_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides);
+ return platform_driver_register(&spear_ehci_hcd_driver);
+}
+module_init(ehci_spear_init);
+
+static void __exit ehci_spear_cleanup(void)
+{
+ platform_driver_unregister(&spear_ehci_hcd_driver);
+}
+module_exit(ehci_spear_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:spear-ehci");
+MODULE_AUTHOR("Deepak Sikri");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c
new file mode 100644
index 000000000..7e4bd39cf
--- /dev/null
+++ b/drivers/usb/host/ehci-st.c
@@ -0,0 +1,375 @@
+/*
+ * ST EHCI driver
+ *
+ * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * Derived from ehci-platform.c
+ *
+ * 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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ehci_pdriver.h>
+
+#include "ehci.h"
+
+#define USB_MAX_CLKS 3
+
+struct st_ehci_platform_priv {
+ struct clk *clks[USB_MAX_CLKS];
+ struct clk *clk48;
+ struct reset_control *rst;
+ struct reset_control *pwr;
+ struct phy *phy;
+};
+
+#define DRIVER_DESC "EHCI STMicroelectronics driver"
+
+#define hcd_to_ehci_priv(h) \
+ ((struct st_ehci_platform_priv *)hcd_to_ehci(h)->priv)
+
+static const char hcd_name[] = "ehci-st";
+
+#define EHCI_CAPS_SIZE 0x10
+#define AHB2STBUS_INSREG01 (EHCI_CAPS_SIZE + 0x84)
+
+static int st_ehci_platform_reset(struct usb_hcd *hcd)
+{
+ struct platform_device *pdev = to_platform_device(hcd->self.controller);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+ u32 threshold;
+
+ /* Set EHCI packet buffer IN/OUT threshold to 128 bytes */
+ threshold = 128 | (128 << 16);
+ writel(threshold, hcd->regs + AHB2STBUS_INSREG01);
+
+ ehci->caps = hcd->regs + pdata->caps_offset;
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+static int st_ehci_platform_power_on(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk, ret;
+
+ ret = reset_control_deassert(priv->pwr);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ goto err_assert_power;
+
+ /* some SoCs don't have a dedicated 48Mhz clock, but those that do
+ need the rate to be explicitly set */
+ if (priv->clk48) {
+ ret = clk_set_rate(priv->clk48, 48000000);
+ if (ret)
+ goto err_assert_reset;
+ }
+
+ for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) {
+ ret = clk_prepare_enable(priv->clks[clk]);
+ if (ret)
+ goto err_disable_clks;
+ }
+
+ ret = phy_init(priv->phy);
+ if (ret)
+ goto err_disable_clks;
+
+ ret = phy_power_on(priv->phy);
+ if (ret)
+ goto err_exit_phy;
+
+ return 0;
+
+err_exit_phy:
+ phy_exit(priv->phy);
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(priv->clks[clk]);
+err_assert_reset:
+ reset_control_assert(priv->rst);
+err_assert_power:
+ reset_control_assert(priv->pwr);
+
+ return ret;
+}
+
+static void st_ehci_platform_power_off(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk;
+
+ reset_control_assert(priv->pwr);
+
+ reset_control_assert(priv->rst);
+
+ phy_power_off(priv->phy);
+
+ phy_exit(priv->phy);
+
+ for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--)
+ if (priv->clks[clk])
+ clk_disable_unprepare(priv->clks[clk]);
+
+}
+
+static struct hc_driver __read_mostly ehci_platform_hc_driver;
+
+static const struct ehci_driver_overrides platform_overrides __initconst = {
+ .reset = st_ehci_platform_reset,
+ .extra_priv_size = sizeof(struct st_ehci_platform_priv),
+};
+
+static struct usb_ehci_pdata ehci_platform_defaults = {
+ .power_on = st_ehci_platform_power_on,
+ .power_suspend = st_ehci_platform_power_off,
+ .power_off = st_ehci_platform_power_off,
+};
+
+static int st_ehci_platform_probe(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_mem;
+ struct usb_ehci_pdata *pdata = &ehci_platform_defaults;
+ struct st_ehci_platform_priv *priv;
+ struct ehci_hcd *ehci;
+ int err, irq, clk = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ dev_err(&dev->dev, "no irq provided");
+ return irq;
+ }
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ dev_err(&dev->dev, "no memory resource provided");
+ return -ENXIO;
+ }
+
+ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(dev, hcd);
+ dev->dev.platform_data = pdata;
+ priv = hcd_to_ehci_priv(hcd);
+ ehci = hcd_to_ehci(hcd);
+
+ priv->phy = devm_phy_get(&dev->dev, "usb");
+ if (IS_ERR(priv->phy)) {
+ err = PTR_ERR(priv->phy);
+ goto err_put_hcd;
+ }
+
+ for (clk = 0; clk < USB_MAX_CLKS; clk++) {
+ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+ if (IS_ERR(priv->clks[clk])) {
+ err = PTR_ERR(priv->clks[clk]);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->clks[clk] = NULL;
+ break;
+ }
+ }
+
+ /* some SoCs don't have a dedicated 48Mhz clock, but those that
+ do need the rate to be explicitly set */
+ priv->clk48 = devm_clk_get(&dev->dev, "clk48");
+ if (IS_ERR(priv->clk48)) {
+ dev_info(&dev->dev, "48MHz clk not found\n");
+ priv->clk48 = NULL;
+ }
+
+ priv->pwr = devm_reset_control_get_optional(&dev->dev, "power");
+ if (IS_ERR(priv->pwr)) {
+ err = PTR_ERR(priv->pwr);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->pwr = NULL;
+ }
+
+ priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset");
+ if (IS_ERR(priv->rst)) {
+ err = PTR_ERR(priv->rst);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->rst = NULL;
+ }
+
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ goto err_put_clks;
+ }
+
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+
+ hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto err_put_clks;
+ }
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_put_clks;
+
+ device_wakeup_enable(hcd->self.controller);
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(priv->clks[clk]);
+err_put_hcd:
+ if (pdata == &ehci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ usb_put_hcd(hcd);
+
+ return err;
+}
+
+static int st_ehci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+ int clk;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
+ for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++)
+ clk_put(priv->clks[clk]);
+
+ usb_put_hcd(hcd);
+
+ if (pdata == &ehci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int st_ehci_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = ehci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return ret;
+}
+
+static int st_ehci_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ int err;
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (pdata->power_on) {
+ err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
+
+ ehci_resume(hcd, false);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st_ehci_pm_ops, st_ehci_suspend, st_ehci_resume);
+
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id st_ehci_ids[] = {
+ { .compatible = "st,st-ehci-300x", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, st_ehci_ids);
+
+static struct platform_driver ehci_platform_driver = {
+ .probe = st_ehci_platform_probe,
+ .remove = st_ehci_platform_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "st-ehci",
+#ifdef CONFIG_PM_SLEEP
+ .pm = &st_ehci_pm_ops,
+#endif
+ .of_match_table = st_ehci_ids,
+ }
+};
+
+static int __init ehci_platform_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
+ return platform_driver_register(&ehci_platform_driver);
+}
+module_init(ehci_platform_init);
+
+static void __exit ehci_platform_cleanup(void)
+{
+ platform_driver_unregister(&ehci_platform_driver);
+}
+module_exit(ehci_platform_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c
new file mode 100644
index 000000000..5e44407aa
--- /dev/null
+++ b/drivers/usb/host/ehci-sysfs.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 by Alan Stern
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+
+/* Display the ports dedicated to the companion controller */
+static ssize_t show_companion(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ehci_hcd *ehci;
+ int nports, index, n;
+ int count = PAGE_SIZE;
+ char *ptr = buf;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ nports = HCS_N_PORTS(ehci->hcs_params);
+
+ for (index = 0; index < nports; ++index) {
+ if (test_bit(index, &ehci->companion_ports)) {
+ n = scnprintf(ptr, count, "%d\n", index + 1);
+ ptr += n;
+ count -= n;
+ }
+ }
+ return ptr - buf;
+}
+
+/*
+ * Dedicate or undedicate a port to the companion controller.
+ * Syntax is "[-]portnum", where a leading '-' sign means
+ * return control of the port to the EHCI controller.
+ */
+static ssize_t store_companion(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ehci_hcd *ehci;
+ int portnum, new_owner;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ new_owner = PORT_OWNER; /* Owned by companion */
+ if (sscanf(buf, "%d", &portnum) != 1)
+ return -EINVAL;
+ if (portnum < 0) {
+ portnum = - portnum;
+ new_owner = 0; /* Owned by EHCI */
+ }
+ if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
+ return -ENOENT;
+ portnum--;
+ if (new_owner)
+ set_bit(portnum, &ehci->companion_ports);
+ else
+ clear_bit(portnum, &ehci->companion_ports);
+ set_owner(ehci, portnum, new_owner);
+ return count;
+}
+static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
+
+
+/*
+ * Display / Set uframe_periodic_max
+ */
+static ssize_t show_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ehci_hcd *ehci;
+ int n;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
+ return n;
+}
+
+
+static ssize_t store_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ehci_hcd *ehci;
+ unsigned uframe_periodic_max;
+ unsigned uframe;
+ unsigned long flags;
+ ssize_t ret;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
+ return -EINVAL;
+
+ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
+ ehci_info(ehci, "rejecting invalid request for "
+ "uframe_periodic_max=%u\n", uframe_periodic_max);
+ return -EINVAL;
+ }
+
+ ret = -EINVAL;
+
+ /*
+ * lock, so that our checking does not race with possible periodic
+ * bandwidth allocation through submitting new urbs.
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /*
+ * for request to decrease max periodic bandwidth, we have to check
+ * to see whether the decrease is possible.
+ */
+ if (uframe_periodic_max < ehci->uframe_periodic_max) {
+ u8 allocated_max = 0;
+
+ for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
+ allocated_max = max(allocated_max,
+ ehci->bandwidth[uframe]);
+
+ if (allocated_max > uframe_periodic_max) {
+ ehci_info(ehci,
+ "cannot decrease uframe_periodic_max because "
+ "periodic bandwidth is already allocated "
+ "(%u > %u)\n",
+ allocated_max, uframe_periodic_max);
+ goto out_unlock;
+ }
+ }
+
+ /* increasing is always ok */
+
+ ehci_info(ehci, "setting max periodic bandwidth to %u%% "
+ "(== %u usec/uframe)\n",
+ 100*uframe_periodic_max/125, uframe_periodic_max);
+
+ if (uframe_periodic_max != 100)
+ ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
+
+ ehci->uframe_periodic_max = uframe_periodic_max;
+ ret = count;
+
+out_unlock:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return ret;
+}
+static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max);
+
+
+static inline int create_sysfs_files(struct ehci_hcd *ehci)
+{
+ struct device *controller = ehci_to_hcd(ehci)->self.controller;
+ int i = 0;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ i = device_create_file(controller, &dev_attr_companion);
+ if (i)
+ goto out;
+
+ i = device_create_file(controller, &dev_attr_uframe_periodic_max);
+out:
+ return i;
+}
+
+static inline void remove_sysfs_files(struct ehci_hcd *ehci)
+{
+ struct device *controller = ehci_to_hcd(ehci)->self.controller;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ device_remove_file(controller, &dev_attr_companion);
+
+ device_remove_file(controller, &dev_attr_uframe_periodic_max);
+}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
new file mode 100644
index 000000000..ff9af29b4
--- /dev/null
+++ b/drivers/usb/host/ehci-tegra.c
@@ -0,0 +1,621 @@
+/*
+ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2009 - 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/usb/ehci_def.h>
+#include <linux/usb/tegra_usb_phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include "ehci.h"
+
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+
+#define TEGRA_USB_DMA_ALIGN 32
+
+#define DRIVER_DESC "Tegra EHCI driver"
+#define DRV_NAME "tegra-ehci"
+
+static struct hc_driver __read_mostly tegra_ehci_hc_driver;
+static bool usb1_reset_attempted;
+
+struct tegra_ehci_soc_config {
+ bool has_hostpc;
+};
+
+struct tegra_ehci_hcd {
+ struct tegra_usb_phy *phy;
+ struct clk *clk;
+ struct reset_control *rst;
+ int port_resuming;
+ bool needs_double_reset;
+ enum tegra_usb_phy_port_speed port_speed;
+};
+
+/*
+ * The 1st USB controller contains some UTMI pad registers that are global for
+ * all the controllers on the chip. Those registers are also cleared when
+ * reset is asserted to the 1st controller. This means that the 1st controller
+ * can only be reset when no other controlled has finished probing. So we'll
+ * reset the 1st controller before doing any other setup on any of the
+ * controllers, and then never again.
+ *
+ * Since this is a PHY issue, the Tegra PHY driver should probably be doing
+ * the resetting of the USB controllers. But to keep compatibility with old
+ * device trees that don't have reset phandles in the PHYs, do it here.
+ * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI
+ * device isn't the first one to finish probing, so warn them.
+ */
+static int tegra_reset_usb_controller(struct platform_device *pdev)
+{
+ struct device_node *phy_np;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct tegra_ehci_hcd *tegra =
+ (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
+
+ phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0);
+ if (!phy_np)
+ return -ENOENT;
+
+ if (!usb1_reset_attempted) {
+ struct reset_control *usb1_reset;
+
+ usb1_reset = of_reset_control_get(phy_np, "usb");
+ if (IS_ERR(usb1_reset)) {
+ dev_warn(&pdev->dev,
+ "can't get utmi-pads reset from the PHY\n");
+ dev_warn(&pdev->dev,
+ "continuing, but please update your DT\n");
+ } else {
+ reset_control_assert(usb1_reset);
+ udelay(1);
+ reset_control_deassert(usb1_reset);
+ }
+
+ reset_control_put(usb1_reset);
+ usb1_reset_attempted = true;
+ }
+
+ if (!of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) {
+ reset_control_assert(tegra->rst);
+ udelay(1);
+ reset_control_deassert(tegra->rst);
+ }
+
+ of_node_put(phy_np);
+
+ return 0;
+}
+
+static int tegra_ehci_internal_port_reset(
+ struct ehci_hcd *ehci,
+ u32 __iomem *portsc_reg
+)
+{
+ u32 temp;
+ unsigned long flags;
+ int retval = 0;
+ int i, tries;
+ u32 saved_usbintr;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable);
+ /* disable USB interrupt */
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+
+ /*
+ * Here we have to do Port Reset at most twice for
+ * Port Enable bit to be set.
+ */
+ for (i = 0; i < 2; i++) {
+ temp = ehci_readl(ehci, portsc_reg);
+ temp |= PORT_RESET;
+ ehci_writel(ehci, temp, portsc_reg);
+ mdelay(10);
+ temp &= ~PORT_RESET;
+ ehci_writel(ehci, temp, portsc_reg);
+ mdelay(1);
+ tries = 100;
+ do {
+ mdelay(1);
+ /*
+ * Up to this point, Port Enable bit is
+ * expected to be set after 2 ms waiting.
+ * USB1 usually takes extra 45 ms, for safety,
+ * we take 100 ms as timeout.
+ */
+ temp = ehci_readl(ehci, portsc_reg);
+ } while (!(temp & PORT_PE) && tries--);
+ if (temp & PORT_PE)
+ break;
+ }
+ if (i == 2)
+ retval = -ETIMEDOUT;
+
+ /*
+ * Clear Connect Status Change bit if it's set.
+ * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared.
+ */
+ if (temp & PORT_CSC)
+ ehci_writel(ehci, PORT_CSC, portsc_reg);
+
+ /*
+ * Write to clear any interrupt status bits that might be set
+ * during port reset.
+ */
+ temp = ehci_readl(ehci, &ehci->regs->status);
+ ehci_writel(ehci, temp, &ehci->regs->status);
+
+ /* restore original interrupt enable bits */
+ ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable);
+ return retval;
+}
+
+static int tegra_ehci_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)ehci->priv;
+ u32 __iomem *status_reg;
+ u32 temp;
+ unsigned long flags;
+ int retval = 0;
+
+ status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
+
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ if (typeReq == GetPortStatus) {
+ temp = ehci_readl(ehci, status_reg);
+ if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
+ /* Resume completed, re-enable disconnect detection */
+ tegra->port_resuming = 0;
+ tegra_usb_phy_postresume(hcd->usb_phy);
+ }
+ }
+
+ else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
+ temp = ehci_readl(ehci, status_reg);
+ if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
+ retval = -EPIPE;
+ goto done;
+ }
+
+ temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E);
+ temp |= PORT_WKDISC_E | PORT_WKOC_E;
+ ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
+ /*
+ * If a transaction is in progress, there may be a delay in
+ * suspending the port. Poll until the port is suspended.
+ */
+ if (ehci_handshake(ehci, status_reg, PORT_SUSPEND,
+ PORT_SUSPEND, 5000))
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
+ set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
+ goto done;
+ }
+
+ /* For USB1 port we need to issue Port Reset twice internally */
+ if (tegra->needs_double_reset &&
+ (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return tegra_ehci_internal_port_reset(ehci, status_reg);
+ }
+
+ /*
+ * Tegra host controller will time the resume operation to clear the bit
+ * when the port control state switches to HS or FS Idle. This behavior
+ * is different from EHCI where the host controller driver is required
+ * to set this bit to a zero after the resume duration is timed in the
+ * driver.
+ */
+ else if (typeReq == ClearPortFeature &&
+ wValue == USB_PORT_FEAT_SUSPEND) {
+ temp = ehci_readl(ehci, status_reg);
+ if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
+ retval = -EPIPE;
+ goto done;
+ }
+
+ if (!(temp & PORT_SUSPEND))
+ goto done;
+
+ /* Disable disconnect detection during port resume */
+ tegra_usb_phy_preresume(hcd->usb_phy);
+
+ ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
+
+ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+ /* start resume signalling */
+ ehci_writel(ehci, temp | PORT_RESUME, status_reg);
+ set_bit(wIndex-1, &ehci->resuming_ports);
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ /* Poll until the controller clears RESUME and SUSPEND */
+ if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
+ pr_err("%s: timeout waiting for RESUME\n", __func__);
+ if (ehci_handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
+ ehci->reset_done[wIndex-1] = 0;
+ clear_bit(wIndex-1, &ehci->resuming_ports);
+
+ tegra->port_resuming = 1;
+ goto done;
+ }
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+
+ /* Handle the hub control events here */
+ return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+
+done:
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return retval;
+}
+
+struct dma_aligned_buffer {
+ void *kmalloc_ptr;
+ void *old_xfer_buffer;
+ u8 data[0];
+};
+
+static void free_dma_aligned_buffer(struct urb *urb)
+{
+ struct dma_aligned_buffer *temp;
+
+ if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+ return;
+
+ temp = container_of(urb->transfer_buffer,
+ struct dma_aligned_buffer, data);
+
+ if (usb_urb_dir_in(urb))
+ memcpy(temp->old_xfer_buffer, temp->data,
+ urb->transfer_buffer_length);
+ urb->transfer_buffer = temp->old_xfer_buffer;
+ kfree(temp->kmalloc_ptr);
+
+ urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+}
+
+static int alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
+{
+ struct dma_aligned_buffer *temp, *kmalloc_ptr;
+ size_t kmalloc_size;
+
+ if (urb->num_sgs || urb->sg ||
+ urb->transfer_buffer_length == 0 ||
+ !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1)))
+ return 0;
+
+ /* Allocate a buffer with enough padding for alignment */
+ kmalloc_size = urb->transfer_buffer_length +
+ sizeof(struct dma_aligned_buffer) + TEGRA_USB_DMA_ALIGN - 1;
+
+ kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+ if (!kmalloc_ptr)
+ return -ENOMEM;
+
+ /* Position our struct dma_aligned_buffer such that data is aligned */
+ temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1;
+ temp->kmalloc_ptr = kmalloc_ptr;
+ temp->old_xfer_buffer = urb->transfer_buffer;
+ if (usb_urb_dir_out(urb))
+ memcpy(temp->data, urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ urb->transfer_buffer = temp->data;
+
+ urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
+
+ return 0;
+}
+
+static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int ret;
+
+ ret = alloc_dma_aligned_buffer(urb, mem_flags);
+ if (ret)
+ return ret;
+
+ ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+ if (ret)
+ free_dma_aligned_buffer(urb);
+
+ return ret;
+}
+
+static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
+ free_dma_aligned_buffer(urb);
+}
+
+static const struct tegra_ehci_soc_config tegra30_soc_config = {
+ .has_hostpc = true,
+};
+
+static const struct tegra_ehci_soc_config tegra20_soc_config = {
+ .has_hostpc = false,
+};
+
+static const struct of_device_id tegra_ehci_of_match[] = {
+ { .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config },
+ { .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config },
+ { },
+};
+
+static int tegra_ehci_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct tegra_ehci_soc_config *soc_config;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct tegra_ehci_hcd *tegra;
+ int err = 0;
+ int irq;
+ struct usb_phy *u_phy;
+
+ match = of_match_device(tegra_ehci_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ soc_config = match->data;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, hcd);
+ ehci = hcd_to_ehci(hcd);
+ tegra = (struct tegra_ehci_hcd *)ehci->priv;
+
+ hcd->has_tt = 1;
+
+ tegra->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tegra->clk)) {
+ dev_err(&pdev->dev, "Can't get ehci clock\n");
+ err = PTR_ERR(tegra->clk);
+ goto cleanup_hcd_create;
+ }
+
+ tegra->rst = devm_reset_control_get(&pdev->dev, "usb");
+ if (IS_ERR(tegra->rst)) {
+ dev_err(&pdev->dev, "Can't get ehci reset\n");
+ err = PTR_ERR(tegra->rst);
+ goto cleanup_hcd_create;
+ }
+
+ err = clk_prepare_enable(tegra->clk);
+ if (err)
+ goto cleanup_hcd_create;
+
+ err = tegra_reset_usb_controller(pdev);
+ if (err)
+ goto cleanup_clk_en;
+
+ u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
+ if (IS_ERR(u_phy)) {
+ err = -EPROBE_DEFER;
+ goto cleanup_clk_en;
+ }
+ hcd->usb_phy = u_phy;
+
+ tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node,
+ "nvidia,needs-double-reset");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto cleanup_clk_en;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ ehci->caps = hcd->regs + 0x100;
+ ehci->has_hostpc = soc_config->has_hostpc;
+
+ err = usb_phy_init(hcd->usb_phy);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to initialize phy\n");
+ goto cleanup_clk_en;
+ }
+
+ u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
+ if (!u_phy->otg) {
+ err = -ENOMEM;
+ goto cleanup_phy;
+ }
+ u_phy->otg->host = hcd_to_bus(hcd);
+
+ err = usb_phy_set_suspend(hcd->usb_phy, 0);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to power on the phy\n");
+ goto cleanup_phy;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "Failed to get IRQ\n");
+ err = -ENODEV;
+ goto cleanup_phy;
+ }
+
+ otg_set_host(u_phy->otg, &hcd->self);
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add USB HCD\n");
+ goto cleanup_otg_set_host;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return err;
+
+cleanup_otg_set_host:
+ otg_set_host(u_phy->otg, NULL);
+cleanup_phy:
+ usb_phy_shutdown(hcd->usb_phy);
+cleanup_clk_en:
+ clk_disable_unprepare(tegra->clk);
+cleanup_hcd_create:
+ usb_put_hcd(hcd);
+ return err;
+}
+
+static int tegra_ehci_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct tegra_ehci_hcd *tegra =
+ (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
+
+ otg_set_host(hcd->usb_phy->otg, NULL);
+
+ usb_phy_shutdown(hcd->usb_phy);
+ usb_remove_hcd(hcd);
+
+ clk_disable_unprepare(tegra->clk);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void tegra_ehci_hcd_shutdown(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver tegra_ehci_driver = {
+ .probe = tegra_ehci_probe,
+ .remove = tegra_ehci_remove,
+ .shutdown = tegra_ehci_hcd_shutdown,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = tegra_ehci_of_match,
+ }
+};
+
+static int tegra_ehci_reset(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+ int txfifothresh;
+
+ retval = ehci_setup(hcd);
+ if (retval)
+ return retval;
+
+ /*
+ * We should really pull this value out of tegra_ehci_soc_config, but
+ * to avoid needing access to it, make use of the fact that Tegra20 is
+ * the only one so far that needs a value of 10, and Tegra20 is the
+ * only one which doesn't set has_hostpc.
+ */
+ txfifothresh = ehci->has_hostpc ? 0x10 : 10;
+ ehci_writel(ehci, txfifothresh << 16, &ehci->regs->txfill_tuning);
+
+ return 0;
+}
+
+static const struct ehci_driver_overrides tegra_overrides __initconst = {
+ .extra_priv_size = sizeof(struct tegra_ehci_hcd),
+ .reset = tegra_ehci_reset,
+};
+
+static int __init ehci_tegra_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info(DRV_NAME ": " DRIVER_DESC "\n");
+
+ ehci_init_driver(&tegra_ehci_hc_driver, &tegra_overrides);
+
+ /*
+ * The Tegra HW has some unusual quirks, which require Tegra-specific
+ * workarounds. We override certain hc_driver functions here to
+ * achieve that. We explicitly do not enhance ehci_driver_overrides to
+ * allow this more easily, since this is an unusual case, and we don't
+ * want to encourage others to override these functions by making it
+ * too easy.
+ */
+
+ tegra_ehci_hc_driver.map_urb_for_dma = tegra_ehci_map_urb_for_dma;
+ tegra_ehci_hc_driver.unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma;
+ tegra_ehci_hc_driver.hub_control = tegra_ehci_hub_control;
+
+ return platform_driver_register(&tegra_ehci_driver);
+}
+module_init(ehci_tegra_init);
+
+static void __exit ehci_tegra_cleanup(void)
+{
+ platform_driver_unregister(&tegra_ehci_driver);
+}
+module_exit(ehci_tegra_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_ehci_of_match);
diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c
new file mode 100644
index 000000000..bdb93b6a3
--- /dev/null
+++ b/drivers/usb/host/ehci-tilegx.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2012 Tilera Corporation. All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * Tilera TILE-Gx USB EHCI host controller driver.
+ */
+
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/usb/tilegx.h>
+#include <linux/usb.h>
+
+#include <asm/homecache.h>
+
+#include <gxio/iorpc_usb_host.h>
+#include <gxio/usb_host.h>
+
+static void tilegx_start_ehc(void)
+{
+}
+
+static void tilegx_stop_ehc(void)
+{
+}
+
+static int tilegx_ehci_setup(struct usb_hcd *hcd)
+{
+ int ret = ehci_init(hcd);
+
+ /*
+ * Some drivers do:
+ *
+ * struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ * ehci->need_io_watchdog = 0;
+ *
+ * here, but since this is a new driver we're going to leave the
+ * watchdog enabled. Later we may try to turn it off and see
+ * whether we run into any problems.
+ */
+
+ return ret;
+}
+
+static const struct hc_driver ehci_tilegx_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Tile-Gx EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * Generic hardware linkage.
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * Basic lifecycle operations.
+ */
+ .reset = tilegx_ehci_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * Managing I/O requests and associated device resources.
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * Scheduling support.
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * Root hub support.
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ pte_t pte = { 0 };
+ int my_cpu = smp_processor_id();
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Try to initialize our GXIO context; if we can't, the device
+ * doesn't exist.
+ */
+ if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0)
+ return -ENXIO;
+
+ hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto err_hcd;
+ }
+
+ /*
+ * We don't use rsrc_start to map in our registers, but seems like
+ * we ought to set it to something, so we use the register VA.
+ */
+ hcd->rsrc_start =
+ (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx);
+ hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx);
+ hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx);
+
+ tilegx_start_ehc();
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs =
+ hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ /* Create our IRQs and register them. */
+ pdata->irq = irq_alloc_hwirq(-1);
+ if (!pdata->irq) {
+ ret = -ENXIO;
+ goto err_no_irq;
+ }
+
+ tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU);
+
+ /* Configure interrupts. */
+ ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx,
+ cpu_x(my_cpu), cpu_y(my_cpu),
+ KERNEL_PL, pdata->irq);
+ if (ret) {
+ ret = -ENXIO;
+ goto err_have_irq;
+ }
+
+ /* Register all of our memory. */
+ pte = pte_set_home(pte, PAGE_HOME_HASH);
+ ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0);
+ if (ret) {
+ ret = -ENXIO;
+ goto err_have_irq;
+ }
+
+ ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED);
+ if (ret == 0) {
+ platform_set_drvdata(pdev, hcd);
+ device_wakeup_enable(hcd->self.controller);
+ return ret;
+ }
+
+err_have_irq:
+ irq_free_hwirq(pdata->irq);
+err_no_irq:
+ tilegx_stop_ehc();
+ usb_put_hcd(hcd);
+err_hcd:
+ gxio_usb_host_destroy(&pdata->usb_ctx);
+ return ret;
+}
+
+static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ tilegx_stop_ehc();
+ gxio_usb_host_destroy(&pdata->usb_ctx);
+ irq_free_hwirq(pdata->irq);
+
+ return 0;
+}
+
+static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev)
+{
+ usb_hcd_platform_shutdown(pdev);
+ ehci_hcd_tilegx_drv_remove(pdev);
+}
+
+static struct platform_driver ehci_hcd_tilegx_driver = {
+ .probe = ehci_hcd_tilegx_drv_probe,
+ .remove = ehci_hcd_tilegx_drv_remove,
+ .shutdown = ehci_hcd_tilegx_drv_shutdown,
+ .driver = {
+ .name = "tilegx-ehci",
+ }
+};
+
+MODULE_ALIAS("platform:tilegx-ehci");
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
new file mode 100644
index 000000000..424ac5d83
--- /dev/null
+++ b/drivers/usb/host/ehci-timer.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2012 by Alan Stern
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/* This file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/* Set a bit in the USBCMD register */
+static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+ ehci->command |= bit;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+ /* unblock posted write */
+ ehci_readl(ehci, &ehci->regs->command);
+}
+
+/* Clear a bit in the USBCMD register */
+static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+ ehci->command &= ~bit;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+ /* unblock posted write */
+ ehci_readl(ehci, &ehci->regs->command);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI timer support... Now using hrtimers.
+ *
+ * Lots of different events are triggered from ehci->hrtimer. Whenever
+ * the timer routine runs, it checks each possible event; events that are
+ * currently enabled and whose expiration time has passed get handled.
+ * The set of enabled events is stored as a collection of bitflags in
+ * ehci->enabled_hrtimer_events, and they are numbered in order of
+ * increasing delay values (ranging between 1 ms and 100 ms).
+ *
+ * Rather than implementing a sorted list or tree of all pending events,
+ * we keep track only of the lowest-numbered pending event, in
+ * ehci->next_hrtimer_event. Whenever ehci->hrtimer gets restarted, its
+ * expiration time is set to the timeout value for this event.
+ *
+ * As a result, events might not get handled right away; the actual delay
+ * could be anywhere up to twice the requested delay. This doesn't
+ * matter, because none of the events are especially time-critical. The
+ * ones that matter most all have a delay of 1 ms, so they will be
+ * handled after 2 ms at most, which is okay. In addition to this, we
+ * allow for an expiration range of 1 ms.
+ */
+
+/*
+ * Delay lengths for the hrtimer event types.
+ * Keep this list sorted by delay length, in the same order as
+ * the event types indexed by enum ehci_hrtimer_event in ehci.h.
+ */
+static unsigned event_delays_ns[] = {
+ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
+ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
+ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
+ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
+ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
+ 5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */
+ 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
+ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
+ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
+ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
+ 100 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IO_WATCHDOG */
+};
+
+/* Enable a pending hrtimer event */
+static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
+ bool resched)
+{
+ ktime_t *timeout = &ehci->hr_timeouts[event];
+
+ if (resched)
+ *timeout = ktime_add(ktime_get(),
+ ktime_set(0, event_delays_ns[event]));
+ ehci->enabled_hrtimer_events |= (1 << event);
+
+ /* Track only the lowest-numbered pending event */
+ if (event < ehci->next_hrtimer_event) {
+ ehci->next_hrtimer_event = event;
+ hrtimer_start_range_ns(&ehci->hrtimer, *timeout,
+ NSEC_PER_MSEC, HRTIMER_MODE_ABS);
+ }
+}
+
+
+/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
+static void ehci_poll_ASS(struct ehci_hcd *ehci)
+{
+ unsigned actual, want;
+
+ /* Don't enable anything if the controller isn't running (e.g., died) */
+ if (ehci->rh_state != EHCI_RH_RUNNING)
+ return;
+
+ want = (ehci->command & CMD_ASE) ? STS_ASS : 0;
+ actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 2-4 ms */
+ if (ehci->ASS_poll_count++ < 2) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
+ return;
+ }
+ ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ ehci->ASS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (ehci->async_count > 0)
+ ehci_set_command_bit(ehci, CMD_ASE);
+
+ } else { /* Running */
+ if (ehci->async_count == 0) {
+
+ /* Turn off the schedule after a while */
+ ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC,
+ true);
+ }
+ }
+}
+
+/* Turn off the async schedule after a brief delay */
+static void ehci_disable_ASE(struct ehci_hcd *ehci)
+{
+ ehci_clear_command_bit(ehci, CMD_ASE);
+}
+
+
+/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
+static void ehci_poll_PSS(struct ehci_hcd *ehci)
+{
+ unsigned actual, want;
+
+ /* Don't do anything if the controller isn't running (e.g., died) */
+ if (ehci->rh_state != EHCI_RH_RUNNING)
+ return;
+
+ want = (ehci->command & CMD_PSE) ? STS_PSS : 0;
+ actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 2-4 ms */
+ if (ehci->PSS_poll_count++ < 2) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
+ return;
+ }
+ ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ ehci->PSS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (ehci->periodic_count > 0)
+ ehci_set_command_bit(ehci, CMD_PSE);
+
+ } else { /* Running */
+ if (ehci->periodic_count == 0) {
+
+ /* Turn off the schedule after a while */
+ ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
+ true);
+ }
+ }
+}
+
+/* Turn off the periodic schedule after a brief delay */
+static void ehci_disable_PSE(struct ehci_hcd *ehci)
+{
+ ehci_clear_command_bit(ehci, CMD_PSE);
+}
+
+
+/* Poll the STS_HALT status bit; see when a dead controller stops */
+static void ehci_handle_controller_death(struct ehci_hcd *ehci)
+{
+ if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {
+
+ /* Give up after a few milliseconds */
+ if (ehci->died_poll_count++ < 5) {
+ /* Try again later */
+ ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
+ return;
+ }
+ ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
+ }
+
+ /* Clean up the mess */
+ ehci->rh_state = EHCI_RH_HALTED;
+ ehci_writel(ehci, 0, &ehci->regs->configured_flag);
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ ehci_work(ehci);
+ end_unlink_async(ehci);
+
+ /* Not in process context, so don't try to reset the controller */
+}
+
+/* start to unlink interrupt QHs */
+static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci)
+{
+ bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
+
+ /*
+ * Process all the QHs on the intr_unlink list that were added
+ * before the current unlink cycle began. The list is in
+ * temporal order, so stop when we reach the first entry in the
+ * current cycle. But if the root hub isn't running then
+ * process all the QHs on the list.
+ */
+ while (!list_empty(&ehci->intr_unlink_wait)) {
+ struct ehci_qh *qh;
+
+ qh = list_first_entry(&ehci->intr_unlink_wait,
+ struct ehci_qh, unlink_node);
+ if (!stopped && (qh->unlink_cycle ==
+ ehci->intr_unlink_wait_cycle))
+ break;
+ list_del_init(&qh->unlink_node);
+ start_unlink_intr(ehci, qh);
+ }
+
+ /* Handle remaining entries later */
+ if (!list_empty(&ehci->intr_unlink_wait)) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true);
+ ++ehci->intr_unlink_wait_cycle;
+ }
+}
+
+/* Handle unlinked interrupt QHs once they are gone from the hardware */
+static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
+{
+ bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
+
+ /*
+ * Process all the QHs on the intr_unlink list that were added
+ * before the current unlink cycle began. The list is in
+ * temporal order, so stop when we reach the first entry in the
+ * current cycle. But if the root hub isn't running then
+ * process all the QHs on the list.
+ */
+ ehci->intr_unlinking = true;
+ while (!list_empty(&ehci->intr_unlink)) {
+ struct ehci_qh *qh;
+
+ qh = list_first_entry(&ehci->intr_unlink, struct ehci_qh,
+ unlink_node);
+ if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle)
+ break;
+ list_del_init(&qh->unlink_node);
+ end_unlink_intr(ehci, qh);
+ }
+
+ /* Handle remaining entries later */
+ if (!list_empty(&ehci->intr_unlink)) {
+ ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
+ ++ehci->intr_unlink_cycle;
+ }
+ ehci->intr_unlinking = false;
+}
+
+
+/* Start another free-iTDs/siTDs cycle */
+static void start_free_itds(struct ehci_hcd *ehci)
+{
+ if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) {
+ ehci->last_itd_to_free = list_entry(
+ ehci->cached_itd_list.prev,
+ struct ehci_itd, itd_list);
+ ehci->last_sitd_to_free = list_entry(
+ ehci->cached_sitd_list.prev,
+ struct ehci_sitd, sitd_list);
+ ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true);
+ }
+}
+
+/* Wait for controller to stop using old iTDs and siTDs */
+static void end_free_itds(struct ehci_hcd *ehci)
+{
+ struct ehci_itd *itd, *n;
+ struct ehci_sitd *sitd, *sn;
+
+ if (ehci->rh_state < EHCI_RH_RUNNING) {
+ ehci->last_itd_to_free = NULL;
+ ehci->last_sitd_to_free = NULL;
+ }
+
+ list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
+ list_del(&itd->itd_list);
+ dma_pool_free(ehci->itd_pool, itd, itd->itd_dma);
+ if (itd == ehci->last_itd_to_free)
+ break;
+ }
+ list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
+ list_del(&sitd->sitd_list);
+ dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma);
+ if (sitd == ehci->last_sitd_to_free)
+ break;
+ }
+
+ if (!list_empty(&ehci->cached_itd_list) ||
+ !list_empty(&ehci->cached_sitd_list))
+ start_free_itds(ehci);
+}
+
+
+/* Handle lost (or very late) IAA interrupts */
+static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
+{
+ u32 cmd, status;
+
+ /*
+ * Lost IAA irqs wedge things badly; seen first with a vt8235.
+ * So we need this watchdog, but must protect it against both
+ * (a) SMP races against real IAA firing and retriggering, and
+ * (b) clean HC shutdown, when IAA watchdog was pending.
+ */
+ if (!ehci->iaa_in_progress || ehci->rh_state != EHCI_RH_RUNNING)
+ return;
+
+ /* If we get here, IAA is *REALLY* late. It's barely
+ * conceivable that the system is so busy that CMD_IAAD
+ * is still legitimately set, so let's be sure it's
+ * clear before we read STS_IAA. (The HC should clear
+ * CMD_IAAD when it sets STS_IAA.)
+ */
+ cmd = ehci_readl(ehci, &ehci->regs->command);
+
+ /*
+ * If IAA is set here it either legitimately triggered
+ * after the watchdog timer expired (_way_ late, so we'll
+ * still count it as lost) ... or a silicon erratum:
+ * - VIA seems to set IAA without triggering the IRQ;
+ * - IAAD potentially cleared without setting IAA.
+ */
+ status = ehci_readl(ehci, &ehci->regs->status);
+ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
+ COUNT(ehci->stats.lost_iaa);
+ ehci_writel(ehci, STS_IAA, &ehci->regs->status);
+ }
+
+ ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd);
+ end_unlink_async(ehci);
+}
+
+
+/* Enable the I/O watchdog, if appropriate */
+static void turn_on_io_watchdog(struct ehci_hcd *ehci)
+{
+ /* Not needed if the controller isn't running or it's already enabled */
+ if (ehci->rh_state != EHCI_RH_RUNNING ||
+ (ehci->enabled_hrtimer_events &
+ BIT(EHCI_HRTIMER_IO_WATCHDOG)))
+ return;
+
+ /*
+ * Isochronous transfers always need the watchdog.
+ * For other sorts we use it only if the flag is set.
+ */
+ if (ehci->isoc_count > 0 || (ehci->need_io_watchdog &&
+ ehci->async_count + ehci->intr_count > 0))
+ ehci_enable_event(ehci, EHCI_HRTIMER_IO_WATCHDOG, true);
+}
+
+
+/*
+ * Handler functions for the hrtimer event types.
+ * Keep this array in the same order as the event types indexed by
+ * enum ehci_hrtimer_event in ehci.h.
+ */
+static void (*event_handlers[])(struct ehci_hcd *) = {
+ ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
+ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
+ ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
+ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
+ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
+ ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */
+ unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
+ ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
+ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
+ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
+ ehci_work, /* EHCI_HRTIMER_IO_WATCHDOG */
+};
+
+static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
+{
+ struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer);
+ ktime_t now;
+ unsigned long events;
+ unsigned long flags;
+ unsigned e;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ events = ehci->enabled_hrtimer_events;
+ ehci->enabled_hrtimer_events = 0;
+ ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
+
+ /*
+ * Check each pending event. If its time has expired, handle
+ * the event; otherwise re-enable it.
+ */
+ now = ktime_get();
+ for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) {
+ if (now.tv64 >= ehci->hr_timeouts[e].tv64)
+ event_handlers[e](ehci);
+ else
+ ehci_enable_event(ehci, e, false);
+ }
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return HRTIMER_NORESTART;
+}
diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c
new file mode 100644
index 000000000..e42a29e8e
--- /dev/null
+++ b/drivers/usb/host/ehci-w90x900.c
@@ -0,0 +1,148 @@
+/*
+ * linux/driver/usb/host/ehci-w90x900.c
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.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.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+/* enable phy0 and phy1 for w90p910 */
+#define ENPHY (0x01<<8)
+#define PHY0_CTR (0xA4)
+#define PHY1_CTR (0xA8)
+
+#define DRIVER_DESC "EHCI w90x900 driver"
+
+static const char hcd_name[] = "ehci-w90x900 ";
+
+static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
+
+static int usb_w90x900_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource *res;
+ int retval = 0, irq;
+ unsigned long val;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, "w90x900 EHCI");
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err2;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ /* enable PHY 0,1,the regs only apply to w90p910
+ * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
+ * w90p910 IC relative to ehci->regs.
+ */
+ val = __raw_readl(ehci->regs+PHY0_CTR);
+ val |= ENPHY;
+ __raw_writel(val, ehci->regs+PHY0_CTR);
+
+ val = __raw_readl(ehci->regs+PHY1_CTR);
+ val |= ENPHY;
+ __raw_writel(val, ehci->regs+PHY1_CTR);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = irq;
+ goto err2;
+ }
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval != 0)
+ goto err2;
+
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+err2:
+ usb_put_hcd(hcd);
+err1:
+ return retval;
+}
+
+static void usb_w90x900_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
+
+static int ehci_w90x900_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_w90x900_probe(&ehci_w90x900_hc_driver, pdev);
+}
+
+static int ehci_w90x900_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_w90x900_remove(hcd, pdev);
+
+ return 0;
+}
+
+static struct platform_driver ehci_hcd_w90x900_driver = {
+ .probe = ehci_w90x900_probe,
+ .remove = ehci_w90x900_remove,
+ .driver = {
+ .name = "w90x900-ehci",
+ },
+};
+
+static int __init ehci_w90X900_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_w90x900_hc_driver, NULL);
+ return platform_driver_register(&ehci_hcd_w90x900_driver);
+}
+module_init(ehci_w90X900_init);
+
+static void __exit ehci_w90X900_cleanup(void)
+{
+ platform_driver_unregister(&ehci_hcd_w90x900_driver);
+}
+module_exit(ehci_w90X900_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_ALIAS("platform:w90p910-ehci");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
new file mode 100644
index 000000000..f54480850
--- /dev/null
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -0,0 +1,241 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * Bus Glue for Xilinx EHCI core on the of_platform bus
+ *
+ * Copyright (c) 2009 Xilinx, Inc.
+ *
+ * Based on "ehci-ppc-of.c" by Valentine Barshak <vbarshak@ru.mvista.com>
+ * and "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
+ * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/signal.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+/**
+ * ehci_xilinx_port_handed_over - hand the port out if failed to enable it
+ * @hcd: Pointer to the usb_hcd device to which the host controller bound
+ * @portnum:Port number to which the device is attached.
+ *
+ * This function is used as a place to tell the user that the Xilinx USB host
+ * controller does support LS devices. And in an HS only configuration, it
+ * does not support FS devices either. It is hoped that this can help a
+ * confused user.
+ *
+ * There are cases when the host controller fails to enable the port due to,
+ * for example, insufficient power that can be supplied to the device from
+ * the USB bus. In those cases, the messages printed here are not helpful.
+ */
+static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum)
+{
+ dev_warn(hcd->self.controller, "port %d cannot be enabled\n", portnum);
+ if (hcd->has_tt) {
+ dev_warn(hcd->self.controller,
+ "Maybe you have connected a low speed device?\n");
+
+ dev_warn(hcd->self.controller,
+ "We do not support low speed devices\n");
+ } else {
+ dev_warn(hcd->self.controller,
+ "Maybe your device is not a high speed device?\n");
+ dev_warn(hcd->self.controller,
+ "The USB host controller does not support full speed "
+ "nor low speed devices\n");
+ dev_warn(hcd->self.controller,
+ "You can reconfigure the host controller to have "
+ "full speed support\n");
+ }
+
+ return 0;
+}
+
+
+static const struct hc_driver ehci_xilinx_of_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OF EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = NULL,
+ .port_handed_over = ehci_xilinx_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+/**
+ * ehci_hcd_xilinx_of_probe - Probe method for the USB host controller
+ * @op: pointer to the platform_device bound to the host controller
+ *
+ * This function requests resources and sets up appropriate properties for the
+ * host controller. Because the Xilinx USB host controller can be configured
+ * as HS only or HS/FS only, it checks the configuration in the device tree
+ * entry, and sets an appropriate value for hcd->has_tt.
+ */
+static int ehci_hcd_xilinx_of_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource res;
+ int irq;
+ int rv;
+ int *value;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ dev_dbg(&op->dev, "initializing XILINX-OF USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ hcd = usb_create_hcd(&ehci_xilinx_of_hc_driver, &op->dev,
+ "XILINX-OF USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = resource_size(&res);
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (!irq) {
+ dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
+ __FILE__);
+ rv = -EBUSY;
+ goto err_irq;
+ }
+
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_irq;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+
+ /* This core always has big-endian register interface and uses
+ * big-endian memory descriptors.
+ */
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+ /* Check whether the FS support option is selected in the hardware.
+ */
+ value = (int *)of_get_property(dn, "xlnx,support-usb-fs", NULL);
+ if (value && (*value == 1)) {
+ ehci_dbg(ehci, "USB host controller supports FS devices\n");
+ hcd->has_tt = 1;
+ } else {
+ ehci_dbg(ehci,
+ "USB host controller is HS only\n");
+ hcd->has_tt = 0;
+ }
+
+ /* Debug registers are at the first 0x100 region
+ */
+ ehci->caps = hcd->regs + 0x100;
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+ }
+
+err_irq:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+/**
+ * ehci_hcd_xilinx_of_remove - shutdown hcd and release resources
+ * @op: pointer to platform_device structure that is to be removed
+ *
+ * Remove the hcd structure, and release resources that has been requested
+ * during probe.
+ */
+static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ dev_dbg(&op->dev, "stopping XILINX-OF USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id ehci_hcd_xilinx_of_match[] = {
+ {.compatible = "xlnx,xps-usb-host-1.00.a",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match);
+
+static struct platform_driver ehci_hcd_xilinx_of_driver = {
+ .probe = ehci_hcd_xilinx_of_probe,
+ .remove = ehci_hcd_xilinx_of_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "xilinx-of-ehci",
+ .of_match_table = ehci_hcd_xilinx_of_match,
+ },
+};
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
new file mode 100644
index 000000000..52ef0844a
--- /dev/null
+++ b/drivers/usb/host/ehci.h
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_EHCI_HCD_H
+#define __LINUX_EHCI_HCD_H
+
+/* definitions used for the EHCI driver */
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+#else
+#define __hc32 __le32
+#define __hc16 __le16
+#endif
+
+/* statistics can be kept for tuning/monitoring */
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define EHCI_STATS
+#endif
+
+struct ehci_stats {
+ /* irq usage */
+ unsigned long normal;
+ unsigned long error;
+ unsigned long iaa;
+ unsigned long lost_iaa;
+
+ /* termination of urbs from core */
+ unsigned long complete;
+ unsigned long unlink;
+};
+
+/*
+ * Scheduling and budgeting information for periodic transfers, for both
+ * high-speed devices and full/low-speed devices lying behind a TT.
+ */
+struct ehci_per_sched {
+ struct usb_device *udev; /* access to the TT */
+ struct usb_host_endpoint *ep;
+ struct list_head ps_list; /* node on ehci_tt's ps_list */
+ u16 tt_usecs; /* time on the FS/LS bus */
+ u16 cs_mask; /* C-mask and S-mask bytes */
+ u16 period; /* actual period in frames */
+ u16 phase; /* actual phase, frame part */
+ u8 bw_phase; /* same, for bandwidth
+ reservation */
+ u8 phase_uf; /* uframe part of the phase */
+ u8 usecs, c_usecs; /* times on the HS bus */
+ u8 bw_uperiod; /* period in microframes, for
+ bandwidth reservation */
+ u8 bw_period; /* same, in frames */
+};
+#define NO_FRAME 29999 /* frame not assigned yet */
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, unlink, periodic (and shadow), ...
+ * usb_host_endpoint: hcpriv
+ * ehci_qh: qh_next, qtd_list
+ * ehci_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+/*
+ * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
+ * controller may be doing DMA. Lower values mean there's no DMA.
+ */
+enum ehci_rh_state {
+ EHCI_RH_HALTED,
+ EHCI_RH_SUSPENDED,
+ EHCI_RH_RUNNING,
+ EHCI_RH_STOPPING
+};
+
+/*
+ * Timer events, ordered by increasing delay length.
+ * Always update event_delays_ns[] and event_handlers[] (defined in
+ * ehci-timer.c) in parallel with this list.
+ */
+enum ehci_hrtimer_event {
+ EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
+ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
+ EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
+ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
+ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
+ EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
+ EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
+ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
+ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
+ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
+ EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
+ EHCI_HRTIMER_NUM_EVENTS /* Must come last */
+};
+#define EHCI_HRTIMER_NO_EVENT 99
+
+struct ehci_hcd { /* one per controller */
+ /* timing support */
+ enum ehci_hrtimer_event next_hrtimer_event;
+ unsigned enabled_hrtimer_events;
+ ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
+ struct hrtimer hrtimer;
+
+ int PSS_poll_count;
+ int ASS_poll_count;
+ int died_poll_count;
+
+ /* glue to PCI and HCD framework */
+ struct ehci_caps __iomem *caps;
+ struct ehci_regs __iomem *regs;
+ struct ehci_dbg_port __iomem *debug;
+
+ __u32 hcs_params; /* cached register copy */
+ spinlock_t lock;
+ enum ehci_rh_state rh_state;
+
+ /* general schedule support */
+ bool scanning:1;
+ bool need_rescan:1;
+ bool intr_unlinking:1;
+ bool iaa_in_progress:1;
+ bool async_unlinking:1;
+ bool shutdown:1;
+ struct ehci_qh *qh_scan_next;
+
+ /* async schedule support */
+ struct ehci_qh *async;
+ struct ehci_qh *dummy; /* For AMD quirk use */
+ struct list_head async_unlink;
+ struct list_head async_idle;
+ unsigned async_unlink_cycle;
+ unsigned async_count; /* async activity count */
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ __hc32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ struct list_head intr_qh_list;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union ehci_shadow *pshadow; /* mirror hw periodic table */
+ struct list_head intr_unlink_wait;
+ struct list_head intr_unlink;
+ unsigned intr_unlink_wait_cycle;
+ unsigned intr_unlink_cycle;
+ unsigned now_frame; /* frame from HC hardware */
+ unsigned last_iso_frame; /* last frame scanned for iso */
+ unsigned intr_count; /* intr activity count */
+ unsigned isoc_count; /* isoc activity count */
+ unsigned periodic_count; /* periodic activity count */
+ unsigned uframe_periodic_max; /* max periodic time per uframe */
+
+
+ /* list of itds & sitds completed while now_frame was still active */
+ struct list_head cached_itd_list;
+ struct ehci_itd *last_itd_to_free;
+ struct list_head cached_sitd_list;
+ struct ehci_sitd *last_sitd_to_free;
+
+ /* per root hub port */
+ unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+
+ /* bit vectors (one bit per port) */
+ unsigned long bus_suspended; /* which ports were
+ already suspended at the start of a bus suspend */
+ unsigned long companion_ports; /* which ports are
+ dedicated to the companion controller */
+ unsigned long owned_ports; /* which ports are
+ owned by the companion during a bus suspend */
+ unsigned long port_c_suspend; /* which ports have
+ the change-suspend feature turned on */
+ unsigned long suspended_ports; /* which ports are
+ suspended */
+ unsigned long resuming_ports; /* which ports have
+ started to resume */
+
+ /* per-HC memory pools (could be per-bus, but ...) */
+ struct dma_pool *qh_pool; /* qh per active urb */
+ struct dma_pool *qtd_pool; /* one or more per qh */
+ struct dma_pool *itd_pool; /* itd per iso urb */
+ struct dma_pool *sitd_pool; /* sitd per split iso urb */
+
+ unsigned random_frame;
+ unsigned long next_statechange;
+ ktime_t last_periodic_enable;
+ u32 command;
+
+ /* SILICON QUIRKS */
+ unsigned no_selective_suspend:1;
+ unsigned has_fsl_port_bug:1; /* FreeScale */
+ unsigned big_endian_mmio:1;
+ unsigned big_endian_desc:1;
+ unsigned big_endian_capbase:1;
+ unsigned has_amcc_usb23:1;
+ unsigned need_io_watchdog:1;
+ unsigned amd_pll_fix:1;
+ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
+ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
+ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
+ unsigned need_oc_pp_cycle:1; /* MPC834X port power */
+ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
+
+ /* required for usb32 quirk */
+ #define OHCI_CTRL_HCFS (3 << 6)
+ #define OHCI_USB_OPER (2 << 6)
+ #define OHCI_USB_SUSPEND (3 << 6)
+
+ #define OHCI_HCCTRL_OFFSET 0x4
+ #define OHCI_HCCTRL_LEN 0x4
+ __hc32 *ohci_hcctrl_reg;
+ unsigned has_hostpc:1;
+ unsigned has_tdi_phy_lpm:1;
+ unsigned has_ppcd:1; /* support per-port change bits */
+ u8 sbrn; /* packed release number */
+
+ /* irq statistics */
+#ifdef EHCI_STATS
+ struct ehci_stats stats;
+# define COUNT(x) do { (x)++; } while (0)
+#else
+# define COUNT(x) do {} while (0)
+#endif
+
+ /* debug files */
+#ifdef CONFIG_DYNAMIC_DEBUG
+ struct dentry *debug_dir;
+#endif
+
+ /* bandwidth usage */
+#define EHCI_BANDWIDTH_SIZE 64
+#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3)
+ u8 bandwidth[EHCI_BANDWIDTH_SIZE];
+ /* us allocated per uframe */
+ u8 tt_budget[EHCI_BANDWIDTH_SIZE];
+ /* us budgeted per uframe */
+ struct list_head tt_list;
+
+ /* platform-specific data -- must come last */
+ unsigned long priv[0] __aligned(sizeof(s64));
+};
+
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
+static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
+{
+ return (struct ehci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
+{
+ return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#include <linux/usb/ehci_def.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.5.1 */
+ __hc32 hw_alt_next; /* see EHCI 3.5.2 */
+ __hc32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+
+#define ACTIVE_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_ACTIVE)
+#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
+#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
+
+ __hc32 hw_buf [5]; /* see EHCI 3.5.4 */
+ __hc32 hw_buf_hi [5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+} __attribute__ ((aligned (32)));
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,sitd,fstn}->hw_next */
+#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1))
+
+/*
+ * Now the following defines are not converted using the
+ * cpu_to_le32() macro anymore, since we have to support
+ * "dynamic" switching between be and le support, so that the driver
+ * can be used on one system with SoC EHCI controller using big-endian
+ * descriptors as well as a normal little-endian PCI EHCI controller.
+ */
+/* values for that type tag */
+#define Q_TYPE_ITD (0 << 1)
+#define Q_TYPE_QH (1 << 1)
+#define Q_TYPE_SITD (2 << 1)
+#define Q_TYPE_FSTN (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH))
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+ struct ehci_qh *qh; /* Q_TYPE_QH */
+ struct ehci_itd *itd; /* Q_TYPE_ITD */
+ struct ehci_sitd *sitd; /* Q_TYPE_SITD */
+ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
+ __hc32 *hw_next; /* (all types) */
+ void *ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+/* first part defined by EHCI spec */
+struct ehci_qh_hw {
+ __hc32 hw_next; /* see EHCI 3.6.1 */
+ __hc32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
+#define QH_HEAD (1 << 15) /* Head of async reclamation list */
+#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
+#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */
+#define QH_LOW_SPEED (1 << 12)
+#define QH_FULL_SPEED (0 << 12)
+#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
+ __hc32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_SMASK 0x000000ff
+#define QH_CMASK 0x0000ff00
+#define QH_HUBADDR 0x007f0000
+#define QH_HUBPORT 0x3f800000
+#define QH_MULT 0xc0000000
+ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ __hc32 hw_qtd_next;
+ __hc32 hw_alt_next;
+ __hc32 hw_token;
+ __hc32 hw_buf [5];
+ __hc32 hw_buf_hi [5];
+} __attribute__ ((aligned(32)));
+
+struct ehci_qh {
+ struct ehci_qh_hw *hw; /* Must come first */
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union ehci_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+ struct list_head intr_node; /* list of intr QHs */
+ struct ehci_qtd *dummy;
+ struct list_head unlink_node;
+ struct ehci_per_sched ps; /* scheduling info */
+
+ unsigned unlink_cycle;
+
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ u8 xacterrs; /* XactErr retry counter */
+#define QH_XACTERR_MAX 32 /* XactErr retry limit */
+
+ u8 gap_uf; /* uframes split/csplit gap */
+
+ unsigned is_out:1; /* bulk or intr OUT */
+ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
+ unsigned dequeue_during_giveback:1;
+ unsigned exception:1; /* got a fault, or an unlink
+ was requested */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* description of one iso transaction (up to 3 KB data if highspeed) */
+struct ehci_iso_packet {
+ /* These will be copied to iTD when scheduling */
+ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
+ __hc32 transaction; /* itd->hw_transaction[i] |= */
+ u8 cross; /* buf crosses pages */
+ /* for full speed OUT splits */
+ u32 buf1;
+};
+
+/* temporary schedule data for packets from iso urbs (both speeds)
+ * each packet is one logical usb transaction to the device (not TT),
+ * beginning at stream->next_uframe
+ */
+struct ehci_iso_sched {
+ struct list_head td_list;
+ unsigned span;
+ unsigned first_packet;
+ struct ehci_iso_packet packet [0];
+};
+
+/*
+ * ehci_iso_stream - groups all (s)itds for this endpoint.
+ * acts like a qh would, if EHCI had them for ISO.
+ */
+struct ehci_iso_stream {
+ /* first field matches ehci_hq, but is NULL */
+ struct ehci_qh_hw *hw;
+
+ u8 bEndpointAddress;
+ u8 highspeed;
+ struct list_head td_list; /* queued itds/sitds */
+ struct list_head free_list; /* list of unused itds/sitds */
+
+ /* output of (re)scheduling */
+ struct ehci_per_sched ps; /* scheduling info */
+ unsigned next_uframe;
+ __hc32 splits;
+
+ /* the rest is derived from the endpoint descriptor,
+ * including the extra info for hw_bufp[0..2]
+ */
+ u16 uperiod; /* period in uframes */
+ u16 maxp;
+ unsigned bandwidth;
+
+ /* This is used to initialize iTD's hw_bufp fields */
+ __hc32 buf0;
+ __hc32 buf1;
+ __hc32 buf2;
+
+ /* this is used to initialize sITD's tt info */
+ __hc32 address;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct ehci_itd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.3.1 */
+ __hc32 hw_transaction [8]; /* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
+#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
+#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
+
+#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
+
+ __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __hc32 hw_bufp_hi [7]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t itd_dma; /* for this itd */
+ union ehci_shadow itd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct ehci_iso_stream *stream; /* endpoint's queue */
+ struct list_head itd_list; /* list of stream's itds */
+
+ /* any/all hw_transactions here may be used by that urb */
+ unsigned frame; /* where scheduled */
+ unsigned pg;
+ unsigned index[8]; /* in urb->iso_frame_desc */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.4
+ * siTD, aka split-transaction isochronous Transfer Descriptor
+ * ... describe full speed iso xfers through TT in hubs
+ * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
+ */
+struct ehci_sitd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next;
+/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
+ __hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
+ __hc32 hw_uframe; /* EHCI table 3-10 */
+ __hc32 hw_results; /* EHCI table 3-11 */
+#define SITD_IOC (1 << 31) /* interrupt on completion */
+#define SITD_PAGE (1 << 30) /* buffer 0/1 */
+#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
+#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define SITD_STS_ERR (1 << 6) /* error from TT */
+#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define SITD_STS_BABBLE (1 << 4) /* device was babbling */
+#define SITD_STS_XACT (1 << 3) /* illegal IN response */
+#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define SITD_STS_STS (1 << 1) /* split transaction state */
+
+#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)
+
+ __hc32 hw_buf [2]; /* EHCI table 3-12 */
+ __hc32 hw_backpointer; /* EHCI table 3-13 */
+ __hc32 hw_buf_hi [2]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t sitd_dma;
+ union ehci_shadow sitd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct ehci_iso_stream *stream; /* endpoint's queue */
+ struct list_head sitd_list; /* list of stream's sitds */
+ unsigned frame;
+ unsigned index;
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct ehci_fstn {
+ __hc32 hw_next; /* any periodic q entry */
+ __hc32 hw_prev; /* qh or EHCI_LIST_END */
+
+ /* the rest is HCD-private */
+ dma_addr_t fstn_dma;
+ union ehci_shadow fstn_next; /* ptr to periodic q entry */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB-2.0 Specification Sections 11.14 and 11.18
+ * Scheduling and budgeting split transactions using TTs
+ *
+ * A hub can have a single TT for all its ports, or multiple TTs (one for each
+ * port). The bandwidth and budgeting information for the full/low-speed bus
+ * below each TT is self-contained and independent of the other TTs or the
+ * high-speed bus.
+ *
+ * "Bandwidth" refers to the number of microseconds on the FS/LS bus allocated
+ * to an interrupt or isochronous endpoint for each frame. "Budget" refers to
+ * the best-case estimate of the number of full-speed bytes allocated to an
+ * endpoint for each microframe within an allocated frame.
+ *
+ * Removal of an endpoint invalidates a TT's budget. Instead of trying to
+ * keep an up-to-date record, we recompute the budget when it is needed.
+ */
+
+struct ehci_tt {
+ u16 bandwidth[EHCI_BANDWIDTH_FRAMES];
+
+ struct list_head tt_list; /* List of all ehci_tt's */
+ struct list_head ps_list; /* Items using this TT */
+ struct usb_tt *usb_tt;
+ int tt_port; /* TT port number */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare the PORTSC wakeup flags during controller suspend/resume */
+
+#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
+ ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup);
+
+#define ehci_prepare_ports_for_controller_resume(ehci) \
+ ehci_adjust_port_wakeup_flags(ehci, false, false);
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
+
+/*
+ * Some EHCI controllers have a Transaction Translator built into the
+ * root hub. This is a non-standard feature. Each controller will need
+ * to add code to the following inline functions, and call them as
+ * needed (mostly in root hub code).
+ */
+
+#define ehci_is_TDI(e) (ehci_to_hcd(e)->has_tt)
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int
+ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
+{
+ if (ehci_is_TDI(ehci)) {
+ switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
+ case 0:
+ return 0;
+ case 1:
+ return USB_PORT_STAT_LOW_SPEED;
+ case 2:
+ default:
+ return USB_PORT_STAT_HIGH_SPEED;
+ }
+ }
+ return USB_PORT_STAT_HIGH_SPEED;
+}
+
+#else
+
+#define ehci_is_TDI(e) (0)
+
+#define ehci_port_speed(ehci, portsc) USB_PORT_STAT_HIGH_SPEED
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PPC_83xx
+/* Some Freescale processors have an erratum in which the TT
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+#define ehci_has_fsl_portno_bug(e) ((e)->has_fsl_port_bug)
+#else
+#define ehci_has_fsl_portno_bug(e) (0)
+#endif
+
+/*
+ * While most USB host controllers implement their registers in
+ * little-endian format, a minority (celleb companion chip) implement
+ * them in big endian format.
+ *
+ * This attempts to support either format at compile time without a
+ * runtime penalty, or both formats with the additional overhead
+ * of checking a flag bit.
+ *
+ * ehci_big_endian_capbase is a special quirk for controllers that
+ * implement the HC capability registers as separate registers and not
+ * as fields of a 32-bit register.
+ */
+
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+#define ehci_big_endian_mmio(e) ((e)->big_endian_mmio)
+#define ehci_big_endian_capbase(e) ((e)->big_endian_capbase)
+#else
+#define ehci_big_endian_mmio(e) 0
+#define ehci_big_endian_capbase(e) 0
+#endif
+
+/*
+ * Big-endian read/write functions are arch-specific.
+ * Other arches can be added if/when they're needed.
+ */
+#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_IXP4XX)
+#define readl_be(addr) __raw_readl((__force unsigned *)addr)
+#define writel_be(val, addr) __raw_writel(val, (__force unsigned *)addr)
+#endif
+
+static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
+ __u32 __iomem * regs)
+{
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+ return ehci_big_endian_mmio(ehci) ?
+ readl_be(regs) :
+ readl(regs);
+#else
+ return readl(regs);
+#endif
+}
+
+#ifdef CONFIG_SOC_IMX28
+static inline void imx28_ehci_writel(const unsigned int val,
+ volatile __u32 __iomem *addr)
+{
+ __asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr));
+}
+#else
+static inline void imx28_ehci_writel(const unsigned int val,
+ volatile __u32 __iomem *addr)
+{
+}
+#endif
+static inline void ehci_writel(const struct ehci_hcd *ehci,
+ const unsigned int val, __u32 __iomem *regs)
+{
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+ ehci_big_endian_mmio(ehci) ?
+ writel_be(val, regs) :
+ writel(val, regs);
+#else
+ if (ehci->imx28_write_fix)
+ imx28_ehci_writel(val, regs);
+ else
+ writel(val, regs);
+#endif
+}
+
+/*
+ * On certain ppc-44x SoC there is a HW issue, that could only worked around with
+ * explicit suspend/operate of OHCI. This function hereby makes sense only on that arch.
+ * Other common bits are dependent on has_amcc_usb23 quirk flag.
+ */
+#ifdef CONFIG_44x
+static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
+{
+ u32 hc_control;
+
+ hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS);
+ if (operational)
+ hc_control |= OHCI_USB_OPER;
+ else
+ hc_control |= OHCI_USB_SUSPEND;
+
+ writel_be(hc_control, ehci->ohci_hcctrl_reg);
+ (void) readl_be(ehci->ohci_hcctrl_reg);
+}
+#else
+static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
+{ }
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The AMCC 440EPx not only implements its EHCI registers in big-endian
+ * format, but also its DMA data structures (descriptors).
+ *
+ * EHCI controllers accessed through PCI work normally (little-endian
+ * everywhere), so we won't bother supporting a BE-only mode for now.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+#define ehci_big_endian_desc(e) ((e)->big_endian_desc)
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? (__force __hc32)cpu_to_be32(x)
+ : (__force __hc32)cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? be32_to_cpu((__force __be32)x)
+ : le32_to_cpu((__force __le32)x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? be32_to_cpup((__force __be32 *)x)
+ : le32_to_cpup((__force __le32 *)x);
+}
+
+#else
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+ return cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+ return le32_to_cpu(x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+ return le32_to_cpup(x);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define ehci_dbg(ehci, fmt, args...) \
+ dev_dbg(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
+#define ehci_err(ehci, fmt, args...) \
+ dev_err(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
+#define ehci_info(ehci, fmt, args...) \
+ dev_info(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
+#define ehci_warn(ehci, fmt, args...) \
+ dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
+
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+#define STUB_DEBUG_FILES
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Declarations of things exported for use by ehci platform drivers */
+
+struct ehci_driver_overrides {
+ size_t extra_priv_size;
+ int (*reset)(struct usb_hcd *hcd);
+ int (*port_power)(struct usb_hcd *hcd,
+ int portnum, bool enable);
+};
+
+extern void ehci_init_driver(struct hc_driver *drv,
+ const struct ehci_driver_overrides *over);
+extern int ehci_setup(struct usb_hcd *hcd);
+extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
+ u32 mask, u32 done, int usec);
+
+#ifdef CONFIG_PM
+extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
+extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
+#endif /* CONFIG_PM */
+
+extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength);
+
+#endif /* __LINUX_EHCI_HCD_H */
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
new file mode 100644
index 000000000..b58e7a609
--- /dev/null
+++ b/drivers/usb/host/fhci-dbg.c
@@ -0,0 +1,135 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "fhci.h"
+
+void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
+{
+ int i;
+
+ if (usb_er == -1) {
+ fhci->usb_irq_stat[12]++;
+ return;
+ }
+
+ for (i = 0; i < 12; ++i) {
+ if (usb_er & (1 << i))
+ fhci->usb_irq_stat[i]++;
+ }
+}
+
+static int fhci_dfs_regs_show(struct seq_file *s, void *v)
+{
+ struct fhci_hcd *fhci = s->private;
+ struct qe_usb_ctlr __iomem *regs = fhci->regs;
+
+ seq_printf(s,
+ "mode: 0x%x\n" "addr: 0x%x\n"
+ "command: 0x%x\n" "ep0: 0x%x\n"
+ "event: 0x%x\n" "mask: 0x%x\n"
+ "status: 0x%x\n" "SOF timer: %d\n"
+ "frame number: %d\n"
+ "lines status: 0x%x\n",
+ in_8(&regs->usb_usmod), in_8(&regs->usb_usadr),
+ in_8(&regs->usb_uscom), in_be16(&regs->usb_usep[0]),
+ in_be16(&regs->usb_usber), in_be16(&regs->usb_usbmr),
+ in_8(&regs->usb_usbs), in_be16(&regs->usb_ussft),
+ in_be16(&regs->usb_usfrn),
+ fhci_ioports_check_bus_state(fhci));
+
+ return 0;
+}
+
+static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
+{
+ struct fhci_hcd *fhci = s->private;
+ int *usb_irq_stat = fhci->usb_irq_stat;
+
+ seq_printf(s,
+ "RXB: %d\n" "TXB: %d\n" "BSY: %d\n"
+ "SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n"
+ "TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n"
+ "RESET: %d\n" "SFT: %d\n" "MSF: %d\n"
+ "IDLE_ONLY: %d\n",
+ usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2],
+ usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5],
+ usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8],
+ usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11],
+ usb_irq_stat[12]);
+
+ return 0;
+}
+
+static int fhci_dfs_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fhci_dfs_regs_show, inode->i_private);
+}
+
+static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fhci_dfs_irq_stat_show, inode->i_private);
+}
+
+static const struct file_operations fhci_dfs_regs_fops = {
+ .open = fhci_dfs_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fhci_dfs_irq_stat_fops = {
+ .open = fhci_dfs_irq_stat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+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), usb_debug_root);
+ if (!fhci->dfs_root) {
+ WARN_ON(1);
+ return;
+ }
+
+ fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO,
+ fhci->dfs_root, fhci, &fhci_dfs_regs_fops);
+
+ fhci->dfs_irq_stat = debugfs_create_file("irq_stat",
+ S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
+ &fhci_dfs_irq_stat_fops);
+
+ WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat);
+}
+
+void fhci_dfs_destroy(struct fhci_hcd *fhci)
+{
+ if (!fhci->dfs_root)
+ return;
+
+ debugfs_remove(fhci->dfs_irq_stat);
+ debugfs_remove(fhci->dfs_regs);
+ debugfs_remove(fhci->dfs_root);
+}
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
new file mode 100644
index 000000000..c6cebb96f
--- /dev/null
+++ b/drivers/usb/host/fhci-hcd.c
@@ -0,0 +1,836 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <asm/qe.h>
+#include <asm/fsl_gtm.h>
+#include "fhci.h"
+
+void fhci_start_sof_timer(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ /* clear frame_n */
+ out_be16(&fhci->pram->frame_num, 0);
+
+ out_be16(&fhci->regs->usb_ussft, 0);
+ setbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+void fhci_stop_sof_timer(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ clrbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE);
+ gtm_stop_timer16(fhci->timer);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+u16 fhci_get_sof_timer_count(struct fhci_usb *usb)
+{
+ return be16_to_cpu(in_be16(&usb->fhci->regs->usb_ussft) / 12);
+}
+
+/* initialize the endpoint zero */
+static u32 endpoint_zero_init(struct fhci_usb *usb,
+ enum fhci_mem_alloc data_mem,
+ u32 ring_len)
+{
+ u32 rc;
+
+ rc = fhci_create_ep(usb, data_mem, ring_len);
+ if (rc)
+ return rc;
+
+ /* inilialize endpoint registers */
+ fhci_init_ep_registers(usb, usb->ep0, data_mem);
+
+ return 0;
+}
+
+/* enable the USB interrupts */
+void fhci_usb_enable_interrupt(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (usb->intr_nesting_cnt == 1) {
+ /* initialize the USB interrupt */
+ enable_irq(fhci_to_hcd(fhci)->irq);
+
+ /* initialize the event register and mask register */
+ out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+ out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+
+ /* enable the timer interrupts */
+ enable_irq(fhci->timer->irq);
+ } else if (usb->intr_nesting_cnt > 1)
+ fhci_info(fhci, "unbalanced USB interrupts nesting\n");
+ usb->intr_nesting_cnt--;
+}
+
+/* disable the usb interrupt */
+void fhci_usb_disable_interrupt(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (usb->intr_nesting_cnt == 0) {
+ /* disable the timer interrupt */
+ disable_irq_nosync(fhci->timer->irq);
+
+ /* disable the usb interrupt */
+ disable_irq_nosync(fhci_to_hcd(fhci)->irq);
+ out_be16(&usb->fhci->regs->usb_usbmr, 0);
+ }
+ usb->intr_nesting_cnt++;
+}
+
+/* enable the USB controller */
+static u32 fhci_usb_enable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+ out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+ setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
+
+ mdelay(100);
+
+ return 0;
+}
+
+/* disable the USB controller */
+static u32 fhci_usb_disable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ fhci_usb_disable_interrupt(usb);
+ fhci_port_disable(fhci);
+
+ /* disable the usb controller */
+ if (usb->port_status == FHCI_PORT_FULL ||
+ usb->port_status == FHCI_PORT_LOW)
+ fhci_device_disconnected_interrupt(fhci);
+
+ clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
+
+ return 0;
+}
+
+/* check the bus state by polling the QE bit on the IO ports */
+int fhci_ioports_check_bus_state(struct fhci_hcd *fhci)
+{
+ u8 bits = 0;
+
+ /* check USBOE,if transmitting,exit */
+ if (!gpio_get_value(fhci->gpios[GPIO_USBOE]))
+ return -1;
+
+ /* check USBRP */
+ if (gpio_get_value(fhci->gpios[GPIO_USBRP]))
+ bits |= 0x2;
+
+ /* check USBRN */
+ if (gpio_get_value(fhci->gpios[GPIO_USBRN]))
+ bits |= 0x1;
+
+ return bits;
+}
+
+static void fhci_mem_free(struct fhci_hcd *fhci)
+{
+ struct ed *ed;
+ struct ed *next_ed;
+ struct td *td;
+ struct td *next_td;
+
+ list_for_each_entry_safe(ed, next_ed, &fhci->empty_eds, node) {
+ list_del(&ed->node);
+ kfree(ed);
+ }
+
+ list_for_each_entry_safe(td, next_td, &fhci->empty_tds, node) {
+ list_del(&td->node);
+ kfree(td);
+ }
+
+ kfree(fhci->vroot_hub);
+ fhci->vroot_hub = NULL;
+
+ kfree(fhci->hc_list);
+ fhci->hc_list = NULL;
+}
+
+static int fhci_mem_init(struct fhci_hcd *fhci)
+{
+ int i;
+
+ fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL);
+ if (!fhci->hc_list)
+ goto err;
+
+ INIT_LIST_HEAD(&fhci->hc_list->ctrl_list);
+ INIT_LIST_HEAD(&fhci->hc_list->bulk_list);
+ INIT_LIST_HEAD(&fhci->hc_list->iso_list);
+ INIT_LIST_HEAD(&fhci->hc_list->intr_list);
+ INIT_LIST_HEAD(&fhci->hc_list->done_list);
+
+ fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL);
+ if (!fhci->vroot_hub)
+ goto err;
+
+ INIT_LIST_HEAD(&fhci->empty_eds);
+ INIT_LIST_HEAD(&fhci->empty_tds);
+
+ /* initialize work queue to handle done list */
+ fhci_tasklet.data = (unsigned long)fhci;
+ fhci->process_done_task = &fhci_tasklet;
+
+ for (i = 0; i < MAX_TDS; i++) {
+ struct td *td;
+
+ td = kmalloc(sizeof(*td), GFP_KERNEL);
+ if (!td)
+ goto err;
+ fhci_recycle_empty_td(fhci, td);
+ }
+ for (i = 0; i < MAX_EDS; i++) {
+ struct ed *ed;
+
+ ed = kmalloc(sizeof(*ed), GFP_KERNEL);
+ if (!ed)
+ goto err;
+ fhci_recycle_empty_ed(fhci, ed);
+ }
+
+ fhci->active_urbs = 0;
+ return 0;
+err:
+ fhci_mem_free(fhci);
+ return -ENOMEM;
+}
+
+/* destroy the fhci_usb structure */
+static void fhci_usb_free(void *lld)
+{
+ struct fhci_usb *usb = lld;
+ struct fhci_hcd *fhci;
+
+ if (usb) {
+ fhci = usb->fhci;
+ fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
+ fhci_ep0_free(usb);
+ kfree(usb->actual_frame);
+ kfree(usb);
+ }
+}
+
+/* initialize the USB */
+static int fhci_usb_init(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE);
+
+ usb->port_status = FHCI_PORT_DISABLED;
+ usb->max_frame_usage = FRAME_TIME_USAGE;
+ usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
+
+ usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL);
+ if (!usb->actual_frame) {
+ fhci_usb_free(usb);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&usb->actual_frame->tds_list);
+
+ /* initializing registers on chip, clear frame number */
+ out_be16(&fhci->pram->frame_num, 0);
+
+ /* clear rx state */
+ out_be32(&fhci->pram->rx_state, 0);
+
+ /* set mask register */
+ usb->saved_msk = (USB_E_TXB_MASK |
+ USB_E_TXE1_MASK |
+ USB_E_IDLE_MASK |
+ USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK);
+
+ out_8(&usb->fhci->regs->usb_usmod, USB_MODE_HOST | USB_MODE_EN);
+
+ /* clearing the mask register */
+ out_be16(&usb->fhci->regs->usb_usbmr, 0);
+
+ /* initialing the event register */
+ out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+
+ if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) {
+ fhci_usb_free(usb);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* initialize the fhci_usb struct and the corresponding data staruct */
+static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb;
+
+ /* allocate memory for SCC data structure */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb) {
+ fhci_err(fhci, "no memory for SCC data struct\n");
+ return NULL;
+ }
+
+ usb->fhci = fhci;
+ usb->hc_list = fhci->hc_list;
+ usb->vroot_hub = fhci->vroot_hub;
+
+ usb->transfer_confirm = fhci_transfer_confirm_callback;
+
+ return usb;
+}
+
+static int fhci_start(struct usb_hcd *hcd)
+{
+ int ret;
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ ret = fhci_mem_init(fhci);
+ if (ret) {
+ fhci_err(fhci, "failed to allocate memory\n");
+ goto err;
+ }
+
+ fhci->usb_lld = fhci_create_lld(fhci);
+ if (!fhci->usb_lld) {
+ fhci_err(fhci, "low level driver config failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = fhci_usb_init(fhci);
+ if (ret) {
+ fhci_err(fhci, "low level driver initialize failed\n");
+ goto err;
+ }
+
+ spin_lock_init(&fhci->lock);
+
+ /* connect the virtual root hub */
+ fhci->vroot_hub->dev_num = 1; /* this field may be needed to fix */
+ fhci->vroot_hub->hub.wHubStatus = 0;
+ fhci->vroot_hub->hub.wHubChange = 0;
+ fhci->vroot_hub->port.wPortStatus = 0;
+ fhci->vroot_hub->port.wPortChange = 0;
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /*
+ * From here on, hub_wq concurrently accesses the root
+ * hub; drivers will be talking to enumerated devices.
+ * (On restart paths, hub_wq already knows about the root
+ * hub and could find work as soon as we wrote FLAG_CF.)
+ *
+ * Before this point the HC was idle/ready. After, hub_wq
+ * and device drivers may start it running.
+ */
+ fhci_usb_enable(fhci);
+ return 0;
+err:
+ fhci_mem_free(fhci);
+ return ret;
+}
+
+static void fhci_stop(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ fhci_usb_disable_interrupt(fhci->usb_lld);
+ fhci_usb_disable(fhci);
+
+ fhci_usb_free(fhci->usb_lld);
+ fhci->usb_lld = NULL;
+ fhci_mem_free(fhci);
+}
+
+static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ u32 pipe = urb->pipe;
+ int ret;
+ int i;
+ int size = 0;
+ struct urb_priv *urb_priv;
+ unsigned long flags;
+
+ switch (usb_pipetype(pipe)) {
+ case PIPE_CONTROL:
+ /* 1 td fro setup,1 for ack */
+ size = 2;
+ case PIPE_BULK:
+ /* one td for every 4096 bytes(can be up to 8k) */
+ size += urb->transfer_buffer_length / 4096;
+ /* ...add for any remaining bytes... */
+ if ((urb->transfer_buffer_length % 4096) != 0)
+ size++;
+ /* ..and maybe a zero length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket(urb->dev, pipe,
+ usb_pipeout(pipe))) != 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS:
+ size = urb->number_of_packets;
+ if (size <= 0)
+ return -EINVAL;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = (u32) (-EXDEV);
+ }
+ break;
+ case PIPE_INTERRUPT:
+ size = 1;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kzalloc(sizeof(*urb_priv), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ /* allocate the private part of the URB */
+ urb_priv->tds = kcalloc(size, sizeof(*urb_priv->tds), mem_flags);
+ if (!urb_priv->tds) {
+ kfree(urb_priv);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto err;
+
+ /* fill the private part of the URB */
+ urb_priv->num_of_tds = size;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->error_count = 0;
+ urb->hcpriv = urb_priv;
+
+ fhci_queue_urb(fhci, urb);
+err:
+ if (ret) {
+ kfree(urb_priv->tds);
+ kfree(urb_priv);
+ }
+ spin_unlock_irqrestore(&fhci->lock, flags);
+ return ret;
+}
+
+/* dequeue FHCI URB */
+static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb = fhci->usb_lld;
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ if (!urb || !urb->dev || !urb->dev->bus)
+ goto out;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (ret)
+ goto out2;
+
+ if (usb->port_status != FHCI_PORT_DISABLED) {
+ struct urb_priv *urb_priv;
+
+ /*
+ * flag the urb's data for deletion in some upcoming
+ * SF interrupt's delete list processing
+ */
+ urb_priv = urb->hcpriv;
+
+ if (!urb_priv || (urb_priv->state == URB_DEL))
+ goto out2;
+
+ urb_priv->state = URB_DEL;
+
+ /* already pending? */
+ urb_priv->ed->state = FHCI_ED_URB_DEL;
+ } else {
+ fhci_urb_complete_free(fhci, urb);
+ }
+
+out2:
+ spin_unlock_irqrestore(&fhci->lock, flags);
+out:
+ return ret;
+}
+
+static void fhci_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct fhci_hcd *fhci;
+ struct ed *ed;
+ unsigned long flags;
+
+ fhci = hcd_to_fhci(hcd);
+ spin_lock_irqsave(&fhci->lock, flags);
+ ed = ep->hcpriv;
+ if (ed) {
+ while (ed->td_head != NULL) {
+ struct td *td = fhci_remove_td_from_ed(ed);
+ fhci_urb_complete_free(fhci, td->urb);
+ }
+ fhci_recycle_empty_ed(fhci, ed);
+ ep->hcpriv = NULL;
+ }
+ spin_unlock_irqrestore(&fhci->lock, flags);
+}
+
+static int fhci_get_frame_number(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ return get_frame_num(fhci);
+}
+
+static const struct hc_driver fhci_driver = {
+ .description = "fsl,usb-fhci",
+ .product_desc = "FHCI HOST Controller",
+ .hcd_priv_size = sizeof(struct fhci_hcd),
+
+ /* generic hardware linkage */
+ .irq = fhci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /* basic lifecycle operation */
+ .start = fhci_start,
+ .stop = fhci_stop,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = fhci_urb_enqueue,
+ .urb_dequeue = fhci_urb_dequeue,
+ .endpoint_disable = fhci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = fhci_get_frame_number,
+
+ /* root hub support */
+ .hub_status_data = fhci_hub_status_data,
+ .hub_control = fhci_hub_control,
+};
+
+static int of_fhci_probe(struct platform_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *node = dev->of_node;
+ struct usb_hcd *hcd;
+ struct fhci_hcd *fhci;
+ struct resource usb_regs;
+ unsigned long pram_addr;
+ unsigned int usb_irq;
+ const char *sprop;
+ const u32 *iprop;
+ int size;
+ int ret;
+ int i;
+ int j;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ sprop = of_get_property(node, "mode", NULL);
+ if (sprop && strcmp(sprop, "host"))
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&fhci_driver, dev, dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "could not create hcd\n");
+ return -ENOMEM;
+ }
+
+ fhci = hcd_to_fhci(hcd);
+ hcd->self.controller = dev;
+ dev_set_drvdata(dev, hcd);
+
+ iprop = of_get_property(node, "hub-power-budget", &size);
+ if (iprop && size == sizeof(*iprop))
+ hcd->power_budget = *iprop;
+
+ /* FHCI registers. */
+ ret = of_address_to_resource(node, 0, &usb_regs);
+ if (ret) {
+ dev_err(dev, "could not get regs\n");
+ goto err_regs;
+ }
+
+ hcd->regs = ioremap(usb_regs.start, resource_size(&usb_regs));
+ if (!hcd->regs) {
+ dev_err(dev, "could not ioremap regs\n");
+ ret = -ENOMEM;
+ goto err_regs;
+ }
+ fhci->regs = hcd->regs;
+
+ /* Parameter RAM. */
+ iprop = of_get_property(node, "reg", &size);
+ if (!iprop || size < sizeof(*iprop) * 4) {
+ dev_err(dev, "can't get pram offset\n");
+ ret = -EINVAL;
+ goto err_pram;
+ }
+
+ pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64);
+ if (IS_ERR_VALUE(pram_addr)) {
+ dev_err(dev, "failed to allocate usb pram\n");
+ ret = -ENOMEM;
+ goto err_pram;
+ }
+
+ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB,
+ QE_CR_PROTOCOL_UNSPECIFIED, pram_addr);
+ fhci->pram = cpm_muram_addr(pram_addr);
+
+ /* GPIOs and pins */
+ for (i = 0; i < NUM_GPIOS; i++) {
+ int gpio;
+ enum of_gpio_flags flags;
+
+ gpio = of_get_gpio_flags(node, i, &flags);
+ fhci->gpios[i] = gpio;
+ fhci->alow_gpios[i] = flags & OF_GPIO_ACTIVE_LOW;
+
+ if (!gpio_is_valid(gpio)) {
+ if (i < GPIO_SPEED) {
+ dev_err(dev, "incorrect GPIO%d: %d\n",
+ i, gpio);
+ goto err_gpios;
+ } else {
+ dev_info(dev, "assuming board doesn't have "
+ "%s gpio\n", i == GPIO_SPEED ?
+ "speed" : "power");
+ continue;
+ }
+ }
+
+ ret = gpio_request(gpio, dev_name(dev));
+ if (ret) {
+ dev_err(dev, "failed to request gpio %d", i);
+ goto err_gpios;
+ }
+
+ if (i >= GPIO_SPEED) {
+ ret = gpio_direction_output(gpio, 0);
+ if (ret) {
+ dev_err(dev, "failed to set gpio %d as "
+ "an output\n", i);
+ i++;
+ goto err_gpios;
+ }
+ }
+ }
+
+ for (j = 0; j < NUM_PINS; j++) {
+ fhci->pins[j] = qe_pin_request(node, j);
+ if (IS_ERR(fhci->pins[j])) {
+ ret = PTR_ERR(fhci->pins[j]);
+ dev_err(dev, "can't get pin %d: %d\n", j, ret);
+ goto err_pins;
+ }
+ }
+
+ /* Frame limit timer and its interrupt. */
+ fhci->timer = gtm_get_timer16();
+ if (IS_ERR(fhci->timer)) {
+ ret = PTR_ERR(fhci->timer);
+ dev_err(dev, "failed to request qe timer: %i", ret);
+ goto err_get_timer;
+ }
+
+ ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq,
+ 0, "qe timer (usb)", hcd);
+ if (ret) {
+ dev_err(dev, "failed to request timer irq");
+ goto err_timer_irq;
+ }
+
+ /* USB Host interrupt. */
+ usb_irq = irq_of_parse_and_map(node, 0);
+ if (usb_irq == NO_IRQ) {
+ dev_err(dev, "could not get usb irq\n");
+ ret = -EINVAL;
+ goto err_usb_irq;
+ }
+
+ /* Clocks. */
+ sprop = of_get_property(node, "fsl,fullspeed-clock", NULL);
+ if (sprop) {
+ fhci->fullspeed_clk = qe_clock_source(sprop);
+ if (fhci->fullspeed_clk == QE_CLK_DUMMY) {
+ dev_err(dev, "wrong fullspeed-clock\n");
+ ret = -EINVAL;
+ goto err_clocks;
+ }
+ }
+
+ sprop = of_get_property(node, "fsl,lowspeed-clock", NULL);
+ if (sprop) {
+ fhci->lowspeed_clk = qe_clock_source(sprop);
+ if (fhci->lowspeed_clk == QE_CLK_DUMMY) {
+ dev_err(dev, "wrong lowspeed-clock\n");
+ ret = -EINVAL;
+ goto err_clocks;
+ }
+ }
+
+ if (fhci->fullspeed_clk == QE_CLK_NONE &&
+ fhci->lowspeed_clk == QE_CLK_NONE) {
+ dev_err(dev, "no clocks specified\n");
+ ret = -EINVAL;
+ goto err_clocks;
+ }
+
+ dev_info(dev, "at 0x%p, irq %d\n", hcd->regs, usb_irq);
+
+ fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
+
+ /* Start with full-speed, if possible. */
+ if (fhci->fullspeed_clk != QE_CLK_NONE) {
+ fhci_config_transceiver(fhci, FHCI_PORT_FULL);
+ qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+ } else {
+ fhci_config_transceiver(fhci, FHCI_PORT_LOW);
+ qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+ }
+
+ /* Clear and disable any pending interrupts. */
+ out_be16(&fhci->regs->usb_usber, 0xffff);
+ out_be16(&fhci->regs->usb_usbmr, 0);
+
+ ret = usb_add_hcd(hcd, usb_irq, 0);
+ if (ret < 0)
+ goto err_add_hcd;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ fhci_dfs_create(fhci);
+
+ return 0;
+
+err_add_hcd:
+err_clocks:
+ irq_dispose_mapping(usb_irq);
+err_usb_irq:
+ free_irq(fhci->timer->irq, hcd);
+err_timer_irq:
+ gtm_put_timer16(fhci->timer);
+err_get_timer:
+err_pins:
+ while (--j >= 0)
+ qe_pin_free(fhci->pins[j]);
+err_gpios:
+ while (--i >= 0) {
+ if (gpio_is_valid(fhci->gpios[i]))
+ gpio_free(fhci->gpios[i]);
+ }
+ cpm_muram_free(pram_addr);
+err_pram:
+ iounmap(hcd->regs);
+err_regs:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int fhci_remove(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ int i;
+ int j;
+
+ usb_remove_hcd(hcd);
+ free_irq(fhci->timer->irq, hcd);
+ gtm_put_timer16(fhci->timer);
+ cpm_muram_free(cpm_muram_offset(fhci->pram));
+ for (i = 0; i < NUM_GPIOS; i++) {
+ if (!gpio_is_valid(fhci->gpios[i]))
+ continue;
+ gpio_free(fhci->gpios[i]);
+ }
+ for (j = 0; j < NUM_PINS; j++)
+ qe_pin_free(fhci->pins[j]);
+ fhci_dfs_destroy(fhci);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static int of_fhci_remove(struct platform_device *ofdev)
+{
+ return fhci_remove(&ofdev->dev);
+}
+
+static const struct of_device_id of_fhci_match[] = {
+ { .compatible = "fsl,mpc8323-qe-usb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_fhci_match);
+
+static struct platform_driver of_fhci_driver = {
+ .driver = {
+ .name = "fsl,usb-fhci",
+ .of_match_table = of_fhci_match,
+ },
+ .probe = of_fhci_probe,
+ .remove = of_fhci_remove,
+};
+
+module_platform_driver(of_fhci_driver);
+
+MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver");
+MODULE_AUTHOR("Shlomi Gridish <gridish@freescale.com>, "
+ "Jerry Huang <Chang-Ming.Huang@freescale.com>, "
+ "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c
new file mode 100644
index 000000000..3bacdd7be
--- /dev/null
+++ b/drivers/usb/host/fhci-hub.c
@@ -0,0 +1,340 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/gpio.h>
+#include <asm/qe.h>
+#include "fhci.h"
+
+/* virtual root hub specific descriptor */
+static u8 root_hub_des[] = {
+ 0x09, /* blength */
+ USB_DT_HUB, /* bDescriptorType;hub-descriptor */
+ 0x01, /* bNbrPorts */
+ HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
+ 0x00, /* per-port power, no overcurrent */
+ 0x01, /* bPwrOn2pwrGood;2ms */
+ 0x00, /* bHubContrCurrent;0mA */
+ 0x00, /* DeviceRemoveable */
+ 0xff, /* PortPwrCtrlMask */
+};
+
+static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
+{
+ int gpio = fhci->gpios[gpio_nr];
+ bool alow = fhci->alow_gpios[gpio_nr];
+
+ if (!gpio_is_valid(gpio))
+ return;
+
+ gpio_set_value(gpio, on ^ alow);
+ mdelay(5);
+}
+
+void fhci_config_transceiver(struct fhci_hcd *fhci,
+ enum fhci_port_status status)
+{
+ fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
+
+ switch (status) {
+ case FHCI_PORT_POWER_OFF:
+ fhci_gpio_set_value(fhci, GPIO_POWER, false);
+ break;
+ case FHCI_PORT_DISABLED:
+ case FHCI_PORT_WAITING:
+ fhci_gpio_set_value(fhci, GPIO_POWER, true);
+ break;
+ case FHCI_PORT_LOW:
+ fhci_gpio_set_value(fhci, GPIO_SPEED, false);
+ break;
+ case FHCI_PORT_FULL:
+ fhci_gpio_set_value(fhci, GPIO_SPEED, true);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
+}
+
+/* disable the USB port by clearing the EN bit in the USBMOD register */
+void fhci_port_disable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
+ enum fhci_port_status port_status;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_stop_sof_timer(fhci);
+
+ fhci_flush_all_transmissions(usb);
+
+ fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
+ port_status = usb->port_status;
+ usb->port_status = FHCI_PORT_DISABLED;
+
+ /* Enable IDLE since we want to know if something comes along */
+ usb->saved_msk |= USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+
+ /* check if during the disconnection process attached new device */
+ if (port_status == FHCI_PORT_WAITING)
+ fhci_device_connected_interrupt(fhci);
+ usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+ fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* enable the USB port by setting the EN bit in the USBMOD register */
+void fhci_port_enable(void *lld)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)lld;
+ struct fhci_hcd *fhci = usb->fhci;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_config_transceiver(fhci, usb->port_status);
+
+ if ((usb->port_status != FHCI_PORT_FULL) &&
+ (usb->port_status != FHCI_PORT_LOW))
+ fhci_start_sof_timer(fhci);
+
+ usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
+ gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
+ gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
+
+ mdelay(5);
+
+ qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
+ qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
+ qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* generate the RESET condition on the bus */
+void fhci_port_reset(void *lld)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)lld;
+ struct fhci_hcd *fhci = usb->fhci;
+ u8 mode;
+ u16 mask;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_stop_sof_timer(fhci);
+ /* disable the USB controller */
+ mode = in_8(&fhci->regs->usb_usmod);
+ out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
+
+ /* disable idle interrupts */
+ mask = in_be16(&fhci->regs->usb_usbmr);
+ out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
+
+ fhci_io_port_generate_reset(fhci);
+
+ /* enable interrupt on this endpoint */
+ out_be16(&fhci->regs->usb_usbmr, mask);
+
+ /* enable the USB controller */
+ mode = in_8(&fhci->regs->usb_usmod);
+ out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
+ fhci_start_sof_timer(fhci);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ int ret = 0;
+ unsigned long flags;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
+ USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
+ *buf = 1 << 1;
+ ret = 1;
+ fhci_dbg(fhci, "-- %s\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+
+ return ret;
+}
+
+int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ int retval = 0;
+ struct usb_hub_status *hub_status;
+ struct usb_port_status *port_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ fhci->vroot_hub->feature &= (1 << wValue);
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_ENABLE;
+ fhci_port_disable(fhci);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_ENABLE;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_SUSPEND;
+ fhci_stop_sof_timer(fhci);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_SUSPEND;
+ break;
+ case USB_PORT_FEAT_POWER:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_POWER;
+ fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_CONNECTION;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_OVERCURRENT;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_RESET;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ memcpy(buf, root_hub_des, sizeof(root_hub_des));
+ break;
+ case GetHubStatus:
+ hub_status = (struct usb_hub_status *)buf;
+ hub_status->wHubStatus =
+ cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
+ hub_status->wHubChange =
+ cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
+ break;
+ case GetPortStatus:
+ port_status = (struct usb_port_status *)buf;
+ port_status->wPortStatus =
+ cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
+ port_status->wPortChange =
+ cpu_to_le16(fhci->vroot_hub->port.wPortChange);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ fhci->vroot_hub->feature |= (1 << wValue);
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ fhci->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_ENABLE;
+ fhci_port_enable(fhci->usb_lld);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ fhci->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_SUSPEND;
+ fhci_stop_sof_timer(fhci);
+ break;
+ case USB_PORT_FEAT_RESET:
+ fhci->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_RESET;
+ fhci_port_reset(fhci->usb_lld);
+ fhci->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_ENABLE;
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_RESET;
+ break;
+ case USB_PORT_FEAT_POWER:
+ fhci->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_POWER;
+ fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+error:
+ retval = -EPIPE;
+ }
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+
+ return retval;
+}
diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c
new file mode 100644
index 000000000..b0b88f57a
--- /dev/null
+++ b/drivers/usb/host/fhci-mem.c
@@ -0,0 +1,114 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "fhci.h"
+
+static void init_td(struct td *td)
+{
+ memset(td, 0, sizeof(*td));
+ INIT_LIST_HEAD(&td->node);
+ INIT_LIST_HEAD(&td->frame_lh);
+}
+
+static void init_ed(struct ed *ed)
+{
+ memset(ed, 0, sizeof(*ed));
+ INIT_LIST_HEAD(&ed->td_list);
+ INIT_LIST_HEAD(&ed->node);
+}
+
+static struct td *get_empty_td(struct fhci_hcd *fhci)
+{
+ struct td *td;
+
+ if (!list_empty(&fhci->empty_tds)) {
+ td = list_entry(fhci->empty_tds.next, struct td, node);
+ list_del(fhci->empty_tds.next);
+ } else {
+ td = kmalloc(sizeof(*td), GFP_ATOMIC);
+ if (!td)
+ fhci_err(fhci, "No memory to allocate to TD\n");
+ else
+ init_td(td);
+ }
+
+ return td;
+}
+
+void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td)
+{
+ init_td(td);
+ list_add(&td->node, &fhci->empty_tds);
+}
+
+struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci)
+{
+ struct ed *ed;
+
+ if (!list_empty(&fhci->empty_eds)) {
+ ed = list_entry(fhci->empty_eds.next, struct ed, node);
+ list_del(fhci->empty_eds.next);
+ } else {
+ ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
+ if (!ed)
+ fhci_err(fhci, "No memory to allocate to ED\n");
+ else
+ init_ed(ed);
+ }
+
+ return ed;
+}
+
+void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed)
+{
+ init_ed(ed);
+ list_add(&ed->node, &fhci->empty_eds);
+}
+
+struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb,
+ struct urb_priv *urb_priv, struct ed *ed, u16 index,
+ enum fhci_ta_type type, int toggle, u8 *data, u32 len,
+ u16 interval, u16 start_frame, bool ioc)
+{
+ struct td *td = get_empty_td(fhci);
+
+ if (!td)
+ return NULL;
+
+ td->urb = urb;
+ td->ed = ed;
+ td->type = type;
+ td->toggle = toggle;
+ td->data = data;
+ td->len = len;
+ td->iso_index = index;
+ td->interval = interval;
+ td->start_frame = start_frame;
+ td->ioc = ioc;
+ td->status = USB_TD_OK;
+
+ urb_priv->tds[index] = td;
+
+ return td;
+}
diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c
new file mode 100644
index 000000000..03be7494a
--- /dev/null
+++ b/drivers/usb/host/fhci-q.c
@@ -0,0 +1,285 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "fhci.h"
+
+/* maps the hardware error code to the USB error code */
+static int status_to_error(u32 status)
+{
+ if (status == USB_TD_OK)
+ return 0;
+ else if (status & USB_TD_RX_ER_CRC)
+ return -EILSEQ;
+ else if (status & USB_TD_RX_ER_NONOCT)
+ return -EPROTO;
+ else if (status & USB_TD_RX_ER_OVERUN)
+ return -ECOMM;
+ else if (status & USB_TD_RX_ER_BITSTUFF)
+ return -EPROTO;
+ else if (status & USB_TD_RX_ER_PID)
+ return -EILSEQ;
+ else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT))
+ return -ETIMEDOUT;
+ else if (status & USB_TD_TX_ER_STALL)
+ return -EPIPE;
+ else if (status & USB_TD_TX_ER_UNDERUN)
+ return -ENOSR;
+ else if (status & USB_TD_RX_DATA_UNDERUN)
+ return -EREMOTEIO;
+ else if (status & USB_TD_RX_DATA_OVERUN)
+ return -EOVERFLOW;
+ else
+ return -EINVAL;
+}
+
+void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td)
+{
+ list_add_tail(&td->frame_lh, &frame->tds_list);
+}
+
+void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number)
+{
+ int i;
+
+ for (i = 0; i < number; i++) {
+ struct td *td = td_list[i];
+ list_add_tail(&td->node, &ed->td_list);
+ }
+ if (ed->td_head == NULL)
+ ed->td_head = td_list[0];
+}
+
+static struct td *peek_td_from_ed(struct ed *ed)
+{
+ struct td *td;
+
+ if (!list_empty(&ed->td_list))
+ td = list_entry(ed->td_list.next, struct td, node);
+ else
+ td = NULL;
+
+ return td;
+}
+
+struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame)
+{
+ struct td *td;
+
+ if (!list_empty(&frame->tds_list)) {
+ td = list_entry(frame->tds_list.next, struct td, frame_lh);
+ list_del_init(frame->tds_list.next);
+ } else
+ td = NULL;
+
+ return td;
+}
+
+struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame)
+{
+ struct td *td;
+
+ if (!list_empty(&frame->tds_list))
+ td = list_entry(frame->tds_list.next, struct td, frame_lh);
+ else
+ td = NULL;
+
+ return td;
+}
+
+struct td *fhci_remove_td_from_ed(struct ed *ed)
+{
+ struct td *td;
+
+ if (!list_empty(&ed->td_list)) {
+ td = list_entry(ed->td_list.next, struct td, node);
+ list_del_init(ed->td_list.next);
+
+ /* if this TD was the ED's head, find next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td,
+ node);
+ else
+ ed->td_head = NULL;
+ } else
+ td = NULL;
+
+ return td;
+}
+
+struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list)
+{
+ struct td *td;
+
+ if (!list_empty(&p_list->done_list)) {
+ td = list_entry(p_list->done_list.next, struct td, node);
+ list_del_init(p_list->done_list.next);
+ } else
+ td = NULL;
+
+ return td;
+}
+
+void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed)
+{
+ struct td *td;
+
+ td = ed->td_head;
+ list_del_init(&td->node);
+
+ /* If this TD was the ED's head,find next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td, node);
+ else {
+ ed->td_head = NULL;
+ ed->state = FHCI_ED_SKIP;
+ }
+ ed->toggle_carry = td->toggle;
+ list_add_tail(&td->node, &usb->hc_list->done_list);
+ if (td->ioc)
+ usb->transfer_confirm(usb->fhci);
+}
+
+/* free done FHCI URB resource such as ED and TD */
+static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb)
+{
+ int i;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct ed *ed = urb_priv->ed;
+
+ for (i = 0; i < urb_priv->num_of_tds; i++) {
+ list_del_init(&urb_priv->tds[i]->node);
+ fhci_recycle_empty_td(fhci, urb_priv->tds[i]);
+ }
+
+ /* if this TD was the ED's head,find the next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td, node);
+ else
+ ed->td_head = NULL;
+
+ kfree(urb_priv->tds);
+ kfree(urb_priv);
+ urb->hcpriv = NULL;
+
+ /* if this TD was the ED's head,find next TD */
+ if (ed->td_head == NULL)
+ list_del_init(&ed->node);
+ fhci->active_urbs--;
+}
+
+/* this routine called to complete and free done URB */
+void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb)
+{
+ free_urb_priv(fhci, urb);
+
+ if (urb->status == -EINPROGRESS) {
+ if (urb->actual_length != urb->transfer_buffer_length &&
+ urb->transfer_flags & URB_SHORT_NOT_OK)
+ urb->status = -EREMOTEIO;
+ else
+ urb->status = 0;
+ }
+
+ usb_hcd_unlink_urb_from_ep(fhci_to_hcd(fhci), urb);
+
+ spin_unlock(&fhci->lock);
+
+ usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status);
+
+ spin_lock(&fhci->lock);
+}
+
+/*
+ * caculate transfer length/stats and update the urb
+ * Precondition: irqsafe(only for urb-?status locking)
+ */
+void fhci_done_td(struct urb *urb, struct td *td)
+{
+ struct ed *ed = td->ed;
+ u32 cc = td->status;
+
+ /* ISO...drivers see per-TD length/status */
+ if (ed->mode == FHCI_TF_ISO) {
+ u32 len;
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK &&
+ cc == USB_TD_RX_DATA_UNDERUN))
+ cc = USB_TD_OK;
+
+ if (usb_pipeout(urb->pipe))
+ len = urb->iso_frame_desc[td->iso_index].length;
+ else
+ len = td->actual_len;
+
+ urb->actual_length += len;
+ urb->iso_frame_desc[td->iso_index].actual_length = len;
+ urb->iso_frame_desc[td->iso_index].status =
+ status_to_error(cc);
+ }
+
+ /* BULK,INT,CONTROL... drivers see aggregate length/status,
+ * except that "setup" bytes aren't counted and "short" transfers
+ * might not be reported as errors.
+ */
+ else {
+ if (td->error_cnt >= 3)
+ urb->error_count = 3;
+
+ /* control endpoint only have soft stalls */
+
+ /* update packet status if needed(short may be ok) */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK) &&
+ cc == USB_TD_RX_DATA_UNDERUN) {
+ ed->state = FHCI_ED_OPER;
+ cc = USB_TD_OK;
+ }
+ if (cc != USB_TD_OK) {
+ if (urb->status == -EINPROGRESS)
+ urb->status = status_to_error(cc);
+ }
+
+ /* count all non-empty packets except control SETUP packet */
+ if (td->type != FHCI_TA_SETUP || td->iso_index != 0)
+ urb->actual_length += td->actual_len;
+ }
+}
+
+/* there are some pedning request to unlink */
+void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed)
+{
+ struct td *td = peek_td_from_ed(ed);
+ struct urb *urb = td->urb;
+ struct urb_priv *urb_priv = urb->hcpriv;
+
+ if (urb_priv->state == URB_DEL) {
+ td = fhci_remove_td_from_ed(ed);
+ /* HC may have partly processed this TD */
+ if (td->status != USB_TD_INPROGRESS)
+ fhci_done_td(urb, td);
+
+ /* URB is done;clean up */
+ if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds)
+ fhci_urb_complete_free(fhci, urb);
+ }
+}
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
new file mode 100644
index 000000000..95ca5986e
--- /dev/null
+++ b/drivers/usb/host/fhci-sched.c
@@ -0,0 +1,898 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006, 2011.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <asm/qe.h>
+#include <asm/fsl_gtm.h>
+#include "fhci.h"
+
+static void recycle_frame(struct fhci_usb *usb, struct packet *pkt)
+{
+ pkt->data = NULL;
+ pkt->len = 0;
+ pkt->status = USB_TD_OK;
+ pkt->info = 0;
+ pkt->priv_data = NULL;
+
+ cq_put(&usb->ep0->empty_frame_Q, pkt);
+}
+
+/* confirm submitted packet */
+void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt)
+{
+ struct td *td;
+ struct packet *td_pkt;
+ struct ed *ed;
+ u32 trans_len;
+ bool td_done = false;
+
+ td = fhci_remove_td_from_frame(usb->actual_frame);
+ td_pkt = td->pkt;
+ trans_len = pkt->len;
+ td->status = pkt->status;
+ if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) {
+ if ((td->data + td->actual_len) && trans_len)
+ memcpy(td->data + td->actual_len, pkt->data,
+ trans_len);
+ cq_put(&usb->ep0->dummy_packets_Q, pkt->data);
+ }
+
+ recycle_frame(usb, pkt);
+
+ ed = td->ed;
+ if (ed->mode == FHCI_TF_ISO) {
+ if (ed->td_list.next->next != &ed->td_list) {
+ struct td *td_next =
+ list_entry(ed->td_list.next->next, struct td,
+ node);
+
+ td_next->start_frame = usb->actual_frame->frame_num;
+ }
+ td->actual_len = trans_len;
+ td_done = true;
+ } else if ((td->status & USB_TD_ERROR) &&
+ !(td->status & USB_TD_TX_ER_NAK)) {
+ /*
+ * There was an error on the transaction (but not NAK).
+ * If it is fatal error (data underrun, stall, bad pid or 3
+ * errors exceeded), mark this TD as done.
+ */
+ if ((td->status & USB_TD_RX_DATA_UNDERUN) ||
+ (td->status & USB_TD_TX_ER_STALL) ||
+ (td->status & USB_TD_RX_ER_PID) ||
+ (++td->error_cnt >= 3)) {
+ ed->state = FHCI_ED_HALTED;
+ td_done = true;
+
+ if (td->status & USB_TD_RX_DATA_UNDERUN) {
+ fhci_dbg(usb->fhci, "td err fu\n");
+ td->toggle = !td->toggle;
+ td->actual_len += trans_len;
+ } else {
+ fhci_dbg(usb->fhci, "td err f!u\n");
+ }
+ } else {
+ fhci_dbg(usb->fhci, "td err !f\n");
+ /* it is not a fatal error -retry this transaction */
+ td->nak_cnt = 0;
+ td->error_cnt++;
+ td->status = USB_TD_OK;
+ }
+ } else if (td->status & USB_TD_TX_ER_NAK) {
+ /* there was a NAK response */
+ fhci_vdbg(usb->fhci, "td nack\n");
+ td->nak_cnt++;
+ td->error_cnt = 0;
+ td->status = USB_TD_OK;
+ } else {
+ /* there was no error on transaction */
+ td->error_cnt = 0;
+ td->nak_cnt = 0;
+ td->toggle = !td->toggle;
+ td->actual_len += trans_len;
+
+ if (td->len == td->actual_len)
+ td_done = true;
+ }
+
+ if (td_done)
+ fhci_move_td_from_ed_to_done_list(usb, ed);
+}
+
+/*
+ * Flush all transmitted packets from BDs
+ * This routine is called when disabling the USB port to flush all
+ * transmissions that are already scheduled in the BDs
+ */
+void fhci_flush_all_transmissions(struct fhci_usb *usb)
+{
+ u8 mode;
+ struct td *td;
+
+ mode = in_8(&usb->fhci->regs->usb_usmod);
+ clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN);
+
+ fhci_flush_bds(usb);
+
+ while ((td = fhci_peek_td_from_frame(usb->actual_frame)) != NULL) {
+ struct packet *pkt = td->pkt;
+
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ fhci_transaction_confirm(usb, pkt);
+ }
+
+ usb->actual_frame->frame_status = FRAME_END_TRANSMISSION;
+
+ /* reset the event register */
+ out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+ /* enable the USB controller */
+ out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN);
+}
+
+/*
+ * This function forms the packet and transmit the packet. This function
+ * will handle all endpoint type:ISO,interrupt,control and bulk
+ */
+static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td)
+{
+ u32 fw_transaction_time, len = 0;
+ struct packet *pkt;
+ u8 *data = NULL;
+
+ /* calcalate data address,len and toggle and then add the transaction */
+ if (td->toggle == USB_TD_TOGGLE_CARRY)
+ td->toggle = ed->toggle_carry;
+
+ switch (ed->mode) {
+ case FHCI_TF_ISO:
+ len = td->len;
+ if (td->type != FHCI_TA_IN)
+ data = td->data;
+ break;
+ case FHCI_TF_CTRL:
+ case FHCI_TF_BULK:
+ len = min(td->len - td->actual_len, ed->max_pkt_size);
+ if (!((td->type == FHCI_TA_IN) &&
+ ((len + td->actual_len) == td->len)))
+ data = td->data + td->actual_len;
+ break;
+ case FHCI_TF_INTR:
+ len = min(td->len, ed->max_pkt_size);
+ if (!((td->type == FHCI_TA_IN) &&
+ ((td->len + CRC_SIZE) >= ed->max_pkt_size)))
+ data = td->data;
+ break;
+ default:
+ break;
+ }
+
+ if (usb->port_status == FHCI_PORT_FULL)
+ fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4);
+ else
+ fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6);
+
+ /* check if there's enough space in this frame to submit this TD */
+ if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >=
+ usb->max_bytes_per_frame) {
+ fhci_vdbg(usb->fhci, "not enough space in this frame: "
+ "%d %d %d\n", usb->actual_frame->total_bytes, len,
+ usb->max_bytes_per_frame);
+ return -1;
+ }
+
+ /* check if there's enough time in this frame to submit this TD */
+ if (usb->actual_frame->frame_status != FRAME_IS_PREPARED &&
+ (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION ||
+ (fw_transaction_time + usb->sw_transaction_time >=
+ 1000 - fhci_get_sof_timer_count(usb)))) {
+ fhci_dbg(usb->fhci, "not enough time in this frame\n");
+ return -1;
+ }
+
+ /* update frame object fields before transmitting */
+ pkt = cq_get(&usb->ep0->empty_frame_Q);
+ if (!pkt) {
+ fhci_dbg(usb->fhci, "there is no empty frame\n");
+ return -1;
+ }
+ td->pkt = pkt;
+
+ pkt->info = 0;
+ if (data == NULL) {
+ data = cq_get(&usb->ep0->dummy_packets_Q);
+ BUG_ON(!data);
+ pkt->info = PKT_DUMMY_PACKET;
+ }
+ pkt->data = data;
+ pkt->len = len;
+ pkt->status = USB_TD_OK;
+ /* update TD status field before transmitting */
+ td->status = USB_TD_INPROGRESS;
+ /* update actual frame time object with the actual transmission */
+ usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD);
+ fhci_add_td_to_frame(usb->actual_frame, td);
+
+ if (usb->port_status != FHCI_PORT_FULL &&
+ usb->port_status != FHCI_PORT_LOW) {
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ pkt->len = 0;
+ fhci_transaction_confirm(usb, pkt);
+ } else if (fhci_host_transaction(usb, pkt, td->type, ed->dev_addr,
+ ed->ep_addr, ed->mode, ed->speed, td->toggle)) {
+ /* remove TD from actual frame */
+ list_del_init(&td->frame_lh);
+ td->status = USB_TD_OK;
+ if (pkt->info & PKT_DUMMY_PACKET)
+ cq_put(&usb->ep0->dummy_packets_Q, pkt->data);
+ recycle_frame(usb, pkt);
+ usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD);
+ fhci_err(usb->fhci, "host transaction failed\n");
+ return -1;
+ }
+
+ return len;
+}
+
+static void move_head_to_tail(struct list_head *list)
+{
+ struct list_head *node = list->next;
+
+ if (!list_empty(list)) {
+ list_move_tail(node, list);
+ }
+}
+
+/*
+ * This function goes through the endpoint list and schedules the
+ * transactions within this list
+ */
+static int scan_ed_list(struct fhci_usb *usb,
+ struct list_head *list, enum fhci_tf_mode list_type)
+{
+ static const int frame_part[4] = {
+ [FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME,
+ [FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME *
+ MAX_PERIODIC_FRAME_USAGE) / 100,
+ [FHCI_TF_BULK] = MAX_BYTES_PER_FRAME,
+ [FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME *
+ MAX_PERIODIC_FRAME_USAGE) / 100
+ };
+ struct ed *ed;
+ struct td *td;
+ int ans = 1;
+ u32 save_transaction_time = usb->sw_transaction_time;
+
+ list_for_each_entry(ed, list, node) {
+ td = ed->td_head;
+
+ if (!td || (td && td->status == USB_TD_INPROGRESS))
+ continue;
+
+ if (ed->state != FHCI_ED_OPER) {
+ if (ed->state == FHCI_ED_URB_DEL) {
+ td->status = USB_TD_OK;
+ fhci_move_td_from_ed_to_done_list(usb, ed);
+ ed->state = FHCI_ED_SKIP;
+ }
+ continue;
+ }
+
+ /*
+ * if it isn't interrupt pipe or it is not iso pipe and the
+ * interval time passed
+ */
+ if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) &&
+ (((usb->actual_frame->frame_num -
+ td->start_frame) & 0x7ff) < td->interval))
+ continue;
+
+ if (add_packet(usb, ed, td) < 0)
+ continue;
+
+ /* update time stamps in the TD */
+ td->start_frame = usb->actual_frame->frame_num;
+ usb->sw_transaction_time += save_transaction_time;
+
+ if (usb->actual_frame->total_bytes >=
+ usb->max_bytes_per_frame) {
+ usb->actual_frame->frame_status =
+ FRAME_DATA_END_TRANSMISSION;
+ fhci_push_dummy_bd(usb->ep0);
+ ans = 0;
+ break;
+ }
+
+ if (usb->actual_frame->total_bytes >= frame_part[list_type])
+ break;
+ }
+
+ /* be fair to each ED(move list head around) */
+ move_head_to_tail(list);
+ usb->sw_transaction_time = save_transaction_time;
+
+ return ans;
+}
+
+static u32 rotate_frames(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (!list_empty(&usb->actual_frame->tds_list)) {
+ if ((((in_be16(&fhci->pram->frame_num) & 0x07ff) -
+ usb->actual_frame->frame_num) & 0x7ff) > 5)
+ fhci_flush_actual_frame(usb);
+ else
+ return -EINVAL;
+ }
+
+ usb->actual_frame->frame_status = FRAME_IS_PREPARED;
+ usb->actual_frame->frame_num = in_be16(&fhci->pram->frame_num) & 0x7ff;
+ usb->actual_frame->total_bytes = 0;
+
+ return 0;
+}
+
+/*
+ * This function schedule the USB transaction and will process the
+ * endpoint in the following order: iso, interrupt, control and bulk.
+ */
+void fhci_schedule_transactions(struct fhci_usb *usb)
+{
+ int left = 1;
+
+ if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+ if (rotate_frames(usb) != 0)
+ return;
+
+ if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+ return;
+
+ if (usb->actual_frame->total_bytes == 0) {
+ /*
+ * schedule the next available ISO transfer
+ *or next stage of the ISO transfer
+ */
+ scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO);
+
+ /*
+ * schedule the next available interrupt transfer or
+ * the next stage of the interrupt transfer
+ */
+ scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR);
+
+ /*
+ * schedule the next available control transfer
+ * or the next stage of the control transfer
+ */
+ left = scan_ed_list(usb, &usb->hc_list->ctrl_list,
+ FHCI_TF_CTRL);
+ }
+
+ /*
+ * schedule the next available bulk transfer or the next stage of the
+ * bulk transfer
+ */
+ if (left > 0)
+ scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK);
+}
+
+/* Handles SOF interrupt */
+static void sof_interrupt(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ if ((usb->port_status == FHCI_PORT_DISABLED) &&
+ (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) &&
+ !(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) {
+ if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED)
+ usb->port_status = FHCI_PORT_LOW;
+ else
+ usb->port_status = FHCI_PORT_FULL;
+ /* Disable IDLE */
+ usb->saved_msk &= ~USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+ }
+
+ gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false);
+
+ fhci_host_transmit_actual_frame(usb);
+ usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED;
+
+ fhci_schedule_transactions(usb);
+}
+
+/* Handles device disconnected interrupt on port */
+void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_usb_disable_interrupt(usb);
+ clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
+ usb->port_status = FHCI_PORT_DISABLED;
+
+ fhci_stop_sof_timer(fhci);
+
+ /* Enable IDLE since we want to know if something comes along */
+ usb->saved_msk |= USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
+
+ usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame = 0;
+ fhci_usb_enable_interrupt(usb);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* detect a new device connected on the USB port */
+void fhci_device_connected_interrupt(struct fhci_hcd *fhci)
+{
+
+ struct fhci_usb *usb = fhci->usb_lld;
+ int state;
+ int ret;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_usb_disable_interrupt(usb);
+ state = fhci_ioports_check_bus_state(fhci);
+
+ /* low-speed device was connected to the USB port */
+ if (state == 1) {
+ ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+ if (ret) {
+ fhci_warn(fhci, "Low-Speed device is not supported, "
+ "try use BRGx\n");
+ goto out;
+ }
+
+ usb->port_status = FHCI_PORT_LOW;
+ setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
+ usb->vroot_hub->port.wPortStatus |=
+ (USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_CONNECTION);
+ usb->vroot_hub->port.wPortChange |=
+ USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame =
+ (MAX_BYTES_PER_FRAME >> 3) - 7;
+ fhci_port_enable(usb);
+ } else if (state == 2) {
+ ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+ if (ret) {
+ fhci_warn(fhci, "Full-Speed device is not supported, "
+ "try use CLKx\n");
+ goto out;
+ }
+
+ usb->port_status = FHCI_PORT_FULL;
+ clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS);
+ usb->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_LOW_SPEED;
+ usb->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_CONNECTION;
+ usb->vroot_hub->port.wPortChange |=
+ USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15);
+ fhci_port_enable(usb);
+ }
+out:
+ fhci_usb_enable_interrupt(usb);
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd)
+{
+ struct usb_hcd *hcd = _hcd;
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ spin_lock(&fhci->lock);
+
+ gtm_set_exact_timer16(fhci->timer, 1000, false);
+
+ if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) {
+ usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+ fhci_push_dummy_bd(usb->ep0);
+ }
+
+ fhci_schedule_transactions(usb);
+
+ spin_unlock(&fhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* Cancel transmission on the USB endpoint */
+static void abort_transmission(struct fhci_usb *usb)
+{
+ fhci_dbg(usb->fhci, "-> %s\n", __func__);
+ /* issue stop Tx command */
+ qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+ /* flush Tx FIFOs */
+ out_8(&usb->fhci->regs->usb_uscom, USB_CMD_FLUSH_FIFO | EP_ZERO);
+ udelay(1000);
+ /* reset Tx BDs */
+ fhci_flush_bds(usb);
+ /* issue restart Tx command */
+ qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+ fhci_dbg(usb->fhci, "<- %s\n", __func__);
+}
+
+irqreturn_t fhci_irq(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb;
+ u16 usb_er = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ usb = fhci->usb_lld;
+
+ usb_er |= in_be16(&usb->fhci->regs->usb_usber) &
+ in_be16(&usb->fhci->regs->usb_usbmr);
+
+ /* clear event bits for next time */
+ out_be16(&usb->fhci->regs->usb_usber, usb_er);
+
+ fhci_dbg_isr(fhci, usb_er);
+
+ if (usb_er & USB_E_RESET_MASK) {
+ if ((usb->port_status == FHCI_PORT_FULL) ||
+ (usb->port_status == FHCI_PORT_LOW)) {
+ fhci_device_disconnected_interrupt(fhci);
+ usb_er &= ~USB_E_IDLE_MASK;
+ } else if (usb->port_status == FHCI_PORT_WAITING) {
+ usb->port_status = FHCI_PORT_DISCONNECTING;
+
+ /* Turn on IDLE since we want to disconnect */
+ usb->saved_msk |= USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_usber,
+ usb->saved_msk);
+ } else if (usb->port_status == FHCI_PORT_DISABLED) {
+ if (fhci_ioports_check_bus_state(fhci) == 1)
+ fhci_device_connected_interrupt(fhci);
+ }
+ usb_er &= ~USB_E_RESET_MASK;
+ }
+
+ if (usb_er & USB_E_MSF_MASK) {
+ abort_transmission(fhci->usb_lld);
+ usb_er &= ~USB_E_MSF_MASK;
+ }
+
+ if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) {
+ sof_interrupt(fhci);
+ usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK);
+ }
+
+ if (usb_er & USB_E_TXB_MASK) {
+ fhci_tx_conf_interrupt(fhci->usb_lld);
+ usb_er &= ~USB_E_TXB_MASK;
+ }
+
+ if (usb_er & USB_E_TXE1_MASK) {
+ fhci_tx_conf_interrupt(fhci->usb_lld);
+ usb_er &= ~USB_E_TXE1_MASK;
+ }
+
+ if (usb_er & USB_E_IDLE_MASK) {
+ if (usb->port_status == FHCI_PORT_DISABLED) {
+ usb_er &= ~USB_E_RESET_MASK;
+ fhci_device_connected_interrupt(fhci);
+ } else if (usb->port_status ==
+ FHCI_PORT_DISCONNECTING) {
+ /* XXX usb->port_status = FHCI_PORT_WAITING; */
+ /* Disable IDLE */
+ usb->saved_msk &= ~USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_usbmr,
+ usb->saved_msk);
+ } else {
+ fhci_dbg_isr(fhci, -1);
+ }
+
+ usb_er &= ~USB_E_IDLE_MASK;
+ }
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * Process normal completions(error or success) and clean the schedule.
+ *
+ * This is the main path for handing urbs back to drivers. The only other patth
+ * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
+ * the (re-reversed) done list as this does.
+ */
+static void process_done_list(unsigned long data)
+{
+ struct urb *urb;
+ struct ed *ed;
+ struct td *td;
+ struct urb_priv *urb_priv;
+ struct fhci_hcd *fhci = (struct fhci_hcd *)data;
+
+ disable_irq(fhci->timer->irq);
+ disable_irq(fhci_to_hcd(fhci)->irq);
+ spin_lock(&fhci->lock);
+
+ td = fhci_remove_td_from_done_list(fhci->hc_list);
+ while (td != NULL) {
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+ ed = td->ed;
+
+ /* update URB's length and status from TD */
+ fhci_done_td(urb, td);
+ urb_priv->tds_cnt++;
+
+ /*
+ * if all this urb's TDs are done, call complete()
+ * Interrupt transfers are the onley special case:
+ * they are reissued,until "deleted" by usb_unlink_urb
+ * (real work done in a SOF intr, by process_del_list)
+ */
+ if (urb_priv->tds_cnt == urb_priv->num_of_tds) {
+ fhci_urb_complete_free(fhci, urb);
+ } else if (urb_priv->state == URB_DEL &&
+ ed->state == FHCI_ED_SKIP) {
+ fhci_del_ed_list(fhci, ed);
+ ed->state = FHCI_ED_OPER;
+ } else if (ed->state == FHCI_ED_HALTED) {
+ urb_priv->state = URB_DEL;
+ ed->state = FHCI_ED_URB_DEL;
+ fhci_del_ed_list(fhci, ed);
+ ed->state = FHCI_ED_OPER;
+ }
+
+ td = fhci_remove_td_from_done_list(fhci->hc_list);
+ }
+
+ spin_unlock(&fhci->lock);
+ enable_irq(fhci->timer->irq);
+ enable_irq(fhci_to_hcd(fhci)->irq);
+}
+
+DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
+
+/* transfer complted callback */
+u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci)
+{
+ if (!fhci->process_done_task->state)
+ tasklet_schedule(fhci->process_done_task);
+ return 0;
+}
+
+/*
+ * adds urb to the endpoint descriptor list
+ * arguments:
+ * fhci data structure for the Low level host controller
+ * ep USB Host endpoint data structure
+ * urb USB request block data structure
+ */
+void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb)
+{
+ struct ed *ed = urb->ep->hcpriv;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ u32 data_len = urb->transfer_buffer_length;
+ int urb_state = 0;
+ int toggle = 0;
+ struct td *td;
+ u8 *data;
+ u16 cnt = 0;
+
+ if (ed == NULL) {
+ ed = fhci_get_empty_ed(fhci);
+ ed->dev_addr = usb_pipedevice(urb->pipe);
+ ed->ep_addr = usb_pipeendpoint(urb->pipe);
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ed->mode = FHCI_TF_CTRL;
+ break;
+ case PIPE_BULK:
+ ed->mode = FHCI_TF_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ ed->mode = FHCI_TF_INTR;
+ break;
+ case PIPE_ISOCHRONOUS:
+ ed->mode = FHCI_TF_ISO;
+ break;
+ default:
+ break;
+ }
+ ed->speed = (urb->dev->speed == USB_SPEED_LOW) ?
+ FHCI_LOW_SPEED : FHCI_FULL_SPEED;
+ ed->max_pkt_size = usb_maxpacket(urb->dev,
+ urb->pipe, usb_pipeout(urb->pipe));
+ urb->ep->hcpriv = ed;
+ fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n",
+ ed->speed, ed->max_pkt_size);
+ }
+
+ /* for ISO transfer calculate start frame index */
+ if (ed->mode == FHCI_TF_ISO) {
+ /* Ignore the possibility of underruns */
+ urb->start_frame = ed->td_head ? ed->next_iso :
+ get_frame_num(fhci);
+ ed->next_iso = (urb->start_frame + urb->interval *
+ urb->number_of_packets) & 0x07ff;
+ }
+
+ /*
+ * OHCI handles the DATA toggle itself,we just use the USB
+ * toggle bits
+ */
+ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)))
+ toggle = USB_TD_TOGGLE_CARRY;
+ else {
+ toggle = USB_TD_TOGGLE_DATA0;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ }
+
+ urb_priv->tds_cnt = 0;
+ urb_priv->ed = ed;
+ if (data_len > 0)
+ data = urb->transfer_buffer;
+ else
+ data = NULL;
+
+ switch (ed->mode) {
+ case FHCI_TF_BULK:
+ if (urb->transfer_flags & URB_ZERO_PACKET &&
+ urb->transfer_buffer_length > 0 &&
+ ((urb->transfer_buffer_length %
+ usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe))) == 0))
+ urb_state = US_BULK0;
+ while (data_len > 4096) {
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ cnt ? USB_TD_TOGGLE_CARRY :
+ toggle,
+ data, 4096, 0, 0, true);
+ data += 4096;
+ data_len -= 4096;
+ cnt++;
+ }
+
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+ cnt ? USB_TD_TOGGLE_CARRY : toggle,
+ data, data_len, 0, 0, true);
+ cnt++;
+
+ if (urb->transfer_flags & URB_ZERO_PACKET &&
+ cnt < urb_priv->num_of_tds) {
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true);
+ cnt++;
+ }
+ break;
+ case FHCI_TF_INTR:
+ urb->start_frame = get_frame_num(fhci) + 1;
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA0, data, data_len,
+ urb->interval, urb->start_frame, true);
+ break;
+ case FHCI_TF_CTRL:
+ ed->dev_addr = usb_pipedevice(urb->pipe);
+ ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ /* setup stage */
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
+ USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true);
+
+ /* data stage */
+ if (data_len > 0) {
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
+ true);
+ }
+
+ /* status stage */
+ if (data_len > 0)
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ (usb_pipeout(urb->pipe) ? FHCI_TA_IN :
+ FHCI_TA_OUT),
+ USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+ else
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+
+ urb_state = US_CTRL_SETUP;
+ break;
+ case FHCI_TF_ISO:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ u16 frame = urb->start_frame;
+
+ /*
+ * FIXME scheduling should handle frame counter
+ * roll-around ... exotic case (and OHCI has
+ * a 2^16 iso range, vs other HCs max of 2^10)
+ */
+ frame += cnt * urb->interval;
+ frame &= 0x07ff;
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA0,
+ data + urb->iso_frame_desc[cnt].offset,
+ urb->iso_frame_desc[cnt].length,
+ urb->interval, frame, true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * set the state of URB
+ * control pipe:3 states -- setup,data,status
+ * interrupt and bulk pipe:1 state -- data
+ */
+ urb->pipe &= ~0x1f;
+ urb->pipe |= urb_state & 0x1f;
+
+ urb_priv->state = URB_INPROGRESS;
+
+ if (!ed->td_head) {
+ ed->state = FHCI_ED_OPER;
+ switch (ed->mode) {
+ case FHCI_TF_CTRL:
+ list_add(&ed->node, &fhci->hc_list->ctrl_list);
+ break;
+ case FHCI_TF_BULK:
+ list_add(&ed->node, &fhci->hc_list->bulk_list);
+ break;
+ case FHCI_TF_INTR:
+ list_add(&ed->node, &fhci->hc_list->intr_list);
+ break;
+ case FHCI_TF_ISO:
+ list_add(&ed->node, &fhci->hc_list->iso_list);
+ break;
+ default:
+ break;
+ }
+ }
+
+ fhci_add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds);
+ fhci->active_urbs++;
+}
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
new file mode 100644
index 000000000..1498061f0
--- /dev/null
+++ b/drivers/usb/host/fhci-tds.c
@@ -0,0 +1,626 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "fhci.h"
+
+#define DUMMY_BD_BUFFER 0xdeadbeef
+#define DUMMY2_BD_BUFFER 0xbaadf00d
+
+/* Transaction Descriptors bits */
+#define TD_R 0x8000 /* ready bit */
+#define TD_W 0x2000 /* wrap bit */
+#define TD_I 0x1000 /* interrupt on completion */
+#define TD_L 0x0800 /* last */
+#define TD_TC 0x0400 /* transmit CRC */
+#define TD_CNF 0x0200 /* CNF - Must be always 1 */
+#define TD_LSP 0x0100 /* Low-speed transaction */
+#define TD_PID 0x00c0 /* packet id */
+#define TD_RXER 0x0020 /* Rx error or not */
+
+#define TD_NAK 0x0010 /* No ack. */
+#define TD_STAL 0x0008 /* Stall received */
+#define TD_TO 0x0004 /* time out */
+#define TD_UN 0x0002 /* underrun */
+#define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */
+#define TD_AB 0x0008 /* Frame Aborted */
+#define TD_CR 0x0004 /* CRC Error */
+#define TD_OV 0x0002 /* Overrun */
+#define TD_BOV 0x0001 /* Buffer Overrun */
+
+#define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \
+ TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV)
+
+#define TD_PID_DATA0 0x0080 /* Data 0 toggle */
+#define TD_PID_DATA1 0x00c0 /* Data 1 toggle */
+#define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */
+
+#define TD_TOK_SETUP 0x0000
+#define TD_TOK_OUT 0x4000
+#define TD_TOK_IN 0x8000
+#define TD_ISO 0x1000
+#define TD_ENDP 0x0780
+#define TD_ADDR 0x007f
+
+#define TD_ENDP_SHIFT 7
+
+struct usb_td {
+ __be16 status;
+ __be16 length;
+ __be32 buf_ptr;
+ __be16 extra;
+ __be16 reserved;
+};
+
+static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
+ struct usb_td __iomem *td,
+ u16 status)
+{
+ if (status & TD_W)
+ return base;
+ else
+ return ++td;
+}
+
+void fhci_push_dummy_bd(struct endpoint *ep)
+{
+ if (ep->already_pushed_dummy_bd == false) {
+ u16 td_status = in_be16(&ep->empty_td->status);
+
+ out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER);
+ /* get the next TD in the ring */
+ ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+ ep->already_pushed_dummy_bd = true;
+ }
+}
+
+/* destroy an USB endpoint */
+void fhci_ep0_free(struct fhci_usb *usb)
+{
+ struct endpoint *ep;
+ int size;
+
+ ep = usb->ep0;
+ if (ep) {
+ if (ep->td_base)
+ cpm_muram_free(cpm_muram_offset(ep->td_base));
+
+ if (kfifo_initialized(&ep->conf_frame_Q)) {
+ size = cq_howmany(&ep->conf_frame_Q);
+ for (; size; size--) {
+ struct packet *pkt = cq_get(&ep->conf_frame_Q);
+
+ kfree(pkt);
+ }
+ cq_delete(&ep->conf_frame_Q);
+ }
+
+ if (kfifo_initialized(&ep->empty_frame_Q)) {
+ size = cq_howmany(&ep->empty_frame_Q);
+ for (; size; size--) {
+ struct packet *pkt = cq_get(&ep->empty_frame_Q);
+
+ kfree(pkt);
+ }
+ cq_delete(&ep->empty_frame_Q);
+ }
+
+ if (kfifo_initialized(&ep->dummy_packets_Q)) {
+ size = cq_howmany(&ep->dummy_packets_Q);
+ for (; size; size--) {
+ u8 *buff = cq_get(&ep->dummy_packets_Q);
+
+ kfree(buff);
+ }
+ cq_delete(&ep->dummy_packets_Q);
+ }
+
+ kfree(ep);
+ usb->ep0 = NULL;
+ }
+}
+
+/*
+ * create the endpoint structure
+ *
+ * arguments:
+ * usb A pointer to the data structure of the USB
+ * data_mem The data memory partition(BUS)
+ * ring_len TD ring length
+ */
+u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+ u32 ring_len)
+{
+ struct endpoint *ep;
+ struct usb_td __iomem *td;
+ unsigned long ep_offset;
+ char *err_for = "endpoint PRAM";
+ int ep_mem_size;
+ u32 i;
+
+ /* we need at least 3 TDs in the ring */
+ if (!(ring_len > 2)) {
+ fhci_err(usb->fhci, "illegal TD ring length parameters\n");
+ return -EINVAL;
+ }
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram);
+ ep_offset = cpm_muram_alloc(ep_mem_size, 32);
+ if (IS_ERR_VALUE(ep_offset))
+ goto err;
+ ep->td_base = cpm_muram_addr(ep_offset);
+
+ /* zero all queue pointers */
+ if (cq_new(&ep->conf_frame_Q, ring_len + 2) ||
+ cq_new(&ep->empty_frame_Q, ring_len + 2) ||
+ cq_new(&ep->dummy_packets_Q, ring_len + 2)) {
+ err_for = "frame_queues";
+ goto err;
+ }
+
+ for (i = 0; i < (ring_len + 1); i++) {
+ struct packet *pkt;
+ u8 *buff;
+
+ pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt) {
+ err_for = "frame";
+ goto err;
+ }
+
+ buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL);
+ if (!buff) {
+ kfree(pkt);
+ err_for = "buffer";
+ goto err;
+ }
+ cq_put(&ep->empty_frame_Q, pkt);
+ cq_put(&ep->dummy_packets_Q, buff);
+ }
+
+ /* we put the endpoint parameter RAM right behind the TD ring */
+ ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len;
+
+ ep->conf_td = ep->td_base;
+ ep->empty_td = ep->td_base;
+
+ ep->already_pushed_dummy_bd = false;
+
+ /* initialize tds */
+ td = ep->td_base;
+ for (i = 0; i < ring_len; i++) {
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->status, 0);
+ out_be16(&td->length, 0);
+ out_be16(&td->extra, 0);
+ td++;
+ }
+ td--;
+ out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+ out_be16(&td->length, 0);
+
+ /* endpoint structure has been created */
+ usb->ep0 = ep;
+
+ return 0;
+err:
+ fhci_ep0_free(usb);
+ kfree(ep);
+ fhci_err(usb->fhci, "no memory for the %s\n", err_for);
+ return -ENOMEM;
+}
+
+/*
+ * initialize the endpoint register according to the given parameters
+ *
+ * artuments:
+ * usb A pointer to the data strucutre of the USB
+ * ep A pointer to the endpoint structre
+ * data_mem The data memory partition(BUS)
+ */
+void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep,
+ enum fhci_mem_alloc data_mem)
+{
+ u8 rt;
+
+ /* set the endpoint registers according to the endpoint */
+ out_be16(&usb->fhci->regs->usb_usep[0],
+ USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
+ out_be16(&usb->fhci->pram->ep_ptr[0],
+ cpm_muram_offset(ep->ep_pram_ptr));
+
+ rt = (BUS_MODE_BO_BE | BUS_MODE_GBL);
+#ifdef MULTI_DATA_BUS
+ if (data_mem == MEM_SECONDARY)
+ rt |= BUS_MODE_DTB;
+#endif
+ out_8(&ep->ep_pram_ptr->rx_func_code, rt);
+ out_8(&ep->ep_pram_ptr->tx_func_code, rt);
+ out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028);
+ out_be16(&ep->ep_pram_ptr->rx_base, 0);
+ out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base));
+ out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0);
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+}
+
+/*
+ * Collect the submitted frames and inform the application about them
+ * It is also preparing the TDs for new frames. If the Tx interrupts
+ * are disabled, the application should call that routine to get
+ * confirmation about the submitted frames. Otherwise, the routine is
+ * called from the interrupt service routine during the Tx interrupt.
+ * In that case the application is informed by calling the application
+ * specific 'fhci_transaction_confirm' routine
+ */
+static void fhci_td_transaction_confirm(struct fhci_usb *usb)
+{
+ struct endpoint *ep = usb->ep0;
+ struct packet *pkt;
+ struct usb_td __iomem *td;
+ u16 extra_data;
+ u16 td_status;
+ u16 td_length;
+ u32 buf;
+
+ /*
+ * collect transmitted BDs from the chip. The routine clears all BDs
+ * with R bit = 0 and the pointer to data buffer is not NULL, that is
+ * BDs which point to the transmitted data buffer
+ */
+ while (1) {
+ td = ep->conf_td;
+ td_status = in_be16(&td->status);
+ td_length = in_be16(&td->length);
+ buf = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+
+ /* check if the TD is empty */
+ if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf)))
+ break;
+ /* check if it is a dummy buffer */
+ else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W))
+ break;
+
+ /* mark TD as empty */
+ clrbits16(&td->status, ~TD_W);
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+ /* advance the TD pointer */
+ ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
+
+ /* check if it is a dummy buffer(type2) */
+ if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W))
+ continue;
+
+ pkt = cq_get(&ep->conf_frame_Q);
+ if (!pkt)
+ fhci_err(usb->fhci, "no frame to confirm\n");
+
+ if (td_status & TD_ERRORS) {
+ if (td_status & TD_RXER) {
+ if (td_status & TD_CR)
+ pkt->status = USB_TD_RX_ER_CRC;
+ else if (td_status & TD_AB)
+ pkt->status = USB_TD_RX_ER_BITSTUFF;
+ else if (td_status & TD_OV)
+ pkt->status = USB_TD_RX_ER_OVERUN;
+ else if (td_status & TD_BOV)
+ pkt->status = USB_TD_RX_DATA_OVERUN;
+ else if (td_status & TD_NO)
+ pkt->status = USB_TD_RX_ER_NONOCT;
+ else
+ fhci_err(usb->fhci, "illegal error "
+ "occurred\n");
+ } else if (td_status & TD_NAK)
+ pkt->status = USB_TD_TX_ER_NAK;
+ else if (td_status & TD_TO)
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ else if (td_status & TD_UN)
+ pkt->status = USB_TD_TX_ER_UNDERUN;
+ else if (td_status & TD_STAL)
+ pkt->status = USB_TD_TX_ER_STALL;
+ else
+ fhci_err(usb->fhci, "illegal error occurred\n");
+ } else if ((extra_data & TD_TOK_IN) &&
+ pkt->len > td_length - CRC_SIZE) {
+ pkt->status = USB_TD_RX_DATA_UNDERUN;
+ }
+
+ if (extra_data & TD_TOK_IN)
+ pkt->len = td_length - CRC_SIZE;
+ else if (pkt->info & PKT_ZLP)
+ pkt->len = 0;
+ else
+ pkt->len = td_length;
+
+ fhci_transaction_confirm(usb, pkt);
+ }
+}
+
+/*
+ * Submitting a data frame to a specified endpoint of a USB device
+ * The frame is put in the driver's transmit queue for this endpoint
+ *
+ * Arguments:
+ * usb A pointer to the USB structure
+ * pkt A pointer to the user frame structure
+ * trans_type Transaction tyep - IN,OUT or SETUP
+ * dest_addr Device address - 0~127
+ * dest_ep Endpoint number of the device - 0~16
+ * trans_mode Pipe type - ISO,Interrupt,bulk or control
+ * dest_speed USB speed - Low speed or FULL speed
+ * data_toggle Data sequence toggle - 0 or 1
+ */
+u32 fhci_host_transaction(struct fhci_usb *usb,
+ struct packet *pkt,
+ enum fhci_ta_type trans_type,
+ u8 dest_addr,
+ u8 dest_ep,
+ enum fhci_tf_mode trans_mode,
+ enum fhci_speed dest_speed, u8 data_toggle)
+{
+ struct endpoint *ep = usb->ep0;
+ struct usb_td __iomem *td;
+ u16 extra_data;
+ u16 td_status;
+
+ fhci_usb_disable_interrupt(usb);
+ /* start from the next BD that should be filled */
+ td = ep->empty_td;
+ td_status = in_be16(&td->status);
+
+ if (td_status & TD_R && in_be16(&td->length)) {
+ /* if the TD is not free */
+ fhci_usb_enable_interrupt(usb);
+ return -1;
+ }
+
+ /* get the next TD in the ring */
+ ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+ fhci_usb_enable_interrupt(usb);
+ pkt->priv_data = td;
+ out_be32(&td->buf_ptr, virt_to_phys(pkt->data));
+ /* sets up transaction parameters - addr,endp,dir,and type */
+ extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr;
+ switch (trans_type) {
+ case FHCI_TA_IN:
+ extra_data |= TD_TOK_IN;
+ break;
+ case FHCI_TA_OUT:
+ extra_data |= TD_TOK_OUT;
+ break;
+ case FHCI_TA_SETUP:
+ extra_data |= TD_TOK_SETUP;
+ break;
+ }
+ if (trans_mode == FHCI_TF_ISO)
+ extra_data |= TD_ISO;
+ out_be16(&td->extra, extra_data);
+
+ /* sets up the buffer descriptor */
+ td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF);
+ if (!(pkt->info & PKT_NO_CRC))
+ td_status |= TD_TC;
+
+ switch (trans_type) {
+ case FHCI_TA_IN:
+ if (data_toggle)
+ pkt->info |= PKT_PID_DATA1;
+ else
+ pkt->info |= PKT_PID_DATA0;
+ break;
+ default:
+ if (data_toggle) {
+ td_status |= TD_PID_DATA1;
+ pkt->info |= PKT_PID_DATA1;
+ } else {
+ td_status |= TD_PID_DATA0;
+ pkt->info |= PKT_PID_DATA0;
+ }
+ break;
+ }
+
+ if ((dest_speed == FHCI_LOW_SPEED) &&
+ (usb->port_status == FHCI_PORT_FULL))
+ td_status |= TD_LSP;
+
+ out_be16(&td->status, td_status);
+
+ /* set up buffer length */
+ if (trans_type == FHCI_TA_IN)
+ out_be16(&td->length, pkt->len + CRC_SIZE);
+ else
+ out_be16(&td->length, pkt->len);
+
+ /* put the frame to the confirmation queue */
+ cq_put(&ep->conf_frame_Q, pkt);
+
+ if (cq_howmany(&ep->conf_frame_Q) == 1)
+ out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
+
+ return 0;
+}
+
+/* Reset the Tx BD ring */
+void fhci_flush_bds(struct fhci_usb *usb)
+{
+ u16 extra_data;
+ u16 td_status;
+ u32 buf;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ td = ep->td_base;
+ while (1) {
+ td_status = in_be16(&td->status);
+ buf = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+
+ /* if the TD is not empty - we'll confirm it as Timeout */
+ if (td_status & TD_R)
+ out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+ /* if this TD is dummy - let's skip this TD */
+ else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
+ out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER);
+ /* if this is the last TD - break */
+ if (td_status & TD_W)
+ break;
+
+ td++;
+ }
+
+ fhci_td_transaction_confirm(usb);
+
+ td = ep->td_base;
+ do {
+ out_be16(&td->status, 0);
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+ td++;
+ } while (!(in_be16(&td->status) & TD_W));
+ out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+ in_be16(&ep->ep_pram_ptr->tx_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+ out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+ ep->empty_td = ep->td_base;
+ ep->conf_td = ep->td_base;
+}
+
+/*
+ * Flush all transmitted packets from TDs in the actual frame.
+ * This routine is called when something wrong with the controller and
+ * we want to get rid of the actual frame and start again next frame
+ */
+void fhci_flush_actual_frame(struct fhci_usb *usb)
+{
+ u8 mode;
+ u16 tb_ptr;
+ u16 extra_data;
+ u16 td_status;
+ u32 buf_ptr;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ /* disable the USB controller */
+ mode = in_8(&usb->fhci->regs->usb_usmod);
+ out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN);
+
+ tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+ td = cpm_muram_addr(tb_ptr);
+ td_status = in_be16(&td->status);
+ buf_ptr = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+ do {
+ if (td_status & TD_R) {
+ out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+ } else {
+ out_be32(&td->buf_ptr, 0);
+ ep->already_pushed_dummy_bd = false;
+ break;
+ }
+
+ /* advance the TD pointer */
+ td = next_bd(ep->td_base, td, td_status);
+ td_status = in_be16(&td->status);
+ buf_ptr = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+ } while ((td_status & TD_R) || buf_ptr);
+
+ fhci_td_transaction_confirm(usb);
+
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+ in_be16(&ep->ep_pram_ptr->tx_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+ out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+ ep->empty_td = ep->td_base;
+ ep->conf_td = ep->td_base;
+
+ usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+
+ /* reset the event register */
+ out_be16(&usb->fhci->regs->usb_usber, 0xffff);
+ /* enable the USB controller */
+ out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN);
+}
+
+/* handles Tx confirm and Tx error interrupt */
+void fhci_tx_conf_interrupt(struct fhci_usb *usb)
+{
+ fhci_td_transaction_confirm(usb);
+
+ /*
+ * Schedule another transaction to this frame only if we have
+ * already confirmed all transaction in the frame.
+ */
+ if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) ||
+ (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
+ (list_empty(&usb->actual_frame->tds_list)))
+ fhci_schedule_transactions(usb);
+}
+
+void fhci_host_transmit_actual_frame(struct fhci_usb *usb)
+{
+ u16 tb_ptr;
+ u16 td_status;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+ td = cpm_muram_addr(tb_ptr);
+
+ if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) {
+ struct usb_td __iomem *old_td = td;
+
+ ep->already_pushed_dummy_bd = false;
+ td_status = in_be16(&td->status);
+ /* gets the next TD in the ring */
+ td = next_bd(ep->td_base, td, td_status);
+ tb_ptr = cpm_muram_offset(td);
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
+
+ /* start transmit only if we have something in the TDs */
+ if (in_be16(&td->status) & TD_R)
+ out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
+
+ if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
+ out_be32(&old_td->buf_ptr, 0);
+ ep->conf_td = next_bd(ep->td_base, ep->conf_td,
+ td_status);
+ } else {
+ out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER);
+ }
+ }
+}
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
new file mode 100644
index 000000000..154e6a007
--- /dev/null
+++ b/drivers/usb/host/fhci.h
@@ -0,0 +1,594 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __FHCI_H
+#define __FHCI_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <asm/qe.h>
+#include <asm/immap_qe.h>
+
+#define USB_CLOCK 48000000
+
+#define FHCI_PRAM_SIZE 0x100
+
+#define MAX_EDS 32
+#define MAX_TDS 32
+
+
+/* CRC16 field size */
+#define CRC_SIZE 2
+
+/* USB protocol overhead for each frame transmitted from the host */
+#define PROTOCOL_OVERHEAD 7
+
+/* Packet structure, info field */
+#define PKT_PID_DATA0 0x80000000 /* PID - Data toggle zero */
+#define PKT_PID_DATA1 0x40000000 /* PID - Data toggle one */
+#define PKT_PID_SETUP 0x20000000 /* PID - Setup bit */
+#define PKT_SETUP_STATUS 0x10000000 /* Setup status bit */
+#define PKT_SETADDR_STATUS 0x08000000 /* Set address status bit */
+#define PKT_SET_HOST_LAST 0x04000000 /* Last data packet */
+#define PKT_HOST_DATA 0x02000000 /* Data packet */
+#define PKT_FIRST_IN_FRAME 0x01000000 /* First packet in the frame */
+#define PKT_TOKEN_FRAME 0x00800000 /* Token packet */
+#define PKT_ZLP 0x00400000 /* Zero length packet */
+#define PKT_IN_TOKEN_FRAME 0x00200000 /* IN token packet */
+#define PKT_OUT_TOKEN_FRAME 0x00100000 /* OUT token packet */
+#define PKT_SETUP_TOKEN_FRAME 0x00080000 /* SETUP token packet */
+#define PKT_STALL_FRAME 0x00040000 /* STALL packet */
+#define PKT_NACK_FRAME 0x00020000 /* NACK packet */
+#define PKT_NO_PID 0x00010000 /* No PID */
+#define PKT_NO_CRC 0x00008000 /* don't append CRC */
+#define PKT_HOST_COMMAND 0x00004000 /* Host command packet */
+#define PKT_DUMMY_PACKET 0x00002000 /* Dummy packet, used for mmm */
+#define PKT_LOW_SPEED_PACKET 0x00001000 /* Low-Speed packet */
+
+#define TRANS_OK (0)
+#define TRANS_INPROGRESS (-1)
+#define TRANS_DISCARD (-2)
+#define TRANS_FAIL (-3)
+
+#define PS_INT 0
+#define PS_DISCONNECTED 1
+#define PS_CONNECTED 2
+#define PS_READY 3
+#define PS_MISSING 4
+
+/* Transfer Descriptor status field */
+#define USB_TD_OK 0x00000000 /* TD transmited or received ok */
+#define USB_TD_INPROGRESS 0x80000000 /* TD is being transmitted */
+#define USB_TD_RX_ER_NONOCT 0x40000000 /* Tx Non Octet Aligned Packet */
+#define USB_TD_RX_ER_BITSTUFF 0x20000000 /* Frame Aborted-Received pkt */
+#define USB_TD_RX_ER_CRC 0x10000000 /* CRC error */
+#define USB_TD_RX_ER_OVERUN 0x08000000 /* Over - run occurred */
+#define USB_TD_RX_ER_PID 0x04000000 /* wrong PID received */
+#define USB_TD_RX_DATA_UNDERUN 0x02000000 /* shorter than expected */
+#define USB_TD_RX_DATA_OVERUN 0x01000000 /* longer than expected */
+#define USB_TD_TX_ER_NAK 0x00800000 /* NAK handshake */
+#define USB_TD_TX_ER_STALL 0x00400000 /* STALL handshake */
+#define USB_TD_TX_ER_TIMEOUT 0x00200000 /* transmit time out */
+#define USB_TD_TX_ER_UNDERUN 0x00100000 /* transmit underrun */
+
+#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \
+ USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \
+ USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \
+ USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \
+ USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN)
+
+/* Transfer Descriptor toggle field */
+#define USB_TD_TOGGLE_DATA0 0
+#define USB_TD_TOGGLE_DATA1 1
+#define USB_TD_TOGGLE_CARRY 2
+
+/* #define MULTI_DATA_BUS */
+
+/* Bus mode register RBMR/TBMR */
+#define BUS_MODE_GBL 0x20 /* Global snooping */
+#define BUS_MODE_BO 0x18 /* Byte ordering */
+#define BUS_MODE_BO_BE 0x10 /* Byte ordering - Big-endian */
+#define BUS_MODE_DTB 0x02 /* Data bus */
+
+/* FHCI QE USB Register Description */
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN 0x01
+#define USB_MODE_HOST 0x02
+#define USB_MODE_TEST 0x04
+#define USB_MODE_SFTE 0x08
+#define USB_MODE_RESUME 0x40
+#define USB_MODE_LSS 0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK 0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK 0xF000
+#define USB_EPNUM_SHIFT 12
+
+#define USB_TRANS_MODE_SHIFT 8
+#define USB_TRANS_CTR 0x0000
+#define USB_TRANS_INT 0x0100
+#define USB_TRANS_BULK 0x0200
+#define USB_TRANS_ISO 0x0300
+
+#define USB_EP_MF 0x0020
+#define USB_EP_RTE 0x0010
+
+#define USB_THS_SHIFT 2
+#define USB_THS_MASK 0x000c
+#define USB_THS_NORMAL 0x0
+#define USB_THS_IGNORE_IN 0x0004
+#define USB_THS_NACK 0x0008
+#define USB_THS_STALL 0x000c
+
+#define USB_RHS_SHIFT 0
+#define USB_RHS_MASK 0x0003
+#define USB_RHS_NORMAL 0x0
+#define USB_RHS_IGNORE_OUT 0x0001
+#define USB_RHS_NACK 0x0002
+#define USB_RHS_STALL 0x0003
+
+#define USB_RTHS_MASK 0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO 0x80
+#define USB_CMD_FLUSH_FIFO 0x40
+#define USB_CMD_ISFT 0x20
+#define USB_CMD_DSFT 0x10
+#define USB_CMD_EP_MASK 0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK 0x0800
+#define USB_E_SFT_MASK 0x0400
+#define USB_E_RESET_MASK 0x0200
+#define USB_E_IDLE_MASK 0x0100
+#define USB_E_TXE4_MASK 0x0080
+#define USB_E_TXE3_MASK 0x0040
+#define USB_E_TXE2_MASK 0x0020
+#define USB_E_TXE1_MASK 0x0010
+#define USB_E_SOF_MASK 0x0008
+#define USB_E_BSY_MASK 0x0004
+#define USB_E_TXB_MASK 0x0002
+#define USB_E_RXB_MASK 0x0001
+
+/* Freescale USB HOST */
+struct fhci_pram {
+ __be16 ep_ptr[4]; /* Endpoint porter reg */
+ __be32 rx_state; /* Rx internal state */
+ __be32 rx_ptr; /* Rx internal data pointer */
+ __be16 frame_num; /* Frame number */
+ __be16 rx_cnt; /* Rx byte count */
+ __be32 rx_temp; /* Rx temp */
+ __be32 rx_data_temp; /* Rx data temp */
+ __be16 rx_u_ptr; /* Rx microcode return address temp */
+ u8 reserved1[2]; /* reserved area */
+ __be32 sof_tbl; /* SOF lookup table pointer */
+ u8 sof_u_crc_temp; /* SOF micorcode CRC5 temp reg */
+ u8 reserved2[0xdb];
+};
+
+/* Freescale USB Endpoint*/
+struct fhci_ep_pram {
+ __be16 rx_base; /* Rx BD base address */
+ __be16 tx_base; /* Tx BD base address */
+ u8 rx_func_code; /* Rx function code */
+ u8 tx_func_code; /* Tx function code */
+ __be16 rx_buff_len; /* Rx buffer length */
+ __be16 rx_bd_ptr; /* Rx BD pointer */
+ __be16 tx_bd_ptr; /* Tx BD pointer */
+ __be32 tx_state; /* Tx internal state */
+ __be32 tx_ptr; /* Tx internal data pointer */
+ __be16 tx_crc; /* temp transmit CRC */
+ __be16 tx_cnt; /* Tx byte count */
+ __be32 tx_temp; /* Tx temp */
+ __be16 tx_u_ptr; /* Tx microcode return address temp */
+ __be16 reserved;
+};
+
+struct fhci_controller_list {
+ struct list_head ctrl_list; /* control endpoints */
+ struct list_head bulk_list; /* bulk endpoints */
+ struct list_head iso_list; /* isochronous endpoints */
+ struct list_head intr_list; /* interruput endpoints */
+ struct list_head done_list; /* done transfers */
+};
+
+struct virtual_root_hub {
+ int dev_num; /* USB address of the root hub */
+ u32 feature; /* indicates what feature has been set */
+ struct usb_hub_status hub;
+ struct usb_port_status port;
+};
+
+enum fhci_gpios {
+ GPIO_USBOE = 0,
+ GPIO_USBTP,
+ GPIO_USBTN,
+ GPIO_USBRP,
+ GPIO_USBRN,
+ /* these are optional */
+ GPIO_SPEED,
+ GPIO_POWER,
+ NUM_GPIOS,
+};
+
+enum fhci_pins {
+ PIN_USBOE = 0,
+ PIN_USBTP,
+ PIN_USBTN,
+ NUM_PINS,
+};
+
+struct fhci_hcd {
+ enum qe_clock fullspeed_clk;
+ enum qe_clock lowspeed_clk;
+ struct qe_pin *pins[NUM_PINS];
+ int gpios[NUM_GPIOS];
+ bool alow_gpios[NUM_GPIOS];
+
+ struct qe_usb_ctlr __iomem *regs; /* I/O memory used to communicate */
+ struct fhci_pram __iomem *pram; /* Parameter RAM */
+ struct gtm_timer *timer;
+
+ spinlock_t lock;
+ struct fhci_usb *usb_lld; /* Low-level driver */
+ struct virtual_root_hub *vroot_hub; /* the virtual root hub */
+ int active_urbs;
+ struct fhci_controller_list *hc_list;
+ struct tasklet_struct *process_done_task; /* tasklet for done list */
+
+ struct list_head empty_eds;
+ struct list_head empty_tds;
+
+#ifdef CONFIG_FHCI_DEBUG
+ int usb_irq_stat[13];
+ struct dentry *dfs_root;
+ struct dentry *dfs_regs;
+ struct dentry *dfs_irq_stat;
+#endif
+};
+
+#define USB_FRAME_USAGE 90
+#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10) /* frame time usage */
+#define SW_FIX_TIME_BETWEEN_TRANSACTION 150 /* SW */
+#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15)
+#define MAX_PERIODIC_FRAME_USAGE 90
+
+/* transaction type */
+enum fhci_ta_type {
+ FHCI_TA_IN = 0, /* input transaction */
+ FHCI_TA_OUT, /* output transaction */
+ FHCI_TA_SETUP, /* setup transaction */
+};
+
+/* transfer mode */
+enum fhci_tf_mode {
+ FHCI_TF_CTRL = 0,
+ FHCI_TF_ISO,
+ FHCI_TF_BULK,
+ FHCI_TF_INTR,
+};
+
+enum fhci_speed {
+ FHCI_FULL_SPEED,
+ FHCI_LOW_SPEED,
+};
+
+/* endpoint state */
+enum fhci_ed_state {
+ FHCI_ED_NEW = 0, /* pipe is new */
+ FHCI_ED_OPER, /* pipe is operating */
+ FHCI_ED_URB_DEL, /* pipe is in hold because urb is being deleted */
+ FHCI_ED_SKIP, /* skip this pipe */
+ FHCI_ED_HALTED, /* pipe is halted */
+};
+
+enum fhci_port_status {
+ FHCI_PORT_POWER_OFF = 0,
+ FHCI_PORT_DISABLED,
+ FHCI_PORT_DISCONNECTING,
+ FHCI_PORT_WAITING, /* waiting for connection */
+ FHCI_PORT_FULL, /* full speed connected */
+ FHCI_PORT_LOW, /* low speed connected */
+};
+
+enum fhci_mem_alloc {
+ MEM_CACHABLE_SYS = 0x00000001, /* primary DDR,cachable */
+ MEM_NOCACHE_SYS = 0x00000004, /* primary DDR,non-cachable */
+ MEM_SECONDARY = 0x00000002, /* either secondary DDR or SDRAM */
+ MEM_PRAM = 0x00000008, /* multi-user RAM identifier */
+};
+
+/* USB default parameters*/
+#define DEFAULT_RING_LEN 8
+#define DEFAULT_DATA_MEM MEM_CACHABLE_SYS
+
+struct ed {
+ u8 dev_addr; /* device address */
+ u8 ep_addr; /* endpoint address */
+ enum fhci_tf_mode mode; /* USB transfer mode */
+ enum fhci_speed speed;
+ unsigned int max_pkt_size;
+ enum fhci_ed_state state;
+ struct list_head td_list; /* a list of all queued TD to this pipe */
+ struct list_head node;
+
+ /* read only parameters, should be cleared upon initialization */
+ u8 toggle_carry; /* toggle carry from the last TD submitted */
+ u16 next_iso; /* time stamp of next queued ISO transfer */
+ struct td *td_head; /* a pointer to the current TD handled */
+};
+
+struct td {
+ void *data; /* a pointer to the data buffer */
+ unsigned int len; /* length of the data to be submitted */
+ unsigned int actual_len; /* actual bytes transferred on this td */
+ enum fhci_ta_type type; /* transaction type */
+ u8 toggle; /* toggle for next trans. within this TD */
+ u16 iso_index; /* ISO transaction index */
+ u16 start_frame; /* start frame time stamp */
+ u16 interval; /* interval between trans. (for ISO/Intr) */
+ u32 status; /* status of the TD */
+ struct ed *ed; /* a handle to the corresponding ED */
+ struct urb *urb; /* a handle to the corresponding URB */
+ bool ioc; /* Inform On Completion */
+ struct list_head node;
+
+ /* read only parameters should be cleared upon initialization */
+ struct packet *pkt;
+ int nak_cnt;
+ int error_cnt;
+ struct list_head frame_lh;
+};
+
+struct packet {
+ u8 *data; /* packet data */
+ u32 len; /* packet length */
+ u32 status; /* status of the packet - equivalent to the status
+ * field for the corresponding structure td */
+ u32 info; /* packet information */
+ void __iomem *priv_data; /* private data of the driver (TDs or BDs) */
+};
+
+/* struct for each URB */
+#define URB_INPROGRESS 0
+#define URB_DEL 1
+
+/* URB states (state field) */
+#define US_BULK 0
+#define US_BULK0 1
+
+/* three setup states */
+#define US_CTRL_SETUP 2
+#define US_CTRL_DATA 1
+#define US_CTRL_ACK 0
+
+#define EP_ZERO 0
+
+struct urb_priv {
+ int num_of_tds;
+ int tds_cnt;
+ int state;
+
+ struct td **tds;
+ struct ed *ed;
+ struct timer_list time_out;
+};
+
+struct endpoint {
+ /* Pointer to ep parameter RAM */
+ struct fhci_ep_pram __iomem *ep_pram_ptr;
+
+ /* Host transactions */
+ struct usb_td __iomem *td_base; /* first TD in the ring */
+ struct usb_td __iomem *conf_td; /* next TD for confirm after transac */
+ struct usb_td __iomem *empty_td;/* next TD for new transaction req. */
+ struct kfifo empty_frame_Q; /* Empty frames list to use */
+ struct kfifo conf_frame_Q; /* frames passed to TDs,waiting for tx */
+ struct kfifo dummy_packets_Q;/* dummy packets for the CRC overun */
+
+ bool already_pushed_dummy_bd;
+};
+
+/* struct for each 1mSec frame time */
+#define FRAME_IS_TRANSMITTED 0x00
+#define FRAME_TIMER_END_TRANSMISSION 0x01
+#define FRAME_DATA_END_TRANSMISSION 0x02
+#define FRAME_END_TRANSMISSION 0x03
+#define FRAME_IS_PREPARED 0x04
+
+struct fhci_time_frame {
+ u16 frame_num; /* frame number */
+ u16 total_bytes; /* total bytes submitted within this frame */
+ u8 frame_status; /* flag that indicates to stop fill this frame */
+ struct list_head tds_list; /* all tds of this frame */
+};
+
+/* internal driver structure*/
+struct fhci_usb {
+ u16 saved_msk; /* saving of the USB mask register */
+ struct endpoint *ep0; /* pointer for endpoint0 structure */
+ int intr_nesting_cnt; /* interrupt nesting counter */
+ u16 max_frame_usage; /* max frame time usage,in micro-sec */
+ u16 max_bytes_per_frame; /* max byte can be tx in one time frame */
+ u32 sw_transaction_time; /* sw complete trans time,in micro-sec */
+ struct fhci_time_frame *actual_frame;
+ struct fhci_controller_list *hc_list; /* main structure for hc */
+ struct virtual_root_hub *vroot_hub;
+ enum fhci_port_status port_status; /* v_rh port status */
+
+ u32 (*transfer_confirm)(struct fhci_hcd *fhci);
+
+ struct fhci_hcd *fhci;
+};
+
+/*
+ * Various helpers and prototypes below.
+ */
+
+static inline u16 get_frame_num(struct fhci_hcd *fhci)
+{
+ return in_be16(&fhci->pram->frame_num) & 0x07ff;
+}
+
+#define fhci_dbg(fhci, fmt, args...) \
+ dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_vdbg(fhci, fmt, args...) \
+ dev_vdbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_err(fhci, fmt, args...) \
+ dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_info(fhci, fmt, args...) \
+ dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_warn(fhci, fmt, args...) \
+ dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+
+static inline struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd)
+{
+ return (struct fhci_hcd *)hcd->hcd_priv;
+}
+
+static inline struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci)
+{
+ return container_of((void *)fhci, struct usb_hcd, hcd_priv);
+}
+
+/* fifo of pointers */
+static inline int cq_new(struct kfifo *fifo, int size)
+{
+ return kfifo_alloc(fifo, size * sizeof(void *), GFP_KERNEL);
+}
+
+static inline void cq_delete(struct kfifo *kfifo)
+{
+ kfifo_free(kfifo);
+}
+
+static inline unsigned int cq_howmany(struct kfifo *kfifo)
+{
+ return kfifo_len(kfifo) / sizeof(void *);
+}
+
+static inline int cq_put(struct kfifo *kfifo, void *p)
+{
+ return kfifo_in(kfifo, (void *)&p, sizeof(p));
+}
+
+static inline void *cq_get(struct kfifo *kfifo)
+{
+ unsigned int sz;
+ void *p;
+
+ sz = kfifo_out(kfifo, (void *)&p, sizeof(p));
+ if (sz != sizeof(p))
+ return NULL;
+
+ return p;
+}
+
+/* fhci-hcd.c */
+void fhci_start_sof_timer(struct fhci_hcd *fhci);
+void fhci_stop_sof_timer(struct fhci_hcd *fhci);
+u16 fhci_get_sof_timer_count(struct fhci_usb *usb);
+void fhci_usb_enable_interrupt(struct fhci_usb *usb);
+void fhci_usb_disable_interrupt(struct fhci_usb *usb);
+int fhci_ioports_check_bus_state(struct fhci_hcd *fhci);
+
+/* fhci-mem.c */
+void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td);
+void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed);
+struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci);
+struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb,
+ struct urb_priv *urb_priv, struct ed *ed, u16 index,
+ enum fhci_ta_type type, int toggle, u8 *data, u32 len,
+ u16 interval, u16 start_frame, bool ioc);
+void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number);
+
+/* fhci-hub.c */
+void fhci_config_transceiver(struct fhci_hcd *fhci,
+ enum fhci_port_status status);
+void fhci_port_disable(struct fhci_hcd *fhci);
+void fhci_port_enable(void *lld);
+void fhci_io_port_generate_reset(struct fhci_hcd *fhci);
+void fhci_port_reset(void *lld);
+int fhci_hub_status_data(struct usb_hcd *hcd, char *buf);
+int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength);
+
+/* fhci-tds.c */
+void fhci_flush_bds(struct fhci_usb *usb);
+void fhci_flush_actual_frame(struct fhci_usb *usb);
+u32 fhci_host_transaction(struct fhci_usb *usb, struct packet *pkt,
+ enum fhci_ta_type trans_type, u8 dest_addr,
+ u8 dest_ep, enum fhci_tf_mode trans_mode,
+ enum fhci_speed dest_speed, u8 data_toggle);
+void fhci_host_transmit_actual_frame(struct fhci_usb *usb);
+void fhci_tx_conf_interrupt(struct fhci_usb *usb);
+void fhci_push_dummy_bd(struct endpoint *ep);
+u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+ u32 ring_len);
+void fhci_init_ep_registers(struct fhci_usb *usb,
+ struct endpoint *ep,
+ enum fhci_mem_alloc data_mem);
+void fhci_ep0_free(struct fhci_usb *usb);
+
+/* fhci-sched.c */
+extern struct tasklet_struct fhci_tasklet;
+void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt);
+void fhci_flush_all_transmissions(struct fhci_usb *usb);
+void fhci_schedule_transactions(struct fhci_usb *usb);
+void fhci_device_connected_interrupt(struct fhci_hcd *fhci);
+void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci);
+void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb);
+u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci);
+irqreturn_t fhci_irq(struct usb_hcd *hcd);
+irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd);
+
+/* fhci-q.h */
+void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb);
+struct td *fhci_remove_td_from_ed(struct ed *ed);
+struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame);
+void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed);
+struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame);
+void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td);
+struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list);
+void fhci_done_td(struct urb *urb, struct td *td);
+void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed);
+
+#ifdef CONFIG_FHCI_DEBUG
+
+void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er);
+void fhci_dfs_destroy(struct fhci_hcd *fhci);
+void fhci_dfs_create(struct fhci_hcd *fhci);
+
+#else
+
+static inline void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {}
+static inline void fhci_dfs_destroy(struct fhci_hcd *fhci) {}
+static inline void fhci_dfs_create(struct fhci_hcd *fhci) {}
+
+#endif /* CONFIG_FHCI_DEBUG */
+
+#endif /* __FHCI_H */
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
new file mode 100644
index 000000000..000ed80ab
--- /dev/null
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -0,0 +1,5951 @@
+/*
+ * Faraday FOTG210 EHCI-like driver
+ *
+ * Copyright (c) 2013 Faraday Technology Corporation
+ *
+ * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+ * Feng-Hsin Chiang <john453@faraday-tech.com>
+ * Po-Yu Chuang <ratbert.chuang@gmail.com>
+ *
+ * Most of code borrowed from the Linux-3.7 EHCI driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+/*-------------------------------------------------------------------------*/
+#define DRIVER_AUTHOR "Yuan-Hsin Chen"
+#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver"
+
+static const char hcd_name[] = "fotg210_hcd";
+
+#undef FOTG210_URB_TRACE
+
+#define FOTG210_STATS
+
+/* magic numbers that can affect system performance */
+#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
+#define FOTG210_TUNE_RL_TT 0
+#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define FOTG210_TUNE_MULT_TT 1
+/*
+ * Some drivers think it's safe to schedule isochronous transfers more than
+ * 256 ms into the future (partly as a result of an old bug in the scheduling
+ * code). In an attempt to avoid trouble, we will use a minimum scheduling
+ * length of 512 frames instead of 256.
+ */
+#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */
+
+/* Initial IRQ latency: faster than hw default */
+static int log2_irq_thresh; /* 0 to 6 */
+module_param(log2_irq_thresh, int, S_IRUGO);
+MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* initial park setting: slower than hw default */
+static unsigned park;
+module_param(park, uint, S_IRUGO);
+MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets");
+
+/* for link power management(LPM) feature */
+static unsigned int hird;
+module_param(hird, int, S_IRUGO);
+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+#include "fotg210.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define fotg210_dbg(fotg210, fmt, args...) \
+ dev_dbg(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args)
+#define fotg210_err(fotg210, fmt, args...) \
+ dev_err(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args)
+#define fotg210_info(fotg210, fmt, args...) \
+ dev_info(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args)
+#define fotg210_warn(fotg210, fmt, args...) \
+ dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args)
+
+/* check the values in the HCSPARAMS register
+ * (host controller _Structural_ parameters)
+ * see EHCI spec, Table 2-4 for each value
+ */
+static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label)
+{
+ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params);
+
+ fotg210_dbg(fotg210,
+ "%s hcs_params 0x%x ports=%d\n",
+ label, params,
+ HCS_N_PORTS(params)
+ );
+}
+
+/* check the values in the HCCPARAMS register
+ * (host controller _Capability_ parameters)
+ * see EHCI Spec, Table 2-5 for each value
+ * */
+static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label)
+{
+ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
+
+ fotg210_dbg(fotg210,
+ "%s hcc_params %04x uframes %s%s\n",
+ label,
+ params,
+ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+ HCC_CANPARK(params) ? " park" : "");
+}
+
+static void __maybe_unused
+dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd)
+{
+ fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ hc32_to_cpup(fotg210, &qtd->hw_next),
+ hc32_to_cpup(fotg210, &qtd->hw_alt_next),
+ hc32_to_cpup(fotg210, &qtd->hw_token),
+ hc32_to_cpup(fotg210, &qtd->hw_buf[0]));
+ if (qtd->hw_buf[1])
+ fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ hc32_to_cpup(fotg210, &qtd->hw_buf[1]),
+ hc32_to_cpup(fotg210, &qtd->hw_buf[2]),
+ hc32_to_cpup(fotg210, &qtd->hw_buf[3]),
+ hc32_to_cpup(fotg210, &qtd->hw_buf[4]));
+}
+
+static void __maybe_unused
+dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ struct fotg210_qh_hw *hw = qh->hw;
+
+ fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
+ dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next);
+}
+
+static void __maybe_unused
+dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd)
+{
+ fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n",
+ label, itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next),
+ itd->urb);
+ fotg210_dbg(fotg210,
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(fotg210, itd->hw_transaction[0]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[1]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[2]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[3]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[4]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[5]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[6]),
+ hc32_to_cpu(fotg210, itd->hw_transaction[7]));
+ fotg210_dbg(fotg210,
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(fotg210, itd->hw_bufp[0]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[1]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[2]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[3]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[4]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[5]),
+ hc32_to_cpu(fotg210, itd->hw_bufp[6]));
+ fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n",
+ itd->index[0], itd->index[1], itd->index[2],
+ itd->index[3], itd->index[4], itd->index[5],
+ itd->index[6], itd->index[7]);
+}
+
+static int __maybe_unused
+dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
+{
+ return scnprintf(buf, len,
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ label, label[0] ? " " : "", status,
+ (status & STS_ASS) ? " Async" : "",
+ (status & STS_PSS) ? " Periodic" : "",
+ (status & STS_RECL) ? " Recl" : "",
+ (status & STS_HALT) ? " Halt" : "",
+ (status & STS_IAA) ? " IAA" : "",
+ (status & STS_FATAL) ? " FATAL" : "",
+ (status & STS_FLR) ? " FLR" : "",
+ (status & STS_PCD) ? " PCD" : "",
+ (status & STS_ERR) ? " ERR" : "",
+ (status & STS_INT) ? " INT" : ""
+ );
+}
+
+static int __maybe_unused
+dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
+{
+ return scnprintf(buf, len,
+ "%s%sintrenable %02x%s%s%s%s%s%s",
+ label, label[0] ? " " : "", enable,
+ (enable & STS_IAA) ? " IAA" : "",
+ (enable & STS_FATAL) ? " FATAL" : "",
+ (enable & STS_FLR) ? " FLR" : "",
+ (enable & STS_PCD) ? " PCD" : "",
+ (enable & STS_ERR) ? " ERR" : "",
+ (enable & STS_INT) ? " INT" : ""
+ );
+}
+
+static const char *const fls_strings[] = { "1024", "512", "256", "??" };
+
+static int
+dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
+{
+ return scnprintf(buf, len,
+ "%s%scommand %07x %s=%d ithresh=%d%s%s%s "
+ "period=%s%s %s",
+ label, label[0] ? " " : "", command,
+ (command & CMD_PARK) ? " park" : "(park)",
+ CMD_PARK_CNT(command),
+ (command >> 16) & 0x3f,
+ (command & CMD_IAAD) ? " IAAD" : "",
+ (command & CMD_ASE) ? " Async" : "",
+ (command & CMD_PSE) ? " Periodic" : "",
+ fls_strings[(command >> 2) & 0x3],
+ (command & CMD_RESET) ? " Reset" : "",
+ (command & CMD_RUN) ? "RUN" : "HALT"
+ );
+}
+
+static char
+*dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
+{
+ char *sig;
+
+ /* signaling state */
+ switch (status & (3 << 10)) {
+ case 0 << 10:
+ sig = "se0";
+ break;
+ case 1 << 10:
+ sig = "k";
+ break; /* low speed */
+ case 2 << 10:
+ sig = "j";
+ break;
+ default:
+ sig = "?";
+ break;
+ }
+
+ scnprintf(buf, len,
+ "%s%sport:%d status %06x %d "
+ "sig=%s%s%s%s%s%s%s%s",
+ label, label[0] ? " " : "", port, status,
+ status>>25,/*device address */
+ sig,
+ (status & PORT_RESET) ? " RESET" : "",
+ (status & PORT_SUSPEND) ? " SUSPEND" : "",
+ (status & PORT_RESUME) ? " RESUME" : "",
+ (status & PORT_PEC) ? " PEC" : "",
+ (status & PORT_PE) ? " PE" : "",
+ (status & PORT_CSC) ? " CSC" : "",
+ (status & PORT_CONNECT) ? " CONNECT" : "");
+ return buf;
+}
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(fotg210, label, status) { \
+ char _buf[80]; \
+ dbg_status_buf(_buf, sizeof(_buf), label, status); \
+ fotg210_dbg(fotg210, "%s\n", _buf); \
+}
+
+#define dbg_cmd(fotg210, label, command) { \
+ char _buf[80]; \
+ dbg_command_buf(_buf, sizeof(_buf), label, command); \
+ fotg210_dbg(fotg210, "%s\n", _buf); \
+}
+
+#define dbg_port(fotg210, label, port, status) { \
+ char _buf[80]; \
+ fotg210_dbg(fotg210, "%s\n", dbg_port_buf(_buf, sizeof(_buf), label, port, status) ); \
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* troubleshooting help: expose state in debugfs */
+
+static int debug_async_open(struct inode *, struct file *);
+static int debug_periodic_open(struct inode *, struct file *);
+static int debug_registers_open(struct inode *, struct file *);
+static int debug_async_open(struct inode *, struct file *);
+
+static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
+static int debug_close(struct inode *, struct file *);
+
+static const struct file_operations debug_async_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_async_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_periodic_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_periodic_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_registers_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_registers_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+
+static struct dentry *fotg210_debug_root;
+
+struct debug_buffer {
+ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
+ struct usb_bus *bus;
+ struct mutex mutex; /* protect filling of buffer */
+ size_t count; /* number of characters filled into buffer */
+ char *output_buf;
+ size_t alloc_size;
+};
+
+#define speed_char(info1)({ char tmp; \
+ switch (info1 & (3 << 12)) { \
+ case QH_FULL_SPEED: \
+ tmp = 'f'; break; \
+ case QH_LOW_SPEED: \
+ tmp = 'l'; break; \
+ case QH_HIGH_SPEED: \
+ tmp = 'h'; break; \
+ default: \
+ tmp = '?'; break; \
+ } tmp; })
+
+static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token)
+{
+ __u32 v = hc32_to_cpu(fotg210, token);
+
+ if (v & QTD_STS_ACTIVE)
+ return '*';
+ if (v & QTD_STS_HALT)
+ return '-';
+ if (!IS_SHORT_READ(v))
+ return ' ';
+ /* tries to advance through hw_alt_next */
+ return '/';
+}
+
+static void qh_lines(
+ struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh,
+ char **nextp,
+ unsigned *sizep
+)
+{
+ u32 scratch;
+ u32 hw_curr;
+ struct fotg210_qtd *td;
+ unsigned temp;
+ unsigned size = *sizep;
+ char *next = *nextp;
+ char mark;
+ __le32 list_end = FOTG210_LIST_END(fotg210);
+ struct fotg210_qh_hw *hw = qh->hw;
+
+ if (hw->hw_qtd_next == list_end) /* NEC does this */
+ mark = '@';
+ else
+ mark = token_mark(fotg210, hw->hw_token);
+ if (mark == '/') { /* qh_alt_next controls qh advance? */
+ if ((hw->hw_alt_next & QTD_MASK(fotg210))
+ == fotg210->async->hw->hw_alt_next)
+ mark = '#'; /* blocked */
+ else if (hw->hw_alt_next == list_end)
+ mark = '.'; /* use hw_qtd_next */
+ /* else alt_next points to some other qtd */
+ }
+ scratch = hc32_to_cpup(fotg210, &hw->hw_info1);
+ hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0;
+ temp = scnprintf(next, size,
+ "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)",
+ qh, scratch & 0x007f,
+ speed_char(scratch),
+ (scratch >> 8) & 0x000f,
+ scratch, hc32_to_cpup(fotg210, &hw->hw_info2),
+ hc32_to_cpup(fotg210, &hw->hw_token), mark,
+ (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token)
+ ? "data1" : "data0",
+ (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f);
+ size -= temp;
+ next += temp;
+
+ /* hc may be modifying the list as we read it ... */
+ list_for_each_entry(td, &qh->qtd_list, qtd_list) {
+ scratch = hc32_to_cpup(fotg210, &td->hw_token);
+ mark = ' ';
+ if (hw_curr == td->qtd_dma)
+ mark = '*';
+ else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma))
+ mark = '+';
+ else if (QTD_LENGTH(scratch)) {
+ if (td->hw_alt_next == fotg210->async->hw->hw_alt_next)
+ mark = '#';
+ else if (td->hw_alt_next != list_end)
+ mark = '/';
+ }
+ temp = snprintf(next, size,
+ "\n\t%p%c%s len=%d %08x urb %p",
+ td, mark, ({ char *tmp;
+ switch ((scratch>>8)&0x03) {
+ case 0:
+ tmp = "out";
+ break;
+ case 1:
+ tmp = "in";
+ break;
+ case 2:
+ tmp = "setup";
+ break;
+ default:
+ tmp = "?";
+ break;
+ } tmp; }),
+ (scratch >> 16) & 0x7fff,
+ scratch,
+ td->urb);
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+ if (temp == size)
+ goto done;
+ }
+
+ temp = snprintf(next, size, "\n");
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+
+done:
+ *sizep = size;
+ *nextp = next;
+}
+
+static ssize_t fill_async_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fotg210_hcd *fotg210;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ struct fotg210_qh *qh;
+
+ hcd = bus_to_hcd(buf->bus);
+ fotg210 = hcd_to_fotg210(hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ *next = 0;
+
+ /* dumps a snapshot of the async schedule.
+ * usually empty except for long-term bulk reads, or head.
+ * one QH per line, and TDs we know about
+ */
+ spin_lock_irqsave(&fotg210->lock, flags);
+ for (qh = fotg210->async->qh_next.qh; size > 0 && qh;
+ qh = qh->qh_next.qh)
+ qh_lines(fotg210, qh, &next, &size);
+ if (fotg210->async_unlink && size > 0) {
+ temp = scnprintf(next, size, "\nunlink =\n");
+ size -= temp;
+ next += temp;
+
+ for (qh = fotg210->async_unlink; size > 0 && qh;
+ qh = qh->unlink_next)
+ qh_lines(fotg210, qh, &next, &size);
+ }
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+
+ return strlen(buf->output_buf);
+}
+
+#define DBG_SCHED_LIMIT 64
+static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fotg210_hcd *fotg210;
+ unsigned long flags;
+ union fotg210_shadow p, *seen;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+ __hc32 tag;
+
+ seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC);
+ if (!seen)
+ return 0;
+ seen_count = 0;
+
+ hcd = bus_to_hcd(buf->bus);
+ fotg210 = hcd_to_fotg210(hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule.
+ * iso changes, interrupt usually doesn't.
+ */
+ spin_lock_irqsave(&fotg210->lock, flags);
+ for (i = 0; i < fotg210->periodic_size; i++) {
+ p = fotg210->pshadow[i];
+ if (likely(!p.ptr))
+ continue;
+ tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]);
+
+ temp = scnprintf(next, size, "%4d: ", i);
+ size -= temp;
+ next += temp;
+
+ do {
+ struct fotg210_qh_hw *hw;
+
+ switch (hc32_to_cpu(fotg210, tag)) {
+ case Q_TYPE_QH:
+ hw = p.qh->hw;
+ temp = scnprintf(next, size, " qh%d-%04x/%p",
+ p.qh->period,
+ hc32_to_cpup(fotg210,
+ &hw->hw_info2)
+ /* uframe masks */
+ & (QH_CMASK | QH_SMASK),
+ p.qh);
+ size -= temp;
+ next += temp;
+ /* don't repeat what follows this qh */
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen[temp].ptr != p.ptr)
+ continue;
+ if (p.qh->qh_next.ptr) {
+ temp = scnprintf(next, size,
+ " ...");
+ size -= temp;
+ next += temp;
+ }
+ break;
+ }
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 scratch = hc32_to_cpup(fotg210,
+ &hw->hw_info1);
+ struct fotg210_qtd *qtd;
+ char *type = "";
+
+ /* count tds, get ep direction */
+ temp = 0;
+ list_for_each_entry(qtd,
+ &p.qh->qtd_list,
+ qtd_list) {
+ temp++;
+ switch (0x03 & (hc32_to_cpu(
+ fotg210,
+ qtd->hw_token) >> 8)) {
+ case 0:
+ type = "out";
+ continue;
+ case 1:
+ type = "in";
+ continue;
+ }
+ }
+
+ temp = scnprintf(next, size,
+ "(%c%d ep%d%s "
+ "[%d/%d] q%d p%d)",
+ speed_char(scratch),
+ scratch & 0x007f,
+ (scratch >> 8) & 0x000f, type,
+ p.qh->usecs, p.qh->c_usecs,
+ temp,
+ 0x7ff & (scratch >> 16));
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen[seen_count++].qh = p.qh;
+ } else
+ temp = 0;
+ tag = Q_NEXT_TYPE(fotg210, hw->hw_next);
+ p = p.qh->qh_next;
+ break;
+ case Q_TYPE_FSTN:
+ temp = scnprintf(next, size,
+ " fstn-%8x/%p", p.fstn->hw_prev,
+ p.fstn);
+ tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next);
+ p = p.fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ temp = scnprintf(next, size,
+ " itd/%p", p.itd);
+ tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next);
+ p = p.itd->itd_next;
+ break;
+ }
+ size -= temp;
+ next += temp;
+ } while (p.ptr);
+
+ temp = scnprintf(next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ kfree(seen);
+
+ return buf->alloc_size - size;
+}
+#undef DBG_SCHED_LIMIT
+
+static const char *rh_state_string(struct fotg210_hcd *fotg210)
+{
+ switch (fotg210->rh_state) {
+ case FOTG210_RH_HALTED:
+ return "halted";
+ case FOTG210_RH_SUSPENDED:
+ return "suspended";
+ case FOTG210_RH_RUNNING:
+ return "running";
+ case FOTG210_RH_STOPPING:
+ return "stopping";
+ }
+ return "?";
+}
+
+static ssize_t fill_registers_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fotg210_hcd *fotg210;
+ unsigned long flags;
+ unsigned temp, size, i;
+ char *next, scratch[80];
+ static const char fmt[] = "%*s\n";
+ static const char label[] = "";
+
+ hcd = bus_to_hcd(buf->bus);
+ fotg210 = hcd_to_fotg210(hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ size = scnprintf(next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "SUSPENDED(no register access)\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc);
+ goto done;
+ }
+
+ /* Capability Registers */
+ i = HC_VERSION(fotg210, fotg210_readl(fotg210,
+ &fotg210->caps->hc_capbase));
+ temp = scnprintf(next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "EHCI %x.%02x, rh state %s\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc,
+ i >> 8, i & 0x0ff, rh_state_string(fotg210));
+ size -= temp;
+ next += temp;
+
+ /* FIXME interpret both types of params */
+ i = fotg210_readl(fotg210, &fotg210->caps->hcs_params);
+ temp = scnprintf(next, size, "structural params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ i = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
+ temp = scnprintf(next, size, "capability params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ /* Operational Registers */
+ temp = dbg_status_buf(scratch, sizeof(scratch), label,
+ fotg210_readl(fotg210, &fotg210->regs->status));
+ temp = scnprintf(next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_command_buf(scratch, sizeof(scratch), label,
+ fotg210_readl(fotg210, &fotg210->regs->command));
+ temp = scnprintf(next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_intr_buf(scratch, sizeof(scratch), label,
+ fotg210_readl(fotg210, &fotg210->regs->intr_enable));
+ temp = scnprintf(next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf(next, size, "uframe %04x\n",
+ fotg210_read_frame_index(fotg210));
+ size -= temp;
+ next += temp;
+
+ if (fotg210->async_unlink) {
+ temp = scnprintf(next, size, "async unlink qh %p\n",
+ fotg210->async_unlink);
+ size -= temp;
+ next += temp;
+ }
+
+#ifdef FOTG210_STATS
+ temp = scnprintf(next, size,
+ "irq normal %ld err %ld iaa %ld(lost %ld)\n",
+ fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa,
+ fotg210->stats.lost_iaa);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf(next, size, "complete %ld unlink %ld\n",
+ fotg210->stats.complete, fotg210->stats.unlink);
+ size -= temp;
+ next += temp;
+#endif
+
+done:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+
+ return buf->alloc_size - size;
+}
+
+static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
+ ssize_t (*fill_func)(struct debug_buffer *))
+{
+ struct debug_buffer *buf;
+
+ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+
+ if (buf) {
+ buf->bus = bus;
+ buf->fill_func = fill_func;
+ mutex_init(&buf->mutex);
+ buf->alloc_size = PAGE_SIZE;
+ }
+
+ return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+ int ret = 0;
+
+ if (!buf->output_buf)
+ buf->output_buf = vmalloc(buf->alloc_size);
+
+ if (!buf->output_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = buf->fill_func(buf);
+
+ if (ret >= 0) {
+ buf->count = ret;
+ ret = 0;
+ }
+
+out:
+ return ret;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+ size_t len, loff_t *offset)
+{
+ struct debug_buffer *buf = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&buf->mutex);
+ if (buf->count == 0) {
+ ret = fill_buffer(buf);
+ if (ret != 0) {
+ mutex_unlock(&buf->mutex);
+ goto out;
+ }
+ }
+ mutex_unlock(&buf->mutex);
+
+ ret = simple_read_from_buffer(user_buf, len, offset,
+ buf->output_buf, buf->count);
+
+out:
+ return ret;
+
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf = file->private_data;
+
+ if (buf) {
+ vfree(buf->output_buf);
+ kfree(buf);
+ }
+
+ return 0;
+}
+static int debug_async_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_periodic_open(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf;
+ buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
+ file->private_data = buf;
+ return 0;
+}
+
+static int debug_registers_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_registers_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static inline void create_debug_files(struct fotg210_hcd *fotg210)
+{
+ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self;
+
+ fotg210->debug_dir = debugfs_create_dir(bus->bus_name,
+ fotg210_debug_root);
+ if (!fotg210->debug_dir)
+ return;
+
+ if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus,
+ &debug_async_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus,
+ &debug_periodic_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus,
+ &debug_registers_fops))
+ goto file_error;
+
+ return;
+
+file_error:
+ debugfs_remove_recursive(fotg210->debug_dir);
+}
+
+static inline void remove_debug_files(struct fotg210_hcd *fotg210)
+{
+ debugfs_remove_recursive(fotg210->debug_dir);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown: shutting down the bridge before the devices using it.
+ */
+static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr,
+ u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = fotg210_readl(fotg210, 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 to halt state from unknown (EHCI spec section 2.3).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int fotg210_halt(struct fotg210_hcd *fotg210)
+{
+ u32 temp;
+
+ spin_lock_irq(&fotg210->lock);
+
+ /* disable any irqs left enabled by previous code */
+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable);
+
+ /*
+ * This routine gets called during probe before fotg210->command
+ * has been initialized, so we can't rely on its value.
+ */
+ fotg210->command &= ~CMD_RUN;
+ temp = fotg210_readl(fotg210, &fotg210->regs->command);
+ temp &= ~(CMD_RUN | CMD_IAAD);
+ fotg210_writel(fotg210, temp, &fotg210->regs->command);
+
+ spin_unlock_irq(&fotg210->lock);
+ synchronize_irq(fotg210_to_hcd(fotg210)->irq);
+
+ return handshake(fotg210, &fotg210->regs->status,
+ STS_HALT, STS_HALT, 16 * 125);
+}
+
+/*
+ * Reset a non-running (STS_HALT == 1) controller.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int fotg210_reset(struct fotg210_hcd *fotg210)
+{
+ int retval;
+ u32 command = fotg210_readl(fotg210, &fotg210->regs->command);
+
+ /* If the EHCI debug controller is active, special care must be
+ * taken before and after a host controller reset */
+ if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210)))
+ fotg210->debug = NULL;
+
+ command |= CMD_RESET;
+ dbg_cmd(fotg210, "reset", command);
+ fotg210_writel(fotg210, command, &fotg210->regs->command);
+ fotg210->rh_state = FOTG210_RH_HALTED;
+ fotg210->next_statechange = jiffies;
+ retval = handshake(fotg210, &fotg210->regs->command,
+ CMD_RESET, 0, 250 * 1000);
+
+ if (retval)
+ return retval;
+
+ if (fotg210->debug)
+ dbgp_external_startup(fotg210_to_hcd(fotg210));
+
+ fotg210->port_c_suspend = fotg210->suspended_ports =
+ fotg210->resuming_ports = 0;
+ return retval;
+}
+
+/*
+ * Idle the controller (turn off the schedules).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void fotg210_quiesce(struct fotg210_hcd *fotg210)
+{
+ u32 temp;
+
+ if (fotg210->rh_state != FOTG210_RH_RUNNING)
+ return;
+
+ /* wait for any schedule enables/disables to take effect */
+ temp = (fotg210->command << 10) & (STS_ASS | STS_PSS);
+ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp,
+ 16 * 125);
+
+ /* then disable anything that's still active */
+ spin_lock_irq(&fotg210->lock);
+ fotg210->command &= ~(CMD_ASE | CMD_PSE);
+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
+ spin_unlock_irq(&fotg210->lock);
+
+ /* hardware can take 16 microframes to turn off ... */
+ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0,
+ 16 * 125);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void end_unlink_async(struct fotg210_hcd *fotg210);
+static void unlink_empty_async(struct fotg210_hcd *fotg210);
+static void fotg210_work(struct fotg210_hcd *fotg210);
+static void start_unlink_intr(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh);
+static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);
+
+/*-------------------------------------------------------------------------*/
+
+/* Set a bit in the USBCMD register */
+static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit)
+{
+ fotg210->command |= bit;
+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
+
+ /* unblock posted write */
+ fotg210_readl(fotg210, &fotg210->regs->command);
+}
+
+/* Clear a bit in the USBCMD register */
+static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit)
+{
+ fotg210->command &= ~bit;
+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
+
+ /* unblock posted write */
+ fotg210_readl(fotg210, &fotg210->regs->command);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI timer support... Now using hrtimers.
+ *
+ * Lots of different events are triggered from fotg210->hrtimer. Whenever
+ * the timer routine runs, it checks each possible event; events that are
+ * currently enabled and whose expiration time has passed get handled.
+ * The set of enabled events is stored as a collection of bitflags in
+ * fotg210->enabled_hrtimer_events, and they are numbered in order of
+ * increasing delay values (ranging between 1 ms and 100 ms).
+ *
+ * Rather than implementing a sorted list or tree of all pending events,
+ * we keep track only of the lowest-numbered pending event, in
+ * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its
+ * expiration time is set to the timeout value for this event.
+ *
+ * As a result, events might not get handled right away; the actual delay
+ * could be anywhere up to twice the requested delay. This doesn't
+ * matter, because none of the events are especially time-critical. The
+ * ones that matter most all have a delay of 1 ms, so they will be
+ * handled after 2 ms at most, which is okay. In addition to this, we
+ * allow for an expiration range of 1 ms.
+ */
+
+/*
+ * Delay lengths for the hrtimer event types.
+ * Keep this list sorted by delay length, in the same order as
+ * the event types indexed by enum fotg210_hrtimer_event in fotg210.h.
+ */
+static unsigned event_delays_ns[] = {
+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */
+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */
+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */
+ 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */
+ 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */
+ 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */
+ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */
+ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */
+ 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */
+ 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */
+};
+
+/* Enable a pending hrtimer event */
+static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event,
+ bool resched)
+{
+ ktime_t *timeout = &fotg210->hr_timeouts[event];
+
+ if (resched)
+ *timeout = ktime_add(ktime_get(),
+ ktime_set(0, event_delays_ns[event]));
+ fotg210->enabled_hrtimer_events |= (1 << event);
+
+ /* Track only the lowest-numbered pending event */
+ if (event < fotg210->next_hrtimer_event) {
+ fotg210->next_hrtimer_event = event;
+ hrtimer_start_range_ns(&fotg210->hrtimer, *timeout,
+ NSEC_PER_MSEC, HRTIMER_MODE_ABS);
+ }
+}
+
+
+/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
+static void fotg210_poll_ASS(struct fotg210_hcd *fotg210)
+{
+ unsigned actual, want;
+
+ /* Don't enable anything if the controller isn't running (e.g., died) */
+ if (fotg210->rh_state != FOTG210_RH_RUNNING)
+ return;
+
+ want = (fotg210->command & CMD_ASE) ? STS_ASS : 0;
+ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 20 ms */
+ if (fotg210->ASS_poll_count++ < 20) {
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS,
+ true);
+ return;
+ }
+ fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ fotg210->ASS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (fotg210->async_count > 0)
+ fotg210_set_command_bit(fotg210, CMD_ASE);
+
+ } else { /* Running */
+ if (fotg210->async_count == 0) {
+
+ /* Turn off the schedule after a while */
+ fotg210_enable_event(fotg210,
+ FOTG210_HRTIMER_DISABLE_ASYNC,
+ true);
+ }
+ }
+}
+
+/* Turn off the async schedule after a brief delay */
+static void fotg210_disable_ASE(struct fotg210_hcd *fotg210)
+{
+ fotg210_clear_command_bit(fotg210, CMD_ASE);
+}
+
+
+/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
+static void fotg210_poll_PSS(struct fotg210_hcd *fotg210)
+{
+ unsigned actual, want;
+
+ /* Don't do anything if the controller isn't running (e.g., died) */
+ if (fotg210->rh_state != FOTG210_RH_RUNNING)
+ return;
+
+ want = (fotg210->command & CMD_PSE) ? STS_PSS : 0;
+ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 20 ms */
+ if (fotg210->PSS_poll_count++ < 20) {
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS,
+ true);
+ return;
+ }
+ fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ fotg210->PSS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (fotg210->periodic_count > 0)
+ fotg210_set_command_bit(fotg210, CMD_PSE);
+
+ } else { /* Running */
+ if (fotg210->periodic_count == 0) {
+
+ /* Turn off the schedule after a while */
+ fotg210_enable_event(fotg210,
+ FOTG210_HRTIMER_DISABLE_PERIODIC,
+ true);
+ }
+ }
+}
+
+/* Turn off the periodic schedule after a brief delay */
+static void fotg210_disable_PSE(struct fotg210_hcd *fotg210)
+{
+ fotg210_clear_command_bit(fotg210, CMD_PSE);
+}
+
+
+/* Poll the STS_HALT status bit; see when a dead controller stops */
+static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210)
+{
+ if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) {
+
+ /* Give up after a few milliseconds */
+ if (fotg210->died_poll_count++ < 5) {
+ /* Try again later */
+ fotg210_enable_event(fotg210,
+ FOTG210_HRTIMER_POLL_DEAD, true);
+ return;
+ }
+ fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n");
+ }
+
+ /* Clean up the mess */
+ fotg210->rh_state = FOTG210_RH_HALTED;
+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable);
+ fotg210_work(fotg210);
+ end_unlink_async(fotg210);
+
+ /* Not in process context, so don't try to reset the controller */
+}
+
+
+/* Handle unlinked interrupt QHs once they are gone from the hardware */
+static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210)
+{
+ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING);
+
+ /*
+ * Process all the QHs on the intr_unlink list that were added
+ * before the current unlink cycle began. The list is in
+ * temporal order, so stop when we reach the first entry in the
+ * current cycle. But if the root hub isn't running then
+ * process all the QHs on the list.
+ */
+ fotg210->intr_unlinking = true;
+ while (fotg210->intr_unlink) {
+ struct fotg210_qh *qh = fotg210->intr_unlink;
+
+ if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle)
+ break;
+ fotg210->intr_unlink = qh->unlink_next;
+ qh->unlink_next = NULL;
+ end_unlink_intr(fotg210, qh);
+ }
+
+ /* Handle remaining entries later */
+ if (fotg210->intr_unlink) {
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR,
+ true);
+ ++fotg210->intr_unlink_cycle;
+ }
+ fotg210->intr_unlinking = false;
+}
+
+
+/* Start another free-iTDs/siTDs cycle */
+static void start_free_itds(struct fotg210_hcd *fotg210)
+{
+ if (!(fotg210->enabled_hrtimer_events &
+ BIT(FOTG210_HRTIMER_FREE_ITDS))) {
+ fotg210->last_itd_to_free = list_entry(
+ fotg210->cached_itd_list.prev,
+ struct fotg210_itd, itd_list);
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true);
+ }
+}
+
+/* Wait for controller to stop using old iTDs and siTDs */
+static void end_free_itds(struct fotg210_hcd *fotg210)
+{
+ struct fotg210_itd *itd, *n;
+
+ if (fotg210->rh_state < FOTG210_RH_RUNNING)
+ fotg210->last_itd_to_free = NULL;
+
+ list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) {
+ list_del(&itd->itd_list);
+ dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma);
+ if (itd == fotg210->last_itd_to_free)
+ break;
+ }
+
+ if (!list_empty(&fotg210->cached_itd_list))
+ start_free_itds(fotg210);
+}
+
+
+/* Handle lost (or very late) IAA interrupts */
+static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210)
+{
+ if (fotg210->rh_state != FOTG210_RH_RUNNING)
+ return;
+
+ /*
+ * Lost IAA irqs wedge things badly; seen first with a vt8235.
+ * So we need this watchdog, but must protect it against both
+ * (a) SMP races against real IAA firing and retriggering, and
+ * (b) clean HC shutdown, when IAA watchdog was pending.
+ */
+ if (fotg210->async_iaa) {
+ u32 cmd, status;
+
+ /* If we get here, IAA is *REALLY* late. It's barely
+ * conceivable that the system is so busy that CMD_IAAD
+ * is still legitimately set, so let's be sure it's
+ * clear before we read STS_IAA. (The HC should clear
+ * CMD_IAAD when it sets STS_IAA.)
+ */
+ cmd = fotg210_readl(fotg210, &fotg210->regs->command);
+
+ /*
+ * If IAA is set here it either legitimately triggered
+ * after the watchdog timer expired (_way_ late, so we'll
+ * still count it as lost) ... or a silicon erratum:
+ * - VIA seems to set IAA without triggering the IRQ;
+ * - IAAD potentially cleared without setting IAA.
+ */
+ status = fotg210_readl(fotg210, &fotg210->regs->status);
+ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
+ COUNT(fotg210->stats.lost_iaa);
+ fotg210_writel(fotg210, STS_IAA,
+ &fotg210->regs->status);
+ }
+
+ fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n",
+ status, cmd);
+ end_unlink_async(fotg210);
+ }
+}
+
+
+/* Enable the I/O watchdog, if appropriate */
+static void turn_on_io_watchdog(struct fotg210_hcd *fotg210)
+{
+ /* Not needed if the controller isn't running or it's already enabled */
+ if (fotg210->rh_state != FOTG210_RH_RUNNING ||
+ (fotg210->enabled_hrtimer_events &
+ BIT(FOTG210_HRTIMER_IO_WATCHDOG)))
+ return;
+
+ /*
+ * Isochronous transfers always need the watchdog.
+ * For other sorts we use it only if the flag is set.
+ */
+ if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog &&
+ fotg210->async_count + fotg210->intr_count > 0))
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG,
+ true);
+}
+
+
+/*
+ * Handler functions for the hrtimer event types.
+ * Keep this array in the same order as the event types indexed by
+ * enum fotg210_hrtimer_event in fotg210.h.
+ */
+static void (*event_handlers[])(struct fotg210_hcd *) = {
+ fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */
+ fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */
+ fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */
+ fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */
+ end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */
+ unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */
+ fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */
+ fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */
+ fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */
+ fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */
+};
+
+static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t)
+{
+ struct fotg210_hcd *fotg210 =
+ container_of(t, struct fotg210_hcd, hrtimer);
+ ktime_t now;
+ unsigned long events;
+ unsigned long flags;
+ unsigned e;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ events = fotg210->enabled_hrtimer_events;
+ fotg210->enabled_hrtimer_events = 0;
+ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT;
+
+ /*
+ * Check each pending event. If its time has expired, handle
+ * the event; otherwise re-enable it.
+ */
+ now = ktime_get();
+ for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) {
+ if (now.tv64 >= fotg210->hr_timeouts[e].tv64)
+ event_handlers[e](fotg210);
+ else
+ fotg210_enable_event(fotg210, e, false);
+ }
+
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define fotg210_bus_suspend NULL
+#define fotg210_bus_resume NULL
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete(
+ struct fotg210_hcd *fotg210,
+ int index,
+ u32 __iomem *status_reg,
+ int port_status
+) {
+ if (!(port_status & PORT_CONNECT))
+ return port_status;
+
+ /* if reset finished and it's still not enabled -- handoff */
+ if (!(port_status & PORT_PE)) {
+ /* with integrated TT, there's nobody to hand it to! */
+ fotg210_dbg(fotg210,
+ "Failed to enable port %d on root hub TT\n",
+ index+1);
+ return port_status;
+ } else {
+ fotg210_dbg(fotg210, "port %d reset complete, port enabled\n",
+ index + 1);
+ }
+
+ return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+fotg210_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ u32 temp, status;
+ u32 mask;
+ int retval = 1;
+ unsigned long flags;
+
+ /* init status to no-changes */
+ buf[0] = 0;
+
+ /* Inform the core about resumes-in-progress by returning
+ * a non-zero value even if there are no status changes.
+ */
+ status = fotg210->resuming_ports;
+
+ mask = PORT_CSC | PORT_PEC;
+ /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ temp = fotg210_readl(fotg210, &fotg210->regs->port_status);
+
+ /*
+ * Return status information even for ports with OWNER set.
+ * Otherwise hub_wq wouldn't see the disconnect event when a
+ * high-speed device is switched over to the companion
+ * controller by the user.
+ */
+
+ if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend)
+ || (fotg210->reset_done[0] && time_after_eq(
+ jiffies, fotg210->reset_done[0]))) {
+ buf[0] |= 1 << 1;
+ status = STS_PCD;
+ }
+ /* FIXME autosuspend idle root hubs */
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+fotg210_hub_descriptor(
+ struct fotg210_hcd *fotg210,
+ struct usb_hub_descriptor *desc
+) {
+ int ports = HCS_N_PORTS(fotg210->hcs_params);
+ u16 temp;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
+
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int fotg210_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ int ports = HCS_N_PORTS(fotg210->hcs_params);
+ u32 __iomem *status_reg = &fotg210->regs->port_status;
+ u32 temp, temp1, status;
+ unsigned long flags;
+ int retval = 0;
+ unsigned selector;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = fotg210_readl(fotg210, status_reg);
+ temp &= ~PORT_RWC_BITS;
+
+ /*
+ * Even if OWNER is set, so the port is owned by the
+ * companion controller, hub_wq needs to be able to clear
+ * the port-change status bits (especially
+ * USB_PORT_STAT_C_CONNECTION).
+ */
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ fotg210_writel(fotg210, temp & ~PORT_PE, status_reg);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ fotg210_writel(fotg210, temp | PORT_PEC, status_reg);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (temp & PORT_RESET)
+ goto error;
+ if (!(temp & PORT_SUSPEND))
+ break;
+ if ((temp & PORT_PE) == 0)
+ goto error;
+
+ /* resume signaling for 20 msec */
+ fotg210_writel(fotg210, temp | PORT_RESUME, status_reg);
+ fotg210->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ clear_bit(wIndex, &fotg210->port_c_suspend);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ fotg210_writel(fotg210, temp | PORT_CSC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ fotg210_writel(fotg210, temp | OTGISR_OVC,
+ &fotg210->regs->otgisr);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ fotg210_readl(fotg210, &fotg210->regs->command);
+ break;
+ case GetHubDescriptor:
+ fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset(buf, 0, 4);
+ /*cpu_to_le32s ((u32 *) buf); */
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ status = 0;
+ temp = fotg210_readl(fotg210, status_reg);
+
+ /* wPortChange bits */
+ if (temp & PORT_CSC)
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
+ if (temp & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+
+ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr);
+ if (temp1 & OTGISR_OVC)
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+
+ /* whoever resumes must GetPortStatus to complete it!! */
+ if (temp & PORT_RESUME) {
+
+ /* Remote Wakeup received? */
+ if (!fotg210->reset_done[wIndex]) {
+ /* resume signaling for 20 msec */
+ fotg210->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ /* check the port again */
+ mod_timer(&fotg210_to_hcd(fotg210)->rh_timer,
+ fotg210->reset_done[wIndex]);
+ }
+
+ /* resume completed? */
+ else if (time_after_eq(jiffies,
+ fotg210->reset_done[wIndex])) {
+ clear_bit(wIndex, &fotg210->suspended_ports);
+ set_bit(wIndex, &fotg210->port_c_suspend);
+ fotg210->reset_done[wIndex] = 0;
+
+ /* stop resume signaling */
+ temp = fotg210_readl(fotg210, status_reg);
+ fotg210_writel(fotg210,
+ temp & ~(PORT_RWC_BITS | PORT_RESUME),
+ status_reg);
+ clear_bit(wIndex, &fotg210->resuming_ports);
+ retval = handshake(fotg210, status_reg,
+ PORT_RESUME, 0, 2000 /* 2msec */);
+ if (retval != 0) {
+ fotg210_err(fotg210,
+ "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
+ }
+ }
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && time_after_eq(jiffies,
+ fotg210->reset_done[wIndex])) {
+ status |= USB_PORT_STAT_C_RESET << 16;
+ fotg210->reset_done[wIndex] = 0;
+ clear_bit(wIndex, &fotg210->resuming_ports);
+
+ /* force reset to complete */
+ fotg210_writel(fotg210,
+ temp & ~(PORT_RWC_BITS | PORT_RESET),
+ status_reg);
+ /* REVISIT: some hardware needs 550+ usec to clear
+ * this bit; seems too long to spin routinely...
+ */
+ retval = handshake(fotg210, status_reg,
+ PORT_RESET, 0, 1000);
+ if (retval != 0) {
+ fotg210_err(fotg210, "port %d reset error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+
+ /* see what we found out */
+ temp = check_reset_complete(fotg210, wIndex, status_reg,
+ fotg210_readl(fotg210, status_reg));
+ }
+
+ if (!(temp & (PORT_RESUME|PORT_RESET))) {
+ fotg210->reset_done[wIndex] = 0;
+ clear_bit(wIndex, &fotg210->resuming_ports);
+ }
+
+ /* transfer dedicated ports to the companion hc */
+ if ((temp & PORT_CONNECT) &&
+ test_bit(wIndex, &fotg210->companion_ports)) {
+ temp &= ~PORT_RWC_BITS;
+ fotg210_writel(fotg210, temp, status_reg);
+ fotg210_dbg(fotg210, "port %d --> companion\n",
+ wIndex + 1);
+ temp = fotg210_readl(fotg210, status_reg);
+ }
+
+ /*
+ * Even if OWNER is set, there's no harm letting hub_wq
+ * see the wPortStatus values (they should all be 0 except
+ * for PORT_POWER anyway).
+ */
+
+ if (temp & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ status |= fotg210_port_speed(fotg210, temp);
+ }
+ if (temp & PORT_PE)
+ status |= USB_PORT_STAT_ENABLE;
+
+ /* maybe the port was unsuspended without our knowledge */
+ if (temp & (PORT_SUSPEND|PORT_RESUME)) {
+ status |= USB_PORT_STAT_SUSPEND;
+ } else if (test_bit(wIndex, &fotg210->suspended_ports)) {
+ clear_bit(wIndex, &fotg210->suspended_ports);
+ clear_bit(wIndex, &fotg210->resuming_ports);
+ fotg210->reset_done[wIndex] = 0;
+ if (temp & PORT_PE)
+ set_bit(wIndex, &fotg210->port_c_suspend);
+ }
+
+ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr);
+ if (temp1 & OTGISR_OVC)
+ status |= USB_PORT_STAT_OVERCURRENT;
+ if (temp & PORT_RESET)
+ status |= USB_PORT_STAT_RESET;
+ if (test_bit(wIndex, &fotg210->port_c_suspend))
+ status |= USB_PORT_STAT_C_SUSPEND << 16;
+
+ if (status & ~0xffff) /* only if wPortChange is interesting */
+ dbg_port(fotg210, "GetStatus", wIndex + 1, temp);
+ put_unaligned_le32(status, buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ selector = wIndex >> 8;
+ wIndex &= 0xff;
+
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = fotg210_readl(fotg210, status_reg);
+ temp &= ~PORT_RWC_BITS;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_RESET) != 0)
+ goto error;
+
+ /* After above check the port must be connected.
+ * Set appropriate bit thus could put phy into low power
+ * mode if we have hostpc feature
+ */
+ fotg210_writel(fotg210, temp | PORT_SUSPEND,
+ status_reg);
+ set_bit(wIndex, &fotg210->suspended_ports);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (temp & PORT_RESUME)
+ goto error;
+ /* line status bits may report this as low speed,
+ * which can be fine if this root hub has a
+ * transaction translator built in.
+ */
+ fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1);
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ fotg210->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(50);
+ fotg210_writel(fotg210, temp, status_reg);
+ break;
+
+ /* For downstream facing ports (these): one hub port is put
+ * into test mode according to USB2 11.24.2.13, then the hub
+ * must be reset (which for root hub now means rmmod+modprobe,
+ * or else system reboot). See EHCI 2.3.9 and 4.14 for info
+ * about the EHCI-specific stuff.
+ */
+ case USB_PORT_FEAT_TEST:
+ if (!selector || selector > 5)
+ goto error;
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ fotg210_quiesce(fotg210);
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ /* Put all enabled ports into suspend */
+ temp = fotg210_readl(fotg210, status_reg) &
+ ~PORT_RWC_BITS;
+ if (temp & PORT_PE)
+ fotg210_writel(fotg210, temp | PORT_SUSPEND,
+ status_reg);
+
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ fotg210_halt(fotg210);
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ temp = fotg210_readl(fotg210, status_reg);
+ temp |= selector << 16;
+ fotg210_writel(fotg210, temp, status_reg);
+ break;
+
+ default:
+ goto error;
+ }
+ fotg210_readl(fotg210, &fotg210->regs->command);
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return retval;
+}
+
+static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd,
+ int portnum)
+{
+ return;
+}
+
+static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd,
+ int portnum)
+{
+ return 0;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
+ * No memory seen by this driver is pageable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210,
+ struct fotg210_qtd *qtd, dma_addr_t dma)
+{
+ memset(qtd, 0, sizeof(*qtd));
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT);
+ qtd->hw_next = FOTG210_LIST_END(fotg210);
+ qtd->hw_alt_next = FOTG210_LIST_END(fotg210);
+ INIT_LIST_HEAD(&qtd->qtd_list);
+}
+
+static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210,
+ gfp_t flags)
+{
+ struct fotg210_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma);
+ if (qtd != NULL)
+ fotg210_qtd_init(fotg210, qtd, dma);
+
+ return qtd;
+}
+
+static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210,
+ struct fotg210_qtd *qtd)
+{
+ dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) {
+ fotg210_dbg(fotg210, "unused qh not empty!\n");
+ BUG();
+ }
+ if (qh->dummy)
+ fotg210_qtd_free(fotg210, qh->dummy);
+ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma);
+ kfree(qh);
+}
+
+static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
+ gfp_t flags)
+{
+ struct fotg210_qh *qh;
+ dma_addr_t dma;
+
+ qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
+ if (!qh)
+ goto done;
+ qh->hw = (struct fotg210_qh_hw *)
+ dma_pool_alloc(fotg210->qh_pool, flags, &dma);
+ if (!qh->hw)
+ goto fail;
+ memset(qh->hw, 0, sizeof(*qh->hw));
+ qh->qh_dma = dma;
+ INIT_LIST_HEAD(&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = fotg210_qtd_alloc(fotg210, flags);
+ if (qh->dummy == NULL) {
+ fotg210_dbg(fotg210, "no dummy td\n");
+ goto fail1;
+ }
+done:
+ return qh;
+fail1:
+ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma);
+fail:
+ kfree(qh);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210)
+{
+ if (fotg210->async)
+ qh_destroy(fotg210, fotg210->async);
+ fotg210->async = NULL;
+
+ if (fotg210->dummy)
+ qh_destroy(fotg210, fotg210->dummy);
+ fotg210->dummy = NULL;
+
+ /* DMA consistent memory and pools */
+ if (fotg210->qtd_pool)
+ dma_pool_destroy(fotg210->qtd_pool);
+ fotg210->qtd_pool = NULL;
+
+ if (fotg210->qh_pool) {
+ dma_pool_destroy(fotg210->qh_pool);
+ fotg210->qh_pool = NULL;
+ }
+
+ if (fotg210->itd_pool)
+ dma_pool_destroy(fotg210->itd_pool);
+ fotg210->itd_pool = NULL;
+
+ if (fotg210->periodic)
+ dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller,
+ fotg210->periodic_size * sizeof(u32),
+ fotg210->periodic, fotg210->periodic_dma);
+ fotg210->periodic = NULL;
+
+ /* shadow periodic table */
+ kfree(fotg210->pshadow);
+ fotg210->pshadow = NULL;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags)
+{
+ int i;
+
+ /* QTDs for control/bulk/intr transfers */
+ fotg210->qtd_pool = dma_pool_create("fotg210_qtd",
+ fotg210_to_hcd(fotg210)->self.controller,
+ sizeof(struct fotg210_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fotg210->qtd_pool)
+ goto fail;
+
+ /* QHs for control/bulk/intr transfers */
+ fotg210->qh_pool = dma_pool_create("fotg210_qh",
+ fotg210_to_hcd(fotg210)->self.controller,
+ sizeof(struct fotg210_qh_hw),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fotg210->qh_pool)
+ goto fail;
+
+ fotg210->async = fotg210_qh_alloc(fotg210, flags);
+ if (!fotg210->async)
+ goto fail;
+
+ /* ITD for high speed ISO transfers */
+ fotg210->itd_pool = dma_pool_create("fotg210_itd",
+ fotg210_to_hcd(fotg210)->self.controller,
+ sizeof(struct fotg210_itd),
+ 64 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fotg210->itd_pool)
+ goto fail;
+
+ /* Hardware periodic table */
+ fotg210->periodic = (__le32 *)
+ dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller,
+ fotg210->periodic_size * sizeof(__le32),
+ &fotg210->periodic_dma, 0);
+ if (fotg210->periodic == NULL)
+ goto fail;
+
+ for (i = 0; i < fotg210->periodic_size; i++)
+ fotg210->periodic[i] = FOTG210_LIST_END(fotg210);
+
+ /* software shadow of hardware table */
+ fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *),
+ flags);
+ if (fotg210->pshadow != NULL)
+ return 0;
+
+fail:
+ fotg210_dbg(fotg210, "couldn't init memory\n");
+ fotg210_mem_cleanup(fotg210);
+ return -ENOMEM;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * EHCI hardware queue manipulation ... the core. QH/QTD manipulation.
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number). We use one QH per endpoint, queue
+ * multiple urbs (all three types) per endpoint. URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge. That's in "ehci-sched.c".
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf,
+ size_t len, int token, int maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr);
+ qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely(len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ addr = buf;
+ qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr);
+ qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210,
+ (u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
+ struct fotg210_qtd *qtd)
+{
+ struct fotg210_qh_hw *hw = qh->hw;
+
+ /* writes to an active overlay are unsafe */
+ BUG_ON(qh->qh_state != QH_STATE_IDLE);
+
+ hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma);
+ hw->hw_alt_next = FOTG210_LIST_END(fotg210);
+
+ /* 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 (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) {
+ unsigned is_out, epnum;
+
+ is_out = qh->is_out;
+ epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f;
+ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) {
+ hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE);
+ usb_settoggle(qh->dev, epnum, is_out, 1);
+ }
+ }
+
+ hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* if it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void
+qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ struct fotg210_qtd *qtd;
+
+ if (list_empty(&qh->qtd_list))
+ qtd = qh->dummy;
+ else {
+ qtd = list_entry(qh->qtd_list.next,
+ struct fotg210_qtd, qtd_list);
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) {
+ qh->hw->hw_qtd_next = qtd->hw_next;
+ qtd = NULL;
+ }
+ }
+
+ if (qtd)
+ qh_update(fotg210, qh, qtd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);
+
+static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ struct fotg210_qh *qh = ep->hcpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ qh->clearing_tt = 0;
+ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
+ && fotg210->rh_state == FOTG210_RH_RUNNING)
+ qh_link_async(fotg210, qh);
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+}
+
+static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh,
+ struct urb *urb, u32 token)
+{
+
+ /* If an async split transaction gets an error or is unlinked,
+ * the TT buffer may be left in an indeterminate state. We
+ * have to clear the TT buffer.
+ *
+ * Note: this routine is never called for Isochronous transfers.
+ */
+ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
+ struct usb_device *tt = urb->dev->tt->hub;
+ dev_dbg(&tt->dev,
+ "clear tt buffer port %d, a%d ep%d t%08x\n",
+ urb->dev->ttport, urb->dev->devnum,
+ usb_pipeendpoint(urb->pipe), token);
+
+ if (urb->dev->tt->hub !=
+ fotg210_to_hcd(fotg210)->self.root_hub) {
+ if (usb_hub_clear_tt_buffer(urb) == 0)
+ qh->clearing_tt = 1;
+ }
+ }
+}
+
+static int qtd_copy_status(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ size_t length,
+ u32 token
+)
+{
+ int status = -EINPROGRESS;
+
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely(QTD_PID(token) != 2))
+ urb->actual_length += length - QTD_LENGTH(token);
+
+ /* don't modify error codes */
+ if (unlikely(urb->unlinked))
+ return status;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely(IS_SHORT_READ(token)))
+ status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ status = -EOVERFLOW;
+ /* CERR nonzero + halt --> stall */
+ } else if (QTD_CERR(token)) {
+ status = -EPIPE;
+
+ /* In theory, more than one of the following bits can be set
+ * since they are sticky and the transaction is retried.
+ * Which to test first is rather arbitrary.
+ */
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ status = -EPROTO;
+ } else if (token & QTD_STS_DBE) {
+ status = (QTD_PID(token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ } else if (token & QTD_STS_XACT) {
+ /* timeout, bad CRC, wrong PID, etc */
+ fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out");
+ status = -EPROTO;
+ } else { /* unknown */
+ status = -EPROTO;
+ }
+
+ fotg210_dbg(fotg210,
+ "dev%d ep%d%s qtd token %08x --> status %d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ token, status);
+ }
+
+ return status;
+}
+
+static void
+fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, int status)
+__releases(fotg210->lock)
+__acquires(fotg210->lock)
+{
+ if (likely(urb->hcpriv != NULL)) {
+ struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--;
+ }
+ }
+
+ if (unlikely(urb->unlinked)) {
+ COUNT(fotg210->stats.unlink);
+ } else {
+ /* report non-error and short read status as zero */
+ if (status == -EINPROGRESS || status == -EREMOTEIO)
+ status = 0;
+ COUNT(fotg210->stats.complete);
+ }
+
+#ifdef FOTG210_URB_TRACE
+ fotg210_dbg(fotg210,
+ "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ status,
+ urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ /* complete() can reenter this HCD */
+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
+ spin_unlock(&fotg210->lock);
+ usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status);
+ spin_lock(&fotg210->lock);
+}
+
+static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+static unsigned
+qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ struct fotg210_qtd *last, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int last_status;
+ int stopped;
+ unsigned count = 0;
+ u8 state;
+ struct fotg210_qh_hw *hw = qh->hw;
+
+ if (unlikely(list_empty(&qh->qtd_list)))
+ return count;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ *
+ * It's a bug for qh->qh_state to be anything other than
+ * QH_STATE_IDLE, unless our caller is scan_async() or
+ * scan_intr().
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ rescan:
+ last = NULL;
+ last_status = -EINPROGRESS;
+ qh->needs_rescan = 0;
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe(entry, tmp, &qh->qtd_list) {
+ struct fotg210_qtd *qtd;
+ struct urb *urb;
+ u32 token = 0;
+
+ qtd = list_entry(entry, struct fotg210_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely(last->urb != urb)) {
+ fotg210_urb_done(fotg210, last->urb,
+ last_status);
+ count++;
+ last_status = -EINPROGRESS;
+ }
+ fotg210_qtd_free(fotg210, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb();
+ token = hc32_to_cpu(fotg210, qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ retry_xacterr:
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ /* Report Data Buffer Error: non-fatal but useful */
+ if (token & QTD_STS_DBE)
+ fotg210_dbg(fotg210,
+ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ urb,
+ usb_endpoint_num(&urb->ep->desc),
+ usb_endpoint_dir_in(&urb->ep->desc)
+ ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd,
+ qh);
+
+ /* on STALL, error, and short reads this urb must
+ * complete and all its qtds must be recycled.
+ */
+ if ((token & QTD_STS_HALT) != 0) {
+
+ /* retry transaction errors until we
+ * reach the software xacterr limit
+ */
+ if ((token & QTD_STS_XACT) &&
+ QTD_CERR(token) == 0 &&
+ ++qh->xacterrs < QH_XACTERR_MAX &&
+ !urb->unlinked) {
+ fotg210_dbg(fotg210,
+ "detected XactErr len %zu/%zu retry %d\n",
+ qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs);
+
+ /* reset the token in the qtd and the
+ * qh overlay (which still contains
+ * the qtd) so that we pick up from
+ * where we left off
+ */
+ token &= ~QTD_STS_HALT;
+ token |= QTD_STS_ACTIVE |
+ (FOTG210_TUNE_CERR << 10);
+ qtd->hw_token = cpu_to_hc32(fotg210,
+ token);
+ wmb();
+ hw->hw_token = cpu_to_hc32(fotg210,
+ token);
+ goto retry_xacterr;
+ }
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ *
+ * other short reads won't stop the queue, including
+ * control transfers (status stage handles that) or
+ * most other single-qtd reads ... the queue stops if
+ * URB_SHORT_NOT_OK was set so the driver submitting
+ * the urbs could clean it up.
+ */
+ } else if (IS_SHORT_READ(token)
+ && !(qtd->hw_alt_next
+ & FOTG210_LIST_END(fotg210))) {
+ stopped = 1;
+ }
+
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely(!stopped
+ && fotg210->rh_state >= FOTG210_RH_RUNNING)) {
+ break;
+
+ /* scan the whole queue for unlinks whenever it stops */
+ } else {
+ stopped = 1;
+
+ /* cancel everything if we halt, suspend, etc */
+ if (fotg210->rh_state < FOTG210_RH_RUNNING)
+ last_status = -ESHUTDOWN;
+
+ /* this qtd is active; skip it unless a previous qtd
+ * for its urb faulted, or its urb was canceled.
+ */
+ else if (last_status == -EINPROGRESS && !urb->unlinked)
+ continue;
+
+ /* qh unlinked; token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_hc32(fotg210, qtd->qtd_dma)
+ == hw->hw_current) {
+ token = hc32_to_cpu(fotg210, hw->hw_token);
+
+ /* An unlink may leave an incomplete
+ * async transaction in the TT buffer.
+ * We have to clear it.
+ */
+ fotg210_clear_tt_buffer(fotg210, qh, urb,
+ token);
+ }
+ }
+
+ /* unless we already know the urb's status, collect qtd status
+ * and update count of bytes transferred. in common short read
+ * cases with only one data qtd (including control transfers),
+ * queue processing won't halt. but with two or more qtds (for
+ * example, with a 32 KB transfer), when the first qtd gets a
+ * short read the second must be removed by hand.
+ */
+ if (last_status == -EINPROGRESS) {
+ last_status = qtd_copy_status(fotg210, urb,
+ qtd->length, token);
+ if (last_status == -EREMOTEIO
+ && (qtd->hw_alt_next
+ & FOTG210_LIST_END(fotg210)))
+ last_status = -EINPROGRESS;
+
+ /* As part of low/full-speed endpoint-halt processing
+ * we must clear the TT buffer (11.17.5).
+ */
+ if (unlikely(last_status != -EINPROGRESS &&
+ last_status != -EREMOTEIO)) {
+ /* The TT's in some hubs malfunction when they
+ * receive this request following a STALL (they
+ * stop sending isochronous packets). Since a
+ * STALL can't leave the TT buffer in a busy
+ * state (if you believe Figures 11-48 - 11-51
+ * in the USB 2.0 spec), we won't clear the TT
+ * buffer in this case. Strictly speaking this
+ * is a violation of the spec.
+ */
+ if (last_status != -EPIPE)
+ fotg210_clear_tt_buffer(fotg210, qh,
+ urb, token);
+ }
+ }
+
+ /* if we're removing something not at the queue head,
+ * patch the hardware queue pointer.
+ */
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry(qtd->qtd_list.prev,
+ struct fotg210_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+
+ /* remove qtd; it's recycled after possible urb completion */
+ list_del(&qtd->qtd_list);
+ last = qtd;
+
+ /* reinit the xacterr counter for the next qtd */
+ qh->xacterrs = 0;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely(last != NULL)) {
+ fotg210_urb_done(fotg210, last->urb, last_status);
+ count++;
+ fotg210_qtd_free(fotg210, last);
+ }
+
+ /* Do we need to rescan for URBs dequeued during a giveback? */
+ if (unlikely(qh->needs_rescan)) {
+ /* If the QH is already unlinked, do the rescan now. */
+ if (state == QH_STATE_IDLE)
+ goto rescan;
+
+ /* Otherwise we have to wait until the QH is fully unlinked.
+ * Our caller will start an unlink if qh->needs_rescan is
+ * set. But if an unlink has already started, nothing needs
+ * to be done.
+ */
+ if (state != QH_STATE_LINKED)
+ qh->needs_rescan = 0;
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ */
+ if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) {
+ switch (state) {
+ case QH_STATE_IDLE:
+ qh_refresh(fotg210, qh);
+ break;
+ case QH_STATE_LINKED:
+ /* We won't refresh a QH that's linked (after the HC
+ * stopped the queue). That avoids a race:
+ * - HC reads first part of QH;
+ * - CPU updates that first part and the token;
+ * - HC reads rest of that QH, including token
+ * Result: HC gets an inconsistent image, and then
+ * DMAs to/from the wrong memory (corrupting it).
+ *
+ * That should be rare for interrupt transfers,
+ * except maybe high bandwidth ...
+ */
+
+ /* Tell the caller to start an unlink */
+ qh->needs_rescan = 1;
+ break;
+ /* otherwise, unlink already started */
+ }
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+/* ... and packet size, for any kind of endpoint descriptor */
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct list_head *qtd_list
+) {
+ struct list_head *entry, *temp;
+
+ list_for_each_safe(entry, temp, qtd_list) {
+ struct fotg210_qtd *qtd;
+
+ qtd = list_entry(entry, struct fotg210_qtd, qtd_list);
+ list_del(&qtd->qtd_list);
+ fotg210_qtd_free(fotg210, qtd);
+ }
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct list_head *head,
+ gfp_t flags
+) {
+ struct fotg210_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, this_sg_len, maxpacket;
+ int is_input;
+ u32 token;
+ int i;
+ struct scatterlist *sg;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = fotg210_qtd_alloc(fotg210, flags);
+ if (unlikely(!qtd))
+ return NULL;
+ list_add_tail(&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (FOTG210_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein(urb->pipe);
+ if (usb_pipecontrol(urb->pipe)) {
+ /* SETUP pid */
+ qtd_fill(fotg210, qtd, urb->setup_dma,
+ sizeof(struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = fotg210_qtd_alloc(fotg210, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ i = urb->num_mapped_sgs;
+ if (len > 0 && i > 0) {
+ sg = urb->sg;
+ buf = sg_dma_address(sg);
+
+ /* urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ } else {
+ sg = NULL;
+ buf = urb->transfer_dma;
+ this_sg_len = len;
+ }
+
+ if (is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+
+ this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token,
+ maxpacket);
+ this_sg_len -= this_qtd_len;
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+
+ /*
+ * short reads advance to a "magic" dummy instead of the next
+ * qtd ... that forces the queue to stop, for manual cleanup.
+ * (this will usually be overridden later.)
+ */
+ if (is_input)
+ qtd->hw_alt_next = fotg210->async->hw->hw_alt_next;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely(this_sg_len <= 0)) {
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ buf = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ }
+
+ qtd_prev = qtd;
+ qtd = fotg210_qtd_alloc(fotg210, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+ }
+
+ /*
+ * unless the caller requires manual cleanup after short reads,
+ * have the alt_next mechanism keep the queue running after the
+ * last data qtd (the only one, for control and most other cases).
+ */
+ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol(urb->pipe)))
+ qtd->hw_alt_next = FOTG210_LIST_END(fotg210);
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * other OUT ones may need a terminating short packet
+ * (zero length).
+ */
+ if (likely(urb->transfer_buffer_length != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipeout(urb->pipe)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = fotg210_qtd_alloc(fotg210, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill(fotg210, qtd, 0, 0, token, 0);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC);
+ return head;
+
+cleanup:
+ qtd_list_free(fotg210, urb, head);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+/*
+ * Would be best to create all qh's from config descriptors,
+ * when each interface/altsetting is established. Unlink
+ * any previous qh and cancel its urbs first; endpoints are
+ * implicitly reset then (data toggle too).
+ * That'd mean updating how usbcore talks to HCDs. (2.7?)
+*/
+
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct fotg210_qh *
+qh_make(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ gfp_t flags
+) {
+ struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+ struct usb_tt *tt = urb->dev->tt;
+ struct fotg210_qh_hw *hw;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint(urb->pipe) << 8;
+ info1 |= usb_pipedevice(urb->pipe) << 0;
+
+ is_input = usb_pipein(urb->pipe);
+ type = usb_pipetype(urb->pipe);
+ maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+
+ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth
+ * acts like up to 3KB, but is built from smaller packets.
+ */
+ if (max_packet(maxp) > 1024) {
+ fotg210_dbg(fotg210, "bogus qh maxpacket %d\n",
+ max_packet(maxp));
+ goto done;
+ }
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+ is_input, 0,
+ hb_mult(maxp) * max_packet(maxp)));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ qh->period = urb->interval >> 3;
+ if (qh->period == 0 && urb->interval != 1) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ urb->interval = 1;
+ } else if (qh->period > fotg210->periodic_size) {
+ qh->period = fotg210->periodic_size;
+ urb->interval = qh->period << 3;
+ }
+ } else {
+ int think_time;
+
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */
+ qh->c_usecs = qh->usecs + HS_USECS(0);
+ qh->usecs = HS_USECS(1);
+ } else { /* SPLIT+DATA, gap, CSPLIT */
+ qh->usecs += HS_USECS(1);
+ qh->c_usecs = HS_USECS(0);
+ }
+
+ think_time = tt ? tt->think_time : 0;
+ qh->tt_usecs = NS_TO_US(think_time +
+ usb_calc_bus_time(urb->dev->speed,
+ is_input, 0, max_packet(maxp)));
+ qh->period = urb->interval;
+ if (qh->period > fotg210->periodic_size) {
+ qh->period = fotg210->periodic_size;
+ urb->interval = qh->period;
+ }
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->dev = urb->dev;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= QH_LOW_SPEED;
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (FOTG210_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= QH_CONTROL_EP; /* for TT */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (FOTG210_TUNE_MULT_TT << 30);
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (fotg210_has_fsl_portno_bug(fotg210))
+ info2 |= (urb->dev->ttport-1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
+
+ /* set the address of the TT; for TDI's integrated
+ * root hub tt, leave it zeroed.
+ */
+ if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub)
+ info2 |= tt->hub->devnum << 16;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= QH_HIGH_SPEED;
+ if (type == PIPE_CONTROL) {
+ info1 |= (FOTG210_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ info2 |= (FOTG210_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (FOTG210_TUNE_RL_HS << 28);
+ /* The USB spec says that high speed bulk endpoints
+ * always use 512 byte maxpacket. But some device
+ * vendors decided to ignore that, and MSFT is happy
+ * to help them do so. So now people expect to use
+ * such nonconformant devices with Linux too; sigh.
+ */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= (FOTG210_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= hb_mult(maxp) << 30;
+ }
+ break;
+ default:
+ fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev,
+ urb->dev->speed);
+done:
+ qh_destroy(fotg210, qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ hw = qh->hw;
+ hw->hw_info1 = cpu_to_hc32(fotg210, info1);
+ hw->hw_info2 = cpu_to_hc32(fotg210, info2);
+ qh->is_out = !is_input;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1);
+ qh_refresh(fotg210, qh);
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_async(struct fotg210_hcd *fotg210)
+{
+ if (fotg210->async_count++)
+ return;
+
+ /* Stop waiting to turn off the async schedule */
+ fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC);
+
+ /* Don't start the schedule until ASS is 0 */
+ fotg210_poll_ASS(fotg210);
+ turn_on_io_watchdog(fotg210);
+}
+
+static void disable_async(struct fotg210_hcd *fotg210)
+{
+ if (--fotg210->async_count)
+ return;
+
+ /* The async schedule and async_unlink list are supposed to be empty */
+ WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink);
+
+ /* Don't turn off the schedule until ASS is 1 */
+ fotg210_poll_ASS(fotg210);
+}
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ __hc32 dma = QH_NEXT(fotg210, qh->qh_dma);
+ struct fotg210_qh *head;
+
+ /* Don't link a QH if there's a Clear-TT-Buffer pending */
+ if (unlikely(qh->clearing_tt))
+ return;
+
+ WARN_ON(qh->qh_state != QH_STATE_IDLE);
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ qh_refresh(fotg210, qh);
+
+ /* splice right after start */
+ head = fotg210->async;
+ qh->qh_next = head->qh_next;
+ qh->hw->hw_next = head->hw->hw_next;
+ wmb();
+
+ head->qh_next.qh = qh;
+ head->hw->hw_next = dma;
+
+ qh->xacterrs = 0;
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+
+ enable_async(fotg210);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct fotg210_qh *qh_append_tds(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int epnum,
+ void **ptr
+)
+{
+ struct fotg210_qh *qh = NULL;
+ __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f);
+
+ qh = (struct fotg210_qh *) *ptr;
+ if (unlikely(qh == NULL)) {
+ /* can't sleep here, we have fotg210->lock... */
+ qh = qh_make(fotg210, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely(qh != NULL)) {
+ struct fotg210_qtd *qtd;
+
+ if (unlikely(list_empty(qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry(qtd_list->next, struct fotg210_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely(epnum == 0)) {
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice(urb->pipe) == 0)
+ qh->hw->hw_info1 &= ~qh_addr_mask;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely(qtd != NULL)) {
+ struct fotg210_qtd *dummy;
+ dma_addr_t dma;
+ __hc32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT(fotg210);
+
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del(&qtd->qtd_list);
+ list_add(&dummy->qtd_list, qtd_list);
+ list_splice_tail(qtd_list, &qh->qtd_list);
+
+ fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry(qh->qtd_list.prev,
+ struct fotg210_qtd, qtd_list);
+ qtd->hw_next = QTD_NEXT(fotg210, dma);
+
+ /* let the hc process these next qtds */
+ wmb();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh;
+ }
+ }
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ int epnum;
+ unsigned long flags;
+ struct fotg210_qh *qh = NULL;
+ int rc;
+
+ epnum = urb->ep->desc.bEndpointAddress;
+
+#ifdef FOTG210_URB_TRACE
+ {
+ struct fotg210_qtd *qtd;
+ qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list);
+ fotg210_dbg(fotg210,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, urb->ep->hcpriv);
+ }
+#endif
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+ rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb);
+ if (unlikely(rc))
+ goto done;
+
+ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely(qh->qh_state == QH_STATE_IDLE))
+ qh_link_async(fotg210, qh);
+ done:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ if (unlikely(qh == NULL))
+ qtd_list_free(fotg210, urb, qtd_list);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void single_unlink_async(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh)
+{
+ struct fotg210_qh *prev;
+
+ /* Add to the end of the list of QHs waiting for the next IAAD */
+ qh->qh_state = QH_STATE_UNLINK;
+ if (fotg210->async_unlink)
+ fotg210->async_unlink_last->unlink_next = qh;
+ else
+ fotg210->async_unlink = qh;
+ fotg210->async_unlink_last = qh;
+
+ /* Unlink it from the schedule */
+ prev = fotg210->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw->hw_next = qh->hw->hw_next;
+ prev->qh_next = qh->qh_next;
+ if (fotg210->qh_scan_next == qh)
+ fotg210->qh_scan_next = qh->qh_next.qh;
+}
+
+static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested)
+{
+ /*
+ * Do nothing if an IAA cycle is already running or
+ * if one will be started shortly.
+ */
+ if (fotg210->async_iaa || fotg210->async_unlinking)
+ return;
+
+ /* Do all the waiting QHs at once */
+ fotg210->async_iaa = fotg210->async_unlink;
+ fotg210->async_unlink = NULL;
+
+ /* If the controller isn't running, we don't have to wait for it */
+ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) {
+ if (!nested) /* Avoid recursion */
+ end_unlink_async(fotg210);
+
+ /* Otherwise start a new IAA cycle */
+ } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) {
+ /* Make sure the unlinks are all visible to the hardware */
+ wmb();
+
+ fotg210_writel(fotg210, fotg210->command | CMD_IAAD,
+ &fotg210->regs->command);
+ fotg210_readl(fotg210, &fotg210->regs->command);
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG,
+ true);
+ }
+}
+
+/* the async qh for the qtds being unlinked are now gone from the HC */
+
+static void end_unlink_async(struct fotg210_hcd *fotg210)
+{
+ struct fotg210_qh *qh;
+
+ /* Process the idle QHs */
+ restart:
+ fotg210->async_unlinking = true;
+ while (fotg210->async_iaa) {
+ qh = fotg210->async_iaa;
+ fotg210->async_iaa = qh->unlink_next;
+ qh->unlink_next = NULL;
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+
+ qh_completions(fotg210, qh);
+ if (!list_empty(&qh->qtd_list) &&
+ fotg210->rh_state == FOTG210_RH_RUNNING)
+ qh_link_async(fotg210, qh);
+ disable_async(fotg210);
+ }
+ fotg210->async_unlinking = false;
+
+ /* Start a new IAA cycle if any QHs are waiting for it */
+ if (fotg210->async_unlink) {
+ start_iaa_cycle(fotg210, true);
+ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING))
+ goto restart;
+ }
+}
+
+static void unlink_empty_async(struct fotg210_hcd *fotg210)
+{
+ struct fotg210_qh *qh, *next;
+ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING);
+ bool check_unlinks_later = false;
+
+ /* Unlink all the async QHs that have been empty for a timer cycle */
+ next = fotg210->async->qh_next.qh;
+ while (next) {
+ qh = next;
+ next = qh->qh_next.qh;
+
+ if (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED) {
+ if (!stopped && qh->unlink_cycle ==
+ fotg210->async_unlink_cycle)
+ check_unlinks_later = true;
+ else
+ single_unlink_async(fotg210, qh);
+ }
+ }
+
+ /* Start a new IAA cycle if any QHs are waiting for it */
+ if (fotg210->async_unlink)
+ start_iaa_cycle(fotg210, false);
+
+ /* QHs that haven't been empty for long enough will be handled later */
+ if (check_unlinks_later) {
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS,
+ true);
+ ++fotg210->async_unlink_cycle;
+ }
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own fotg210->lock */
+
+static void start_unlink_async(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh)
+{
+ /*
+ * If the QH isn't linked then there's nothing we can do
+ * unless we were called during a giveback, in which case
+ * qh_completions() has to deal with it.
+ */
+ if (qh->qh_state != QH_STATE_LINKED) {
+ if (qh->qh_state == QH_STATE_COMPLETING)
+ qh->needs_rescan = 1;
+ return;
+ }
+
+ single_unlink_async(fotg210, qh);
+ start_iaa_cycle(fotg210, false);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async(struct fotg210_hcd *fotg210)
+{
+ struct fotg210_qh *qh;
+ bool check_unlinks_later = false;
+
+ fotg210->qh_scan_next = fotg210->async->qh_next.qh;
+ while (fotg210->qh_scan_next) {
+ qh = fotg210->qh_scan_next;
+ fotg210->qh_scan_next = qh->qh_next.qh;
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why fotg210->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then fotg210->qh_scan_next is adjusted
+ * in single_unlink_async().
+ */
+ temp = qh_completions(fotg210, qh);
+ if (qh->needs_rescan) {
+ start_unlink_async(fotg210, qh);
+ } else if (list_empty(&qh->qtd_list)
+ && qh->qh_state == QH_STATE_LINKED) {
+ qh->unlink_cycle = fotg210->async_unlink_cycle;
+ check_unlinks_later = true;
+ } else if (temp != 0)
+ goto rescan;
+ }
+ }
+
+ /*
+ * Unlink empty entries, reducing DMA usage as well
+ * as HCD schedule-scanning costs. Delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ */
+ if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING &&
+ !(fotg210->enabled_hrtimer_events &
+ BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) {
+ fotg210_enable_event(fotg210,
+ FOTG210_HRTIMER_ASYNC_UNLINKS, true);
+ ++fotg210->async_unlink_cycle;
+ }
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * EHCI scheduled transaction support: interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ *
+ * Note that for interrupt transfers, the QH/QTD manipulation is shared
+ * with the "asynchronous" transaction support (control/bulk transfers).
+ * The only real difference is in how interrupt transfers are scheduled.
+ *
+ * For ISO, we make an "iso_stream" head to serve the same role as a QH.
+ * It keeps track of every ITD (or SITD) that's linked, and holds enough
+ * pre-calculated schedule data to make appending to the queue be quick.
+ */
+
+static int fotg210_get_frame(struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd
+ * @tag: hardware tag for type of this record
+ */
+static union fotg210_shadow *
+periodic_next_shadow(struct fotg210_hcd *fotg210,
+ union fotg210_shadow *periodic, __hc32 tag)
+{
+ switch (hc32_to_cpu(fotg210, tag)) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+ default:
+ return &periodic->itd->itd_next;
+ }
+}
+
+static __hc32 *
+shadow_next_periodic(struct fotg210_hcd *fotg210,
+ union fotg210_shadow *periodic, __hc32 tag)
+{
+ switch (hc32_to_cpu(fotg210, tag)) {
+ /* our fotg210_shadow.qh is actually software part */
+ case Q_TYPE_QH:
+ return &periodic->qh->hw->hw_next;
+ /* others are hw parts */
+ default:
+ return periodic->hw_next;
+ }
+}
+
+/* caller must hold fotg210->lock */
+static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame,
+ void *ptr)
+{
+ union fotg210_shadow *prev_p = &fotg210->pshadow[frame];
+ __hc32 *hw_p = &fotg210->periodic[frame];
+ union fotg210_shadow here = *prev_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow(fotg210, prev_p,
+ Q_NEXT_TYPE(fotg210, *hw_p));
+ hw_p = shadow_next_periodic(fotg210, &here,
+ Q_NEXT_TYPE(fotg210, *hw_p));
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr)
+ return;
+
+ /* update shadow and hardware lists ... the old "next" pointers
+ * from ptr may still be in use, the caller updates them.
+ */
+ *prev_p = *periodic_next_shadow(fotg210, &here,
+ Q_NEXT_TYPE(fotg210, *hw_p));
+
+ *hw_p = *shadow_next_periodic(fotg210, &here,
+ Q_NEXT_TYPE(fotg210, *hw_p));
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe)
+{
+ __hc32 *hw_p = &fotg210->periodic[frame];
+ union fotg210_shadow *q = &fotg210->pshadow[frame];
+ unsigned usecs = 0;
+ struct fotg210_qh_hw *hw;
+
+ while (q->ptr) {
+ switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) {
+ case Q_TYPE_QH:
+ hw = q->qh->hw;
+ /* is it in the S-mask? */
+ if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe))
+ usecs += q->qh->usecs;
+ /* ... or C-mask? */
+ if (hw->hw_info2 & cpu_to_hc32(fotg210,
+ 1 << (8 + uframe)))
+ usecs += q->qh->c_usecs;
+ hw_p = &hw->hw_next;
+ q = &q->qh->qh_next;
+ break;
+ /* case Q_TYPE_FSTN: */
+ default:
+ /* for "save place" FSTNs, count the relevant INTR
+ * bandwidth from the previous frame
+ */
+ if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210))
+ fotg210_dbg(fotg210, "ignoring FSTN cost ...\n");
+
+ hw_p = &q->fstn->hw_next;
+ q = &q->fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ if (q->itd->hw_transaction[uframe])
+ usecs += q->itd->stream->usecs;
+ hw_p = &q->itd->hw_next;
+ q = &q->itd->itd_next;
+ break;
+ }
+ }
+ if (usecs > fotg210->uframe_periodic_max)
+ fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n",
+ frame * 8 + uframe, usecs);
+ return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int same_tt(struct usb_device *dev1, struct usb_device *dev2)
+{
+ if (!dev1->tt || !dev2->tt)
+ return 0;
+ if (dev1->tt != dev2->tt)
+ return 0;
+ if (dev1->tt->multi)
+ return dev1->ttport == dev2->ttport;
+ else
+ return 1;
+}
+
+/* return true iff the device's transaction translator is available
+ * for a periodic transfer starting at the specified frame, using
+ * all the uframes in the mask.
+ */
+static int tt_no_collision(
+ struct fotg210_hcd *fotg210,
+ unsigned period,
+ struct usb_device *dev,
+ unsigned frame,
+ u32 uf_mask
+)
+{
+ if (period == 0) /* error */
+ return 0;
+
+ /* note bandwidth wastage: split never follows csplit
+ * (different dev or endpoint) until the next uframe.
+ * calling convention doesn't make that distinction.
+ */
+ for (; frame < fotg210->periodic_size; frame += period) {
+ union fotg210_shadow here;
+ __hc32 type;
+ struct fotg210_qh_hw *hw;
+
+ here = fotg210->pshadow[frame];
+ type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]);
+ while (here.ptr) {
+ switch (hc32_to_cpu(fotg210, type)) {
+ case Q_TYPE_ITD:
+ type = Q_NEXT_TYPE(fotg210, here.itd->hw_next);
+ here = here.itd->itd_next;
+ continue;
+ case Q_TYPE_QH:
+ hw = here.qh->hw;
+ if (same_tt(dev, here.qh->dev)) {
+ u32 mask;
+
+ mask = hc32_to_cpu(fotg210,
+ hw->hw_info2);
+ /* "knows" no gap is needed */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE(fotg210, hw->hw_next);
+ here = here.qh->qh_next;
+ continue;
+ /* case Q_TYPE_FSTN: */
+ default:
+ fotg210_dbg(fotg210,
+ "periodic frame %d bogus type %d\n",
+ frame, type);
+ }
+
+ /* collision or error */
+ return 0;
+ }
+ }
+
+ /* no collision */
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_periodic(struct fotg210_hcd *fotg210)
+{
+ if (fotg210->periodic_count++)
+ return;
+
+ /* Stop waiting to turn off the periodic schedule */
+ fotg210->enabled_hrtimer_events &=
+ ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC);
+
+ /* Don't start the schedule until PSS is 0 */
+ fotg210_poll_PSS(fotg210);
+ turn_on_io_watchdog(fotg210);
+}
+
+static void disable_periodic(struct fotg210_hcd *fotg210)
+{
+ if (--fotg210->periodic_count)
+ return;
+
+ /* Don't turn off the schedule until PSS is 1 */
+ fotg210_poll_PSS(fotg210);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* periodic schedule slots have iso tds (normal or split) first, then a
+ * sparse tree for active interrupt transfers.
+ *
+ * this just links in a qh; caller guarantees uframe masks are set right.
+ * no FSTN support (yet; fotg210 0.96+)
+ */
+static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ unsigned i;
+ unsigned period = qh->period;
+
+ dev_dbg(&qh->dev->dev,
+ "link qh%d-%04x/%p start %d [%d/%d us]\n",
+ period, hc32_to_cpup(fotg210, &qh->hw->hw_info2)
+ & (QH_CMASK | QH_SMASK),
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* high bandwidth, or otherwise every microframe */
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->start; i < fotg210->periodic_size; i += period) {
+ union fotg210_shadow *prev = &fotg210->pshadow[i];
+ __hc32 *hw_p = &fotg210->periodic[i];
+ union fotg210_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip the iso nodes at list head */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(fotg210, *hw_p);
+ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(fotg210, prev, type);
+ hw_p = shadow_next_periodic(fotg210, &here, type);
+ here = *prev;
+ }
+
+ /* sorting each branch by period (slow-->fast)
+ * enables sharing interior tree nodes
+ */
+ while (here.ptr && qh != here.qh) {
+ if (qh->period > here.qh->period)
+ break;
+ prev = &here.qh->qh_next;
+ hw_p = &here.qh->hw->hw_next;
+ here = *prev;
+ }
+ /* link in this qh, unless some earlier pass did that */
+ if (qh != here.qh) {
+ qh->qh_next = here;
+ if (here.qh)
+ qh->hw->hw_next = *hw_p;
+ wmb();
+ prev->qh = qh;
+ *hw_p = QH_NEXT(fotg210, qh->qh_dma);
+ }
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ qh->xacterrs = 0;
+
+ /* update per-qh bandwidth for usbfs */
+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ list_add(&qh->intr_node, &fotg210->intr_qh_list);
+
+ /* maybe enable periodic schedule processing */
+ ++fotg210->intr_count;
+ enable_periodic(fotg210);
+}
+
+static void qh_unlink_periodic(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh)
+{
+ unsigned i;
+ unsigned period;
+
+ /*
+ * If qh is for a low/full-speed device, simply unlinking it
+ * could interfere with an ongoing split transaction. To unlink
+ * it safely would require setting the QH_INACTIVATE bit and
+ * waiting at least one frame, as described in EHCI 4.12.2.5.
+ *
+ * We won't bother with any of this. Instead, we assume that the
+ * only reason for unlinking an interrupt QH while the current URB
+ * is still active is to dequeue all the URBs (flush the whole
+ * endpoint queue).
+ *
+ * If rebalancing the periodic schedule is ever implemented, this
+ * approach will no longer be valid.
+ */
+
+ /* high bandwidth, or otherwise part of every microframe */
+ period = qh->period;
+ if (!period)
+ period = 1;
+
+ for (i = qh->start; i < fotg210->periodic_size; i += period)
+ periodic_unlink(fotg210, i, qh);
+
+ /* update per-qh bandwidth for usbfs */
+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ dev_dbg(&qh->dev->dev,
+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
+ qh->period,
+ hc32_to_cpup(fotg210, &qh->hw->hw_info2) &
+ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* qh->qh_next still "live" to HC */
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = NULL;
+
+ if (fotg210->qh_scan_next == qh)
+ fotg210->qh_scan_next = list_entry(qh->intr_node.next,
+ struct fotg210_qh, intr_node);
+ list_del(&qh->intr_node);
+}
+
+static void start_unlink_intr(struct fotg210_hcd *fotg210,
+ struct fotg210_qh *qh)
+{
+ /* If the QH isn't linked then there's nothing we can do
+ * unless we were called during a giveback, in which case
+ * qh_completions() has to deal with it.
+ */
+ if (qh->qh_state != QH_STATE_LINKED) {
+ if (qh->qh_state == QH_STATE_COMPLETING)
+ qh->needs_rescan = 1;
+ return;
+ }
+
+ qh_unlink_periodic(fotg210, qh);
+
+ /* Make sure the unlinks are visible before starting the timer */
+ wmb();
+
+ /*
+ * The EHCI spec doesn't say how long it takes the controller to
+ * stop accessing an unlinked interrupt QH. The timer delay is
+ * 9 uframes; presumably that will be long enough.
+ */
+ qh->unlink_cycle = fotg210->intr_unlink_cycle;
+
+ /* New entries go at the end of the intr_unlink list */
+ if (fotg210->intr_unlink)
+ fotg210->intr_unlink_last->unlink_next = qh;
+ else
+ fotg210->intr_unlink = qh;
+ fotg210->intr_unlink_last = qh;
+
+ if (fotg210->intr_unlinking)
+ ; /* Avoid recursive calls */
+ else if (fotg210->rh_state < FOTG210_RH_RUNNING)
+ fotg210_handle_intr_unlinks(fotg210);
+ else if (fotg210->intr_unlink == qh) {
+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR,
+ true);
+ ++fotg210->intr_unlink_cycle;
+ }
+}
+
+static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ struct fotg210_qh_hw *hw = qh->hw;
+ int rc;
+
+ qh->qh_state = QH_STATE_IDLE;
+ hw->hw_next = FOTG210_LIST_END(fotg210);
+
+ qh_completions(fotg210, qh);
+
+ /* reschedule QH iff another request is queued */
+ if (!list_empty(&qh->qtd_list) &&
+ fotg210->rh_state == FOTG210_RH_RUNNING) {
+ rc = qh_schedule(fotg210, qh);
+
+ /* An error here likely indicates handshake failure
+ * or no space left in the schedule. Neither fault
+ * should happen often ...
+ *
+ * FIXME kill the now-dysfunctional queued urbs
+ */
+ if (rc != 0)
+ fotg210_err(fotg210, "can't reschedule qh %p, err %d\n",
+ qh, rc);
+ }
+
+ /* maybe turn off periodic schedule */
+ --fotg210->intr_count;
+ disable_periodic(fotg210);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_period(
+ struct fotg210_hcd *fotg210,
+ unsigned frame,
+ unsigned uframe,
+ unsigned period,
+ unsigned usecs
+) {
+ int claimed;
+
+ /* complete split running into next frame?
+ * given FSTN support, we could sometimes check...
+ */
+ if (uframe >= 8)
+ return 0;
+
+ /* convert "usecs we need" to "max already claimed" */
+ usecs = fotg210->uframe_periodic_max - usecs;
+
+ /* we "know" 2 and 4 uframe intervals were rejected; so
+ * for period 0, check _every_ microframe in the schedule.
+ */
+ if (unlikely(period == 0)) {
+ do {
+ for (uframe = 0; uframe < 7; uframe++) {
+ claimed = periodic_usecs(fotg210, frame,
+ uframe);
+ if (claimed > usecs)
+ return 0;
+ }
+ } while ((frame += 1) < fotg210->periodic_size);
+
+ /* just check the specified uframe, at that period */
+ } else {
+ do {
+ claimed = periodic_usecs(fotg210, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ } while ((frame += period) < fotg210->periodic_size);
+ }
+
+ /* success! */
+ return 1;
+}
+
+static int check_intr_schedule(
+ struct fotg210_hcd *fotg210,
+ unsigned frame,
+ unsigned uframe,
+ const struct fotg210_qh *qh,
+ __hc32 *c_maskp
+)
+{
+ int retval = -ENOSPC;
+ u8 mask = 0;
+
+ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
+ goto done;
+
+ if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs))
+ goto done;
+ if (!qh->c_usecs) {
+ retval = 0;
+ *c_maskp = 0;
+ goto done;
+ }
+
+ /* Make sure this tt's buffer is also available for CSPLITs.
+ * We pessimize a bit; probably the typical full speed case
+ * doesn't need the second CSPLIT.
+ *
+ * NOTE: both SPLIT and CSPLIT could be checked in just
+ * one smart pass...
+ */
+ mask = 0x03 << (uframe + qh->gap_uf);
+ *c_maskp = cpu_to_hc32(fotg210, mask << 8);
+
+ mask |= 1 << uframe;
+ if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) {
+ if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1,
+ qh->period, qh->c_usecs))
+ goto done;
+ if (!check_period(fotg210, frame, uframe + qh->gap_uf,
+ qh->period, qh->c_usecs))
+ goto done;
+ retval = 0;
+ }
+done:
+ return retval;
+}
+
+/* "first fit" scheduling policy used the first time through,
+ * or when the previous schedule slot can't be re-used.
+ */
+static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
+{
+ int status;
+ unsigned uframe;
+ __hc32 c_mask;
+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+ struct fotg210_qh_hw *hw = qh->hw;
+
+ qh_refresh(fotg210, qh);
+ hw->hw_next = FOTG210_LIST_END(fotg210);
+ frame = qh->start;
+
+ /* reuse the previous schedule slots, if we can */
+ if (frame < qh->period) {
+ uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK);
+ status = check_intr_schedule(fotg210, frame, --uframe,
+ qh, &c_mask);
+ } else {
+ uframe = 0;
+ c_mask = 0;
+ status = -ENOSPC;
+ }
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ if (status) {
+ /* "normal" case, uframing flexible except with splits */
+ if (qh->period) {
+ int i;
+
+ for (i = qh->period; status && i > 0; --i) {
+ frame = ++fotg210->random_frame % qh->period;
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule(fotg210,
+ frame, uframe, qh,
+ &c_mask);
+ if (status == 0)
+ break;
+ }
+ }
+
+ /* qh->period == 0 means every uframe */
+ } else {
+ frame = 0;
+ status = check_intr_schedule(fotg210, 0, 0, qh,
+ &c_mask);
+ }
+ if (status)
+ goto done;
+ qh->start = frame;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK));
+ hw->hw_info2 |= qh->period
+ ? cpu_to_hc32(fotg210, 1 << uframe)
+ : cpu_to_hc32(fotg210, QH_SMASK);
+ hw->hw_info2 |= c_mask;
+ } else
+ fotg210_dbg(fotg210, "reused qh %p schedule\n", qh);
+
+ /* stuff into the periodic schedule */
+ qh_link_periodic(fotg210, qh);
+done:
+ return status;
+}
+
+static int intr_submit(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ unsigned epnum;
+ unsigned long flags;
+ struct fotg210_qh *qh;
+ int status;
+ struct list_head empty;
+
+ /* get endpoint and transfer/schedule data */
+ epnum = urb->ep->desc.bEndpointAddress;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD(&empty);
+ qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv);
+ if (qh == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ if (qh->qh_state == QH_STATE_IDLE) {
+ status = qh_schedule(fotg210, qh);
+ if (status)
+ goto done;
+ }
+
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ BUG_ON(qh == NULL);
+
+ /* ... update usbfs periodic stats */
+ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++;
+
+done:
+ if (unlikely(status))
+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
+done_not_linked:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ if (status)
+ qtd_list_free(fotg210, urb, qtd_list);
+
+ return status;
+}
+
+static void scan_intr(struct fotg210_hcd *fotg210)
+{
+ struct fotg210_qh *qh;
+
+ list_for_each_entry_safe(qh, fotg210->qh_scan_next,
+ &fotg210->intr_qh_list, intr_node) {
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why fotg210->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then fotg210->qh_scan_next is adjusted
+ * in qh_unlink_periodic().
+ */
+ temp = qh_completions(fotg210, qh);
+ if (unlikely(qh->needs_rescan ||
+ (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED)))
+ start_unlink_intr(fotg210, qh);
+ else if (temp != 0)
+ goto rescan;
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* fotg210_iso_stream ops work with both ITD and SITD */
+
+static struct fotg210_iso_stream *
+iso_stream_alloc(gfp_t mem_flags)
+{
+ struct fotg210_iso_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), mem_flags);
+ if (likely(stream != NULL)) {
+ INIT_LIST_HEAD(&stream->td_list);
+ INIT_LIST_HEAD(&stream->free_list);
+ stream->next_uframe = -1;
+ }
+ return stream;
+}
+
+static void
+iso_stream_init(
+ struct fotg210_hcd *fotg210,
+ struct fotg210_iso_stream *stream,
+ struct usb_device *dev,
+ int pipe,
+ unsigned interval
+)
+{
+ u32 buf1;
+ unsigned epnum, maxp;
+ int is_input;
+ long bandwidth;
+ unsigned multi;
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's wMaxPacket field
+ */
+ epnum = usb_pipeendpoint(pipe);
+ is_input = usb_pipein(pipe) ? USB_DIR_IN : 0;
+ maxp = usb_maxpacket(dev, pipe, !is_input);
+ if (is_input)
+ buf1 = (1 << 11);
+ else
+ buf1 = 0;
+
+ maxp = max_packet(maxp);
+ multi = hb_mult(maxp);
+ buf1 |= maxp;
+ maxp *= multi;
+
+ stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum);
+ stream->buf1 = cpu_to_hc32(fotg210, buf1);
+ stream->buf2 = cpu_to_hc32(fotg210, multi);
+
+ /* usbfs wants to report the average usecs per frame tied up
+ * when transfers on this endpoint are scheduled ...
+ */
+ if (dev->speed == USB_SPEED_FULL) {
+ interval <<= 3;
+ stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed,
+ is_input, 1, maxp));
+ stream->usecs /= 8;
+ } else {
+ stream->highspeed = 1;
+ stream->usecs = HS_USECS_ISO(maxp);
+ }
+ bandwidth = stream->usecs * 8;
+ bandwidth /= interval;
+
+ stream->bandwidth = bandwidth;
+ stream->udev = dev;
+ stream->bEndpointAddress = is_input | epnum;
+ stream->interval = interval;
+ stream->maxp = maxp;
+}
+
+static struct fotg210_iso_stream *
+iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb)
+{
+ unsigned epnum;
+ struct fotg210_iso_stream *stream;
+ struct usb_host_endpoint *ep;
+ unsigned long flags;
+
+ epnum = usb_pipeendpoint(urb->pipe);
+ if (usb_pipein(urb->pipe))
+ ep = urb->dev->ep_in[epnum];
+ else
+ ep = urb->dev->ep_out[epnum];
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ stream = ep->hcpriv;
+
+ if (unlikely(stream == NULL)) {
+ stream = iso_stream_alloc(GFP_ATOMIC);
+ if (likely(stream != NULL)) {
+ ep->hcpriv = stream;
+ stream->ep = ep;
+ iso_stream_init(fotg210, stream, urb->dev, urb->pipe,
+ urb->interval);
+ }
+
+ /* if dev->ep[epnum] is a QH, hw is set */
+ } else if (unlikely(stream->hw != NULL)) {
+ fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n",
+ urb->dev->devpath, epnum,
+ usb_pipein(urb->pipe) ? "in" : "out");
+ stream = NULL;
+ }
+
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return stream;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* fotg210_iso_sched ops can be ITD-only or SITD-only */
+
+static struct fotg210_iso_sched *
+iso_sched_alloc(unsigned packets, gfp_t mem_flags)
+{
+ struct fotg210_iso_sched *iso_sched;
+ int size = sizeof(*iso_sched);
+
+ size += packets * sizeof(struct fotg210_iso_packet);
+ iso_sched = kzalloc(size, mem_flags);
+ if (likely(iso_sched != NULL))
+ INIT_LIST_HEAD(&iso_sched->td_list);
+
+ return iso_sched;
+}
+
+static inline void
+itd_sched_init(
+ struct fotg210_hcd *fotg210,
+ struct fotg210_iso_sched *iso_sched,
+ struct fotg210_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many uframes are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->interval;
+
+ /* figure out per-uframe itd fields that we'll need later
+ * when we fit new itds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct fotg210_iso_packet *uframe = &iso_sched->packet[i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc[i].length;
+ buf = dma + urb->iso_frame_desc[i].offset;
+
+ trans = FOTG210_ISOC_ACTIVE;
+ trans |= buf & 0x0fff;
+ if (unlikely(((i + 1) == urb->number_of_packets))
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= FOTG210_ITD_IOC;
+ trans |= length << 16;
+ uframe->transaction = cpu_to_hc32(fotg210, trans);
+
+ /* might need to cross a buffer page within a uframe */
+ uframe->bufp = (buf & ~(u64)0x0fff);
+ buf += length;
+ if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff))))
+ uframe->cross = 1;
+ }
+}
+
+static void
+iso_sched_free(
+ struct fotg210_iso_stream *stream,
+ struct fotg210_iso_sched *iso_sched
+)
+{
+ if (!iso_sched)
+ return;
+ /* caller must hold fotg210->lock!*/
+ list_splice(&iso_sched->td_list, &stream->free_list);
+ kfree(iso_sched);
+}
+
+static int
+itd_urb_transaction(
+ struct fotg210_iso_stream *stream,
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ gfp_t mem_flags
+)
+{
+ struct fotg210_itd *itd;
+ dma_addr_t itd_dma;
+ int i;
+ unsigned num_itds;
+ struct fotg210_iso_sched *sched;
+ unsigned long flags;
+
+ sched = iso_sched_alloc(urb->number_of_packets, mem_flags);
+ if (unlikely(sched == NULL))
+ return -ENOMEM;
+
+ itd_sched_init(fotg210, sched, stream, urb);
+
+ if (urb->interval < 8)
+ num_itds = 1 + (sched->span + 7) / 8;
+ else
+ num_itds = urb->number_of_packets;
+
+ /* allocate/init ITDs */
+ spin_lock_irqsave(&fotg210->lock, flags);
+ for (i = 0; i < num_itds; i++) {
+
+ /*
+ * Use iTDs from the free list, but not iTDs that may
+ * still be in use by the hardware.
+ */
+ if (likely(!list_empty(&stream->free_list))) {
+ itd = list_first_entry(&stream->free_list,
+ struct fotg210_itd, itd_list);
+ if (itd->frame == fotg210->now_frame)
+ goto alloc_itd;
+ list_del(&itd->itd_list);
+ itd_dma = itd->itd_dma;
+ } else {
+ alloc_itd:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ itd = dma_pool_alloc(fotg210->itd_pool, mem_flags,
+ &itd_dma);
+ spin_lock_irqsave(&fotg210->lock, flags);
+ if (!itd) {
+ iso_sched_free(stream, sched);
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return -ENOMEM;
+ }
+ }
+
+ memset(itd, 0, sizeof(*itd));
+ itd->itd_dma = itd_dma;
+ list_add(&itd->itd_list, &sched->td_list);
+ }
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = sched;
+ urb->error_count = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+itd_slot_ok(
+ struct fotg210_hcd *fotg210,
+ u32 mod,
+ u32 uframe,
+ u8 usecs,
+ u32 period
+)
+{
+ uframe %= period;
+ do {
+ /* can't commit more than uframe_periodic_max usec */
+ if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7)
+ > (fotg210->uframe_periodic_max - usecs))
+ return 0;
+
+ /* we know urb->interval is 2^N uframes */
+ uframe += period;
+ } while (uframe < mod);
+ return 1;
+}
+
+/*
+ * This scheduler plans almost as far into the future as it has actual
+ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
+ * "as small as possible" to be cache-friendlier.) That limits the size
+ * transfers you can stream reliably; avoid more than 64 msec per urb.
+ * Also avoid queue depths of less than fotg210's worst irq latency (affected
+ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
+ * and other factors); or more than about 230 msec total (for portability,
+ * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler!
+ */
+
+#define SCHEDULE_SLOP 80 /* microframes */
+
+static int
+iso_stream_schedule(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ struct fotg210_iso_stream *stream
+)
+{
+ u32 now, next, start, period, span;
+ int status;
+ unsigned mod = fotg210->periodic_size << 3;
+ struct fotg210_iso_sched *sched = urb->hcpriv;
+
+ period = urb->interval;
+ span = sched->span;
+
+ if (span > mod - SCHEDULE_SLOP) {
+ fotg210_dbg(fotg210, "iso request %p too long\n", urb);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ now = fotg210_read_frame_index(fotg210) & (mod - 1);
+
+ /* Typical case: reuse current schedule, stream is still active.
+ * Hopefully there are no gaps from the host falling behind
+ * (irq delays etc), but if there are we'll take the next
+ * slot in the schedule, implicitly assuming URB_ISO_ASAP.
+ */
+ if (likely(!list_empty(&stream->td_list))) {
+ u32 excess;
+
+ /* For high speed devices, allow scheduling within the
+ * isochronous scheduling threshold. For full speed devices
+ * and Intel PCI-based controllers, don't (work around for
+ * Intel ICH9 bug).
+ */
+ if (!stream->highspeed && fotg210->fs_i_thresh)
+ next = now + fotg210->i_thresh;
+ else
+ next = now;
+
+ /* Fell behind (by up to twice the slop amount)?
+ * We decide based on the time of the last currently-scheduled
+ * slot, not the time of the next available slot.
+ */
+ excess = (stream->next_uframe - period - next) & (mod - 1);
+ if (excess >= mod - 2 * SCHEDULE_SLOP)
+ start = next + excess - mod + period *
+ DIV_ROUND_UP(mod - excess, period);
+ else
+ start = next + excess + period;
+ if (start - now >= mod) {
+ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now - period, period,
+ mod);
+ status = -EFBIG;
+ goto fail;
+ }
+ }
+
+ /* need to schedule; when's the next (u)frame we could start?
+ * this is bigger than fotg210->i_thresh allows; scheduling itself
+ * isn't free, the slop should handle reasonably slow cpus. it
+ * can also help high bandwidth if the dma and irq loads don't
+ * jump until after the queue is primed.
+ */
+ else {
+ int done = 0;
+ start = SCHEDULE_SLOP + (now & ~0x07);
+
+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+ /* find a uframe slot with enough bandwidth.
+ * Early uframes are more precious because full-speed
+ * iso IN transfers can't use late uframes,
+ * and therefore they should be allocated last.
+ */
+ next = start;
+ start += period;
+ do {
+ start--;
+ /* check schedule: enough space? */
+ if (itd_slot_ok(fotg210, mod, start,
+ stream->usecs, period))
+ done = 1;
+ } while (start > next && !done);
+
+ /* no room in the schedule */
+ if (!done) {
+ fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n",
+ urb, now, now + mod);
+ status = -ENOSPC;
+ goto fail;
+ }
+ }
+
+ /* Tried to schedule too far into the future? */
+ if (unlikely(start - now + span - period
+ >= mod - 2 * SCHEDULE_SLOP)) {
+ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now, span - period,
+ mod - 2 * SCHEDULE_SLOP);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ stream->next_uframe = start & (mod - 1);
+
+ /* report high speed start in uframes; full speed, in frames */
+ urb->start_frame = stream->next_uframe;
+ if (!stream->highspeed)
+ urb->start_frame >>= 3;
+
+ /* Make sure scan_isoc() sees these */
+ if (fotg210->isoc_count == 0)
+ fotg210->next_frame = now >> 3;
+ return 0;
+
+ fail:
+ iso_sched_free(stream, sched);
+ urb->hcpriv = NULL;
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream,
+ struct fotg210_itd *itd)
+{
+ int i;
+
+ /* it's been recently zeroed */
+ itd->hw_next = FOTG210_LIST_END(fotg210);
+ itd->hw_bufp[0] = stream->buf0;
+ itd->hw_bufp[1] = stream->buf1;
+ itd->hw_bufp[2] = stream->buf2;
+
+ for (i = 0; i < 8; i++)
+ itd->index[i] = -1;
+
+ /* All other fields are filled when scheduling */
+}
+
+static inline void
+itd_patch(
+ struct fotg210_hcd *fotg210,
+ struct fotg210_itd *itd,
+ struct fotg210_iso_sched *iso_sched,
+ unsigned index,
+ u16 uframe
+)
+{
+ struct fotg210_iso_packet *uf = &iso_sched->packet[index];
+ unsigned pg = itd->pg;
+
+ uframe &= 0x07;
+ itd->index[uframe] = index;
+
+ itd->hw_transaction[uframe] = uf->transaction;
+ itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12);
+ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32));
+
+ /* iso_frame_desc[].offset must be strictly increasing */
+ if (unlikely(uf->cross)) {
+ u64 bufp = uf->bufp + 4096;
+
+ itd->pg = ++pg;
+ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32));
+ }
+}
+
+static inline void
+itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd)
+{
+ union fotg210_shadow *prev = &fotg210->pshadow[frame];
+ __hc32 *hw_p = &fotg210->periodic[frame];
+ union fotg210_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip any iso nodes which might belong to previous microframes */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(fotg210, *hw_p);
+ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(fotg210, prev, type);
+ hw_p = shadow_next_periodic(fotg210, &here, type);
+ here = *prev;
+ }
+
+ itd->itd_next = here;
+ itd->hw_next = *hw_p;
+ prev->itd = itd;
+ itd->frame = frame;
+ wmb();
+ *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD);
+}
+
+/* fit urb's itds into the selected schedule slot; activate as needed */
+static void itd_link_urb(
+ struct fotg210_hcd *fotg210,
+ struct urb *urb,
+ unsigned mod,
+ struct fotg210_iso_stream *stream
+)
+{
+ int packet;
+ unsigned next_uframe, uframe, frame;
+ struct fotg210_iso_sched *iso_sched = urb->hcpriv;
+ struct fotg210_itd *itd;
+
+ next_uframe = stream->next_uframe & (mod - 1);
+
+ if (unlikely(list_empty(&stream->td_list))) {
+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated
+ += stream->bandwidth;
+ fotg210_dbg(fotg210,
+ "schedule devp %s ep%d%s-iso period %d start %d.%d\n",
+ urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ urb->interval,
+ next_uframe >> 3, next_uframe & 0x7);
+ }
+
+ /* fill iTDs uframe by uframe */
+ for (packet = 0, itd = NULL; packet < urb->number_of_packets;) {
+ if (itd == NULL) {
+ /* ASSERT: we have all necessary itds */
+
+ /* ASSERT: no itds for this endpoint in this uframe */
+
+ itd = list_entry(iso_sched->td_list.next,
+ struct fotg210_itd, itd_list);
+ list_move_tail(&itd->itd_list, &stream->td_list);
+ itd->stream = stream;
+ itd->urb = urb;
+ itd_init(fotg210, stream, itd);
+ }
+
+ uframe = next_uframe & 0x07;
+ frame = next_uframe >> 3;
+
+ itd_patch(fotg210, itd, iso_sched, packet, uframe);
+
+ next_uframe += stream->interval;
+ next_uframe &= mod - 1;
+ packet++;
+
+ /* link completed itds into the schedule */
+ if (((next_uframe >> 3) != frame)
+ || packet == urb->number_of_packets) {
+ itd_link(fotg210, frame & (fotg210->periodic_size - 1),
+ itd);
+ itd = NULL;
+ }
+ }
+ stream->next_uframe = next_uframe;
+
+ /* don't need that schedule data any more */
+ iso_sched_free(stream, iso_sched);
+ urb->hcpriv = NULL;
+
+ ++fotg210->isoc_count;
+ enable_periodic(fotg210);
+}
+
+#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\
+ FOTG210_ISOC_XACTERR)
+
+/* Process and recycle a completed ITD. Return true iff its urb completed,
+ * and hence its completion callback probably added things to the hardware
+ * schedule.
+ *
+ * Note that we carefully avoid recycling this descriptor until after any
+ * completion callback runs, so that it won't be reused quickly. That is,
+ * assuming (a) no more than two urbs per frame on this endpoint, and also
+ * (b) only this endpoint's completions submit URBs. It seems some silicon
+ * corrupts things if you reuse completed descriptors very quickly...
+ */
+static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd)
+{
+ struct urb *urb = itd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ unsigned uframe;
+ int urb_index = -1;
+ struct fotg210_iso_stream *stream = itd->stream;
+ struct usb_device *dev;
+ bool retval = false;
+
+ /* for each uframe with a packet */
+ for (uframe = 0; uframe < 8; uframe++) {
+ if (likely(itd->index[uframe] == -1))
+ continue;
+ urb_index = itd->index[uframe];
+ desc = &urb->iso_frame_desc[urb_index];
+
+ t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]);
+ itd->hw_transaction[uframe] = 0;
+
+ /* report transfer status */
+ if (unlikely(t & ISO_ERRS)) {
+ urb->error_count++;
+ if (t & FOTG210_ISOC_BUF_ERR)
+ desc->status = usb_pipein(urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & FOTG210_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & FOTG210_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+
+ /* HC need not update length with this error */
+ if (!(t & FOTG210_ISOC_BABBLE)) {
+ desc->actual_length =
+ fotg210_itdlen(urb, desc, t);
+ urb->actual_length += desc->actual_length;
+ }
+ } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) {
+ desc->status = 0;
+ desc->actual_length = fotg210_itdlen(urb, desc, t);
+ urb->actual_length += desc->actual_length;
+ } else {
+ /* URB was too late */
+ desc->status = -EXDEV;
+ }
+ }
+
+ /* handle completion now? */
+ if (likely((urb_index + 1) != urb->number_of_packets))
+ goto done;
+
+ /* ASSERT: it's really the last itd for this urb
+ list_for_each_entry (itd, &stream->td_list, itd_list)
+ BUG_ON (itd->urb == urb);
+ */
+
+ /* give urb back to the driver; completion often (re)submits */
+ dev = urb->dev;
+ fotg210_urb_done(fotg210, urb, 0);
+ retval = true;
+ urb = NULL;
+
+ --fotg210->isoc_count;
+ disable_periodic(fotg210);
+
+ if (unlikely(list_is_singular(&stream->td_list))) {
+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated
+ -= stream->bandwidth;
+ fotg210_dbg(fotg210,
+ "deschedule devp %s ep%d%s-iso\n",
+ dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+ }
+
+done:
+ itd->urb = NULL;
+
+ /* Add to the end of the free list for later reuse */
+ list_move_tail(&itd->itd_list, &stream->free_list);
+
+ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */
+ if (list_empty(&stream->td_list)) {
+ list_splice_tail_init(&stream->free_list,
+ &fotg210->cached_itd_list);
+ start_free_itds(fotg210);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct fotg210_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find(fotg210, urb);
+ if (unlikely(stream == NULL)) {
+ fotg210_dbg(fotg210, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (unlikely(urb->interval != stream->interval &&
+ fotg210_port_speed(fotg210, 0) ==
+ USB_PORT_STAT_HIGH_SPEED)) {
+ fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n",
+ stream->interval, urb->interval);
+ goto done;
+ }
+
+#ifdef FOTG210_URB_TRACE
+ fotg210_dbg(fotg210,
+ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length,
+ urb->number_of_packets, urb->interval,
+ stream);
+#endif
+
+ /* allocate ITDs w/o locking anything */
+ status = itd_urb_transaction(stream, fotg210, urb, mem_flags);
+ if (unlikely(status < 0)) {
+ fotg210_dbg(fotg210, "can't init itds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave(&fotg210->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+ status = iso_stream_schedule(fotg210, urb, stream);
+ if (likely(status == 0))
+ itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream);
+ else
+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
+ done_not_linked:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ done:
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_isoc(struct fotg210_hcd *fotg210)
+{
+ unsigned uf, now_frame, frame;
+ unsigned fmask = fotg210->periodic_size - 1;
+ bool modified, live;
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
+ * Touches as few pages as possible: cache-friendly.
+ */
+ if (fotg210->rh_state >= FOTG210_RH_RUNNING) {
+ uf = fotg210_read_frame_index(fotg210);
+ now_frame = (uf >> 3) & fmask;
+ live = true;
+ } else {
+ now_frame = (fotg210->next_frame - 1) & fmask;
+ live = false;
+ }
+ fotg210->now_frame = now_frame;
+
+ frame = fotg210->next_frame;
+ for (;;) {
+ union fotg210_shadow q, *q_p;
+ __hc32 type, *hw_p;
+
+restart:
+ /* scan each element in frame's queue for completions */
+ q_p = &fotg210->pshadow[frame];
+ hw_p = &fotg210->periodic[frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE(fotg210, *hw_p);
+ modified = false;
+
+ while (q.ptr != NULL) {
+ switch (hc32_to_cpu(fotg210, type)) {
+ case Q_TYPE_ITD:
+ /* If this ITD is still active, leave it for
+ * later processing ... check the next entry.
+ * No need to check for activity unless the
+ * frame is current.
+ */
+ if (frame == now_frame && live) {
+ rmb();
+ for (uf = 0; uf < 8; uf++) {
+ if (q.itd->hw_transaction[uf] &
+ ITD_ACTIVE(fotg210))
+ break;
+ }
+ if (uf < 8) {
+ q_p = &q.itd->itd_next;
+ hw_p = &q.itd->hw_next;
+ type = Q_NEXT_TYPE(fotg210,
+ q.itd->hw_next);
+ q = *q_p;
+ break;
+ }
+ }
+
+ /* Take finished ITDs out of the schedule
+ * and process them: recycle, maybe report
+ * URB completion. HC won't cache the
+ * pointer for much longer, if at all.
+ */
+ *q_p = q.itd->itd_next;
+ *hw_p = q.itd->hw_next;
+ type = Q_NEXT_TYPE(fotg210, q.itd->hw_next);
+ wmb();
+ modified = itd_complete(fotg210, q.itd);
+ q = *q_p;
+ break;
+ default:
+ fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n",
+ type, frame, q.ptr);
+ /* FALL THROUGH */
+ case Q_TYPE_QH:
+ case Q_TYPE_FSTN:
+ /* End of the iTDs and siTDs */
+ q.ptr = NULL;
+ break;
+ }
+
+ /* assume completion callbacks modify the queue */
+ if (unlikely(modified && fotg210->isoc_count > 0))
+ goto restart;
+ }
+
+ /* Stop when we have reached the current frame */
+ if (frame == now_frame)
+ break;
+ frame = (frame + 1) & fmask;
+ }
+ fotg210->next_frame = now_frame;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * Display / Set uframe_periodic_max
+ */
+static ssize_t show_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fotg210_hcd *fotg210;
+ int n;
+
+ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev)));
+ n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max);
+ return n;
+}
+
+
+static ssize_t store_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fotg210_hcd *fotg210;
+ unsigned uframe_periodic_max;
+ unsigned frame, uframe;
+ unsigned short allocated_max;
+ unsigned long flags;
+ ssize_t ret;
+
+ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev)));
+ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
+ return -EINVAL;
+
+ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
+ fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n",
+ uframe_periodic_max);
+ return -EINVAL;
+ }
+
+ ret = -EINVAL;
+
+ /*
+ * lock, so that our checking does not race with possible periodic
+ * bandwidth allocation through submitting new urbs.
+ */
+ spin_lock_irqsave(&fotg210->lock, flags);
+
+ /*
+ * for request to decrease max periodic bandwidth, we have to check
+ * every microframe in the schedule to see whether the decrease is
+ * possible.
+ */
+ if (uframe_periodic_max < fotg210->uframe_periodic_max) {
+ allocated_max = 0;
+
+ for (frame = 0; frame < fotg210->periodic_size; ++frame)
+ for (uframe = 0; uframe < 7; ++uframe)
+ allocated_max = max(allocated_max,
+ periodic_usecs(fotg210, frame, uframe));
+
+ if (allocated_max > uframe_periodic_max) {
+ fotg210_info(fotg210,
+ "cannot decrease uframe_periodic_max because "
+ "periodic bandwidth is already allocated "
+ "(%u > %u)\n",
+ allocated_max, uframe_periodic_max);
+ goto out_unlock;
+ }
+ }
+
+ /* increasing is always ok */
+
+ fotg210_info(fotg210, "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n",
+ 100 * uframe_periodic_max/125, uframe_periodic_max);
+
+ if (uframe_periodic_max != 100)
+ fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n");
+
+ fotg210->uframe_periodic_max = uframe_periodic_max;
+ ret = count;
+
+out_unlock:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return ret;
+}
+
+static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max,
+ store_uframe_periodic_max);
+
+static inline int create_sysfs_files(struct fotg210_hcd *fotg210)
+{
+ struct device *controller = fotg210_to_hcd(fotg210)->self.controller;
+ int i = 0;
+
+ if (i)
+ goto out;
+
+ i = device_create_file(controller, &dev_attr_uframe_periodic_max);
+out:
+ return i;
+}
+
+static inline void remove_sysfs_files(struct fotg210_hcd *fotg210)
+{
+ struct device *controller = fotg210_to_hcd(fotg210)->self.controller;
+
+ device_remove_file(controller, &dev_attr_uframe_periodic_max);
+}
+/*-------------------------------------------------------------------------*/
+
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210)
+{
+ u32 __iomem *status_reg = &fotg210->regs->port_status;
+
+ fotg210_writel(fotg210, PORT_RWC_BITS, status_reg);
+}
+
+/*
+ * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void fotg210_silence_controller(struct fotg210_hcd *fotg210)
+{
+ fotg210_halt(fotg210);
+
+ spin_lock_irq(&fotg210->lock);
+ fotg210->rh_state = FOTG210_RH_HALTED;
+ fotg210_turn_off_all_ports(fotg210);
+ spin_unlock_irq(&fotg210->lock);
+}
+
+/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc).
+ * This forcibly disables dma and IRQs, helping kexec and other cases
+ * where the next system software may expect clean state.
+ */
+static void fotg210_shutdown(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+
+ spin_lock_irq(&fotg210->lock);
+ fotg210->shutdown = true;
+ fotg210->rh_state = FOTG210_RH_STOPPING;
+ fotg210->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&fotg210->lock);
+
+ fotg210_silence_controller(fotg210);
+
+ hrtimer_cancel(&fotg210->hrtimer);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * fotg210_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping fotg210->lock.
+ */
+static void fotg210_work(struct fotg210_hcd *fotg210)
+{
+ /* another CPU may drop fotg210->lock during a schedule scan while
+ * it reports urb completions. this flag guards against bogus
+ * attempts at re-entrant schedule scanning.
+ */
+ if (fotg210->scanning) {
+ fotg210->need_rescan = true;
+ return;
+ }
+ fotg210->scanning = true;
+
+ rescan:
+ fotg210->need_rescan = false;
+ if (fotg210->async_count)
+ scan_async(fotg210);
+ if (fotg210->intr_count > 0)
+ scan_intr(fotg210);
+ if (fotg210->isoc_count > 0)
+ scan_isoc(fotg210);
+ if (fotg210->need_rescan)
+ goto rescan;
+ fotg210->scanning = false;
+
+ /* the IO watchdog guards against hardware or driver bugs that
+ * misplace IRQs, and should let us run completely without IRQs.
+ * such lossage has been observed on both VT6202 and VT8235.
+ */
+ turn_on_io_watchdog(fotg210);
+}
+
+/*
+ * Called when the fotg210_hcd module is removed.
+ */
+static void fotg210_stop(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+
+ fotg210_dbg(fotg210, "stop\n");
+
+ /* no more interrupts ... */
+
+ spin_lock_irq(&fotg210->lock);
+ fotg210->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&fotg210->lock);
+
+ fotg210_quiesce(fotg210);
+ fotg210_silence_controller(fotg210);
+ fotg210_reset(fotg210);
+
+ hrtimer_cancel(&fotg210->hrtimer);
+ remove_sysfs_files(fotg210);
+ remove_debug_files(fotg210);
+
+ /* root hub is shut down separately (first, when possible) */
+ spin_lock_irq(&fotg210->lock);
+ end_free_itds(fotg210);
+ spin_unlock_irq(&fotg210->lock);
+ fotg210_mem_cleanup(fotg210);
+
+#ifdef FOTG210_STATS
+ fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+ fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa,
+ fotg210->stats.lost_iaa);
+ fotg210_dbg(fotg210, "complete %ld unlink %ld\n",
+ fotg210->stats.complete, fotg210->stats.unlink);
+#endif
+
+ dbg_status(fotg210, "fotg210_stop completed",
+ fotg210_readl(fotg210, &fotg210->regs->status));
+}
+
+/* one-time init, only for memory state */
+static int hcd_fotg210_init(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ u32 temp;
+ int retval;
+ u32 hcc_params;
+ struct fotg210_qh_hw *hw;
+
+ spin_lock_init(&fotg210->lock);
+
+ /*
+ * keep io watchdog by default, those good HCDs could turn off it later
+ */
+ fotg210->need_io_watchdog = 1;
+
+ hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ fotg210->hrtimer.function = fotg210_hrtimer_func;
+ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT;
+
+ hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
+
+ /*
+ * by default set standard 80% (== 100 usec/uframe) max periodic
+ * bandwidth as required by USB 2.0
+ */
+ fotg210->uframe_periodic_max = 100;
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ fotg210->periodic_size = DEFAULT_I_TDPS;
+ INIT_LIST_HEAD(&fotg210->intr_qh_list);
+ INIT_LIST_HEAD(&fotg210->cached_itd_list);
+
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ switch (FOTG210_TUNE_FLS) {
+ case 0:
+ fotg210->periodic_size = 1024;
+ break;
+ case 1:
+ fotg210->periodic_size = 512;
+ break;
+ case 2:
+ fotg210->periodic_size = 256;
+ break;
+ default:
+ BUG();
+ }
+ }
+ retval = fotg210_mem_init(fotg210, GFP_KERNEL);
+ if (retval < 0)
+ return retval;
+
+ /* controllers may cache some of the periodic schedule ... */
+ fotg210->i_thresh = 2;
+
+ /*
+ * dedicate a qh for the async ring head, since we couldn't unlink
+ * a 'real' qh without stopping the async schedule [4.8]. use it
+ * as the 'reclamation list head' too.
+ * its dummy is used in hw_alt_next of many tds, to prevent the qh
+ * from automatically advancing to the next td after short reads.
+ */
+ fotg210->async->qh_next.qh = NULL;
+ hw = fotg210->async->hw;
+ hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma);
+ hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD);
+ hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT);
+ hw->hw_qtd_next = FOTG210_LIST_END(fotg210);
+ fotg210->async->qh_state = QH_STATE_LINKED;
+ hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma);
+
+ /* clear interrupt enables, set irq latency */
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_CANPARK(hcc_params)) {
+ /* HW default park == 3, on hardware that supports it (like
+ * NVidia and ALI silicon), maximizes throughput on the async
+ * schedule by avoiding QH fetches between transfers.
+ *
+ * With fast usb storage devices and NForce2, "park" seems to
+ * make problems: throughput reduction (!), data errors...
+ */
+ if (park) {
+ park = min_t(unsigned, park, 3);
+ temp |= CMD_PARK;
+ temp |= park << 8;
+ }
+ fotg210_dbg(fotg210, "park %d\n", park);
+ }
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (FOTG210_TUNE_FLS << 2);
+ }
+ fotg210->command = temp;
+
+ /* Accept arbitrarily long scatter-gather lists */
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
+ return 0;
+}
+
+/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */
+static int fotg210_run(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ u32 temp;
+ u32 hcc_params;
+
+ hcd->uses_new_polling = 1;
+
+ /* EHCI spec section 4.1 */
+
+ fotg210_writel(fotg210, fotg210->periodic_dma,
+ &fotg210->regs->frame_list);
+ fotg210_writel(fotg210, (u32)fotg210->async->qh_dma,
+ &fotg210->regs->async_next);
+
+ /*
+ * hcc_params controls whether fotg210->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * pci_pool consistent memory always uses segment zero.
+ * streaming mappings for I/O buffers, like pci_map_single(),
+ * can return segments above 4GB, if the device allows.
+ *
+ * NOTE: the dma mask is visible through dma_supported(), so
+ * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+ * Scsi_Host.highmem_io, and so forth. It's readonly to all
+ * host side drivers though.
+ */
+ hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
+
+ /*
+ * Philips, Intel, and maybe others need CMD_RUN before the
+ * root hub will detect new devices (why?); NEC doesn't
+ */
+ fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+ fotg210->command |= CMD_RUN;
+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
+ dbg_cmd(fotg210, "init", fotg210->command);
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub. (Except where one is integrated,
+ * and there's no companion controller unless maybe for USB OTG.)
+ *
+ * Turning on the CF flag will transfer ownership of all ports
+ * from the companions to the EHCI controller. If any of the
+ * companions are in the middle of a port reset at the time, it
+ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem
+ * guarantees that no resets are in progress. After we set CF,
+ * a short delay lets the hardware catch up; new resets shouldn't
+ * be started before the port switching actions could complete.
+ */
+ down_write(&ehci_cf_port_reset_rwsem);
+ fotg210->rh_state = FOTG210_RH_RUNNING;
+ /* unblock posted writes */
+ fotg210_readl(fotg210, &fotg210->regs->command);
+ msleep(5);
+ up_write(&ehci_cf_port_reset_rwsem);
+ fotg210->last_periodic_enable = ktime_get_real();
+
+ temp = HC_VERSION(fotg210,
+ fotg210_readl(fotg210, &fotg210->caps->hc_capbase));
+ fotg210_info(fotg210,
+ "USB %x.%x started, EHCI %x.%02x\n",
+ ((fotg210->sbrn & 0xf0)>>4), (fotg210->sbrn & 0x0f),
+ temp >> 8, temp & 0xff);
+
+ fotg210_writel(fotg210, INTR_MASK,
+ &fotg210->regs->intr_enable); /* Turn On Interrupts */
+
+ /* GRR this is run-once init(), being done every time the HC starts.
+ * So long as they're part of class devices, we can't do it init()
+ * since the class device isn't created that early.
+ */
+ create_debug_files(fotg210);
+ create_sysfs_files(fotg210);
+
+ return 0;
+}
+
+static int fotg210_setup(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ int retval;
+
+ fotg210->regs = (void __iomem *)fotg210->caps +
+ HC_LENGTH(fotg210,
+ fotg210_readl(fotg210, &fotg210->caps->hc_capbase));
+ dbg_hcs_params(fotg210, "reset");
+ dbg_hcc_params(fotg210, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ fotg210->hcs_params = fotg210_readl(fotg210,
+ &fotg210->caps->hcs_params);
+
+ fotg210->sbrn = HCD_USB2;
+
+ /* data structure init */
+ retval = hcd_fotg210_init(hcd);
+ if (retval)
+ return retval;
+
+ retval = fotg210_halt(fotg210);
+ if (retval)
+ return retval;
+
+ fotg210_reset(fotg210);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t fotg210_irq(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ u32 status, masked_status, pcd_status = 0, cmd;
+ int bh;
+
+ spin_lock(&fotg210->lock);
+
+ status = fotg210_readl(fotg210, &fotg210->regs->status);
+
+ /* e.g. cardbus physical eject */
+ if (status == ~(u32) 0) {
+ fotg210_dbg(fotg210, "device removed\n");
+ goto dead;
+ }
+
+ /*
+ * We don't use STS_FLR, but some controllers don't like it to
+ * remain on, so mask it out along with the other status bits.
+ */
+ masked_status = status & (INTR_MASK | STS_FLR);
+
+ /* Shared IRQ? */
+ if (!masked_status ||
+ unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) {
+ spin_unlock(&fotg210->lock);
+ return IRQ_NONE;
+ }
+
+ /* clear (just) interrupts */
+ fotg210_writel(fotg210, masked_status, &fotg210->regs->status);
+ cmd = fotg210_readl(fotg210, &fotg210->regs->command);
+ bh = 0;
+
+ /* unrequested/ignored: Frame List Rollover */
+ dbg_status(fotg210, "irq", status);
+
+ /* INT, ERR, and IAA interrupt rates can be throttled */
+
+ /* normal [4.15.1.2] or error [4.15.1.1] completion */
+ if (likely((status & (STS_INT|STS_ERR)) != 0)) {
+ if (likely((status & STS_ERR) == 0))
+ COUNT(fotg210->stats.normal);
+ else
+ COUNT(fotg210->stats.error);
+ bh = 1;
+ }
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+
+ /* Turn off the IAA watchdog */
+ fotg210->enabled_hrtimer_events &=
+ ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG);
+
+ /*
+ * Mild optimization: Allow another IAAD to reset the
+ * hrtimer, if one occurs before the next expiration.
+ * In theory we could always cancel the hrtimer, but
+ * tests show that about half the time it will be reset
+ * for some other event anyway.
+ */
+ if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG)
+ ++fotg210->next_hrtimer_event;
+
+ /* guard against (alleged) silicon errata */
+ if (cmd & CMD_IAAD)
+ fotg210_dbg(fotg210, "IAA with IAAD still set?\n");
+ if (fotg210->async_iaa) {
+ COUNT(fotg210->stats.iaa);
+ end_unlink_async(fotg210);
+ } else
+ fotg210_dbg(fotg210, "IAA with nothing unlinked?\n");
+ }
+
+ /* remote wakeup [4.3.1] */
+ if (status & STS_PCD) {
+ int pstatus;
+ u32 __iomem *status_reg = &fotg210->regs->port_status;
+
+ /* kick root hub later */
+ pcd_status = status;
+
+ /* resume root hub? */
+ if (fotg210->rh_state == FOTG210_RH_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+
+ pstatus = fotg210_readl(fotg210, status_reg);
+
+ if (test_bit(0, &fotg210->suspended_ports) &&
+ ((pstatus & PORT_RESUME) ||
+ !(pstatus & PORT_SUSPEND)) &&
+ (pstatus & PORT_PE) &&
+ fotg210->reset_done[0] == 0) {
+
+ /* start 20 msec resume signaling from this port,
+ * and make hub_wq collect PORT_STAT_C_SUSPEND to
+ * stop that signaling. Use 5 ms extra for safety,
+ * like usb_port_resume() does.
+ */
+ fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25);
+ set_bit(0, &fotg210->resuming_ports);
+ fotg210_dbg(fotg210, "port 1 remote wakeup\n");
+ mod_timer(&hcd->rh_timer, fotg210->reset_done[0]);
+ }
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely((status & STS_FATAL) != 0)) {
+ fotg210_err(fotg210, "fatal error\n");
+ dbg_cmd(fotg210, "fatal", cmd);
+ dbg_status(fotg210, "fatal", status);
+dead:
+ usb_hc_died(hcd);
+
+ /* Don't let the controller do anything more */
+ fotg210->shutdown = true;
+ fotg210->rh_state = FOTG210_RH_STOPPING;
+ fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
+ fotg210_writel(fotg210, fotg210->command,
+ &fotg210->regs->command);
+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable);
+ fotg210_handle_controller_death(fotg210);
+
+ /* Handle completions when the controller stops */
+ bh = 0;
+ }
+
+ if (bh)
+ fotg210_work(fotg210);
+ spin_unlock(&fotg210->lock);
+ if (pcd_status)
+ usb_hcd_poll_rh_status(hcd);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd.self.controller.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int fotg210_urb_enqueue(
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
+) {
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ struct list_head qtd_list;
+
+ INIT_LIST_HEAD(&qtd_list);
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ /* qh_completions() code doesn't handle all the fault cases
+ * in multi-TD control transfers. Even 1KB is rare anyway.
+ */
+ if (urb->transfer_buffer_length > (16 * 1024))
+ return -EMSGSIZE;
+ /* FALLTHROUGH */
+ /* case PIPE_BULK: */
+ default:
+ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return submit_async(fotg210, urb, &qtd_list, mem_flags);
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit(fotg210, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+ return itd_submit(fotg210, urb, mem_flags);
+ }
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ struct fotg210_qh *qh;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ switch (usb_pipetype(urb->pipe)) {
+ /* case PIPE_CONTROL: */
+ /* case PIPE_BULK:*/
+ default:
+ qh = (struct fotg210_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ start_unlink_async(fotg210, qh);
+ break;
+ case QH_STATE_UNLINK:
+ case QH_STATE_UNLINK_WAIT:
+ /* already started */
+ break;
+ case QH_STATE_IDLE:
+ /* QH might be waiting for a Clear-TT-Buffer */
+ qh_completions(fotg210, qh);
+ break;
+ }
+ break;
+
+ case PIPE_INTERRUPT:
+ qh = (struct fotg210_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ start_unlink_intr(fotg210, qh);
+ break;
+ case QH_STATE_IDLE:
+ qh_completions(fotg210, qh);
+ break;
+ default:
+ fotg210_dbg(fotg210, "bogus qh %p state %d\n",
+ qh, qh->qh_state);
+ goto done;
+ }
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ /* itd... */
+
+ /* wait till next completion, do it then. */
+ /* completion irqs can wait up to 1024 msec, */
+ break;
+ }
+done:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* bulk qh holds the data toggle */
+
+static void
+fotg210_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ unsigned long flags;
+ struct fotg210_qh *qh, *tmp;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+rescan:
+ spin_lock_irqsave(&fotg210->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh)
+ goto done;
+
+ /* endpoints can be iso streams. for now, we don't
+ * accelerate iso completions ... so spin a while.
+ */
+ if (qh->hw == NULL) {
+ struct fotg210_iso_stream *stream = ep->hcpriv;
+
+ if (!list_empty(&stream->td_list))
+ goto idle_timeout;
+
+ /* BUG_ON(!list_empty(&stream->free_list)); */
+ kfree(stream);
+ goto done;
+ }
+
+ if (fotg210->rh_state < FOTG210_RH_RUNNING)
+ qh->qh_state = QH_STATE_IDLE;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ for (tmp = fotg210->async->qh_next.qh;
+ tmp && tmp != qh;
+ tmp = tmp->qh_next.qh)
+ continue;
+ /* periodic qh self-unlinks on empty, and a COMPLETING qh
+ * may already be unlinked.
+ */
+ if (tmp)
+ start_unlink_async(fotg210, qh);
+ /* FALL THROUGH */
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+ case QH_STATE_UNLINK_WAIT:
+idle_timeout:
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ case QH_STATE_IDLE: /* fully unlinked */
+ if (qh->clearing_tt)
+ goto idle_timeout;
+ if (list_empty(&qh->qtd_list)) {
+ qh_destroy(fotg210, qh);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty(&qh->qtd_list) ? "" : "(has tds)");
+ break;
+ }
+ done:
+ ep->hcpriv = NULL;
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+}
+
+static void
+fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ struct fotg210_qh *qh;
+ int eptype = usb_endpoint_type(&ep->desc);
+ int epnum = usb_endpoint_num(&ep->desc);
+ int is_out = usb_endpoint_dir_out(&ep->desc);
+ unsigned long flags;
+
+ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
+ return;
+
+ spin_lock_irqsave(&fotg210->lock, flags);
+ 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) {
+ usb_settoggle(qh->dev, epnum, is_out, 0);
+ if (!list_empty(&qh->qtd_list)) {
+ WARN_ONCE(1, "clear_halt for a busy endpoint\n");
+ } else if (qh->qh_state == QH_STATE_LINKED ||
+ qh->qh_state == QH_STATE_COMPLETING) {
+
+ /* The toggle value in the QH can't be updated
+ * while the QH is active. Unlink it now;
+ * re-linking will call qh_refresh().
+ */
+ if (eptype == USB_ENDPOINT_XFER_BULK)
+ start_unlink_async(fotg210, qh);
+ else
+ start_unlink_intr(fotg210, qh);
+ }
+ }
+ spin_unlock_irqrestore(&fotg210->lock, flags);
+}
+
+static int fotg210_get_frame(struct usb_hcd *hcd)
+{
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
+ return (fotg210_read_frame_index(fotg210) >> 3) %
+ fotg210->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The EHCI in ChipIdea HDRC cannot be a separate module or device,
+ * because its registers (and irq) are shared between host/gadget/otg
+ * functions and in order to facilitate role switching we cannot
+ * give the fotg210 driver exclusive access to those.
+ */
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static const struct hc_driver fotg210_fotg210_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Faraday USB2.0 Host Controller",
+ .hcd_priv_size = sizeof(struct fotg210_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = fotg210_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = hcd_fotg210_init,
+ .start = fotg210_run,
+ .stop = fotg210_stop,
+ .shutdown = fotg210_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = fotg210_urb_enqueue,
+ .urb_dequeue = fotg210_urb_dequeue,
+ .endpoint_disable = fotg210_endpoint_disable,
+ .endpoint_reset = fotg210_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = fotg210_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = fotg210_hub_status_data,
+ .hub_control = fotg210_hub_control,
+ .bus_suspend = fotg210_bus_suspend,
+ .bus_resume = fotg210_bus_resume,
+
+ .relinquish_port = fotg210_relinquish_port,
+ .port_handed_over = fotg210_port_handed_over,
+
+ .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete,
+};
+
+static void fotg210_init(struct fotg210_hcd *fotg210)
+{
+ u32 value;
+
+ iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY,
+ &fotg210->regs->gmir);
+
+ value = ioread32(&fotg210->regs->otgcsr);
+ value &= ~OTGCSR_A_BUS_DROP;
+ value |= OTGCSR_A_BUS_REQ;
+ iowrite32(value, &fotg210->regs->otgcsr);
+}
+
+/**
+ * fotg210_hcd_probe - initialize faraday FOTG210 HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int fotg210_hcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval = -ENODEV;
+ struct fotg210_hcd *fotg210;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(dev));
+ return -ENODEV;
+ }
+
+ irq = res->start;
+
+ hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "failed to create hcd with err %d\n", retval);
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ hcd->has_tt = 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto failed;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ fotg210 = hcd_to_fotg210(hcd);
+
+ fotg210->caps = hcd->regs;
+
+ retval = fotg210_setup(hcd);
+ if (retval)
+ goto failed;
+
+ fotg210_init(fotg210);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval) {
+ dev_err(dev, "failed to add hcd with err %d\n", retval);
+ goto failed;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return retval;
+
+failed:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval);
+ return retval;
+}
+
+/**
+ * fotg210_hcd_remove - shutdown processing for EHCI HCDs
+ * @dev: USB Host Controller being removed
+ *
+ */
+static int fotg210_hcd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ if (!hcd)
+ return 0;
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static struct platform_driver fotg210_hcd_driver = {
+ .driver = {
+ .name = "fotg210-hcd",
+ },
+ .probe = fotg210_hcd_probe,
+ .remove = fotg210_hcd_remove,
+};
+
+static int __init fotg210_hcd_init(void)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
+ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
+ pr_warn(KERN_WARNING "Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n",
+ hcd_name,
+ sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd),
+ sizeof(struct fotg210_itd));
+
+ fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root);
+ if (!fotg210_debug_root) {
+ retval = -ENOENT;
+ goto err_debug;
+ }
+
+ retval = platform_driver_register(&fotg210_hcd_driver);
+ if (retval < 0)
+ goto clean;
+ return retval;
+
+ platform_driver_unregister(&fotg210_hcd_driver);
+clean:
+ debugfs_remove(fotg210_debug_root);
+ fotg210_debug_root = NULL;
+err_debug:
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ return retval;
+}
+module_init(fotg210_hcd_init);
+
+static void __exit fotg210_hcd_cleanup(void)
+{
+ platform_driver_unregister(&fotg210_hcd_driver);
+ debugfs_remove(fotg210_debug_root);
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+}
+module_exit(fotg210_hcd_cleanup);
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
new file mode 100644
index 000000000..3bad17859
--- /dev/null
+++ b/drivers/usb/host/fotg210.h
@@ -0,0 +1,686 @@
+#ifndef __LINUX_FOTG210_H
+#define __LINUX_FOTG210_H
+
+#include <linux/usb/ehci-dbgp.h>
+
+/* definitions used for the EHCI driver */
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#define __hc32 __le32
+#define __hc16 __le16
+
+/* statistics can be kept for tuning/monitoring */
+struct fotg210_stats {
+ /* irq usage */
+ unsigned long normal;
+ unsigned long error;
+ unsigned long iaa;
+ unsigned long lost_iaa;
+
+ /* termination of urbs from core */
+ unsigned long complete;
+ unsigned long unlink;
+};
+
+/* fotg210_hcd->lock guards shared data against other CPUs:
+ * fotg210_hcd: async, unlink, periodic (and shadow), ...
+ * usb_host_endpoint: hcpriv
+ * fotg210_qh: qh_next, qtd_list
+ * fotg210_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */
+
+/*
+ * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the
+ * controller may be doing DMA. Lower values mean there's no DMA.
+ */
+enum fotg210_rh_state {
+ FOTG210_RH_HALTED,
+ FOTG210_RH_SUSPENDED,
+ FOTG210_RH_RUNNING,
+ FOTG210_RH_STOPPING
+};
+
+/*
+ * Timer events, ordered by increasing delay length.
+ * Always update event_delays_ns[] and event_handlers[] (defined in
+ * ehci-timer.c) in parallel with this list.
+ */
+enum fotg210_hrtimer_event {
+ FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */
+ FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
+ FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
+ FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
+ FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
+ FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
+ FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
+ FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
+ FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
+ FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
+ FOTG210_HRTIMER_NUM_EVENTS /* Must come last */
+};
+#define FOTG210_HRTIMER_NO_EVENT 99
+
+struct fotg210_hcd { /* one per controller */
+ /* timing support */
+ enum fotg210_hrtimer_event next_hrtimer_event;
+ unsigned enabled_hrtimer_events;
+ ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS];
+ struct hrtimer hrtimer;
+
+ int PSS_poll_count;
+ int ASS_poll_count;
+ int died_poll_count;
+
+ /* glue to PCI and HCD framework */
+ struct fotg210_caps __iomem *caps;
+ struct fotg210_regs __iomem *regs;
+ struct ehci_dbg_port __iomem *debug;
+
+ __u32 hcs_params; /* cached register copy */
+ spinlock_t lock;
+ enum fotg210_rh_state rh_state;
+
+ /* general schedule support */
+ bool scanning:1;
+ bool need_rescan:1;
+ bool intr_unlinking:1;
+ bool async_unlinking:1;
+ bool shutdown:1;
+ struct fotg210_qh *qh_scan_next;
+
+ /* async schedule support */
+ struct fotg210_qh *async;
+ struct fotg210_qh *dummy; /* For AMD quirk use */
+ struct fotg210_qh *async_unlink;
+ struct fotg210_qh *async_unlink_last;
+ struct fotg210_qh *async_iaa;
+ unsigned async_unlink_cycle;
+ unsigned async_count; /* async activity count */
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ __hc32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ struct list_head intr_qh_list;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union fotg210_shadow *pshadow; /* mirror hw periodic table */
+ struct fotg210_qh *intr_unlink;
+ struct fotg210_qh *intr_unlink_last;
+ unsigned intr_unlink_cycle;
+ unsigned now_frame; /* frame from HC hardware */
+ unsigned next_frame; /* scan periodic, start here */
+ unsigned intr_count; /* intr activity count */
+ unsigned isoc_count; /* isoc activity count */
+ unsigned periodic_count; /* periodic activity count */
+ /* max periodic time per uframe */
+ unsigned uframe_periodic_max;
+
+
+ /* list of itds completed while now_frame was still active */
+ struct list_head cached_itd_list;
+ struct fotg210_itd *last_itd_to_free;
+
+ /* per root hub port */
+ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS];
+
+ /* bit vectors (one bit per port) */
+ unsigned long bus_suspended; /* which ports were
+ already suspended at the start of a bus suspend */
+ unsigned long companion_ports; /* which ports are
+ dedicated to the companion controller */
+ unsigned long owned_ports; /* which ports are
+ owned by the companion during a bus suspend */
+ unsigned long port_c_suspend; /* which ports have
+ the change-suspend feature turned on */
+ unsigned long suspended_ports; /* which ports are
+ suspended */
+ unsigned long resuming_ports; /* which ports have
+ started to resume */
+
+ /* per-HC memory pools (could be per-bus, but ...) */
+ struct dma_pool *qh_pool; /* qh per active urb */
+ struct dma_pool *qtd_pool; /* one or more per qh */
+ struct dma_pool *itd_pool; /* itd per iso urb */
+
+ unsigned random_frame;
+ unsigned long next_statechange;
+ ktime_t last_periodic_enable;
+ u32 command;
+
+ /* SILICON QUIRKS */
+ unsigned need_io_watchdog:1;
+ unsigned fs_i_thresh:1; /* Intel iso scheduling */
+
+ u8 sbrn; /* packed release number */
+
+ /* irq statistics */
+#ifdef FOTG210_STATS
+ struct fotg210_stats stats;
+# define COUNT(x) ((x)++)
+#else
+# define COUNT(x)
+#endif
+
+ /* debug files */
+ struct dentry *debug_dir;
+};
+
+/* convert between an HCD pointer and the corresponding FOTG210_HCD */
+static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd)
+{
+ return (struct fotg210_hcd *)(hcd->hcd_priv);
+}
+static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210)
+{
+ return container_of((void *) fotg210, struct usb_hcd, hcd_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct fotg210_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ * some hosts treat caplength and hciversion as parts of a 32-bit
+ * register, others treat them as two separate registers, this
+ * affects the memory map for big endian controllers.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \
+ (fotg210_big_endian_capbase(fotg210) ? 24 : 0)))
+#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \
+ (fotg210_big_endian_capbase(fotg210) ? 0 : 16)))
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+ u8 portroute[8]; /* nibbles for routing - offset 0xC */
+};
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct fotg210_regs {
+
+ /* USBCMD: offset 0x00 */
+ u32 command;
+
+/* EHCI 1.1 addendum */
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved1;
+ /* PORTSC: offset 0x20 */
+ u32 port_status;
+/* 31:23 reserved */
+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC)
+ u32 reserved2[19];
+
+ /* OTGCSR: offet 0x70 */
+ u32 otgcsr;
+#define OTGCSR_HOST_SPD_TYP (3 << 22)
+#define OTGCSR_A_BUS_DROP (1 << 5)
+#define OTGCSR_A_BUS_REQ (1 << 4)
+
+ /* OTGISR: offset 0x74 */
+ u32 otgisr;
+#define OTGISR_OVC (1 << 10)
+
+ u32 reserved3[15];
+
+ /* GMIR: offset 0xB4 */
+ u32 gmir;
+#define GMIR_INT_POLARITY (1 << 3) /*Active High*/
+#define GMIR_MHC_INT (1 << 2)
+#define GMIR_MOTG_INT (1 << 1)
+#define GMIR_MDEV_INT (1 << 0)
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct fotg210_qtd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.5.1 */
+ __hc32 hw_alt_next; /* see EHCI 3.5.2 */
+ __hc32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+
+#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE)
+#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT)
+#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS)
+
+ __hc32 hw_buf[5]; /* see EHCI 3.5.4 */
+ __hc32 hw_buf_hi[5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+} __aligned(32);
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,fstn}->hw_next */
+#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1))
+
+/*
+ * Now the following defines are not converted using the
+ * cpu_to_le32() macro anymore, since we have to support
+ * "dynamic" switching between be and le support, so that the driver
+ * can be used on one system with SoC EHCI controller using big-endian
+ * descriptors as well as a normal little-endian PCI EHCI controller.
+ */
+/* values for that type tag */
+#define Q_TYPE_ITD (0 << 1)
+#define Q_TYPE_QH (1 << 1)
+#define Q_TYPE_SITD (2 << 1)
+#define Q_TYPE_FSTN (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(fotg210, dma) \
+ (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH))
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define FOTG210_LIST_END(fotg210) \
+ cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union fotg210_shadow {
+ struct fotg210_qh *qh; /* Q_TYPE_QH */
+ struct fotg210_itd *itd; /* Q_TYPE_ITD */
+ struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */
+ __hc32 *hw_next; /* (all types) */
+ void *ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+/* first part defined by EHCI spec */
+struct fotg210_qh_hw {
+ __hc32 hw_next; /* see EHCI 3.6.1 */
+ __hc32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
+#define QH_HEAD (1 << 15) /* Head of async reclamation list */
+#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
+#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */
+#define QH_LOW_SPEED (1 << 12)
+#define QH_FULL_SPEED (0 << 12)
+#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
+ __hc32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_SMASK 0x000000ff
+#define QH_CMASK 0x0000ff00
+#define QH_HUBADDR 0x007f0000
+#define QH_HUBPORT 0x3f800000
+#define QH_MULT 0xc0000000
+ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct fotg210_qtd) */
+ __hc32 hw_qtd_next;
+ __hc32 hw_alt_next;
+ __hc32 hw_token;
+ __hc32 hw_buf[5];
+ __hc32 hw_buf_hi[5];
+} __aligned(32);
+
+struct fotg210_qh {
+ struct fotg210_qh_hw *hw; /* Must come first */
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union fotg210_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+ struct list_head intr_node; /* list of intr QHs */
+ struct fotg210_qtd *dummy;
+ struct fotg210_qh *unlink_next; /* next on unlink list */
+
+ unsigned unlink_cycle;
+
+ u8 needs_rescan; /* Dequeue during giveback */
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ u8 xacterrs; /* XactErr retry counter */
+#define QH_XACTERR_MAX 32 /* XactErr retry limit */
+
+ /* periodic schedule info */
+ u8 usecs; /* intr bandwidth */
+ u8 gap_uf; /* uframes split/csplit gap */
+ u8 c_usecs; /* ... split completion bw */
+ u16 tt_usecs; /* tt downstream bandwidth */
+ unsigned short period; /* polling interval */
+ unsigned short start; /* where polling starts */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+
+ struct usb_device *dev; /* access to TT */
+ unsigned is_out:1; /* bulk or intr OUT */
+ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* description of one iso transaction (up to 3 KB data if highspeed) */
+struct fotg210_iso_packet {
+ /* These will be copied to iTD when scheduling */
+ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
+ __hc32 transaction; /* itd->hw_transaction[i] |= */
+ u8 cross; /* buf crosses pages */
+ /* for full speed OUT splits */
+ u32 buf1;
+};
+
+/* temporary schedule data for packets from iso urbs (both speeds)
+ * each packet is one logical usb transaction to the device (not TT),
+ * beginning at stream->next_uframe
+ */
+struct fotg210_iso_sched {
+ struct list_head td_list;
+ unsigned span;
+ struct fotg210_iso_packet packet[0];
+};
+
+/*
+ * fotg210_iso_stream - groups all (s)itds for this endpoint.
+ * acts like a qh would, if EHCI had them for ISO.
+ */
+struct fotg210_iso_stream {
+ /* first field matches fotg210_hq, but is NULL */
+ struct fotg210_qh_hw *hw;
+
+ u8 bEndpointAddress;
+ u8 highspeed;
+ struct list_head td_list; /* queued itds */
+ struct list_head free_list; /* list of unused itds */
+ struct usb_device *udev;
+ struct usb_host_endpoint *ep;
+
+ /* output of (re)scheduling */
+ int next_uframe;
+ __hc32 splits;
+
+ /* the rest is derived from the endpoint descriptor,
+ * trusting urb->interval == f(epdesc->bInterval) and
+ * including the extra info for hw_bufp[0..2]
+ */
+ u8 usecs, c_usecs;
+ u16 interval;
+ u16 tt_usecs;
+ u16 maxp;
+ u16 raw_mask;
+ unsigned bandwidth;
+
+ /* This is used to initialize iTD's hw_bufp fields */
+ __hc32 buf0;
+ __hc32 buf1;
+ __hc32 buf2;
+
+ /* this is used to initialize sITD's tt info */
+ __hc32 address;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct fotg210_itd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.3.1 */
+ __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
+#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */
+#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
+#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */
+
+#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE)
+
+ __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
+ __hc32 hw_bufp_hi[7]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t itd_dma; /* for this itd */
+ union fotg210_shadow itd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct fotg210_iso_stream *stream; /* endpoint's queue */
+ struct list_head itd_list; /* list of stream's itds */
+
+ /* any/all hw_transactions here may be used by that urb */
+ unsigned frame; /* where scheduled */
+ unsigned pg;
+ unsigned index[8]; /* in urb->iso_frame_desc */
+} __aligned(32);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct fotg210_fstn {
+ __hc32 hw_next; /* any periodic q entry */
+ __hc32 hw_prev; /* qh or FOTG210_LIST_END */
+
+ /* the rest is HCD-private */
+ dma_addr_t fstn_dma;
+ union fotg210_shadow fstn_next; /* ptr to periodic q entry */
+} __aligned(32);
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare the PORTSC wakeup flags during controller suspend/resume */
+
+#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \
+ fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup);
+
+#define fotg210_prepare_ports_for_controller_resume(fotg210) \
+ fotg210_adjust_port_wakeup_flags(fotg210, false, false);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Some EHCI controllers have a Transaction Translator built into the
+ * root hub. This is a non-standard feature. Each controller will need
+ * to add code to the following inline functions, and call them as
+ * needed (mostly in root hub code).
+ */
+
+static inline unsigned int
+fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc)
+{
+ return (readl(&fotg210->regs->otgcsr)
+ & OTGCSR_HOST_SPD_TYP) >> 22;
+}
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int
+fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc)
+{
+ switch (fotg210_get_speed(fotg210, portsc)) {
+ case 0:
+ return 0;
+ case 1:
+ return USB_PORT_STAT_LOW_SPEED;
+ case 2:
+ default:
+ return USB_PORT_STAT_HIGH_SPEED;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define fotg210_has_fsl_portno_bug(e) (0)
+
+/*
+ * While most USB host controllers implement their registers in
+ * little-endian format, a minority (celleb companion chip) implement
+ * them in big endian format.
+ *
+ * This attempts to support either format at compile time without a
+ * runtime penalty, or both formats with the additional overhead
+ * of checking a flag bit.
+ *
+ */
+
+#define fotg210_big_endian_mmio(e) 0
+#define fotg210_big_endian_capbase(e) 0
+
+static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210,
+ __u32 __iomem *regs)
+{
+ return readl(regs);
+}
+
+static inline void fotg210_writel(const struct fotg210_hcd *fotg210,
+ const unsigned int val, __u32 __iomem *regs)
+{
+ writel(val, regs);
+}
+
+/* cpu to fotg210 */
+static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x)
+{
+ return cpu_to_le32(x);
+}
+
+/* fotg210 to cpu */
+static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x)
+{
+ return le32_to_cpu(x);
+}
+
+static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210,
+ const __hc32 *x)
+{
+ return le32_to_cpup(x);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210)
+{
+ return fotg210_readl(fotg210, &fotg210->regs->frame_index);
+}
+
+#define fotg210_itdlen(urb, desc, t) ({ \
+ usb_pipein((urb)->pipe) ? \
+ (desc)->length - FOTG210_ITD_LENGTH(t) : \
+ FOTG210_ITD_LENGTH(t); \
+})
+/*-------------------------------------------------------------------------*/
+
+#endif /* __LINUX_FOTG210_H */
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
new file mode 100644
index 000000000..7e325e90d
--- /dev/null
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -0,0 +1,339 @@
+/*
+ * Setup platform devices needed by the Freescale multi-port host
+ * and/or dual-role USB controller modules based on the description
+ * in flat device tree.
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+
+struct fsl_usb2_dev_data {
+ char *dr_mode; /* controller mode */
+ char *drivers[3]; /* drivers to instantiate for this mode */
+ enum fsl_usb2_operating_modes op_mode; /* operating mode */
+};
+
+static struct fsl_usb2_dev_data dr_mode_data[] = {
+ {
+ .dr_mode = "host",
+ .drivers = { "fsl-ehci", NULL, NULL, },
+ .op_mode = FSL_USB2_DR_HOST,
+ },
+ {
+ .dr_mode = "otg",
+ .drivers = { "fsl-usb2-otg", "fsl-ehci", "fsl-usb2-udc", },
+ .op_mode = FSL_USB2_DR_OTG,
+ },
+ {
+ .dr_mode = "peripheral",
+ .drivers = { "fsl-usb2-udc", NULL, NULL, },
+ .op_mode = FSL_USB2_DR_DEVICE,
+ },
+};
+
+static struct fsl_usb2_dev_data *get_dr_mode_data(struct device_node *np)
+{
+ const unsigned char *prop;
+ int i;
+
+ prop = of_get_property(np, "dr_mode", NULL);
+ if (prop) {
+ for (i = 0; i < ARRAY_SIZE(dr_mode_data); i++) {
+ if (!strcmp(prop, dr_mode_data[i].dr_mode))
+ return &dr_mode_data[i];
+ }
+ }
+ pr_warn("%s: Invalid 'dr_mode' property, fallback to host mode\n",
+ np->full_name);
+ return &dr_mode_data[0]; /* mode not specified, use host */
+}
+
+static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
+{
+ if (!phy_type)
+ return FSL_USB2_PHY_NONE;
+ if (!strcasecmp(phy_type, "ulpi"))
+ return FSL_USB2_PHY_ULPI;
+ if (!strcasecmp(phy_type, "utmi"))
+ return FSL_USB2_PHY_UTMI;
+ if (!strcasecmp(phy_type, "utmi_wide"))
+ return FSL_USB2_PHY_UTMI_WIDE;
+ if (!strcasecmp(phy_type, "serial"))
+ return FSL_USB2_PHY_SERIAL;
+
+ return FSL_USB2_PHY_NONE;
+}
+
+static struct platform_device *fsl_usb2_device_register(
+ struct platform_device *ofdev,
+ struct fsl_usb2_platform_data *pdata,
+ const char *name, int id)
+{
+ struct platform_device *pdev;
+ const struct resource *res = ofdev->resource;
+ unsigned int num = ofdev->num_resources;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pdev->dev.parent = &ofdev->dev;
+
+ pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask;
+ *pdev->dev.dma_mask = *ofdev->dev.dma_mask;
+
+ retval = platform_device_add_data(pdev, pdata, sizeof(*pdata));
+ if (retval)
+ goto error;
+
+ if (num) {
+ retval = platform_device_add_resources(pdev, res, num);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+
+static const struct of_device_id fsl_usb2_mph_dr_of_match[];
+
+static int usb_get_ver_info(struct device_node *np)
+{
+ int ver = -1;
+
+ /*
+ * returns 1 for usb controller version 1.6
+ * returns 2 for usb controller version 2.2
+ * returns 0 otherwise
+ */
+ if (of_device_is_compatible(np, "fsl-usb2-dr")) {
+ if (of_device_is_compatible(np, "fsl-usb2-dr-v1.6"))
+ ver = FSL_USB_VER_1_6;
+ else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.2"))
+ ver = FSL_USB_VER_2_2;
+ else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4"))
+ ver = FSL_USB_VER_2_4;
+ else /* for previous controller versions */
+ ver = FSL_USB_VER_OLD;
+
+ if (ver > -1)
+ return ver;
+ }
+
+ if (of_device_is_compatible(np, "fsl,mpc5121-usb2-dr"))
+ return FSL_USB_VER_OLD;
+
+ if (of_device_is_compatible(np, "fsl-usb2-mph")) {
+ if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6"))
+ ver = FSL_USB_VER_1_6;
+ else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2"))
+ ver = FSL_USB_VER_2_2;
+ else /* for previous controller versions */
+ ver = FSL_USB_VER_OLD;
+ }
+
+ return ver;
+}
+
+static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
+{
+ struct device_node *np = ofdev->dev.of_node;
+ struct platform_device *usb_dev;
+ struct fsl_usb2_platform_data data, *pdata;
+ struct fsl_usb2_dev_data *dev_data;
+ const struct of_device_id *match;
+ const unsigned char *prop;
+ static unsigned int idx;
+ int i;
+
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ match = of_match_device(fsl_usb2_mph_dr_of_match, &ofdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ pdata = &data;
+ if (match->data)
+ memcpy(pdata, match->data, sizeof(data));
+ else
+ memset(pdata, 0, sizeof(data));
+
+ dev_data = get_dr_mode_data(np);
+
+ if (of_device_is_compatible(np, "fsl-usb2-mph")) {
+ if (of_get_property(np, "port0", NULL))
+ pdata->port_enables |= FSL_USB2_PORT0_ENABLED;
+
+ if (of_get_property(np, "port1", NULL))
+ pdata->port_enables |= FSL_USB2_PORT1_ENABLED;
+
+ pdata->operating_mode = FSL_USB2_MPH_HOST;
+ } else {
+ if (of_get_property(np, "fsl,invert-drvvbus", NULL))
+ pdata->invert_drvvbus = 1;
+
+ if (of_get_property(np, "fsl,invert-pwr-fault", NULL))
+ pdata->invert_pwr_fault = 1;
+
+ /* setup mode selected in the device tree */
+ pdata->operating_mode = dev_data->op_mode;
+ }
+
+ prop = of_get_property(np, "phy_type", NULL);
+ pdata->phy_mode = determine_usb_phy(prop);
+ pdata->controller_ver = usb_get_ver_info(np);
+
+ if (pdata->have_sysif_regs) {
+ if (pdata->controller_ver < 0) {
+ dev_warn(&ofdev->dev, "Could not get controller version\n");
+ return -ENODEV;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) {
+ if (!dev_data->drivers[i])
+ continue;
+ usb_dev = fsl_usb2_device_register(ofdev, pdata,
+ dev_data->drivers[i], idx);
+ if (IS_ERR(usb_dev)) {
+ dev_err(&ofdev->dev, "Can't register usb device\n");
+ return PTR_ERR(usb_dev);
+ }
+ }
+ idx++;
+ return 0;
+}
+
+static int __unregister_subdev(struct device *dev, void *d)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
+{
+ device_for_each_child(&ofdev->dev, NULL, __unregister_subdev);
+ return 0;
+}
+
+#ifdef CONFIG_PPC_MPC512x
+
+#define USBGENCTRL 0x200 /* NOTE: big endian */
+#define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */
+#define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/
+#define GC_PPP (1 << 3) /* Inv. Port Power Polarity */
+#define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */
+#define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */
+#define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */
+
+#define ISIPHYCTRL 0x204 /* NOTE: big endian */
+#define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */
+#define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */
+#define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */
+#define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */
+#define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */
+
+int fsl_usb2_mpc5121_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct clk *clk;
+ int err;
+
+ clk = devm_clk_get(pdev->dev.parent, "ipg");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clk\n");
+ return PTR_ERR(clk);
+ }
+ err = clk_prepare_enable(clk);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable clk\n");
+ return err;
+ }
+ pdata->clk = clk;
+
+ if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) {
+ u32 reg = 0;
+
+ if (pdata->invert_drvvbus)
+ reg |= GC_PPP;
+
+ if (pdata->invert_pwr_fault)
+ reg |= GC_PFP;
+
+ out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE);
+ out_be32(pdata->regs + USBGENCTRL, reg);
+ }
+ return 0;
+}
+
+static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ pdata->regs = NULL;
+
+ if (pdata->clk)
+ clk_disable_unprepare(pdata->clk);
+}
+
+static struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
+ .big_endian_desc = 1,
+ .big_endian_mmio = 1,
+ .es = 1,
+ .have_sysif_regs = 0,
+ .le_setup_buf = 1,
+ .init = fsl_usb2_mpc5121_init,
+ .exit = fsl_usb2_mpc5121_exit,
+};
+#endif /* CONFIG_PPC_MPC512x */
+
+static struct fsl_usb2_platform_data fsl_usb2_mpc8xxx_pd = {
+ .have_sysif_regs = 1,
+};
+
+static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
+ { .compatible = "fsl-usb2-mph", .data = &fsl_usb2_mpc8xxx_pd, },
+ { .compatible = "fsl-usb2-dr", .data = &fsl_usb2_mpc8xxx_pd, },
+#ifdef CONFIG_PPC_MPC512x
+ { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, },
+#endif
+ {},
+};
+
+static struct platform_driver fsl_usb2_mph_dr_driver = {
+ .driver = {
+ .name = "fsl-usb2-mph-dr",
+ .of_match_table = fsl_usb2_mph_dr_of_match,
+ },
+ .probe = fsl_usb2_mph_dr_of_probe,
+ .remove = fsl_usb2_mph_dr_of_remove,
+};
+
+module_platform_driver(fsl_usb2_mph_dr_driver);
+
+MODULE_DESCRIPTION("FSL MPH DR OF devices driver");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c
new file mode 100644
index 000000000..00e492eab
--- /dev/null
+++ b/drivers/usb/host/fusbh200-hcd.c
@@ -0,0 +1,5893 @@
+/*
+ * Faraday FUSBH200 EHCI-like driver
+ *
+ * Copyright (c) 2013 Faraday Technology Corporation
+ *
+ * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+ * Feng-Hsin Chiang <john453@faraday-tech.com>
+ * Po-Yu Chuang <ratbert.chuang@gmail.com>
+ *
+ * Most of code borrowed from the Linux-3.7 EHCI driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+/*-------------------------------------------------------------------------*/
+#define DRIVER_AUTHOR "Yuan-Hsin Chen"
+#define DRIVER_DESC "FUSBH200 Host Controller (EHCI) Driver"
+
+static const char hcd_name [] = "fusbh200_hcd";
+
+#undef FUSBH200_URB_TRACE
+
+/* magic numbers that can affect system performance */
+#define FUSBH200_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define FUSBH200_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
+#define FUSBH200_TUNE_RL_TT 0
+#define FUSBH200_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define FUSBH200_TUNE_MULT_TT 1
+/*
+ * Some drivers think it's safe to schedule isochronous transfers more than
+ * 256 ms into the future (partly as a result of an old bug in the scheduling
+ * code). In an attempt to avoid trouble, we will use a minimum scheduling
+ * length of 512 frames instead of 256.
+ */
+#define FUSBH200_TUNE_FLS 1 /* (medium) 512-frame schedule */
+
+/* Initial IRQ latency: faster than hw default */
+static int log2_irq_thresh = 0; // 0 to 6
+module_param (log2_irq_thresh, int, S_IRUGO);
+MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* initial park setting: slower than hw default */
+static unsigned park = 0;
+module_param (park, uint, S_IRUGO);
+MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
+
+/* for link power management(LPM) feature */
+static unsigned int hird;
+module_param(hird, int, S_IRUGO);
+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+#include "fusbh200.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define fusbh200_dbg(fusbh200, fmt, args...) \
+ dev_dbg (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args )
+#define fusbh200_err(fusbh200, fmt, args...) \
+ dev_err (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args )
+#define fusbh200_info(fusbh200, fmt, args...) \
+ dev_info (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args )
+#define fusbh200_warn(fusbh200, fmt, args...) \
+ dev_warn (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args )
+
+/* check the values in the HCSPARAMS register
+ * (host controller _Structural_ parameters)
+ * see EHCI spec, Table 2-4 for each value
+ */
+static void dbg_hcs_params (struct fusbh200_hcd *fusbh200, char *label)
+{
+ u32 params = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params);
+
+ fusbh200_dbg (fusbh200,
+ "%s hcs_params 0x%x ports=%d\n",
+ label, params,
+ HCS_N_PORTS (params)
+ );
+}
+
+/* check the values in the HCCPARAMS register
+ * (host controller _Capability_ parameters)
+ * see EHCI Spec, Table 2-5 for each value
+ * */
+static void dbg_hcc_params (struct fusbh200_hcd *fusbh200, char *label)
+{
+ u32 params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params);
+
+ fusbh200_dbg (fusbh200,
+ "%s hcc_params %04x uframes %s%s\n",
+ label,
+ params,
+ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+ HCC_CANPARK(params) ? " park" : "");
+}
+
+static void __maybe_unused
+dbg_qtd (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd)
+{
+ fusbh200_dbg(fusbh200, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ hc32_to_cpup(fusbh200, &qtd->hw_next),
+ hc32_to_cpup(fusbh200, &qtd->hw_alt_next),
+ hc32_to_cpup(fusbh200, &qtd->hw_token),
+ hc32_to_cpup(fusbh200, &qtd->hw_buf [0]));
+ if (qtd->hw_buf [1])
+ fusbh200_dbg(fusbh200, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ hc32_to_cpup(fusbh200, &qtd->hw_buf[1]),
+ hc32_to_cpup(fusbh200, &qtd->hw_buf[2]),
+ hc32_to_cpup(fusbh200, &qtd->hw_buf[3]),
+ hc32_to_cpup(fusbh200, &qtd->hw_buf[4]));
+}
+
+static void __maybe_unused
+dbg_qh (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ struct fusbh200_qh_hw *hw = qh->hw;
+
+ fusbh200_dbg (fusbh200, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
+ dbg_qtd("overlay", fusbh200, (struct fusbh200_qtd *) &hw->hw_qtd_next);
+}
+
+static void __maybe_unused
+dbg_itd (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_itd *itd)
+{
+ fusbh200_dbg (fusbh200, "%s [%d] itd %p, next %08x, urb %p\n",
+ label, itd->frame, itd, hc32_to_cpu(fusbh200, itd->hw_next),
+ itd->urb);
+ fusbh200_dbg (fusbh200,
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(fusbh200, itd->hw_transaction[0]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[1]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[2]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[3]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[4]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[5]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[6]),
+ hc32_to_cpu(fusbh200, itd->hw_transaction[7]));
+ fusbh200_dbg (fusbh200,
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ hc32_to_cpu(fusbh200, itd->hw_bufp[0]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[1]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[2]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[3]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[4]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[5]),
+ hc32_to_cpu(fusbh200, itd->hw_bufp[6]));
+ fusbh200_dbg (fusbh200, " index: %d %d %d %d %d %d %d %d\n",
+ itd->index[0], itd->index[1], itd->index[2],
+ itd->index[3], itd->index[4], itd->index[5],
+ itd->index[6], itd->index[7]);
+}
+
+static int __maybe_unused
+dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
+{
+ return scnprintf (buf, len,
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", status,
+ (status & STS_ASS) ? " Async" : "",
+ (status & STS_PSS) ? " Periodic" : "",
+ (status & STS_RECL) ? " Recl" : "",
+ (status & STS_HALT) ? " Halt" : "",
+ (status & STS_IAA) ? " IAA" : "",
+ (status & STS_FATAL) ? " FATAL" : "",
+ (status & STS_FLR) ? " FLR" : "",
+ (status & STS_PCD) ? " PCD" : "",
+ (status & STS_ERR) ? " ERR" : "",
+ (status & STS_INT) ? " INT" : ""
+ );
+}
+
+static int __maybe_unused
+dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
+{
+ return scnprintf (buf, len,
+ "%s%sintrenable %02x%s%s%s%s%s%s",
+ label, label [0] ? " " : "", enable,
+ (enable & STS_IAA) ? " IAA" : "",
+ (enable & STS_FATAL) ? " FATAL" : "",
+ (enable & STS_FLR) ? " FLR" : "",
+ (enable & STS_PCD) ? " PCD" : "",
+ (enable & STS_ERR) ? " ERR" : "",
+ (enable & STS_INT) ? " INT" : ""
+ );
+}
+
+static const char *const fls_strings [] =
+ { "1024", "512", "256", "??" };
+
+static int
+dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
+{
+ return scnprintf (buf, len,
+ "%s%scommand %07x %s=%d ithresh=%d%s%s%s "
+ "period=%s%s %s",
+ label, label [0] ? " " : "", command,
+ (command & CMD_PARK) ? " park" : "(park)",
+ CMD_PARK_CNT (command),
+ (command >> 16) & 0x3f,
+ (command & CMD_IAAD) ? " IAAD" : "",
+ (command & CMD_ASE) ? " Async" : "",
+ (command & CMD_PSE) ? " Periodic" : "",
+ fls_strings [(command >> 2) & 0x3],
+ (command & CMD_RESET) ? " Reset" : "",
+ (command & CMD_RUN) ? "RUN" : "HALT"
+ );
+}
+
+static int
+dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
+{
+ char *sig;
+
+ /* signaling state */
+ switch (status & (3 << 10)) {
+ case 0 << 10: sig = "se0"; break;
+ case 1 << 10: sig = "k"; break; /* low speed */
+ case 2 << 10: sig = "j"; break;
+ default: sig = "?"; break;
+ }
+
+ return scnprintf (buf, len,
+ "%s%sport:%d status %06x %d "
+ "sig=%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", port, status,
+ status>>25,/*device address */
+ sig,
+ (status & PORT_RESET) ? " RESET" : "",
+ (status & PORT_SUSPEND) ? " SUSPEND" : "",
+ (status & PORT_RESUME) ? " RESUME" : "",
+ (status & PORT_PEC) ? " PEC" : "",
+ (status & PORT_PE) ? " PE" : "",
+ (status & PORT_CSC) ? " CSC" : "",
+ (status & PORT_CONNECT) ? " CONNECT" : "");
+}
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(fusbh200, label, status) { \
+ char _buf [80]; \
+ dbg_status_buf (_buf, sizeof _buf, label, status); \
+ fusbh200_dbg (fusbh200, "%s\n", _buf); \
+}
+
+#define dbg_cmd(fusbh200, label, command) { \
+ char _buf [80]; \
+ dbg_command_buf (_buf, sizeof _buf, label, command); \
+ fusbh200_dbg (fusbh200, "%s\n", _buf); \
+}
+
+#define dbg_port(fusbh200, label, port, status) { \
+ char _buf [80]; \
+ dbg_port_buf (_buf, sizeof _buf, label, port, status); \
+ fusbh200_dbg (fusbh200, "%s\n", _buf); \
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* troubleshooting help: expose state in debugfs */
+
+static int debug_async_open(struct inode *, struct file *);
+static int debug_periodic_open(struct inode *, struct file *);
+static int debug_registers_open(struct inode *, struct file *);
+static int debug_async_open(struct inode *, struct file *);
+
+static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
+static int debug_close(struct inode *, struct file *);
+
+static const struct file_operations debug_async_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_async_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_periodic_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_periodic_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_registers_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_registers_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+
+static struct dentry *fusbh200_debug_root;
+
+struct debug_buffer {
+ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
+ struct usb_bus *bus;
+ struct mutex mutex; /* protect filling of buffer */
+ size_t count; /* number of characters filled into buffer */
+ char *output_buf;
+ size_t alloc_size;
+};
+
+#define speed_char(info1) ({ char tmp; \
+ switch (info1 & (3 << 12)) { \
+ case QH_FULL_SPEED: tmp = 'f'; break; \
+ case QH_LOW_SPEED: tmp = 'l'; break; \
+ case QH_HIGH_SPEED: tmp = 'h'; break; \
+ default: tmp = '?'; break; \
+ } tmp; })
+
+static inline char token_mark(struct fusbh200_hcd *fusbh200, __hc32 token)
+{
+ __u32 v = hc32_to_cpu(fusbh200, token);
+
+ if (v & QTD_STS_ACTIVE)
+ return '*';
+ if (v & QTD_STS_HALT)
+ return '-';
+ if (!IS_SHORT_READ (v))
+ return ' ';
+ /* tries to advance through hw_alt_next */
+ return '/';
+}
+
+static void qh_lines (
+ struct fusbh200_hcd *fusbh200,
+ struct fusbh200_qh *qh,
+ char **nextp,
+ unsigned *sizep
+)
+{
+ u32 scratch;
+ u32 hw_curr;
+ struct fusbh200_qtd *td;
+ unsigned temp;
+ unsigned size = *sizep;
+ char *next = *nextp;
+ char mark;
+ __le32 list_end = FUSBH200_LIST_END(fusbh200);
+ struct fusbh200_qh_hw *hw = qh->hw;
+
+ if (hw->hw_qtd_next == list_end) /* NEC does this */
+ mark = '@';
+ else
+ mark = token_mark(fusbh200, hw->hw_token);
+ if (mark == '/') { /* qh_alt_next controls qh advance? */
+ if ((hw->hw_alt_next & QTD_MASK(fusbh200))
+ == fusbh200->async->hw->hw_alt_next)
+ mark = '#'; /* blocked */
+ else if (hw->hw_alt_next == list_end)
+ mark = '.'; /* use hw_qtd_next */
+ /* else alt_next points to some other qtd */
+ }
+ scratch = hc32_to_cpup(fusbh200, &hw->hw_info1);
+ hw_curr = (mark == '*') ? hc32_to_cpup(fusbh200, &hw->hw_current) : 0;
+ temp = scnprintf (next, size,
+ "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+ qh, scratch & 0x007f,
+ speed_char (scratch),
+ (scratch >> 8) & 0x000f,
+ scratch, hc32_to_cpup(fusbh200, &hw->hw_info2),
+ hc32_to_cpup(fusbh200, &hw->hw_token), mark,
+ (cpu_to_hc32(fusbh200, QTD_TOGGLE) & hw->hw_token)
+ ? "data1" : "data0",
+ (hc32_to_cpup(fusbh200, &hw->hw_alt_next) >> 1) & 0x0f);
+ size -= temp;
+ next += temp;
+
+ /* hc may be modifying the list as we read it ... */
+ list_for_each_entry(td, &qh->qtd_list, qtd_list) {
+ scratch = hc32_to_cpup(fusbh200, &td->hw_token);
+ mark = ' ';
+ if (hw_curr == td->qtd_dma)
+ mark = '*';
+ else if (hw->hw_qtd_next == cpu_to_hc32(fusbh200, td->qtd_dma))
+ mark = '+';
+ else if (QTD_LENGTH (scratch)) {
+ if (td->hw_alt_next == fusbh200->async->hw->hw_alt_next)
+ mark = '#';
+ else if (td->hw_alt_next != list_end)
+ mark = '/';
+ }
+ temp = snprintf (next, size,
+ "\n\t%p%c%s len=%d %08x urb %p",
+ td, mark, ({ char *tmp;
+ switch ((scratch>>8)&0x03) {
+ case 0: tmp = "out"; break;
+ case 1: tmp = "in"; break;
+ case 2: tmp = "setup"; break;
+ default: tmp = "?"; break;
+ } tmp;}),
+ (scratch >> 16) & 0x7fff,
+ scratch,
+ td->urb);
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+ if (temp == size)
+ goto done;
+ }
+
+ temp = snprintf (next, size, "\n");
+ if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+
+done:
+ *sizep = size;
+ *nextp = next;
+}
+
+static ssize_t fill_async_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fusbh200_hcd *fusbh200;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ struct fusbh200_qh *qh;
+
+ hcd = bus_to_hcd(buf->bus);
+ fusbh200 = hcd_to_fusbh200 (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ *next = 0;
+
+ /* dumps a snapshot of the async schedule.
+ * usually empty except for long-term bulk reads, or head.
+ * one QH per line, and TDs we know about
+ */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ for (qh = fusbh200->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+ qh_lines (fusbh200, qh, &next, &size);
+ if (fusbh200->async_unlink && size > 0) {
+ temp = scnprintf(next, size, "\nunlink =\n");
+ size -= temp;
+ next += temp;
+
+ for (qh = fusbh200->async_unlink; size > 0 && qh;
+ qh = qh->unlink_next)
+ qh_lines (fusbh200, qh, &next, &size);
+ }
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+
+ return strlen(buf->output_buf);
+}
+
+#define DBG_SCHED_LIMIT 64
+static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fusbh200_hcd *fusbh200;
+ unsigned long flags;
+ union fusbh200_shadow p, *seen;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+ __hc32 tag;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ hcd = bus_to_hcd(buf->bus);
+ fusbh200 = hcd_to_fusbh200 (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ temp = scnprintf (next, size, "size = %d\n", fusbh200->periodic_size);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule.
+ * iso changes, interrupt usually doesn't.
+ */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ for (i = 0; i < fusbh200->periodic_size; i++) {
+ p = fusbh200->pshadow [i];
+ if (likely (!p.ptr))
+ continue;
+ tag = Q_NEXT_TYPE(fusbh200, fusbh200->periodic [i]);
+
+ temp = scnprintf (next, size, "%4d: ", i);
+ size -= temp;
+ next += temp;
+
+ do {
+ struct fusbh200_qh_hw *hw;
+
+ switch (hc32_to_cpu(fusbh200, tag)) {
+ case Q_TYPE_QH:
+ hw = p.qh->hw;
+ temp = scnprintf (next, size, " qh%d-%04x/%p",
+ p.qh->period,
+ hc32_to_cpup(fusbh200,
+ &hw->hw_info2)
+ /* uframe masks */
+ & (QH_CMASK | QH_SMASK),
+ p.qh);
+ size -= temp;
+ next += temp;
+ /* don't repeat what follows this qh */
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp].ptr != p.ptr)
+ continue;
+ if (p.qh->qh_next.ptr) {
+ temp = scnprintf (next, size,
+ " ...");
+ size -= temp;
+ next += temp;
+ }
+ break;
+ }
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 scratch = hc32_to_cpup(fusbh200,
+ &hw->hw_info1);
+ struct fusbh200_qtd *qtd;
+ char *type = "";
+
+ /* count tds, get ep direction */
+ temp = 0;
+ list_for_each_entry (qtd,
+ &p.qh->qtd_list,
+ qtd_list) {
+ temp++;
+ switch (0x03 & (hc32_to_cpu(
+ fusbh200,
+ qtd->hw_token) >> 8)) {
+ case 0: type = "out"; continue;
+ case 1: type = "in"; continue;
+ }
+ }
+
+ temp = scnprintf (next, size,
+ " (%c%d ep%d%s "
+ "[%d/%d] q%d p%d)",
+ speed_char (scratch),
+ scratch & 0x007f,
+ (scratch >> 8) & 0x000f, type,
+ p.qh->usecs, p.qh->c_usecs,
+ temp,
+ 0x7ff & (scratch >> 16));
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++].qh = p.qh;
+ } else
+ temp = 0;
+ tag = Q_NEXT_TYPE(fusbh200, hw->hw_next);
+ p = p.qh->qh_next;
+ break;
+ case Q_TYPE_FSTN:
+ temp = scnprintf (next, size,
+ " fstn-%8x/%p", p.fstn->hw_prev,
+ p.fstn);
+ tag = Q_NEXT_TYPE(fusbh200, p.fstn->hw_next);
+ p = p.fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ temp = scnprintf (next, size,
+ " itd/%p", p.itd);
+ tag = Q_NEXT_TYPE(fusbh200, p.itd->hw_next);
+ p = p.itd->itd_next;
+ break;
+ }
+ size -= temp;
+ next += temp;
+ } while (p.ptr);
+
+ temp = scnprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ kfree (seen);
+
+ return buf->alloc_size - size;
+}
+#undef DBG_SCHED_LIMIT
+
+static const char *rh_state_string(struct fusbh200_hcd *fusbh200)
+{
+ switch (fusbh200->rh_state) {
+ case FUSBH200_RH_HALTED:
+ return "halted";
+ case FUSBH200_RH_SUSPENDED:
+ return "suspended";
+ case FUSBH200_RH_RUNNING:
+ return "running";
+ case FUSBH200_RH_STOPPING:
+ return "stopping";
+ }
+ return "?";
+}
+
+static ssize_t fill_registers_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct fusbh200_hcd *fusbh200;
+ unsigned long flags;
+ unsigned temp, size, i;
+ char *next, scratch [80];
+ static char fmt [] = "%*s\n";
+ static char label [] = "";
+
+ hcd = bus_to_hcd(buf->bus);
+ fusbh200 = hcd_to_fusbh200 (hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ size = scnprintf (next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "SUSPENDED (no register access)\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc);
+ goto done;
+ }
+
+ /* Capability Registers */
+ i = HC_VERSION(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase));
+ temp = scnprintf (next, size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "EHCI %x.%02x, rh state %s\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc,
+ i >> 8, i & 0x0ff, rh_state_string(fusbh200));
+ size -= temp;
+ next += temp;
+
+ // FIXME interpret both types of params
+ i = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params);
+ temp = scnprintf (next, size, "structural params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ i = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params);
+ temp = scnprintf (next, size, "capability params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ /* Operational Registers */
+ temp = dbg_status_buf (scratch, sizeof scratch, label,
+ fusbh200_readl(fusbh200, &fusbh200->regs->status));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_command_buf (scratch, sizeof scratch, label,
+ fusbh200_readl(fusbh200, &fusbh200->regs->command));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_intr_buf (scratch, sizeof scratch, label,
+ fusbh200_readl(fusbh200, &fusbh200->regs->intr_enable));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "uframe %04x\n",
+ fusbh200_read_frame_index(fusbh200));
+ size -= temp;
+ next += temp;
+
+ if (fusbh200->async_unlink) {
+ temp = scnprintf(next, size, "async unlink qh %p\n",
+ fusbh200->async_unlink);
+ size -= temp;
+ next += temp;
+ }
+
+ temp = scnprintf (next, size,
+ "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+ fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa,
+ fusbh200->stats.lost_iaa);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "complete %ld unlink %ld\n",
+ fusbh200->stats.complete, fusbh200->stats.unlink);
+ size -= temp;
+ next += temp;
+
+done:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+
+ return buf->alloc_size - size;
+}
+
+static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
+ ssize_t (*fill_func)(struct debug_buffer *))
+{
+ struct debug_buffer *buf;
+
+ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+
+ if (buf) {
+ buf->bus = bus;
+ buf->fill_func = fill_func;
+ mutex_init(&buf->mutex);
+ buf->alloc_size = PAGE_SIZE;
+ }
+
+ return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+ int ret = 0;
+
+ if (!buf->output_buf)
+ buf->output_buf = vmalloc(buf->alloc_size);
+
+ if (!buf->output_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = buf->fill_func(buf);
+
+ if (ret >= 0) {
+ buf->count = ret;
+ ret = 0;
+ }
+
+out:
+ return ret;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+ size_t len, loff_t *offset)
+{
+ struct debug_buffer *buf = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&buf->mutex);
+ if (buf->count == 0) {
+ ret = fill_buffer(buf);
+ if (ret != 0) {
+ mutex_unlock(&buf->mutex);
+ goto out;
+ }
+ }
+ mutex_unlock(&buf->mutex);
+
+ ret = simple_read_from_buffer(user_buf, len, offset,
+ buf->output_buf, buf->count);
+
+out:
+ return ret;
+
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf = file->private_data;
+
+ if (buf) {
+ vfree(buf->output_buf);
+ kfree(buf);
+ }
+
+ return 0;
+}
+static int debug_async_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_periodic_open(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf;
+ buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
+ file->private_data = buf;
+ return 0;
+}
+
+static int debug_registers_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_registers_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static inline void create_debug_files (struct fusbh200_hcd *fusbh200)
+{
+ struct usb_bus *bus = &fusbh200_to_hcd(fusbh200)->self;
+
+ fusbh200->debug_dir = debugfs_create_dir(bus->bus_name, fusbh200_debug_root);
+ if (!fusbh200->debug_dir)
+ return;
+
+ if (!debugfs_create_file("async", S_IRUGO, fusbh200->debug_dir, bus,
+ &debug_async_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("periodic", S_IRUGO, fusbh200->debug_dir, bus,
+ &debug_periodic_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("registers", S_IRUGO, fusbh200->debug_dir, bus,
+ &debug_registers_fops))
+ goto file_error;
+
+ return;
+
+file_error:
+ debugfs_remove_recursive(fusbh200->debug_dir);
+}
+
+static inline void remove_debug_files (struct fusbh200_hcd *fusbh200)
+{
+ debugfs_remove_recursive(fusbh200->debug_dir);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown: shutting down the bridge before the devices using it.
+ */
+static int handshake (struct fusbh200_hcd *fusbh200, void __iomem *ptr,
+ u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = fusbh200_readl(fusbh200, 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 to halt state from unknown (EHCI spec section 2.3).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int fusbh200_halt (struct fusbh200_hcd *fusbh200)
+{
+ u32 temp;
+
+ spin_lock_irq(&fusbh200->lock);
+
+ /* disable any irqs left enabled by previous code */
+ fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable);
+
+ /*
+ * This routine gets called during probe before fusbh200->command
+ * has been initialized, so we can't rely on its value.
+ */
+ fusbh200->command &= ~CMD_RUN;
+ temp = fusbh200_readl(fusbh200, &fusbh200->regs->command);
+ temp &= ~(CMD_RUN | CMD_IAAD);
+ fusbh200_writel(fusbh200, temp, &fusbh200->regs->command);
+
+ spin_unlock_irq(&fusbh200->lock);
+ synchronize_irq(fusbh200_to_hcd(fusbh200)->irq);
+
+ return handshake(fusbh200, &fusbh200->regs->status,
+ STS_HALT, STS_HALT, 16 * 125);
+}
+
+/*
+ * Reset a non-running (STS_HALT == 1) controller.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static int fusbh200_reset (struct fusbh200_hcd *fusbh200)
+{
+ int retval;
+ u32 command = fusbh200_readl(fusbh200, &fusbh200->regs->command);
+
+ /* If the EHCI debug controller is active, special care must be
+ * taken before and after a host controller reset */
+ if (fusbh200->debug && !dbgp_reset_prep(fusbh200_to_hcd(fusbh200)))
+ fusbh200->debug = NULL;
+
+ command |= CMD_RESET;
+ dbg_cmd (fusbh200, "reset", command);
+ fusbh200_writel(fusbh200, command, &fusbh200->regs->command);
+ fusbh200->rh_state = FUSBH200_RH_HALTED;
+ fusbh200->next_statechange = jiffies;
+ retval = handshake (fusbh200, &fusbh200->regs->command,
+ CMD_RESET, 0, 250 * 1000);
+
+ if (retval)
+ return retval;
+
+ if (fusbh200->debug)
+ dbgp_external_startup(fusbh200_to_hcd(fusbh200));
+
+ fusbh200->port_c_suspend = fusbh200->suspended_ports =
+ fusbh200->resuming_ports = 0;
+ return retval;
+}
+
+/*
+ * Idle the controller (turn off the schedules).
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void fusbh200_quiesce (struct fusbh200_hcd *fusbh200)
+{
+ u32 temp;
+
+ if (fusbh200->rh_state != FUSBH200_RH_RUNNING)
+ return;
+
+ /* wait for any schedule enables/disables to take effect */
+ temp = (fusbh200->command << 10) & (STS_ASS | STS_PSS);
+ handshake(fusbh200, &fusbh200->regs->status, STS_ASS | STS_PSS, temp, 16 * 125);
+
+ /* then disable anything that's still active */
+ spin_lock_irq(&fusbh200->lock);
+ fusbh200->command &= ~(CMD_ASE | CMD_PSE);
+ fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command);
+ spin_unlock_irq(&fusbh200->lock);
+
+ /* hardware can take 16 microframes to turn off ... */
+ handshake(fusbh200, &fusbh200->regs->status, STS_ASS | STS_PSS, 0, 16 * 125);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void end_unlink_async(struct fusbh200_hcd *fusbh200);
+static void unlink_empty_async(struct fusbh200_hcd *fusbh200);
+static void fusbh200_work(struct fusbh200_hcd *fusbh200);
+static void start_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh);
+static void end_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh);
+
+/*-------------------------------------------------------------------------*/
+
+/* Set a bit in the USBCMD register */
+static void fusbh200_set_command_bit(struct fusbh200_hcd *fusbh200, u32 bit)
+{
+ fusbh200->command |= bit;
+ fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command);
+
+ /* unblock posted write */
+ fusbh200_readl(fusbh200, &fusbh200->regs->command);
+}
+
+/* Clear a bit in the USBCMD register */
+static void fusbh200_clear_command_bit(struct fusbh200_hcd *fusbh200, u32 bit)
+{
+ fusbh200->command &= ~bit;
+ fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command);
+
+ /* unblock posted write */
+ fusbh200_readl(fusbh200, &fusbh200->regs->command);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI timer support... Now using hrtimers.
+ *
+ * Lots of different events are triggered from fusbh200->hrtimer. Whenever
+ * the timer routine runs, it checks each possible event; events that are
+ * currently enabled and whose expiration time has passed get handled.
+ * The set of enabled events is stored as a collection of bitflags in
+ * fusbh200->enabled_hrtimer_events, and they are numbered in order of
+ * increasing delay values (ranging between 1 ms and 100 ms).
+ *
+ * Rather than implementing a sorted list or tree of all pending events,
+ * we keep track only of the lowest-numbered pending event, in
+ * fusbh200->next_hrtimer_event. Whenever fusbh200->hrtimer gets restarted, its
+ * expiration time is set to the timeout value for this event.
+ *
+ * As a result, events might not get handled right away; the actual delay
+ * could be anywhere up to twice the requested delay. This doesn't
+ * matter, because none of the events are especially time-critical. The
+ * ones that matter most all have a delay of 1 ms, so they will be
+ * handled after 2 ms at most, which is okay. In addition to this, we
+ * allow for an expiration range of 1 ms.
+ */
+
+/*
+ * Delay lengths for the hrtimer event types.
+ * Keep this list sorted by delay length, in the same order as
+ * the event types indexed by enum fusbh200_hrtimer_event in fusbh200.h.
+ */
+static unsigned event_delays_ns[] = {
+ 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_ASS */
+ 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_PSS */
+ 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_DEAD */
+ 1125 * NSEC_PER_USEC, /* FUSBH200_HRTIMER_UNLINK_INTR */
+ 2 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_FREE_ITDS */
+ 6 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_ASYNC_UNLINKS */
+ 10 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_IAA_WATCHDOG */
+ 10 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_DISABLE_PERIODIC */
+ 15 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_DISABLE_ASYNC */
+ 100 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_IO_WATCHDOG */
+};
+
+/* Enable a pending hrtimer event */
+static void fusbh200_enable_event(struct fusbh200_hcd *fusbh200, unsigned event,
+ bool resched)
+{
+ ktime_t *timeout = &fusbh200->hr_timeouts[event];
+
+ if (resched)
+ *timeout = ktime_add(ktime_get(),
+ ktime_set(0, event_delays_ns[event]));
+ fusbh200->enabled_hrtimer_events |= (1 << event);
+
+ /* Track only the lowest-numbered pending event */
+ if (event < fusbh200->next_hrtimer_event) {
+ fusbh200->next_hrtimer_event = event;
+ hrtimer_start_range_ns(&fusbh200->hrtimer, *timeout,
+ NSEC_PER_MSEC, HRTIMER_MODE_ABS);
+ }
+}
+
+
+/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
+static void fusbh200_poll_ASS(struct fusbh200_hcd *fusbh200)
+{
+ unsigned actual, want;
+
+ /* Don't enable anything if the controller isn't running (e.g., died) */
+ if (fusbh200->rh_state != FUSBH200_RH_RUNNING)
+ return;
+
+ want = (fusbh200->command & CMD_ASE) ? STS_ASS : 0;
+ actual = fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_ASS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 20 ms */
+ if (fusbh200->ASS_poll_count++ < 20) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_ASS, true);
+ return;
+ }
+ fusbh200_dbg(fusbh200, "Waited too long for the async schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ fusbh200->ASS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (fusbh200->async_count > 0)
+ fusbh200_set_command_bit(fusbh200, CMD_ASE);
+
+ } else { /* Running */
+ if (fusbh200->async_count == 0) {
+
+ /* Turn off the schedule after a while */
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_DISABLE_ASYNC,
+ true);
+ }
+ }
+}
+
+/* Turn off the async schedule after a brief delay */
+static void fusbh200_disable_ASE(struct fusbh200_hcd *fusbh200)
+{
+ fusbh200_clear_command_bit(fusbh200, CMD_ASE);
+}
+
+
+/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
+static void fusbh200_poll_PSS(struct fusbh200_hcd *fusbh200)
+{
+ unsigned actual, want;
+
+ /* Don't do anything if the controller isn't running (e.g., died) */
+ if (fusbh200->rh_state != FUSBH200_RH_RUNNING)
+ return;
+
+ want = (fusbh200->command & CMD_PSE) ? STS_PSS : 0;
+ actual = fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_PSS;
+
+ if (want != actual) {
+
+ /* Poll again later, but give up after about 20 ms */
+ if (fusbh200->PSS_poll_count++ < 20) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_PSS, true);
+ return;
+ }
+ fusbh200_dbg(fusbh200, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
+ want, actual);
+ }
+ fusbh200->PSS_poll_count = 0;
+
+ /* The status is up-to-date; restart or stop the schedule as needed */
+ if (want == 0) { /* Stopped */
+ if (fusbh200->periodic_count > 0)
+ fusbh200_set_command_bit(fusbh200, CMD_PSE);
+
+ } else { /* Running */
+ if (fusbh200->periodic_count == 0) {
+
+ /* Turn off the schedule after a while */
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_DISABLE_PERIODIC,
+ true);
+ }
+ }
+}
+
+/* Turn off the periodic schedule after a brief delay */
+static void fusbh200_disable_PSE(struct fusbh200_hcd *fusbh200)
+{
+ fusbh200_clear_command_bit(fusbh200, CMD_PSE);
+}
+
+
+/* Poll the STS_HALT status bit; see when a dead controller stops */
+static void fusbh200_handle_controller_death(struct fusbh200_hcd *fusbh200)
+{
+ if (!(fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_HALT)) {
+
+ /* Give up after a few milliseconds */
+ if (fusbh200->died_poll_count++ < 5) {
+ /* Try again later */
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_DEAD, true);
+ return;
+ }
+ fusbh200_warn(fusbh200, "Waited too long for the controller to stop, giving up\n");
+ }
+
+ /* Clean up the mess */
+ fusbh200->rh_state = FUSBH200_RH_HALTED;
+ fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable);
+ fusbh200_work(fusbh200);
+ end_unlink_async(fusbh200);
+
+ /* Not in process context, so don't try to reset the controller */
+}
+
+
+/* Handle unlinked interrupt QHs once they are gone from the hardware */
+static void fusbh200_handle_intr_unlinks(struct fusbh200_hcd *fusbh200)
+{
+ bool stopped = (fusbh200->rh_state < FUSBH200_RH_RUNNING);
+
+ /*
+ * Process all the QHs on the intr_unlink list that were added
+ * before the current unlink cycle began. The list is in
+ * temporal order, so stop when we reach the first entry in the
+ * current cycle. But if the root hub isn't running then
+ * process all the QHs on the list.
+ */
+ fusbh200->intr_unlinking = true;
+ while (fusbh200->intr_unlink) {
+ struct fusbh200_qh *qh = fusbh200->intr_unlink;
+
+ if (!stopped && qh->unlink_cycle == fusbh200->intr_unlink_cycle)
+ break;
+ fusbh200->intr_unlink = qh->unlink_next;
+ qh->unlink_next = NULL;
+ end_unlink_intr(fusbh200, qh);
+ }
+
+ /* Handle remaining entries later */
+ if (fusbh200->intr_unlink) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_UNLINK_INTR, true);
+ ++fusbh200->intr_unlink_cycle;
+ }
+ fusbh200->intr_unlinking = false;
+}
+
+
+/* Start another free-iTDs/siTDs cycle */
+static void start_free_itds(struct fusbh200_hcd *fusbh200)
+{
+ if (!(fusbh200->enabled_hrtimer_events & BIT(FUSBH200_HRTIMER_FREE_ITDS))) {
+ fusbh200->last_itd_to_free = list_entry(
+ fusbh200->cached_itd_list.prev,
+ struct fusbh200_itd, itd_list);
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_FREE_ITDS, true);
+ }
+}
+
+/* Wait for controller to stop using old iTDs and siTDs */
+static void end_free_itds(struct fusbh200_hcd *fusbh200)
+{
+ struct fusbh200_itd *itd, *n;
+
+ if (fusbh200->rh_state < FUSBH200_RH_RUNNING) {
+ fusbh200->last_itd_to_free = NULL;
+ }
+
+ list_for_each_entry_safe(itd, n, &fusbh200->cached_itd_list, itd_list) {
+ list_del(&itd->itd_list);
+ dma_pool_free(fusbh200->itd_pool, itd, itd->itd_dma);
+ if (itd == fusbh200->last_itd_to_free)
+ break;
+ }
+
+ if (!list_empty(&fusbh200->cached_itd_list))
+ start_free_itds(fusbh200);
+}
+
+
+/* Handle lost (or very late) IAA interrupts */
+static void fusbh200_iaa_watchdog(struct fusbh200_hcd *fusbh200)
+{
+ if (fusbh200->rh_state != FUSBH200_RH_RUNNING)
+ return;
+
+ /*
+ * Lost IAA irqs wedge things badly; seen first with a vt8235.
+ * So we need this watchdog, but must protect it against both
+ * (a) SMP races against real IAA firing and retriggering, and
+ * (b) clean HC shutdown, when IAA watchdog was pending.
+ */
+ if (fusbh200->async_iaa) {
+ u32 cmd, status;
+
+ /* If we get here, IAA is *REALLY* late. It's barely
+ * conceivable that the system is so busy that CMD_IAAD
+ * is still legitimately set, so let's be sure it's
+ * clear before we read STS_IAA. (The HC should clear
+ * CMD_IAAD when it sets STS_IAA.)
+ */
+ cmd = fusbh200_readl(fusbh200, &fusbh200->regs->command);
+
+ /*
+ * If IAA is set here it either legitimately triggered
+ * after the watchdog timer expired (_way_ late, so we'll
+ * still count it as lost) ... or a silicon erratum:
+ * - VIA seems to set IAA without triggering the IRQ;
+ * - IAAD potentially cleared without setting IAA.
+ */
+ status = fusbh200_readl(fusbh200, &fusbh200->regs->status);
+ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
+ COUNT(fusbh200->stats.lost_iaa);
+ fusbh200_writel(fusbh200, STS_IAA, &fusbh200->regs->status);
+ }
+
+ fusbh200_dbg(fusbh200, "IAA watchdog: status %x cmd %x\n",
+ status, cmd);
+ end_unlink_async(fusbh200);
+ }
+}
+
+
+/* Enable the I/O watchdog, if appropriate */
+static void turn_on_io_watchdog(struct fusbh200_hcd *fusbh200)
+{
+ /* Not needed if the controller isn't running or it's already enabled */
+ if (fusbh200->rh_state != FUSBH200_RH_RUNNING ||
+ (fusbh200->enabled_hrtimer_events &
+ BIT(FUSBH200_HRTIMER_IO_WATCHDOG)))
+ return;
+
+ /*
+ * Isochronous transfers always need the watchdog.
+ * For other sorts we use it only if the flag is set.
+ */
+ if (fusbh200->isoc_count > 0 || (fusbh200->need_io_watchdog &&
+ fusbh200->async_count + fusbh200->intr_count > 0))
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_IO_WATCHDOG, true);
+}
+
+
+/*
+ * Handler functions for the hrtimer event types.
+ * Keep this array in the same order as the event types indexed by
+ * enum fusbh200_hrtimer_event in fusbh200.h.
+ */
+static void (*event_handlers[])(struct fusbh200_hcd *) = {
+ fusbh200_poll_ASS, /* FUSBH200_HRTIMER_POLL_ASS */
+ fusbh200_poll_PSS, /* FUSBH200_HRTIMER_POLL_PSS */
+ fusbh200_handle_controller_death, /* FUSBH200_HRTIMER_POLL_DEAD */
+ fusbh200_handle_intr_unlinks, /* FUSBH200_HRTIMER_UNLINK_INTR */
+ end_free_itds, /* FUSBH200_HRTIMER_FREE_ITDS */
+ unlink_empty_async, /* FUSBH200_HRTIMER_ASYNC_UNLINKS */
+ fusbh200_iaa_watchdog, /* FUSBH200_HRTIMER_IAA_WATCHDOG */
+ fusbh200_disable_PSE, /* FUSBH200_HRTIMER_DISABLE_PERIODIC */
+ fusbh200_disable_ASE, /* FUSBH200_HRTIMER_DISABLE_ASYNC */
+ fusbh200_work, /* FUSBH200_HRTIMER_IO_WATCHDOG */
+};
+
+static enum hrtimer_restart fusbh200_hrtimer_func(struct hrtimer *t)
+{
+ struct fusbh200_hcd *fusbh200 = container_of(t, struct fusbh200_hcd, hrtimer);
+ ktime_t now;
+ unsigned long events;
+ unsigned long flags;
+ unsigned e;
+
+ spin_lock_irqsave(&fusbh200->lock, flags);
+
+ events = fusbh200->enabled_hrtimer_events;
+ fusbh200->enabled_hrtimer_events = 0;
+ fusbh200->next_hrtimer_event = FUSBH200_HRTIMER_NO_EVENT;
+
+ /*
+ * Check each pending event. If its time has expired, handle
+ * the event; otherwise re-enable it.
+ */
+ now = ktime_get();
+ for_each_set_bit(e, &events, FUSBH200_HRTIMER_NUM_EVENTS) {
+ if (now.tv64 >= fusbh200->hr_timeouts[e].tv64)
+ event_handlers[e](fusbh200);
+ else
+ fusbh200_enable_event(fusbh200, e, false);
+ }
+
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define fusbh200_bus_suspend NULL
+#define fusbh200_bus_resume NULL
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete (
+ struct fusbh200_hcd *fusbh200,
+ int index,
+ u32 __iomem *status_reg,
+ int port_status
+) {
+ if (!(port_status & PORT_CONNECT))
+ return port_status;
+
+ /* if reset finished and it's still not enabled -- handoff */
+ if (!(port_status & PORT_PE)) {
+ /* with integrated TT, there's nobody to hand it to! */
+ fusbh200_dbg (fusbh200,
+ "Failed to enable port %d on root hub TT\n",
+ index+1);
+ return port_status;
+ } else {
+ fusbh200_dbg(fusbh200, "port %d reset complete, port enabled\n",
+ index + 1);
+ }
+
+ return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+fusbh200_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ u32 temp, status;
+ u32 mask;
+ int retval = 1;
+ unsigned long flags;
+
+ /* init status to no-changes */
+ buf [0] = 0;
+
+ /* Inform the core about resumes-in-progress by returning
+ * a non-zero value even if there are no status changes.
+ */
+ status = fusbh200->resuming_ports;
+
+ mask = PORT_CSC | PORT_PEC;
+ // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+
+ temp = fusbh200_readl(fusbh200, &fusbh200->regs->port_status);
+
+ /*
+ * Return status information even for ports with OWNER set.
+ * Otherwise hub_wq wouldn't see the disconnect event when a
+ * high-speed device is switched over to the companion
+ * controller by the user.
+ */
+
+ if ((temp & mask) != 0 || test_bit(0, &fusbh200->port_c_suspend)
+ || (fusbh200->reset_done[0] && time_after_eq(
+ jiffies, fusbh200->reset_done[0]))) {
+ buf [0] |= 1 << 1;
+ status = STS_PCD;
+ }
+ /* FIXME autosuspend idle root hubs */
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+fusbh200_hub_descriptor (
+ struct fusbh200_hcd *fusbh200,
+ struct usb_hub_descriptor *desc
+) {
+ int ports = HCS_N_PORTS (fusbh200->hcs_params);
+ u16 temp;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = 10; /* fusbh200 1.0, 2.3.9 says 20ms max */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
+
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int fusbh200_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ int ports = HCS_N_PORTS (fusbh200->hcs_params);
+ u32 __iomem *status_reg = &fusbh200->regs->port_status;
+ u32 temp, temp1, status;
+ unsigned long flags;
+ int retval = 0;
+ unsigned selector;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = fusbh200_readl(fusbh200, status_reg);
+ temp &= ~PORT_RWC_BITS;
+
+ /*
+ * Even if OWNER is set, so the port is owned by the
+ * companion controller, hub_wq needs to be able to clear
+ * the port-change status bits (especially
+ * USB_PORT_STAT_C_CONNECTION).
+ */
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ fusbh200_writel(fusbh200, temp & ~PORT_PE, status_reg);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ fusbh200_writel(fusbh200, temp | PORT_PEC, status_reg);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (temp & PORT_RESET)
+ goto error;
+ if (!(temp & PORT_SUSPEND))
+ break;
+ if ((temp & PORT_PE) == 0)
+ goto error;
+
+ fusbh200_writel(fusbh200, temp | PORT_RESUME, status_reg);
+ fusbh200->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ clear_bit(wIndex, &fusbh200->port_c_suspend);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ fusbh200_writel(fusbh200, temp | PORT_CSC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ fusbh200_writel(fusbh200, temp | BMISR_OVC, &fusbh200->regs->bmisr);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted write */
+ break;
+ case GetHubDescriptor:
+ fusbh200_hub_descriptor (fusbh200, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset (buf, 0, 4);
+ //cpu_to_le32s ((u32 *) buf);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ status = 0;
+ temp = fusbh200_readl(fusbh200, status_reg);
+
+ // wPortChange bits
+ if (temp & PORT_CSC)
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
+ if (temp & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+
+ temp1 = fusbh200_readl(fusbh200, &fusbh200->regs->bmisr);
+ if (temp1 & BMISR_OVC)
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+
+ /* whoever resumes must GetPortStatus to complete it!! */
+ if (temp & PORT_RESUME) {
+
+ /* Remote Wakeup received? */
+ if (!fusbh200->reset_done[wIndex]) {
+ /* resume signaling for 20 msec */
+ fusbh200->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ /* check the port again */
+ mod_timer(&fusbh200_to_hcd(fusbh200)->rh_timer,
+ fusbh200->reset_done[wIndex]);
+ }
+
+ /* resume completed? */
+ else if (time_after_eq(jiffies,
+ fusbh200->reset_done[wIndex])) {
+ clear_bit(wIndex, &fusbh200->suspended_ports);
+ set_bit(wIndex, &fusbh200->port_c_suspend);
+ fusbh200->reset_done[wIndex] = 0;
+
+ /* stop resume signaling */
+ temp = fusbh200_readl(fusbh200, status_reg);
+ fusbh200_writel(fusbh200,
+ temp & ~(PORT_RWC_BITS | PORT_RESUME),
+ status_reg);
+ clear_bit(wIndex, &fusbh200->resuming_ports);
+ retval = handshake(fusbh200, status_reg,
+ PORT_RESUME, 0, 2000 /* 2msec */);
+ if (retval != 0) {
+ fusbh200_err(fusbh200,
+ "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
+ }
+ }
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && time_after_eq(jiffies,
+ fusbh200->reset_done[wIndex])) {
+ status |= USB_PORT_STAT_C_RESET << 16;
+ fusbh200->reset_done [wIndex] = 0;
+ clear_bit(wIndex, &fusbh200->resuming_ports);
+
+ /* force reset to complete */
+ fusbh200_writel(fusbh200, temp & ~(PORT_RWC_BITS | PORT_RESET),
+ status_reg);
+ /* REVISIT: some hardware needs 550+ usec to clear
+ * this bit; seems too long to spin routinely...
+ */
+ retval = handshake(fusbh200, status_reg,
+ PORT_RESET, 0, 1000);
+ if (retval != 0) {
+ fusbh200_err (fusbh200, "port %d reset error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+
+ /* see what we found out */
+ temp = check_reset_complete (fusbh200, wIndex, status_reg,
+ fusbh200_readl(fusbh200, status_reg));
+ }
+
+ if (!(temp & (PORT_RESUME|PORT_RESET))) {
+ fusbh200->reset_done[wIndex] = 0;
+ clear_bit(wIndex, &fusbh200->resuming_ports);
+ }
+
+ /* transfer dedicated ports to the companion hc */
+ if ((temp & PORT_CONNECT) &&
+ test_bit(wIndex, &fusbh200->companion_ports)) {
+ temp &= ~PORT_RWC_BITS;
+ fusbh200_writel(fusbh200, temp, status_reg);
+ fusbh200_dbg(fusbh200, "port %d --> companion\n", wIndex + 1);
+ temp = fusbh200_readl(fusbh200, status_reg);
+ }
+
+ /*
+ * Even if OWNER is set, there's no harm letting hub_wq
+ * see the wPortStatus values (they should all be 0 except
+ * for PORT_POWER anyway).
+ */
+
+ if (temp & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ status |= fusbh200_port_speed(fusbh200, temp);
+ }
+ if (temp & PORT_PE)
+ status |= USB_PORT_STAT_ENABLE;
+
+ /* maybe the port was unsuspended without our knowledge */
+ if (temp & (PORT_SUSPEND|PORT_RESUME)) {
+ status |= USB_PORT_STAT_SUSPEND;
+ } else if (test_bit(wIndex, &fusbh200->suspended_ports)) {
+ clear_bit(wIndex, &fusbh200->suspended_ports);
+ clear_bit(wIndex, &fusbh200->resuming_ports);
+ fusbh200->reset_done[wIndex] = 0;
+ if (temp & PORT_PE)
+ set_bit(wIndex, &fusbh200->port_c_suspend);
+ }
+
+ temp1 = fusbh200_readl(fusbh200, &fusbh200->regs->bmisr);
+ if (temp1 & BMISR_OVC)
+ status |= USB_PORT_STAT_OVERCURRENT;
+ if (temp & PORT_RESET)
+ status |= USB_PORT_STAT_RESET;
+ if (test_bit(wIndex, &fusbh200->port_c_suspend))
+ status |= USB_PORT_STAT_C_SUSPEND << 16;
+
+ if (status & ~0xffff) /* only if wPortChange is interesting */
+ dbg_port(fusbh200, "GetStatus", wIndex + 1, temp);
+ put_unaligned_le32(status, buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ selector = wIndex >> 8;
+ wIndex &= 0xff;
+
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = fusbh200_readl(fusbh200, status_reg);
+ temp &= ~PORT_RWC_BITS;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_RESET) != 0)
+ goto error;
+
+ /* After above check the port must be connected.
+ * Set appropriate bit thus could put phy into low power
+ * mode if we have hostpc feature
+ */
+ fusbh200_writel(fusbh200, temp | PORT_SUSPEND, status_reg);
+ set_bit(wIndex, &fusbh200->suspended_ports);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (temp & PORT_RESUME)
+ goto error;
+ /* line status bits may report this as low speed,
+ * which can be fine if this root hub has a
+ * transaction translator built in.
+ */
+ fusbh200_dbg(fusbh200, "port %d reset\n", wIndex + 1);
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ fusbh200->reset_done [wIndex] = jiffies
+ + msecs_to_jiffies (50);
+ fusbh200_writel(fusbh200, temp, status_reg);
+ break;
+
+ /* For downstream facing ports (these): one hub port is put
+ * into test mode according to USB2 11.24.2.13, then the hub
+ * must be reset (which for root hub now means rmmod+modprobe,
+ * or else system reboot). See EHCI 2.3.9 and 4.14 for info
+ * about the EHCI-specific stuff.
+ */
+ case USB_PORT_FEAT_TEST:
+ if (!selector || selector > 5)
+ goto error;
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+ fusbh200_quiesce(fusbh200);
+ spin_lock_irqsave(&fusbh200->lock, flags);
+
+ /* Put all enabled ports into suspend */
+ temp = fusbh200_readl(fusbh200, status_reg) & ~PORT_RWC_BITS;
+ if (temp & PORT_PE)
+ fusbh200_writel(fusbh200, temp | PORT_SUSPEND,
+ status_reg);
+
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+ fusbh200_halt(fusbh200);
+ spin_lock_irqsave(&fusbh200->lock, flags);
+
+ temp = fusbh200_readl(fusbh200, status_reg);
+ temp |= selector << 16;
+ fusbh200_writel(fusbh200, temp, status_reg);
+ break;
+
+ default:
+ goto error;
+ }
+ fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted writes */
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ return retval;
+}
+
+static void __maybe_unused fusbh200_relinquish_port(struct usb_hcd *hcd,
+ int portnum)
+{
+ return;
+}
+
+static int __maybe_unused fusbh200_port_handed_over(struct usb_hcd *hcd,
+ int portnum)
+{
+ return 0;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
+ * No memory seen by this driver is pageable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static inline void fusbh200_qtd_init(struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd,
+ dma_addr_t dma)
+{
+ memset (qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_hc32(fusbh200, QTD_STS_HALT);
+ qtd->hw_next = FUSBH200_LIST_END(fusbh200);
+ qtd->hw_alt_next = FUSBH200_LIST_END(fusbh200);
+ INIT_LIST_HEAD (&qtd->qtd_list);
+}
+
+static struct fusbh200_qtd *fusbh200_qtd_alloc (struct fusbh200_hcd *fusbh200, gfp_t flags)
+{
+ struct fusbh200_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = dma_pool_alloc (fusbh200->qtd_pool, flags, &dma);
+ if (qtd != NULL) {
+ fusbh200_qtd_init(fusbh200, qtd, dma);
+ }
+ return qtd;
+}
+
+static inline void fusbh200_qtd_free (struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd)
+{
+ dma_pool_free (fusbh200->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static void qh_destroy(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+ fusbh200_dbg (fusbh200, "unused qh not empty!\n");
+ BUG ();
+ }
+ if (qh->dummy)
+ fusbh200_qtd_free (fusbh200, qh->dummy);
+ dma_pool_free(fusbh200->qh_pool, qh->hw, qh->qh_dma);
+ kfree(qh);
+}
+
+static struct fusbh200_qh *fusbh200_qh_alloc (struct fusbh200_hcd *fusbh200, gfp_t flags)
+{
+ struct fusbh200_qh *qh;
+ dma_addr_t dma;
+
+ qh = kzalloc(sizeof *qh, GFP_ATOMIC);
+ if (!qh)
+ goto done;
+ qh->hw = (struct fusbh200_qh_hw *)
+ dma_pool_alloc(fusbh200->qh_pool, flags, &dma);
+ if (!qh->hw)
+ goto fail;
+ memset(qh->hw, 0, sizeof *qh->hw);
+ qh->qh_dma = dma;
+ // INIT_LIST_HEAD (&qh->qh_list);
+ INIT_LIST_HEAD (&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = fusbh200_qtd_alloc (fusbh200, flags);
+ if (qh->dummy == NULL) {
+ fusbh200_dbg (fusbh200, "no dummy td\n");
+ goto fail1;
+ }
+done:
+ return qh;
+fail1:
+ dma_pool_free(fusbh200->qh_pool, qh->hw, qh->qh_dma);
+fail:
+ kfree(qh);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void fusbh200_mem_cleanup (struct fusbh200_hcd *fusbh200)
+{
+ if (fusbh200->async)
+ qh_destroy(fusbh200, fusbh200->async);
+ fusbh200->async = NULL;
+
+ if (fusbh200->dummy)
+ qh_destroy(fusbh200, fusbh200->dummy);
+ fusbh200->dummy = NULL;
+
+ /* DMA consistent memory and pools */
+ if (fusbh200->qtd_pool)
+ dma_pool_destroy (fusbh200->qtd_pool);
+ fusbh200->qtd_pool = NULL;
+
+ if (fusbh200->qh_pool) {
+ dma_pool_destroy (fusbh200->qh_pool);
+ fusbh200->qh_pool = NULL;
+ }
+
+ if (fusbh200->itd_pool)
+ dma_pool_destroy (fusbh200->itd_pool);
+ fusbh200->itd_pool = NULL;
+
+ if (fusbh200->periodic)
+ dma_free_coherent (fusbh200_to_hcd(fusbh200)->self.controller,
+ fusbh200->periodic_size * sizeof (u32),
+ fusbh200->periodic, fusbh200->periodic_dma);
+ fusbh200->periodic = NULL;
+
+ /* shadow periodic table */
+ kfree(fusbh200->pshadow);
+ fusbh200->pshadow = NULL;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int fusbh200_mem_init (struct fusbh200_hcd *fusbh200, gfp_t flags)
+{
+ int i;
+
+ /* QTDs for control/bulk/intr transfers */
+ fusbh200->qtd_pool = dma_pool_create ("fusbh200_qtd",
+ fusbh200_to_hcd(fusbh200)->self.controller,
+ sizeof (struct fusbh200_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fusbh200->qtd_pool) {
+ goto fail;
+ }
+
+ /* QHs for control/bulk/intr transfers */
+ fusbh200->qh_pool = dma_pool_create ("fusbh200_qh",
+ fusbh200_to_hcd(fusbh200)->self.controller,
+ sizeof(struct fusbh200_qh_hw),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fusbh200->qh_pool) {
+ goto fail;
+ }
+ fusbh200->async = fusbh200_qh_alloc (fusbh200, flags);
+ if (!fusbh200->async) {
+ goto fail;
+ }
+
+ /* ITD for high speed ISO transfers */
+ fusbh200->itd_pool = dma_pool_create ("fusbh200_itd",
+ fusbh200_to_hcd(fusbh200)->self.controller,
+ sizeof (struct fusbh200_itd),
+ 64 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!fusbh200->itd_pool) {
+ goto fail;
+ }
+
+ /* Hardware periodic table */
+ fusbh200->periodic = (__le32 *)
+ dma_alloc_coherent (fusbh200_to_hcd(fusbh200)->self.controller,
+ fusbh200->periodic_size * sizeof(__le32),
+ &fusbh200->periodic_dma, 0);
+ if (fusbh200->periodic == NULL) {
+ goto fail;
+ }
+
+ for (i = 0; i < fusbh200->periodic_size; i++)
+ fusbh200->periodic[i] = FUSBH200_LIST_END(fusbh200);
+
+ /* software shadow of hardware table */
+ fusbh200->pshadow = kcalloc(fusbh200->periodic_size, sizeof(void *), flags);
+ if (fusbh200->pshadow != NULL)
+ return 0;
+
+fail:
+ fusbh200_dbg (fusbh200, "couldn't init memory\n");
+ fusbh200_mem_cleanup (fusbh200);
+ return -ENOMEM;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * EHCI hardware queue manipulation ... the core. QH/QTD manipulation.
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number). We use one QH per endpoint, queue
+ * multiple urbs (all three types) per endpoint. URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge. That's in "ehci-sched.c".
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill(struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd, dma_addr_t buf,
+ size_t len, int token, int maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf[0] = cpu_to_hc32(fusbh200, (u32)addr);
+ qtd->hw_buf_hi[0] = cpu_to_hc32(fusbh200, (u32)(addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely (len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ addr = buf;
+ qtd->hw_buf[i] = cpu_to_hc32(fusbh200, (u32)addr);
+ qtd->hw_buf_hi[i] = cpu_to_hc32(fusbh200,
+ (u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_hc32(fusbh200, (count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+qh_update (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh, struct fusbh200_qtd *qtd)
+{
+ struct fusbh200_qh_hw *hw = qh->hw;
+
+ /* writes to an active overlay are unsafe */
+ BUG_ON(qh->qh_state != QH_STATE_IDLE);
+
+ hw->hw_qtd_next = QTD_NEXT(fusbh200, qtd->qtd_dma);
+ hw->hw_alt_next = FUSBH200_LIST_END(fusbh200);
+
+ /* 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 (!(hw->hw_info1 & cpu_to_hc32(fusbh200, QH_TOGGLE_CTL))) {
+ unsigned is_out, epnum;
+
+ is_out = qh->is_out;
+ epnum = (hc32_to_cpup(fusbh200, &hw->hw_info1) >> 8) & 0x0f;
+ if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
+ hw->hw_token &= ~cpu_to_hc32(fusbh200, QTD_TOGGLE);
+ usb_settoggle (qh->dev, epnum, is_out, 1);
+ }
+ }
+
+ hw->hw_token &= cpu_to_hc32(fusbh200, QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* if it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void
+qh_refresh (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ struct fusbh200_qtd *qtd;
+
+ if (list_empty (&qh->qtd_list))
+ qtd = qh->dummy;
+ else {
+ qtd = list_entry (qh->qtd_list.next,
+ struct fusbh200_qtd, qtd_list);
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (cpu_to_hc32(fusbh200, qtd->qtd_dma) == qh->hw->hw_current) {
+ qh->hw->hw_qtd_next = qtd->hw_next;
+ qtd = NULL;
+ }
+ }
+
+ if (qtd)
+ qh_update (fusbh200, qh, qtd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void qh_link_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh);
+
+static void fusbh200_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd);
+ struct fusbh200_qh *qh = ep->hcpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fusbh200->lock, flags);
+ qh->clearing_tt = 0;
+ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
+ && fusbh200->rh_state == FUSBH200_RH_RUNNING)
+ qh_link_async(fusbh200, qh);
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+}
+
+static void fusbh200_clear_tt_buffer(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh,
+ struct urb *urb, u32 token)
+{
+
+ /* If an async split transaction gets an error or is unlinked,
+ * the TT buffer may be left in an indeterminate state. We
+ * have to clear the TT buffer.
+ *
+ * Note: this routine is never called for Isochronous transfers.
+ */
+ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
+ struct usb_device *tt = urb->dev->tt->hub;
+
+ dev_dbg(&tt->dev,
+ "clear tt buffer port %d, a%d ep%d t%08x\n",
+ urb->dev->ttport, urb->dev->devnum,
+ usb_pipeendpoint(urb->pipe), token);
+
+ if (urb->dev->tt->hub !=
+ fusbh200_to_hcd(fusbh200)->self.root_hub) {
+ if (usb_hub_clear_tt_buffer(urb) == 0)
+ qh->clearing_tt = 1;
+ }
+ }
+}
+
+static int qtd_copy_status (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ size_t length,
+ u32 token
+)
+{
+ int status = -EINPROGRESS;
+
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely (QTD_PID (token) != 2))
+ urb->actual_length += length - QTD_LENGTH (token);
+
+ /* don't modify error codes */
+ if (unlikely(urb->unlinked))
+ return status;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely (IS_SHORT_READ (token)))
+ status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ status = -EOVERFLOW;
+ /* CERR nonzero + halt --> stall */
+ } else if (QTD_CERR(token)) {
+ status = -EPIPE;
+
+ /* In theory, more than one of the following bits can be set
+ * since they are sticky and the transaction is retried.
+ * Which to test first is rather arbitrary.
+ */
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ status = -EPROTO;
+ } else if (token & QTD_STS_DBE) {
+ status = (QTD_PID (token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ } else if (token & QTD_STS_XACT) {
+ /* timeout, bad CRC, wrong PID, etc */
+ fusbh200_dbg(fusbh200, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out");
+ status = -EPROTO;
+ } else { /* unknown */
+ status = -EPROTO;
+ }
+
+ fusbh200_dbg(fusbh200,
+ "dev%d ep%d%s qtd token %08x --> status %d\n",
+ usb_pipedevice (urb->pipe),
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ token, status);
+ }
+
+ return status;
+}
+
+static void
+fusbh200_urb_done(struct fusbh200_hcd *fusbh200, struct urb *urb, int status)
+__releases(fusbh200->lock)
+__acquires(fusbh200->lock)
+{
+ if (likely (urb->hcpriv != NULL)) {
+ struct fusbh200_qh *qh = (struct fusbh200_qh *) urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw->hw_info2 & cpu_to_hc32(fusbh200, QH_SMASK)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_int_reqs--;
+ }
+ }
+
+ if (unlikely(urb->unlinked)) {
+ COUNT(fusbh200->stats.unlink);
+ } else {
+ /* report non-error and short read status as zero */
+ if (status == -EINPROGRESS || status == -EREMOTEIO)
+ status = 0;
+ COUNT(fusbh200->stats.complete);
+ }
+
+#ifdef FUSBH200_URB_TRACE
+ fusbh200_dbg (fusbh200,
+ "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ status,
+ urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ /* complete() can reenter this HCD */
+ usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb);
+ spin_unlock (&fusbh200->lock);
+ usb_hcd_giveback_urb(fusbh200_to_hcd(fusbh200), urb, status);
+ spin_lock (&fusbh200->lock);
+}
+
+static int qh_schedule (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh);
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+static unsigned
+qh_completions (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ struct fusbh200_qtd *last, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int last_status;
+ int stopped;
+ unsigned count = 0;
+ u8 state;
+ struct fusbh200_qh_hw *hw = qh->hw;
+
+ if (unlikely (list_empty (&qh->qtd_list)))
+ return count;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ *
+ * It's a bug for qh->qh_state to be anything other than
+ * QH_STATE_IDLE, unless our caller is scan_async() or
+ * scan_intr().
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ rescan:
+ last = NULL;
+ last_status = -EINPROGRESS;
+ qh->needs_rescan = 0;
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe (entry, tmp, &qh->qtd_list) {
+ struct fusbh200_qtd *qtd;
+ struct urb *urb;
+ u32 token = 0;
+
+ qtd = list_entry (entry, struct fusbh200_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely (last->urb != urb)) {
+ fusbh200_urb_done(fusbh200, last->urb, last_status);
+ count++;
+ last_status = -EINPROGRESS;
+ }
+ fusbh200_qtd_free (fusbh200, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb ();
+ token = hc32_to_cpu(fusbh200, qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ retry_xacterr:
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ /* Report Data Buffer Error: non-fatal but useful */
+ if (token & QTD_STS_DBE)
+ fusbh200_dbg(fusbh200,
+ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ urb,
+ usb_endpoint_num(&urb->ep->desc),
+ usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd,
+ qh);
+
+ /* on STALL, error, and short reads this urb must
+ * complete and all its qtds must be recycled.
+ */
+ if ((token & QTD_STS_HALT) != 0) {
+
+ /* retry transaction errors until we
+ * reach the software xacterr limit
+ */
+ if ((token & QTD_STS_XACT) &&
+ QTD_CERR(token) == 0 &&
+ ++qh->xacterrs < QH_XACTERR_MAX &&
+ !urb->unlinked) {
+ fusbh200_dbg(fusbh200,
+ "detected XactErr len %zu/%zu retry %d\n",
+ qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs);
+
+ /* reset the token in the qtd and the
+ * qh overlay (which still contains
+ * the qtd) so that we pick up from
+ * where we left off
+ */
+ token &= ~QTD_STS_HALT;
+ token |= QTD_STS_ACTIVE |
+ (FUSBH200_TUNE_CERR << 10);
+ qtd->hw_token = cpu_to_hc32(fusbh200,
+ token);
+ wmb();
+ hw->hw_token = cpu_to_hc32(fusbh200,
+ token);
+ goto retry_xacterr;
+ }
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ *
+ * other short reads won't stop the queue, including
+ * control transfers (status stage handles that) or
+ * most other single-qtd reads ... the queue stops if
+ * URB_SHORT_NOT_OK was set so the driver submitting
+ * the urbs could clean it up.
+ */
+ } else if (IS_SHORT_READ (token)
+ && !(qtd->hw_alt_next
+ & FUSBH200_LIST_END(fusbh200))) {
+ stopped = 1;
+ }
+
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely (!stopped
+ && fusbh200->rh_state >= FUSBH200_RH_RUNNING)) {
+ break;
+
+ /* scan the whole queue for unlinks whenever it stops */
+ } else {
+ stopped = 1;
+
+ /* cancel everything if we halt, suspend, etc */
+ if (fusbh200->rh_state < FUSBH200_RH_RUNNING)
+ last_status = -ESHUTDOWN;
+
+ /* this qtd is active; skip it unless a previous qtd
+ * for its urb faulted, or its urb was canceled.
+ */
+ else if (last_status == -EINPROGRESS && !urb->unlinked)
+ continue;
+
+ /* qh unlinked; token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_hc32(fusbh200, qtd->qtd_dma)
+ == hw->hw_current) {
+ token = hc32_to_cpu(fusbh200, hw->hw_token);
+
+ /* An unlink may leave an incomplete
+ * async transaction in the TT buffer.
+ * We have to clear it.
+ */
+ fusbh200_clear_tt_buffer(fusbh200, qh, urb, token);
+ }
+ }
+
+ /* unless we already know the urb's status, collect qtd status
+ * and update count of bytes transferred. in common short read
+ * cases with only one data qtd (including control transfers),
+ * queue processing won't halt. but with two or more qtds (for
+ * example, with a 32 KB transfer), when the first qtd gets a
+ * short read the second must be removed by hand.
+ */
+ if (last_status == -EINPROGRESS) {
+ last_status = qtd_copy_status(fusbh200, urb,
+ qtd->length, token);
+ if (last_status == -EREMOTEIO
+ && (qtd->hw_alt_next
+ & FUSBH200_LIST_END(fusbh200)))
+ last_status = -EINPROGRESS;
+
+ /* As part of low/full-speed endpoint-halt processing
+ * we must clear the TT buffer (11.17.5).
+ */
+ if (unlikely(last_status != -EINPROGRESS &&
+ last_status != -EREMOTEIO)) {
+ /* The TT's in some hubs malfunction when they
+ * receive this request following a STALL (they
+ * stop sending isochronous packets). Since a
+ * STALL can't leave the TT buffer in a busy
+ * state (if you believe Figures 11-48 - 11-51
+ * in the USB 2.0 spec), we won't clear the TT
+ * buffer in this case. Strictly speaking this
+ * is a violation of the spec.
+ */
+ if (last_status != -EPIPE)
+ fusbh200_clear_tt_buffer(fusbh200, qh, urb,
+ token);
+ }
+ }
+
+ /* if we're removing something not at the queue head,
+ * patch the hardware queue pointer.
+ */
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry (qtd->qtd_list.prev,
+ struct fusbh200_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+
+ /* remove qtd; it's recycled after possible urb completion */
+ list_del (&qtd->qtd_list);
+ last = qtd;
+
+ /* reinit the xacterr counter for the next qtd */
+ qh->xacterrs = 0;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely (last != NULL)) {
+ fusbh200_urb_done(fusbh200, last->urb, last_status);
+ count++;
+ fusbh200_qtd_free (fusbh200, last);
+ }
+
+ /* Do we need to rescan for URBs dequeued during a giveback? */
+ if (unlikely(qh->needs_rescan)) {
+ /* If the QH is already unlinked, do the rescan now. */
+ if (state == QH_STATE_IDLE)
+ goto rescan;
+
+ /* Otherwise we have to wait until the QH is fully unlinked.
+ * Our caller will start an unlink if qh->needs_rescan is
+ * set. But if an unlink has already started, nothing needs
+ * to be done.
+ */
+ if (state != QH_STATE_LINKED)
+ qh->needs_rescan = 0;
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ */
+ if (stopped != 0 || hw->hw_qtd_next == FUSBH200_LIST_END(fusbh200)) {
+ switch (state) {
+ case QH_STATE_IDLE:
+ qh_refresh(fusbh200, qh);
+ break;
+ case QH_STATE_LINKED:
+ /* We won't refresh a QH that's linked (after the HC
+ * stopped the queue). That avoids a race:
+ * - HC reads first part of QH;
+ * - CPU updates that first part and the token;
+ * - HC reads rest of that QH, including token
+ * Result: HC gets an inconsistent image, and then
+ * DMAs to/from the wrong memory (corrupting it).
+ *
+ * That should be rare for interrupt transfers,
+ * except maybe high bandwidth ...
+ */
+
+ /* Tell the caller to start an unlink */
+ qh->needs_rescan = 1;
+ break;
+ /* otherwise, unlink already started */
+ }
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+// ... and packet size, for any kind of endpoint descriptor
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct list_head *qtd_list
+) {
+ struct list_head *entry, *temp;
+
+ list_for_each_safe (entry, temp, qtd_list) {
+ struct fusbh200_qtd *qtd;
+
+ qtd = list_entry (entry, struct fusbh200_qtd, qtd_list);
+ list_del (&qtd->qtd_list);
+ fusbh200_qtd_free (fusbh200, qtd);
+ }
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct list_head *head,
+ gfp_t flags
+) {
+ struct fusbh200_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, this_sg_len, maxpacket;
+ int is_input;
+ u32 token;
+ int i;
+ struct scatterlist *sg;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = fusbh200_qtd_alloc (fusbh200, flags);
+ if (unlikely (!qtd))
+ return NULL;
+ list_add_tail (&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (FUSBH200_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein (urb->pipe);
+ if (usb_pipecontrol (urb->pipe)) {
+ /* SETUP pid */
+ qtd_fill(fusbh200, qtd, urb->setup_dma,
+ sizeof (struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = fusbh200_qtd_alloc (fusbh200, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ i = urb->num_mapped_sgs;
+ if (len > 0 && i > 0) {
+ sg = urb->sg;
+ buf = sg_dma_address(sg);
+
+ /* urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ } else {
+ sg = NULL;
+ buf = urb->transfer_dma;
+ this_sg_len = len;
+ }
+
+ if (is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+
+ this_qtd_len = qtd_fill(fusbh200, qtd, buf, this_sg_len, token,
+ maxpacket);
+ this_sg_len -= this_qtd_len;
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+
+ /*
+ * short reads advance to a "magic" dummy instead of the next
+ * qtd ... that forces the queue to stop, for manual cleanup.
+ * (this will usually be overridden later.)
+ */
+ if (is_input)
+ qtd->hw_alt_next = fusbh200->async->hw->hw_alt_next;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely(this_sg_len <= 0)) {
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ buf = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ }
+
+ qtd_prev = qtd;
+ qtd = fusbh200_qtd_alloc (fusbh200, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * unless the caller requires manual cleanup after short reads,
+ * have the alt_next mechanism keep the queue running after the
+ * last data qtd (the only one, for control and most other cases).
+ */
+ if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol (urb->pipe)))
+ qtd->hw_alt_next = FUSBH200_LIST_END(fusbh200);
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * other OUT ones may need a terminating short packet
+ * (zero length).
+ */
+ if (likely (urb->transfer_buffer_length != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol (urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipeout(urb->pipe)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = fusbh200_qtd_alloc (fusbh200, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill(fusbh200, qtd, 0, 0, token, 0);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= cpu_to_hc32(fusbh200, QTD_IOC);
+ return head;
+
+cleanup:
+ qtd_list_free (fusbh200, urb, head);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// Would be best to create all qh's from config descriptors,
+// when each interface/altsetting is established. Unlink
+// any previous qh and cancel its urbs first; endpoints are
+// implicitly reset then (data toggle too).
+// That'd mean updating how usbcore talks to HCDs. (2.7?)
+
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct fusbh200_qh *
+qh_make (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ gfp_t flags
+) {
+ struct fusbh200_qh *qh = fusbh200_qh_alloc (fusbh200, flags);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+ struct usb_tt *tt = urb->dev->tt;
+ struct fusbh200_qh_hw *hw;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint (urb->pipe) << 8;
+ info1 |= usb_pipedevice (urb->pipe) << 0;
+
+ is_input = usb_pipein (urb->pipe);
+ type = usb_pipetype (urb->pipe);
+ maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+
+ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth
+ * acts like up to 3KB, but is built from smaller packets.
+ */
+ if (max_packet(maxp) > 1024) {
+ fusbh200_dbg(fusbh200, "bogus qh maxpacket %d\n", max_packet(maxp));
+ goto done;
+ }
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+ is_input, 0,
+ hb_mult(maxp) * max_packet(maxp)));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ qh->period = urb->interval >> 3;
+ if (qh->period == 0 && urb->interval != 1) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ urb->interval = 1;
+ } else if (qh->period > fusbh200->periodic_size) {
+ qh->period = fusbh200->periodic_size;
+ urb->interval = qh->period << 3;
+ }
+ } else {
+ int think_time;
+
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { // SPLIT, gap, CSPLIT+DATA
+ qh->c_usecs = qh->usecs + HS_USECS (0);
+ qh->usecs = HS_USECS (1);
+ } else { // SPLIT+DATA, gap, CSPLIT
+ qh->usecs += HS_USECS (1);
+ qh->c_usecs = HS_USECS (0);
+ }
+
+ think_time = tt ? tt->think_time : 0;
+ qh->tt_usecs = NS_TO_US (think_time +
+ usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, max_packet (maxp)));
+ qh->period = urb->interval;
+ if (qh->period > fusbh200->periodic_size) {
+ qh->period = fusbh200->periodic_size;
+ urb->interval = qh->period;
+ }
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->dev = urb->dev;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= QH_LOW_SPEED;
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (FUSBH200_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= QH_CONTROL_EP; /* for TT */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (FUSBH200_TUNE_MULT_TT << 30);
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (fusbh200_has_fsl_portno_bug(fusbh200))
+ info2 |= (urb->dev->ttport-1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
+
+ /* set the address of the TT; for TDI's integrated
+ * root hub tt, leave it zeroed.
+ */
+ if (tt && tt->hub != fusbh200_to_hcd(fusbh200)->self.root_hub)
+ info2 |= tt->hub->devnum << 16;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= QH_HIGH_SPEED;
+ if (type == PIPE_CONTROL) {
+ info1 |= (FUSBH200_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
+ info2 |= (FUSBH200_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (FUSBH200_TUNE_RL_HS << 28);
+ /* The USB spec says that high speed bulk endpoints
+ * always use 512 byte maxpacket. But some device
+ * vendors decided to ignore that, and MSFT is happy
+ * to help them do so. So now people expect to use
+ * such nonconformant devices with Linux too; sigh.
+ */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= (FUSBH200_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet (maxp) << 16;
+ info2 |= hb_mult (maxp) << 30;
+ }
+ break;
+ default:
+ fusbh200_dbg(fusbh200, "bogus dev %p speed %d\n", urb->dev,
+ urb->dev->speed);
+done:
+ qh_destroy(fusbh200, qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ hw = qh->hw;
+ hw->hw_info1 = cpu_to_hc32(fusbh200, info1);
+ hw->hw_info2 = cpu_to_hc32(fusbh200, info2);
+ qh->is_out = !is_input;
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
+ qh_refresh (fusbh200, qh);
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_async(struct fusbh200_hcd *fusbh200)
+{
+ if (fusbh200->async_count++)
+ return;
+
+ /* Stop waiting to turn off the async schedule */
+ fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_DISABLE_ASYNC);
+
+ /* Don't start the schedule until ASS is 0 */
+ fusbh200_poll_ASS(fusbh200);
+ turn_on_io_watchdog(fusbh200);
+}
+
+static void disable_async(struct fusbh200_hcd *fusbh200)
+{
+ if (--fusbh200->async_count)
+ return;
+
+ /* The async schedule and async_unlink list are supposed to be empty */
+ WARN_ON(fusbh200->async->qh_next.qh || fusbh200->async_unlink);
+
+ /* Don't turn off the schedule until ASS is 1 */
+ fusbh200_poll_ASS(fusbh200);
+}
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ __hc32 dma = QH_NEXT(fusbh200, qh->qh_dma);
+ struct fusbh200_qh *head;
+
+ /* Don't link a QH if there's a Clear-TT-Buffer pending */
+ if (unlikely(qh->clearing_tt))
+ return;
+
+ WARN_ON(qh->qh_state != QH_STATE_IDLE);
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ qh_refresh(fusbh200, qh);
+
+ /* splice right after start */
+ head = fusbh200->async;
+ qh->qh_next = head->qh_next;
+ qh->hw->hw_next = head->hw->hw_next;
+ wmb ();
+
+ head->qh_next.qh = qh;
+ head->hw->hw_next = dma;
+
+ qh->xacterrs = 0;
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+
+ enable_async(fusbh200);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct fusbh200_qh *qh_append_tds (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int epnum,
+ void **ptr
+)
+{
+ struct fusbh200_qh *qh = NULL;
+ __hc32 qh_addr_mask = cpu_to_hc32(fusbh200, 0x7f);
+
+ qh = (struct fusbh200_qh *) *ptr;
+ if (unlikely (qh == NULL)) {
+ /* can't sleep here, we have fusbh200->lock... */
+ qh = qh_make (fusbh200, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely (qh != NULL)) {
+ struct fusbh200_qtd *qtd;
+
+ if (unlikely (list_empty (qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry (qtd_list->next, struct fusbh200_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely (epnum == 0)) {
+
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice (urb->pipe) == 0)
+ qh->hw->hw_info1 &= ~qh_addr_mask;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely (qtd != NULL)) {
+ struct fusbh200_qtd *dummy;
+ dma_addr_t dma;
+ __hc32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT(fusbh200);
+
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del (&qtd->qtd_list);
+ list_add (&dummy->qtd_list, qtd_list);
+ list_splice_tail(qtd_list, &qh->qtd_list);
+
+ fusbh200_qtd_init(fusbh200, qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry (qh->qtd_list.prev,
+ struct fusbh200_qtd, qtd_list);
+ qtd->hw_next = QTD_NEXT(fusbh200, dma);
+
+ /* let the hc process these next qtds */
+ wmb ();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh;
+ }
+ }
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ int epnum;
+ unsigned long flags;
+ struct fusbh200_qh *qh = NULL;
+ int rc;
+
+ epnum = urb->ep->desc.bEndpointAddress;
+
+#ifdef FUSBH200_URB_TRACE
+ {
+ struct fusbh200_qtd *qtd;
+ qtd = list_entry(qtd_list->next, struct fusbh200_qtd, qtd_list);
+ fusbh200_dbg(fusbh200,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, urb->ep->hcpriv);
+ }
+#endif
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+ rc = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb);
+ if (unlikely(rc))
+ goto done;
+
+ qh = qh_append_tds(fusbh200, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async(fusbh200, qh);
+ done:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ if (unlikely (qh == NULL))
+ qtd_list_free (fusbh200, urb, qtd_list);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void single_unlink_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ struct fusbh200_qh *prev;
+
+ /* Add to the end of the list of QHs waiting for the next IAAD */
+ qh->qh_state = QH_STATE_UNLINK;
+ if (fusbh200->async_unlink)
+ fusbh200->async_unlink_last->unlink_next = qh;
+ else
+ fusbh200->async_unlink = qh;
+ fusbh200->async_unlink_last = qh;
+
+ /* Unlink it from the schedule */
+ prev = fusbh200->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw->hw_next = qh->hw->hw_next;
+ prev->qh_next = qh->qh_next;
+ if (fusbh200->qh_scan_next == qh)
+ fusbh200->qh_scan_next = qh->qh_next.qh;
+}
+
+static void start_iaa_cycle(struct fusbh200_hcd *fusbh200, bool nested)
+{
+ /*
+ * Do nothing if an IAA cycle is already running or
+ * if one will be started shortly.
+ */
+ if (fusbh200->async_iaa || fusbh200->async_unlinking)
+ return;
+
+ /* Do all the waiting QHs at once */
+ fusbh200->async_iaa = fusbh200->async_unlink;
+ fusbh200->async_unlink = NULL;
+
+ /* If the controller isn't running, we don't have to wait for it */
+ if (unlikely(fusbh200->rh_state < FUSBH200_RH_RUNNING)) {
+ if (!nested) /* Avoid recursion */
+ end_unlink_async(fusbh200);
+
+ /* Otherwise start a new IAA cycle */
+ } else if (likely(fusbh200->rh_state == FUSBH200_RH_RUNNING)) {
+ /* Make sure the unlinks are all visible to the hardware */
+ wmb();
+
+ fusbh200_writel(fusbh200, fusbh200->command | CMD_IAAD,
+ &fusbh200->regs->command);
+ fusbh200_readl(fusbh200, &fusbh200->regs->command);
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_IAA_WATCHDOG, true);
+ }
+}
+
+/* the async qh for the qtds being unlinked are now gone from the HC */
+
+static void end_unlink_async(struct fusbh200_hcd *fusbh200)
+{
+ struct fusbh200_qh *qh;
+
+ /* Process the idle QHs */
+ restart:
+ fusbh200->async_unlinking = true;
+ while (fusbh200->async_iaa) {
+ qh = fusbh200->async_iaa;
+ fusbh200->async_iaa = qh->unlink_next;
+ qh->unlink_next = NULL;
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+
+ qh_completions(fusbh200, qh);
+ if (!list_empty(&qh->qtd_list) &&
+ fusbh200->rh_state == FUSBH200_RH_RUNNING)
+ qh_link_async(fusbh200, qh);
+ disable_async(fusbh200);
+ }
+ fusbh200->async_unlinking = false;
+
+ /* Start a new IAA cycle if any QHs are waiting for it */
+ if (fusbh200->async_unlink) {
+ start_iaa_cycle(fusbh200, true);
+ if (unlikely(fusbh200->rh_state < FUSBH200_RH_RUNNING))
+ goto restart;
+ }
+}
+
+static void unlink_empty_async(struct fusbh200_hcd *fusbh200)
+{
+ struct fusbh200_qh *qh, *next;
+ bool stopped = (fusbh200->rh_state < FUSBH200_RH_RUNNING);
+ bool check_unlinks_later = false;
+
+ /* Unlink all the async QHs that have been empty for a timer cycle */
+ next = fusbh200->async->qh_next.qh;
+ while (next) {
+ qh = next;
+ next = qh->qh_next.qh;
+
+ if (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED) {
+ if (!stopped && qh->unlink_cycle ==
+ fusbh200->async_unlink_cycle)
+ check_unlinks_later = true;
+ else
+ single_unlink_async(fusbh200, qh);
+ }
+ }
+
+ /* Start a new IAA cycle if any QHs are waiting for it */
+ if (fusbh200->async_unlink)
+ start_iaa_cycle(fusbh200, false);
+
+ /* QHs that haven't been empty for long enough will be handled later */
+ if (check_unlinks_later) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_ASYNC_UNLINKS, true);
+ ++fusbh200->async_unlink_cycle;
+ }
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own fusbh200->lock */
+
+static void start_unlink_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ /*
+ * If the QH isn't linked then there's nothing we can do
+ * unless we were called during a giveback, in which case
+ * qh_completions() has to deal with it.
+ */
+ if (qh->qh_state != QH_STATE_LINKED) {
+ if (qh->qh_state == QH_STATE_COMPLETING)
+ qh->needs_rescan = 1;
+ return;
+ }
+
+ single_unlink_async(fusbh200, qh);
+ start_iaa_cycle(fusbh200, false);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async (struct fusbh200_hcd *fusbh200)
+{
+ struct fusbh200_qh *qh;
+ bool check_unlinks_later = false;
+
+ fusbh200->qh_scan_next = fusbh200->async->qh_next.qh;
+ while (fusbh200->qh_scan_next) {
+ qh = fusbh200->qh_scan_next;
+ fusbh200->qh_scan_next = qh->qh_next.qh;
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why fusbh200->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then fusbh200->qh_scan_next is adjusted
+ * in single_unlink_async().
+ */
+ temp = qh_completions(fusbh200, qh);
+ if (qh->needs_rescan) {
+ start_unlink_async(fusbh200, qh);
+ } else if (list_empty(&qh->qtd_list)
+ && qh->qh_state == QH_STATE_LINKED) {
+ qh->unlink_cycle = fusbh200->async_unlink_cycle;
+ check_unlinks_later = true;
+ } else if (temp != 0)
+ goto rescan;
+ }
+ }
+
+ /*
+ * Unlink empty entries, reducing DMA usage as well
+ * as HCD schedule-scanning costs. Delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ */
+ if (check_unlinks_later && fusbh200->rh_state == FUSBH200_RH_RUNNING &&
+ !(fusbh200->enabled_hrtimer_events &
+ BIT(FUSBH200_HRTIMER_ASYNC_UNLINKS))) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_ASYNC_UNLINKS, true);
+ ++fusbh200->async_unlink_cycle;
+ }
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * EHCI scheduled transaction support: interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ *
+ * Note that for interrupt transfers, the QH/QTD manipulation is shared
+ * with the "asynchronous" transaction support (control/bulk transfers).
+ * The only real difference is in how interrupt transfers are scheduled.
+ *
+ * For ISO, we make an "iso_stream" head to serve the same role as a QH.
+ * It keeps track of every ITD (or SITD) that's linked, and holds enough
+ * pre-calculated schedule data to make appending to the queue be quick.
+ */
+
+static int fusbh200_get_frame (struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd
+ * @tag: hardware tag for type of this record
+ */
+static union fusbh200_shadow *
+periodic_next_shadow(struct fusbh200_hcd *fusbh200, union fusbh200_shadow *periodic,
+ __hc32 tag)
+{
+ switch (hc32_to_cpu(fusbh200, tag)) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+ default:
+ return &periodic->itd->itd_next;
+ }
+}
+
+static __hc32 *
+shadow_next_periodic(struct fusbh200_hcd *fusbh200, union fusbh200_shadow *periodic,
+ __hc32 tag)
+{
+ switch (hc32_to_cpu(fusbh200, tag)) {
+ /* our fusbh200_shadow.qh is actually software part */
+ case Q_TYPE_QH:
+ return &periodic->qh->hw->hw_next;
+ /* others are hw parts */
+ default:
+ return periodic->hw_next;
+ }
+}
+
+/* caller must hold fusbh200->lock */
+static void periodic_unlink (struct fusbh200_hcd *fusbh200, unsigned frame, void *ptr)
+{
+ union fusbh200_shadow *prev_p = &fusbh200->pshadow[frame];
+ __hc32 *hw_p = &fusbh200->periodic[frame];
+ union fusbh200_shadow here = *prev_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow(fusbh200, prev_p,
+ Q_NEXT_TYPE(fusbh200, *hw_p));
+ hw_p = shadow_next_periodic(fusbh200, &here,
+ Q_NEXT_TYPE(fusbh200, *hw_p));
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr)
+ return;
+
+ /* update shadow and hardware lists ... the old "next" pointers
+ * from ptr may still be in use, the caller updates them.
+ */
+ *prev_p = *periodic_next_shadow(fusbh200, &here,
+ Q_NEXT_TYPE(fusbh200, *hw_p));
+
+ *hw_p = *shadow_next_periodic(fusbh200, &here,
+ Q_NEXT_TYPE(fusbh200, *hw_p));
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs (struct fusbh200_hcd *fusbh200, unsigned frame, unsigned uframe)
+{
+ __hc32 *hw_p = &fusbh200->periodic [frame];
+ union fusbh200_shadow *q = &fusbh200->pshadow [frame];
+ unsigned usecs = 0;
+ struct fusbh200_qh_hw *hw;
+
+ while (q->ptr) {
+ switch (hc32_to_cpu(fusbh200, Q_NEXT_TYPE(fusbh200, *hw_p))) {
+ case Q_TYPE_QH:
+ hw = q->qh->hw;
+ /* is it in the S-mask? */
+ if (hw->hw_info2 & cpu_to_hc32(fusbh200, 1 << uframe))
+ usecs += q->qh->usecs;
+ /* ... or C-mask? */
+ if (hw->hw_info2 & cpu_to_hc32(fusbh200,
+ 1 << (8 + uframe)))
+ usecs += q->qh->c_usecs;
+ hw_p = &hw->hw_next;
+ q = &q->qh->qh_next;
+ break;
+ // case Q_TYPE_FSTN:
+ default:
+ /* for "save place" FSTNs, count the relevant INTR
+ * bandwidth from the previous frame
+ */
+ if (q->fstn->hw_prev != FUSBH200_LIST_END(fusbh200)) {
+ fusbh200_dbg (fusbh200, "ignoring FSTN cost ...\n");
+ }
+ hw_p = &q->fstn->hw_next;
+ q = &q->fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ if (q->itd->hw_transaction[uframe])
+ usecs += q->itd->stream->usecs;
+ hw_p = &q->itd->hw_next;
+ q = &q->itd->itd_next;
+ break;
+ }
+ }
+ if (usecs > fusbh200->uframe_periodic_max)
+ fusbh200_err (fusbh200, "uframe %d sched overrun: %d usecs\n",
+ frame * 8 + uframe, usecs);
+ return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
+{
+ if (!dev1->tt || !dev2->tt)
+ return 0;
+ if (dev1->tt != dev2->tt)
+ return 0;
+ if (dev1->tt->multi)
+ return dev1->ttport == dev2->ttport;
+ else
+ return 1;
+}
+
+/* return true iff the device's transaction translator is available
+ * for a periodic transfer starting at the specified frame, using
+ * all the uframes in the mask.
+ */
+static int tt_no_collision (
+ struct fusbh200_hcd *fusbh200,
+ unsigned period,
+ struct usb_device *dev,
+ unsigned frame,
+ u32 uf_mask
+)
+{
+ if (period == 0) /* error */
+ return 0;
+
+ /* note bandwidth wastage: split never follows csplit
+ * (different dev or endpoint) until the next uframe.
+ * calling convention doesn't make that distinction.
+ */
+ for (; frame < fusbh200->periodic_size; frame += period) {
+ union fusbh200_shadow here;
+ __hc32 type;
+ struct fusbh200_qh_hw *hw;
+
+ here = fusbh200->pshadow [frame];
+ type = Q_NEXT_TYPE(fusbh200, fusbh200->periodic [frame]);
+ while (here.ptr) {
+ switch (hc32_to_cpu(fusbh200, type)) {
+ case Q_TYPE_ITD:
+ type = Q_NEXT_TYPE(fusbh200, here.itd->hw_next);
+ here = here.itd->itd_next;
+ continue;
+ case Q_TYPE_QH:
+ hw = here.qh->hw;
+ if (same_tt (dev, here.qh->dev)) {
+ u32 mask;
+
+ mask = hc32_to_cpu(fusbh200,
+ hw->hw_info2);
+ /* "knows" no gap is needed */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE(fusbh200, hw->hw_next);
+ here = here.qh->qh_next;
+ continue;
+ // case Q_TYPE_FSTN:
+ default:
+ fusbh200_dbg (fusbh200,
+ "periodic frame %d bogus type %d\n",
+ frame, type);
+ }
+
+ /* collision or error */
+ return 0;
+ }
+ }
+
+ /* no collision */
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void enable_periodic(struct fusbh200_hcd *fusbh200)
+{
+ if (fusbh200->periodic_count++)
+ return;
+
+ /* Stop waiting to turn off the periodic schedule */
+ fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_DISABLE_PERIODIC);
+
+ /* Don't start the schedule until PSS is 0 */
+ fusbh200_poll_PSS(fusbh200);
+ turn_on_io_watchdog(fusbh200);
+}
+
+static void disable_periodic(struct fusbh200_hcd *fusbh200)
+{
+ if (--fusbh200->periodic_count)
+ return;
+
+ /* Don't turn off the schedule until PSS is 1 */
+ fusbh200_poll_PSS(fusbh200);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* periodic schedule slots have iso tds (normal or split) first, then a
+ * sparse tree for active interrupt transfers.
+ *
+ * this just links in a qh; caller guarantees uframe masks are set right.
+ * no FSTN support (yet; fusbh200 0.96+)
+ */
+static void qh_link_periodic(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ unsigned i;
+ unsigned period = qh->period;
+
+ dev_dbg (&qh->dev->dev,
+ "link qh%d-%04x/%p start %d [%d/%d us]\n",
+ period, hc32_to_cpup(fusbh200, &qh->hw->hw_info2)
+ & (QH_CMASK | QH_SMASK),
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* high bandwidth, or otherwise every microframe */
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->start; i < fusbh200->periodic_size; i += period) {
+ union fusbh200_shadow *prev = &fusbh200->pshadow[i];
+ __hc32 *hw_p = &fusbh200->periodic[i];
+ union fusbh200_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip the iso nodes at list head */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(fusbh200, *hw_p);
+ if (type == cpu_to_hc32(fusbh200, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(fusbh200, prev, type);
+ hw_p = shadow_next_periodic(fusbh200, &here, type);
+ here = *prev;
+ }
+
+ /* sorting each branch by period (slow-->fast)
+ * enables sharing interior tree nodes
+ */
+ while (here.ptr && qh != here.qh) {
+ if (qh->period > here.qh->period)
+ break;
+ prev = &here.qh->qh_next;
+ hw_p = &here.qh->hw->hw_next;
+ here = *prev;
+ }
+ /* link in this qh, unless some earlier pass did that */
+ if (qh != here.qh) {
+ qh->qh_next = here;
+ if (here.qh)
+ qh->hw->hw_next = *hw_p;
+ wmb ();
+ prev->qh = qh;
+ *hw_p = QH_NEXT (fusbh200, qh->qh_dma);
+ }
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ qh->xacterrs = 0;
+
+ /* update per-qh bandwidth for usbfs */
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated += qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ list_add(&qh->intr_node, &fusbh200->intr_qh_list);
+
+ /* maybe enable periodic schedule processing */
+ ++fusbh200->intr_count;
+ enable_periodic(fusbh200);
+}
+
+static void qh_unlink_periodic(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ unsigned i;
+ unsigned period;
+
+ /*
+ * If qh is for a low/full-speed device, simply unlinking it
+ * could interfere with an ongoing split transaction. To unlink
+ * it safely would require setting the QH_INACTIVATE bit and
+ * waiting at least one frame, as described in EHCI 4.12.2.5.
+ *
+ * We won't bother with any of this. Instead, we assume that the
+ * only reason for unlinking an interrupt QH while the current URB
+ * is still active is to dequeue all the URBs (flush the whole
+ * endpoint queue).
+ *
+ * If rebalancing the periodic schedule is ever implemented, this
+ * approach will no longer be valid.
+ */
+
+ /* high bandwidth, or otherwise part of every microframe */
+ if ((period = qh->period) == 0)
+ period = 1;
+
+ for (i = qh->start; i < fusbh200->periodic_size; i += period)
+ periodic_unlink (fusbh200, i, qh);
+
+ /* update per-qh bandwidth for usbfs */
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated -= qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ dev_dbg (&qh->dev->dev,
+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
+ qh->period,
+ hc32_to_cpup(fusbh200, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* qh->qh_next still "live" to HC */
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = NULL;
+
+ if (fusbh200->qh_scan_next == qh)
+ fusbh200->qh_scan_next = list_entry(qh->intr_node.next,
+ struct fusbh200_qh, intr_node);
+ list_del(&qh->intr_node);
+}
+
+static void start_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ /* If the QH isn't linked then there's nothing we can do
+ * unless we were called during a giveback, in which case
+ * qh_completions() has to deal with it.
+ */
+ if (qh->qh_state != QH_STATE_LINKED) {
+ if (qh->qh_state == QH_STATE_COMPLETING)
+ qh->needs_rescan = 1;
+ return;
+ }
+
+ qh_unlink_periodic (fusbh200, qh);
+
+ /* Make sure the unlinks are visible before starting the timer */
+ wmb();
+
+ /*
+ * The EHCI spec doesn't say how long it takes the controller to
+ * stop accessing an unlinked interrupt QH. The timer delay is
+ * 9 uframes; presumably that will be long enough.
+ */
+ qh->unlink_cycle = fusbh200->intr_unlink_cycle;
+
+ /* New entries go at the end of the intr_unlink list */
+ if (fusbh200->intr_unlink)
+ fusbh200->intr_unlink_last->unlink_next = qh;
+ else
+ fusbh200->intr_unlink = qh;
+ fusbh200->intr_unlink_last = qh;
+
+ if (fusbh200->intr_unlinking)
+ ; /* Avoid recursive calls */
+ else if (fusbh200->rh_state < FUSBH200_RH_RUNNING)
+ fusbh200_handle_intr_unlinks(fusbh200);
+ else if (fusbh200->intr_unlink == qh) {
+ fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_UNLINK_INTR, true);
+ ++fusbh200->intr_unlink_cycle;
+ }
+}
+
+static void end_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ struct fusbh200_qh_hw *hw = qh->hw;
+ int rc;
+
+ qh->qh_state = QH_STATE_IDLE;
+ hw->hw_next = FUSBH200_LIST_END(fusbh200);
+
+ qh_completions(fusbh200, qh);
+
+ /* reschedule QH iff another request is queued */
+ if (!list_empty(&qh->qtd_list) && fusbh200->rh_state == FUSBH200_RH_RUNNING) {
+ rc = qh_schedule(fusbh200, qh);
+
+ /* An error here likely indicates handshake failure
+ * or no space left in the schedule. Neither fault
+ * should happen often ...
+ *
+ * FIXME kill the now-dysfunctional queued urbs
+ */
+ if (rc != 0)
+ fusbh200_err(fusbh200, "can't reschedule qh %p, err %d\n",
+ qh, rc);
+ }
+
+ /* maybe turn off periodic schedule */
+ --fusbh200->intr_count;
+ disable_periodic(fusbh200);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_period (
+ struct fusbh200_hcd *fusbh200,
+ unsigned frame,
+ unsigned uframe,
+ unsigned period,
+ unsigned usecs
+) {
+ int claimed;
+
+ /* complete split running into next frame?
+ * given FSTN support, we could sometimes check...
+ */
+ if (uframe >= 8)
+ return 0;
+
+ /* convert "usecs we need" to "max already claimed" */
+ usecs = fusbh200->uframe_periodic_max - usecs;
+
+ /* we "know" 2 and 4 uframe intervals were rejected; so
+ * for period 0, check _every_ microframe in the schedule.
+ */
+ if (unlikely (period == 0)) {
+ do {
+ for (uframe = 0; uframe < 7; uframe++) {
+ claimed = periodic_usecs (fusbh200, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ }
+ } while ((frame += 1) < fusbh200->periodic_size);
+
+ /* just check the specified uframe, at that period */
+ } else {
+ do {
+ claimed = periodic_usecs (fusbh200, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ } while ((frame += period) < fusbh200->periodic_size);
+ }
+
+ // success!
+ return 1;
+}
+
+static int check_intr_schedule (
+ struct fusbh200_hcd *fusbh200,
+ unsigned frame,
+ unsigned uframe,
+ const struct fusbh200_qh *qh,
+ __hc32 *c_maskp
+)
+{
+ int retval = -ENOSPC;
+ u8 mask = 0;
+
+ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
+ goto done;
+
+ if (!check_period (fusbh200, frame, uframe, qh->period, qh->usecs))
+ goto done;
+ if (!qh->c_usecs) {
+ retval = 0;
+ *c_maskp = 0;
+ goto done;
+ }
+
+ /* Make sure this tt's buffer is also available for CSPLITs.
+ * We pessimize a bit; probably the typical full speed case
+ * doesn't need the second CSPLIT.
+ *
+ * NOTE: both SPLIT and CSPLIT could be checked in just
+ * one smart pass...
+ */
+ mask = 0x03 << (uframe + qh->gap_uf);
+ *c_maskp = cpu_to_hc32(fusbh200, mask << 8);
+
+ mask |= 1 << uframe;
+ if (tt_no_collision (fusbh200, qh->period, qh->dev, frame, mask)) {
+ if (!check_period (fusbh200, frame, uframe + qh->gap_uf + 1,
+ qh->period, qh->c_usecs))
+ goto done;
+ if (!check_period (fusbh200, frame, uframe + qh->gap_uf,
+ qh->period, qh->c_usecs))
+ goto done;
+ retval = 0;
+ }
+done:
+ return retval;
+}
+
+/* "first fit" scheduling policy used the first time through,
+ * or when the previous schedule slot can't be re-used.
+ */
+static int qh_schedule(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh)
+{
+ int status;
+ unsigned uframe;
+ __hc32 c_mask;
+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+ struct fusbh200_qh_hw *hw = qh->hw;
+
+ qh_refresh(fusbh200, qh);
+ hw->hw_next = FUSBH200_LIST_END(fusbh200);
+ frame = qh->start;
+
+ /* reuse the previous schedule slots, if we can */
+ if (frame < qh->period) {
+ uframe = ffs(hc32_to_cpup(fusbh200, &hw->hw_info2) & QH_SMASK);
+ status = check_intr_schedule (fusbh200, frame, --uframe,
+ qh, &c_mask);
+ } else {
+ uframe = 0;
+ c_mask = 0;
+ status = -ENOSPC;
+ }
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ if (status) {
+ /* "normal" case, uframing flexible except with splits */
+ if (qh->period) {
+ int i;
+
+ for (i = qh->period; status && i > 0; --i) {
+ frame = ++fusbh200->random_frame % qh->period;
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule (fusbh200,
+ frame, uframe, qh,
+ &c_mask);
+ if (status == 0)
+ break;
+ }
+ }
+
+ /* qh->period == 0 means every uframe */
+ } else {
+ frame = 0;
+ status = check_intr_schedule (fusbh200, 0, 0, qh, &c_mask);
+ }
+ if (status)
+ goto done;
+ qh->start = frame;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ hw->hw_info2 &= cpu_to_hc32(fusbh200, ~(QH_CMASK | QH_SMASK));
+ hw->hw_info2 |= qh->period
+ ? cpu_to_hc32(fusbh200, 1 << uframe)
+ : cpu_to_hc32(fusbh200, QH_SMASK);
+ hw->hw_info2 |= c_mask;
+ } else
+ fusbh200_dbg (fusbh200, "reused qh %p schedule\n", qh);
+
+ /* stuff into the periodic schedule */
+ qh_link_periodic(fusbh200, qh);
+done:
+ return status;
+}
+
+static int intr_submit (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ gfp_t mem_flags
+) {
+ unsigned epnum;
+ unsigned long flags;
+ struct fusbh200_qh *qh;
+ int status;
+ struct list_head empty;
+
+ /* get endpoint and transfer/schedule data */
+ epnum = urb->ep->desc.bEndpointAddress;
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD (&empty);
+ qh = qh_append_tds(fusbh200, urb, &empty, epnum, &urb->ep->hcpriv);
+ if (qh == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ if (qh->qh_state == QH_STATE_IDLE) {
+ if ((status = qh_schedule (fusbh200, qh)) != 0)
+ goto done;
+ }
+
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds(fusbh200, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ BUG_ON (qh == NULL);
+
+ /* ... update usbfs periodic stats */
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_int_reqs++;
+
+done:
+ if (unlikely(status))
+ usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb);
+done_not_linked:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ if (status)
+ qtd_list_free (fusbh200, urb, qtd_list);
+
+ return status;
+}
+
+static void scan_intr(struct fusbh200_hcd *fusbh200)
+{
+ struct fusbh200_qh *qh;
+
+ list_for_each_entry_safe(qh, fusbh200->qh_scan_next, &fusbh200->intr_qh_list,
+ intr_node) {
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why fusbh200->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then fusbh200->qh_scan_next is adjusted
+ * in qh_unlink_periodic().
+ */
+ temp = qh_completions(fusbh200, qh);
+ if (unlikely(qh->needs_rescan ||
+ (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED)))
+ start_unlink_intr(fusbh200, qh);
+ else if (temp != 0)
+ goto rescan;
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* fusbh200_iso_stream ops work with both ITD and SITD */
+
+static struct fusbh200_iso_stream *
+iso_stream_alloc (gfp_t mem_flags)
+{
+ struct fusbh200_iso_stream *stream;
+
+ stream = kzalloc(sizeof *stream, mem_flags);
+ if (likely (stream != NULL)) {
+ INIT_LIST_HEAD(&stream->td_list);
+ INIT_LIST_HEAD(&stream->free_list);
+ stream->next_uframe = -1;
+ }
+ return stream;
+}
+
+static void
+iso_stream_init (
+ struct fusbh200_hcd *fusbh200,
+ struct fusbh200_iso_stream *stream,
+ struct usb_device *dev,
+ int pipe,
+ unsigned interval
+)
+{
+ u32 buf1;
+ unsigned epnum, maxp;
+ int is_input;
+ long bandwidth;
+ unsigned multi;
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's wMaxPacket field
+ */
+ epnum = usb_pipeendpoint (pipe);
+ is_input = usb_pipein (pipe) ? USB_DIR_IN : 0;
+ maxp = usb_maxpacket(dev, pipe, !is_input);
+ if (is_input) {
+ buf1 = (1 << 11);
+ } else {
+ buf1 = 0;
+ }
+
+ maxp = max_packet(maxp);
+ multi = hb_mult(maxp);
+ buf1 |= maxp;
+ maxp *= multi;
+
+ stream->buf0 = cpu_to_hc32(fusbh200, (epnum << 8) | dev->devnum);
+ stream->buf1 = cpu_to_hc32(fusbh200, buf1);
+ stream->buf2 = cpu_to_hc32(fusbh200, multi);
+
+ /* usbfs wants to report the average usecs per frame tied up
+ * when transfers on this endpoint are scheduled ...
+ */
+ if (dev->speed == USB_SPEED_FULL) {
+ interval <<= 3;
+ stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed,
+ is_input, 1, maxp));
+ stream->usecs /= 8;
+ } else {
+ stream->highspeed = 1;
+ stream->usecs = HS_USECS_ISO (maxp);
+ }
+ bandwidth = stream->usecs * 8;
+ bandwidth /= interval;
+
+ stream->bandwidth = bandwidth;
+ stream->udev = dev;
+ stream->bEndpointAddress = is_input | epnum;
+ stream->interval = interval;
+ stream->maxp = maxp;
+}
+
+static struct fusbh200_iso_stream *
+iso_stream_find (struct fusbh200_hcd *fusbh200, struct urb *urb)
+{
+ unsigned epnum;
+ struct fusbh200_iso_stream *stream;
+ struct usb_host_endpoint *ep;
+ unsigned long flags;
+
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein(urb->pipe))
+ ep = urb->dev->ep_in[epnum];
+ else
+ ep = urb->dev->ep_out[epnum];
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ stream = ep->hcpriv;
+
+ if (unlikely (stream == NULL)) {
+ stream = iso_stream_alloc(GFP_ATOMIC);
+ if (likely (stream != NULL)) {
+ ep->hcpriv = stream;
+ stream->ep = ep;
+ iso_stream_init(fusbh200, stream, urb->dev, urb->pipe,
+ urb->interval);
+ }
+
+ /* if dev->ep [epnum] is a QH, hw is set */
+ } else if (unlikely (stream->hw != NULL)) {
+ fusbh200_dbg (fusbh200, "dev %s ep%d%s, not iso??\n",
+ urb->dev->devpath, epnum,
+ usb_pipein(urb->pipe) ? "in" : "out");
+ stream = NULL;
+ }
+
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ return stream;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* fusbh200_iso_sched ops can be ITD-only or SITD-only */
+
+static struct fusbh200_iso_sched *
+iso_sched_alloc (unsigned packets, gfp_t mem_flags)
+{
+ struct fusbh200_iso_sched *iso_sched;
+ int size = sizeof *iso_sched;
+
+ size += packets * sizeof (struct fusbh200_iso_packet);
+ iso_sched = kzalloc(size, mem_flags);
+ if (likely (iso_sched != NULL)) {
+ INIT_LIST_HEAD (&iso_sched->td_list);
+ }
+ return iso_sched;
+}
+
+static inline void
+itd_sched_init(
+ struct fusbh200_hcd *fusbh200,
+ struct fusbh200_iso_sched *iso_sched,
+ struct fusbh200_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many uframes are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->interval;
+
+ /* figure out per-uframe itd fields that we'll need later
+ * when we fit new itds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct fusbh200_iso_packet *uframe = &iso_sched->packet [i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc [i].length;
+ buf = dma + urb->iso_frame_desc [i].offset;
+
+ trans = FUSBH200_ISOC_ACTIVE;
+ trans |= buf & 0x0fff;
+ if (unlikely (((i + 1) == urb->number_of_packets))
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= FUSBH200_ITD_IOC;
+ trans |= length << 16;
+ uframe->transaction = cpu_to_hc32(fusbh200, trans);
+
+ /* might need to cross a buffer page within a uframe */
+ uframe->bufp = (buf & ~(u64)0x0fff);
+ buf += length;
+ if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
+ uframe->cross = 1;
+ }
+}
+
+static void
+iso_sched_free (
+ struct fusbh200_iso_stream *stream,
+ struct fusbh200_iso_sched *iso_sched
+)
+{
+ if (!iso_sched)
+ return;
+ // caller must hold fusbh200->lock!
+ list_splice (&iso_sched->td_list, &stream->free_list);
+ kfree (iso_sched);
+}
+
+static int
+itd_urb_transaction (
+ struct fusbh200_iso_stream *stream,
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ gfp_t mem_flags
+)
+{
+ struct fusbh200_itd *itd;
+ dma_addr_t itd_dma;
+ int i;
+ unsigned num_itds;
+ struct fusbh200_iso_sched *sched;
+ unsigned long flags;
+
+ sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+ if (unlikely (sched == NULL))
+ return -ENOMEM;
+
+ itd_sched_init(fusbh200, sched, stream, urb);
+
+ if (urb->interval < 8)
+ num_itds = 1 + (sched->span + 7) / 8;
+ else
+ num_itds = urb->number_of_packets;
+
+ /* allocate/init ITDs */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ for (i = 0; i < num_itds; i++) {
+
+ /*
+ * Use iTDs from the free list, but not iTDs that may
+ * still be in use by the hardware.
+ */
+ if (likely(!list_empty(&stream->free_list))) {
+ itd = list_first_entry(&stream->free_list,
+ struct fusbh200_itd, itd_list);
+ if (itd->frame == fusbh200->now_frame)
+ goto alloc_itd;
+ list_del (&itd->itd_list);
+ itd_dma = itd->itd_dma;
+ } else {
+ alloc_itd:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ itd = dma_pool_alloc (fusbh200->itd_pool, mem_flags,
+ &itd_dma);
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ if (!itd) {
+ iso_sched_free(stream, sched);
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+ return -ENOMEM;
+ }
+ }
+
+ memset (itd, 0, sizeof *itd);
+ itd->itd_dma = itd_dma;
+ list_add (&itd->itd_list, &sched->td_list);
+ }
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = sched;
+ urb->error_count = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+itd_slot_ok (
+ struct fusbh200_hcd *fusbh200,
+ u32 mod,
+ u32 uframe,
+ u8 usecs,
+ u32 period
+)
+{
+ uframe %= period;
+ do {
+ /* can't commit more than uframe_periodic_max usec */
+ if (periodic_usecs (fusbh200, uframe >> 3, uframe & 0x7)
+ > (fusbh200->uframe_periodic_max - usecs))
+ return 0;
+
+ /* we know urb->interval is 2^N uframes */
+ uframe += period;
+ } while (uframe < mod);
+ return 1;
+}
+
+/*
+ * This scheduler plans almost as far into the future as it has actual
+ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
+ * "as small as possible" to be cache-friendlier.) That limits the size
+ * transfers you can stream reliably; avoid more than 64 msec per urb.
+ * Also avoid queue depths of less than fusbh200's worst irq latency (affected
+ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
+ * and other factors); or more than about 230 msec total (for portability,
+ * given FUSBH200_TUNE_FLS and the slop). Or, write a smarter scheduler!
+ */
+
+#define SCHEDULE_SLOP 80 /* microframes */
+
+static int
+iso_stream_schedule (
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ struct fusbh200_iso_stream *stream
+)
+{
+ u32 now, next, start, period, span;
+ int status;
+ unsigned mod = fusbh200->periodic_size << 3;
+ struct fusbh200_iso_sched *sched = urb->hcpriv;
+
+ period = urb->interval;
+ span = sched->span;
+
+ if (span > mod - SCHEDULE_SLOP) {
+ fusbh200_dbg (fusbh200, "iso request %p too long\n", urb);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ now = fusbh200_read_frame_index(fusbh200) & (mod - 1);
+
+ /* Typical case: reuse current schedule, stream is still active.
+ * Hopefully there are no gaps from the host falling behind
+ * (irq delays etc), but if there are we'll take the next
+ * slot in the schedule, implicitly assuming URB_ISO_ASAP.
+ */
+ if (likely (!list_empty (&stream->td_list))) {
+ u32 excess;
+
+ /* For high speed devices, allow scheduling within the
+ * isochronous scheduling threshold. For full speed devices
+ * and Intel PCI-based controllers, don't (work around for
+ * Intel ICH9 bug).
+ */
+ if (!stream->highspeed && fusbh200->fs_i_thresh)
+ next = now + fusbh200->i_thresh;
+ else
+ next = now;
+
+ /* Fell behind (by up to twice the slop amount)?
+ * We decide based on the time of the last currently-scheduled
+ * slot, not the time of the next available slot.
+ */
+ excess = (stream->next_uframe - period - next) & (mod - 1);
+ if (excess >= mod - 2 * SCHEDULE_SLOP)
+ start = next + excess - mod + period *
+ DIV_ROUND_UP(mod - excess, period);
+ else
+ start = next + excess + period;
+ if (start - now >= mod) {
+ fusbh200_dbg(fusbh200, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now - period, period,
+ mod);
+ status = -EFBIG;
+ goto fail;
+ }
+ }
+
+ /* need to schedule; when's the next (u)frame we could start?
+ * this is bigger than fusbh200->i_thresh allows; scheduling itself
+ * isn't free, the slop should handle reasonably slow cpus. it
+ * can also help high bandwidth if the dma and irq loads don't
+ * jump until after the queue is primed.
+ */
+ else {
+ int done = 0;
+ start = SCHEDULE_SLOP + (now & ~0x07);
+
+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+ /* find a uframe slot with enough bandwidth.
+ * Early uframes are more precious because full-speed
+ * iso IN transfers can't use late uframes,
+ * and therefore they should be allocated last.
+ */
+ next = start;
+ start += period;
+ do {
+ start--;
+ /* check schedule: enough space? */
+ if (itd_slot_ok(fusbh200, mod, start,
+ stream->usecs, period))
+ done = 1;
+ } while (start > next && !done);
+
+ /* no room in the schedule */
+ if (!done) {
+ fusbh200_dbg(fusbh200, "iso resched full %p (now %d max %d)\n",
+ urb, now, now + mod);
+ status = -ENOSPC;
+ goto fail;
+ }
+ }
+
+ /* Tried to schedule too far into the future? */
+ if (unlikely(start - now + span - period
+ >= mod - 2 * SCHEDULE_SLOP)) {
+ fusbh200_dbg(fusbh200, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now, span - period,
+ mod - 2 * SCHEDULE_SLOP);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ stream->next_uframe = start & (mod - 1);
+
+ /* report high speed start in uframes; full speed, in frames */
+ urb->start_frame = stream->next_uframe;
+ if (!stream->highspeed)
+ urb->start_frame >>= 3;
+
+ /* Make sure scan_isoc() sees these */
+ if (fusbh200->isoc_count == 0)
+ fusbh200->next_frame = now >> 3;
+ return 0;
+
+ fail:
+ iso_sched_free(stream, sched);
+ urb->hcpriv = NULL;
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+itd_init(struct fusbh200_hcd *fusbh200, struct fusbh200_iso_stream *stream,
+ struct fusbh200_itd *itd)
+{
+ int i;
+
+ /* it's been recently zeroed */
+ itd->hw_next = FUSBH200_LIST_END(fusbh200);
+ itd->hw_bufp [0] = stream->buf0;
+ itd->hw_bufp [1] = stream->buf1;
+ itd->hw_bufp [2] = stream->buf2;
+
+ for (i = 0; i < 8; i++)
+ itd->index[i] = -1;
+
+ /* All other fields are filled when scheduling */
+}
+
+static inline void
+itd_patch(
+ struct fusbh200_hcd *fusbh200,
+ struct fusbh200_itd *itd,
+ struct fusbh200_iso_sched *iso_sched,
+ unsigned index,
+ u16 uframe
+)
+{
+ struct fusbh200_iso_packet *uf = &iso_sched->packet [index];
+ unsigned pg = itd->pg;
+
+ // BUG_ON (pg == 6 && uf->cross);
+
+ uframe &= 0x07;
+ itd->index [uframe] = index;
+
+ itd->hw_transaction[uframe] = uf->transaction;
+ itd->hw_transaction[uframe] |= cpu_to_hc32(fusbh200, pg << 12);
+ itd->hw_bufp[pg] |= cpu_to_hc32(fusbh200, uf->bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fusbh200, (u32)(uf->bufp >> 32));
+
+ /* iso_frame_desc[].offset must be strictly increasing */
+ if (unlikely (uf->cross)) {
+ u64 bufp = uf->bufp + 4096;
+
+ itd->pg = ++pg;
+ itd->hw_bufp[pg] |= cpu_to_hc32(fusbh200, bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fusbh200, (u32)(bufp >> 32));
+ }
+}
+
+static inline void
+itd_link (struct fusbh200_hcd *fusbh200, unsigned frame, struct fusbh200_itd *itd)
+{
+ union fusbh200_shadow *prev = &fusbh200->pshadow[frame];
+ __hc32 *hw_p = &fusbh200->periodic[frame];
+ union fusbh200_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip any iso nodes which might belong to previous microframes */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(fusbh200, *hw_p);
+ if (type == cpu_to_hc32(fusbh200, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(fusbh200, prev, type);
+ hw_p = shadow_next_periodic(fusbh200, &here, type);
+ here = *prev;
+ }
+
+ itd->itd_next = here;
+ itd->hw_next = *hw_p;
+ prev->itd = itd;
+ itd->frame = frame;
+ wmb ();
+ *hw_p = cpu_to_hc32(fusbh200, itd->itd_dma | Q_TYPE_ITD);
+}
+
+/* fit urb's itds into the selected schedule slot; activate as needed */
+static void itd_link_urb(
+ struct fusbh200_hcd *fusbh200,
+ struct urb *urb,
+ unsigned mod,
+ struct fusbh200_iso_stream *stream
+)
+{
+ int packet;
+ unsigned next_uframe, uframe, frame;
+ struct fusbh200_iso_sched *iso_sched = urb->hcpriv;
+ struct fusbh200_itd *itd;
+
+ next_uframe = stream->next_uframe & (mod - 1);
+
+ if (unlikely (list_empty(&stream->td_list))) {
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated
+ += stream->bandwidth;
+ fusbh200_dbg(fusbh200,
+ "schedule devp %s ep%d%s-iso period %d start %d.%d\n",
+ urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ urb->interval,
+ next_uframe >> 3, next_uframe & 0x7);
+ }
+
+ /* fill iTDs uframe by uframe */
+ for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) {
+ if (itd == NULL) {
+ /* ASSERT: we have all necessary itds */
+ // BUG_ON (list_empty (&iso_sched->td_list));
+
+ /* ASSERT: no itds for this endpoint in this uframe */
+
+ itd = list_entry (iso_sched->td_list.next,
+ struct fusbh200_itd, itd_list);
+ list_move_tail (&itd->itd_list, &stream->td_list);
+ itd->stream = stream;
+ itd->urb = urb;
+ itd_init (fusbh200, stream, itd);
+ }
+
+ uframe = next_uframe & 0x07;
+ frame = next_uframe >> 3;
+
+ itd_patch(fusbh200, itd, iso_sched, packet, uframe);
+
+ next_uframe += stream->interval;
+ next_uframe &= mod - 1;
+ packet++;
+
+ /* link completed itds into the schedule */
+ if (((next_uframe >> 3) != frame)
+ || packet == urb->number_of_packets) {
+ itd_link(fusbh200, frame & (fusbh200->periodic_size - 1), itd);
+ itd = NULL;
+ }
+ }
+ stream->next_uframe = next_uframe;
+
+ /* don't need that schedule data any more */
+ iso_sched_free (stream, iso_sched);
+ urb->hcpriv = NULL;
+
+ ++fusbh200->isoc_count;
+ enable_periodic(fusbh200);
+}
+
+#define ISO_ERRS (FUSBH200_ISOC_BUF_ERR | FUSBH200_ISOC_BABBLE | FUSBH200_ISOC_XACTERR)
+
+/* Process and recycle a completed ITD. Return true iff its urb completed,
+ * and hence its completion callback probably added things to the hardware
+ * schedule.
+ *
+ * Note that we carefully avoid recycling this descriptor until after any
+ * completion callback runs, so that it won't be reused quickly. That is,
+ * assuming (a) no more than two urbs per frame on this endpoint, and also
+ * (b) only this endpoint's completions submit URBs. It seems some silicon
+ * corrupts things if you reuse completed descriptors very quickly...
+ */
+static bool itd_complete(struct fusbh200_hcd *fusbh200, struct fusbh200_itd *itd)
+{
+ struct urb *urb = itd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ unsigned uframe;
+ int urb_index = -1;
+ struct fusbh200_iso_stream *stream = itd->stream;
+ struct usb_device *dev;
+ bool retval = false;
+
+ /* for each uframe with a packet */
+ for (uframe = 0; uframe < 8; uframe++) {
+ if (likely (itd->index[uframe] == -1))
+ continue;
+ urb_index = itd->index[uframe];
+ desc = &urb->iso_frame_desc [urb_index];
+
+ t = hc32_to_cpup(fusbh200, &itd->hw_transaction [uframe]);
+ itd->hw_transaction [uframe] = 0;
+
+ /* report transfer status */
+ if (unlikely (t & ISO_ERRS)) {
+ urb->error_count++;
+ if (t & FUSBH200_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & FUSBH200_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & FUSBH200_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+
+ /* HC need not update length with this error */
+ if (!(t & FUSBH200_ISOC_BABBLE)) {
+ desc->actual_length = fusbh200_itdlen(urb, desc, t);
+ urb->actual_length += desc->actual_length;
+ }
+ } else if (likely ((t & FUSBH200_ISOC_ACTIVE) == 0)) {
+ desc->status = 0;
+ desc->actual_length = fusbh200_itdlen(urb, desc, t);
+ urb->actual_length += desc->actual_length;
+ } else {
+ /* URB was too late */
+ desc->status = -EXDEV;
+ }
+ }
+
+ /* handle completion now? */
+ if (likely ((urb_index + 1) != urb->number_of_packets))
+ goto done;
+
+ /* ASSERT: it's really the last itd for this urb
+ list_for_each_entry (itd, &stream->td_list, itd_list)
+ BUG_ON (itd->urb == urb);
+ */
+
+ /* give urb back to the driver; completion often (re)submits */
+ dev = urb->dev;
+ fusbh200_urb_done(fusbh200, urb, 0);
+ retval = true;
+ urb = NULL;
+
+ --fusbh200->isoc_count;
+ disable_periodic(fusbh200);
+
+ if (unlikely(list_is_singular(&stream->td_list))) {
+ fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated
+ -= stream->bandwidth;
+ fusbh200_dbg(fusbh200,
+ "deschedule devp %s ep%d%s-iso\n",
+ dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+ }
+
+done:
+ itd->urb = NULL;
+
+ /* Add to the end of the free list for later reuse */
+ list_move_tail(&itd->itd_list, &stream->free_list);
+
+ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */
+ if (list_empty(&stream->td_list)) {
+ list_splice_tail_init(&stream->free_list,
+ &fusbh200->cached_itd_list);
+ start_free_itds(fusbh200);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct fusbh200_hcd *fusbh200, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct fusbh200_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find (fusbh200, urb);
+ if (unlikely (stream == NULL)) {
+ fusbh200_dbg (fusbh200, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (unlikely (urb->interval != stream->interval &&
+ fusbh200_port_speed(fusbh200, 0) == USB_PORT_STAT_HIGH_SPEED)) {
+ fusbh200_dbg (fusbh200, "can't change iso interval %d --> %d\n",
+ stream->interval, urb->interval);
+ goto done;
+ }
+
+#ifdef FUSBH200_URB_TRACE
+ fusbh200_dbg (fusbh200,
+ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length,
+ urb->number_of_packets, urb->interval,
+ stream);
+#endif
+
+ /* allocate ITDs w/o locking anything */
+ status = itd_urb_transaction (stream, fusbh200, urb, mem_flags);
+ if (unlikely (status < 0)) {
+ fusbh200_dbg (fusbh200, "can't init itds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) {
+ status = -ESHUTDOWN;
+ goto done_not_linked;
+ }
+ status = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb);
+ if (unlikely(status))
+ goto done_not_linked;
+ status = iso_stream_schedule(fusbh200, urb, stream);
+ if (likely (status == 0))
+ itd_link_urb (fusbh200, urb, fusbh200->periodic_size << 3, stream);
+ else
+ usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb);
+ done_not_linked:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ done:
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_isoc(struct fusbh200_hcd *fusbh200)
+{
+ unsigned uf, now_frame, frame;
+ unsigned fmask = fusbh200->periodic_size - 1;
+ bool modified, live;
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
+ * Touches as few pages as possible: cache-friendly.
+ */
+ if (fusbh200->rh_state >= FUSBH200_RH_RUNNING) {
+ uf = fusbh200_read_frame_index(fusbh200);
+ now_frame = (uf >> 3) & fmask;
+ live = true;
+ } else {
+ now_frame = (fusbh200->next_frame - 1) & fmask;
+ live = false;
+ }
+ fusbh200->now_frame = now_frame;
+
+ frame = fusbh200->next_frame;
+ for (;;) {
+ union fusbh200_shadow q, *q_p;
+ __hc32 type, *hw_p;
+
+restart:
+ /* scan each element in frame's queue for completions */
+ q_p = &fusbh200->pshadow [frame];
+ hw_p = &fusbh200->periodic [frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE(fusbh200, *hw_p);
+ modified = false;
+
+ while (q.ptr != NULL) {
+ switch (hc32_to_cpu(fusbh200, type)) {
+ case Q_TYPE_ITD:
+ /* If this ITD is still active, leave it for
+ * later processing ... check the next entry.
+ * No need to check for activity unless the
+ * frame is current.
+ */
+ if (frame == now_frame && live) {
+ rmb();
+ for (uf = 0; uf < 8; uf++) {
+ if (q.itd->hw_transaction[uf] &
+ ITD_ACTIVE(fusbh200))
+ break;
+ }
+ if (uf < 8) {
+ q_p = &q.itd->itd_next;
+ hw_p = &q.itd->hw_next;
+ type = Q_NEXT_TYPE(fusbh200,
+ q.itd->hw_next);
+ q = *q_p;
+ break;
+ }
+ }
+
+ /* Take finished ITDs out of the schedule
+ * and process them: recycle, maybe report
+ * URB completion. HC won't cache the
+ * pointer for much longer, if at all.
+ */
+ *q_p = q.itd->itd_next;
+ *hw_p = q.itd->hw_next;
+ type = Q_NEXT_TYPE(fusbh200, q.itd->hw_next);
+ wmb();
+ modified = itd_complete (fusbh200, q.itd);
+ q = *q_p;
+ break;
+ default:
+ fusbh200_dbg(fusbh200, "corrupt type %d frame %d shadow %p\n",
+ type, frame, q.ptr);
+ // BUG ();
+ /* FALL THROUGH */
+ case Q_TYPE_QH:
+ case Q_TYPE_FSTN:
+ /* End of the iTDs and siTDs */
+ q.ptr = NULL;
+ break;
+ }
+
+ /* assume completion callbacks modify the queue */
+ if (unlikely(modified && fusbh200->isoc_count > 0))
+ goto restart;
+ }
+
+ /* Stop when we have reached the current frame */
+ if (frame == now_frame)
+ break;
+ frame = (frame + 1) & fmask;
+ }
+ fusbh200->next_frame = now_frame;
+}
+/*-------------------------------------------------------------------------*/
+/*
+ * Display / Set uframe_periodic_max
+ */
+static ssize_t show_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fusbh200_hcd *fusbh200;
+ int n;
+
+ fusbh200 = hcd_to_fusbh200(bus_to_hcd(dev_get_drvdata(dev)));
+ n = scnprintf(buf, PAGE_SIZE, "%d\n", fusbh200->uframe_periodic_max);
+ return n;
+}
+
+
+static ssize_t store_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fusbh200_hcd *fusbh200;
+ unsigned uframe_periodic_max;
+ unsigned frame, uframe;
+ unsigned short allocated_max;
+ unsigned long flags;
+ ssize_t ret;
+
+ fusbh200 = hcd_to_fusbh200(bus_to_hcd(dev_get_drvdata(dev)));
+ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
+ return -EINVAL;
+
+ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
+ fusbh200_info(fusbh200, "rejecting invalid request for "
+ "uframe_periodic_max=%u\n", uframe_periodic_max);
+ return -EINVAL;
+ }
+
+ ret = -EINVAL;
+
+ /*
+ * lock, so that our checking does not race with possible periodic
+ * bandwidth allocation through submitting new urbs.
+ */
+ spin_lock_irqsave (&fusbh200->lock, flags);
+
+ /*
+ * for request to decrease max periodic bandwidth, we have to check
+ * every microframe in the schedule to see whether the decrease is
+ * possible.
+ */
+ if (uframe_periodic_max < fusbh200->uframe_periodic_max) {
+ allocated_max = 0;
+
+ for (frame = 0; frame < fusbh200->periodic_size; ++frame)
+ for (uframe = 0; uframe < 7; ++uframe)
+ allocated_max = max(allocated_max,
+ periodic_usecs (fusbh200, frame, uframe));
+
+ if (allocated_max > uframe_periodic_max) {
+ fusbh200_info(fusbh200,
+ "cannot decrease uframe_periodic_max because "
+ "periodic bandwidth is already allocated "
+ "(%u > %u)\n",
+ allocated_max, uframe_periodic_max);
+ goto out_unlock;
+ }
+ }
+
+ /* increasing is always ok */
+
+ fusbh200_info(fusbh200, "setting max periodic bandwidth to %u%% "
+ "(== %u usec/uframe)\n",
+ 100*uframe_periodic_max/125, uframe_periodic_max);
+
+ if (uframe_periodic_max != 100)
+ fusbh200_warn(fusbh200, "max periodic bandwidth set is non-standard\n");
+
+ fusbh200->uframe_periodic_max = uframe_periodic_max;
+ ret = count;
+
+out_unlock:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ return ret;
+}
+static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max);
+
+
+static inline int create_sysfs_files(struct fusbh200_hcd *fusbh200)
+{
+ struct device *controller = fusbh200_to_hcd(fusbh200)->self.controller;
+ int i = 0;
+
+ if (i)
+ goto out;
+
+ i = device_create_file(controller, &dev_attr_uframe_periodic_max);
+out:
+ return i;
+}
+
+static inline void remove_sysfs_files(struct fusbh200_hcd *fusbh200)
+{
+ struct device *controller = fusbh200_to_hcd(fusbh200)->self.controller;
+
+ device_remove_file(controller, &dev_attr_uframe_periodic_max);
+}
+/*-------------------------------------------------------------------------*/
+
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void fusbh200_turn_off_all_ports(struct fusbh200_hcd *fusbh200)
+{
+ u32 __iomem *status_reg = &fusbh200->regs->port_status;
+
+ fusbh200_writel(fusbh200, PORT_RWC_BITS, status_reg);
+}
+
+/*
+ * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
+ * Must be called with interrupts enabled and the lock not held.
+ */
+static void fusbh200_silence_controller(struct fusbh200_hcd *fusbh200)
+{
+ fusbh200_halt(fusbh200);
+
+ spin_lock_irq(&fusbh200->lock);
+ fusbh200->rh_state = FUSBH200_RH_HALTED;
+ fusbh200_turn_off_all_ports(fusbh200);
+ spin_unlock_irq(&fusbh200->lock);
+}
+
+/* fusbh200_shutdown kick in for silicon on any bus (not just pci, etc).
+ * This forcibly disables dma and IRQs, helping kexec and other cases
+ * where the next system software may expect clean state.
+ */
+static void fusbh200_shutdown(struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd);
+
+ spin_lock_irq(&fusbh200->lock);
+ fusbh200->shutdown = true;
+ fusbh200->rh_state = FUSBH200_RH_STOPPING;
+ fusbh200->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&fusbh200->lock);
+
+ fusbh200_silence_controller(fusbh200);
+
+ hrtimer_cancel(&fusbh200->hrtimer);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * fusbh200_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping fusbh200->lock.
+ */
+static void fusbh200_work (struct fusbh200_hcd *fusbh200)
+{
+ /* another CPU may drop fusbh200->lock during a schedule scan while
+ * it reports urb completions. this flag guards against bogus
+ * attempts at re-entrant schedule scanning.
+ */
+ if (fusbh200->scanning) {
+ fusbh200->need_rescan = true;
+ return;
+ }
+ fusbh200->scanning = true;
+
+ rescan:
+ fusbh200->need_rescan = false;
+ if (fusbh200->async_count)
+ scan_async(fusbh200);
+ if (fusbh200->intr_count > 0)
+ scan_intr(fusbh200);
+ if (fusbh200->isoc_count > 0)
+ scan_isoc(fusbh200);
+ if (fusbh200->need_rescan)
+ goto rescan;
+ fusbh200->scanning = false;
+
+ /* the IO watchdog guards against hardware or driver bugs that
+ * misplace IRQs, and should let us run completely without IRQs.
+ * such lossage has been observed on both VT6202 and VT8235.
+ */
+ turn_on_io_watchdog(fusbh200);
+}
+
+/*
+ * Called when the fusbh200_hcd module is removed.
+ */
+static void fusbh200_stop (struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+
+ fusbh200_dbg (fusbh200, "stop\n");
+
+ /* no more interrupts ... */
+
+ spin_lock_irq(&fusbh200->lock);
+ fusbh200->enabled_hrtimer_events = 0;
+ spin_unlock_irq(&fusbh200->lock);
+
+ fusbh200_quiesce(fusbh200);
+ fusbh200_silence_controller(fusbh200);
+ fusbh200_reset (fusbh200);
+
+ hrtimer_cancel(&fusbh200->hrtimer);
+ remove_sysfs_files(fusbh200);
+ remove_debug_files (fusbh200);
+
+ /* root hub is shut down separately (first, when possible) */
+ spin_lock_irq (&fusbh200->lock);
+ end_free_itds(fusbh200);
+ spin_unlock_irq (&fusbh200->lock);
+ fusbh200_mem_cleanup (fusbh200);
+
+ fusbh200_dbg(fusbh200, "irq normal %ld err %ld iaa %ld (lost %ld)\n",
+ fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa,
+ fusbh200->stats.lost_iaa);
+ fusbh200_dbg (fusbh200, "complete %ld unlink %ld\n",
+ fusbh200->stats.complete, fusbh200->stats.unlink);
+
+ dbg_status (fusbh200, "fusbh200_stop completed",
+ fusbh200_readl(fusbh200, &fusbh200->regs->status));
+}
+
+/* one-time init, only for memory state */
+static int hcd_fusbh200_init(struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd);
+ u32 temp;
+ int retval;
+ u32 hcc_params;
+ struct fusbh200_qh_hw *hw;
+
+ spin_lock_init(&fusbh200->lock);
+
+ /*
+ * keep io watchdog by default, those good HCDs could turn off it later
+ */
+ fusbh200->need_io_watchdog = 1;
+
+ hrtimer_init(&fusbh200->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ fusbh200->hrtimer.function = fusbh200_hrtimer_func;
+ fusbh200->next_hrtimer_event = FUSBH200_HRTIMER_NO_EVENT;
+
+ hcc_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params);
+
+ /*
+ * by default set standard 80% (== 100 usec/uframe) max periodic
+ * bandwidth as required by USB 2.0
+ */
+ fusbh200->uframe_periodic_max = 100;
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ fusbh200->periodic_size = DEFAULT_I_TDPS;
+ INIT_LIST_HEAD(&fusbh200->intr_qh_list);
+ INIT_LIST_HEAD(&fusbh200->cached_itd_list);
+
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ switch (FUSBH200_TUNE_FLS) {
+ case 0: fusbh200->periodic_size = 1024; break;
+ case 1: fusbh200->periodic_size = 512; break;
+ case 2: fusbh200->periodic_size = 256; break;
+ default: BUG();
+ }
+ }
+ if ((retval = fusbh200_mem_init(fusbh200, GFP_KERNEL)) < 0)
+ return retval;
+
+ /* controllers may cache some of the periodic schedule ... */
+ fusbh200->i_thresh = 2;
+
+ /*
+ * dedicate a qh for the async ring head, since we couldn't unlink
+ * a 'real' qh without stopping the async schedule [4.8]. use it
+ * as the 'reclamation list head' too.
+ * its dummy is used in hw_alt_next of many tds, to prevent the qh
+ * from automatically advancing to the next td after short reads.
+ */
+ fusbh200->async->qh_next.qh = NULL;
+ hw = fusbh200->async->hw;
+ hw->hw_next = QH_NEXT(fusbh200, fusbh200->async->qh_dma);
+ hw->hw_info1 = cpu_to_hc32(fusbh200, QH_HEAD);
+ hw->hw_token = cpu_to_hc32(fusbh200, QTD_STS_HALT);
+ hw->hw_qtd_next = FUSBH200_LIST_END(fusbh200);
+ fusbh200->async->qh_state = QH_STATE_LINKED;
+ hw->hw_alt_next = QTD_NEXT(fusbh200, fusbh200->async->dummy->qtd_dma);
+
+ /* clear interrupt enables, set irq latency */
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_CANPARK(hcc_params)) {
+ /* HW default park == 3, on hardware that supports it (like
+ * NVidia and ALI silicon), maximizes throughput on the async
+ * schedule by avoiding QH fetches between transfers.
+ *
+ * With fast usb storage devices and NForce2, "park" seems to
+ * make problems: throughput reduction (!), data errors...
+ */
+ if (park) {
+ park = min(park, (unsigned) 3);
+ temp |= CMD_PARK;
+ temp |= park << 8;
+ }
+ fusbh200_dbg(fusbh200, "park %d\n", park);
+ }
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (FUSBH200_TUNE_FLS << 2);
+ }
+ fusbh200->command = temp;
+
+ /* Accept arbitrarily long scatter-gather lists */
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
+ return 0;
+}
+
+/* start HC running; it's halted, hcd_fusbh200_init() has been run (once) */
+static int fusbh200_run (struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ u32 temp;
+ u32 hcc_params;
+
+ hcd->uses_new_polling = 1;
+
+ /* EHCI spec section 4.1 */
+
+ fusbh200_writel(fusbh200, fusbh200->periodic_dma, &fusbh200->regs->frame_list);
+ fusbh200_writel(fusbh200, (u32)fusbh200->async->qh_dma, &fusbh200->regs->async_next);
+
+ /*
+ * hcc_params controls whether fusbh200->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * pci_pool consistent memory always uses segment zero.
+ * streaming mappings for I/O buffers, like pci_map_single(),
+ * can return segments above 4GB, if the device allows.
+ *
+ * NOTE: the dma mask is visible through dma_supported(), so
+ * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+ * Scsi_Host.highmem_io, and so forth. It's readonly to all
+ * host side drivers though.
+ */
+ hcc_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params);
+
+ // Philips, Intel, and maybe others need CMD_RUN before the
+ // root hub will detect new devices (why?); NEC doesn't
+ fusbh200->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+ fusbh200->command |= CMD_RUN;
+ fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command);
+ dbg_cmd (fusbh200, "init", fusbh200->command);
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub. (Except where one is integrated,
+ * and there's no companion controller unless maybe for USB OTG.)
+ *
+ * Turning on the CF flag will transfer ownership of all ports
+ * from the companions to the EHCI controller. If any of the
+ * companions are in the middle of a port reset at the time, it
+ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem
+ * guarantees that no resets are in progress. After we set CF,
+ * a short delay lets the hardware catch up; new resets shouldn't
+ * be started before the port switching actions could complete.
+ */
+ down_write(&ehci_cf_port_reset_rwsem);
+ fusbh200->rh_state = FUSBH200_RH_RUNNING;
+ fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted writes */
+ msleep(5);
+ up_write(&ehci_cf_port_reset_rwsem);
+ fusbh200->last_periodic_enable = ktime_get_real();
+
+ temp = HC_VERSION(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase));
+ fusbh200_info (fusbh200,
+ "USB %x.%x started, EHCI %x.%02x\n",
+ ((fusbh200->sbrn & 0xf0)>>4), (fusbh200->sbrn & 0x0f),
+ temp >> 8, temp & 0xff);
+
+ fusbh200_writel(fusbh200, INTR_MASK,
+ &fusbh200->regs->intr_enable); /* Turn On Interrupts */
+
+ /* GRR this is run-once init(), being done every time the HC starts.
+ * So long as they're part of class devices, we can't do it init()
+ * since the class device isn't created that early.
+ */
+ create_debug_files(fusbh200);
+ create_sysfs_files(fusbh200);
+
+ return 0;
+}
+
+static int fusbh200_setup(struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd);
+ int retval;
+
+ fusbh200->regs = (void __iomem *)fusbh200->caps +
+ HC_LENGTH(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase));
+ dbg_hcs_params(fusbh200, "reset");
+ dbg_hcc_params(fusbh200, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ fusbh200->hcs_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params);
+
+ fusbh200->sbrn = HCD_USB2;
+
+ /* data structure init */
+ retval = hcd_fusbh200_init(hcd);
+ if (retval)
+ return retval;
+
+ retval = fusbh200_halt(fusbh200);
+ if (retval)
+ return retval;
+
+ fusbh200_reset(fusbh200);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t fusbh200_irq (struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ u32 status, masked_status, pcd_status = 0, cmd;
+ int bh;
+
+ spin_lock (&fusbh200->lock);
+
+ status = fusbh200_readl(fusbh200, &fusbh200->regs->status);
+
+ /* e.g. cardbus physical eject */
+ if (status == ~(u32) 0) {
+ fusbh200_dbg (fusbh200, "device removed\n");
+ goto dead;
+ }
+
+ /*
+ * We don't use STS_FLR, but some controllers don't like it to
+ * remain on, so mask it out along with the other status bits.
+ */
+ masked_status = status & (INTR_MASK | STS_FLR);
+
+ /* Shared IRQ? */
+ if (!masked_status || unlikely(fusbh200->rh_state == FUSBH200_RH_HALTED)) {
+ spin_unlock(&fusbh200->lock);
+ return IRQ_NONE;
+ }
+
+ /* clear (just) interrupts */
+ fusbh200_writel(fusbh200, masked_status, &fusbh200->regs->status);
+ cmd = fusbh200_readl(fusbh200, &fusbh200->regs->command);
+ bh = 0;
+
+ /* normal [4.15.1.2] or error [4.15.1.1] completion */
+ if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
+ if (likely ((status & STS_ERR) == 0))
+ COUNT (fusbh200->stats.normal);
+ else
+ COUNT (fusbh200->stats.error);
+ bh = 1;
+ }
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+
+ /* Turn off the IAA watchdog */
+ fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_IAA_WATCHDOG);
+
+ /*
+ * Mild optimization: Allow another IAAD to reset the
+ * hrtimer, if one occurs before the next expiration.
+ * In theory we could always cancel the hrtimer, but
+ * tests show that about half the time it will be reset
+ * for some other event anyway.
+ */
+ if (fusbh200->next_hrtimer_event == FUSBH200_HRTIMER_IAA_WATCHDOG)
+ ++fusbh200->next_hrtimer_event;
+
+ /* guard against (alleged) silicon errata */
+ if (cmd & CMD_IAAD)
+ fusbh200_dbg(fusbh200, "IAA with IAAD still set?\n");
+ if (fusbh200->async_iaa) {
+ COUNT(fusbh200->stats.iaa);
+ end_unlink_async(fusbh200);
+ } else
+ fusbh200_dbg(fusbh200, "IAA with nothing unlinked?\n");
+ }
+
+ /* remote wakeup [4.3.1] */
+ if (status & STS_PCD) {
+ int pstatus;
+ u32 __iomem *status_reg = &fusbh200->regs->port_status;
+
+ /* kick root hub later */
+ pcd_status = status;
+
+ /* resume root hub? */
+ if (fusbh200->rh_state == FUSBH200_RH_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+
+ pstatus = fusbh200_readl(fusbh200, status_reg);
+
+ if (test_bit(0, &fusbh200->suspended_ports) &&
+ ((pstatus & PORT_RESUME) ||
+ !(pstatus & PORT_SUSPEND)) &&
+ (pstatus & PORT_PE) &&
+ fusbh200->reset_done[0] == 0) {
+
+ /* start 20 msec resume signaling from this port,
+ * and make hub_wq collect PORT_STAT_C_SUSPEND to
+ * stop that signaling. Use 5 ms extra for safety,
+ * like usb_port_resume() does.
+ */
+ fusbh200->reset_done[0] = jiffies + msecs_to_jiffies(25);
+ set_bit(0, &fusbh200->resuming_ports);
+ fusbh200_dbg (fusbh200, "port 1 remote wakeup\n");
+ mod_timer(&hcd->rh_timer, fusbh200->reset_done[0]);
+ }
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely ((status & STS_FATAL) != 0)) {
+ fusbh200_err(fusbh200, "fatal error\n");
+ dbg_cmd(fusbh200, "fatal", cmd);
+ dbg_status(fusbh200, "fatal", status);
+dead:
+ usb_hc_died(hcd);
+
+ /* Don't let the controller do anything more */
+ fusbh200->shutdown = true;
+ fusbh200->rh_state = FUSBH200_RH_STOPPING;
+ fusbh200->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
+ fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command);
+ fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable);
+ fusbh200_handle_controller_death(fusbh200);
+
+ /* Handle completions when the controller stops */
+ bh = 0;
+ }
+
+ if (bh)
+ fusbh200_work (fusbh200);
+ spin_unlock (&fusbh200->lock);
+ if (pcd_status)
+ usb_hcd_poll_rh_status(hcd);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd.self.controller.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int fusbh200_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
+) {
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ struct list_head qtd_list;
+
+ INIT_LIST_HEAD (&qtd_list);
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_CONTROL:
+ /* qh_completions() code doesn't handle all the fault cases
+ * in multi-TD control transfers. Even 1KB is rare anyway.
+ */
+ if (urb->transfer_buffer_length > (16 * 1024))
+ return -EMSGSIZE;
+ /* FALLTHROUGH */
+ /* case PIPE_BULK: */
+ default:
+ if (!qh_urb_transaction (fusbh200, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return submit_async(fusbh200, urb, &qtd_list, mem_flags);
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction (fusbh200, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit(fusbh200, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+ return itd_submit (fusbh200, urb, mem_flags);
+ }
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int fusbh200_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ struct fusbh200_qh *qh;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ switch (usb_pipetype (urb->pipe)) {
+ // case PIPE_CONTROL:
+ // case PIPE_BULK:
+ default:
+ qh = (struct fusbh200_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ start_unlink_async(fusbh200, qh);
+ break;
+ case QH_STATE_UNLINK:
+ case QH_STATE_UNLINK_WAIT:
+ /* already started */
+ break;
+ case QH_STATE_IDLE:
+ /* QH might be waiting for a Clear-TT-Buffer */
+ qh_completions(fusbh200, qh);
+ break;
+ }
+ break;
+
+ case PIPE_INTERRUPT:
+ qh = (struct fusbh200_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ start_unlink_intr(fusbh200, qh);
+ break;
+ case QH_STATE_IDLE:
+ qh_completions (fusbh200, qh);
+ break;
+ default:
+ fusbh200_dbg (fusbh200, "bogus qh %p state %d\n",
+ qh, qh->qh_state);
+ goto done;
+ }
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ // itd...
+
+ // wait till next completion, do it then.
+ // completion irqs can wait up to 1024 msec,
+ break;
+ }
+done:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void
+fusbh200_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ unsigned long flags;
+ struct fusbh200_qh *qh, *tmp;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+rescan:
+ spin_lock_irqsave (&fusbh200->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh)
+ goto done;
+
+ /* endpoints can be iso streams. for now, we don't
+ * accelerate iso completions ... so spin a while.
+ */
+ if (qh->hw == NULL) {
+ struct fusbh200_iso_stream *stream = ep->hcpriv;
+
+ if (!list_empty(&stream->td_list))
+ goto idle_timeout;
+
+ /* BUG_ON(!list_empty(&stream->free_list)); */
+ kfree(stream);
+ goto done;
+ }
+
+ if (fusbh200->rh_state < FUSBH200_RH_RUNNING)
+ qh->qh_state = QH_STATE_IDLE;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ for (tmp = fusbh200->async->qh_next.qh;
+ tmp && tmp != qh;
+ tmp = tmp->qh_next.qh)
+ continue;
+ /* periodic qh self-unlinks on empty, and a COMPLETING qh
+ * may already be unlinked.
+ */
+ if (tmp)
+ start_unlink_async(fusbh200, qh);
+ /* FALL THROUGH */
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+ case QH_STATE_UNLINK_WAIT:
+idle_timeout:
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ case QH_STATE_IDLE: /* fully unlinked */
+ if (qh->clearing_tt)
+ goto idle_timeout;
+ if (list_empty (&qh->qtd_list)) {
+ qh_destroy(fusbh200, qh);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ fusbh200_err (fusbh200, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty (&qh->qtd_list) ? "" : "(has tds)");
+ break;
+ }
+ done:
+ ep->hcpriv = NULL;
+ spin_unlock_irqrestore (&fusbh200->lock, flags);
+}
+
+static void
+fusbh200_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd);
+ struct fusbh200_qh *qh;
+ int eptype = usb_endpoint_type(&ep->desc);
+ int epnum = usb_endpoint_num(&ep->desc);
+ int is_out = usb_endpoint_dir_out(&ep->desc);
+ unsigned long flags;
+
+ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
+ return;
+
+ spin_lock_irqsave(&fusbh200->lock, flags);
+ 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) {
+ usb_settoggle(qh->dev, epnum, is_out, 0);
+ if (!list_empty(&qh->qtd_list)) {
+ WARN_ONCE(1, "clear_halt for a busy endpoint\n");
+ } else if (qh->qh_state == QH_STATE_LINKED ||
+ qh->qh_state == QH_STATE_COMPLETING) {
+
+ /* The toggle value in the QH can't be updated
+ * while the QH is active. Unlink it now;
+ * re-linking will call qh_refresh().
+ */
+ if (eptype == USB_ENDPOINT_XFER_BULK)
+ start_unlink_async(fusbh200, qh);
+ else
+ start_unlink_intr(fusbh200, qh);
+ }
+ }
+ spin_unlock_irqrestore(&fusbh200->lock, flags);
+}
+
+static int fusbh200_get_frame (struct usb_hcd *hcd)
+{
+ struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd);
+ return (fusbh200_read_frame_index(fusbh200) >> 3) % fusbh200->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The EHCI in ChipIdea HDRC cannot be a separate module or device,
+ * because its registers (and irq) are shared between host/gadget/otg
+ * functions and in order to facilitate role switching we cannot
+ * give the fusbh200 driver exclusive access to those.
+ */
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_LICENSE ("GPL");
+
+static const struct hc_driver fusbh200_fusbh200_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Faraday USB2.0 Host Controller",
+ .hcd_priv_size = sizeof(struct fusbh200_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = fusbh200_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = hcd_fusbh200_init,
+ .start = fusbh200_run,
+ .stop = fusbh200_stop,
+ .shutdown = fusbh200_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = fusbh200_urb_enqueue,
+ .urb_dequeue = fusbh200_urb_dequeue,
+ .endpoint_disable = fusbh200_endpoint_disable,
+ .endpoint_reset = fusbh200_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = fusbh200_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = fusbh200_hub_status_data,
+ .hub_control = fusbh200_hub_control,
+ .bus_suspend = fusbh200_bus_suspend,
+ .bus_resume = fusbh200_bus_resume,
+
+ .relinquish_port = fusbh200_relinquish_port,
+ .port_handed_over = fusbh200_port_handed_over,
+
+ .clear_tt_buffer_complete = fusbh200_clear_tt_buffer_complete,
+};
+
+static void fusbh200_init(struct fusbh200_hcd *fusbh200)
+{
+ u32 reg;
+
+ reg = fusbh200_readl(fusbh200, &fusbh200->regs->bmcsr);
+ reg |= BMCSR_INT_POLARITY;
+ reg &= ~BMCSR_VBUS_OFF;
+ fusbh200_writel(fusbh200, reg, &fusbh200->regs->bmcsr);
+
+ reg = fusbh200_readl(fusbh200, &fusbh200->regs->bmier);
+ fusbh200_writel(fusbh200, reg | BMIER_OVC_EN | BMIER_VBUS_ERR_EN,
+ &fusbh200->regs->bmier);
+}
+
+/**
+ * fusbh200_hcd_probe - initialize faraday FUSBH200 HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int fusbh200_hcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval = -ENODEV;
+ struct fusbh200_hcd *fusbh200;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(dev));
+ return -ENODEV;
+ }
+
+ irq = res->start;
+
+ hcd = usb_create_hcd(&fusbh200_fusbh200_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "failed to create hcd with err %d\n", retval);
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->has_tt = 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ fusbh200_fusbh200_hc_driver.description)) {
+ dev_dbg(dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto fail_request_resource;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+
+ hcd->regs = ioremap_nocache(res->start, resource_size(res));
+ if (hcd->regs == NULL) {
+ dev_dbg(dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto fail_ioremap;
+ }
+
+ fusbh200 = hcd_to_fusbh200(hcd);
+
+ fusbh200->caps = hcd->regs;
+
+ retval = fusbh200_setup(hcd);
+ if (retval)
+ goto fail_add_hcd;
+
+ fusbh200_init(fusbh200);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval) {
+ dev_err(dev, "failed to add hcd with err %d\n", retval);
+ goto fail_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return retval;
+
+fail_add_hcd:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval);
+ return retval;
+}
+
+/**
+ * fusbh200_hcd_remove - shutdown processing for EHCI HCDs
+ * @dev: USB Host Controller being removed
+ *
+ * Reverses the effect of fotg2xx_usb_hcd_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static int fusbh200_hcd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ if (!hcd)
+ return 0;
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static struct platform_driver fusbh200_hcd_fusbh200_driver = {
+ .driver = {
+ .name = "fusbh200",
+ },
+ .probe = fusbh200_hcd_probe,
+ .remove = fusbh200_hcd_remove,
+};
+
+static int __init fusbh200_hcd_init(void)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
+ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
+ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
+ printk(KERN_WARNING "Warning! fusbh200_hcd should always be loaded"
+ " before uhci_hcd and ohci_hcd, not after\n");
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n",
+ hcd_name,
+ sizeof(struct fusbh200_qh), sizeof(struct fusbh200_qtd),
+ sizeof(struct fusbh200_itd));
+
+ fusbh200_debug_root = debugfs_create_dir("fusbh200", usb_debug_root);
+ if (!fusbh200_debug_root) {
+ retval = -ENOENT;
+ goto err_debug;
+ }
+
+ retval = platform_driver_register(&fusbh200_hcd_fusbh200_driver);
+ if (retval < 0)
+ goto clean;
+ return retval;
+
+ platform_driver_unregister(&fusbh200_hcd_fusbh200_driver);
+clean:
+ debugfs_remove(fusbh200_debug_root);
+ fusbh200_debug_root = NULL;
+err_debug:
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+ return retval;
+}
+module_init(fusbh200_hcd_init);
+
+static void __exit fusbh200_hcd_cleanup(void)
+{
+ platform_driver_unregister(&fusbh200_hcd_fusbh200_driver);
+ debugfs_remove(fusbh200_debug_root);
+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
+}
+module_exit(fusbh200_hcd_cleanup);
diff --git a/drivers/usb/host/fusbh200.h b/drivers/usb/host/fusbh200.h
new file mode 100644
index 000000000..d6e5b3d4a
--- /dev/null
+++ b/drivers/usb/host/fusbh200.h
@@ -0,0 +1,675 @@
+#ifndef __LINUX_FUSBH200_H
+#define __LINUX_FUSBH200_H
+
+#include <linux/usb/ehci-dbgp.h>
+
+/* definitions used for the EHCI driver */
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given FUSBH200_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#define __hc32 __le32
+#define __hc16 __le16
+
+/* statistics can be kept for tuning/monitoring */
+struct fusbh200_stats {
+ /* irq usage */
+ unsigned long normal;
+ unsigned long error;
+ unsigned long iaa;
+ unsigned long lost_iaa;
+
+ /* termination of urbs from core */
+ unsigned long complete;
+ unsigned long unlink;
+};
+
+/* fusbh200_hcd->lock guards shared data against other CPUs:
+ * fusbh200_hcd: async, unlink, periodic (and shadow), ...
+ * usb_host_endpoint: hcpriv
+ * fusbh200_qh: qh_next, qtd_list
+ * fusbh200_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define FUSBH200_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */
+
+/*
+ * fusbh200_rh_state values of FUSBH200_RH_RUNNING or above mean that the
+ * controller may be doing DMA. Lower values mean there's no DMA.
+ */
+enum fusbh200_rh_state {
+ FUSBH200_RH_HALTED,
+ FUSBH200_RH_SUSPENDED,
+ FUSBH200_RH_RUNNING,
+ FUSBH200_RH_STOPPING
+};
+
+/*
+ * Timer events, ordered by increasing delay length.
+ * Always update event_delays_ns[] and event_handlers[] (defined in
+ * ehci-timer.c) in parallel with this list.
+ */
+enum fusbh200_hrtimer_event {
+ FUSBH200_HRTIMER_POLL_ASS, /* Poll for async schedule off */
+ FUSBH200_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
+ FUSBH200_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
+ FUSBH200_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
+ FUSBH200_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
+ FUSBH200_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
+ FUSBH200_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
+ FUSBH200_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
+ FUSBH200_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
+ FUSBH200_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
+ FUSBH200_HRTIMER_NUM_EVENTS /* Must come last */
+};
+#define FUSBH200_HRTIMER_NO_EVENT 99
+
+struct fusbh200_hcd { /* one per controller */
+ /* timing support */
+ enum fusbh200_hrtimer_event next_hrtimer_event;
+ unsigned enabled_hrtimer_events;
+ ktime_t hr_timeouts[FUSBH200_HRTIMER_NUM_EVENTS];
+ struct hrtimer hrtimer;
+
+ int PSS_poll_count;
+ int ASS_poll_count;
+ int died_poll_count;
+
+ /* glue to PCI and HCD framework */
+ struct fusbh200_caps __iomem *caps;
+ struct fusbh200_regs __iomem *regs;
+ struct ehci_dbg_port __iomem *debug;
+
+ __u32 hcs_params; /* cached register copy */
+ spinlock_t lock;
+ enum fusbh200_rh_state rh_state;
+
+ /* general schedule support */
+ bool scanning:1;
+ bool need_rescan:1;
+ bool intr_unlinking:1;
+ bool async_unlinking:1;
+ bool shutdown:1;
+ struct fusbh200_qh *qh_scan_next;
+
+ /* async schedule support */
+ struct fusbh200_qh *async;
+ struct fusbh200_qh *dummy; /* For AMD quirk use */
+ struct fusbh200_qh *async_unlink;
+ struct fusbh200_qh *async_unlink_last;
+ struct fusbh200_qh *async_iaa;
+ unsigned async_unlink_cycle;
+ unsigned async_count; /* async activity count */
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ __hc32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ struct list_head intr_qh_list;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union fusbh200_shadow *pshadow; /* mirror hw periodic table */
+ struct fusbh200_qh *intr_unlink;
+ struct fusbh200_qh *intr_unlink_last;
+ unsigned intr_unlink_cycle;
+ unsigned now_frame; /* frame from HC hardware */
+ unsigned next_frame; /* scan periodic, start here */
+ unsigned intr_count; /* intr activity count */
+ unsigned isoc_count; /* isoc activity count */
+ unsigned periodic_count; /* periodic activity count */
+ unsigned uframe_periodic_max; /* max periodic time per uframe */
+
+
+ /* list of itds completed while now_frame was still active */
+ struct list_head cached_itd_list;
+ struct fusbh200_itd *last_itd_to_free;
+
+ /* per root hub port */
+ unsigned long reset_done [FUSBH200_MAX_ROOT_PORTS];
+
+ /* bit vectors (one bit per port) */
+ unsigned long bus_suspended; /* which ports were
+ already suspended at the start of a bus suspend */
+ unsigned long companion_ports; /* which ports are
+ dedicated to the companion controller */
+ unsigned long owned_ports; /* which ports are
+ owned by the companion during a bus suspend */
+ unsigned long port_c_suspend; /* which ports have
+ the change-suspend feature turned on */
+ unsigned long suspended_ports; /* which ports are
+ suspended */
+ unsigned long resuming_ports; /* which ports have
+ started to resume */
+
+ /* per-HC memory pools (could be per-bus, but ...) */
+ struct dma_pool *qh_pool; /* qh per active urb */
+ struct dma_pool *qtd_pool; /* one or more per qh */
+ struct dma_pool *itd_pool; /* itd per iso urb */
+
+ unsigned random_frame;
+ unsigned long next_statechange;
+ ktime_t last_periodic_enable;
+ u32 command;
+
+ /* SILICON QUIRKS */
+ unsigned need_io_watchdog:1;
+ unsigned fs_i_thresh:1; /* Intel iso scheduling */
+
+ u8 sbrn; /* packed release number */
+
+ /* irq statistics */
+ struct fusbh200_stats stats;
+# define COUNT(x) do { (x)++; } while (0)
+
+ /* debug files */
+ struct dentry *debug_dir;
+};
+
+/* convert between an HCD pointer and the corresponding FUSBH200_HCD */
+static inline struct fusbh200_hcd *hcd_to_fusbh200 (struct usb_hcd *hcd)
+{
+ return (struct fusbh200_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *fusbh200_to_hcd (struct fusbh200_hcd *fusbh200)
+{
+ return container_of ((void *) fusbh200, struct usb_hcd, hcd_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct fusbh200_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ * some hosts treat caplength and hciversion as parts of a 32-bit
+ * register, others treat them as two separate registers, this
+ * affects the memory map for big endian controllers.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(fusbh200, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \
+ (fusbh200_big_endian_capbase(fusbh200) ? 24 : 0)))
+#define HC_VERSION(fusbh200, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \
+ (fusbh200_big_endian_capbase(fusbh200) ? 0 : 16)))
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+ u8 portroute[8]; /* nibbles for routing - offset 0xC */
+};
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct fusbh200_regs {
+
+ /* USBCMD: offset 0x00 */
+ u32 command;
+
+/* EHCI 1.1 addendum */
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved1;
+ /* PORTSC: offset 0x20 */
+ u32 port_status;
+/* 31:23 reserved */
+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC)
+
+ u32 reserved2[3];
+
+ /* BMCSR: offset 0x30 */
+ u32 bmcsr; /* Bus Moniter Control/Status Register */
+#define BMCSR_HOST_SPD_TYP (3<<9)
+#define BMCSR_VBUS_OFF (1<<4)
+#define BMCSR_INT_POLARITY (1<<3)
+
+ /* BMISR: offset 0x34 */
+ u32 bmisr; /* Bus Moniter Interrupt Status Register*/
+#define BMISR_OVC (1<<1)
+
+ /* BMIER: offset 0x38 */
+ u32 bmier; /* Bus Moniter Interrupt Enable Register */
+#define BMIER_OVC_EN (1<<1)
+#define BMIER_VBUS_ERR_EN (1<<0)
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(fusbh200, dma) cpu_to_hc32(fusbh200, (u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct fusbh200_qtd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.5.1 */
+ __hc32 hw_alt_next; /* see EHCI 3.5.2 */
+ __hc32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+
+#define ACTIVE_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_ACTIVE)
+#define HALT_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_HALT)
+#define STATUS_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_STS)
+
+ __hc32 hw_buf [5]; /* see EHCI 3.5.4 */
+ __hc32 hw_buf_hi [5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+} __attribute__ ((aligned (32)));
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK(fusbh200) cpu_to_hc32 (fusbh200, ~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,fstn}->hw_next */
+#define Q_NEXT_TYPE(fusbh200,dma) ((dma) & cpu_to_hc32(fusbh200, 3 << 1))
+
+/*
+ * Now the following defines are not converted using the
+ * cpu_to_le32() macro anymore, since we have to support
+ * "dynamic" switching between be and le support, so that the driver
+ * can be used on one system with SoC EHCI controller using big-endian
+ * descriptors as well as a normal little-endian PCI EHCI controller.
+ */
+/* values for that type tag */
+#define Q_TYPE_ITD (0 << 1)
+#define Q_TYPE_QH (1 << 1)
+#define Q_TYPE_SITD (2 << 1)
+#define Q_TYPE_FSTN (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(fusbh200,dma) (cpu_to_hc32(fusbh200, (((u32)dma)&~0x01f)|Q_TYPE_QH))
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define FUSBH200_LIST_END(fusbh200) cpu_to_hc32(fusbh200, 1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union fusbh200_shadow {
+ struct fusbh200_qh *qh; /* Q_TYPE_QH */
+ struct fusbh200_itd *itd; /* Q_TYPE_ITD */
+ struct fusbh200_fstn *fstn; /* Q_TYPE_FSTN */
+ __hc32 *hw_next; /* (all types) */
+ void *ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+/* first part defined by EHCI spec */
+struct fusbh200_qh_hw {
+ __hc32 hw_next; /* see EHCI 3.6.1 */
+ __hc32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
+#define QH_HEAD (1 << 15) /* Head of async reclamation list */
+#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
+#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */
+#define QH_LOW_SPEED (1 << 12)
+#define QH_FULL_SPEED (0 << 12)
+#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
+ __hc32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_SMASK 0x000000ff
+#define QH_CMASK 0x0000ff00
+#define QH_HUBADDR 0x007f0000
+#define QH_HUBPORT 0x3f800000
+#define QH_MULT 0xc0000000
+ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct fusbh200_qtd) */
+ __hc32 hw_qtd_next;
+ __hc32 hw_alt_next;
+ __hc32 hw_token;
+ __hc32 hw_buf [5];
+ __hc32 hw_buf_hi [5];
+} __attribute__ ((aligned(32)));
+
+struct fusbh200_qh {
+ struct fusbh200_qh_hw *hw; /* Must come first */
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union fusbh200_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+ struct list_head intr_node; /* list of intr QHs */
+ struct fusbh200_qtd *dummy;
+ struct fusbh200_qh *unlink_next; /* next on unlink list */
+
+ unsigned unlink_cycle;
+
+ u8 needs_rescan; /* Dequeue during giveback */
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ u8 xacterrs; /* XactErr retry counter */
+#define QH_XACTERR_MAX 32 /* XactErr retry limit */
+
+ /* periodic schedule info */
+ u8 usecs; /* intr bandwidth */
+ u8 gap_uf; /* uframes split/csplit gap */
+ u8 c_usecs; /* ... split completion bw */
+ u16 tt_usecs; /* tt downstream bandwidth */
+ unsigned short period; /* polling interval */
+ unsigned short start; /* where polling starts */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+
+ struct usb_device *dev; /* access to TT */
+ unsigned is_out:1; /* bulk or intr OUT */
+ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* description of one iso transaction (up to 3 KB data if highspeed) */
+struct fusbh200_iso_packet {
+ /* These will be copied to iTD when scheduling */
+ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
+ __hc32 transaction; /* itd->hw_transaction[i] |= */
+ u8 cross; /* buf crosses pages */
+ /* for full speed OUT splits */
+ u32 buf1;
+};
+
+/* temporary schedule data for packets from iso urbs (both speeds)
+ * each packet is one logical usb transaction to the device (not TT),
+ * beginning at stream->next_uframe
+ */
+struct fusbh200_iso_sched {
+ struct list_head td_list;
+ unsigned span;
+ struct fusbh200_iso_packet packet [0];
+};
+
+/*
+ * fusbh200_iso_stream - groups all (s)itds for this endpoint.
+ * acts like a qh would, if EHCI had them for ISO.
+ */
+struct fusbh200_iso_stream {
+ /* first field matches fusbh200_hq, but is NULL */
+ struct fusbh200_qh_hw *hw;
+
+ u8 bEndpointAddress;
+ u8 highspeed;
+ struct list_head td_list; /* queued itds */
+ struct list_head free_list; /* list of unused itds */
+ struct usb_device *udev;
+ struct usb_host_endpoint *ep;
+
+ /* output of (re)scheduling */
+ int next_uframe;
+ __hc32 splits;
+
+ /* the rest is derived from the endpoint descriptor,
+ * trusting urb->interval == f(epdesc->bInterval) and
+ * including the extra info for hw_bufp[0..2]
+ */
+ u8 usecs, c_usecs;
+ u16 interval;
+ u16 tt_usecs;
+ u16 maxp;
+ u16 raw_mask;
+ unsigned bandwidth;
+
+ /* This is used to initialize iTD's hw_bufp fields */
+ __hc32 buf0;
+ __hc32 buf1;
+ __hc32 buf2;
+
+ /* this is used to initialize sITD's tt info */
+ __hc32 address;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct fusbh200_itd {
+ /* first part defined by EHCI spec */
+ __hc32 hw_next; /* see EHCI 3.3.1 */
+ __hc32 hw_transaction [8]; /* see EHCI 3.3.2 */
+#define FUSBH200_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define FUSBH200_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define FUSBH200_ISOC_BABBLE (1<<29) /* babble detected */
+#define FUSBH200_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define FUSBH200_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
+#define FUSBH200_ITD_IOC (1 << 15) /* interrupt on complete */
+
+#define ITD_ACTIVE(fusbh200) cpu_to_hc32(fusbh200, FUSBH200_ISOC_ACTIVE)
+
+ __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __hc32 hw_bufp_hi [7]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t itd_dma; /* for this itd */
+ union fusbh200_shadow itd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct fusbh200_iso_stream *stream; /* endpoint's queue */
+ struct list_head itd_list; /* list of stream's itds */
+
+ /* any/all hw_transactions here may be used by that urb */
+ unsigned frame; /* where scheduled */
+ unsigned pg;
+ unsigned index[8]; /* in urb->iso_frame_desc */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct fusbh200_fstn {
+ __hc32 hw_next; /* any periodic q entry */
+ __hc32 hw_prev; /* qh or FUSBH200_LIST_END */
+
+ /* the rest is HCD-private */
+ dma_addr_t fstn_dma;
+ union fusbh200_shadow fstn_next; /* ptr to periodic q entry */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare the PORTSC wakeup flags during controller suspend/resume */
+
+#define fusbh200_prepare_ports_for_controller_suspend(fusbh200, do_wakeup) \
+ fusbh200_adjust_port_wakeup_flags(fusbh200, true, do_wakeup);
+
+#define fusbh200_prepare_ports_for_controller_resume(fusbh200) \
+ fusbh200_adjust_port_wakeup_flags(fusbh200, false, false);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Some EHCI controllers have a Transaction Translator built into the
+ * root hub. This is a non-standard feature. Each controller will need
+ * to add code to the following inline functions, and call them as
+ * needed (mostly in root hub code).
+ */
+
+static inline unsigned int
+fusbh200_get_speed(struct fusbh200_hcd *fusbh200, unsigned int portsc)
+{
+ return (readl(&fusbh200->regs->bmcsr)
+ & BMCSR_HOST_SPD_TYP) >> 9;
+}
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int
+fusbh200_port_speed(struct fusbh200_hcd *fusbh200, unsigned int portsc)
+{
+ switch (fusbh200_get_speed(fusbh200, portsc)) {
+ case 0:
+ return 0;
+ case 1:
+ return USB_PORT_STAT_LOW_SPEED;
+ case 2:
+ default:
+ return USB_PORT_STAT_HIGH_SPEED;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define fusbh200_has_fsl_portno_bug(e) (0)
+
+/*
+ * While most USB host controllers implement their registers in
+ * little-endian format, a minority (celleb companion chip) implement
+ * them in big endian format.
+ *
+ * This attempts to support either format at compile time without a
+ * runtime penalty, or both formats with the additional overhead
+ * of checking a flag bit.
+ *
+ */
+
+#define fusbh200_big_endian_mmio(e) 0
+#define fusbh200_big_endian_capbase(e) 0
+
+static inline unsigned int fusbh200_readl(const struct fusbh200_hcd *fusbh200,
+ __u32 __iomem * regs)
+{
+ return readl(regs);
+}
+
+static inline void fusbh200_writel(const struct fusbh200_hcd *fusbh200,
+ const unsigned int val, __u32 __iomem *regs)
+{
+ writel(val, regs);
+}
+
+/* cpu to fusbh200 */
+static inline __hc32 cpu_to_hc32 (const struct fusbh200_hcd *fusbh200, const u32 x)
+{
+ return cpu_to_le32(x);
+}
+
+/* fusbh200 to cpu */
+static inline u32 hc32_to_cpu (const struct fusbh200_hcd *fusbh200, const __hc32 x)
+{
+ return le32_to_cpu(x);
+}
+
+static inline u32 hc32_to_cpup (const struct fusbh200_hcd *fusbh200, const __hc32 *x)
+{
+ return le32_to_cpup(x);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline unsigned fusbh200_read_frame_index(struct fusbh200_hcd *fusbh200)
+{
+ return fusbh200_readl(fusbh200, &fusbh200->regs->frame_index);
+}
+
+#define fusbh200_itdlen(urb, desc, t) ({ \
+ usb_pipein((urb)->pipe) ? \
+ (desc)->length - FUSBH200_ITD_LENGTH(t) : \
+ FUSBH200_ITD_LENGTH(t); \
+})
+/*-------------------------------------------------------------------------*/
+
+#endif /* __LINUX_FUSBH200_H */
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
new file mode 100644
index 000000000..1db0626c8
--- /dev/null
+++ b/drivers/usb/host/hwa-hc.c
@@ -0,0 +1,889 @@
+/*
+ * Host Wire Adapter:
+ * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * The HWA driver is a simple layer that forwards requests to the WAHC
+ * (Wire Adater Host Controller) or WUSBHC (Wireless USB Host
+ * Controller) layers.
+ *
+ * Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB
+ * Host Controller that is connected to your system via USB (a USB
+ * dongle that implements a USB host...). There is also a Device Wired
+ * Adaptor, DWA (Wireless USB hub) that uses the same mechanism for
+ * transferring data (it is after all a USB host connected via
+ * Wireless USB), we have a common layer called Wire Adapter Host
+ * Controller that does all the hard work. The WUSBHC (Wireless USB
+ * Host Controller) is the part common to WUSB Host Controllers, the
+ * HWA and the PCI-based one, that is implemented following the WHCI
+ * spec. All these layers are implemented in ../wusbcore.
+ *
+ * The main functions are hwahc_op_urb_{en,de}queue(), that pass the
+ * job of converting a URB to a Wire Adapter
+ *
+ * Entry points:
+ *
+ * hwahc_driver_*() Driver initialization, registration and
+ * teardown.
+ *
+ * hwahc_probe() New device came up, create an instance for
+ * it [from device enumeration].
+ *
+ * hwahc_disconnect() Remove device instance [from device
+ * enumeration].
+ *
+ * [__]hwahc_op_*() Host-Wire-Adaptor specific functions for
+ * starting/stopping/etc (some might be made also
+ * DWA).
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include "../wusbcore/wa-hc.h"
+#include "../wusbcore/wusbhc.h"
+
+struct hwahc {
+ struct wusbhc wusbhc; /* has to be 1st */
+ struct wahc wa;
+};
+
+/*
+ * FIXME should be wusbhc
+ *
+ * NOTE: we need to cache the Cluster ID because later...there is no
+ * way to get it :)
+ */
+static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id)
+{
+ int result;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct wahc *wa = &hwahc->wa;
+ struct device *dev = &wa->usb_iface->dev;
+
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_CLUSTER_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ cluster_id,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (result < 0)
+ dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n",
+ cluster_id, result);
+ else
+ wusbhc->cluster_id = cluster_id;
+ dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id);
+ return result;
+}
+
+static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_NUM_DNTS,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ interval << 8 | slots,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+/*
+ * Reset a WUSB host controller and wait for it to complete doing it.
+ *
+ * @usb_hcd: Pointer to WUSB Host Controller instance.
+ *
+ */
+static int hwahc_op_reset(struct usb_hcd *usb_hcd)
+{
+ int result;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ mutex_lock(&wusbhc->mutex);
+ wa_nep_disarm(&hwahc->wa);
+ result = __wa_set_feature(&hwahc->wa, WA_RESET);
+ if (result < 0) {
+ dev_err(dev, "error commanding HC to reset: %d\n", result);
+ goto error_unlock;
+ }
+ result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0);
+ if (result < 0) {
+ dev_err(dev, "error waiting for HC to reset: %d\n", result);
+ goto error_unlock;
+ }
+error_unlock:
+ mutex_unlock(&wusbhc->mutex);
+ return result;
+}
+
+/*
+ * FIXME: break this function up
+ */
+static int hwahc_op_start(struct usb_hcd *usb_hcd)
+{
+ u8 addr;
+ int result;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ result = -ENOSPC;
+ mutex_lock(&wusbhc->mutex);
+ addr = wusb_cluster_id_get();
+ if (addr == 0)
+ goto error_cluster_id_get;
+ result = __hwahc_set_cluster_id(hwahc, addr);
+ if (result < 0)
+ goto error_set_cluster_id;
+
+ usb_hcd->uses_new_polling = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
+ usb_hcd->state = HC_STATE_RUNNING;
+
+ /*
+ * prevent USB core from suspending the root hub since
+ * bus_suspend and bus_resume are not yet supported.
+ */
+ pm_runtime_get_noresume(&usb_hcd->self.root_hub->dev);
+
+ result = 0;
+out:
+ mutex_unlock(&wusbhc->mutex);
+ return result;
+
+error_set_cluster_id:
+ wusb_cluster_id_put(wusbhc->cluster_id);
+error_cluster_id_get:
+ goto out;
+
+}
+
+/*
+ * No need to abort pipes, as when this is called, all the children
+ * has been disconnected and that has done it [through
+ * usb_disable_interface() -> usb_disable_endpoint() ->
+ * hwahc_op_ep_disable() - >rpipe_ep_disable()].
+ */
+static void hwahc_op_stop(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+
+ mutex_lock(&wusbhc->mutex);
+ wusb_cluster_id_put(wusbhc->cluster_id);
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+
+ /*
+ * We cannot query the HWA for the WUSB time since that requires sending
+ * a synchronous URB and this function can be called in_interrupt.
+ * Instead, query the USB frame number for our parent and use that.
+ */
+ return usb_get_current_frame_number(wa->usb_dev);
+}
+
+static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+ gfp_t gfp)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp);
+}
+
+static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb,
+ int status)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ return wa_urb_dequeue(&hwahc->wa, urb, status);
+}
+
+/*
+ * Release resources allocated for an endpoint
+ *
+ * If there is an associated rpipe to this endpoint, go ahead and put it.
+ */
+static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ rpipe_ep_disable(&hwahc->wa, ep);
+}
+
+static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
+{
+ int result;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
+ if (result < 0) {
+ dev_err(dev, "error commanding HC to start: %d\n", result);
+ goto error_stop;
+ }
+ result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
+ if (result < 0) {
+ dev_err(dev, "error waiting for HC to start: %d\n", result);
+ goto error_stop;
+ }
+ result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "cannot listen to notifications: %d\n", result);
+ goto error_stop;
+ }
+ /*
+ * If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set,
+ * disable transfer notifications.
+ */
+ if (hwahc->wa.quirks &
+ WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) {
+ struct usb_host_interface *cur_altsetting =
+ hwahc->wa.usb_iface->cur_altsetting;
+
+ result = usb_control_msg(hwahc->wa.usb_dev,
+ usb_sndctrlpipe(hwahc->wa.usb_dev, 0),
+ WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE,
+ WA_REQ_ALEREON_FEATURE_SET,
+ cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ /*
+ * If we successfully sent the control message, start DTI here
+ * because no transfer notifications will be received which is
+ * where DTI is normally started.
+ */
+ if (result == 0)
+ result = wa_dti_start(&hwahc->wa);
+ else
+ result = 0; /* OK. Continue normally. */
+
+ if (result < 0) {
+ dev_err(dev, "cannot start DTI: %d\n", result);
+ goto error_dti_start;
+ }
+ }
+
+ return result;
+
+error_dti_start:
+ wa_nep_disarm(&hwahc->wa);
+error_stop:
+ __wa_clear_feature(&hwahc->wa, WA_ENABLE);
+ return result;
+}
+
+static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ int ret;
+
+ ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_CHAN_STOP,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ delay * 1000,
+ iface_no,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret == 0)
+ msleep(delay);
+
+ wa_nep_disarm(&hwahc->wa);
+ __wa_stop(&hwahc->wa);
+}
+
+/*
+ * Set the UWB MAS allocation for the WUSB cluster
+ *
+ * @stream_index: stream to use (-1 for cancelling the allocation)
+ * @mas: mas bitmap to use
+ */
+static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index,
+ const struct uwb_mas_bm *mas)
+{
+ int result;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ struct device *dev = &wa->usb_iface->dev;
+ u8 mas_le[UWB_NUM_MAS/8];
+
+ /* Set the stream index */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_STREAM_IDX,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ stream_index,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (result < 0) {
+ dev_err(dev, "Cannot set WUSB stream index: %d\n", result);
+ goto out;
+ }
+ uwb_mas_bm_copy_le(mas_le, mas);
+ /* Set the MAS allocation */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_WUSB_MAS,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ mas_le, 32, USB_CTRL_SET_TIMEOUT);
+ if (result < 0)
+ dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result);
+out:
+ return result;
+}
+
+/*
+ * Add an IE to the host's MMC
+ *
+ * @interval: See WUSB1.0[8.5.3.1]
+ * @repeat_cnt: See WUSB1.0[8.5.3.1]
+ * @handle: See WUSB1.0[8.5.3.1]
+ * @wuie: Pointer to the header of the WUSB IE data to add.
+ * MUST BE allocated in a kmalloc buffer (no stack or
+ * vmalloc).
+ *
+ * NOTE: the format of the WUSB IEs for MMCs are different to the
+ * normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length +
+ * Id in WUSB IEs). Standards...you gotta love'em.
+ */
+static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval,
+ u8 repeat_cnt, u8 handle,
+ struct wuie_hdr *wuie)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_ADD_MMC_IE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ interval << 8 | repeat_cnt,
+ handle << 8 | iface_no,
+ wuie, wuie->bLength, USB_CTRL_SET_TIMEOUT);
+}
+
+/*
+ * Remove an IE to the host's MMC
+ *
+ * @handle: See WUSB1.0[8.5.3.1]
+ */
+static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_REMOVE_MMC_IE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, handle << 8 | iface_no,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+/*
+ * Update device information for a given fake port
+ *
+ * @port_idx: Fake port to which device is connected (wusbhc index, not
+ * USB port number).
+ */
+static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc,
+ struct wusb_dev *wusb_dev)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ struct hwa_dev_info *dev_info;
+ int ret;
+
+ /* fill out the Device Info buffer and send it */
+ dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL);
+ if (!dev_info)
+ return -ENOMEM;
+ uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability,
+ &wusb_dev->availability);
+ dev_info->bDeviceAddress = wusb_dev->addr;
+
+ /*
+ * If the descriptors haven't been read yet, use a default PHY
+ * rate of 53.3 Mbit/s only. The correct value will be used
+ * when this will be called again as part of the
+ * authentication process (which occurs after the descriptors
+ * have been read).
+ */
+ if (wusb_dev->wusb_cap_descr)
+ dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates;
+ else
+ dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53);
+
+ ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_DEV_INFO,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, wusb_dev->port_idx << 8 | iface_no,
+ dev_info, sizeof(struct hwa_dev_info),
+ USB_CTRL_SET_TIMEOUT);
+ kfree(dev_info);
+ return ret;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *key, size_t key_size,
+ u8 key_idx)
+{
+ int result = -ENOMEM;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ struct usb_key_descriptor *keyd;
+ size_t keyd_len;
+
+ keyd_len = sizeof(*keyd) + key_size;
+ keyd = kzalloc(keyd_len, GFP_KERNEL);
+ if (keyd == NULL)
+ return -ENOMEM;
+
+ keyd->bLength = keyd_len;
+ keyd->bDescriptorType = USB_DT_KEY;
+ keyd->tTKID[0] = (tkid >> 0) & 0xff;
+ keyd->tTKID[1] = (tkid >> 8) & 0xff;
+ keyd->tTKID[2] = (tkid >> 16) & 0xff;
+ memcpy(keyd->bKeyData, key, key_size);
+
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_SET_DESCRIPTOR,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ USB_DT_KEY << 8 | key_idx,
+ port_idx << 8 | iface_no,
+ keyd, keyd_len, USB_CTRL_SET_TIMEOUT);
+
+ kzfree(keyd); /* clear keys etc. */
+ return result;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *key, size_t key_size)
+{
+ int result = -ENOMEM;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ u8 encryption_value;
+
+ /* Tell the host which key to use to talk to the device */
+ if (key) {
+ u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_PTK,
+ WUSB_KEY_INDEX_ORIGINATOR_HOST);
+
+ result = __hwahc_dev_set_key(wusbhc, port_idx, tkid,
+ key, key_size, key_idx);
+ if (result < 0)
+ goto error_set_key;
+ encryption_value = wusbhc->ccm1_etd->bEncryptionValue;
+ } else {
+ /* FIXME: this should come from wusbhc->etd[UNSECURE].value */
+ encryption_value = 0;
+ }
+
+ /* Set the encryption type for communicating with the device */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_SET_ENCRYPTION,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ encryption_value, port_idx << 8 | iface_no,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (result < 0)
+ dev_err(wusbhc->dev, "Can't set host's WUSB encryption for "
+ "port index %u to %s (value %d): %d\n", port_idx,
+ wusb_et_name(wusbhc->ccm1_etd->bEncryptionType),
+ wusbhc->ccm1_etd->bEncryptionValue, result);
+error_set_key:
+ return result;
+}
+
+/*
+ * Set host's GTK key
+ */
+static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *key, size_t key_size)
+{
+ u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
+ WUSB_KEY_INDEX_ORIGINATOR_HOST);
+
+ return __hwahc_dev_set_key(wusbhc, 0, tkid, key, key_size, key_idx);
+}
+
+/*
+ * Get the Wire Adapter class-specific descriptor
+ *
+ * NOTE: this descriptor comes with the big bundled configuration
+ * descriptor that includes the interfaces' and endpoints', so
+ * we just look for it in the cached copy kept by the USB stack.
+ *
+ * NOTE2: We convert LE fields to CPU order.
+ */
+static int wa_fill_descr(struct wahc *wa)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+ char *itr;
+ struct usb_device *usb_dev = wa->usb_dev;
+ struct usb_descriptor_header *hdr;
+ struct usb_wa_descriptor *wa_descr;
+ size_t itr_size, actconfig_idx;
+
+ actconfig_idx = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[actconfig_idx];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ while (itr_size >= sizeof(*hdr)) {
+ hdr = (struct usb_descriptor_header *) itr;
+ dev_dbg(dev, "Extra device descriptor: "
+ "type %02x/%u bytes @ %zu (%zu left)\n",
+ hdr->bDescriptorType, hdr->bLength,
+ (itr - usb_dev->rawdescriptors[actconfig_idx]),
+ itr_size);
+ if (hdr->bDescriptorType == USB_DT_WIRE_ADAPTER)
+ goto found;
+ itr += hdr->bLength;
+ itr_size -= hdr->bLength;
+ }
+ dev_err(dev, "cannot find Wire Adapter Class descriptor\n");
+ return -ENODEV;
+
+found:
+ result = -EINVAL;
+ if (hdr->bLength > itr_size) { /* is it available? */
+ dev_err(dev, "incomplete Wire Adapter Class descriptor "
+ "(%zu bytes left, %u needed)\n",
+ itr_size, hdr->bLength);
+ goto error;
+ }
+ if (hdr->bLength < sizeof(*wa->wa_descr)) {
+ dev_err(dev, "short Wire Adapter Class descriptor\n");
+ goto error;
+ }
+ wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
+ if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
+ dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
+ (le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00) >> 8,
+ le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
+ result = 0;
+error:
+ return result;
+}
+
+static struct hc_driver hwahc_hc_driver = {
+ .description = "hwa-hcd",
+ .product_desc = "Wireless USB HWA host controller",
+ .hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd),
+ .irq = NULL, /* FIXME */
+ .flags = HCD_USB25,
+ .reset = hwahc_op_reset,
+ .start = hwahc_op_start,
+ .stop = hwahc_op_stop,
+ .get_frame_number = hwahc_op_get_frame_number,
+ .urb_enqueue = hwahc_op_urb_enqueue,
+ .urb_dequeue = hwahc_op_urb_dequeue,
+ .endpoint_disable = hwahc_op_endpoint_disable,
+
+ .hub_status_data = wusbhc_rh_status_data,
+ .hub_control = wusbhc_rh_control,
+ .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int hwahc_security_create(struct hwahc *hwahc)
+{
+ int result;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct usb_device *usb_dev = hwahc->wa.usb_dev;
+ struct device *dev = &usb_dev->dev;
+ struct usb_security_descriptor *secd;
+ struct usb_encryption_descriptor *etd;
+ void *itr, *top;
+ size_t itr_size, needed, bytes;
+ u8 index;
+ char buf[64];
+
+ /* Find the host's security descriptors in the config descr bundle */
+ index = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[index];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ top = itr + itr_size;
+ result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
+ le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
+ USB_DT_SECURITY, (void **) &secd);
+ if (result == -1) {
+ dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
+ return 0;
+ }
+ needed = sizeof(*secd);
+ if (top - (void *)secd < needed) {
+ dev_err(dev, "BUG? Not enough data to process security "
+ "descriptor header (%zu bytes left vs %zu needed)\n",
+ top - (void *) secd, needed);
+ return 0;
+ }
+ needed = le16_to_cpu(secd->wTotalLength);
+ if (top - (void *)secd < needed) {
+ dev_err(dev, "BUG? Not enough data to process security "
+ "descriptors (%zu bytes left vs %zu needed)\n",
+ top - (void *) secd, needed);
+ return 0;
+ }
+ /* Walk over the sec descriptors and store CCM1's on wusbhc */
+ itr = (void *) secd + sizeof(*secd);
+ top = (void *) secd + le16_to_cpu(secd->wTotalLength);
+ index = 0;
+ bytes = 0;
+ while (itr < top) {
+ etd = itr;
+ if (top - itr < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad host security descriptor; "
+ "not enough data (%zu vs %zu left)\n",
+ top - itr, sizeof(*etd));
+ break;
+ }
+ if (etd->bLength < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad host encryption descriptor; "
+ "descriptor is too short "
+ "(%zu vs %zu needed)\n",
+ (size_t)etd->bLength, sizeof(*etd));
+ break;
+ }
+ itr += etd->bLength;
+ bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
+ "%s (0x%02x) ",
+ wusb_et_name(etd->bEncryptionType),
+ etd->bEncryptionValue);
+ wusbhc->ccm1_etd = etd;
+ }
+ dev_info(dev, "supported encryption types: %s\n", buf);
+ if (wusbhc->ccm1_etd == NULL) {
+ dev_err(dev, "E: host doesn't support CCM-1 crypto\n");
+ return 0;
+ }
+ /* Pretty print what we support */
+ return 0;
+}
+
+static void hwahc_security_release(struct hwahc *hwahc)
+{
+ /* nothing to do here so far... */
+}
+
+static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface,
+ kernel_ulong_t quirks)
+{
+ int result;
+ struct device *dev = &iface->dev;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct wahc *wa = &hwahc->wa;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+
+ wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */
+ wa->usb_iface = usb_get_intf(iface);
+ wusbhc->dev = dev;
+ /* defer getting the uwb_rc handle until it is needed since it
+ * may not have been registered by the hwa_rc driver yet. */
+ wusbhc->uwb_rc = NULL;
+ result = wa_fill_descr(wa); /* Get the device descriptor */
+ if (result < 0)
+ goto error_fill_descriptor;
+ if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) {
+ dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB "
+ "adapter (%u ports)\n", wa->wa_descr->bNumPorts);
+ wusbhc->ports_max = USB_MAXCHILDREN;
+ } else {
+ wusbhc->ports_max = wa->wa_descr->bNumPorts;
+ }
+ wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs;
+ wusbhc->start = __hwahc_op_wusbhc_start;
+ wusbhc->stop = __hwahc_op_wusbhc_stop;
+ wusbhc->mmcie_add = __hwahc_op_mmcie_add;
+ wusbhc->mmcie_rm = __hwahc_op_mmcie_rm;
+ wusbhc->dev_info_set = __hwahc_op_dev_info_set;
+ wusbhc->bwa_set = __hwahc_op_bwa_set;
+ wusbhc->set_num_dnts = __hwahc_op_set_num_dnts;
+ wusbhc->set_ptk = __hwahc_op_set_ptk;
+ wusbhc->set_gtk = __hwahc_op_set_gtk;
+ result = hwahc_security_create(hwahc);
+ if (result < 0) {
+ dev_err(dev, "Can't initialize security: %d\n", result);
+ goto error_security_create;
+ }
+ wa->wusb = wusbhc; /* FIXME: ugly, need to fix */
+ result = wusbhc_create(&hwahc->wusbhc);
+ if (result < 0) {
+ dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
+ goto error_wusbhc_create;
+ }
+ result = wa_create(&hwahc->wa, iface, quirks);
+ if (result < 0)
+ goto error_wa_create;
+ return 0;
+
+error_wa_create:
+ wusbhc_destroy(&hwahc->wusbhc);
+error_wusbhc_create:
+ /* WA Descr fill allocs no resources */
+error_security_create:
+error_fill_descriptor:
+ usb_put_intf(iface);
+ usb_put_dev(usb_dev);
+ return result;
+}
+
+static void hwahc_destroy(struct hwahc *hwahc)
+{
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+
+ mutex_lock(&wusbhc->mutex);
+ __wa_destroy(&hwahc->wa);
+ wusbhc_destroy(&hwahc->wusbhc);
+ hwahc_security_release(hwahc);
+ hwahc->wusbhc.dev = NULL;
+ uwb_rc_put(wusbhc->uwb_rc);
+ usb_put_intf(hwahc->wa.usb_iface);
+ usb_put_dev(hwahc->wa.usb_dev);
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static void hwahc_init(struct hwahc *hwahc)
+{
+ wa_init(&hwahc->wa);
+}
+
+static int hwahc_probe(struct usb_interface *usb_iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc;
+ struct hwahc *hwahc;
+ struct device *dev = &usb_iface->dev;
+
+ result = -ENOMEM;
+ usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa");
+ if (usb_hcd == NULL) {
+ dev_err(dev, "unable to allocate instance\n");
+ goto error_alloc;
+ }
+ usb_hcd->wireless = 1;
+ usb_hcd->self.sg_tablesize = ~0;
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ hwahc_init(hwahc);
+ result = hwahc_create(hwahc, usb_iface, id->driver_info);
+ if (result < 0) {
+ dev_err(dev, "Cannot initialize internals: %d\n", result);
+ goto error_hwahc_create;
+ }
+ result = usb_add_hcd(usb_hcd, 0, 0);
+ if (result < 0) {
+ dev_err(dev, "Cannot add HCD: %d\n", result);
+ goto error_add_hcd;
+ }
+ device_wakeup_enable(usb_hcd->self.controller);
+ result = wusbhc_b_create(&hwahc->wusbhc);
+ if (result < 0) {
+ dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result);
+ goto error_wusbhc_b_create;
+ }
+ return 0;
+
+error_wusbhc_b_create:
+ usb_remove_hcd(usb_hcd);
+error_add_hcd:
+ hwahc_destroy(hwahc);
+error_hwahc_create:
+ usb_put_hcd(usb_hcd);
+error_alloc:
+ return result;
+}
+
+static void hwahc_disconnect(struct usb_interface *usb_iface)
+{
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc;
+ struct hwahc *hwahc;
+
+ usb_hcd = usb_get_intfdata(usb_iface);
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ wusbhc_b_destroy(&hwahc->wusbhc);
+ usb_remove_hcd(usb_hcd);
+ hwahc_destroy(hwahc);
+ usb_put_hcd(usb_hcd);
+}
+
+static struct usb_device_id hwahc_id_table[] = {
+ /* Alereon 5310 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
+ .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
+ WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
+ /* Alereon 5611 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
+ .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
+ WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
+ /* FIXME: use class labels for this */
+ { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, hwahc_id_table);
+
+static struct usb_driver hwahc_driver = {
+ .name = "hwa-hc",
+ .probe = hwahc_probe,
+ .disconnect = hwahc_disconnect,
+ .id_table = hwahc_id_table,
+};
+
+module_usb_driver(hwahc_driver);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c
new file mode 100644
index 000000000..4f320d050
--- /dev/null
+++ b/drivers/usb/host/imx21-dbg.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2009 by Martin Fuzzey
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of imx21-hcd.c */
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define DEBUG
+#endif
+
+#ifndef DEBUG
+
+static inline void create_debug_files(struct imx21 *imx21) { }
+static inline void remove_debug_files(struct imx21 *imx21) { }
+static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {}
+static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb,
+ int status) {}
+static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {}
+static inline void debug_urb_queued_for_etd(struct imx21 *imx21,
+ struct urb *urb) {}
+static inline void debug_urb_queued_for_dmem(struct imx21 *imx21,
+ struct urb *urb) {}
+static inline void debug_etd_allocated(struct imx21 *imx21) {}
+static inline void debug_etd_freed(struct imx21 *imx21) {}
+static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {}
+static inline void debug_dmem_freed(struct imx21 *imx21, int size) {}
+static inline void debug_isoc_submitted(struct imx21 *imx21,
+ int frame, struct td *td) {}
+static inline void debug_isoc_completed(struct imx21 *imx21,
+ int frame, struct td *td, int cc, int len) {}
+
+#else
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static const char *dir_labels[] = {
+ "TD 0",
+ "OUT",
+ "IN",
+ "TD 1"
+};
+
+static const char *speed_labels[] = {
+ "Full",
+ "Low"
+};
+
+static const char *format_labels[] = {
+ "Control",
+ "ISO",
+ "Bulk",
+ "Interrupt"
+};
+
+static inline struct debug_stats *stats_for_urb(struct imx21 *imx21,
+ struct urb *urb)
+{
+ return usb_pipeisoc(urb->pipe) ?
+ &imx21->isoc_stats : &imx21->nonisoc_stats;
+}
+
+static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb)
+{
+ stats_for_urb(imx21, urb)->submitted++;
+}
+
+static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st)
+{
+ if (st)
+ stats_for_urb(imx21, urb)->completed_failed++;
+ else
+ stats_for_urb(imx21, urb)->completed_ok++;
+}
+
+static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb)
+{
+ stats_for_urb(imx21, urb)->unlinked++;
+}
+
+static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb)
+{
+ stats_for_urb(imx21, urb)->queue_etd++;
+}
+
+static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb)
+{
+ stats_for_urb(imx21, urb)->queue_dmem++;
+}
+
+static inline void debug_etd_allocated(struct imx21 *imx21)
+{
+ imx21->etd_usage.maximum = max(
+ ++(imx21->etd_usage.value),
+ imx21->etd_usage.maximum);
+}
+
+static inline void debug_etd_freed(struct imx21 *imx21)
+{
+ imx21->etd_usage.value--;
+}
+
+static inline void debug_dmem_allocated(struct imx21 *imx21, int size)
+{
+ imx21->dmem_usage.value += size;
+ imx21->dmem_usage.maximum = max(
+ imx21->dmem_usage.value,
+ imx21->dmem_usage.maximum);
+}
+
+static inline void debug_dmem_freed(struct imx21 *imx21, int size)
+{
+ imx21->dmem_usage.value -= size;
+}
+
+
+static void debug_isoc_submitted(struct imx21 *imx21,
+ int frame, struct td *td)
+{
+ struct debug_isoc_trace *trace = &imx21->isoc_trace[
+ imx21->isoc_trace_index++];
+
+ imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace);
+ trace->schedule_frame = td->frame;
+ trace->submit_frame = frame;
+ trace->request_len = td->len;
+ trace->td = td;
+}
+
+static inline void debug_isoc_completed(struct imx21 *imx21,
+ int frame, struct td *td, int cc, int len)
+{
+ struct debug_isoc_trace *trace, *trace_failed;
+ int i;
+ int found = 0;
+
+ trace = imx21->isoc_trace;
+ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) {
+ if (trace->td == td) {
+ trace->done_frame = frame;
+ trace->done_len = len;
+ trace->cc = cc;
+ trace->td = NULL;
+ found = 1;
+ break;
+ }
+ }
+
+ if (found && cc) {
+ trace_failed = &imx21->isoc_trace_failed[
+ imx21->isoc_trace_index_failed++];
+
+ imx21->isoc_trace_index_failed %= ARRAY_SIZE(
+ imx21->isoc_trace_failed);
+ *trace_failed = *trace;
+ }
+}
+
+
+static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize)
+{
+ if (ep)
+ snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)",
+ ep->desc.bEndpointAddress,
+ usb_endpoint_type(&ep->desc),
+ ep);
+ else
+ snprintf(buf, bufsize, "none");
+ return buf;
+}
+
+static char *format_etd_dword0(u32 value, char *buf, int bufsize)
+{
+ snprintf(buf, bufsize,
+ "addr=%d ep=%d dir=%s speed=%s format=%s halted=%d",
+ value & 0x7F,
+ (value >> DW0_ENDPNT) & 0x0F,
+ dir_labels[(value >> DW0_DIRECT) & 0x03],
+ speed_labels[(value >> DW0_SPEED) & 0x01],
+ format_labels[(value >> DW0_FORMAT) & 0x03],
+ (value >> DW0_HALTED) & 0x01);
+ return buf;
+}
+
+static int debug_status_show(struct seq_file *s, void *v)
+{
+ struct imx21 *imx21 = s->private;
+ int etds_allocated = 0;
+ int etds_sw_busy = 0;
+ int etds_hw_busy = 0;
+ int dmem_blocks = 0;
+ int queued_for_etd = 0;
+ int queued_for_dmem = 0;
+ unsigned int dmem_bytes = 0;
+ int i;
+ struct etd_priv *etd;
+ u32 etd_enable_mask;
+ unsigned long flags;
+ struct imx21_dmem_area *dmem;
+ struct ep_priv *ep_priv;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ etd_enable_mask = readl(imx21->regs + USBH_ETDENSET);
+ for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
+ if (etd->alloc)
+ etds_allocated++;
+ if (etd->urb)
+ etds_sw_busy++;
+ if (etd_enable_mask & (1<<i))
+ etds_hw_busy++;
+ }
+
+ list_for_each_entry(dmem, &imx21->dmem_list, list) {
+ dmem_bytes += dmem->size;
+ dmem_blocks++;
+ }
+
+ list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue)
+ queued_for_etd++;
+
+ list_for_each_entry(etd, &imx21->queue_for_dmem, queue)
+ queued_for_dmem++;
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ seq_printf(s,
+ "Frame: %d\n"
+ "ETDs allocated: %d/%d (max=%d)\n"
+ "ETDs in use sw: %d\n"
+ "ETDs in use hw: %d\n"
+ "DMEM allocated: %d/%d (max=%d)\n"
+ "DMEM blocks: %d\n"
+ "Queued waiting for ETD: %d\n"
+ "Queued waiting for DMEM: %d\n",
+ readl(imx21->regs + USBH_FRMNUB) & 0xFFFF,
+ etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum,
+ etds_sw_busy,
+ etds_hw_busy,
+ dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum,
+ dmem_blocks,
+ queued_for_etd,
+ queued_for_dmem);
+
+ return 0;
+}
+
+static int debug_dmem_show(struct seq_file *s, void *v)
+{
+ struct imx21 *imx21 = s->private;
+ struct imx21_dmem_area *dmem;
+ unsigned long flags;
+ char ep_text[40];
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ list_for_each_entry(dmem, &imx21->dmem_list, list)
+ seq_printf(s,
+ "%04X: size=0x%X "
+ "ep=%s\n",
+ dmem->offset, dmem->size,
+ format_ep(dmem->ep, ep_text, sizeof(ep_text)));
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ return 0;
+}
+
+static int debug_etd_show(struct seq_file *s, void *v)
+{
+ struct imx21 *imx21 = s->private;
+ struct etd_priv *etd;
+ char buf[60];
+ u32 dword;
+ int i, j;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
+ int state = -1;
+ struct urb_priv *urb_priv;
+ if (etd->urb) {
+ urb_priv = etd->urb->hcpriv;
+ if (urb_priv)
+ state = urb_priv->state;
+ }
+
+ seq_printf(s,
+ "etd_num: %d\n"
+ "ep: %s\n"
+ "alloc: %d\n"
+ "len: %d\n"
+ "busy sw: %d\n"
+ "busy hw: %d\n"
+ "urb state: %d\n"
+ "current urb: %p\n",
+
+ i,
+ format_ep(etd->ep, buf, sizeof(buf)),
+ etd->alloc,
+ etd->len,
+ etd->urb != NULL,
+ (readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0,
+ state,
+ etd->urb);
+
+ for (j = 0; j < 4; j++) {
+ dword = etd_readl(imx21, i, j);
+ switch (j) {
+ case 0:
+ format_etd_dword0(dword, buf, sizeof(buf));
+ break;
+ case 2:
+ snprintf(buf, sizeof(buf),
+ "cc=0X%02X", dword >> DW2_COMPCODE);
+ break;
+ default:
+ *buf = 0;
+ break;
+ }
+ seq_printf(s,
+ "dword %d: submitted=%08X cur=%08X [%s]\n",
+ j,
+ etd->submitted_dwords[j],
+ dword,
+ buf);
+ }
+ seq_printf(s, "\n");
+ }
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ return 0;
+}
+
+static void debug_statistics_show_one(struct seq_file *s,
+ const char *name, struct debug_stats *stats)
+{
+ seq_printf(s, "%s:\n"
+ "submitted URBs: %lu\n"
+ "completed OK: %lu\n"
+ "completed failed: %lu\n"
+ "unlinked: %lu\n"
+ "queued for ETD: %lu\n"
+ "queued for DMEM: %lu\n\n",
+ name,
+ stats->submitted,
+ stats->completed_ok,
+ stats->completed_failed,
+ stats->unlinked,
+ stats->queue_etd,
+ stats->queue_dmem);
+}
+
+static int debug_statistics_show(struct seq_file *s, void *v)
+{
+ struct imx21 *imx21 = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats);
+ debug_statistics_show_one(s, "isoc", &imx21->isoc_stats);
+ seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks);
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ return 0;
+}
+
+static void debug_isoc_show_one(struct seq_file *s,
+ const char *name, int index, struct debug_isoc_trace *trace)
+{
+ seq_printf(s, "%s %d:\n"
+ "cc=0X%02X\n"
+ "scheduled frame %d (%d)\n"
+ "submitted frame %d (%d)\n"
+ "completed frame %d (%d)\n"
+ "requested length=%d\n"
+ "completed length=%d\n\n",
+ name, index,
+ trace->cc,
+ trace->schedule_frame, trace->schedule_frame & 0xFFFF,
+ trace->submit_frame, trace->submit_frame & 0xFFFF,
+ trace->done_frame, trace->done_frame & 0xFFFF,
+ trace->request_len,
+ trace->done_len);
+}
+
+static int debug_isoc_show(struct seq_file *s, void *v)
+{
+ struct imx21 *imx21 = s->private;
+ struct debug_isoc_trace *trace;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ trace = imx21->isoc_trace_failed;
+ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++)
+ debug_isoc_show_one(s, "isoc failed", i, trace);
+
+ trace = imx21->isoc_trace;
+ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++)
+ debug_isoc_show_one(s, "isoc", i, trace);
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ return 0;
+}
+
+static int debug_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_status_show, inode->i_private);
+}
+
+static int debug_dmem_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_dmem_show, inode->i_private);
+}
+
+static int debug_etd_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_etd_show, inode->i_private);
+}
+
+static int debug_statistics_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_statistics_show, inode->i_private);
+}
+
+static int debug_isoc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_isoc_show, inode->i_private);
+}
+
+static const struct file_operations debug_status_fops = {
+ .open = debug_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations debug_dmem_fops = {
+ .open = debug_dmem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations debug_etd_fops = {
+ .open = debug_etd_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations debug_statistics_fops = {
+ .open = debug_statistics_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations debug_isoc_fops = {
+ .open = debug_isoc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void create_debug_files(struct imx21 *imx21)
+{
+ imx21->debug_root = debugfs_create_dir(dev_name(imx21->dev), NULL);
+ if (!imx21->debug_root)
+ goto failed_create_rootdir;
+
+ if (!debugfs_create_file("status", S_IRUGO,
+ imx21->debug_root, imx21, &debug_status_fops))
+ goto failed_create;
+
+ if (!debugfs_create_file("dmem", S_IRUGO,
+ imx21->debug_root, imx21, &debug_dmem_fops))
+ goto failed_create;
+
+ if (!debugfs_create_file("etd", S_IRUGO,
+ imx21->debug_root, imx21, &debug_etd_fops))
+ goto failed_create;
+
+ if (!debugfs_create_file("statistics", S_IRUGO,
+ imx21->debug_root, imx21, &debug_statistics_fops))
+ goto failed_create;
+
+ if (!debugfs_create_file("isoc", S_IRUGO,
+ imx21->debug_root, imx21, &debug_isoc_fops))
+ goto failed_create;
+
+ return;
+
+failed_create:
+ debugfs_remove_recursive(imx21->debug_root);
+
+failed_create_rootdir:
+ imx21->debug_root = NULL;
+}
+
+
+static void remove_debug_files(struct imx21 *imx21)
+{
+ if (imx21->debug_root) {
+ debugfs_remove_recursive(imx21->debug_root);
+ imx21->debug_root = NULL;
+ }
+}
+
+#endif
+
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
new file mode 100644
index 000000000..f542045dc
--- /dev/null
+++ b/drivers/usb/host/imx21-hcd.c
@@ -0,0 +1,1946 @@
+/*
+ * USB Host Controller Driver for IMX21
+ *
+ * Copyright (C) 2006 Loping Dog Embedded Systems
+ * Copyright (C) 2009 Martin Fuzzey
+ * Originally written by Jay Monkman <jtm@lopingdog.com>
+ * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+ /*
+ * The i.MX21 USB hardware contains
+ * * 32 transfer descriptors (called ETDs)
+ * * 4Kb of Data memory
+ *
+ * The data memory is shared between the host and function controllers
+ * (but this driver only supports the host controller)
+ *
+ * So setting up a transfer involves:
+ * * Allocating a ETD
+ * * Fill in ETD with appropriate information
+ * * Allocating data memory (and putting the offset in the ETD)
+ * * Activate the ETD
+ * * Get interrupt when done.
+ *
+ * An ETD is assigned to each active endpoint.
+ *
+ * Low resource (ETD and Data memory) situations are handled differently for
+ * isochronous and non insosynchronous transactions :
+ *
+ * Non ISOC transfers are queued if either ETDs or Data memory are unavailable
+ *
+ * ISOC transfers use 2 ETDs per endpoint to achieve double buffering.
+ * They allocate both ETDs and Data memory during URB submission
+ * (and fail if unavailable).
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+
+#include "imx21-hcd.h"
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define DEBUG
+#endif
+
+#ifdef DEBUG
+#define DEBUG_LOG_FRAME(imx21, etd, event) \
+ (etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB)
+#else
+#define DEBUG_LOG_FRAME(imx21, etd, event) do { } while (0)
+#endif
+
+static const char hcd_name[] = "imx21-hcd";
+
+static inline struct imx21 *hcd_to_imx21(struct usb_hcd *hcd)
+{
+ return (struct imx21 *)hcd->hcd_priv;
+}
+
+
+/* =========================================== */
+/* Hardware access helpers */
+/* =========================================== */
+
+static inline void set_register_bits(struct imx21 *imx21, u32 offset, u32 mask)
+{
+ void __iomem *reg = imx21->regs + offset;
+ writel(readl(reg) | mask, reg);
+}
+
+static inline void clear_register_bits(struct imx21 *imx21,
+ u32 offset, u32 mask)
+{
+ void __iomem *reg = imx21->regs + offset;
+ writel(readl(reg) & ~mask, reg);
+}
+
+static inline void clear_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask)
+{
+ void __iomem *reg = imx21->regs + offset;
+
+ if (readl(reg) & mask)
+ writel(mask, reg);
+}
+
+static inline void set_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask)
+{
+ void __iomem *reg = imx21->regs + offset;
+
+ if (!(readl(reg) & mask))
+ writel(mask, reg);
+}
+
+static void etd_writel(struct imx21 *imx21, int etd_num, int dword, u32 value)
+{
+ writel(value, imx21->regs + USB_ETD_DWORD(etd_num, dword));
+}
+
+static u32 etd_readl(struct imx21 *imx21, int etd_num, int dword)
+{
+ return readl(imx21->regs + USB_ETD_DWORD(etd_num, dword));
+}
+
+static inline int wrap_frame(int counter)
+{
+ return counter & 0xFFFF;
+}
+
+static inline int frame_after(int frame, int after)
+{
+ /* handle wrapping like jiffies time_afer */
+ return (s16)((s16)after - (s16)frame) < 0;
+}
+
+static int imx21_hc_get_frame(struct usb_hcd *hcd)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+
+ return wrap_frame(readl(imx21->regs + USBH_FRMNUB));
+}
+
+static inline bool unsuitable_for_dma(dma_addr_t addr)
+{
+ return (addr & 3) != 0;
+}
+
+#include "imx21-dbg.c"
+
+static void nonisoc_urb_completed_for_etd(
+ struct imx21 *imx21, struct etd_priv *etd, int status);
+static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb);
+static void free_dmem(struct imx21 *imx21, struct etd_priv *etd);
+
+/* =========================================== */
+/* ETD management */
+/* =========================================== */
+
+static int alloc_etd(struct imx21 *imx21)
+{
+ int i;
+ struct etd_priv *etd = imx21->etd;
+
+ for (i = 0; i < USB_NUM_ETD; i++, etd++) {
+ if (etd->alloc == 0) {
+ memset(etd, 0, sizeof(imx21->etd[0]));
+ etd->alloc = 1;
+ debug_etd_allocated(imx21);
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void disactivate_etd(struct imx21 *imx21, int num)
+{
+ int etd_mask = (1 << num);
+ struct etd_priv *etd = &imx21->etd[num];
+
+ writel(etd_mask, imx21->regs + USBH_ETDENCLR);
+ clear_register_bits(imx21, USBH_ETDDONEEN, etd_mask);
+ writel(etd_mask, imx21->regs + USB_ETDDMACHANLCLR);
+ clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask);
+
+ etd->active_count = 0;
+
+ DEBUG_LOG_FRAME(imx21, etd, disactivated);
+}
+
+static void reset_etd(struct imx21 *imx21, int num)
+{
+ struct etd_priv *etd = imx21->etd + num;
+ int i;
+
+ disactivate_etd(imx21, num);
+
+ for (i = 0; i < 4; i++)
+ etd_writel(imx21, num, i, 0);
+ etd->urb = NULL;
+ etd->ep = NULL;
+ etd->td = NULL;
+ etd->bounce_buffer = NULL;
+}
+
+static void free_etd(struct imx21 *imx21, int num)
+{
+ if (num < 0)
+ return;
+
+ if (num >= USB_NUM_ETD) {
+ dev_err(imx21->dev, "BAD etd=%d!\n", num);
+ return;
+ }
+ if (imx21->etd[num].alloc == 0) {
+ dev_err(imx21->dev, "ETD %d already free!\n", num);
+ return;
+ }
+
+ debug_etd_freed(imx21);
+ reset_etd(imx21, num);
+ memset(&imx21->etd[num], 0, sizeof(imx21->etd[0]));
+}
+
+
+static void setup_etd_dword0(struct imx21 *imx21,
+ int etd_num, struct urb *urb, u8 dir, u16 maxpacket)
+{
+ etd_writel(imx21, etd_num, 0,
+ ((u32) usb_pipedevice(urb->pipe)) << DW0_ADDRESS |
+ ((u32) usb_pipeendpoint(urb->pipe) << DW0_ENDPNT) |
+ ((u32) dir << DW0_DIRECT) |
+ ((u32) ((urb->dev->speed == USB_SPEED_LOW) ?
+ 1 : 0) << DW0_SPEED) |
+ ((u32) fmt_urb_to_etd[usb_pipetype(urb->pipe)] << DW0_FORMAT) |
+ ((u32) maxpacket << DW0_MAXPKTSIZ));
+}
+
+/**
+ * Copy buffer to data controller data memory.
+ * We cannot use memcpy_toio() because the hardware requires 32bit writes
+ */
+static void copy_to_dmem(
+ struct imx21 *imx21, int dmem_offset, void *src, int count)
+{
+ void __iomem *dmem = imx21->regs + USBOTG_DMEM + dmem_offset;
+ u32 word = 0;
+ u8 *p = src;
+ int byte = 0;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ byte = i % 4;
+ word += (*p++ << (byte * 8));
+ if (byte == 3) {
+ writel(word, dmem);
+ dmem += 4;
+ word = 0;
+ }
+ }
+
+ if (count && byte != 3)
+ writel(word, dmem);
+}
+
+static void activate_etd(struct imx21 *imx21, int etd_num, u8 dir)
+{
+ u32 etd_mask = 1 << etd_num;
+ struct etd_priv *etd = &imx21->etd[etd_num];
+
+ if (etd->dma_handle && unsuitable_for_dma(etd->dma_handle)) {
+ /* For non aligned isoc the condition below is always true */
+ if (etd->len <= etd->dmem_size) {
+ /* Fits into data memory, use PIO */
+ if (dir != TD_DIR_IN) {
+ copy_to_dmem(imx21,
+ etd->dmem_offset,
+ etd->cpu_buffer, etd->len);
+ }
+ etd->dma_handle = 0;
+
+ } else {
+ /* Too big for data memory, use bounce buffer */
+ enum dma_data_direction dmadir;
+
+ if (dir == TD_DIR_IN) {
+ dmadir = DMA_FROM_DEVICE;
+ etd->bounce_buffer = kmalloc(etd->len,
+ GFP_ATOMIC);
+ } else {
+ dmadir = DMA_TO_DEVICE;
+ etd->bounce_buffer = kmemdup(etd->cpu_buffer,
+ etd->len,
+ GFP_ATOMIC);
+ }
+ if (!etd->bounce_buffer) {
+ dev_err(imx21->dev, "failed bounce alloc\n");
+ goto err_bounce_alloc;
+ }
+
+ etd->dma_handle =
+ dma_map_single(imx21->dev,
+ etd->bounce_buffer,
+ etd->len,
+ dmadir);
+ if (dma_mapping_error(imx21->dev, etd->dma_handle)) {
+ dev_err(imx21->dev, "failed bounce map\n");
+ goto err_bounce_map;
+ }
+ }
+ }
+
+ clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask);
+ set_register_bits(imx21, USBH_ETDDONEEN, etd_mask);
+ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
+ clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
+
+ if (etd->dma_handle) {
+ set_register_bits(imx21, USB_ETDDMACHANLCLR, etd_mask);
+ clear_toggle_bit(imx21, USBH_XBUFSTAT, etd_mask);
+ clear_toggle_bit(imx21, USBH_YBUFSTAT, etd_mask);
+ writel(etd->dma_handle, imx21->regs + USB_ETDSMSA(etd_num));
+ set_register_bits(imx21, USB_ETDDMAEN, etd_mask);
+ } else {
+ if (dir != TD_DIR_IN) {
+ /* need to set for ZLP and PIO */
+ set_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
+ set_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
+ }
+ }
+
+ DEBUG_LOG_FRAME(imx21, etd, activated);
+
+#ifdef DEBUG
+ if (!etd->active_count) {
+ int i;
+ etd->activated_frame = readl(imx21->regs + USBH_FRMNUB);
+ etd->disactivated_frame = -1;
+ etd->last_int_frame = -1;
+ etd->last_req_frame = -1;
+
+ for (i = 0; i < 4; i++)
+ etd->submitted_dwords[i] = etd_readl(imx21, etd_num, i);
+ }
+#endif
+
+ etd->active_count = 1;
+ writel(etd_mask, imx21->regs + USBH_ETDENSET);
+ return;
+
+err_bounce_map:
+ kfree(etd->bounce_buffer);
+
+err_bounce_alloc:
+ free_dmem(imx21, etd);
+ nonisoc_urb_completed_for_etd(imx21, etd, -ENOMEM);
+}
+
+/* =========================================== */
+/* Data memory management */
+/* =========================================== */
+
+static int alloc_dmem(struct imx21 *imx21, unsigned int size,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int offset = 0;
+ struct imx21_dmem_area *area;
+ struct imx21_dmem_area *tmp;
+
+ size += (~size + 1) & 0x3; /* Round to 4 byte multiple */
+
+ if (size > DMEM_SIZE) {
+ dev_err(imx21->dev, "size=%d > DMEM_SIZE(%d)\n",
+ size, DMEM_SIZE);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(tmp, &imx21->dmem_list, list) {
+ if ((size + offset) < offset)
+ goto fail;
+ if ((size + offset) <= tmp->offset)
+ break;
+ offset = tmp->size + tmp->offset;
+ if ((offset + size) > DMEM_SIZE)
+ goto fail;
+ }
+
+ area = kmalloc(sizeof(struct imx21_dmem_area), GFP_ATOMIC);
+ if (area == NULL)
+ return -ENOMEM;
+
+ area->ep = ep;
+ area->offset = offset;
+ area->size = size;
+ list_add_tail(&area->list, &tmp->list);
+ debug_dmem_allocated(imx21, size);
+ return offset;
+
+fail:
+ return -ENOMEM;
+}
+
+/* Memory now available for a queued ETD - activate it */
+static void activate_queued_etd(struct imx21 *imx21,
+ struct etd_priv *etd, u32 dmem_offset)
+{
+ struct urb_priv *urb_priv = etd->urb->hcpriv;
+ int etd_num = etd - &imx21->etd[0];
+ u32 maxpacket = etd_readl(imx21, etd_num, 1) >> DW1_YBUFSRTAD;
+ u8 dir = (etd_readl(imx21, etd_num, 2) >> DW2_DIRPID) & 0x03;
+
+ dev_dbg(imx21->dev, "activating queued ETD %d now DMEM available\n",
+ etd_num);
+ etd_writel(imx21, etd_num, 1,
+ ((dmem_offset + maxpacket) << DW1_YBUFSRTAD) | dmem_offset);
+
+ etd->dmem_offset = dmem_offset;
+ urb_priv->active = 1;
+ activate_etd(imx21, etd_num, dir);
+}
+
+static void free_dmem(struct imx21 *imx21, struct etd_priv *etd)
+{
+ struct imx21_dmem_area *area;
+ struct etd_priv *tmp;
+ int found = 0;
+ int offset;
+
+ if (!etd->dmem_size)
+ return;
+ etd->dmem_size = 0;
+
+ offset = etd->dmem_offset;
+ list_for_each_entry(area, &imx21->dmem_list, list) {
+ if (area->offset == offset) {
+ debug_dmem_freed(imx21, area->size);
+ list_del(&area->list);
+ kfree(area);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_err(imx21->dev,
+ "Trying to free unallocated DMEM %d\n", offset);
+ return;
+ }
+
+ /* Try again to allocate memory for anything we've queued */
+ list_for_each_entry_safe(etd, tmp, &imx21->queue_for_dmem, queue) {
+ offset = alloc_dmem(imx21, etd->dmem_size, etd->ep);
+ if (offset >= 0) {
+ list_del(&etd->queue);
+ activate_queued_etd(imx21, etd, (u32)offset);
+ }
+ }
+}
+
+static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep)
+{
+ struct imx21_dmem_area *area, *tmp;
+
+ list_for_each_entry_safe(area, tmp, &imx21->dmem_list, list) {
+ if (area->ep == ep) {
+ dev_err(imx21->dev,
+ "Active DMEM %d for disabled ep=%p\n",
+ area->offset, ep);
+ list_del(&area->list);
+ kfree(area);
+ }
+ }
+}
+
+
+/* =========================================== */
+/* End handling */
+/* =========================================== */
+
+/* Endpoint now idle - release its ETD(s) or assign to queued request */
+static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv)
+{
+ int i;
+
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ int etd_num = ep_priv->etd[i];
+ struct etd_priv *etd;
+ if (etd_num < 0)
+ continue;
+
+ etd = &imx21->etd[etd_num];
+ ep_priv->etd[i] = -1;
+
+ free_dmem(imx21, etd); /* for isoc */
+
+ if (list_empty(&imx21->queue_for_etd)) {
+ free_etd(imx21, etd_num);
+ continue;
+ }
+
+ dev_dbg(imx21->dev,
+ "assigning idle etd %d for queued request\n", etd_num);
+ ep_priv = list_first_entry(&imx21->queue_for_etd,
+ struct ep_priv, queue);
+ list_del(&ep_priv->queue);
+ reset_etd(imx21, etd_num);
+ ep_priv->waiting_etd = 0;
+ ep_priv->etd[i] = etd_num;
+
+ if (list_empty(&ep_priv->ep->urb_list)) {
+ dev_err(imx21->dev, "No urb for queued ep!\n");
+ continue;
+ }
+ schedule_nonisoc_etd(imx21, list_first_entry(
+ &ep_priv->ep->urb_list, struct urb, urb_list));
+ }
+}
+
+static void urb_done(struct usb_hcd *hcd, struct urb *urb, int status)
+__releases(imx21->lock)
+__acquires(imx21->lock)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct ep_priv *ep_priv = urb->ep->hcpriv;
+ struct urb_priv *urb_priv = urb->hcpriv;
+
+ debug_urb_completed(imx21, urb, status);
+ dev_vdbg(imx21->dev, "urb %p done %d\n", urb, status);
+
+ kfree(urb_priv->isoc_td);
+ kfree(urb->hcpriv);
+ urb->hcpriv = NULL;
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock(&imx21->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ spin_lock(&imx21->lock);
+ if (list_empty(&ep_priv->ep->urb_list))
+ ep_idle(imx21, ep_priv);
+}
+
+static void nonisoc_urb_completed_for_etd(
+ struct imx21 *imx21, struct etd_priv *etd, int status)
+{
+ struct usb_host_endpoint *ep = etd->ep;
+
+ urb_done(imx21->hcd, etd->urb, status);
+ etd->urb = NULL;
+
+ if (!list_empty(&ep->urb_list)) {
+ struct urb *urb = list_first_entry(
+ &ep->urb_list, struct urb, urb_list);
+
+ dev_vdbg(imx21->dev, "next URB %p\n", urb);
+ schedule_nonisoc_etd(imx21, urb);
+ }
+}
+
+
+/* =========================================== */
+/* ISOC Handling ... */
+/* =========================================== */
+
+static void schedule_isoc_etds(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct ep_priv *ep_priv = ep->hcpriv;
+ struct etd_priv *etd;
+ struct urb_priv *urb_priv;
+ struct td *td;
+ int etd_num;
+ int i;
+ int cur_frame;
+ u8 dir;
+
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+too_late:
+ if (list_empty(&ep_priv->td_list))
+ break;
+
+ etd_num = ep_priv->etd[i];
+ if (etd_num < 0)
+ break;
+
+ etd = &imx21->etd[etd_num];
+ if (etd->urb)
+ continue;
+
+ td = list_entry(ep_priv->td_list.next, struct td, list);
+ list_del(&td->list);
+ urb_priv = td->urb->hcpriv;
+
+ cur_frame = imx21_hc_get_frame(hcd);
+ if (frame_after(cur_frame, td->frame)) {
+ dev_dbg(imx21->dev, "isoc too late frame %d > %d\n",
+ cur_frame, td->frame);
+ urb_priv->isoc_status = -EXDEV;
+ td->urb->iso_frame_desc[
+ td->isoc_index].actual_length = 0;
+ td->urb->iso_frame_desc[td->isoc_index].status = -EXDEV;
+ if (--urb_priv->isoc_remaining == 0)
+ urb_done(hcd, td->urb, urb_priv->isoc_status);
+ goto too_late;
+ }
+
+ urb_priv->active = 1;
+ etd->td = td;
+ etd->ep = td->ep;
+ etd->urb = td->urb;
+ etd->len = td->len;
+ etd->dma_handle = td->dma_handle;
+ etd->cpu_buffer = td->cpu_buffer;
+
+ debug_isoc_submitted(imx21, cur_frame, td);
+
+ dir = usb_pipeout(td->urb->pipe) ? TD_DIR_OUT : TD_DIR_IN;
+ setup_etd_dword0(imx21, etd_num, td->urb, dir, etd->dmem_size);
+ etd_writel(imx21, etd_num, 1, etd->dmem_offset);
+ etd_writel(imx21, etd_num, 2,
+ (TD_NOTACCESSED << DW2_COMPCODE) |
+ ((td->frame & 0xFFFF) << DW2_STARTFRM));
+ etd_writel(imx21, etd_num, 3,
+ (TD_NOTACCESSED << DW3_COMPCODE0) |
+ (td->len << DW3_PKTLEN0));
+
+ activate_etd(imx21, etd_num, dir);
+ }
+}
+
+static void isoc_etd_done(struct usb_hcd *hcd, int etd_num)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ int etd_mask = 1 << etd_num;
+ struct etd_priv *etd = imx21->etd + etd_num;
+ struct urb *urb = etd->urb;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct td *td = etd->td;
+ struct usb_host_endpoint *ep = etd->ep;
+ int isoc_index = td->isoc_index;
+ unsigned int pipe = urb->pipe;
+ int dir_in = usb_pipein(pipe);
+ int cc;
+ int bytes_xfrd;
+
+ disactivate_etd(imx21, etd_num);
+
+ cc = (etd_readl(imx21, etd_num, 3) >> DW3_COMPCODE0) & 0xf;
+ bytes_xfrd = etd_readl(imx21, etd_num, 3) & 0x3ff;
+
+ /* Input doesn't always fill the buffer, don't generate an error
+ * when this happens.
+ */
+ if (dir_in && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
+ if (cc == TD_NOTACCESSED)
+ bytes_xfrd = 0;
+
+ debug_isoc_completed(imx21,
+ imx21_hc_get_frame(hcd), td, cc, bytes_xfrd);
+ if (cc) {
+ urb_priv->isoc_status = -EXDEV;
+ dev_dbg(imx21->dev,
+ "bad iso cc=0x%X frame=%d sched frame=%d "
+ "cnt=%d len=%d urb=%p etd=%d index=%d\n",
+ cc, imx21_hc_get_frame(hcd), td->frame,
+ bytes_xfrd, td->len, urb, etd_num, isoc_index);
+ }
+
+ if (dir_in) {
+ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
+ if (!etd->dma_handle)
+ memcpy_fromio(etd->cpu_buffer,
+ imx21->regs + USBOTG_DMEM + etd->dmem_offset,
+ bytes_xfrd);
+ }
+
+ urb->actual_length += bytes_xfrd;
+ urb->iso_frame_desc[isoc_index].actual_length = bytes_xfrd;
+ urb->iso_frame_desc[isoc_index].status = cc_to_error[cc];
+
+ etd->td = NULL;
+ etd->urb = NULL;
+ etd->ep = NULL;
+
+ if (--urb_priv->isoc_remaining == 0)
+ urb_done(hcd, urb, urb_priv->isoc_status);
+
+ schedule_isoc_etds(hcd, ep);
+}
+
+static struct ep_priv *alloc_isoc_ep(
+ struct imx21 *imx21, struct usb_host_endpoint *ep)
+{
+ struct ep_priv *ep_priv;
+ int i;
+
+ ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
+ if (!ep_priv)
+ return NULL;
+
+ for (i = 0; i < NUM_ISO_ETDS; i++)
+ ep_priv->etd[i] = -1;
+
+ INIT_LIST_HEAD(&ep_priv->td_list);
+ ep_priv->ep = ep;
+ ep->hcpriv = ep_priv;
+ return ep_priv;
+}
+
+static int alloc_isoc_etds(struct imx21 *imx21, struct ep_priv *ep_priv)
+{
+ int i, j;
+ int etd_num;
+
+ /* Allocate the ETDs if required */
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ if (ep_priv->etd[i] < 0) {
+ etd_num = alloc_etd(imx21);
+ if (etd_num < 0)
+ goto alloc_etd_failed;
+
+ ep_priv->etd[i] = etd_num;
+ imx21->etd[etd_num].ep = ep_priv->ep;
+ }
+ }
+ return 0;
+
+alloc_etd_failed:
+ dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
+ for (j = 0; j < i; j++) {
+ free_etd(imx21, ep_priv->etd[j]);
+ ep_priv->etd[j] = -1;
+ }
+ return -ENOMEM;
+}
+
+static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct urb_priv *urb_priv;
+ unsigned long flags;
+ struct ep_priv *ep_priv;
+ struct td *td = NULL;
+ int i;
+ int ret;
+ int cur_frame;
+ u16 maxpacket;
+
+ urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags);
+ if (urb_priv == NULL)
+ return -ENOMEM;
+
+ urb_priv->isoc_td = kzalloc(
+ sizeof(struct td) * urb->number_of_packets, mem_flags);
+ if (urb_priv->isoc_td == NULL) {
+ ret = -ENOMEM;
+ goto alloc_td_failed;
+ }
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ if (ep->hcpriv == NULL) {
+ ep_priv = alloc_isoc_ep(imx21, ep);
+ if (ep_priv == NULL) {
+ ret = -ENOMEM;
+ goto alloc_ep_failed;
+ }
+ } else {
+ ep_priv = ep->hcpriv;
+ }
+
+ ret = alloc_isoc_etds(imx21, ep_priv);
+ if (ret)
+ goto alloc_etd_failed;
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto link_failed;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->error_count = 0;
+ urb->hcpriv = urb_priv;
+ urb_priv->ep = ep;
+
+ /* allocate data memory for largest packets if not already done */
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ struct etd_priv *etd = &imx21->etd[ep_priv->etd[i]];
+
+ if (etd->dmem_size > 0 && etd->dmem_size < maxpacket) {
+ /* not sure if this can really occur.... */
+ dev_err(imx21->dev, "increasing isoc buffer %d->%d\n",
+ etd->dmem_size, maxpacket);
+ ret = -EMSGSIZE;
+ goto alloc_dmem_failed;
+ }
+
+ if (etd->dmem_size == 0) {
+ etd->dmem_offset = alloc_dmem(imx21, maxpacket, ep);
+ if (etd->dmem_offset < 0) {
+ dev_dbg(imx21->dev, "failed alloc isoc dmem\n");
+ ret = -EAGAIN;
+ goto alloc_dmem_failed;
+ }
+ etd->dmem_size = maxpacket;
+ }
+ }
+
+ /* calculate frame */
+ cur_frame = imx21_hc_get_frame(hcd);
+ i = 0;
+ if (list_empty(&ep_priv->td_list)) {
+ urb->start_frame = wrap_frame(cur_frame + 5);
+ } else {
+ urb->start_frame = wrap_frame(list_entry(ep_priv->td_list.prev,
+ struct td, list)->frame + urb->interval);
+
+ if (frame_after(cur_frame, urb->start_frame)) {
+ dev_dbg(imx21->dev,
+ "enqueue: adjusting iso start %d (cur=%d) asap=%d\n",
+ urb->start_frame, cur_frame,
+ (urb->transfer_flags & URB_ISO_ASAP) != 0);
+ i = DIV_ROUND_UP(wrap_frame(
+ cur_frame - urb->start_frame),
+ urb->interval);
+
+ /* Treat underruns as if URB_ISO_ASAP was set */
+ if ((urb->transfer_flags & URB_ISO_ASAP) ||
+ i >= urb->number_of_packets) {
+ urb->start_frame = wrap_frame(urb->start_frame
+ + i * urb->interval);
+ i = 0;
+ }
+ }
+ }
+
+ /* set up transfers */
+ urb_priv->isoc_remaining = urb->number_of_packets - i;
+ td = urb_priv->isoc_td;
+ for (; i < urb->number_of_packets; i++, td++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ td->ep = ep;
+ td->urb = urb;
+ td->len = urb->iso_frame_desc[i].length;
+ td->isoc_index = i;
+ td->frame = wrap_frame(urb->start_frame + urb->interval * i);
+ td->dma_handle = urb->transfer_dma + offset;
+ td->cpu_buffer = urb->transfer_buffer + offset;
+ list_add_tail(&td->list, &ep_priv->td_list);
+ }
+
+ dev_vdbg(imx21->dev, "setup %d packets for iso frame %d->%d\n",
+ urb->number_of_packets, urb->start_frame, td->frame);
+
+ debug_urb_submitted(imx21, urb);
+ schedule_isoc_etds(hcd, ep);
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ return 0;
+
+alloc_dmem_failed:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+link_failed:
+alloc_etd_failed:
+alloc_ep_failed:
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ kfree(urb_priv->isoc_td);
+
+alloc_td_failed:
+ kfree(urb_priv);
+ return ret;
+}
+
+static void dequeue_isoc_urb(struct imx21 *imx21,
+ struct urb *urb, struct ep_priv *ep_priv)
+{
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct td *td, *tmp;
+ int i;
+
+ if (urb_priv->active) {
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ int etd_num = ep_priv->etd[i];
+ if (etd_num != -1 && imx21->etd[etd_num].urb == urb) {
+ struct etd_priv *etd = imx21->etd + etd_num;
+
+ reset_etd(imx21, etd_num);
+ free_dmem(imx21, etd);
+ }
+ }
+ }
+
+ list_for_each_entry_safe(td, tmp, &ep_priv->td_list, list) {
+ if (td->urb == urb) {
+ dev_vdbg(imx21->dev, "removing td %p\n", td);
+ list_del(&td->list);
+ }
+ }
+}
+
+/* =========================================== */
+/* NON ISOC Handling ... */
+/* =========================================== */
+
+static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
+{
+ unsigned int pipe = urb->pipe;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct ep_priv *ep_priv = urb_priv->ep->hcpriv;
+ int state = urb_priv->state;
+ int etd_num = ep_priv->etd[0];
+ struct etd_priv *etd;
+ u32 count;
+ u16 etd_buf_size;
+ u16 maxpacket;
+ u8 dir;
+ u8 bufround;
+ u8 datatoggle;
+ u8 interval = 0;
+ u8 relpolpos = 0;
+
+ if (etd_num < 0) {
+ dev_err(imx21->dev, "No valid ETD\n");
+ return;
+ }
+ if (readl(imx21->regs + USBH_ETDENSET) & (1 << etd_num))
+ dev_err(imx21->dev, "submitting to active ETD %d\n", etd_num);
+
+ etd = &imx21->etd[etd_num];
+ maxpacket = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+ if (!maxpacket)
+ maxpacket = 8;
+
+ if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) {
+ if (state == US_CTRL_SETUP) {
+ dir = TD_DIR_SETUP;
+ if (unsuitable_for_dma(urb->setup_dma))
+ usb_hcd_unmap_urb_setup_for_dma(imx21->hcd,
+ urb);
+ etd->dma_handle = urb->setup_dma;
+ etd->cpu_buffer = urb->setup_packet;
+ bufround = 0;
+ count = 8;
+ datatoggle = TD_TOGGLE_DATA0;
+ } else { /* US_CTRL_ACK */
+ dir = usb_pipeout(pipe) ? TD_DIR_IN : TD_DIR_OUT;
+ bufround = 0;
+ count = 0;
+ datatoggle = TD_TOGGLE_DATA1;
+ }
+ } else {
+ dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN;
+ bufround = (dir == TD_DIR_IN) ? 1 : 0;
+ if (unsuitable_for_dma(urb->transfer_dma))
+ usb_hcd_unmap_urb_for_dma(imx21->hcd, urb);
+
+ etd->dma_handle = urb->transfer_dma;
+ etd->cpu_buffer = urb->transfer_buffer;
+ if (usb_pipebulk(pipe) && (state == US_BULK0))
+ count = 0;
+ else
+ count = urb->transfer_buffer_length;
+
+ if (usb_pipecontrol(pipe)) {
+ datatoggle = TD_TOGGLE_DATA1;
+ } else {
+ if (usb_gettoggle(
+ urb->dev,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)))
+ datatoggle = TD_TOGGLE_DATA1;
+ else
+ datatoggle = TD_TOGGLE_DATA0;
+ }
+ }
+
+ etd->urb = urb;
+ etd->ep = urb_priv->ep;
+ etd->len = count;
+
+ if (usb_pipeint(pipe)) {
+ interval = urb->interval;
+ relpolpos = (readl(imx21->regs + USBH_FRMNUB) + 1) & 0xff;
+ }
+
+ /* Write ETD to device memory */
+ setup_etd_dword0(imx21, etd_num, urb, dir, maxpacket);
+
+ etd_writel(imx21, etd_num, 2,
+ (u32) interval << DW2_POLINTERV |
+ ((u32) relpolpos << DW2_RELPOLPOS) |
+ ((u32) dir << DW2_DIRPID) |
+ ((u32) bufround << DW2_BUFROUND) |
+ ((u32) datatoggle << DW2_DATATOG) |
+ ((u32) TD_NOTACCESSED << DW2_COMPCODE));
+
+ /* DMA will always transfer buffer size even if TOBYCNT in DWORD3
+ is smaller. Make sure we don't overrun the buffer!
+ */
+ if (count && count < maxpacket)
+ etd_buf_size = count;
+ else
+ etd_buf_size = maxpacket;
+
+ etd_writel(imx21, etd_num, 3,
+ ((u32) (etd_buf_size - 1) << DW3_BUFSIZE) | (u32) count);
+
+ if (!count)
+ etd->dma_handle = 0;
+
+ /* allocate x and y buffer space at once */
+ etd->dmem_size = (count > maxpacket) ? maxpacket * 2 : maxpacket;
+ etd->dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep);
+ if (etd->dmem_offset < 0) {
+ /* Setup everything we can in HW and update when we get DMEM */
+ etd_writel(imx21, etd_num, 1, (u32)maxpacket << 16);
+
+ dev_dbg(imx21->dev, "Queuing etd %d for DMEM\n", etd_num);
+ debug_urb_queued_for_dmem(imx21, urb);
+ list_add_tail(&etd->queue, &imx21->queue_for_dmem);
+ return;
+ }
+
+ etd_writel(imx21, etd_num, 1,
+ (((u32) etd->dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) |
+ (u32) etd->dmem_offset);
+
+ urb_priv->active = 1;
+
+ /* enable the ETD to kick off transfer */
+ dev_vdbg(imx21->dev, "Activating etd %d for %d bytes %s\n",
+ etd_num, count, dir != TD_DIR_IN ? "out" : "in");
+ activate_etd(imx21, etd_num, dir);
+
+}
+
+static void nonisoc_etd_done(struct usb_hcd *hcd, int etd_num)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct etd_priv *etd = &imx21->etd[etd_num];
+ struct urb *urb = etd->urb;
+ u32 etd_mask = 1 << etd_num;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ int dir;
+ int cc;
+ u32 bytes_xfrd;
+ int etd_done;
+
+ disactivate_etd(imx21, etd_num);
+
+ dir = (etd_readl(imx21, etd_num, 0) >> DW0_DIRECT) & 0x3;
+ cc = (etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE) & 0xf;
+ bytes_xfrd = etd->len - (etd_readl(imx21, etd_num, 3) & 0x1fffff);
+
+ /* save toggle carry */
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe),
+ (etd_readl(imx21, etd_num, 0) >> DW0_TOGCRY) & 0x1);
+
+ if (dir == TD_DIR_IN) {
+ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
+ clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
+
+ if (etd->bounce_buffer) {
+ memcpy(etd->cpu_buffer, etd->bounce_buffer, bytes_xfrd);
+ dma_unmap_single(imx21->dev,
+ etd->dma_handle, etd->len, DMA_FROM_DEVICE);
+ } else if (!etd->dma_handle && bytes_xfrd) {/* PIO */
+ memcpy_fromio(etd->cpu_buffer,
+ imx21->regs + USBOTG_DMEM + etd->dmem_offset,
+ bytes_xfrd);
+ }
+ }
+
+ kfree(etd->bounce_buffer);
+ etd->bounce_buffer = NULL;
+ free_dmem(imx21, etd);
+
+ urb->error_count = 0;
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK)
+ && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
+ if (cc != 0)
+ dev_vdbg(imx21->dev, "cc is 0x%x\n", cc);
+
+ etd_done = (cc_to_error[cc] != 0); /* stop if error */
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ switch (urb_priv->state) {
+ case US_CTRL_SETUP:
+ if (urb->transfer_buffer_length > 0)
+ urb_priv->state = US_CTRL_DATA;
+ else
+ urb_priv->state = US_CTRL_ACK;
+ break;
+ case US_CTRL_DATA:
+ urb->actual_length += bytes_xfrd;
+ urb_priv->state = US_CTRL_ACK;
+ break;
+ case US_CTRL_ACK:
+ etd_done = 1;
+ break;
+ default:
+ dev_err(imx21->dev,
+ "Invalid pipe state %d\n", urb_priv->state);
+ etd_done = 1;
+ break;
+ }
+ break;
+
+ case PIPE_BULK:
+ urb->actual_length += bytes_xfrd;
+ if ((urb_priv->state == US_BULK)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && urb->transfer_buffer_length > 0
+ && ((urb->transfer_buffer_length %
+ usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe))) == 0)) {
+ /* need a 0-packet */
+ urb_priv->state = US_BULK0;
+ } else {
+ etd_done = 1;
+ }
+ break;
+
+ case PIPE_INTERRUPT:
+ urb->actual_length += bytes_xfrd;
+ etd_done = 1;
+ break;
+ }
+
+ if (etd_done)
+ nonisoc_urb_completed_for_etd(imx21, etd, cc_to_error[cc]);
+ else {
+ dev_vdbg(imx21->dev, "next state=%d\n", urb_priv->state);
+ schedule_nonisoc_etd(imx21, urb);
+ }
+}
+
+
+static struct ep_priv *alloc_ep(void)
+{
+ int i;
+ struct ep_priv *ep_priv;
+
+ ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
+ if (!ep_priv)
+ return NULL;
+
+ for (i = 0; i < NUM_ISO_ETDS; ++i)
+ ep_priv->etd[i] = -1;
+
+ return ep_priv;
+}
+
+static int imx21_hc_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct urb_priv *urb_priv;
+ struct ep_priv *ep_priv;
+ struct etd_priv *etd;
+ int ret;
+ unsigned long flags;
+
+ dev_vdbg(imx21->dev,
+ "enqueue urb=%p ep=%p len=%d "
+ "buffer=%p dma=%pad setupBuf=%p setupDma=%pad\n",
+ urb, ep,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer, &urb->transfer_dma,
+ urb->setup_packet, &urb->setup_dma);
+
+ if (usb_pipeisoc(urb->pipe))
+ return imx21_hc_urb_enqueue_isoc(hcd, ep, urb, mem_flags);
+
+ urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ ep_priv = ep->hcpriv;
+ if (ep_priv == NULL) {
+ ep_priv = alloc_ep();
+ if (!ep_priv) {
+ ret = -ENOMEM;
+ goto failed_alloc_ep;
+ }
+ ep->hcpriv = ep_priv;
+ ep_priv->ep = ep;
+ }
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto failed_link;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->error_count = 0;
+ urb->hcpriv = urb_priv;
+ urb_priv->ep = ep;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ urb_priv->state = US_CTRL_SETUP;
+ break;
+ case PIPE_BULK:
+ urb_priv->state = US_BULK;
+ break;
+ }
+
+ debug_urb_submitted(imx21, urb);
+ if (ep_priv->etd[0] < 0) {
+ if (ep_priv->waiting_etd) {
+ dev_dbg(imx21->dev,
+ "no ETD available already queued %p\n",
+ ep_priv);
+ debug_urb_queued_for_etd(imx21, urb);
+ goto out;
+ }
+ ep_priv->etd[0] = alloc_etd(imx21);
+ if (ep_priv->etd[0] < 0) {
+ dev_dbg(imx21->dev,
+ "no ETD available queueing %p\n", ep_priv);
+ debug_urb_queued_for_etd(imx21, urb);
+ list_add_tail(&ep_priv->queue, &imx21->queue_for_etd);
+ ep_priv->waiting_etd = 1;
+ goto out;
+ }
+ }
+
+ /* Schedule if no URB already active for this endpoint */
+ etd = &imx21->etd[ep_priv->etd[0]];
+ if (etd->urb == NULL) {
+ DEBUG_LOG_FRAME(imx21, etd, last_req);
+ schedule_nonisoc_etd(imx21, urb);
+ }
+
+out:
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ return 0;
+
+failed_link:
+failed_alloc_ep:
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ kfree(urb_priv);
+ return ret;
+}
+
+static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ unsigned long flags;
+ struct usb_host_endpoint *ep;
+ struct ep_priv *ep_priv;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ int ret = -EINVAL;
+
+ dev_vdbg(imx21->dev, "dequeue urb=%p iso=%d status=%d\n",
+ urb, usb_pipeisoc(urb->pipe), status);
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (ret)
+ goto fail;
+ ep = urb_priv->ep;
+ ep_priv = ep->hcpriv;
+
+ debug_urb_unlinked(imx21, urb);
+
+ if (usb_pipeisoc(urb->pipe)) {
+ dequeue_isoc_urb(imx21, urb, ep_priv);
+ schedule_isoc_etds(hcd, ep);
+ } else if (urb_priv->active) {
+ int etd_num = ep_priv->etd[0];
+ if (etd_num != -1) {
+ struct etd_priv *etd = &imx21->etd[etd_num];
+
+ disactivate_etd(imx21, etd_num);
+ free_dmem(imx21, etd);
+ etd->urb = NULL;
+ kfree(etd->bounce_buffer);
+ etd->bounce_buffer = NULL;
+ }
+ }
+
+ urb_done(hcd, urb, status);
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ return 0;
+
+fail:
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ return ret;
+}
+
+/* =========================================== */
+/* Interrupt dispatch */
+/* =========================================== */
+
+static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof)
+{
+ int etd_num;
+ int enable_sof_int = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ for (etd_num = 0; etd_num < USB_NUM_ETD; etd_num++) {
+ u32 etd_mask = 1 << etd_num;
+ u32 enabled = readl(imx21->regs + USBH_ETDENSET) & etd_mask;
+ u32 done = readl(imx21->regs + USBH_ETDDONESTAT) & etd_mask;
+ struct etd_priv *etd = &imx21->etd[etd_num];
+
+
+ if (done) {
+ DEBUG_LOG_FRAME(imx21, etd, last_int);
+ } else {
+/*
+ * Kludge warning!
+ *
+ * When multiple transfers are using the bus we sometimes get into a state
+ * where the transfer has completed (the CC field of the ETD is != 0x0F),
+ * the ETD has self disabled but the ETDDONESTAT flag is not set
+ * (and hence no interrupt occurs).
+ * This causes the transfer in question to hang.
+ * The kludge below checks for this condition at each SOF and processes any
+ * blocked ETDs (after an arbitrary 10 frame wait)
+ *
+ * With a single active transfer the usbtest test suite will run for days
+ * without the kludge.
+ * With other bus activity (eg mass storage) even just test1 will hang without
+ * the kludge.
+ */
+ u32 dword0;
+ int cc;
+
+ if (etd->active_count && !enabled) /* suspicious... */
+ enable_sof_int = 1;
+
+ if (!sof || enabled || !etd->active_count)
+ continue;
+
+ cc = etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE;
+ if (cc == TD_NOTACCESSED)
+ continue;
+
+ if (++etd->active_count < 10)
+ continue;
+
+ dword0 = etd_readl(imx21, etd_num, 0);
+ dev_dbg(imx21->dev,
+ "unblock ETD %d dev=0x%X ep=0x%X cc=0x%02X!\n",
+ etd_num, dword0 & 0x7F,
+ (dword0 >> DW0_ENDPNT) & 0x0F,
+ cc);
+
+#ifdef DEBUG
+ dev_dbg(imx21->dev,
+ "frame: act=%d disact=%d"
+ " int=%d req=%d cur=%d\n",
+ etd->activated_frame,
+ etd->disactivated_frame,
+ etd->last_int_frame,
+ etd->last_req_frame,
+ readl(imx21->regs + USBH_FRMNUB));
+ imx21->debug_unblocks++;
+#endif
+ etd->active_count = 0;
+/* End of kludge */
+ }
+
+ if (etd->ep == NULL || etd->urb == NULL) {
+ dev_dbg(imx21->dev,
+ "Interrupt for unexpected etd %d"
+ " ep=%p urb=%p\n",
+ etd_num, etd->ep, etd->urb);
+ disactivate_etd(imx21, etd_num);
+ continue;
+ }
+
+ if (usb_pipeisoc(etd->urb->pipe))
+ isoc_etd_done(hcd, etd_num);
+ else
+ nonisoc_etd_done(hcd, etd_num);
+ }
+
+ /* only enable SOF interrupt if it may be needed for the kludge */
+ if (enable_sof_int)
+ set_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT);
+ else
+ clear_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT);
+
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+}
+
+static irqreturn_t imx21_irq(struct usb_hcd *hcd)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ u32 ints = readl(imx21->regs + USBH_SYSISR);
+
+ if (ints & USBH_SYSIEN_HERRINT)
+ dev_dbg(imx21->dev, "Scheduling error\n");
+
+ if (ints & USBH_SYSIEN_SORINT)
+ dev_dbg(imx21->dev, "Scheduling overrun\n");
+
+ if (ints & (USBH_SYSISR_DONEINT | USBH_SYSISR_SOFINT))
+ process_etds(hcd, imx21, ints & USBH_SYSISR_SOFINT);
+
+ writel(ints, imx21->regs + USBH_SYSISR);
+ return IRQ_HANDLED;
+}
+
+static void imx21_hc_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ unsigned long flags;
+ struct ep_priv *ep_priv;
+ int i;
+
+ if (ep == NULL)
+ return;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+ ep_priv = ep->hcpriv;
+ dev_vdbg(imx21->dev, "disable ep=%p, ep->hcpriv=%p\n", ep, ep_priv);
+
+ if (!list_empty(&ep->urb_list))
+ dev_dbg(imx21->dev, "ep's URB list is not empty\n");
+
+ if (ep_priv != NULL) {
+ for (i = 0; i < NUM_ISO_ETDS; i++) {
+ if (ep_priv->etd[i] > -1)
+ dev_dbg(imx21->dev, "free etd %d for disable\n",
+ ep_priv->etd[i]);
+
+ free_etd(imx21, ep_priv->etd[i]);
+ }
+ kfree(ep_priv);
+ ep->hcpriv = NULL;
+ }
+
+ for (i = 0; i < USB_NUM_ETD; i++) {
+ if (imx21->etd[i].alloc && imx21->etd[i].ep == ep) {
+ dev_err(imx21->dev,
+ "Active etd %d for disabled ep=%p!\n", i, ep);
+ free_etd(imx21, i);
+ }
+ }
+ free_epdmem(imx21, ep);
+ spin_unlock_irqrestore(&imx21->lock, flags);
+}
+
+/* =========================================== */
+/* Hub handling */
+/* =========================================== */
+
+static int get_hub_descriptor(struct usb_hcd *hcd,
+ struct usb_hub_descriptor *desc)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ desc->bDescriptorType = USB_DT_HUB; /* HUB descriptor */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = readl(imx21->regs + USBH_ROOTHUBA)
+ & USBH_ROOTHUBA_NDNSTMPRT_MASK;
+ desc->bDescLength = 9;
+ desc->bPwrOn2PwrGood = 0;
+ desc->wHubCharacteristics = (__force __u16) cpu_to_le16(
+ HUB_CHAR_NO_LPSM | /* No power switching */
+ HUB_CHAR_NO_OCPM); /* No over current protection */
+
+ desc->u.hs.DeviceRemovable[0] = 1 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+ return 0;
+}
+
+static int imx21_hc_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ int ports;
+ int changed = 0;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+ ports = readl(imx21->regs + USBH_ROOTHUBA)
+ & USBH_ROOTHUBA_NDNSTMPRT_MASK;
+ if (ports > 7) {
+ ports = 7;
+ dev_err(imx21->dev, "ports %d > 7\n", ports);
+ }
+ for (i = 0; i < ports; i++) {
+ if (readl(imx21->regs + USBH_PORTSTAT(i)) &
+ (USBH_PORTSTAT_CONNECTSC |
+ USBH_PORTSTAT_PRTENBLSC |
+ USBH_PORTSTAT_PRTSTATSC |
+ USBH_PORTSTAT_OVRCURIC |
+ USBH_PORTSTAT_PRTRSTSC)) {
+
+ changed = 1;
+ buf[0] |= 1 << (i + 1);
+ }
+ }
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ if (changed)
+ dev_info(imx21->dev, "Hub status changed\n");
+ return changed;
+}
+
+static int imx21_hc_hub_control(struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ int rc = 0;
+ u32 status_write = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ dev_dbg(imx21->dev, "ClearHubFeature\n");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ dev_dbg(imx21->dev, " OVER_CURRENT\n");
+ break;
+ case C_HUB_LOCAL_POWER:
+ dev_dbg(imx21->dev, " LOCAL_POWER\n");
+ break;
+ default:
+ dev_dbg(imx21->dev, " unknown\n");
+ rc = -EINVAL;
+ break;
+ }
+ break;
+
+ case ClearPortFeature:
+ dev_dbg(imx21->dev, "ClearPortFeature\n");
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ dev_dbg(imx21->dev, " ENABLE\n");
+ status_write = USBH_PORTSTAT_CURCONST;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(imx21->dev, " SUSPEND\n");
+ status_write = USBH_PORTSTAT_PRTOVRCURI;
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(imx21->dev, " POWER\n");
+ status_write = USBH_PORTSTAT_LSDEVCON;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ dev_dbg(imx21->dev, " C_ENABLE\n");
+ status_write = USBH_PORTSTAT_PRTENBLSC;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ dev_dbg(imx21->dev, " C_SUSPEND\n");
+ status_write = USBH_PORTSTAT_PRTSTATSC;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ dev_dbg(imx21->dev, " C_CONNECTION\n");
+ status_write = USBH_PORTSTAT_CONNECTSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(imx21->dev, " C_OVER_CURRENT\n");
+ status_write = USBH_PORTSTAT_OVRCURIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ dev_dbg(imx21->dev, " C_RESET\n");
+ status_write = USBH_PORTSTAT_PRTRSTSC;
+ break;
+ default:
+ dev_dbg(imx21->dev, " unknown\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ break;
+
+ case GetHubDescriptor:
+ dev_dbg(imx21->dev, "GetHubDescriptor\n");
+ rc = get_hub_descriptor(hcd, (void *)buf);
+ break;
+
+ case GetHubStatus:
+ dev_dbg(imx21->dev, " GetHubStatus\n");
+ *(__le32 *) buf = 0;
+ break;
+
+ case GetPortStatus:
+ dev_dbg(imx21->dev, "GetPortStatus: port: %d, 0x%x\n",
+ wIndex, USBH_PORTSTAT(wIndex - 1));
+ *(__le32 *) buf = readl(imx21->regs +
+ USBH_PORTSTAT(wIndex - 1));
+ break;
+
+ case SetHubFeature:
+ dev_dbg(imx21->dev, "SetHubFeature\n");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ dev_dbg(imx21->dev, " OVER_CURRENT\n");
+ break;
+
+ case C_HUB_LOCAL_POWER:
+ dev_dbg(imx21->dev, " LOCAL_POWER\n");
+ break;
+ default:
+ dev_dbg(imx21->dev, " unknown\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ break;
+
+ case SetPortFeature:
+ dev_dbg(imx21->dev, "SetPortFeature\n");
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(imx21->dev, " SUSPEND\n");
+ status_write = USBH_PORTSTAT_PRTSUSPST;
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(imx21->dev, " POWER\n");
+ status_write = USBH_PORTSTAT_PRTPWRST;
+ break;
+ case USB_PORT_FEAT_RESET:
+ dev_dbg(imx21->dev, " RESET\n");
+ status_write = USBH_PORTSTAT_PRTRSTST;
+ break;
+ default:
+ dev_dbg(imx21->dev, " unknown\n");
+ rc = -EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ dev_dbg(imx21->dev, " unknown\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ if (status_write)
+ writel(status_write, imx21->regs + USBH_PORTSTAT(wIndex - 1));
+ return rc;
+}
+
+/* =========================================== */
+/* Host controller management */
+/* =========================================== */
+
+static int imx21_hc_reset(struct usb_hcd *hcd)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ unsigned long timeout;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ /* Reset the Host controller modules */
+ writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH |
+ USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC,
+ imx21->regs + USBOTG_RST_CTRL);
+
+ /* Wait for reset to finish */
+ timeout = jiffies + HZ;
+ while (readl(imx21->regs + USBOTG_RST_CTRL) != 0) {
+ if (time_after(jiffies, timeout)) {
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ dev_err(imx21->dev, "timeout waiting for reset\n");
+ return -ETIMEDOUT;
+ }
+ spin_unlock_irq(&imx21->lock);
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irq(&imx21->lock);
+ }
+ spin_unlock_irqrestore(&imx21->lock, flags);
+ return 0;
+}
+
+static int imx21_hc_start(struct usb_hcd *hcd)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ unsigned long flags;
+ int i, j;
+ u32 hw_mode = USBOTG_HWMODE_CRECFG_HOST;
+ u32 usb_control = 0;
+
+ hw_mode |= ((imx21->pdata->host_xcvr << USBOTG_HWMODE_HOSTXCVR_SHIFT) &
+ USBOTG_HWMODE_HOSTXCVR_MASK);
+ hw_mode |= ((imx21->pdata->otg_xcvr << USBOTG_HWMODE_OTGXCVR_SHIFT) &
+ USBOTG_HWMODE_OTGXCVR_MASK);
+
+ if (imx21->pdata->host1_txenoe)
+ usb_control |= USBCTRL_HOST1_TXEN_OE;
+
+ if (!imx21->pdata->host1_xcverless)
+ usb_control |= USBCTRL_HOST1_BYP_TLL;
+
+ if (imx21->pdata->otg_ext_xcvr)
+ usb_control |= USBCTRL_OTC_RCV_RXDP;
+
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ writel((USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN),
+ imx21->regs + USBOTG_CLK_CTRL);
+ writel(hw_mode, imx21->regs + USBOTG_HWMODE);
+ writel(usb_control, imx21->regs + USBCTRL);
+ writel(USB_MISCCONTROL_SKPRTRY | USB_MISCCONTROL_ARBMODE,
+ imx21->regs + USB_MISCCONTROL);
+
+ /* Clear the ETDs */
+ for (i = 0; i < USB_NUM_ETD; i++)
+ for (j = 0; j < 4; j++)
+ etd_writel(imx21, i, j, 0);
+
+ /* Take the HC out of reset */
+ writel(USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL | USBH_HOST_CTRL_CTLBLKSR_1,
+ imx21->regs + USBH_HOST_CTRL);
+
+ /* Enable ports */
+ if (imx21->pdata->enable_otg_host)
+ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
+ imx21->regs + USBH_PORTSTAT(0));
+
+ if (imx21->pdata->enable_host1)
+ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
+ imx21->regs + USBH_PORTSTAT(1));
+
+ if (imx21->pdata->enable_host2)
+ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
+ imx21->regs + USBH_PORTSTAT(2));
+
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Enable host controller interrupts */
+ set_register_bits(imx21, USBH_SYSIEN,
+ USBH_SYSIEN_HERRINT |
+ USBH_SYSIEN_DONEINT | USBH_SYSIEN_SORINT);
+ set_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT);
+
+ spin_unlock_irqrestore(&imx21->lock, flags);
+
+ return 0;
+}
+
+static void imx21_hc_stop(struct usb_hcd *hcd)
+{
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ unsigned long flags;
+
+ spin_lock_irqsave(&imx21->lock, flags);
+
+ writel(0, imx21->regs + USBH_SYSIEN);
+ clear_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT);
+ clear_register_bits(imx21, USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN,
+ USBOTG_CLK_CTRL);
+ spin_unlock_irqrestore(&imx21->lock, flags);
+}
+
+/* =========================================== */
+/* Driver glue */
+/* =========================================== */
+
+static struct hc_driver imx21_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "IMX21 USB Host Controller",
+ .hcd_priv_size = sizeof(struct imx21),
+
+ .flags = HCD_USB11,
+ .irq = imx21_irq,
+
+ .reset = imx21_hc_reset,
+ .start = imx21_hc_start,
+ .stop = imx21_hc_stop,
+
+ /* I/O requests */
+ .urb_enqueue = imx21_hc_urb_enqueue,
+ .urb_dequeue = imx21_hc_urb_dequeue,
+ .endpoint_disable = imx21_hc_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = imx21_hc_get_frame,
+
+ /* Root hub support */
+ .hub_status_data = imx21_hc_hub_status_data,
+ .hub_control = imx21_hc_hub_control,
+
+};
+
+static struct mx21_usbh_platform_data default_pdata = {
+ .host_xcvr = MX21_USBXCVR_TXDIF_RXDIF,
+ .otg_xcvr = MX21_USBXCVR_TXDIF_RXDIF,
+ .enable_host1 = 1,
+ .enable_host2 = 1,
+ .enable_otg_host = 1,
+
+};
+
+static int imx21_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct imx21 *imx21 = hcd_to_imx21(hcd);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ remove_debug_files(imx21);
+ usb_remove_hcd(hcd);
+
+ if (res != NULL) {
+ clk_disable_unprepare(imx21->clk);
+ clk_put(imx21->clk);
+ iounmap(imx21->regs);
+ release_mem_region(res->start, resource_size(res));
+ }
+
+ kfree(hcd);
+ return 0;
+}
+
+
+static int imx21_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct imx21 *imx21;
+ struct resource *res;
+ int ret;
+ int irq;
+
+ printk(KERN_INFO "%s\n", imx21_hc_driver.product_desc);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ hcd = usb_create_hcd(&imx21_hc_driver,
+ &pdev->dev, dev_name(&pdev->dev));
+ if (hcd == NULL) {
+ dev_err(&pdev->dev, "Cannot create hcd (%s)\n",
+ dev_name(&pdev->dev));
+ return -ENOMEM;
+ }
+
+ imx21 = hcd_to_imx21(hcd);
+ imx21->hcd = hcd;
+ imx21->dev = &pdev->dev;
+ imx21->pdata = dev_get_platdata(&pdev->dev);
+ if (!imx21->pdata)
+ imx21->pdata = &default_pdata;
+
+ spin_lock_init(&imx21->lock);
+ INIT_LIST_HEAD(&imx21->dmem_list);
+ INIT_LIST_HEAD(&imx21->queue_for_etd);
+ INIT_LIST_HEAD(&imx21->queue_for_dmem);
+ create_debug_files(imx21);
+
+ res = request_mem_region(res->start, resource_size(res), hcd_name);
+ if (!res) {
+ ret = -EBUSY;
+ goto failed_request_mem;
+ }
+
+ imx21->regs = ioremap(res->start, resource_size(res));
+ if (imx21->regs == NULL) {
+ dev_err(imx21->dev, "Cannot map registers\n");
+ ret = -ENOMEM;
+ goto failed_ioremap;
+ }
+
+ /* Enable clocks source */
+ imx21->clk = clk_get(imx21->dev, NULL);
+ if (IS_ERR(imx21->clk)) {
+ dev_err(imx21->dev, "no clock found\n");
+ ret = PTR_ERR(imx21->clk);
+ goto failed_clock_get;
+ }
+
+ ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000));
+ if (ret)
+ goto failed_clock_set;
+ ret = clk_prepare_enable(imx21->clk);
+ if (ret)
+ goto failed_clock_enable;
+
+ dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n",
+ (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF);
+
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret != 0) {
+ dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret);
+ goto failed_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return 0;
+
+failed_add_hcd:
+ clk_disable_unprepare(imx21->clk);
+failed_clock_enable:
+failed_clock_set:
+ clk_put(imx21->clk);
+failed_clock_get:
+ iounmap(imx21->regs);
+failed_ioremap:
+ release_mem_region(res->start, resource_size(res));
+failed_request_mem:
+ remove_debug_files(imx21);
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static struct platform_driver imx21_hcd_driver = {
+ .driver = {
+ .name = hcd_name,
+ },
+ .probe = imx21_probe,
+ .remove = imx21_remove,
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+module_platform_driver(imx21_hcd_driver);
+
+MODULE_DESCRIPTION("i.MX21 USB Host controller");
+MODULE_AUTHOR("Martin Fuzzey");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx21-hcd");
diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h
new file mode 100644
index 000000000..05122f8a6
--- /dev/null
+++ b/drivers/usb/host/imx21-hcd.h
@@ -0,0 +1,444 @@
+/*
+ * Macros and prototypes for i.MX21
+ *
+ * Copyright (C) 2006 Loping Dog Embedded Systems
+ * Copyright (C) 2009 Martin Fuzzey
+ * Originally written by Jay Monkman <jtm@lopingdog.com>
+ * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_IMX21_HCD_H__
+#define __LINUX_IMX21_HCD_H__
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/platform_data/usb-mx2.h>
+
+#define NUM_ISO_ETDS 2
+#define USB_NUM_ETD 32
+#define DMEM_SIZE 4096
+
+/* Register definitions */
+#define USBOTG_HWMODE 0x00
+#define USBOTG_HWMODE_ANASDBEN (1 << 14)
+#define USBOTG_HWMODE_OTGXCVR_SHIFT 6
+#define USBOTG_HWMODE_OTGXCVR_MASK (3 << 6)
+#define USBOTG_HWMODE_OTGXCVR_TD_RD (0 << 6)
+#define USBOTG_HWMODE_OTGXCVR_TS_RD (2 << 6)
+#define USBOTG_HWMODE_OTGXCVR_TD_RS (1 << 6)
+#define USBOTG_HWMODE_OTGXCVR_TS_RS (3 << 6)
+#define USBOTG_HWMODE_HOSTXCVR_SHIFT 4
+#define USBOTG_HWMODE_HOSTXCVR_MASK (3 << 4)
+#define USBOTG_HWMODE_HOSTXCVR_TD_RD (0 << 4)
+#define USBOTG_HWMODE_HOSTXCVR_TS_RD (2 << 4)
+#define USBOTG_HWMODE_HOSTXCVR_TD_RS (1 << 4)
+#define USBOTG_HWMODE_HOSTXCVR_TS_RS (3 << 4)
+#define USBOTG_HWMODE_CRECFG_MASK (3 << 0)
+#define USBOTG_HWMODE_CRECFG_HOST (1 << 0)
+#define USBOTG_HWMODE_CRECFG_FUNC (2 << 0)
+#define USBOTG_HWMODE_CRECFG_HNP (3 << 0)
+
+#define USBOTG_CINT_STAT 0x04
+#define USBOTG_CINT_STEN 0x08
+#define USBOTG_ASHNPINT (1 << 5)
+#define USBOTG_ASFCINT (1 << 4)
+#define USBOTG_ASHCINT (1 << 3)
+#define USBOTG_SHNPINT (1 << 2)
+#define USBOTG_FCINT (1 << 1)
+#define USBOTG_HCINT (1 << 0)
+
+#define USBOTG_CLK_CTRL 0x0c
+#define USBOTG_CLK_CTRL_FUNC (1 << 2)
+#define USBOTG_CLK_CTRL_HST (1 << 1)
+#define USBOTG_CLK_CTRL_MAIN (1 << 0)
+
+#define USBOTG_RST_CTRL 0x10
+#define USBOTG_RST_RSTI2C (1 << 15)
+#define USBOTG_RST_RSTCTRL (1 << 5)
+#define USBOTG_RST_RSTFC (1 << 4)
+#define USBOTG_RST_RSTFSKE (1 << 3)
+#define USBOTG_RST_RSTRH (1 << 2)
+#define USBOTG_RST_RSTHSIE (1 << 1)
+#define USBOTG_RST_RSTHC (1 << 0)
+
+#define USBOTG_FRM_INTVL 0x14
+#define USBOTG_FRM_REMAIN 0x18
+#define USBOTG_HNP_CSR 0x1c
+#define USBOTG_HNP_ISR 0x2c
+#define USBOTG_HNP_IEN 0x30
+
+#define USBOTG_I2C_TXCVR_REG(x) (0x100 + (x))
+#define USBOTG_I2C_XCVR_DEVAD 0x118
+#define USBOTG_I2C_SEQ_OP_REG 0x119
+#define USBOTG_I2C_SEQ_RD_STARTAD 0x11a
+#define USBOTG_I2C_OP_CTRL_REG 0x11b
+#define USBOTG_I2C_SCLK_TO_SCK_HPER 0x11e
+#define USBOTG_I2C_MASTER_INT_REG 0x11f
+
+#define USBH_HOST_CTRL 0x80
+#define USBH_HOST_CTRL_HCRESET (1 << 31)
+#define USBH_HOST_CTRL_SCHDOVR(x) ((x) << 16)
+#define USBH_HOST_CTRL_RMTWUEN (1 << 4)
+#define USBH_HOST_CTRL_HCUSBSTE_RESET (0 << 2)
+#define USBH_HOST_CTRL_HCUSBSTE_RESUME (1 << 2)
+#define USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL (2 << 2)
+#define USBH_HOST_CTRL_HCUSBSTE_SUSPEND (3 << 2)
+#define USBH_HOST_CTRL_CTLBLKSR_1 (0 << 0)
+#define USBH_HOST_CTRL_CTLBLKSR_2 (1 << 0)
+#define USBH_HOST_CTRL_CTLBLKSR_3 (2 << 0)
+#define USBH_HOST_CTRL_CTLBLKSR_4 (3 << 0)
+
+#define USBH_SYSISR 0x88
+#define USBH_SYSISR_PSCINT (1 << 6)
+#define USBH_SYSISR_FMOFINT (1 << 5)
+#define USBH_SYSISR_HERRINT (1 << 4)
+#define USBH_SYSISR_RESDETINT (1 << 3)
+#define USBH_SYSISR_SOFINT (1 << 2)
+#define USBH_SYSISR_DONEINT (1 << 1)
+#define USBH_SYSISR_SORINT (1 << 0)
+
+#define USBH_SYSIEN 0x8c
+#define USBH_SYSIEN_PSCINT (1 << 6)
+#define USBH_SYSIEN_FMOFINT (1 << 5)
+#define USBH_SYSIEN_HERRINT (1 << 4)
+#define USBH_SYSIEN_RESDETINT (1 << 3)
+#define USBH_SYSIEN_SOFINT (1 << 2)
+#define USBH_SYSIEN_DONEINT (1 << 1)
+#define USBH_SYSIEN_SORINT (1 << 0)
+
+#define USBH_XBUFSTAT 0x98
+#define USBH_YBUFSTAT 0x9c
+#define USBH_XYINTEN 0xa0
+#define USBH_XFILLSTAT 0xa8
+#define USBH_YFILLSTAT 0xac
+#define USBH_ETDENSET 0xc0
+#define USBH_ETDENCLR 0xc4
+#define USBH_IMMEDINT 0xcc
+#define USBH_ETDDONESTAT 0xd0
+#define USBH_ETDDONEEN 0xd4
+#define USBH_FRMNUB 0xe0
+#define USBH_LSTHRESH 0xe4
+
+#define USBH_ROOTHUBA 0xe8
+#define USBH_ROOTHUBA_PWRTOGOOD_MASK (0xff)
+#define USBH_ROOTHUBA_PWRTOGOOD_SHIFT (24)
+#define USBH_ROOTHUBA_NOOVRCURP (1 << 12)
+#define USBH_ROOTHUBA_OVRCURPM (1 << 11)
+#define USBH_ROOTHUBA_DEVTYPE (1 << 10)
+#define USBH_ROOTHUBA_PWRSWTMD (1 << 9)
+#define USBH_ROOTHUBA_NOPWRSWT (1 << 8)
+#define USBH_ROOTHUBA_NDNSTMPRT_MASK (0xff)
+
+#define USBH_ROOTHUBB 0xec
+#define USBH_ROOTHUBB_PRTPWRCM(x) (1 << ((x) + 16))
+#define USBH_ROOTHUBB_DEVREMOVE(x) (1 << (x))
+
+#define USBH_ROOTSTAT 0xf0
+#define USBH_ROOTSTAT_CLRRMTWUE (1 << 31)
+#define USBH_ROOTSTAT_OVRCURCHG (1 << 17)
+#define USBH_ROOTSTAT_DEVCONWUE (1 << 15)
+#define USBH_ROOTSTAT_OVRCURI (1 << 1)
+#define USBH_ROOTSTAT_LOCPWRS (1 << 0)
+
+#define USBH_PORTSTAT(x) (0xf4 + ((x) * 4))
+#define USBH_PORTSTAT_PRTRSTSC (1 << 20)
+#define USBH_PORTSTAT_OVRCURIC (1 << 19)
+#define USBH_PORTSTAT_PRTSTATSC (1 << 18)
+#define USBH_PORTSTAT_PRTENBLSC (1 << 17)
+#define USBH_PORTSTAT_CONNECTSC (1 << 16)
+#define USBH_PORTSTAT_LSDEVCON (1 << 9)
+#define USBH_PORTSTAT_PRTPWRST (1 << 8)
+#define USBH_PORTSTAT_PRTRSTST (1 << 4)
+#define USBH_PORTSTAT_PRTOVRCURI (1 << 3)
+#define USBH_PORTSTAT_PRTSUSPST (1 << 2)
+#define USBH_PORTSTAT_PRTENABST (1 << 1)
+#define USBH_PORTSTAT_CURCONST (1 << 0)
+
+#define USB_DMAREV 0x800
+#define USB_DMAINTSTAT 0x804
+#define USB_DMAINTSTAT_EPERR (1 << 1)
+#define USB_DMAINTSTAT_ETDERR (1 << 0)
+
+#define USB_DMAINTEN 0x808
+#define USB_DMAINTEN_EPERRINTEN (1 << 1)
+#define USB_DMAINTEN_ETDERRINTEN (1 << 0)
+
+#define USB_ETDDMAERSTAT 0x80c
+#define USB_EPDMAERSTAT 0x810
+#define USB_ETDDMAEN 0x820
+#define USB_EPDMAEN 0x824
+#define USB_ETDDMAXTEN 0x828
+#define USB_EPDMAXTEN 0x82c
+#define USB_ETDDMAENXYT 0x830
+#define USB_EPDMAENXYT 0x834
+#define USB_ETDDMABST4EN 0x838
+#define USB_EPDMABST4EN 0x83c
+
+#define USB_MISCCONTROL 0x840
+#define USB_MISCCONTROL_ISOPREVFRM (1 << 3)
+#define USB_MISCCONTROL_SKPRTRY (1 << 2)
+#define USB_MISCCONTROL_ARBMODE (1 << 1)
+#define USB_MISCCONTROL_FILTCC (1 << 0)
+
+#define USB_ETDDMACHANLCLR 0x848
+#define USB_EPDMACHANLCLR 0x84c
+#define USB_ETDSMSA(x) (0x900 + ((x) * 4))
+#define USB_EPSMSA(x) (0x980 + ((x) * 4))
+#define USB_ETDDMABUFPTR(x) (0xa00 + ((x) * 4))
+#define USB_EPDMABUFPTR(x) (0xa80 + ((x) * 4))
+
+#define USB_ETD_DWORD(x, w) (0x200 + ((x) * 16) + ((w) * 4))
+#define DW0_ADDRESS 0
+#define DW0_ENDPNT 7
+#define DW0_DIRECT 11
+#define DW0_SPEED 13
+#define DW0_FORMAT 14
+#define DW0_MAXPKTSIZ 16
+#define DW0_HALTED 27
+#define DW0_TOGCRY 28
+#define DW0_SNDNAK 30
+
+#define DW1_XBUFSRTAD 0
+#define DW1_YBUFSRTAD 16
+
+#define DW2_RTRYDELAY 0
+#define DW2_POLINTERV 0
+#define DW2_STARTFRM 0
+#define DW2_RELPOLPOS 8
+#define DW2_DIRPID 16
+#define DW2_BUFROUND 18
+#define DW2_DELAYINT 19
+#define DW2_DATATOG 22
+#define DW2_ERRORCNT 24
+#define DW2_COMPCODE 28
+
+#define DW3_TOTBYECNT 0
+#define DW3_PKTLEN0 0
+#define DW3_COMPCODE0 12
+#define DW3_PKTLEN1 16
+#define DW3_BUFSIZE 21
+#define DW3_COMPCODE1 28
+
+#define USBCTRL 0x600
+#define USBCTRL_I2C_WU_INT_STAT (1 << 27)
+#define USBCTRL_OTG_WU_INT_STAT (1 << 26)
+#define USBCTRL_HOST_WU_INT_STAT (1 << 25)
+#define USBCTRL_FNT_WU_INT_STAT (1 << 24)
+#define USBCTRL_I2C_WU_INT_EN (1 << 19)
+#define USBCTRL_OTG_WU_INT_EN (1 << 18)
+#define USBCTRL_HOST_WU_INT_EN (1 << 17)
+#define USBCTRL_FNT_WU_INT_EN (1 << 16)
+#define USBCTRL_OTC_RCV_RXDP (1 << 13)
+#define USBCTRL_HOST1_BYP_TLL (1 << 12)
+#define USBCTRL_OTG_BYP_VAL(x) ((x) << 10)
+#define USBCTRL_HOST1_BYP_VAL(x) ((x) << 8)
+#define USBCTRL_OTG_PWR_MASK (1 << 6)
+#define USBCTRL_HOST1_PWR_MASK (1 << 5)
+#define USBCTRL_HOST2_PWR_MASK (1 << 4)
+#define USBCTRL_USB_BYP (1 << 2)
+#define USBCTRL_HOST1_TXEN_OE (1 << 1)
+
+#define USBOTG_DMEM 0x1000
+
+/* Values in TD blocks */
+#define TD_DIR_SETUP 0
+#define TD_DIR_OUT 1
+#define TD_DIR_IN 2
+#define TD_FORMAT_CONTROL 0
+#define TD_FORMAT_ISO 1
+#define TD_FORMAT_BULK 2
+#define TD_FORMAT_INT 3
+#define TD_TOGGLE_CARRY 0
+#define TD_TOGGLE_DATA0 2
+#define TD_TOGGLE_DATA1 3
+
+/* control transfer states */
+#define US_CTRL_SETUP 2
+#define US_CTRL_DATA 1
+#define US_CTRL_ACK 0
+
+/* bulk transfer main state and 0-length packet */
+#define US_BULK 1
+#define US_BULK0 0
+
+/*ETD format description*/
+#define IMX_FMT_CTRL 0x0
+#define IMX_FMT_ISO 0x1
+#define IMX_FMT_BULK 0x2
+#define IMX_FMT_INT 0x3
+
+static char fmt_urb_to_etd[4] = {
+/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO,
+/*PIPE_INTERRUPT*/ IMX_FMT_INT,
+/*PIPE_CONTROL*/ IMX_FMT_CTRL,
+/*PIPE_BULK*/ IMX_FMT_BULK
+};
+
+/* condition (error) CC codes and mapping (OHCI like) */
+
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+/*#define TD_UNEXPECTEDPID 0x07 - reserved, not active on MX2*/
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+#define TD_SCHEDULEOVERRUN 0x0E
+#define TD_NOTACCESSED 0x0F
+
+static const int cc_to_error[16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -ENOSPC,
+ /* (for HCD) */ -EALREADY
+};
+
+/* HCD data associated with a usb core URB */
+struct urb_priv {
+ struct urb *urb;
+ struct usb_host_endpoint *ep;
+ int active;
+ int state;
+ struct td *isoc_td;
+ int isoc_remaining;
+ int isoc_status;
+};
+
+/* HCD data associated with a usb core endpoint */
+struct ep_priv {
+ struct usb_host_endpoint *ep;
+ struct list_head td_list;
+ struct list_head queue;
+ int etd[NUM_ISO_ETDS];
+ int waiting_etd;
+};
+
+/* isoc packet */
+struct td {
+ struct list_head list;
+ struct urb *urb;
+ struct usb_host_endpoint *ep;
+ dma_addr_t dma_handle;
+ void *cpu_buffer;
+ int len;
+ int frame;
+ int isoc_index;
+};
+
+/* HCD data associated with a hardware ETD */
+struct etd_priv {
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ struct td *td;
+ struct list_head queue;
+ dma_addr_t dma_handle;
+ void *cpu_buffer;
+ void *bounce_buffer;
+ int alloc;
+ int len;
+ int dmem_size;
+ int dmem_offset;
+ int active_count;
+#ifdef DEBUG
+ int activated_frame;
+ int disactivated_frame;
+ int last_int_frame;
+ int last_req_frame;
+ u32 submitted_dwords[4];
+#endif
+};
+
+/* Hardware data memory info */
+struct imx21_dmem_area {
+ struct usb_host_endpoint *ep;
+ unsigned int offset;
+ unsigned int size;
+ struct list_head list;
+};
+
+#ifdef DEBUG
+struct debug_usage_stats {
+ unsigned int value;
+ unsigned int maximum;
+};
+
+struct debug_stats {
+ unsigned long submitted;
+ unsigned long completed_ok;
+ unsigned long completed_failed;
+ unsigned long unlinked;
+ unsigned long queue_etd;
+ unsigned long queue_dmem;
+};
+
+struct debug_isoc_trace {
+ int schedule_frame;
+ int submit_frame;
+ int request_len;
+ int done_frame;
+ int done_len;
+ int cc;
+ struct td *td;
+};
+#endif
+
+/* HCD data structure */
+struct imx21 {
+ spinlock_t lock;
+ struct device *dev;
+ struct usb_hcd *hcd;
+ struct mx21_usbh_platform_data *pdata;
+ struct list_head dmem_list;
+ struct list_head queue_for_etd; /* eps queued due to etd shortage */
+ struct list_head queue_for_dmem; /* etds queued due to dmem shortage */
+ struct etd_priv etd[USB_NUM_ETD];
+ struct clk *clk;
+ void __iomem *regs;
+#ifdef DEBUG
+ struct dentry *debug_root;
+ struct debug_stats nonisoc_stats;
+ struct debug_stats isoc_stats;
+ struct debug_usage_stats etd_usage;
+ struct debug_usage_stats dmem_usage;
+ struct debug_isoc_trace isoc_trace[20];
+ struct debug_isoc_trace isoc_trace_failed[20];
+ unsigned long debug_unblocks;
+ int isoc_trace_index;
+ int isoc_trace_index_failed;
+#endif
+};
+
+#endif
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
new file mode 100644
index 000000000..13181dcd9
--- /dev/null
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -0,0 +1,1716 @@
+/*
+ * ISP116x HCD (Host Controller Driver) for USB.
+ *
+ * Derived from the SL811 HCD, rewritten for ISP116x.
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ *
+ * Portions:
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ *
+ * Periodic scheduling is based on Roman's OHCI code
+ * Copyright (C) 1999 Roman Weissgaerber
+ *
+ */
+
+/*
+ * The driver basically works. A number of people have used it with a range
+ * of devices.
+ *
+ * The driver passes all usbtests 1-14.
+ *
+ * Suspending/resuming of root hub via sysfs works. Remote wakeup works too.
+ * And suspending/resuming of platform device works too. Suspend/resume
+ * via HCD operations vector is not implemented.
+ *
+ * Iso transfer support is not implemented. Adding this would include
+ * implementing recovery from the failure to service the processed ITL
+ * fifo ram in time, which will involve chip reset.
+ *
+ * TODO:
+ + More testing of suspend/resume.
+*/
+
+/*
+ ISP116x chips require certain delays between accesses to its
+ registers. The following timing options exist.
+
+ 1. Configure your memory controller (the best)
+ 2. Implement platform-specific delay function possibly
+ combined with configuring the memory controller; see
+ include/linux/usb-isp116x.h for more info. Some broken
+ memory controllers line LH7A400 SMC need this. Also,
+ uncomment for that to work the following
+ USE_PLATFORM_DELAY macro.
+ 3. Use ndelay (easiest, poorest). For that, uncomment
+ the following USE_NDELAY macro.
+*/
+#define USE_PLATFORM_DELAY
+//#define USE_NDELAY
+
+//#define DEBUG
+//#define VERBOSE
+/* Transfer descriptors. See dump_ptd() for printout format */
+//#define PTD_TRACE
+/* enqueuing/finishing log of urbs */
+//#define URB_TRACE
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/isp116x.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+#include "isp116x.h"
+
+#define DRIVER_VERSION "03 Nov 2005"
+#define DRIVER_DESC "ISP116x USB Host Controller Driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char hcd_name[] = "isp116x-hcd";
+
+/*-----------------------------------------------------------------*/
+
+/*
+ Write len bytes to fifo, pad till 32-bit boundary
+ */
+static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+ u8 *dp = (u8 *) buf;
+ u16 *dp2 = (u16 *) buf;
+ u16 w;
+ int quot = len % 4;
+
+ /* buffer is already in 'usb data order', which is LE. */
+ /* When reading buffer as u16, we have to take care byte order */
+ /* doesn't get mixed up */
+
+ if ((unsigned long)dp2 & 1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ w = *dp++;
+ w |= *dp++ << 8;
+ isp116x_raw_write_data16(isp116x, w);
+ }
+ if (len)
+ isp116x_write_data16(isp116x, (u16) * dp);
+ } else {
+ /* aligned */
+ for (; len > 1; len -= 2) {
+ /* Keep byte order ! */
+ isp116x_raw_write_data16(isp116x, cpu_to_le16(*dp2++));
+ }
+
+ if (len)
+ isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
+ }
+ if (quot == 1 || quot == 2)
+ isp116x_raw_write_data16(isp116x, 0);
+}
+
+/*
+ Read len bytes from fifo and then read till 32-bit boundary.
+ */
+static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+ u8 *dp = (u8 *) buf;
+ u16 *dp2 = (u16 *) buf;
+ u16 w;
+ int quot = len % 4;
+
+ /* buffer is already in 'usb data order', which is LE. */
+ /* When reading buffer as u16, we have to take care byte order */
+ /* doesn't get mixed up */
+
+ if ((unsigned long)dp2 & 1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ w = isp116x_raw_read_data16(isp116x);
+ *dp++ = w & 0xff;
+ *dp++ = (w >> 8) & 0xff;
+ }
+
+ if (len)
+ *dp = 0xff & isp116x_read_data16(isp116x);
+ } else {
+ /* aligned */
+ for (; len > 1; len -= 2) {
+ /* Keep byte order! */
+ *dp2++ = le16_to_cpu(isp116x_raw_read_data16(isp116x));
+ }
+
+ if (len)
+ *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
+ }
+ if (quot == 1 || quot == 2)
+ isp116x_raw_read_data16(isp116x);
+}
+
+/*
+ Write ptd's and data for scheduled transfers into
+ the fifo ram. Fifo must be empty and ready.
+*/
+static void pack_fifo(struct isp116x *isp116x)
+{
+ struct isp116x_ep *ep;
+ struct ptd *ptd;
+ int buflen = isp116x->atl_last_dir == PTD_DIR_IN
+ ? isp116x->atl_bufshrt : isp116x->atl_buflen;
+
+ isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+ isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+ isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
+ for (ep = isp116x->atl_active; ep; ep = ep->active) {
+ ptd = &ep->ptd;
+ dump_ptd(ptd);
+ dump_ptd_out_data(ptd, ep->data);
+ isp116x_write_data16(isp116x, ptd->count);
+ isp116x_write_data16(isp116x, ptd->mps);
+ isp116x_write_data16(isp116x, ptd->len);
+ isp116x_write_data16(isp116x, ptd->faddr);
+ buflen -= sizeof(struct ptd);
+ /* Skip writing data for last IN PTD */
+ if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) {
+ write_ptddata_to_fifo(isp116x, ep->data, ep->length);
+ buflen -= ALIGN(ep->length, 4);
+ }
+ }
+ BUG_ON(buflen);
+}
+
+/*
+ Read the processed ptd's and data from fifo ram back to
+ URBs' buffers. Fifo must be full and done
+*/
+static void unpack_fifo(struct isp116x *isp116x)
+{
+ struct isp116x_ep *ep;
+ struct ptd *ptd;
+ int buflen = isp116x->atl_last_dir == PTD_DIR_IN
+ ? isp116x->atl_buflen : isp116x->atl_bufshrt;
+
+ isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+ isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+ isp116x_write_addr(isp116x, HCATLPORT);
+ for (ep = isp116x->atl_active; ep; ep = ep->active) {
+ ptd = &ep->ptd;
+ ptd->count = isp116x_read_data16(isp116x);
+ ptd->mps = isp116x_read_data16(isp116x);
+ ptd->len = isp116x_read_data16(isp116x);
+ ptd->faddr = isp116x_read_data16(isp116x);
+ buflen -= sizeof(struct ptd);
+ /* Skip reading data for last Setup or Out PTD */
+ if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) {
+ read_ptddata_from_fifo(isp116x, ep->data, ep->length);
+ buflen -= ALIGN(ep->length, 4);
+ }
+ dump_ptd(ptd);
+ dump_ptd_in_data(ptd, ep->data);
+ }
+ BUG_ON(buflen);
+}
+
+/*---------------------------------------------------------------*/
+
+/*
+ Set up PTD's.
+*/
+static void preproc_atl_queue(struct isp116x *isp116x)
+{
+ struct isp116x_ep *ep;
+ struct urb *urb;
+ struct ptd *ptd;
+ u16 len;
+
+ for (ep = isp116x->atl_active; ep; ep = ep->active) {
+ u16 toggle = 0, dir = PTD_DIR_SETUP;
+
+ BUG_ON(list_empty(&ep->hep->urb_list));
+ urb = container_of(ep->hep->urb_list.next,
+ struct urb, urb_list);
+ ptd = &ep->ptd;
+ len = ep->length;
+ ep->data = (unsigned char *)urb->transfer_buffer
+ + urb->actual_length;
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
+ dir = PTD_DIR_IN;
+ break;
+ case USB_PID_OUT:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
+ dir = PTD_DIR_OUT;
+ break;
+ case USB_PID_SETUP:
+ len = sizeof(struct usb_ctrlrequest);
+ ep->data = urb->setup_packet;
+ break;
+ case USB_PID_ACK:
+ toggle = 1;
+ len = 0;
+ dir = (urb->transfer_buffer_length
+ && usb_pipein(urb->pipe))
+ ? PTD_DIR_OUT : PTD_DIR_IN;
+ break;
+ default:
+ ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__,
+ ep->nextpid);
+ BUG();
+ }
+
+ ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
+ ptd->mps = PTD_MPS(ep->maxpacket)
+ | PTD_SPD(urb->dev->speed == USB_SPEED_LOW)
+ | PTD_EP(ep->epnum);
+ ptd->len = PTD_LEN(len) | PTD_DIR(dir);
+ ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
+ if (!ep->active) {
+ ptd->mps |= PTD_LAST_MSK;
+ isp116x->atl_last_dir = dir;
+ }
+ isp116x->atl_bufshrt = sizeof(struct ptd) + isp116x->atl_buflen;
+ isp116x->atl_buflen = isp116x->atl_bufshrt + ALIGN(len, 4);
+ }
+}
+
+/*
+ Take done or failed requests out of schedule. Give back
+ processed urbs.
+*/
+static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
+ struct urb *urb, int status)
+__releases(isp116x->lock) __acquires(isp116x->lock)
+{
+ unsigned i;
+
+ ep->error_count = 0;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ urb_dbg(urb, "Finish");
+
+ usb_hcd_unlink_urb_from_ep(isp116x_to_hcd(isp116x), urb);
+ spin_unlock(&isp116x->lock);
+ usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, status);
+ spin_lock(&isp116x->lock);
+
+ /* take idle endpoints out of the schedule */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ return;
+ }
+
+ /* periodic deschedule */
+ DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct isp116x_ep *temp;
+ struct isp116x_ep **prev = &isp116x->periodic[i];
+
+ while (*prev && ((temp = *prev) != ep))
+ prev = &temp->next;
+ if (*prev)
+ *prev = ep->next;
+ isp116x->load[i] -= ep->load;
+ }
+ ep->branch = PERIODIC_SIZE;
+ isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
+ ep->load / ep->period;
+
+ /* switch irq type? */
+ if (!--isp116x->periodic_count) {
+ isp116x->irqenb &= ~HCuPINT_SOF;
+ isp116x->irqenb |= HCuPINT_ATL;
+ }
+}
+
+/*
+ Analyze transfer results, handle partial transfers and errors
+*/
+static void postproc_atl_queue(struct isp116x *isp116x)
+{
+ struct isp116x_ep *ep;
+ struct urb *urb;
+ struct usb_device *udev;
+ struct ptd *ptd;
+ int short_not_ok;
+ int status;
+ u8 cc;
+
+ for (ep = isp116x->atl_active; ep; ep = ep->active) {
+ BUG_ON(list_empty(&ep->hep->urb_list));
+ urb =
+ container_of(ep->hep->urb_list.next, struct urb, urb_list);
+ udev = urb->dev;
+ ptd = &ep->ptd;
+ cc = PTD_GET_CC(ptd);
+ short_not_ok = 1;
+ status = -EINPROGRESS;
+
+ /* Data underrun is special. For allowed underrun
+ we clear the error and continue as normal. For
+ forbidden underrun we finish the DATA stage
+ immediately while for control transfer,
+ we do a STATUS stage. */
+ if (cc == TD_DATAUNDERRUN) {
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK) ||
+ usb_pipecontrol(urb->pipe)) {
+ DBG("Allowed or control data underrun\n");
+ cc = TD_CC_NOERROR;
+ short_not_ok = 0;
+ } else {
+ ep->error_count = 1;
+ usb_settoggle(udev, ep->epnum,
+ ep->nextpid == USB_PID_OUT,
+ PTD_GET_TOGGLE(ptd));
+ urb->actual_length += PTD_GET_COUNT(ptd);
+ status = cc_to_error[TD_DATAUNDERRUN];
+ goto done;
+ }
+ }
+
+ if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED
+ && (++ep->error_count >= 3 || cc == TD_CC_STALL
+ || cc == TD_DATAOVERRUN)) {
+ status = cc_to_error[cc];
+ if (ep->nextpid == USB_PID_ACK)
+ ep->nextpid = 0;
+ goto done;
+ }
+ /* According to usb spec, zero-length Int transfer signals
+ finishing of the urb. Hey, does this apply only
+ for IN endpoints? */
+ if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {
+ status = 0;
+ goto done;
+ }
+
+ /* Relax after previously failed, but later succeeded
+ or correctly NAK'ed retransmission attempt */
+ if (ep->error_count
+ && (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED))
+ ep->error_count = 0;
+
+ /* Take into account idiosyncracies of the isp116x chip
+ regarding toggle bit for failed transfers */
+ if (ep->nextpid == USB_PID_OUT)
+ usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)
+ ^ (ep->error_count > 0));
+ else if (ep->nextpid == USB_PID_IN)
+ usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)
+ ^ (ep->error_count > 0));
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ case USB_PID_OUT:
+ urb->actual_length += PTD_GET_COUNT(ptd);
+ if (PTD_GET_ACTIVE(ptd)
+ || (cc != TD_CC_NOERROR && cc < 0x0E))
+ break;
+ if (urb->transfer_buffer_length != urb->actual_length) {
+ if (short_not_ok)
+ break;
+ } else {
+ if (urb->transfer_flags & URB_ZERO_PACKET
+ && ep->nextpid == USB_PID_OUT
+ && !(PTD_GET_COUNT(ptd) % ep->maxpacket)) {
+ DBG("Zero packet requested\n");
+ break;
+ }
+ }
+ /* All data for this URB is transferred, let's finish */
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_ACK;
+ else
+ status = 0;
+ break;
+ case USB_PID_SETUP:
+ if (PTD_GET_ACTIVE(ptd)
+ || (cc != TD_CC_NOERROR && cc < 0x0E))
+ break;
+ if (urb->transfer_buffer_length == urb->actual_length)
+ ep->nextpid = USB_PID_ACK;
+ else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ if (PTD_GET_ACTIVE(ptd)
+ || (cc != TD_CC_NOERROR && cc < 0x0E))
+ break;
+ status = 0;
+ ep->nextpid = 0;
+ break;
+ default:
+ BUG();
+ }
+
+ done:
+ if (status != -EINPROGRESS || urb->unlinked)
+ finish_request(isp116x, ep, urb, status);
+ }
+}
+
+/*
+ Scan transfer lists, schedule transfers, send data off
+ to chip.
+ */
+static void start_atl_transfers(struct isp116x *isp116x)
+{
+ struct isp116x_ep *last_ep = NULL, *ep;
+ struct urb *urb;
+ u16 load = 0;
+ int len, index, speed, byte_time;
+
+ if (atomic_read(&isp116x->atl_finishing))
+ return;
+
+ if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state))
+ return;
+
+ /* FIFO not empty? */
+ if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL)
+ return;
+
+ isp116x->atl_active = NULL;
+ isp116x->atl_buflen = isp116x->atl_bufshrt = 0;
+
+ /* Schedule int transfers */
+ if (isp116x->periodic_count) {
+ isp116x->fmindex = index =
+ (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1);
+ if ((load = isp116x->load[index])) {
+ /* Bring all int transfers for this frame
+ into the active queue */
+ isp116x->atl_active = last_ep =
+ isp116x->periodic[index];
+ while (last_ep->next)
+ last_ep = (last_ep->active = last_ep->next);
+ last_ep->active = NULL;
+ }
+ }
+
+ /* Schedule control/bulk transfers */
+ list_for_each_entry(ep, &isp116x->async, schedule) {
+ urb = container_of(ep->hep->urb_list.next,
+ struct urb, urb_list);
+ speed = urb->dev->speed;
+ byte_time = speed == USB_SPEED_LOW
+ ? BYTE_TIME_LOWSPEED : BYTE_TIME_FULLSPEED;
+
+ if (ep->nextpid == USB_PID_SETUP) {
+ len = sizeof(struct usb_ctrlrequest);
+ } else if (ep->nextpid == USB_PID_ACK) {
+ len = 0;
+ } else {
+ /* Find current free length ... */
+ len = (MAX_LOAD_LIMIT - load) / byte_time;
+
+ /* ... then limit it to configured max size ... */
+ len = min(len, speed == USB_SPEED_LOW ?
+ MAX_TRANSFER_SIZE_LOWSPEED :
+ MAX_TRANSFER_SIZE_FULLSPEED);
+
+ /* ... and finally cut to the multiple of MaxPacketSize,
+ or to the real length if there's enough room. */
+ if (len <
+ (urb->transfer_buffer_length -
+ urb->actual_length)) {
+ len -= len % ep->maxpacket;
+ if (!len)
+ continue;
+ } else
+ len = urb->transfer_buffer_length -
+ urb->actual_length;
+ BUG_ON(len < 0);
+ }
+
+ load += len * byte_time;
+ if (load > MAX_LOAD_LIMIT)
+ break;
+
+ ep->active = NULL;
+ ep->length = len;
+ if (last_ep)
+ last_ep->active = ep;
+ else
+ isp116x->atl_active = ep;
+ last_ep = ep;
+ }
+
+ /* Avoid starving of endpoints */
+ if ((&isp116x->async)->next != (&isp116x->async)->prev)
+ list_move(&isp116x->async, (&isp116x->async)->next);
+
+ if (isp116x->atl_active) {
+ preproc_atl_queue(isp116x);
+ pack_fifo(isp116x);
+ }
+}
+
+/*
+ Finish the processed transfers
+*/
+static void finish_atl_transfers(struct isp116x *isp116x)
+{
+ if (!isp116x->atl_active)
+ return;
+ /* Fifo not ready? */
+ if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE))
+ return;
+
+ atomic_inc(&isp116x->atl_finishing);
+ unpack_fifo(isp116x);
+ postproc_atl_queue(isp116x);
+ atomic_dec(&isp116x->atl_finishing);
+}
+
+static irqreturn_t isp116x_irq(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ u16 irqstat;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&isp116x->lock);
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+ irqstat = isp116x_read_reg16(isp116x, HCuPINT);
+ isp116x_write_reg16(isp116x, HCuPINT, irqstat);
+
+ if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
+ ret = IRQ_HANDLED;
+ finish_atl_transfers(isp116x);
+ }
+
+ if (irqstat & HCuPINT_OPR) {
+ u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
+ isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
+ if (intstat & HCINT_UE) {
+ ERR("Unrecoverable error, HC is dead!\n");
+ /* IRQ's are off, we do no DMA,
+ perfectly ready to die ... */
+ hcd->state = HC_STATE_HALT;
+ usb_hc_died(hcd);
+ ret = IRQ_HANDLED;
+ goto done;
+ }
+ if (intstat & HCINT_RHSC)
+ /* When root hub or any of its ports is going
+ to come out of suspend, it may take more
+ than 10ms for status bits to stabilize. */
+ mod_timer(&hcd->rh_timer, jiffies
+ + msecs_to_jiffies(20) + 1);
+ if (intstat & HCINT_RD) {
+ DBG("---- remote wakeup\n");
+ usb_hcd_resume_root_hub(hcd);
+ }
+ irqstat &= ~HCuPINT_OPR;
+ ret = IRQ_HANDLED;
+ }
+
+ if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
+ start_atl_transfers(isp116x);
+ }
+
+ isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+ done:
+ spin_unlock(&isp116x->lock);
+ return ret;
+}
+
+/*-----------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ */
+
+/* out of 1000 us */
+#define MAX_PERIODIC_LOAD 600
+static int balance(struct isp116x *isp116x, u16 period, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that period
+ which has enough bandwidth left unreserved. */
+ for (i = 0; i < period; i++) {
+ if (branch < 0 || isp116x->load[branch] > isp116x->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += period) {
+ if ((isp116x->load[j] + load)
+ > MAX_PERIODIC_LOAD)
+ break;
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/* NB! ALL the code above this point runs with isp116x->lock
+ held, irqs off
+*/
+
+/*-----------------------------------------------------------------*/
+
+static int isp116x_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct usb_host_endpoint *hep = urb->ep;
+ struct isp116x_ep *ep = NULL;
+ unsigned long flags;
+ int i;
+ int ret = 0;
+
+ urb_dbg(urb, "Enqueue");
+
+ if (type == PIPE_ISOCHRONOUS) {
+ ERR("Isochronous transfers not supported\n");
+ urb_dbg(urb, "Refused to enqueue");
+ return -ENXIO;
+ }
+ /* avoid all allocations within spinlocks: request or endpoint */
+ if (!hep->hcpriv) {
+ ep = kzalloc(sizeof *ep, mem_flags);
+ if (!ep)
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ if (!HC_IS_RUNNING(hcd->state)) {
+ kfree(ep);
+ ret = -ENODEV;
+ goto fail_not_linked;
+ }
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret) {
+ kfree(ep);
+ goto fail_not_linked;
+ }
+
+ if (hep->hcpriv)
+ ep = hep->hcpriv;
+ else {
+ INIT_LIST_HEAD(&ep->schedule);
+ ep->udev = udev;
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL) {
+ ep->nextpid = USB_PID_SETUP;
+ } else if (is_out) {
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ ep->nextpid = USB_PID_IN;
+ }
+
+ if (urb->interval) {
+ /*
+ With INT URBs submitted, the driver works with SOF
+ interrupt enabled and ATL interrupt disabled. After
+ the PTDs are written to fifo ram, the chip starts
+ fifo processing and usb transfers after the next
+ SOF and continues until the transfers are finished
+ (succeeded or failed) or the frame ends. Therefore,
+ the transfers occur only in every second frame,
+ while fifo reading/writing and data processing
+ occur in every other second frame. */
+ if (urb->interval < 2)
+ urb->interval = 2;
+ if (urb->interval > 2 * PERIODIC_SIZE)
+ urb->interval = 2 * PERIODIC_SIZE;
+ ep->period = urb->interval >> 1;
+ ep->branch = PERIODIC_SIZE;
+ ep->load = usb_calc_bus_time(udev->speed,
+ !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe,
+ is_out)) /
+ 1000;
+ }
+ hep->hcpriv = ep;
+ ep->hep = hep;
+ }
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule))
+ list_add_tail(&ep->schedule, &isp116x->async);
+ break;
+ case PIPE_INTERRUPT:
+ urb->interval = ep->period;
+ ep->length = min_t(u32, ep->maxpacket,
+ urb->transfer_buffer_length);
+
+ /* urb submitted for already existing endpoint */
+ if (ep->branch < PERIODIC_SIZE)
+ break;
+
+ ep->branch = ret = balance(isp116x, ep->period, ep->load);
+ if (ret < 0)
+ goto fail;
+ ret = 0;
+
+ urb->start_frame = (isp116x->fmindex & (PERIODIC_SIZE - 1))
+ + ep->branch;
+
+ /* sort each schedule branch by period (slow before fast)
+ to share the faster parts of the tree without needing
+ dummy/placeholder nodes */
+ DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct isp116x_ep **prev = &isp116x->periodic[i];
+ struct isp116x_ep *here = *prev;
+
+ while (here && ep != here) {
+ if (ep->period > here->period)
+ break;
+ prev = &here->next;
+ here = *prev;
+ }
+ if (ep != here) {
+ ep->next = here;
+ *prev = ep;
+ }
+ isp116x->load[i] += ep->load;
+ }
+ hcd->self.bandwidth_allocated += ep->load / ep->period;
+
+ /* switch over to SOFint */
+ if (!isp116x->periodic_count++) {
+ isp116x->irqenb &= ~HCuPINT_ATL;
+ isp116x->irqenb |= HCuPINT_SOF;
+ isp116x_write_reg16(isp116x, HCuPINTENB,
+ isp116x->irqenb);
+ }
+ }
+
+ urb->hcpriv = hep;
+ start_atl_transfers(isp116x);
+
+ fail:
+ if (ret)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ fail_not_linked:
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return ret;
+}
+
+/*
+ Dequeue URBs.
+*/
+static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ struct usb_host_endpoint *hep;
+ struct isp116x_ep *ep, *ep_act;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ hep = urb->hcpriv;
+ ep = hep->hcpriv;
+ WARN_ON(hep != ep->hep);
+
+ /* In front of queue? */
+ if (ep->hep->urb_list.next == &urb->urb_list)
+ /* active? */
+ for (ep_act = isp116x->atl_active; ep_act;
+ ep_act = ep_act->active)
+ if (ep_act == ep) {
+ VDBG("dequeue, urb %p active; wait for irq\n",
+ urb);
+ urb = NULL;
+ break;
+ }
+
+ if (urb)
+ finish_request(isp116x, ep, urb, status);
+ done:
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return rc;
+}
+
+static void isp116x_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ int i;
+ struct isp116x_ep *ep = hep->hcpriv;
+
+ if (!ep)
+ return;
+
+ /* assume we'd just wait for the irq */
+ for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++)
+ msleep(3);
+ if (!list_empty(&hep->urb_list))
+ WARNING("ep %p not empty?\n", ep);
+
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int isp116x_get_frame(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ u32 fmnum;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ fmnum = isp116x_read_reg32(isp116x, HCFMNUM);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return (int)fmnum;
+}
+
+/*
+ Adapted from ohci-hub.c. Currently we don't support autosuspend.
+*/
+static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ int ports, i, changed = 0;
+ unsigned long flags;
+
+ if (!HC_IS_RUNNING(hcd->state))
+ return -ESHUTDOWN;
+
+ /* Report no status change now, if we are scheduled to be
+ called later */
+ if (timer_pending(&hcd->rh_timer))
+ return 0;
+
+ ports = isp116x->rhdesca & RH_A_NDP;
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+ if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
+ buf[0] = changed = 1;
+ else
+ buf[0] = 0;
+
+ for (i = 0; i < ports; i++) {
+ u32 status = isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1);
+
+ if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+ | RH_PS_OCIC | RH_PS_PRSC)) {
+ changed = 1;
+ buf[0] |= 1 << (i + 1);
+ }
+ }
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return changed;
+}
+
+static void isp116x_hub_descriptor(struct isp116x *isp116x,
+ struct usb_hub_descriptor *desc)
+{
+ u32 reg = isp116x->rhdesca;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bDescLength = 9;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = (u8) (reg & 0x3);
+ /* Power switching, device type, overcurrent. */
+ desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) &
+ (HUB_CHAR_LPSM |
+ HUB_CHAR_COMPOUND |
+ HUB_CHAR_OCPM)));
+ desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+}
+
+/* Perform reset of a given port.
+ It would be great to just start the reset and let the
+ USB core to clear the reset in due time. However,
+ root hub ports should be reset for at least 50 ms, while
+ our chip stays in reset for about 10 ms. I.e., we must
+ repeatedly reset it ourself here.
+*/
+static inline void root_port_reset(struct isp116x *isp116x, unsigned port)
+{
+ u32 tmp;
+ unsigned long flags, t;
+
+ /* Root hub reset should be 50 ms, but some devices
+ want it even longer. */
+ t = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, t)) {
+ spin_lock_irqsave(&isp116x->lock, flags);
+ /* spin until any current reset finishes */
+ for (;;) {
+ tmp = isp116x_read_reg32(isp116x, port ?
+ HCRHPORT2 : HCRHPORT1);
+ if (!(tmp & RH_PS_PRS))
+ break;
+ udelay(500);
+ }
+ /* Don't reset a disconnected port */
+ if (!(tmp & RH_PS_CCS)) {
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ break;
+ }
+ /* Reset lasts 10ms (claims datasheet) */
+ isp116x_write_reg32(isp116x, port ? HCRHPORT2 :
+ HCRHPORT1, (RH_PS_PRS));
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ msleep(10);
+ }
+}
+
+/* Adapted from ohci-hub.c */
+static int isp116x_hub_control(struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ int ret = 0;
+ unsigned long flags;
+ int ports = isp116x->rhdesca & RH_A_NDP;
+ u32 tmp = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ DBG("ClearHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ DBG("C_HUB_OVER_CURRENT\n");
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ case C_HUB_LOCAL_POWER:
+ DBG("C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetHubFeature:
+ DBG("SetHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ DBG("C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ DBG("GetHubDescriptor\n");
+ isp116x_hub_descriptor(isp116x,
+ (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ DBG("GetHubStatus\n");
+ *(__le32 *) buf = 0;
+ break;
+ case GetPortStatus:
+ DBG("GetPortStatus\n");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ spin_lock_irqsave(&isp116x->lock, flags);
+ tmp = isp116x_read_reg32(isp116x, (--wIndex) ? HCRHPORT2 : HCRHPORT1);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ *(__le32 *) buf = cpu_to_le32(tmp);
+ DBG("GetPortStatus: port[%d] %08x\n", wIndex + 1, tmp);
+ break;
+ case ClearPortFeature:
+ DBG("ClearPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ DBG("USB_PORT_FEAT_ENABLE\n");
+ tmp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ DBG("USB_PORT_FEAT_C_ENABLE\n");
+ tmp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ DBG("USB_PORT_FEAT_SUSPEND\n");
+ tmp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ DBG("USB_PORT_FEAT_C_SUSPEND\n");
+ tmp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ DBG("USB_PORT_FEAT_POWER\n");
+ tmp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ DBG("USB_PORT_FEAT_C_CONNECTION\n");
+ tmp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ DBG("USB_PORT_FEAT_C_OVER_CURRENT\n");
+ tmp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ DBG("USB_PORT_FEAT_C_RESET\n");
+ tmp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg32(isp116x, wIndex
+ ? HCRHPORT2 : HCRHPORT1, tmp);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ break;
+ case SetPortFeature:
+ DBG("SetPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ DBG("USB_PORT_FEAT_SUSPEND\n");
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg32(isp116x, wIndex
+ ? HCRHPORT2 : HCRHPORT1, RH_PS_PSS);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ break;
+ case USB_PORT_FEAT_POWER:
+ DBG("USB_PORT_FEAT_POWER\n");
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg32(isp116x, wIndex
+ ? HCRHPORT2 : HCRHPORT1, RH_PS_PPS);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ break;
+ case USB_PORT_FEAT_RESET:
+ DBG("USB_PORT_FEAT_RESET\n");
+ root_port_reset(isp116x, wIndex);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+ error:
+ /* "protocol stall" on error */
+ DBG("PROTOCOL STALL\n");
+ ret = -EPIPE;
+ }
+ return ret;
+}
+
+/*-----------------------------------------------------------------*/
+
+#ifdef CONFIG_DEBUG_FS
+
+static void dump_irq(struct seq_file *s, char *label, u16 mask)
+{
+ seq_printf(s, "%s %04x%s%s%s%s%s%s\n", label, mask,
+ mask & HCuPINT_CLKRDY ? " clkrdy" : "",
+ mask & HCuPINT_SUSP ? " susp" : "",
+ mask & HCuPINT_OPR ? " opr" : "",
+ mask & HCuPINT_AIIEOT ? " eot" : "",
+ mask & HCuPINT_ATL ? " atl" : "",
+ mask & HCuPINT_SOF ? " sof" : "");
+}
+
+static void dump_int(struct seq_file *s, char *label, u32 mask)
+{
+ seq_printf(s, "%s %08x%s%s%s%s%s%s%s\n", label, mask,
+ mask & HCINT_MIE ? " MIE" : "",
+ mask & HCINT_RHSC ? " rhsc" : "",
+ mask & HCINT_FNO ? " fno" : "",
+ mask & HCINT_UE ? " ue" : "",
+ mask & HCINT_RD ? " rd" : "",
+ mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
+}
+
+static int isp116x_show_dbg(struct seq_file *s, void *unused)
+{
+ struct isp116x *isp116x = s->private;
+
+ seq_printf(s, "%s\n%s version %s\n",
+ isp116x_to_hcd(isp116x)->product_desc, hcd_name,
+ DRIVER_VERSION);
+
+ if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) {
+ seq_printf(s, "HCD is suspended\n");
+ return 0;
+ }
+ if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) {
+ seq_printf(s, "HCD not running\n");
+ return 0;
+ }
+
+ spin_lock_irq(&isp116x->lock);
+ dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));
+ dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));
+ dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));
+ dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));
+ isp116x_show_regs_seq(isp116x, s);
+ spin_unlock_irq(&isp116x->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int isp116x_open_seq(struct inode *inode, struct file *file)
+{
+ return single_open(file, isp116x_show_dbg, inode->i_private);
+}
+
+static const struct file_operations isp116x_debug_fops = {
+ .open = isp116x_open_seq,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int create_debug_file(struct isp116x *isp116x)
+{
+ isp116x->dentry = debugfs_create_file(hcd_name,
+ S_IRUGO, NULL, isp116x,
+ &isp116x_debug_fops);
+ if (!isp116x->dentry)
+ return -ENOMEM;
+ return 0;
+}
+
+static void remove_debug_file(struct isp116x *isp116x)
+{
+ debugfs_remove(isp116x->dentry);
+}
+
+#else
+
+#define create_debug_file(d) 0
+#define remove_debug_file(d) do{}while(0)
+
+#endif /* CONFIG_DEBUG_FS */
+
+/*-----------------------------------------------------------------*/
+
+/*
+ Software reset - can be called from any contect.
+*/
+static int isp116x_sw_reset(struct isp116x *isp116x)
+{
+ int retries = 15;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
+ isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);
+ while (--retries) {
+ /* It usually resets within 1 ms */
+ mdelay(1);
+ if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))
+ break;
+ }
+ if (!retries) {
+ ERR("Software reset timeout\n");
+ ret = -ETIME;
+ }
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return ret;
+}
+
+static int isp116x_reset(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ unsigned long t;
+ u16 clkrdy = 0;
+ int ret, timeout = 15 /* ms */ ;
+
+ ret = isp116x_sw_reset(isp116x);
+ if (ret)
+ return ret;
+
+ t = jiffies + msecs_to_jiffies(timeout);
+ while (time_before_eq(jiffies, t)) {
+ msleep(4);
+ spin_lock_irq(&isp116x->lock);
+ clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
+ spin_unlock_irq(&isp116x->lock);
+ if (clkrdy)
+ break;
+ }
+ if (!clkrdy) {
+ ERR("Clock not ready after %dms\n", timeout);
+ /* After sw_reset the clock won't report to be ready, if
+ H_WAKEUP pin is high. */
+ ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
+ ret = -ENODEV;
+ }
+ return ret;
+}
+
+static void isp116x_stop(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+ /* Switch off ports' power, some devices don't come up
+ after next 'insmod' without this */
+ val = isp116x_read_reg32(isp116x, HCRHDESCA);
+ val &= ~(RH_A_NPS | RH_A_PSM);
+ isp116x_write_reg32(isp116x, HCRHDESCA, val);
+ isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+
+ isp116x_sw_reset(isp116x);
+}
+
+/*
+ Configure the chip. The chip must be successfully reset by now.
+*/
+static int isp116x_start(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ struct isp116x_platform_data *board = isp116x->board;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+
+ /* clear interrupt status and disable all interrupt sources */
+ isp116x_write_reg16(isp116x, HCuPINT, 0xff);
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+ val = isp116x_read_reg16(isp116x, HCCHIPID);
+ if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+ ERR("Invalid chip ID %04x\n", val);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return -ENODEV;
+ }
+
+ /* To be removed in future */
+ hcd->uses_new_polling = 1;
+
+ isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
+ isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
+
+ /* ----- HW conf */
+ val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
+ if (board->sel15Kres)
+ val |= HCHWCFG_15KRSEL;
+ /* Remote wakeup won't work without working clock */
+ if (board->remote_wakeup_enable)
+ val |= HCHWCFG_CLKNOTSTOP;
+ if (board->oc_enable)
+ val |= HCHWCFG_ANALOG_OC;
+ if (board->int_act_high)
+ val |= HCHWCFG_INT_POL;
+ if (board->int_edge_triggered)
+ val |= HCHWCFG_INT_TRIGGER;
+ isp116x_write_reg16(isp116x, HCHWCFG, val);
+
+ /* ----- Root hub conf */
+ val = (25 << 24) & RH_A_POTPGT;
+ /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
+ be always set. Yet, instead, we request individual port
+ power switching. */
+ val |= RH_A_PSM;
+ /* Report overcurrent per port */
+ val |= RH_A_OCPM;
+ isp116x_write_reg32(isp116x, HCRHDESCA, val);
+ isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+ val = RH_B_PPCM;
+ isp116x_write_reg32(isp116x, HCRHDESCB, val);
+ isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+ val = 0;
+ if (board->remote_wakeup_enable) {
+ if (!device_can_wakeup(hcd->self.controller))
+ device_init_wakeup(hcd->self.controller, 1);
+ val |= RH_HS_DRWE;
+ }
+ isp116x_write_reg32(isp116x, HCRHSTATUS, val);
+ isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+ isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Set up interrupts */
+ isp116x->intenb = HCINT_MIE | HCINT_RHSC | HCINT_UE;
+ if (board->remote_wakeup_enable)
+ isp116x->intenb |= HCINT_RD;
+ isp116x->irqenb = HCuPINT_ATL | HCuPINT_OPR; /* | HCuPINT_SUSP; */
+ isp116x_write_reg32(isp116x, HCINTENB, isp116x->intenb);
+ isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+
+ /* Go operational */
+ val = HCCONTROL_USB_OPER;
+ if (board->remote_wakeup_enable)
+ val |= HCCONTROL_RWE;
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+
+ /* Disable ports to avoid race in device enumeration */
+ isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
+ isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
+
+ isp116x_show_regs_log(isp116x);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int isp116x_bus_suspend(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ unsigned long flags;
+ u32 val;
+ int ret = 0;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+
+ switch (val & HCCONTROL_HCFS) {
+ case HCCONTROL_USB_OPER:
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
+ val |= HCCONTROL_USB_SUSPEND;
+ if (hcd->self.root_hub->do_remote_wakeup)
+ val |= HCCONTROL_RWE;
+ /* Wait for usb transfers to finish */
+ msleep(2);
+ spin_lock_irqsave(&isp116x->lock, flags);
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ /* Wait for devices to suspend */
+ msleep(5);
+ break;
+ case HCCONTROL_USB_RESUME:
+ isp116x_write_reg32(isp116x, HCCONTROL,
+ (val & ~HCCONTROL_HCFS) |
+ HCCONTROL_USB_RESET);
+ case HCCONTROL_USB_RESET:
+ ret = -EBUSY;
+ default: /* HCCONTROL_USB_SUSPEND */
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ break;
+ }
+
+ return ret;
+}
+
+static int isp116x_bus_resume(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ u32 val;
+
+ msleep(5);
+ spin_lock_irq(&isp116x->lock);
+
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+ switch (val & HCCONTROL_HCFS) {
+ case HCCONTROL_USB_SUSPEND:
+ val &= ~HCCONTROL_HCFS;
+ val |= HCCONTROL_USB_RESUME;
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+ case HCCONTROL_USB_RESUME:
+ break;
+ case HCCONTROL_USB_OPER:
+ spin_unlock_irq(&isp116x->lock);
+ return 0;
+ default:
+ /* HCCONTROL_USB_RESET: this may happen, when during
+ suspension the HC lost power. Reinitialize completely */
+ spin_unlock_irq(&isp116x->lock);
+ DBG("Chip has been reset while suspended. Reinit from scratch.\n");
+ isp116x_reset(hcd);
+ isp116x_start(hcd);
+ isp116x_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_POWER, 1, NULL, 0);
+ if ((isp116x->rhdesca & RH_A_NDP) == 2)
+ isp116x_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_POWER, 2, NULL, 0);
+ return 0;
+ }
+
+ val = isp116x->rhdesca & RH_A_NDP;
+ while (val--) {
+ u32 stat =
+ isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
+ /* force global, not selective, resume */
+ if (!(stat & RH_PS_PSS))
+ continue;
+ DBG("%s: Resuming port %d\n", __func__, val);
+ isp116x_write_reg32(isp116x, RH_PS_POCI, val
+ ? HCRHPORT2 : HCRHPORT1);
+ }
+ spin_unlock_irq(&isp116x->lock);
+
+ hcd->state = HC_STATE_RESUMING;
+ msleep(USB_RESUME_TIMEOUT);
+
+ /* Go operational */
+ spin_lock_irq(&isp116x->lock);
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+ isp116x_write_reg32(isp116x, HCCONTROL,
+ (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
+ spin_unlock_irq(&isp116x->lock);
+ hcd->state = HC_STATE_RUNNING;
+
+ return 0;
+}
+
+#else
+
+#define isp116x_bus_suspend NULL
+#define isp116x_bus_resume NULL
+
+#endif
+
+static struct hc_driver isp116x_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "ISP116x Host Controller",
+ .hcd_priv_size = sizeof(struct isp116x),
+
+ .irq = isp116x_irq,
+ .flags = HCD_USB11,
+
+ .reset = isp116x_reset,
+ .start = isp116x_start,
+ .stop = isp116x_stop,
+
+ .urb_enqueue = isp116x_urb_enqueue,
+ .urb_dequeue = isp116x_urb_dequeue,
+ .endpoint_disable = isp116x_endpoint_disable,
+
+ .get_frame_number = isp116x_get_frame,
+
+ .hub_status_data = isp116x_hub_status_data,
+ .hub_control = isp116x_hub_control,
+ .bus_suspend = isp116x_bus_suspend,
+ .bus_resume = isp116x_bus_resume,
+};
+
+/*----------------------------------------------------------------*/
+
+static int isp116x_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp116x *isp116x;
+ struct resource *res;
+
+ if (!hcd)
+ return 0;
+ isp116x = hcd_to_isp116x(hcd);
+ remove_debug_file(isp116x);
+ usb_remove_hcd(hcd);
+
+ iounmap(isp116x->data_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, 2);
+ iounmap(isp116x->addr_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, 2);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static int isp116x_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct isp116x *isp116x;
+ struct resource *addr, *data, *ires;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int irq;
+ int ret = 0;
+ unsigned long irqflags;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (pdev->num_resources < 3) {
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!addr || !data || !ires) {
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ irq = ires->start;
+ irqflags = ires->flags & IRQF_TRIGGER_MASK;
+
+ if (pdev->dev.dma_mask) {
+ DBG("DMA not supported\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ if (!request_mem_region(addr->start, 2, hcd_name)) {
+ ret = -EBUSY;
+ goto err1;
+ }
+ addr_reg = ioremap(addr->start, resource_size(addr));
+ if (addr_reg == NULL) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+ if (!request_mem_region(data->start, 2, hcd_name)) {
+ ret = -EBUSY;
+ goto err3;
+ }
+ data_reg = ioremap(data->start, resource_size(data));
+ if (data_reg == NULL) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto err5;
+ }
+ /* this rsrc_start is bogus */
+ hcd->rsrc_start = addr->start;
+ isp116x = hcd_to_isp116x(hcd);
+ isp116x->data_reg = data_reg;
+ isp116x->addr_reg = addr_reg;
+ spin_lock_init(&isp116x->lock);
+ INIT_LIST_HEAD(&isp116x->async);
+ isp116x->board = dev_get_platdata(&pdev->dev);
+
+ if (!isp116x->board) {
+ ERR("Platform data structure not initialized\n");
+ ret = -ENODEV;
+ goto err6;
+ }
+ if (isp116x_check_platform_delay(isp116x)) {
+ ERR("USE_PLATFORM_DELAY defined, but delay function not "
+ "implemented.\n");
+ ERR("See comments in drivers/usb/host/isp116x-hcd.c\n");
+ ret = -ENODEV;
+ goto err6;
+ }
+
+ ret = usb_add_hcd(hcd, irq, irqflags);
+ if (ret)
+ goto err6;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ ret = create_debug_file(isp116x);
+ if (ret) {
+ ERR("Couldn't create debugfs entry\n");
+ goto err7;
+ }
+
+ return 0;
+
+ err7:
+ usb_remove_hcd(hcd);
+ err6:
+ usb_put_hcd(hcd);
+ err5:
+ iounmap(data_reg);
+ err4:
+ release_mem_region(data->start, 2);
+ err3:
+ iounmap(addr_reg);
+ err2:
+ release_mem_region(addr->start, 2);
+ err1:
+ ERR("init error, %d\n", ret);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ Suspend of platform device
+*/
+static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
+{
+ VDBG("%s: state %x\n", __func__, state.event);
+ return 0;
+}
+
+/*
+ Resume platform device
+*/
+static int isp116x_resume(struct platform_device *dev)
+{
+ VDBG("%s\n", __func__);
+ return 0;
+}
+
+#else
+
+#define isp116x_suspend NULL
+#define isp116x_resume NULL
+
+#endif
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:isp116x-hcd");
+
+static struct platform_driver isp116x_driver = {
+ .probe = isp116x_probe,
+ .remove = isp116x_remove,
+ .suspend = isp116x_suspend,
+ .resume = isp116x_resume,
+ .driver = {
+ .name = hcd_name,
+ },
+};
+
+module_platform_driver(isp116x_driver);
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
new file mode 100644
index 000000000..dd34b7a33
--- /dev/null
+++ b/drivers/usb/host/isp116x.h
@@ -0,0 +1,595 @@
+/*
+ * ISP116x register declarations and HCD data structures
+ *
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ * Portions:
+ * Copyright (C) 2004 Lothar Wassmann
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ */
+
+/* us of 1ms frame */
+#define MAX_LOAD_LIMIT 850
+
+/* Full speed: max # of bytes to transfer for a single urb
+ at a time must be < 1024 && must be multiple of 64.
+ 832 allows transferring 4kiB within 5 frames. */
+#define MAX_TRANSFER_SIZE_FULLSPEED 832
+
+/* Low speed: there is no reason to schedule in very big
+ chunks; often the requested long transfers are for
+ string descriptors containing short strings. */
+#define MAX_TRANSFER_SIZE_LOWSPEED 64
+
+/* Bytetime (us), a rough indication of how much time it
+ would take to transfer a byte of useful data over USB */
+#define BYTE_TIME_FULLSPEED 1
+#define BYTE_TIME_LOWSPEED 20
+
+/* Buffer sizes */
+#define ISP116x_BUF_SIZE 4096
+#define ISP116x_ITL_BUFSIZE 0
+#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
+
+#define ISP116x_WRITE_OFFSET 0x80
+
+/*------------ ISP116x registers/bits ------------*/
+#define HCREVISION 0x00
+#define HCCONTROL 0x01
+#define HCCONTROL_HCFS (3 << 6) /* host controller
+ functional state */
+#define HCCONTROL_USB_RESET (0 << 6)
+#define HCCONTROL_USB_RESUME (1 << 6)
+#define HCCONTROL_USB_OPER (2 << 6)
+#define HCCONTROL_USB_SUSPEND (3 << 6)
+#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */
+#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */
+#define HCCMDSTAT 0x02
+#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */
+#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */
+#define HCINTSTAT 0x03
+#define HCINT_SO (1 << 0) /* scheduling overrun */
+#define HCINT_WDH (1 << 1) /* writeback of done_head */
+#define HCINT_SF (1 << 2) /* start frame */
+#define HCINT_RD (1 << 3) /* resume detect */
+#define HCINT_UE (1 << 4) /* unrecoverable error */
+#define HCINT_FNO (1 << 5) /* frame number overflow */
+#define HCINT_RHSC (1 << 6) /* root hub status change */
+#define HCINT_OC (1 << 30) /* ownership change */
+#define HCINT_MIE (1 << 31) /* master interrupt enable */
+#define HCINTENB 0x04
+#define HCINTDIS 0x05
+#define HCFMINTVL 0x0d
+#define HCFMREM 0x0e
+#define HCFMNUM 0x0f
+#define HCLSTHRESH 0x11
+#define HCRHDESCA 0x12
+#define RH_A_NDP (0x3 << 0) /* # downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* overcurrent protection
+ mode */
+#define RH_A_NOCP (1 << 12) /* no overcurrent protection */
+#define RH_A_POTPGT (0xff << 24) /* power on -> power good
+ time */
+#define HCRHDESCB 0x13
+#define RH_B_DR (0xffff << 0) /* device removable flags */
+#define RH_B_PPCM (0xffff << 16) /* port power control mask */
+#define HCRHSTATUS 0x14
+#define RH_HS_LPS (1 << 0) /* local power status */
+#define RH_HS_OCI (1 << 1) /* over current indicator */
+#define RH_HS_DRWE (1 << 15) /* device remote wakeup
+ enable */
+#define RH_HS_LPSC (1 << 16) /* local power status change */
+#define RH_HS_OCIC (1 << 17) /* over current indicator
+ change */
+#define RH_HS_CRWE (1 << 31) /* clear remote wakeup
+ enable */
+#define HCRHPORT1 0x15
+#define RH_PS_CCS (1 << 0) /* current connect status */
+#define RH_PS_PES (1 << 1) /* port enable status */
+#define RH_PS_PSS (1 << 2) /* port suspend status */
+#define RH_PS_POCI (1 << 3) /* port over current
+ indicator */
+#define RH_PS_PRS (1 << 4) /* port reset status */
+#define RH_PS_PPS (1 << 8) /* port power status */
+#define RH_PS_LSDA (1 << 9) /* low speed device attached */
+#define RH_PS_CSC (1 << 16) /* connect status change */
+#define RH_PS_PESC (1 << 17) /* port enable status change */
+#define RH_PS_PSSC (1 << 18) /* port suspend status
+ change */
+#define RH_PS_OCIC (1 << 19) /* over current indicator
+ change */
+#define RH_PS_PRSC (1 << 20) /* port reset status change */
+#define HCRHPORT_CLRMASK (0x1f << 16)
+#define HCRHPORT2 0x16
+#define HCHWCFG 0x20
+#define HCHWCFG_15KRSEL (1 << 12)
+#define HCHWCFG_CLKNOTSTOP (1 << 11)
+#define HCHWCFG_ANALOG_OC (1 << 10)
+#define HCHWCFG_DACK_MODE (1 << 8)
+#define HCHWCFG_EOT_POL (1 << 7)
+#define HCHWCFG_DACK_POL (1 << 6)
+#define HCHWCFG_DREQ_POL (1 << 5)
+#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
+#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define HCHWCFG_INT_POL (1 << 2)
+#define HCHWCFG_INT_TRIGGER (1 << 1)
+#define HCHWCFG_INT_ENABLE (1 << 0)
+#define HCDMACFG 0x21
+#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
+#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
+#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
+#define HCDMACFG_DMA_ENABLE (1 << 4)
+#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
+#define HCDMACFG_CTR_SEL (1 << 2)
+#define HCDMACFG_ITLATL_SEL (1 << 1)
+#define HCDMACFG_DMA_RW_SELECT (1 << 0)
+#define HCXFERCTR 0x22
+#define HCuPINT 0x24
+#define HCuPINT_SOF (1 << 0)
+#define HCuPINT_ATL (1 << 1)
+#define HCuPINT_AIIEOT (1 << 2)
+#define HCuPINT_OPR (1 << 4)
+#define HCuPINT_SUSP (1 << 5)
+#define HCuPINT_CLKRDY (1 << 6)
+#define HCuPINTENB 0x25
+#define HCCHIPID 0x27
+#define HCCHIPID_MASK 0xff00
+#define HCCHIPID_MAGIC 0x6100
+#define HCSCRATCH 0x28
+#define HCSWRES 0x29
+#define HCSWRES_MAGIC 0x00f6
+#define HCITLBUFLEN 0x2a
+#define HCATLBUFLEN 0x2b
+#define HCBUFSTAT 0x2c
+#define HCBUFSTAT_ITL0_FULL (1 << 0)
+#define HCBUFSTAT_ITL1_FULL (1 << 1)
+#define HCBUFSTAT_ATL_FULL (1 << 2)
+#define HCBUFSTAT_ITL0_DONE (1 << 3)
+#define HCBUFSTAT_ITL1_DONE (1 << 4)
+#define HCBUFSTAT_ATL_DONE (1 << 5)
+#define HCRDITL0LEN 0x2d
+#define HCRDITL1LEN 0x2e
+#define HCITLPORT 0x40
+#define HCATLPORT 0x41
+
+/* Philips transfer descriptor */
+struct ptd {
+ u16 count;
+#define PTD_COUNT_MSK (0x3ff << 0)
+#define PTD_TOGGLE_MSK (1 << 10)
+#define PTD_ACTIVE_MSK (1 << 11)
+#define PTD_CC_MSK (0xf << 12)
+ u16 mps;
+#define PTD_MPS_MSK (0x3ff << 0)
+#define PTD_SPD_MSK (1 << 10)
+#define PTD_LAST_MSK (1 << 11)
+#define PTD_EP_MSK (0xf << 12)
+ u16 len;
+#define PTD_LEN_MSK (0x3ff << 0)
+#define PTD_DIR_MSK (3 << 10)
+#define PTD_DIR_SETUP (0)
+#define PTD_DIR_OUT (1)
+#define PTD_DIR_IN (2)
+#define PTD_B5_5_MSK (1 << 13)
+ u16 faddr;
+#define PTD_FA_MSK (0x7f << 0)
+#define PTD_FMT_MSK (1 << 7)
+} __attribute__ ((packed, aligned(2)));
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13)
+#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK)
+#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7)
+#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK)
+
+/* Hardware transfer status codes -- CC from ptd->count */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+/* map PTD status codes (CC) to errno values */
+static const int cc_to_error[16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIME,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+/*--------------------------------------------------------------*/
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct isp116x {
+ spinlock_t lock;
+
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+
+ struct isp116x_platform_data *board;
+
+ struct dentry *dentry;
+ unsigned long stat1, stat2, stat4, stat8, stat16;
+
+ /* HC registers */
+ u32 intenb; /* "OHCI" interrupts */
+ u16 irqenb; /* uP interrupts */
+
+ /* Root hub registers */
+ u32 rhdesca;
+ u32 rhdescb;
+ u32 rhstatus;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: int */
+ u16 load[PERIODIC_SIZE];
+ struct isp116x_ep *periodic[PERIODIC_SIZE];
+ unsigned periodic_count;
+ u16 fmindex;
+
+ /* Schedule for the current frame */
+ struct isp116x_ep *atl_active;
+ int atl_buflen;
+ int atl_bufshrt;
+ int atl_last_dir;
+ atomic_t atl_finishing;
+};
+
+static inline struct isp116x *hcd_to_isp116x(struct usb_hcd *hcd)
+{
+ return (struct isp116x *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *isp116x_to_hcd(struct isp116x *isp116x)
+{
+ return container_of((void *)isp116x, struct usb_hcd, hcd_priv);
+}
+
+struct isp116x_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+ struct ptd ptd;
+
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+ u16 error_count;
+ u16 length; /* of current packet */
+ unsigned char *data; /* to databuf */
+ /* queue of active EP's (the ones scheduled for the
+ current frame) */
+ struct isp116x_ep *active;
+
+ /* periodic schedule */
+ u16 period;
+ u16 branch;
+ u16 load;
+ struct isp116x_ep *next;
+
+ /* async schedule */
+ struct list_head schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define DBG(stuff...) pr_debug("116x: " stuff)
+
+#ifdef VERBOSE
+# define VDBG DBG
+#else
+# define VDBG(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "116x: " stuff)
+#define WARNING(stuff...) printk(KERN_WARNING "116x: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "116x: " stuff)
+
+/* ------------------------------------------------- */
+
+#if defined(USE_PLATFORM_DELAY)
+#if defined(USE_NDELAY)
+#error USE_PLATFORM_DELAY and USE_NDELAY simultaneously defined.
+#endif
+#define isp116x_delay(h,d) (h)->board->delay( \
+ isp116x_to_hcd(h)->self.controller,d)
+#define isp116x_check_platform_delay(h) ((h)->board->delay == NULL)
+#elif defined(USE_NDELAY)
+#define isp116x_delay(h,d) ndelay(d)
+#define isp116x_check_platform_delay(h) 0
+#else
+#define isp116x_delay(h,d) do{}while(0)
+#define isp116x_check_platform_delay(h) 0
+#endif
+
+static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
+{
+ writew(reg & 0xff, isp116x->addr_reg);
+ isp116x_delay(isp116x, 300);
+}
+
+static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
+{
+ writew(val, isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+}
+
+static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
+{
+ __raw_writew(val, isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+}
+
+static inline u16 isp116x_read_data16(struct isp116x *isp116x)
+{
+ u16 val;
+
+ val = readw(isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+ return val;
+}
+
+static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
+{
+ u16 val;
+
+ val = __raw_readw(isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+ return val;
+}
+
+static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
+{
+ writew(val & 0xffff, isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+ writew(val >> 16, isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+}
+
+static inline u32 isp116x_read_data32(struct isp116x *isp116x)
+{
+ u32 val;
+
+ val = (u32) readw(isp116x->data_reg);
+ isp116x_delay(isp116x, 150);
+ val |= ((u32) readw(isp116x->data_reg)) << 16;
+ isp116x_delay(isp116x, 150);
+ return val;
+}
+
+/* Let's keep register access functions out of line. Hint:
+ we wait at least 150 ns at every access.
+*/
+static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
+{
+ isp116x_write_addr(isp116x, reg);
+ return isp116x_read_data16(isp116x);
+}
+
+static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
+{
+ isp116x_write_addr(isp116x, reg);
+ return isp116x_read_data32(isp116x);
+}
+
+static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
+ unsigned val)
+{
+ isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+ isp116x_write_data16(isp116x, (u16) (val & 0xffff));
+}
+
+static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
+ unsigned val)
+{
+ isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+ isp116x_write_data32(isp116x, (u32) val);
+}
+
+#define isp116x_show_reg_log(d,r,s) { \
+ if ((r) < 0x20) { \
+ DBG("%-12s[%02x]: %08x\n", #r, \
+ r, isp116x_read_reg32(d, r)); \
+ } else { \
+ DBG("%-12s[%02x]: %04x\n", #r, \
+ r, isp116x_read_reg16(d, r)); \
+ } \
+}
+#define isp116x_show_reg_seq(d,r,s) { \
+ if ((r) < 0x20) { \
+ seq_printf(s, "%-12s[%02x]: %08x\n", #r, \
+ r, isp116x_read_reg32(d, r)); \
+ } else { \
+ seq_printf(s, "%-12s[%02x]: %04x\n", #r, \
+ r, isp116x_read_reg16(d, r)); \
+ } \
+}
+
+#define isp116x_show_regs(d,type,s) { \
+ isp116x_show_reg_##type(d, HCREVISION, s); \
+ isp116x_show_reg_##type(d, HCCONTROL, s); \
+ isp116x_show_reg_##type(d, HCCMDSTAT, s); \
+ isp116x_show_reg_##type(d, HCINTSTAT, s); \
+ isp116x_show_reg_##type(d, HCINTENB, s); \
+ isp116x_show_reg_##type(d, HCFMINTVL, s); \
+ isp116x_show_reg_##type(d, HCFMREM, s); \
+ isp116x_show_reg_##type(d, HCFMNUM, s); \
+ isp116x_show_reg_##type(d, HCLSTHRESH, s); \
+ isp116x_show_reg_##type(d, HCRHDESCA, s); \
+ isp116x_show_reg_##type(d, HCRHDESCB, s); \
+ isp116x_show_reg_##type(d, HCRHSTATUS, s); \
+ isp116x_show_reg_##type(d, HCRHPORT1, s); \
+ isp116x_show_reg_##type(d, HCRHPORT2, s); \
+ isp116x_show_reg_##type(d, HCHWCFG, s); \
+ isp116x_show_reg_##type(d, HCDMACFG, s); \
+ isp116x_show_reg_##type(d, HCXFERCTR, s); \
+ isp116x_show_reg_##type(d, HCuPINT, s); \
+ isp116x_show_reg_##type(d, HCuPINTENB, s); \
+ isp116x_show_reg_##type(d, HCCHIPID, s); \
+ isp116x_show_reg_##type(d, HCSCRATCH, s); \
+ isp116x_show_reg_##type(d, HCITLBUFLEN, s); \
+ isp116x_show_reg_##type(d, HCATLBUFLEN, s); \
+ isp116x_show_reg_##type(d, HCBUFSTAT, s); \
+ isp116x_show_reg_##type(d, HCRDITL0LEN, s); \
+ isp116x_show_reg_##type(d, HCRDITL1LEN, s); \
+}
+
+/*
+ Dump registers for debugfs.
+*/
+static inline void isp116x_show_regs_seq(struct isp116x *isp116x,
+ struct seq_file *s)
+{
+ isp116x_show_regs(isp116x, seq, s);
+}
+
+/*
+ Dump registers to syslog.
+*/
+static inline void isp116x_show_regs_log(struct isp116x *isp116x)
+{
+ isp116x_show_regs(isp116x, log, NULL);
+}
+
+#if defined(URB_TRACE)
+
+#define PIPETYPE(pipe) ({ char *__s; \
+ if (usb_pipecontrol(pipe)) __s = "ctrl"; \
+ else if (usb_pipeint(pipe)) __s = "int"; \
+ else if (usb_pipebulk(pipe)) __s = "bulk"; \
+ else __s = "iso"; \
+ __s;})
+#define PIPEDIR(pipe) ({ usb_pipein(pipe) ? "in" : "out"; })
+#define URB_NOTSHORT(urb) ({ (urb)->transfer_flags & URB_SHORT_NOT_OK ? \
+ "short_not_ok" : ""; })
+
+/* print debug info about the URB */
+static void urb_dbg(struct urb *urb, char *msg)
+{
+ unsigned int pipe;
+
+ if (!urb) {
+ DBG("%s: zero urb\n", msg);
+ return;
+ }
+ pipe = urb->pipe;
+ DBG("%s: FA %d ep%d%s %s: len %d/%d %s\n", msg,
+ usb_pipedevice(pipe), usb_pipeendpoint(pipe),
+ PIPEDIR(pipe), PIPETYPE(pipe),
+ urb->transfer_buffer_length, urb->actual_length, URB_NOTSHORT(urb));
+}
+
+#else
+
+#define urb_dbg(urb,msg) do{}while(0)
+
+#endif /* ! defined(URB_TRACE) */
+
+#if defined(PTD_TRACE)
+
+#define PTD_DIR_STR(ptd) ({char __c; \
+ switch(PTD_GET_DIR(ptd)){ \
+ case 0: __c = 's'; break; \
+ case 1: __c = 'o'; break; \
+ default: __c = 'i'; break; \
+ }; __c;})
+
+/*
+ Dump PTD info. The code documents the format
+ perfectly, right :)
+*/
+static inline void dump_ptd(struct ptd *ptd)
+{
+ printk(KERN_WARNING "td: %x %d%c%d %d,%d,%d %x %x%x%x\n",
+ PTD_GET_CC(ptd), PTD_GET_FA(ptd),
+ PTD_DIR_STR(ptd), PTD_GET_EP(ptd),
+ PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+ PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd),
+ PTD_GET_SPD(ptd), PTD_GET_LAST(ptd));
+}
+
+static inline void dump_ptd_out_data(struct ptd *ptd, u8 * buf)
+{
+ int k;
+
+ if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
+ printk(KERN_WARNING "-> ");
+ for (k = 0; k < PTD_GET_LEN(ptd); ++k)
+ printk("%02x ", ((u8 *) buf)[k]);
+ printk("\n");
+ }
+}
+
+static inline void dump_ptd_in_data(struct ptd *ptd, u8 * buf)
+{
+ int k;
+
+ if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
+ printk(KERN_WARNING "<- ");
+ for (k = 0; k < PTD_GET_COUNT(ptd); ++k)
+ printk("%02x ", ((u8 *) buf)[k]);
+ printk("\n");
+ }
+ if (PTD_GET_LAST(ptd))
+ printk(KERN_WARNING "-\n");
+}
+
+#else
+
+#define dump_ptd(ptd) do{}while(0)
+#define dump_ptd_in_data(ptd,buf) do{}while(0)
+#define dump_ptd_out_data(ptd,buf) do{}while(0)
+
+#endif /* ! defined(PTD_TRACE) */
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
new file mode 100644
index 000000000..6cf82ee46
--- /dev/null
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -0,0 +1,2788 @@
+/*
+ * ISP1362 HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de>
+ *
+ * Derived from the SL811 HCD, rewritten for ISP116x.
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ *
+ * Portions:
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ */
+
+/*
+ * The ISP1362 chip requires a large delay (300ns and 462ns) between
+ * accesses to the address and data register.
+ * The following timing options exist:
+ *
+ * 1. Configure your memory controller to add such delays if it can (the best)
+ * 2. Implement platform-specific delay function possibly
+ * combined with configuring the memory controller; see
+ * include/linux/usb_isp1362.h for more info.
+ * 3. Use ndelay (easiest, poorest).
+ *
+ * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the
+ * platform specific section of isp1362.h to select the appropriate variant.
+ *
+ * Also note that according to the Philips "ISP1362 Errata" document
+ * Rev 1.00 from 27 May data corruption may occur when the #WR signal
+ * is reasserted (even with #CS deasserted) within 132ns after a
+ * write cycle to any controller register. If the hardware doesn't
+ * implement the recommended fix (gating the #WR with #CS) software
+ * must ensure that no further write cycle (not necessarily to the chip!)
+ * is issued by the CPU within this interval.
+
+ * For PXA25x this can be ensured by using VLIO with the maximum
+ * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz.
+ */
+
+#undef ISP1362_DEBUG
+
+/*
+ * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and
+ * GET_INTERFACE requests correctly when the SETUP and DATA stages of the
+ * requests are carried out in separate frames. This will delay any SETUP
+ * packets until the start of the next frame so that this situation is
+ * unlikely to occur (and makes usbtest happy running with a PXA255 target
+ * device).
+ */
+#undef BUGGY_PXA2XX_UDC_USBTEST
+
+#undef PTD_TRACE
+#undef URB_TRACE
+#undef VERBOSE
+#undef REGISTERS
+
+/* This enables a memory test on the ISP1362 chip memory to make sure the
+ * chip access timing is correct.
+ */
+#undef CHIP_BUFFER_TEST
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/isp1362.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/bitmap.h>
+#include <linux/prefetch.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+static int dbg_level;
+#ifdef ISP1362_DEBUG
+module_param(dbg_level, int, 0644);
+#else
+module_param(dbg_level, int, 0);
+#endif
+
+#include "../core/usb.h"
+#include "isp1362.h"
+
+
+#define DRIVER_VERSION "2005-04-04"
+#define DRIVER_DESC "ISP1362 USB Host Controller Driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char hcd_name[] = "isp1362-hcd";
+
+static void isp1362_hc_stop(struct usb_hcd *hcd);
+static int isp1362_hc_start(struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * When called from the interrupthandler only isp1362_hcd->irqenb is modified,
+ * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon
+ * completion.
+ * We don't need a 'disable' counterpart, since interrupts will be disabled
+ * only by the interrupt handler.
+ */
+static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask)
+{
+ if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb)
+ return;
+ if (mask & ~isp1362_hcd->irqenb)
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb);
+ isp1362_hcd->irqenb |= mask;
+ if (isp1362_hcd->irq_active)
+ return;
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd,
+ u16 offset)
+{
+ struct isp1362_ep_queue *epq = NULL;
+
+ if (offset < isp1362_hcd->istl_queue[1].buf_start)
+ epq = &isp1362_hcd->istl_queue[0];
+ else if (offset < isp1362_hcd->intl_queue.buf_start)
+ epq = &isp1362_hcd->istl_queue[1];
+ else if (offset < isp1362_hcd->atl_queue.buf_start)
+ epq = &isp1362_hcd->intl_queue;
+ else if (offset < isp1362_hcd->atl_queue.buf_start +
+ isp1362_hcd->atl_queue.buf_size)
+ epq = &isp1362_hcd->atl_queue;
+
+ if (epq)
+ DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name);
+ else
+ pr_warning("%s: invalid PTD $%04x\n", __func__, offset);
+
+ return epq;
+}
+
+static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
+{
+ int offset;
+
+ if (index * epq->blk_size > epq->buf_size) {
+ pr_warning("%s: Bad %s index %d(%d)\n", __func__, epq->name, index,
+ epq->buf_size / epq->blk_size);
+ return -EINVAL;
+ }
+ offset = epq->buf_start + index * epq->blk_size;
+ DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset);
+
+ return offset;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size,
+ int mps)
+{
+ u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size);
+
+ xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE);
+ if (xfer_size < size && xfer_size % mps)
+ xfer_size -= xfer_size % mps;
+
+ return xfer_size;
+}
+
+static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
+ struct isp1362_ep *ep, u16 len)
+{
+ int ptd_offset = -EINVAL;
+ int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1;
+ int found;
+
+ BUG_ON(len > epq->buf_size);
+
+ if (!epq->buf_avail)
+ return -ENOMEM;
+
+ if (ep->num_ptds)
+ pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__,
+ epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map);
+ BUG_ON(ep->num_ptds != 0);
+
+ found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0,
+ num_ptds, 0);
+ if (found >= epq->buf_count)
+ return -EOVERFLOW;
+
+ DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__,
+ num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE));
+ ptd_offset = get_ptd_offset(epq, found);
+ WARN_ON(ptd_offset < 0);
+ ep->ptd_offset = ptd_offset;
+ ep->num_ptds += num_ptds;
+ epq->buf_avail -= num_ptds;
+ BUG_ON(epq->buf_avail > epq->buf_count);
+ ep->ptd_index = found;
+ bitmap_set(&epq->buf_map, found, num_ptds);
+ DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n",
+ __func__, epq->name, ep->ptd_index, ep->ptd_offset,
+ epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map);
+
+ return found;
+}
+
+static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
+{
+ int last = ep->ptd_index + ep->num_ptds;
+
+ if (last > epq->buf_count)
+ pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n",
+ __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index,
+ ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail,
+ epq->buf_map, epq->skip_map);
+ BUG_ON(last > epq->buf_count);
+
+ bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds);
+ bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds);
+ epq->buf_avail += ep->num_ptds;
+ epq->ptd_count--;
+
+ BUG_ON(epq->buf_avail > epq->buf_count);
+ BUG_ON(epq->ptd_count > epq->buf_count);
+
+ DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n",
+ __func__, epq->name,
+ ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count);
+ DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__,
+ epq->buf_map, epq->skip_map);
+
+ ep->num_ptds = 0;
+ ep->ptd_offset = -EINVAL;
+ ep->ptd_index = -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ Set up PTD's.
+*/
+static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
+ struct isp1362_ep *ep, struct isp1362_ep_queue *epq,
+ u16 fno)
+{
+ struct ptd *ptd;
+ int toggle;
+ int dir;
+ u16 len;
+ size_t buf_len = urb->transfer_buffer_length - urb->actual_length;
+
+ DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep);
+
+ ptd = &ep->ptd;
+
+ ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length;
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
+ dir = PTD_DIR_IN;
+ if (usb_pipecontrol(urb->pipe)) {
+ len = min_t(size_t, ep->maxpacket, buf_len);
+ } else if (usb_pipeisoc(urb->pipe)) {
+ len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE);
+ ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset;
+ } else
+ len = max_transfer_size(epq, buf_len, ep->maxpacket);
+ DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
+ (int)buf_len);
+ break;
+ case USB_PID_OUT:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
+ dir = PTD_DIR_OUT;
+ if (usb_pipecontrol(urb->pipe))
+ len = min_t(size_t, ep->maxpacket, buf_len);
+ else if (usb_pipeisoc(urb->pipe))
+ len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE);
+ else
+ len = max_transfer_size(epq, buf_len, ep->maxpacket);
+ if (len == 0)
+ pr_info("%s: Sending ZERO packet: %d\n", __func__,
+ urb->transfer_flags & URB_ZERO_PACKET);
+ DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
+ (int)buf_len);
+ break;
+ case USB_PID_SETUP:
+ toggle = 0;
+ dir = PTD_DIR_SETUP;
+ len = sizeof(struct usb_ctrlrequest);
+ DBG(1, "%s: SETUP len %d\n", __func__, len);
+ ep->data = urb->setup_packet;
+ break;
+ case USB_PID_ACK:
+ toggle = 1;
+ len = 0;
+ dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ?
+ PTD_DIR_OUT : PTD_DIR_IN;
+ DBG(1, "%s: ACK len %d\n", __func__, len);
+ break;
+ default:
+ toggle = dir = len = 0;
+ pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid);
+ BUG_ON(1);
+ }
+
+ ep->length = len;
+ if (!len)
+ ep->data = NULL;
+
+ ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
+ ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) |
+ PTD_EP(ep->epnum);
+ ptd->len = PTD_LEN(len) | PTD_DIR(dir);
+ ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
+
+ if (usb_pipeint(urb->pipe)) {
+ ptd->faddr |= PTD_SF_INT(ep->branch);
+ ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0);
+ }
+ if (usb_pipeisoc(urb->pipe))
+ ptd->faddr |= PTD_SF_ISO(fno);
+
+ DBG(1, "%s: Finished\n", __func__);
+}
+
+static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct isp1362_ep_queue *epq)
+{
+ struct ptd *ptd = &ep->ptd;
+ int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length;
+
+ prefetch(ptd);
+ isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
+ if (len)
+ isp1362_write_buffer(isp1362_hcd, ep->data,
+ ep->ptd_offset + PTD_HEADER_SIZE, len);
+
+ dump_ptd(ptd);
+ dump_ptd_out_data(ptd, ep->data);
+}
+
+static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct isp1362_ep_queue *epq)
+{
+ struct ptd *ptd = &ep->ptd;
+ int act_len;
+
+ WARN_ON(list_empty(&ep->active));
+ BUG_ON(ep->ptd_offset < 0);
+
+ list_del_init(&ep->active);
+ DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active);
+
+ prefetchw(ptd);
+ isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
+ dump_ptd(ptd);
+ act_len = PTD_GET_COUNT(ptd);
+ if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0)
+ return;
+ if (act_len > ep->length)
+ pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep,
+ ep->ptd_offset, act_len, ep->length);
+ BUG_ON(act_len > ep->length);
+ /* Only transfer the amount of data that has actually been overwritten
+ * in the chip buffer. We don't want any data that doesn't belong to the
+ * transfer to leak out of the chip to the callers transfer buffer!
+ */
+ prefetchw(ep->data);
+ isp1362_read_buffer(isp1362_hcd, ep->data,
+ ep->ptd_offset + PTD_HEADER_SIZE, act_len);
+ dump_ptd_in_data(ptd, ep->data);
+}
+
+/*
+ * INT PTDs will stay in the chip until data is available.
+ * This function will remove a PTD from the chip when the URB is dequeued.
+ * Must be called with the spinlock held and IRQs disabled
+ */
+static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
+
+{
+ int index;
+ struct isp1362_ep_queue *epq;
+
+ DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset);
+ BUG_ON(ep->ptd_offset < 0);
+
+ epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset);
+ BUG_ON(!epq);
+
+ /* put ep in remove_list for cleanup */
+ WARN_ON(!list_empty(&ep->remove_list));
+ list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list);
+ /* let SOF interrupt handle the cleanup */
+ isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
+
+ index = ep->ptd_index;
+ if (index < 0)
+ /* ISO queues don't have SKIP registers */
+ return;
+
+ DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__,
+ index, ep->ptd_offset, epq->skip_map, 1 << index);
+
+ /* prevent further processing of PTD (will be effective after next SOF) */
+ epq->skip_map |= 1 << index;
+ if (epq == &isp1362_hcd->atl_queue) {
+ DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map);
+ if (~epq->skip_map == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ } else if (epq == &isp1362_hcd->intl_queue) {
+ DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map);
+ if (~epq->skip_map == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+ }
+}
+
+/*
+ Take done or failed requests out of schedule. Give back
+ processed urbs.
+*/
+static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct urb *urb, int status)
+ __releases(isp1362_hcd->lock)
+ __acquires(isp1362_hcd->lock)
+{
+ urb->hcpriv = NULL;
+ ep->error_count = 0;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__,
+ ep->num_req, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ !usb_pipein(urb->pipe) ? "out" : "in",
+ usb_pipecontrol(urb->pipe) ? "ctrl" :
+ usb_pipeint(urb->pipe) ? "int" :
+ usb_pipebulk(urb->pipe) ? "bulk" :
+ "iso",
+ urb->actual_length, urb->transfer_buffer_length,
+ !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
+ "short_ok" : "", urb->status);
+
+
+ usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb);
+ spin_unlock(&isp1362_hcd->lock);
+ usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status);
+ spin_lock(&isp1362_hcd->lock);
+
+ /* take idle endpoints out of the schedule right away */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ return;
+ }
+
+
+ if (ep->interval) {
+ /* periodic deschedule */
+ DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval,
+ ep, ep->branch, ep->load,
+ isp1362_hcd->load[ep->branch],
+ isp1362_hcd->load[ep->branch] - ep->load);
+ isp1362_hcd->load[ep->branch] -= ep->load;
+ ep->branch = PERIODIC_SIZE;
+ }
+}
+
+/*
+ * Analyze transfer results, handle partial transfers and errors
+*/
+static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
+{
+ struct urb *urb = get_urb(ep);
+ struct usb_device *udev;
+ struct ptd *ptd;
+ int short_ok;
+ u16 len;
+ int urbstat = -EINPROGRESS;
+ u8 cc;
+
+ DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req);
+
+ udev = urb->dev;
+ ptd = &ep->ptd;
+ cc = PTD_GET_CC(ptd);
+ if (cc == PTD_NOTACCESSED) {
+ pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__,
+ ep->num_req, ptd);
+ cc = PTD_DEVNOTRESP;
+ }
+
+ short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK);
+ len = urb->transfer_buffer_length - urb->actual_length;
+
+ /* Data underrun is special. For allowed underrun
+ we clear the error and continue as normal. For
+ forbidden underrun we finish the DATA stage
+ immediately while for control transfer,
+ we do a STATUS stage.
+ */
+ if (cc == PTD_DATAUNDERRUN) {
+ if (short_ok) {
+ DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n",
+ __func__, ep->num_req, short_ok ? "" : "not_",
+ PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ cc = PTD_CC_NOERROR;
+ urbstat = 0;
+ } else {
+ DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n",
+ __func__, ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
+ short_ok ? "" : "not_",
+ PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ /* save the data underrun error code for later and
+ * proceed with the status stage
+ */
+ urb->actual_length += PTD_GET_COUNT(ptd);
+ if (usb_pipecontrol(urb->pipe)) {
+ ep->nextpid = USB_PID_ACK;
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+
+ if (urb->status == -EINPROGRESS)
+ urb->status = cc_to_error[PTD_DATAUNDERRUN];
+ } else {
+ usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT,
+ PTD_GET_TOGGLE(ptd));
+ urbstat = cc_to_error[PTD_DATAUNDERRUN];
+ }
+ goto out;
+ }
+ }
+
+ if (cc != PTD_CC_NOERROR) {
+ if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) {
+ urbstat = cc_to_error[cc];
+ DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n",
+ __func__, ep->num_req, ep->nextpid, urbstat, cc,
+ ep->error_count);
+ }
+ goto out;
+ }
+
+ switch (ep->nextpid) {
+ case USB_PID_OUT:
+ if (PTD_GET_COUNT(ptd) != ep->length)
+ pr_err("%s: count=%d len=%d\n", __func__,
+ PTD_GET_COUNT(ptd), ep->length);
+ BUG_ON(PTD_GET_COUNT(ptd) != ep->length);
+ urb->actual_length += ep->length;
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+ usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd));
+ if (urb->actual_length == urb->transfer_buffer_length) {
+ DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
+ ep->num_req, len, ep->maxpacket, urbstat);
+ if (usb_pipecontrol(urb->pipe)) {
+ DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
+ ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT");
+ ep->nextpid = USB_PID_ACK;
+ } else {
+ if (len % ep->maxpacket ||
+ !(urb->transfer_flags & URB_ZERO_PACKET)) {
+ urbstat = 0;
+ DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
+ __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
+ urbstat, len, ep->maxpacket, urb->actual_length);
+ }
+ }
+ }
+ break;
+ case USB_PID_IN:
+ len = PTD_GET_COUNT(ptd);
+ BUG_ON(len > ep->length);
+ urb->actual_length += len;
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+ usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd));
+ /* if transfer completed or (allowed) data underrun */
+ if ((urb->transfer_buffer_length == urb->actual_length) ||
+ len % ep->maxpacket) {
+ DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
+ ep->num_req, len, ep->maxpacket, urbstat);
+ if (usb_pipecontrol(urb->pipe)) {
+ DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
+ ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT");
+ ep->nextpid = USB_PID_ACK;
+ } else {
+ urbstat = 0;
+ DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
+ __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
+ urbstat, len, ep->maxpacket, urb->actual_length);
+ }
+ }
+ break;
+ case USB_PID_SETUP:
+ if (urb->transfer_buffer_length == urb->actual_length) {
+ ep->nextpid = USB_PID_ACK;
+ } else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req,
+ urbstat);
+ WARN_ON(urbstat != -EINPROGRESS);
+ urbstat = 0;
+ ep->nextpid = 0;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ out:
+ if (urbstat != -EINPROGRESS) {
+ DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__,
+ ep, ep->num_req, urb, urbstat);
+ finish_request(isp1362_hcd, ep, urb, urbstat);
+ }
+}
+
+static void finish_unlinks(struct isp1362_hcd *isp1362_hcd)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) {
+ struct isp1362_ep_queue *epq =
+ get_ptd_queue(isp1362_hcd, ep->ptd_offset);
+ int index = ep->ptd_index;
+
+ BUG_ON(epq == NULL);
+ if (index >= 0) {
+ DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset);
+ BUG_ON(ep->num_ptds == 0);
+ release_ptd_buffers(epq, ep);
+ }
+ if (!list_empty(&ep->hep->urb_list)) {
+ struct urb *urb = get_urb(ep);
+
+ DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__,
+ ep->num_req, ep);
+ finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN);
+ }
+ WARN_ON(list_empty(&ep->active));
+ if (!list_empty(&ep->active)) {
+ list_del_init(&ep->active);
+ DBG(1, "%s: ep %p removed from active list\n", __func__, ep);
+ }
+ list_del_init(&ep->remove_list);
+ DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep);
+ }
+ DBG(1, "%s: Done\n", __func__);
+}
+
+static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count)
+{
+ if (count > 0) {
+ if (count < isp1362_hcd->atl_queue.ptd_count)
+ isp1362_write_reg16(isp1362_hcd, HCATLDTC, count);
+ isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ } else
+ isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
+}
+
+static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ isp1362_enable_int(isp1362_hcd, HCuPINT_INTL);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map);
+}
+
+static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip)
+{
+ isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ?
+ HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL);
+}
+
+static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
+ struct isp1362_ep *ep, struct isp1362_ep_queue *epq)
+{
+ int index = epq->free_ptd;
+
+ prepare_ptd(isp1362_hcd, urb, ep, epq, 0);
+ index = claim_ptd_buffers(epq, ep, ep->length);
+ if (index == -ENOMEM) {
+ DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__,
+ ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map);
+ return index;
+ } else if (index == -EOVERFLOW) {
+ DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n",
+ __func__, ep->num_req, ep->length, epq->name, ep->num_ptds,
+ epq->buf_map, epq->skip_map);
+ return index;
+ } else
+ BUG_ON(index < 0);
+ list_add_tail(&ep->active, &epq->active);
+ DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__,
+ ep, ep->num_req, ep->length, &epq->active);
+ DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name,
+ ep->ptd_offset, ep, ep->num_req);
+ isp1362_write_ptd(isp1362_hcd, ep, epq);
+ __clear_bit(ep->ptd_index, &epq->skip_map);
+
+ return 0;
+}
+
+static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue;
+ struct isp1362_ep *ep;
+ int defer = 0;
+
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
+ struct urb *urb = get_urb(ep);
+ int ret;
+
+ if (!list_empty(&ep->active)) {
+ DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep);
+ continue;
+ }
+
+ DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name,
+ ep, ep->num_req);
+
+ ret = submit_req(isp1362_hcd, urb, ep, epq);
+ if (ret == -ENOMEM) {
+ defer = 1;
+ break;
+ } else if (ret == -EOVERFLOW) {
+ defer = 1;
+ continue;
+ }
+#ifdef BUGGY_PXA2XX_UDC_USBTEST
+ defer = ep->nextpid == USB_PID_SETUP;
+#endif
+ ptd_count++;
+ }
+
+ /* Avoid starving of endpoints */
+ if (isp1362_hcd->async.next != isp1362_hcd->async.prev) {
+ DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count);
+ list_move(&isp1362_hcd->async, isp1362_hcd->async.next);
+ }
+ if (ptd_count || defer)
+ enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count);
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds) {
+ epq->stat_maxptds = epq->ptd_count;
+ DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds);
+ }
+}
+
+static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue;
+ struct isp1362_ep *ep;
+
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
+ struct urb *urb = get_urb(ep);
+ int ret;
+
+ if (!list_empty(&ep->active)) {
+ DBG(1, "%s: Skipping active %s ep %p\n", __func__,
+ epq->name, ep);
+ continue;
+ }
+
+ DBG(1, "%s: Processing %s ep %p req %d\n", __func__,
+ epq->name, ep, ep->num_req);
+ ret = submit_req(isp1362_hcd, urb, ep, epq);
+ if (ret == -ENOMEM)
+ break;
+ else if (ret == -EOVERFLOW)
+ continue;
+ ptd_count++;
+ }
+
+ if (ptd_count) {
+ static int last_count;
+
+ if (ptd_count != last_count) {
+ DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count);
+ last_count = ptd_count;
+ }
+ enable_intl_transfers(isp1362_hcd);
+ }
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds)
+ epq->stat_maxptds = epq->ptd_count;
+}
+
+static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
+{
+ u16 ptd_offset = ep->ptd_offset;
+ int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size;
+
+ DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset,
+ ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size);
+
+ ptd_offset += num_ptds * epq->blk_size;
+ if (ptd_offset < epq->buf_start + epq->buf_size)
+ return ptd_offset;
+ else
+ return -ENOMEM;
+}
+
+static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ int flip = isp1362_hcd->istl_flip;
+ struct isp1362_ep_queue *epq;
+ int ptd_offset;
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+ u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+
+ fill2:
+ epq = &isp1362_hcd->istl_queue[flip];
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ if (!list_empty(&epq->active))
+ return;
+
+ ptd_offset = epq->buf_start;
+ list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) {
+ struct urb *urb = get_urb(ep);
+ s16 diff = fno - (u16)urb->start_frame;
+
+ DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep);
+
+ if (diff > urb->number_of_packets) {
+ /* time frame for this URB has elapsed */
+ finish_request(isp1362_hcd, ep, urb, -EOVERFLOW);
+ continue;
+ } else if (diff < -1) {
+ /* URB is not due in this frame or the next one.
+ * Comparing with '-1' instead of '0' accounts for double
+ * buffering in the ISP1362 which enables us to queue the PTD
+ * one frame ahead of time
+ */
+ } else if (diff == -1) {
+ /* submit PTD's that are due in the next frame */
+ prepare_ptd(isp1362_hcd, urb, ep, epq, fno);
+ if (ptd_offset + PTD_HEADER_SIZE + ep->length >
+ epq->buf_start + epq->buf_size) {
+ pr_err("%s: Not enough ISO buffer space for %d byte PTD\n",
+ __func__, ep->length);
+ continue;
+ }
+ ep->ptd_offset = ptd_offset;
+ list_add_tail(&ep->active, &epq->active);
+
+ ptd_offset = next_ptd(epq, ep);
+ if (ptd_offset < 0) {
+ pr_warning("%s: req %d No more %s PTD buffers available\n", __func__,
+ ep->num_req, epq->name);
+ break;
+ }
+ }
+ }
+ list_for_each_entry(ep, &epq->active, active) {
+ if (epq->active.next == &ep->active)
+ ep->ptd.mps |= PTD_LAST_MSK;
+ isp1362_write_ptd(isp1362_hcd, ep, epq);
+ ptd_count++;
+ }
+
+ if (ptd_count)
+ enable_istl_transfers(isp1362_hcd, flip);
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds)
+ epq->stat_maxptds = epq->ptd_count;
+
+ /* check, whether the second ISTL buffer may also be filled */
+ if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) {
+ fno++;
+ ptd_count = 0;
+ flip = 1 - flip;
+ goto fill2;
+ }
+}
+
+static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map,
+ struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ if (list_empty(&epq->active)) {
+ DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
+ return;
+ }
+
+ DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map);
+
+ atomic_inc(&epq->finishing);
+ list_for_each_entry_safe(ep, tmp, &epq->active, active) {
+ int index = ep->ptd_index;
+
+ DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name,
+ index, ep->ptd_offset);
+
+ BUG_ON(index < 0);
+ if (__test_and_clear_bit(index, &done_map)) {
+ isp1362_read_ptd(isp1362_hcd, ep, epq);
+ epq->free_ptd = index;
+ BUG_ON(ep->num_ptds == 0);
+ release_ptd_buffers(epq, ep);
+
+ DBG(1, "%s: ep %p req %d removed from active list\n", __func__,
+ ep, ep->num_req);
+ if (!list_empty(&ep->remove_list)) {
+ list_del_init(&ep->remove_list);
+ DBG(1, "%s: ep %p removed from remove list\n", __func__, ep);
+ }
+ DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name,
+ ep, ep->num_req);
+ postproc_ep(isp1362_hcd, ep);
+ }
+ if (!done_map)
+ break;
+ }
+ if (done_map)
+ pr_warning("%s: done_map not clear: %08lx:%08lx\n", __func__, done_map,
+ epq->skip_map);
+ atomic_dec(&epq->finishing);
+}
+
+static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ if (list_empty(&epq->active)) {
+ DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
+ return;
+ }
+
+ DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name);
+
+ atomic_inc(&epq->finishing);
+ list_for_each_entry_safe(ep, tmp, &epq->active, active) {
+ DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset);
+
+ isp1362_read_ptd(isp1362_hcd, ep, epq);
+ DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep);
+ postproc_ep(isp1362_hcd, ep);
+ }
+ WARN_ON(epq->blk_size != 0);
+ atomic_dec(&epq->finishing);
+}
+
+static irqreturn_t isp1362_irq(struct usb_hcd *hcd)
+{
+ int handled = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u16 irqstat;
+ u16 svc_mask;
+
+ spin_lock(&isp1362_hcd->lock);
+
+ BUG_ON(isp1362_hcd->irq_active++);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT);
+ DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb);
+
+ /* only handle interrupts that are currently enabled */
+ irqstat &= isp1362_hcd->irqenb;
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat);
+ svc_mask = irqstat;
+
+ if (irqstat & HCuPINT_SOF) {
+ isp1362_hcd->irqenb &= ~HCuPINT_SOF;
+ isp1362_hcd->irq_stat[ISP1362_INT_SOF]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_SOF;
+ DBG(3, "%s: SOF\n", __func__);
+ isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ if (!list_empty(&isp1362_hcd->remove_list))
+ finish_unlinks(isp1362_hcd);
+ if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) {
+ if (list_empty(&isp1362_hcd->atl_queue.active)) {
+ start_atl_transfers(isp1362_hcd);
+ } else {
+ isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP,
+ isp1362_hcd->atl_queue.skip_map);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ }
+ }
+ }
+
+ if (irqstat & HCuPINT_ISTL0) {
+ isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_ISTL0;
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL);
+ DBG(1, "%s: ISTL0\n", __func__);
+ WARN_ON((int)!!isp1362_hcd->istl_flip);
+ WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ HCBUFSTAT_ISTL0_ACTIVE);
+ WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ HCBUFSTAT_ISTL0_DONE));
+ isp1362_hcd->irqenb &= ~HCuPINT_ISTL0;
+ }
+
+ if (irqstat & HCuPINT_ISTL1) {
+ isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_ISTL1;
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL);
+ DBG(1, "%s: ISTL1\n", __func__);
+ WARN_ON(!(int)isp1362_hcd->istl_flip);
+ WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ HCBUFSTAT_ISTL1_ACTIVE);
+ WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ HCBUFSTAT_ISTL1_DONE));
+ isp1362_hcd->irqenb &= ~HCuPINT_ISTL1;
+ }
+
+ if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) {
+ WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) ==
+ (HCuPINT_ISTL0 | HCuPINT_ISTL1));
+ finish_iso_transfers(isp1362_hcd,
+ &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]);
+ start_iso_transfers(isp1362_hcd);
+ isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip;
+ }
+
+ if (irqstat & HCuPINT_INTL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
+ u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP);
+ isp1362_hcd->irq_stat[ISP1362_INT_INTL]++;
+
+ DBG(2, "%s: INTL\n", __func__);
+
+ svc_mask &= ~HCuPINT_INTL;
+
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map);
+ if (~(done_map | skip_map) == 0)
+ /* All PTDs are finished, disable INTL processing entirely */
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+
+ handled = 1;
+ WARN_ON(!done_map);
+ if (done_map) {
+ DBG(3, "%s: INTL done_map %08x\n", __func__, done_map);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
+ start_intl_transfers(isp1362_hcd);
+ }
+ }
+
+ if (irqstat & HCuPINT_ATL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
+ u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP);
+ isp1362_hcd->irq_stat[ISP1362_INT_ATL]++;
+
+ DBG(2, "%s: ATL\n", __func__);
+
+ svc_mask &= ~HCuPINT_ATL;
+
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map);
+ if (~(done_map | skip_map) == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ if (done_map) {
+ DBG(3, "%s: ATL done_map %08x\n", __func__, done_map);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
+ start_atl_transfers(isp1362_hcd);
+ }
+ handled = 1;
+ }
+
+ if (irqstat & HCuPINT_OPR) {
+ u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT);
+ isp1362_hcd->irq_stat[ISP1362_INT_OPR]++;
+
+ svc_mask &= ~HCuPINT_OPR;
+ DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb);
+ intstat &= isp1362_hcd->intenb;
+ if (intstat & OHCI_INTR_UE) {
+ pr_err("Unrecoverable error\n");
+ /* FIXME: do here reset or cleanup or whatever */
+ }
+ if (intstat & OHCI_INTR_RHSC) {
+ isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS);
+ isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1);
+ isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2);
+ }
+ if (intstat & OHCI_INTR_RD) {
+ pr_info("%s: RESUME DETECTED\n", __func__);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ usb_hcd_resume_root_hub(hcd);
+ }
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat);
+ irqstat &= ~HCuPINT_OPR;
+ handled = 1;
+ }
+
+ if (irqstat & HCuPINT_SUSP) {
+ isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_SUSP;
+
+ pr_info("%s: SUSPEND IRQ\n", __func__);
+ }
+
+ if (irqstat & HCuPINT_CLKRDY) {
+ isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++;
+ handled = 1;
+ isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY;
+ svc_mask &= ~HCuPINT_CLKRDY;
+ pr_info("%s: CLKRDY IRQ\n", __func__);
+ }
+
+ if (svc_mask)
+ pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+ isp1362_hcd->irq_active--;
+ spin_unlock(&isp1362_hcd->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */
+static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that interval
+ * which has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < interval; i++) {
+ if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += interval) {
+ if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) {
+ pr_err("%s: new load %d load[%02x] %d max %d\n", __func__,
+ load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD);
+ break;
+ }
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/* NB! ALL the code above this point runs with isp1362_hcd->lock
+ held, irqs off
+*/
+
+/*-------------------------------------------------------------------------*/
+
+static int isp1362_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct usb_host_endpoint *hep = urb->ep;
+ struct isp1362_ep *ep = NULL;
+ unsigned long flags;
+ int retval = 0;
+
+ DBG(3, "%s: urb %p\n", __func__, urb);
+
+ if (type == PIPE_ISOCHRONOUS) {
+ pr_err("Isochronous transfers not supported\n");
+ return -ENOSPC;
+ }
+
+ URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__,
+ usb_pipedevice(pipe), epnum,
+ is_out ? "out" : "in",
+ usb_pipecontrol(pipe) ? "ctrl" :
+ usb_pipeint(pipe) ? "int" :
+ usb_pipebulk(pipe) ? "bulk" :
+ "iso",
+ urb->transfer_buffer_length,
+ (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "",
+ !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
+ "short_ok" : "");
+
+ /* avoid all allocations within spinlocks: request or endpoint */
+ if (!hep->hcpriv) {
+ ep = kzalloc(sizeof *ep, mem_flags);
+ if (!ep)
+ return -ENOMEM;
+ }
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ /* don't submit to a dead or disabled port */
+ if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) &
+ USB_PORT_STAT_ENABLE) ||
+ !HC_IS_RUNNING(hcd->state)) {
+ kfree(ep);
+ retval = -ENODEV;
+ goto fail_not_linked;
+ }
+
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval) {
+ kfree(ep);
+ goto fail_not_linked;
+ }
+
+ if (hep->hcpriv) {
+ ep = hep->hcpriv;
+ } else {
+ INIT_LIST_HEAD(&ep->schedule);
+ INIT_LIST_HEAD(&ep->active);
+ INIT_LIST_HEAD(&ep->remove_list);
+ ep->udev = usb_get_dev(udev);
+ ep->hep = hep;
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ ep->ptd_offset = -EINVAL;
+ ep->ptd_index = -EINVAL;
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL)
+ ep->nextpid = USB_PID_SETUP;
+ else if (is_out)
+ ep->nextpid = USB_PID_OUT;
+ else
+ ep->nextpid = USB_PID_IN;
+
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ if (urb->interval > PERIODIC_SIZE)
+ urb->interval = PERIODIC_SIZE;
+ ep->interval = urb->interval;
+ ep->branch = PERIODIC_SIZE;
+ ep->load = usb_calc_bus_time(udev->speed, !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe, is_out)) / 1000;
+ break;
+ }
+ hep->hcpriv = ep;
+ }
+ ep->num_req = isp1362_hcd->req_serial++;
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule)) {
+ DBG(1, "%s: Adding ep %p req %d to async schedule\n",
+ __func__, ep, ep->num_req);
+ list_add_tail(&ep->schedule, &isp1362_hcd->async);
+ }
+ break;
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ urb->interval = ep->interval;
+
+ /* urb submitted for already existing EP */
+ if (ep->branch < PERIODIC_SIZE)
+ break;
+
+ retval = balance(isp1362_hcd, ep->interval, ep->load);
+ if (retval < 0) {
+ pr_err("%s: balance returned %d\n", __func__, retval);
+ goto fail;
+ }
+ ep->branch = retval;
+ retval = 0;
+ isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n",
+ __func__, isp1362_hcd->fmindex, ep->branch,
+ ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) &
+ ~(PERIODIC_SIZE - 1)) + ep->branch,
+ (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch);
+
+ if (list_empty(&ep->schedule)) {
+ if (type == PIPE_ISOCHRONOUS) {
+ u16 frame = isp1362_hcd->fmindex;
+
+ frame += max_t(u16, 8, ep->interval);
+ frame &= ~(ep->interval - 1);
+ frame |= ep->branch;
+ if (frame_before(frame, isp1362_hcd->fmindex))
+ frame += ep->interval;
+ urb->start_frame = frame;
+
+ DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep);
+ list_add_tail(&ep->schedule, &isp1362_hcd->isoc);
+ } else {
+ DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep);
+ list_add_tail(&ep->schedule, &isp1362_hcd->periodic);
+ }
+ } else
+ DBG(1, "%s: ep %p already scheduled\n", __func__, ep);
+
+ DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__,
+ ep->load / ep->interval, isp1362_hcd->load[ep->branch],
+ isp1362_hcd->load[ep->branch] + ep->load);
+ isp1362_hcd->load[ep->branch] += ep->load;
+ }
+
+ urb->hcpriv = hep;
+ ALIGNSTAT(isp1362_hcd, urb->transfer_buffer);
+
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ start_atl_transfers(isp1362_hcd);
+ break;
+ case PIPE_INTERRUPT:
+ start_intl_transfers(isp1362_hcd);
+ break;
+ case PIPE_ISOCHRONOUS:
+ start_iso_transfers(isp1362_hcd);
+ break;
+ default:
+ BUG();
+ }
+ fail:
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+
+ fail_not_linked:
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (retval)
+ DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval);
+ return retval;
+}
+
+static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct usb_host_endpoint *hep;
+ unsigned long flags;
+ struct isp1362_ep *ep;
+ int retval = 0;
+
+ DBG(3, "%s: urb %p\n", __func__, urb);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval)
+ goto done;
+
+ hep = urb->hcpriv;
+
+ if (!hep) {
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return -EIDRM;
+ }
+
+ ep = hep->hcpriv;
+ if (ep) {
+ /* In front of queue? */
+ if (ep->hep->urb_list.next == &urb->urb_list) {
+ if (!list_empty(&ep->active)) {
+ DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__,
+ urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
+ /* disable processing and queue PTD for removal */
+ remove_ptd(isp1362_hcd, ep);
+ urb = NULL;
+ }
+ }
+ if (urb) {
+ DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep,
+ ep->num_req);
+ finish_request(isp1362_hcd, ep, urb, status);
+ } else
+ DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb);
+ } else {
+ pr_warning("%s: No EP in URB %p\n", __func__, urb);
+ retval = -EINVAL;
+ }
+done:
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ DBG(3, "%s: exit\n", __func__);
+
+ return retval;
+}
+
+static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+ struct isp1362_ep *ep = hep->hcpriv;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ DBG(1, "%s: ep %p\n", __func__, ep);
+ if (!ep)
+ return;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ if (!list_empty(&hep->urb_list)) {
+ if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) {
+ DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__,
+ ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
+ remove_ptd(isp1362_hcd, ep);
+ pr_info("%s: Waiting for Interrupt to clean up\n", __func__);
+ }
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ /* Wait for interrupt to clear out active list */
+ while (!list_empty(&ep->active))
+ msleep(1);
+
+ DBG(1, "%s: Freeing EP %p\n", __func__, ep);
+
+ usb_put_dev(ep->udev);
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int isp1362_get_frame(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u32 fmnum;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return (int)fmnum;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Adapted from ohci-hub.c */
+static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ int ports, i, changed = 0;
+ unsigned long flags;
+
+ if (!HC_IS_RUNNING(hcd->state))
+ return -ESHUTDOWN;
+
+ /* Report no status change now, if we are scheduled to be
+ called later */
+ if (timer_pending(&hcd->rh_timer))
+ return 0;
+
+ ports = isp1362_hcd->rhdesca & RH_A_NDP;
+ BUG_ON(ports > 2);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* init status */
+ if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
+ buf[0] = changed = 1;
+ else
+ buf[0] = 0;
+
+ for (i = 0; i < ports; i++) {
+ u32 status = isp1362_hcd->rhport[i];
+
+ if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
+ RH_PS_OCIC | RH_PS_PRSC)) {
+ changed = 1;
+ buf[0] |= 1 << (i + 1);
+ continue;
+ }
+
+ if (!(status & RH_PS_CCS))
+ continue;
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return changed;
+}
+
+static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
+ struct usb_hub_descriptor *desc)
+{
+ u32 reg = isp1362_hcd->rhdesca;
+
+ DBG(3, "%s: enter\n", __func__);
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bDescLength = 9;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = reg & 0x3;
+ /* Power switching, device type, overcurrent. */
+ desc->wHubCharacteristics = cpu_to_le16((reg >> 8) &
+ (HUB_CHAR_LPSM |
+ HUB_CHAR_COMPOUND |
+ HUB_CHAR_OCPM));
+ DBG(0, "%s: hubcharacteristics = %02x\n", __func__,
+ desc->wHubCharacteristics);
+ desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+
+ DBG(3, "%s: exit\n", __func__);
+}
+
+/* Adapted from ohci-hub.c */
+static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ int retval = 0;
+ unsigned long flags;
+ unsigned long t1;
+ int ports = isp1362_hcd->rhdesca & RH_A_NDP;
+ u32 tmp = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ DBG(0, "ClearHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ DBG(0, "C_HUB_OVER_CURRENT\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ case C_HUB_LOCAL_POWER:
+ DBG(0, "C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetHubFeature:
+ DBG(0, "SetHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ DBG(0, "GetHubDescriptor\n");
+ isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ DBG(0, "GetHubStatus\n");
+ put_unaligned(cpu_to_le32(0), (__le32 *) buf);
+ break;
+ case GetPortStatus:
+#ifndef VERBOSE
+ DBG(0, "GetPortStatus\n");
+#endif
+ if (!wIndex || wIndex > ports)
+ goto error;
+ tmp = isp1362_hcd->rhport[--wIndex];
+ put_unaligned(cpu_to_le32(tmp), (__le32 *) buf);
+ break;
+ case ClearPortFeature:
+ DBG(0, "ClearPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ DBG(0, "USB_PORT_FEAT_ENABLE\n");
+ tmp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ DBG(0, "USB_PORT_FEAT_C_ENABLE\n");
+ tmp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ DBG(0, "USB_PORT_FEAT_SUSPEND\n");
+ tmp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ DBG(0, "USB_PORT_FEAT_C_SUSPEND\n");
+ tmp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ DBG(0, "USB_PORT_FEAT_POWER\n");
+ tmp = RH_PS_LSDA;
+
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ DBG(0, "USB_PORT_FEAT_C_CONNECTION\n");
+ tmp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n");
+ tmp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ DBG(0, "USB_PORT_FEAT_C_RESET\n");
+ tmp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case SetPortFeature:
+ DBG(0, "SetPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ DBG(0, "USB_PORT_FEAT_SUSPEND\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case USB_PORT_FEAT_POWER:
+ DBG(0, "USB_PORT_FEAT_POWER\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case USB_PORT_FEAT_RESET:
+ DBG(0, "USB_PORT_FEAT_RESET\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH);
+ while (time_before(jiffies, t1)) {
+ /* spin until any current reset finishes */
+ for (;;) {
+ tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ if (!(tmp & RH_PS_PRS))
+ break;
+ udelay(500);
+ }
+ if (!(tmp & RH_PS_CCS))
+ break;
+ /* Reset lasts 10ms (claims datasheet) */
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS));
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ }
+
+ isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd,
+ HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+ error:
+ /* "protocol stall" on error */
+ DBG(0, "PROTOCOL STALL\n");
+ retval = -EPIPE;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int isp1362_bus_suspend(struct usb_hcd *hcd)
+{
+ int status = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ if (time_before(jiffies, isp1362_hcd->next_statechange))
+ msleep(5);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESUME:
+ DBG(0, "%s: resume/suspend?\n", __func__);
+ isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
+ isp1362_hcd->hc_control |= OHCI_USB_RESET;
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ /* FALL THROUGH */
+ case OHCI_USB_RESET:
+ status = -EBUSY;
+ pr_warning("%s: needs reinit!\n", __func__);
+ goto done;
+ case OHCI_USB_SUSPEND:
+ pr_warning("%s: already suspended?\n", __func__);
+ goto done;
+ }
+ DBG(0, "%s: suspend root hub\n", __func__);
+
+ /* First stop any processing */
+ hcd->state = HC_STATE_QUIESCING;
+ if (!list_empty(&isp1362_hcd->atl_queue.active) ||
+ !list_empty(&isp1362_hcd->intl_queue.active) ||
+ !list_empty(&isp1362_hcd->istl_queue[0] .active) ||
+ !list_empty(&isp1362_hcd->istl_queue[1] .active)) {
+ int limit;
+
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
+ isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF);
+
+ DBG(0, "%s: stopping schedules ...\n", __func__);
+ limit = 2000;
+ while (limit > 0) {
+ udelay(250);
+ limit -= 250;
+ if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF)
+ break;
+ }
+ mdelay(7);
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
+ }
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
+ }
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0)
+ finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]);
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1)
+ finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]);
+ }
+ DBG(0, "%s: HCINTSTAT: %08x\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT,
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+
+ /* Suspend hub */
+ isp1362_hcd->hc_control = OHCI_USB_SUSPEND;
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+
+#if 1
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) {
+ pr_err("%s: controller won't suspend %08x\n", __func__,
+ isp1362_hcd->hc_control);
+ status = -EBUSY;
+ } else
+#endif
+ {
+ /* no resumes until devices finish suspending */
+ isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5);
+ }
+done:
+ if (status == 0) {
+ hcd->state = HC_STATE_SUSPENDED;
+ DBG(0, "%s: HCD suspended: %08x\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return status;
+}
+
+static int isp1362_bus_resume(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u32 port;
+ unsigned long flags;
+ int status = -EINPROGRESS;
+
+ if (time_before(jiffies, isp1362_hcd->next_statechange))
+ msleep(5);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control);
+ if (hcd->state == HC_STATE_RESUMING) {
+ pr_warning("%s: duplicate resume\n", __func__);
+ status = 0;
+ } else
+ switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_SUSPEND:
+ DBG(0, "%s: resume root hub\n", __func__);
+ isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
+ isp1362_hcd->hc_control |= OHCI_USB_RESUME;
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ break;
+ case OHCI_USB_RESUME:
+ /* HCFS changes sometime after INTR_RD */
+ DBG(0, "%s: remote wakeup\n", __func__);
+ break;
+ case OHCI_USB_OPER:
+ DBG(0, "%s: odd resume\n", __func__);
+ status = 0;
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ break;
+ default: /* RESET, we lost power */
+ DBG(0, "%s: root hub hardware reset\n", __func__);
+ status = -EBUSY;
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (status == -EBUSY) {
+ DBG(0, "%s: Restarting HC\n", __func__);
+ isp1362_hc_stop(hcd);
+ return isp1362_hc_start(hcd);
+ }
+ if (status != -EINPROGRESS)
+ return status;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP;
+ while (port--) {
+ u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port);
+
+ /* force global, not selective, resume */
+ if (!(stat & RH_PS_PSS)) {
+ DBG(0, "%s: Not Resuming RH port %d\n", __func__, port);
+ continue;
+ }
+ DBG(0, "%s: Resuming RH port %d\n", __func__, port);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI);
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ /* Some controllers (lucent) need extra-long delays */
+ hcd->state = HC_STATE_RESUMING;
+ mdelay(20 /* usb 11.5.1.10 */ + 15);
+
+ isp1362_hcd->hc_control = OHCI_USB_OPER;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ /* TRSMRCY */
+ msleep(10);
+
+ /* keep it alive for ~5x suspend + resume costs */
+ isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250);
+
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+}
+#else
+#define isp1362_bus_suspend NULL
+#define isp1362_bus_resume NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void dump_irq(struct seq_file *s, char *label, u16 mask)
+{
+ seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask,
+ mask & HCuPINT_CLKRDY ? " clkrdy" : "",
+ mask & HCuPINT_SUSP ? " susp" : "",
+ mask & HCuPINT_OPR ? " opr" : "",
+ mask & HCuPINT_EOT ? " eot" : "",
+ mask & HCuPINT_ATL ? " atl" : "",
+ mask & HCuPINT_SOF ? " sof" : "");
+}
+
+static void dump_int(struct seq_file *s, char *label, u32 mask)
+{
+ seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask,
+ mask & OHCI_INTR_MIE ? " MIE" : "",
+ mask & OHCI_INTR_RHSC ? " rhsc" : "",
+ mask & OHCI_INTR_FNO ? " fno" : "",
+ mask & OHCI_INTR_UE ? " ue" : "",
+ mask & OHCI_INTR_RD ? " rd" : "",
+ mask & OHCI_INTR_SF ? " sof" : "",
+ mask & OHCI_INTR_SO ? " so" : "");
+}
+
+static void dump_ctrl(struct seq_file *s, char *label, u32 mask)
+{
+ seq_printf(s, "%-15s %08x%s%s%s\n", label, mask,
+ mask & OHCI_CTRL_RWC ? " rwc" : "",
+ mask & OHCI_CTRL_RWE ? " rwe" : "",
+ ({
+ char *hcfs;
+ switch (mask & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ hcfs = " oper";
+ break;
+ case OHCI_USB_RESET:
+ hcfs = " reset";
+ break;
+ case OHCI_USB_RESUME:
+ hcfs = " resume";
+ break;
+ case OHCI_USB_SUSPEND:
+ hcfs = " suspend";
+ break;
+ default:
+ hcfs = " ?";
+ }
+ hcfs;
+ }));
+}
+
+static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd)
+{
+ seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION),
+ isp1362_read_reg32(isp1362_hcd, HCREVISION));
+ seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL),
+ isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+ seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT),
+ isp1362_read_reg32(isp1362_hcd, HCCMDSTAT));
+ seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT),
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB),
+ isp1362_read_reg32(isp1362_hcd, HCINTENB));
+ seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL),
+ isp1362_read_reg32(isp1362_hcd, HCFMINTVL));
+ seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM),
+ isp1362_read_reg32(isp1362_hcd, HCFMREM));
+ seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM),
+ isp1362_read_reg32(isp1362_hcd, HCFMNUM));
+ seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH),
+ isp1362_read_reg32(isp1362_hcd, HCLSTHRESH));
+ seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA),
+ isp1362_read_reg32(isp1362_hcd, HCRHDESCA));
+ seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB),
+ isp1362_read_reg32(isp1362_hcd, HCRHDESCB));
+ seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS),
+ isp1362_read_reg32(isp1362_hcd, HCRHSTATUS));
+ seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1),
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1));
+ seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2),
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT2));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG),
+ isp1362_read_reg16(isp1362_hcd, HCHWCFG));
+ seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG),
+ isp1362_read_reg16(isp1362_hcd, HCDMACFG));
+ seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR),
+ isp1362_read_reg16(isp1362_hcd, HCXFERCTR));
+ seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT),
+ isp1362_read_reg16(isp1362_hcd, HCuPINT));
+ seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB),
+ isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
+ seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID),
+ isp1362_read_reg16(isp1362_hcd, HCCHIPID));
+ seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH),
+ isp1362_read_reg16(isp1362_hcd, HCSCRATCH));
+ seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT),
+ isp1362_read_reg16(isp1362_hcd, HCBUFSTAT));
+ seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR),
+ isp1362_read_reg32(isp1362_hcd, HCDIRADDR));
+#if 0
+ seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA),
+ isp1362_read_reg16(isp1362_hcd, HCDIRDATA));
+#endif
+ seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ));
+ seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE),
+ isp1362_read_reg16(isp1362_hcd, HCISTLRATE));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ));
+ seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ),
+ isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ));
+ seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE),
+ isp1362_read_reg32(isp1362_hcd, HCINTLDONE));
+ seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP),
+ isp1362_read_reg32(isp1362_hcd, HCINTLSKIP));
+ seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST),
+ isp1362_read_reg32(isp1362_hcd, HCINTLLAST));
+ seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR),
+ isp1362_read_reg16(isp1362_hcd, HCINTLCURR));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ));
+ seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ),
+ isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ));
+#if 0
+ seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE),
+ isp1362_read_reg32(isp1362_hcd, HCATLDONE));
+#endif
+ seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP),
+ isp1362_read_reg32(isp1362_hcd, HCATLSKIP));
+ seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST),
+ isp1362_read_reg32(isp1362_hcd, HCATLLAST));
+ seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR),
+ isp1362_read_reg16(isp1362_hcd, HCATLCURR));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC),
+ isp1362_read_reg16(isp1362_hcd, HCATLDTC));
+ seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO),
+ isp1362_read_reg16(isp1362_hcd, HCATLDTCTO));
+}
+
+static int isp1362_show(struct seq_file *s, void *unused)
+{
+ struct isp1362_hcd *isp1362_hcd = s->private;
+ struct isp1362_ep *ep;
+ int i;
+
+ seq_printf(s, "%s\n%s version %s\n",
+ isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION);
+
+ /* collect statistics to help estimate potential win for
+ * DMA engines that care about alignment (PXA)
+ */
+ seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n",
+ isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4,
+ isp1362_hcd->stat2, isp1362_hcd->stat1);
+ seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds);
+ seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds);
+ seq_printf(s, "max # ptds in ISTL fifo: %d\n",
+ max(isp1362_hcd->istl_queue[0] .stat_maxptds,
+ isp1362_hcd->istl_queue[1] .stat_maxptds));
+
+ /* FIXME: don't show the following in suspended state */
+ spin_lock_irq(&isp1362_hcd->lock);
+
+ dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
+ dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT));
+ dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB));
+ dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+
+ for (i = 0; i < NUM_ISP1362_IRQS; i++)
+ if (isp1362_hcd->irq_stat[i])
+ seq_printf(s, "%-15s: %d\n",
+ ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]);
+
+ dump_regs(s, isp1362_hcd);
+ list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
+ struct urb *urb;
+
+ seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum,
+ ({
+ char *s;
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ s = "in";
+ break;
+ case USB_PID_OUT:
+ s = "out";
+ break;
+ case USB_PID_SETUP:
+ s = "setup";
+ break;
+ case USB_PID_ACK:
+ s = "status";
+ break;
+ default:
+ s = "?";
+ break;
+ }
+ s;}), ep->maxpacket) ;
+ list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
+ seq_printf(s, " urb%p, %d/%d\n", urb,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ }
+ if (!list_empty(&isp1362_hcd->async))
+ seq_printf(s, "\n");
+ dump_ptd_queue(&isp1362_hcd->atl_queue);
+
+ seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+ list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
+ seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch,
+ isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset);
+
+ seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
+ ep->interval, ep,
+ (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? "" :
+ ((ep->nextpid == USB_PID_IN) ?
+ "in" : "out"), ep->maxpacket);
+ }
+ dump_ptd_queue(&isp1362_hcd->intl_queue);
+
+ seq_printf(s, "ISO:\n");
+
+ list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) {
+ seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
+ ep->interval, ep,
+ (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? "" :
+ ((ep->nextpid == USB_PID_IN) ?
+ "in" : "out"), ep->maxpacket);
+ }
+
+ spin_unlock_irq(&isp1362_hcd->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int isp1362_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, isp1362_show, inode);
+}
+
+static const struct file_operations debug_ops = {
+ .open = isp1362_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* expect just one isp1362_hcd per system */
+static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+ isp1362_hcd->debug_file = debugfs_create_file("isp1362", S_IRUGO,
+ usb_debug_root,
+ isp1362_hcd, &debug_ops);
+}
+
+static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+ debugfs_remove(isp1362_hcd->debug_file);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
+{
+ int tmp = 20;
+
+ isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC);
+ isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR);
+ while (--tmp) {
+ mdelay(1);
+ if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR))
+ break;
+ }
+ if (!tmp)
+ pr_err("Software reset timeout\n");
+}
+
+static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ __isp1362_sw_reset(isp1362_hcd);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+}
+
+static int isp1362_mem_config(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ u32 total;
+ u16 istl_size = ISP1362_ISTL_BUFSIZE;
+ u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE;
+ u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize;
+ u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE;
+ u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize;
+ u16 atl_size;
+ int i;
+
+ WARN_ON(istl_size & 3);
+ WARN_ON(atl_blksize & 3);
+ WARN_ON(intl_blksize & 3);
+ WARN_ON(atl_blksize < PTD_HEADER_SIZE);
+ WARN_ON(intl_blksize < PTD_HEADER_SIZE);
+
+ BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32);
+ if (atl_buffers > 32)
+ atl_buffers = 32;
+ atl_size = atl_buffers * atl_blksize;
+ total = atl_size + intl_size + istl_size;
+ dev_info(hcd->self.controller, "ISP1362 Memory usage:\n");
+ dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n",
+ istl_size / 2, istl_size, 0, istl_size / 2);
+ dev_info(hcd->self.controller, " INTL: %4d * (%3zu+8): %4d @ $%04x\n",
+ ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE,
+ intl_size, istl_size);
+ dev_info(hcd->self.controller, " ATL : %4d * (%3zu+8): %4d @ $%04x\n",
+ atl_buffers, atl_blksize - PTD_HEADER_SIZE,
+ atl_size, istl_size + intl_size);
+ dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total,
+ ISP1362_BUF_SIZE - total);
+
+ if (total > ISP1362_BUF_SIZE) {
+ dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n",
+ __func__, total, ISP1362_BUF_SIZE);
+ return -ENOMEM;
+ }
+
+ total = istl_size + intl_size + atl_size;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ for (i = 0; i < 2; i++) {
+ isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2,
+ isp1362_hcd->istl_queue[i].buf_size = istl_size / 2;
+ isp1362_hcd->istl_queue[i].blk_size = 4;
+ INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active);
+ snprintf(isp1362_hcd->istl_queue[i].name,
+ sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i);
+ DBG(3, "%s: %5s buf $%04x %d\n", __func__,
+ isp1362_hcd->istl_queue[i].name,
+ isp1362_hcd->istl_queue[i].buf_start,
+ isp1362_hcd->istl_queue[i].buf_size);
+ }
+ isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2);
+
+ isp1362_hcd->intl_queue.buf_start = istl_size;
+ isp1362_hcd->intl_queue.buf_size = intl_size;
+ isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS;
+ isp1362_hcd->intl_queue.blk_size = intl_blksize;
+ isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count;
+ isp1362_hcd->intl_queue.skip_map = ~0;
+ INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active);
+
+ isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ,
+ isp1362_hcd->intl_queue.buf_size);
+ isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ,
+ isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCINTLLAST,
+ 1 << (ISP1362_INTL_BUFFERS - 1));
+
+ isp1362_hcd->atl_queue.buf_start = istl_size + intl_size;
+ isp1362_hcd->atl_queue.buf_size = atl_size;
+ isp1362_hcd->atl_queue.buf_count = atl_buffers;
+ isp1362_hcd->atl_queue.blk_size = atl_blksize;
+ isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count;
+ isp1362_hcd->atl_queue.skip_map = ~0;
+ INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active);
+
+ isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ,
+ isp1362_hcd->atl_queue.buf_size);
+ isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ,
+ isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCATLLAST,
+ 1 << (atl_buffers - 1));
+
+ snprintf(isp1362_hcd->atl_queue.name,
+ sizeof(isp1362_hcd->atl_queue.name), "ATL");
+ snprintf(isp1362_hcd->intl_queue.name,
+ sizeof(isp1362_hcd->intl_queue.name), "INTL");
+ DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
+ isp1362_hcd->intl_queue.name,
+ isp1362_hcd->intl_queue.buf_start,
+ ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size,
+ isp1362_hcd->intl_queue.buf_size);
+ DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
+ isp1362_hcd->atl_queue.name,
+ isp1362_hcd->atl_queue.buf_start,
+ atl_buffers, isp1362_hcd->atl_queue.blk_size,
+ isp1362_hcd->atl_queue.buf_size);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return 0;
+}
+
+static int isp1362_hc_reset(struct usb_hcd *hcd)
+{
+ int ret = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long t;
+ unsigned long timeout = 100;
+ unsigned long flags;
+ int clkrdy = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (isp1362_hcd->board && isp1362_hcd->board->reset) {
+ isp1362_hcd->board->reset(hcd->self.controller, 1);
+ msleep(20);
+ if (isp1362_hcd->board->clock)
+ isp1362_hcd->board->clock(hcd->self.controller, 1);
+ isp1362_hcd->board->reset(hcd->self.controller, 0);
+ } else
+ isp1362_sw_reset(isp1362_hcd);
+
+ /* chip has been reset. First we need to see a clock */
+ t = jiffies + msecs_to_jiffies(timeout);
+ while (!clkrdy && time_before_eq(jiffies, t)) {
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY;
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (!clkrdy)
+ msleep(4);
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (!clkrdy) {
+ pr_err("Clock not ready after %lums\n", timeout);
+ ret = -ENODEV;
+ }
+ return ret;
+}
+
+static void isp1362_hc_stop(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ u32 tmp;
+
+ pr_debug("%s:\n", __func__);
+
+ del_timer_sync(&hcd->rh_timer);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ /* Switch off power for all ports */
+ tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
+ tmp &= ~(RH_A_NPS | RH_A_PSM);
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
+
+ /* Reset the chip */
+ if (isp1362_hcd->board && isp1362_hcd->board->reset)
+ isp1362_hcd->board->reset(hcd->self.controller, 1);
+ else
+ __isp1362_sw_reset(isp1362_hcd);
+
+ if (isp1362_hcd->board && isp1362_hcd->board->clock)
+ isp1362_hcd->board->clock(hcd->self.controller, 0);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+}
+
+#ifdef CHIP_BUFFER_TEST
+static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
+{
+ int ret = 0;
+ u16 *ref;
+ unsigned long flags;
+
+ ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL);
+ if (ref) {
+ int offset;
+ u16 *tst = &ref[ISP1362_BUF_SIZE / 2];
+
+ for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) {
+ ref[offset] = ~offset;
+ tst[offset] = offset;
+ }
+
+ for (offset = 0; offset < 4; offset++) {
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j);
+ isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if (memcmp(ref, tst, j)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check with %d byte offset %d failed\n",
+ __func__, j, offset);
+ dump_data((u8 *)ref + offset, j);
+ dump_data((u8 *)tst + offset, j);
+ }
+ }
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE);
+ isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if (memcmp(ref, tst, ISP1362_BUF_SIZE)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check failed\n", __func__);
+ dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2);
+ }
+
+ for (offset = 0; offset < 256; offset++) {
+ int test_size = 0;
+
+ yield();
+
+ memset(tst, 0, ISP1362_BUF_SIZE);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))),
+ ISP1362_BUF_SIZE / 2)) {
+ pr_err("%s: Failed to clear buffer\n", __func__);
+ dump_data((u8 *)tst, ISP1362_BUF_SIZE);
+ break;
+ }
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE);
+ isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref),
+ offset * 2 + PTD_HEADER_SIZE, test_size);
+ isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
+ PTD_HEADER_SIZE + test_size);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
+ dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size);
+ dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
+ PTD_HEADER_SIZE + test_size);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check with offset %02x failed\n",
+ __func__, offset);
+ break;
+ }
+ pr_warning("%s: memory check with offset %02x ok after second read\n",
+ __func__, offset);
+ }
+ }
+ kfree(ref);
+ }
+ return ret;
+}
+#endif
+
+static int isp1362_hc_start(struct usb_hcd *hcd)
+{
+ int ret;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct isp1362_platform_data *board = isp1362_hcd->board;
+ u16 hwcfg;
+ u16 chipid;
+ unsigned long flags;
+
+ pr_debug("%s:\n", __func__);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+ pr_err("%s: Invalid chip ID %04x\n", __func__, chipid);
+ return -ENODEV;
+ }
+
+#ifdef CHIP_BUFFER_TEST
+ ret = isp1362_chip_test(isp1362_hcd);
+ if (ret)
+ return -ENODEV;
+#endif
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* clear interrupt status and disable all interrupt sources */
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ /* HW conf */
+ hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
+ if (board->sel15Kres)
+ hwcfg |= HCHWCFG_PULLDOWN_DS2 |
+ ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0);
+ if (board->clknotstop)
+ hwcfg |= HCHWCFG_CLKNOTSTOP;
+ if (board->oc_enable)
+ hwcfg |= HCHWCFG_ANALOG_OC;
+ if (board->int_act_high)
+ hwcfg |= HCHWCFG_INT_POL;
+ if (board->int_edge_triggered)
+ hwcfg |= HCHWCFG_INT_TRIGGER;
+ if (board->dreq_act_high)
+ hwcfg |= HCHWCFG_DREQ_POL;
+ if (board->dack_act_high)
+ hwcfg |= HCHWCFG_DACK_POL;
+ isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg);
+ isp1362_show_reg(isp1362_hcd, HCHWCFG);
+ isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ ret = isp1362_mem_config(hcd);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ /* Root hub conf */
+ isp1362_hcd->rhdesca = 0;
+ if (board->no_power_switching)
+ isp1362_hcd->rhdesca |= RH_A_NPS;
+ if (board->power_switching_mode)
+ isp1362_hcd->rhdesca |= RH_A_PSM;
+ if (board->potpg)
+ isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT;
+ else
+ isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT;
+
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM);
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM);
+ isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
+
+ isp1362_hcd->rhdescb = RH_B_PPCM;
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb);
+ isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB);
+
+ isp1362_read_reg32(isp1362_hcd, HCFMINTVL);
+ isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI);
+ isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ isp1362_hcd->hc_control = OHCI_USB_OPER;
+ hcd->state = HC_STATE_RUNNING;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* Set up interrupts */
+ isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE;
+ isp1362_hcd->intenb |= OHCI_INTR_RD;
+ isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP;
+ isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+
+ /* Go operational */
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ /* enable global power */
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver isp1362_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "ISP1362 Host Controller",
+ .hcd_priv_size = sizeof(struct isp1362_hcd),
+
+ .irq = isp1362_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ .reset = isp1362_hc_reset,
+ .start = isp1362_hc_start,
+ .stop = isp1362_hc_stop,
+
+ .urb_enqueue = isp1362_urb_enqueue,
+ .urb_dequeue = isp1362_urb_dequeue,
+ .endpoint_disable = isp1362_endpoint_disable,
+
+ .get_frame_number = isp1362_get_frame,
+
+ .hub_status_data = isp1362_hub_status_data,
+ .hub_control = isp1362_hub_control,
+ .bus_suspend = isp1362_bus_suspend,
+ .bus_resume = isp1362_bus_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int isp1362_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+
+ remove_debug_file(isp1362_hcd);
+ DBG(0, "%s: Removing HCD\n", __func__);
+ usb_remove_hcd(hcd);
+ DBG(0, "%s: put_hcd\n", __func__);
+ usb_put_hcd(hcd);
+ DBG(0, "%s: Done\n", __func__);
+
+ return 0;
+}
+
+static int isp1362_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct isp1362_hcd *isp1362_hcd;
+ struct resource *addr, *data, *irq_res;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int irq;
+ int retval = 0;
+ unsigned int irq_flags = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* basic sanity checks first. board-specific init logic should
+ * have initialized this the three resources and probably board
+ * specific platform_data. we don't probe for IRQs, and do only
+ * minimal sanity checking.
+ */
+ if (pdev->num_resources < 3)
+ return -ENODEV;
+
+ if (pdev->dev.dma_mask) {
+ DBG(1, "won't do DMA");
+ return -ENODEV;
+ }
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq_res)
+ return -ENODEV;
+
+ irq = irq_res->start;
+
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ addr_reg = devm_ioremap_resource(&pdev->dev, addr);
+ if (IS_ERR(addr_reg))
+ return PTR_ERR(addr_reg);
+
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data_reg = devm_ioremap_resource(&pdev->dev, data);
+ if (IS_ERR(data_reg))
+ return PTR_ERR(data_reg);
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = data->start;
+ isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ isp1362_hcd->data_reg = data_reg;
+ isp1362_hcd->addr_reg = addr_reg;
+
+ isp1362_hcd->next_statechange = jiffies;
+ spin_lock_init(&isp1362_hcd->lock);
+ INIT_LIST_HEAD(&isp1362_hcd->async);
+ INIT_LIST_HEAD(&isp1362_hcd->periodic);
+ INIT_LIST_HEAD(&isp1362_hcd->isoc);
+ INIT_LIST_HEAD(&isp1362_hcd->remove_list);
+ isp1362_hcd->board = dev_get_platdata(&pdev->dev);
+#if USE_PLATFORM_DELAY
+ if (!isp1362_hcd->board->delay) {
+ dev_err(hcd->self.controller, "No platform delay function given\n");
+ retval = -ENODEV;
+ goto err;
+ }
+#endif
+
+ if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE)
+ irq_flags |= IRQF_TRIGGER_RISING;
+ if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE)
+ irq_flags |= IRQF_TRIGGER_FALLING;
+ if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL)
+ irq_flags |= IRQF_TRIGGER_HIGH;
+ if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
+ irq_flags |= IRQF_TRIGGER_LOW;
+
+ retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED);
+ if (retval != 0)
+ goto err;
+ device_wakeup_enable(hcd->self.controller);
+
+ dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq);
+
+ create_debug_file(isp1362_hcd);
+
+ return 0;
+
+ err:
+ usb_put_hcd(hcd);
+
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int isp1362_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ int retval = 0;
+
+ DBG(0, "%s: Suspending device\n", __func__);
+
+ if (state.event == PM_EVENT_FREEZE) {
+ DBG(0, "%s: Suspending root hub\n", __func__);
+ retval = isp1362_bus_suspend(hcd);
+ } else {
+ DBG(0, "%s: Suspending RH ports\n", __func__);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ }
+ if (retval == 0)
+ pdev->dev.power.power_state = state;
+ return retval;
+}
+
+static int isp1362_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ DBG(0, "%s: Resuming\n", __func__);
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ DBG(0, "%s: Resume RH ports\n", __func__);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return 0;
+ }
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd));
+}
+#else
+#define isp1362_suspend NULL
+#define isp1362_resume NULL
+#endif
+
+static struct platform_driver isp1362_driver = {
+ .probe = isp1362_probe,
+ .remove = isp1362_remove,
+
+ .suspend = isp1362_suspend,
+ .resume = isp1362_resume,
+ .driver = {
+ .name = hcd_name,
+ },
+};
+
+module_platform_driver(isp1362_driver);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
new file mode 100644
index 000000000..3b0b4847c
--- /dev/null
+++ b/drivers/usb/host/isp1362.h
@@ -0,0 +1,1014 @@
+/*
+ * ISP1362 HCD (Host Controller Driver) for USB.
+ *
+ * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de>
+ */
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Platform specific compile time options
+ */
+#if defined(CONFIG_BLACKFIN)
+
+#include <linux/io.h>
+#define USE_32BIT 0
+#define MAX_ROOT_PORTS 2
+#define USE_PLATFORM_DELAY 0
+#define USE_NDELAY 1
+
+#define DUMMY_DELAY_ACCESS \
+ do { \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ } while (0)
+
+#undef insw
+#undef outsw
+
+#define insw delayed_insw
+#define outsw delayed_outsw
+
+static inline void delayed_outsw(unsigned int addr, void *buf, int len)
+{
+ unsigned short *bp = (unsigned short *)buf;
+ while (len--) {
+ DUMMY_DELAY_ACCESS;
+ outw(*bp++, addr);
+ }
+}
+
+static inline void delayed_insw(unsigned int addr, void *buf, int len)
+{
+ unsigned short *bp = (unsigned short *)buf;
+ while (len--) {
+ DUMMY_DELAY_ACCESS;
+ *bp++ = inw(addr);
+ }
+}
+
+#else
+
+#define MAX_ROOT_PORTS 2
+
+#define USE_32BIT 0
+
+/* These options are mutually eclusive */
+#define USE_PLATFORM_DELAY 0
+#define USE_NDELAY 0
+
+#define DUMMY_DELAY_ACCESS do {} while (0)
+
+#endif
+
+
+/* ------------------------------------------------------------------------- */
+
+#define USB_RESET_WIDTH 50
+#define MAX_XFER_SIZE 1023
+
+/* Buffer sizes */
+#define ISP1362_BUF_SIZE 4096
+#define ISP1362_ISTL_BUFSIZE 512
+#define ISP1362_INTL_BLKSIZE 64
+#define ISP1362_INTL_BUFFERS 16
+#define ISP1362_ATL_BLKSIZE 64
+
+#define ISP1362_REG_WRITE_OFFSET 0x80
+
+#define REG_WIDTH_16 0x000
+#define REG_WIDTH_32 0x100
+#define REG_WIDTH_MASK 0x100
+#define REG_NO_MASK 0x0ff
+
+#ifdef ISP1362_DEBUG
+typedef const unsigned int isp1362_reg_t;
+
+#define REG_ACCESS_R 0x200
+#define REG_ACCESS_W 0x400
+#define REG_ACCESS_RW 0x600
+#define REG_ACCESS_MASK 0x600
+
+#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK)
+
+#define ISP1362_REG(name, addr, width, rw) \
+static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw))
+
+#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W))
+#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w))
+#else
+typedef const unsigned char isp1362_reg_t;
+#define ISP1362_REG_NO(r) (r)
+
+#define ISP1362_REG(name, addr, width, rw) \
+static isp1362_reg_t ISP1362_REG_##name = addr
+
+#define REG_ACCESS_TEST(r) do {} while (0)
+#define REG_WIDTH_TEST(r, w) do {} while (0)
+#endif
+
+/* OHCI compatible registers */
+/*
+ * Note: Some of the ISP1362 'OHCI' registers implement only
+ * a subset of the bits defined in the OHCI spec.
+ *
+ * Bitmasks for the individual bits of these registers are defined in "ohci.h"
+ */
+ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW);
+
+/* Philips ISP1362 specific registers */
+ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCHWCFG_DISABLE_SUSPEND (1 << 15)
+#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14)
+#define HCHWCFG_PULLDOWN_DS2 (1 << 13)
+#define HCHWCFG_PULLDOWN_DS1 (1 << 12)
+#define HCHWCFG_CLKNOTSTOP (1 << 11)
+#define HCHWCFG_ANALOG_OC (1 << 10)
+#define HCHWCFG_ONEINT (1 << 9)
+#define HCHWCFG_DACK_MODE (1 << 8)
+#define HCHWCFG_ONEDMA (1 << 7)
+#define HCHWCFG_DACK_POL (1 << 6)
+#define HCHWCFG_DREQ_POL (1 << 5)
+#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
+#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define HCHWCFG_INT_POL (1 << 2)
+#define HCHWCFG_INT_TRIGGER (1 << 1)
+#define HCHWCFG_INT_ENABLE (1 << 0)
+
+ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCDMACFG_CTR_ENABLE (1 << 7)
+#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
+#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
+#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
+#define HCDMACFG_DMA_ENABLE (1 << 4)
+#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
+#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK)
+#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0)
+#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1)
+#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2)
+#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3)
+#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4)
+#define HCDMACFG_DMA_RW_SELECT (1 << 0)
+
+ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCuPINT_SOF (1 << 0)
+#define HCuPINT_ISTL0 (1 << 1)
+#define HCuPINT_ISTL1 (1 << 2)
+#define HCuPINT_EOT (1 << 3)
+#define HCuPINT_OPR (1 << 4)
+#define HCuPINT_SUSP (1 << 5)
+#define HCuPINT_CLKRDY (1 << 6)
+#define HCuPINT_INTL (1 << 7)
+#define HCuPINT_ATL (1 << 8)
+#define HCuPINT_OTG (1 << 9)
+
+ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW);
+/* same bit definitions apply as for HCuPINT */
+
+ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R);
+#define HCCHIPID_MASK 0xff00
+#define HCCHIPID_MAGIC 0x3600
+
+ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W);
+#define HCSWRES_MAGIC 0x00f6
+
+ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCBUFSTAT_ISTL0_FULL (1 << 0)
+#define HCBUFSTAT_ISTL1_FULL (1 << 1)
+#define HCBUFSTAT_INTL_ACTIVE (1 << 2)
+#define HCBUFSTAT_ATL_ACTIVE (1 << 3)
+#define HCBUFSTAT_RESET_HWPP (1 << 4)
+#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5)
+#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6)
+#define HCBUFSTAT_ISTL0_DONE (1 << 8)
+#define HCBUFSTAT_ISTL1_DONE (1 << 9)
+#define HCBUFSTAT_PAIRED_PTDPP (1 << 10)
+
+ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW);
+#define HCDIRADDR_ADDR_MASK 0x0000ffff
+#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK)
+#define HCDIRADDR_COUNT_MASK 0xffff0000
+#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK)
+ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R);
+
+ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R);
+
+ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW);
+
+
+ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R);
+ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW);
+
+/* Philips transfer descriptor, cpu-endian */
+struct ptd {
+ u16 count;
+#define PTD_COUNT_MSK (0x3ff << 0)
+#define PTD_TOGGLE_MSK (1 << 10)
+#define PTD_ACTIVE_MSK (1 << 11)
+#define PTD_CC_MSK (0xf << 12)
+ u16 mps;
+#define PTD_MPS_MSK (0x3ff << 0)
+#define PTD_SPD_MSK (1 << 10)
+#define PTD_LAST_MSK (1 << 11)
+#define PTD_EP_MSK (0xf << 12)
+ u16 len;
+#define PTD_LEN_MSK (0x3ff << 0)
+#define PTD_DIR_MSK (3 << 10)
+#define PTD_DIR_SETUP (0)
+#define PTD_DIR_OUT (1)
+#define PTD_DIR_IN (2)
+ u16 faddr;
+#define PTD_FA_MSK (0x7f << 0)
+/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */
+#define PTD_SF_ISO_MSK (0xff << 8)
+#define PTD_SF_INT_MSK (0x1f << 8)
+#define PTD_PR_MSK (0x07 << 13)
+} __attribute__ ((packed, aligned(2)));
+#define PTD_HEADER_SIZE sizeof(struct ptd)
+
+/* ------------------------------------------------------------------------- */
+/* Copied from ohci.h: */
+/*
+ * Hardware transfer status codes -- CC from PTD
+ */
+#define PTD_CC_NOERROR 0x00
+#define PTD_CC_CRC 0x01
+#define PTD_CC_BITSTUFFING 0x02
+#define PTD_CC_DATATOGGLEM 0x03
+#define PTD_CC_STALL 0x04
+#define PTD_DEVNOTRESP 0x05
+#define PTD_PIDCHECKFAIL 0x06
+#define PTD_UNEXPECTEDPID 0x07
+#define PTD_DATAOVERRUN 0x08
+#define PTD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define PTD_BUFFEROVERRUN 0x0C
+#define PTD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define PTD_NOTACCESSED 0x0F
+
+
+/* map OHCI TD status codes (CC) to errno values */
+static const int cc_to_error[16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+#define FI 0x2edf /* 12000 bits per frame (-1) */
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
+#define LSTHRESH 0x628 /* lowspeed bit threshold */
+
+/* ------------------------------------------------------------------------- */
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8)
+#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK)
+#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8)
+#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK)
+#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13)
+#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK)
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct isp1362_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+
+ /* philips transfer descriptor */
+ struct ptd ptd;
+
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+ u16 error_count;
+ u16 length; /* of current packet */
+ s16 ptd_offset; /* buffer offset in ISP1362 where
+ PTD has been stored
+ (for access thru HCDIRDATA) */
+ int ptd_index;
+ int num_ptds;
+ void *data; /* to databuf */
+ /* queue of active EPs (the ones transmitted to the chip) */
+ struct list_head active;
+
+ /* periodic schedule */
+ u8 branch;
+ u16 interval;
+ u16 load;
+ u16 last_iso;
+
+ /* async schedule */
+ struct list_head schedule; /* list of all EPs that need processing */
+ struct list_head remove_list;
+ int num_req;
+};
+
+struct isp1362_ep_queue {
+ struct list_head active; /* list of PTDs currently processed by HC */
+ atomic_t finishing;
+ unsigned long buf_map;
+ unsigned long skip_map;
+ int free_ptd;
+ u16 buf_start;
+ u16 buf_size;
+ u16 blk_size; /* PTD buffer block size for ATL and INTL */
+ u8 buf_count;
+ u8 buf_avail;
+ char name[16];
+
+ /* for statistical tracking */
+ u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */
+ u8 ptd_count; /* number of ptds submitted to this queue */
+};
+
+struct isp1362_hcd {
+ spinlock_t lock;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+
+ struct isp1362_platform_data *board;
+
+ struct dentry *debug_file;
+ unsigned long stat1, stat2, stat4, stat8, stat16;
+
+ /* HC registers */
+ u32 intenb; /* "OHCI" interrupts */
+ u16 irqenb; /* uP interrupts */
+
+ /* Root hub registers */
+ u32 rhdesca;
+ u32 rhdescb;
+ u32 rhstatus;
+ u32 rhport[MAX_ROOT_PORTS];
+ unsigned long next_statechange;
+
+ /* HC control reg shadow copy */
+ u32 hc_control;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: int */
+ u16 load[PERIODIC_SIZE];
+ struct list_head periodic;
+ u16 fmindex;
+
+ /* periodic schedule: isochronous */
+ struct list_head isoc;
+ unsigned int istl_flip:1;
+ unsigned int irq_active:1;
+
+ /* Schedules for the current frame */
+ struct isp1362_ep_queue atl_queue;
+ struct isp1362_ep_queue intl_queue;
+ struct isp1362_ep_queue istl_queue[2];
+
+ /* list of PTDs retrieved from HC */
+ struct list_head remove_list;
+ enum {
+ ISP1362_INT_SOF,
+ ISP1362_INT_ISTL0,
+ ISP1362_INT_ISTL1,
+ ISP1362_INT_EOT,
+ ISP1362_INT_OPR,
+ ISP1362_INT_SUSP,
+ ISP1362_INT_CLKRDY,
+ ISP1362_INT_INTL,
+ ISP1362_INT_ATL,
+ ISP1362_INT_OTG,
+ NUM_ISP1362_IRQS
+ } IRQ_NAMES;
+ unsigned int irq_stat[NUM_ISP1362_IRQS];
+ int req_serial;
+};
+
+static inline const char *ISP1362_INT_NAME(int n)
+{
+ switch (n) {
+ case ISP1362_INT_SOF: return "SOF";
+ case ISP1362_INT_ISTL0: return "ISTL0";
+ case ISP1362_INT_ISTL1: return "ISTL1";
+ case ISP1362_INT_EOT: return "EOT";
+ case ISP1362_INT_OPR: return "OPR";
+ case ISP1362_INT_SUSP: return "SUSP";
+ case ISP1362_INT_CLKRDY: return "CLKRDY";
+ case ISP1362_INT_INTL: return "INTL";
+ case ISP1362_INT_ATL: return "ATL";
+ case ISP1362_INT_OTG: return "OTG";
+ default: return "unknown";
+ }
+}
+
+static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr)
+{
+ unsigned long p = (unsigned long)ptr;
+ if (!(p & 0xf))
+ isp1362_hcd->stat16++;
+ else if (!(p & 0x7))
+ isp1362_hcd->stat8++;
+ else if (!(p & 0x3))
+ isp1362_hcd->stat4++;
+ else if (!(p & 0x1))
+ isp1362_hcd->stat2++;
+ else
+ isp1362_hcd->stat1++;
+}
+
+static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd)
+{
+ return (struct isp1362_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd)
+{
+ return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv);
+}
+
+#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0)
+
+/*
+ * ISP1362 HW Interface
+ */
+
+#define DBG(level, fmt...) \
+ do { \
+ if (dbg_level > level) \
+ pr_debug(fmt); \
+ } while (0)
+
+#ifdef VERBOSE
+# define VDBG(fmt...) DBG(3, fmt)
+#else
+# define VDBG(fmt...) do {} while (0)
+#endif
+
+#ifdef REGISTERS
+# define RDBG(fmt...) DBG(1, fmt)
+#else
+# define RDBG(fmt...) do {} while (0)
+#endif
+
+#ifdef URB_TRACE
+#define URB_DBG(fmt...) DBG(0, fmt)
+#else
+#define URB_DBG(fmt...) do {} while (0)
+#endif
+
+
+#if USE_PLATFORM_DELAY
+#if USE_NDELAY
+#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously.
+#endif
+#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d)
+#elif USE_NDELAY
+#define isp1362_delay(h, d) ndelay(d)
+#else
+#define isp1362_delay(h, d) do {} while (0)
+#endif
+
+#define get_urb(ep) ({ \
+ BUG_ON(list_empty(&ep->hep->urb_list)); \
+ container_of(ep->hep->urb_list.next, struct urb, urb_list); \
+})
+
+/* basic access functions for ISP1362 chip registers */
+/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure,
+ * that all register accesses are performed with interrupts disabled, since the interrupt
+ * handler has no way of restoring the previous state.
+ */
+static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg)
+{
+ REG_ACCESS_TEST(reg);
+ DUMMY_DELAY_ACCESS;
+ writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg);
+ DUMMY_DELAY_ACCESS;
+ isp1362_delay(isp1362_hcd, 1);
+}
+
+static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val)
+{
+ DUMMY_DELAY_ACCESS;
+ writew(val, isp1362_hcd->data_reg);
+}
+
+static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd)
+{
+ u16 val;
+
+ DUMMY_DELAY_ACCESS;
+ val = readw(isp1362_hcd->data_reg);
+
+ return val;
+}
+
+static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val)
+{
+#if USE_32BIT
+ DUMMY_DELAY_ACCESS;
+ writel(val, isp1362_hcd->data_reg);
+#else
+ DUMMY_DELAY_ACCESS;
+ writew((u16)val, isp1362_hcd->data_reg);
+ DUMMY_DELAY_ACCESS;
+ writew(val >> 16, isp1362_hcd->data_reg);
+#endif
+}
+
+static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd)
+{
+ u32 val;
+
+#if USE_32BIT
+ DUMMY_DELAY_ACCESS;
+ val = readl(isp1362_hcd->data_reg);
+#else
+ DUMMY_DELAY_ACCESS;
+ val = (u32)readw(isp1362_hcd->data_reg);
+ DUMMY_DELAY_ACCESS;
+ val |= (u32)readw(isp1362_hcd->data_reg) << 16;
+#endif
+ return val;
+}
+
+/* use readsw/writesw to access the fifo whenever possible */
+/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */
+static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
+{
+ u8 *dp = buf;
+ u16 data;
+
+ if (!len)
+ return;
+
+ RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf);
+#if USE_32BIT
+ if (len >= 4) {
+ RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2);
+ readsl(isp1362_hcd->data_reg, dp, len >> 2);
+ dp += len & ~3;
+ len &= 3;
+ }
+#endif
+ if (len >= 2) {
+ RDBG("%s: Using readsw for %d words\n", __func__, len >> 1);
+ insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
+ dp += len & ~1;
+ len &= 1;
+ }
+
+ BUG_ON(len & ~1);
+ if (len > 0) {
+ data = isp1362_read_data16(isp1362_hcd);
+ RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__,
+ (u8)data, (u32)dp);
+ *dp = (u8)data;
+ }
+}
+
+static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
+{
+ u8 *dp = buf;
+ u16 data;
+
+ if (!len)
+ return;
+
+ if ((unsigned long)dp & 0x1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ data = *dp++;
+ data |= *dp++ << 8;
+ isp1362_write_data16(isp1362_hcd, data);
+ }
+ if (len)
+ isp1362_write_data16(isp1362_hcd, *dp);
+ return;
+ }
+
+ RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf);
+#if USE_32BIT
+ if (len >= 4) {
+ RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2);
+ writesl(isp1362_hcd->data_reg, dp, len >> 2);
+ dp += len & ~3;
+ len &= 3;
+ }
+#endif
+ if (len >= 2) {
+ RDBG("%s: Using writesw for %d words\n", __func__, len >> 1);
+ outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
+ dp += len & ~1;
+ len &= 1;
+ }
+
+ BUG_ON(len & ~1);
+ if (len > 0) {
+ /* finally write any trailing byte; we don't need to care
+ * about the high byte of the last word written
+ */
+ data = (u16)*dp;
+ RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__,
+ data, (u32)dp);
+ isp1362_write_data16(isp1362_hcd, data);
+ }
+}
+
+#define isp1362_read_reg16(d, r) ({ \
+ u16 __v; \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
+ isp1362_write_addr(d, ISP1362_REG_##r); \
+ __v = isp1362_read_data16(d); \
+ RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+ __v; \
+})
+
+#define isp1362_read_reg32(d, r) ({ \
+ u32 __v; \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
+ isp1362_write_addr(d, ISP1362_REG_##r); \
+ __v = isp1362_read_data32(d); \
+ RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+ __v; \
+})
+
+#define isp1362_write_reg16(d, r, v) { \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
+ isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
+ isp1362_write_data16(d, (u16)(v)); \
+ RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+}
+
+#define isp1362_write_reg32(d, r, v) { \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
+ isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
+ isp1362_write_data32(d, (u32)(v)); \
+ RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+}
+
+#define isp1362_set_mask16(d, r, m) { \
+ u16 __v; \
+ __v = isp1362_read_reg16(d, r); \
+ if ((__v | m) != __v) \
+ isp1362_write_reg16(d, r, __v | m); \
+}
+
+#define isp1362_clr_mask16(d, r, m) { \
+ u16 __v; \
+ __v = isp1362_read_reg16(d, r); \
+ if ((__v & ~m) != __v) \
+ isp1362_write_reg16(d, r, __v & ~m); \
+}
+
+#define isp1362_set_mask32(d, r, m) { \
+ u32 __v; \
+ __v = isp1362_read_reg32(d, r); \
+ if ((__v | m) != __v) \
+ isp1362_write_reg32(d, r, __v | m); \
+}
+
+#define isp1362_clr_mask32(d, r, m) { \
+ u32 __v; \
+ __v = isp1362_read_reg32(d, r); \
+ if ((__v & ~m) != __v) \
+ isp1362_write_reg32(d, r, __v & ~m); \
+}
+
+#define isp1362_show_reg(d, r) { \
+ if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \
+ DBG(0, "%-12s[%02x]: %08x\n", #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \
+ else \
+ DBG(0, "%-12s[%02x]: %04x\n", #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \
+}
+
+static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd)
+{
+ isp1362_show_reg(isp1362_hcd, HCREVISION);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_show_reg(isp1362_hcd, HCCMDSTAT);
+ isp1362_show_reg(isp1362_hcd, HCINTSTAT);
+ isp1362_show_reg(isp1362_hcd, HCINTENB);
+ isp1362_show_reg(isp1362_hcd, HCFMINTVL);
+ isp1362_show_reg(isp1362_hcd, HCFMREM);
+ isp1362_show_reg(isp1362_hcd, HCFMNUM);
+ isp1362_show_reg(isp1362_hcd, HCLSTHRESH);
+ isp1362_show_reg(isp1362_hcd, HCRHDESCA);
+ isp1362_show_reg(isp1362_hcd, HCRHDESCB);
+ isp1362_show_reg(isp1362_hcd, HCRHSTATUS);
+ isp1362_show_reg(isp1362_hcd, HCRHPORT1);
+ isp1362_show_reg(isp1362_hcd, HCRHPORT2);
+
+ isp1362_show_reg(isp1362_hcd, HCHWCFG);
+ isp1362_show_reg(isp1362_hcd, HCDMACFG);
+ isp1362_show_reg(isp1362_hcd, HCXFERCTR);
+ isp1362_show_reg(isp1362_hcd, HCuPINT);
+
+ if (in_interrupt())
+ DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB",
+ ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb);
+ else
+ isp1362_show_reg(isp1362_hcd, HCuPINTENB);
+ isp1362_show_reg(isp1362_hcd, HCCHIPID);
+ isp1362_show_reg(isp1362_hcd, HCSCRATCH);
+ isp1362_show_reg(isp1362_hcd, HCBUFSTAT);
+ isp1362_show_reg(isp1362_hcd, HCDIRADDR);
+ /* Access would advance fifo
+ * isp1362_show_reg(isp1362_hcd, HCDIRDATA);
+ */
+ isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCISTLRATE);
+ isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ);
+ isp1362_show_reg(isp1362_hcd, HCINTLDONE);
+ isp1362_show_reg(isp1362_hcd, HCINTLSKIP);
+ isp1362_show_reg(isp1362_hcd, HCINTLLAST);
+ isp1362_show_reg(isp1362_hcd, HCINTLCURR);
+ isp1362_show_reg(isp1362_hcd, HCATLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCATLBLKSZ);
+ /* only valid after ATL_DONE interrupt
+ * isp1362_show_reg(isp1362_hcd, HCATLDONE);
+ */
+ isp1362_show_reg(isp1362_hcd, HCATLSKIP);
+ isp1362_show_reg(isp1362_hcd, HCATLLAST);
+ isp1362_show_reg(isp1362_hcd, HCATLCURR);
+ isp1362_show_reg(isp1362_hcd, HCATLDTC);
+ isp1362_show_reg(isp1362_hcd, HCATLDTCTO);
+}
+
+static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len)
+{
+ len = (len + 1) & ~1;
+
+ isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE);
+ isp1362_write_reg32(isp1362_hcd, HCDIRADDR,
+ HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len));
+}
+
+static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
+{
+ isp1362_write_diraddr(isp1362_hcd, offset, len);
+
+ DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n",
+ __func__, len, offset, buf);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+
+ isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA);
+
+ isp1362_read_fifo(isp1362_hcd, buf, len);
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+}
+
+static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
+{
+ isp1362_write_diraddr(isp1362_hcd, offset, len);
+
+ DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n",
+ __func__, len, offset, buf);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+
+ isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET);
+ isp1362_write_fifo(isp1362_hcd, buf, len);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+}
+
+static void __attribute__((unused)) dump_data(char *buf, int len)
+{
+ if (dbg_level > 0) {
+ int k;
+ int lf = 0;
+
+ for (k = 0; k < len; ++k) {
+ if (!lf)
+ DBG(0, "%04x:", k);
+ printk(" %02x", ((u8 *) buf)[k]);
+ lf = 1;
+ if (!k)
+ continue;
+ if (k % 16 == 15) {
+ printk("\n");
+ lf = 0;
+ continue;
+ }
+ if (k % 8 == 7)
+ printk(" ");
+ if (k % 4 == 3)
+ printk(" ");
+ }
+ if (lf)
+ printk("\n");
+ }
+}
+
+#if defined(PTD_TRACE)
+
+static void dump_ptd(struct ptd *ptd)
+{
+ DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n",
+ container_of(ptd, struct isp1362_ep, ptd),
+ PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd),
+ PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+ PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd),
+ PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd));
+ DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr);
+}
+
+static void dump_ptd_out_data(struct ptd *ptd, u8 *buf)
+{
+ if (dbg_level > 0) {
+ if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
+ DBG(0, "--out->\n");
+ dump_data(buf, PTD_GET_LEN(ptd));
+ }
+ }
+}
+
+static void dump_ptd_in_data(struct ptd *ptd, u8 *buf)
+{
+ if (dbg_level > 0) {
+ if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
+ DBG(0, "<--in--\n");
+ dump_data(buf, PTD_GET_COUNT(ptd));
+ }
+ DBG(0, "-----\n");
+ }
+}
+
+static void dump_ptd_queue(struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ int dbg = dbg_level;
+
+ dbg_level = 1;
+ list_for_each_entry(ep, &epq->active, active) {
+ dump_ptd(&ep->ptd);
+ dump_data(ep->data, ep->length);
+ }
+ dbg_level = dbg;
+}
+#else
+#define dump_ptd(ptd) do {} while (0)
+#define dump_ptd_in_data(ptd, buf) do {} while (0)
+#define dump_ptd_out_data(ptd, buf) do {} while (0)
+#define dump_ptd_data(ptd, buf) do {} while (0)
+#define dump_ptd_queue(epq) do {} while (0)
+#endif
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
new file mode 100644
index 000000000..fc1fd4039
--- /dev/null
+++ b/drivers/usb/host/max3421-hcd.c
@@ -0,0 +1,1955 @@
+/*
+ * MAX3421 Host Controller driver for USB.
+ *
+ * Author: David Mosberger-Tang <davidm@egauge.net>
+ *
+ * (C) Copyright 2014 David Mosberger-Tang <davidm@egauge.net>
+ *
+ * MAX3421 is a chip implementing a USB 2.0 Full-/Low-Speed host
+ * controller on a SPI bus.
+ *
+ * Based on:
+ * o MAX3421E datasheet
+ * http://datasheets.maximintegrated.com/en/ds/MAX3421E.pdf
+ * o MAX3421E Programming Guide
+ * http://www.hdl.co.jp/ftpdata/utl-001/AN3785.pdf
+ * o gadget/dummy_hcd.c
+ * For USB HCD implementation.
+ * o Arduino MAX3421 driver
+ * https://github.com/felis/USB_Host_Shield_2.0/blob/master/Usb.cpp
+ *
+ * This file is licenced under the GPL v2.
+ *
+ * Important note on worst-case (full-speed) packet size constraints
+ * (See USB 2.0 Section 5.6.3 and following):
+ *
+ * - control: 64 bytes
+ * - isochronous: 1023 bytes
+ * - interrupt: 64 bytes
+ * - bulk: 64 bytes
+ *
+ * Since the MAX3421 FIFO size is 64 bytes, we do not have to work about
+ * multi-FIFO writes/reads for a single USB packet *except* for isochronous
+ * transfers. We don't support isochronous transfers at this time, so we
+ * just assume that a USB packet always fits into a single FIFO buffer.
+ *
+ * NOTE: The June 2006 version of "MAX3421E Programming Guide"
+ * (AN3785) has conflicting info for the RCVDAVIRQ bit:
+ *
+ * The description of RCVDAVIRQ says "The CPU *must* clear
+ * this IRQ bit (by writing a 1 to it) before reading the
+ * RCVFIFO data.
+ *
+ * However, the earlier section on "Programming BULK-IN
+ * Transfers" says * that:
+ *
+ * After the CPU retrieves the data, it clears the
+ * RCVDAVIRQ bit.
+ *
+ * The December 2006 version has been corrected and it consistently
+ * states the second behavior is the correct one.
+ *
+ * Synchronous SPI transactions sleep so we can't perform any such
+ * transactions while holding a spin-lock (and/or while interrupts are
+ * masked). To achieve this, all SPI transactions are issued from a
+ * single thread (max3421_spi_thread).
+ */
+
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <linux/platform_data/max3421-hcd.h>
+
+#define DRIVER_DESC "MAX3421 USB Host-Controller Driver"
+#define DRIVER_VERSION "1.0"
+
+/* 11-bit counter that wraps around (USB 2.0 Section 8.3.3): */
+#define USB_MAX_FRAME_NUMBER 0x7ff
+#define USB_MAX_RETRIES 3 /* # of retries before error is reported */
+
+/*
+ * Max. # of times we're willing to retransmit a request immediately in
+ * resposne to a NAK. Afterwards, we fall back on trying once a frame.
+ */
+#define NAK_MAX_FAST_RETRANSMITS 2
+
+#define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */
+
+/* Port-change mask: */
+#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | \
+ USB_PORT_STAT_C_ENABLE | \
+ USB_PORT_STAT_C_SUSPEND | \
+ USB_PORT_STAT_C_OVERCURRENT | \
+ USB_PORT_STAT_C_RESET) << 16)
+
+enum max3421_rh_state {
+ MAX3421_RH_RESET,
+ MAX3421_RH_SUSPENDED,
+ MAX3421_RH_RUNNING
+};
+
+enum pkt_state {
+ PKT_STATE_SETUP, /* waiting to send setup packet to ctrl pipe */
+ PKT_STATE_TRANSFER, /* waiting to xfer transfer_buffer */
+ PKT_STATE_TERMINATE /* waiting to terminate control transfer */
+};
+
+enum scheduling_pass {
+ SCHED_PASS_PERIODIC,
+ SCHED_PASS_NON_PERIODIC,
+ SCHED_PASS_DONE
+};
+
+/* Bit numbers for max3421_hcd->todo: */
+enum {
+ ENABLE_IRQ = 0,
+ RESET_HCD,
+ RESET_PORT,
+ CHECK_UNLINK,
+ IOPIN_UPDATE
+};
+
+struct max3421_dma_buf {
+ u8 data[2];
+};
+
+struct max3421_hcd {
+ spinlock_t lock;
+
+ struct task_struct *spi_thread;
+
+ struct max3421_hcd *next;
+
+ enum max3421_rh_state rh_state;
+ /* lower 16 bits contain port status, upper 16 bits the change mask: */
+ u32 port_status;
+
+ unsigned active:1;
+
+ struct list_head ep_list; /* list of EP's with work */
+
+ /*
+ * The following are owned by spi_thread (may be accessed by
+ * SPI-thread without acquiring the HCD lock:
+ */
+ u8 rev; /* chip revision */
+ u16 frame_number;
+ /*
+ * kmalloc'd buffers guaranteed to be in separate (DMA)
+ * cache-lines:
+ */
+ struct max3421_dma_buf *tx;
+ struct max3421_dma_buf *rx;
+ /*
+ * URB we're currently processing. Must not be reset to NULL
+ * unless MAX3421E chip is idle:
+ */
+ struct urb *curr_urb;
+ enum scheduling_pass sched_pass;
+ struct usb_device *loaded_dev; /* dev that's loaded into the chip */
+ int loaded_epnum; /* epnum whose toggles are loaded */
+ int urb_done; /* > 0 -> no errors, < 0: errno */
+ size_t curr_len;
+ u8 hien;
+ u8 mode;
+ u8 iopins[2];
+ unsigned long todo;
+#ifdef DEBUG
+ unsigned long err_stat[16];
+#endif
+};
+
+struct max3421_ep {
+ struct usb_host_endpoint *ep;
+ struct list_head ep_list;
+ u32 naks;
+ u16 last_active; /* frame # this ep was last active */
+ enum pkt_state pkt_state;
+ u8 retries;
+ u8 retransmit; /* packet needs retransmission */
+};
+
+static struct max3421_hcd *max3421_hcd_list;
+
+#define MAX3421_FIFO_SIZE 64
+
+#define MAX3421_SPI_DIR_RD 0 /* read register from MAX3421 */
+#define MAX3421_SPI_DIR_WR 1 /* write register to MAX3421 */
+
+/* SPI commands: */
+#define MAX3421_SPI_DIR_SHIFT 1
+#define MAX3421_SPI_REG_SHIFT 3
+
+#define MAX3421_REG_RCVFIFO 1
+#define MAX3421_REG_SNDFIFO 2
+#define MAX3421_REG_SUDFIFO 4
+#define MAX3421_REG_RCVBC 6
+#define MAX3421_REG_SNDBC 7
+#define MAX3421_REG_USBIRQ 13
+#define MAX3421_REG_USBIEN 14
+#define MAX3421_REG_USBCTL 15
+#define MAX3421_REG_CPUCTL 16
+#define MAX3421_REG_PINCTL 17
+#define MAX3421_REG_REVISION 18
+#define MAX3421_REG_IOPINS1 20
+#define MAX3421_REG_IOPINS2 21
+#define MAX3421_REG_GPINIRQ 22
+#define MAX3421_REG_GPINIEN 23
+#define MAX3421_REG_GPINPOL 24
+#define MAX3421_REG_HIRQ 25
+#define MAX3421_REG_HIEN 26
+#define MAX3421_REG_MODE 27
+#define MAX3421_REG_PERADDR 28
+#define MAX3421_REG_HCTL 29
+#define MAX3421_REG_HXFR 30
+#define MAX3421_REG_HRSL 31
+
+enum {
+ MAX3421_USBIRQ_OSCOKIRQ_BIT = 0,
+ MAX3421_USBIRQ_NOVBUSIRQ_BIT = 5,
+ MAX3421_USBIRQ_VBUSIRQ_BIT
+};
+
+enum {
+ MAX3421_CPUCTL_IE_BIT = 0,
+ MAX3421_CPUCTL_PULSEWID0_BIT = 6,
+ MAX3421_CPUCTL_PULSEWID1_BIT
+};
+
+enum {
+ MAX3421_USBCTL_PWRDOWN_BIT = 4,
+ MAX3421_USBCTL_CHIPRES_BIT
+};
+
+enum {
+ MAX3421_PINCTL_GPXA_BIT = 0,
+ MAX3421_PINCTL_GPXB_BIT,
+ MAX3421_PINCTL_POSINT_BIT,
+ MAX3421_PINCTL_INTLEVEL_BIT,
+ MAX3421_PINCTL_FDUPSPI_BIT,
+ MAX3421_PINCTL_EP0INAK_BIT,
+ MAX3421_PINCTL_EP2INAK_BIT,
+ MAX3421_PINCTL_EP3INAK_BIT,
+};
+
+enum {
+ MAX3421_HI_BUSEVENT_BIT = 0, /* bus-reset/-resume */
+ MAX3421_HI_RWU_BIT, /* remote wakeup */
+ MAX3421_HI_RCVDAV_BIT, /* receive FIFO data available */
+ MAX3421_HI_SNDBAV_BIT, /* send buffer available */
+ MAX3421_HI_SUSDN_BIT, /* suspend operation done */
+ MAX3421_HI_CONDET_BIT, /* peripheral connect/disconnect */
+ MAX3421_HI_FRAME_BIT, /* frame generator */
+ MAX3421_HI_HXFRDN_BIT, /* host transfer done */
+};
+
+enum {
+ MAX3421_HCTL_BUSRST_BIT = 0,
+ MAX3421_HCTL_FRMRST_BIT,
+ MAX3421_HCTL_SAMPLEBUS_BIT,
+ MAX3421_HCTL_SIGRSM_BIT,
+ MAX3421_HCTL_RCVTOG0_BIT,
+ MAX3421_HCTL_RCVTOG1_BIT,
+ MAX3421_HCTL_SNDTOG0_BIT,
+ MAX3421_HCTL_SNDTOG1_BIT
+};
+
+enum {
+ MAX3421_MODE_HOST_BIT = 0,
+ MAX3421_MODE_LOWSPEED_BIT,
+ MAX3421_MODE_HUBPRE_BIT,
+ MAX3421_MODE_SOFKAENAB_BIT,
+ MAX3421_MODE_SEPIRQ_BIT,
+ MAX3421_MODE_DELAYISO_BIT,
+ MAX3421_MODE_DMPULLDN_BIT,
+ MAX3421_MODE_DPPULLDN_BIT
+};
+
+enum {
+ MAX3421_HRSL_OK = 0,
+ MAX3421_HRSL_BUSY,
+ MAX3421_HRSL_BADREQ,
+ MAX3421_HRSL_UNDEF,
+ MAX3421_HRSL_NAK,
+ MAX3421_HRSL_STALL,
+ MAX3421_HRSL_TOGERR,
+ MAX3421_HRSL_WRONGPID,
+ MAX3421_HRSL_BADBC,
+ MAX3421_HRSL_PIDERR,
+ MAX3421_HRSL_PKTERR,
+ MAX3421_HRSL_CRCERR,
+ MAX3421_HRSL_KERR,
+ MAX3421_HRSL_JERR,
+ MAX3421_HRSL_TIMEOUT,
+ MAX3421_HRSL_BABBLE,
+ MAX3421_HRSL_RESULT_MASK = 0xf,
+ MAX3421_HRSL_RCVTOGRD_BIT = 4,
+ MAX3421_HRSL_SNDTOGRD_BIT,
+ MAX3421_HRSL_KSTATUS_BIT,
+ MAX3421_HRSL_JSTATUS_BIT
+};
+
+/* Return same error-codes as ohci.h:cc_to_error: */
+static const int hrsl_to_error[] = {
+ [MAX3421_HRSL_OK] = 0,
+ [MAX3421_HRSL_BUSY] = -EINVAL,
+ [MAX3421_HRSL_BADREQ] = -EINVAL,
+ [MAX3421_HRSL_UNDEF] = -EINVAL,
+ [MAX3421_HRSL_NAK] = -EAGAIN,
+ [MAX3421_HRSL_STALL] = -EPIPE,
+ [MAX3421_HRSL_TOGERR] = -EILSEQ,
+ [MAX3421_HRSL_WRONGPID] = -EPROTO,
+ [MAX3421_HRSL_BADBC] = -EREMOTEIO,
+ [MAX3421_HRSL_PIDERR] = -EPROTO,
+ [MAX3421_HRSL_PKTERR] = -EPROTO,
+ [MAX3421_HRSL_CRCERR] = -EILSEQ,
+ [MAX3421_HRSL_KERR] = -EIO,
+ [MAX3421_HRSL_JERR] = -EIO,
+ [MAX3421_HRSL_TIMEOUT] = -ETIME,
+ [MAX3421_HRSL_BABBLE] = -EOVERFLOW
+};
+
+/*
+ * See http://www.beyondlogic.org/usbnutshell/usb4.shtml#Control for a
+ * reasonable overview of how control transfers use the the IN/OUT
+ * tokens.
+ */
+#define MAX3421_HXFR_BULK_IN(ep) (0x00 | (ep)) /* bulk or interrupt */
+#define MAX3421_HXFR_SETUP 0x10
+#define MAX3421_HXFR_BULK_OUT(ep) (0x20 | (ep)) /* bulk or interrupt */
+#define MAX3421_HXFR_ISO_IN(ep) (0x40 | (ep))
+#define MAX3421_HXFR_ISO_OUT(ep) (0x60 | (ep))
+#define MAX3421_HXFR_HS_IN 0x80 /* handshake in */
+#define MAX3421_HXFR_HS_OUT 0xa0 /* handshake out */
+
+#define field(val, bit) ((val) << (bit))
+
+static inline s16
+frame_diff(u16 left, u16 right)
+{
+ return ((unsigned) (left - right)) % (USB_MAX_FRAME_NUMBER + 1);
+}
+
+static inline struct max3421_hcd *
+hcd_to_max3421(struct usb_hcd *hcd)
+{
+ return (struct max3421_hcd *) hcd->hcd_priv;
+}
+
+static inline struct usb_hcd *
+max3421_to_hcd(struct max3421_hcd *max3421_hcd)
+{
+ return container_of((void *) max3421_hcd, struct usb_hcd, hcd_priv);
+}
+
+static u8
+spi_rd8(struct usb_hcd *hcd, unsigned int reg)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct spi_transfer transfer;
+ struct spi_message msg;
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ max3421_hcd->tx->data[0] =
+ (field(reg, MAX3421_SPI_REG_SHIFT) |
+ field(MAX3421_SPI_DIR_RD, MAX3421_SPI_DIR_SHIFT));
+
+ transfer.tx_buf = max3421_hcd->tx->data;
+ transfer.rx_buf = max3421_hcd->rx->data;
+ transfer.len = 2;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+
+ return max3421_hcd->rx->data[1];
+}
+
+static void
+spi_wr8(struct usb_hcd *hcd, unsigned int reg, u8 val)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct spi_transfer transfer;
+ struct spi_message msg;
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ max3421_hcd->tx->data[0] =
+ (field(reg, MAX3421_SPI_REG_SHIFT) |
+ field(MAX3421_SPI_DIR_WR, MAX3421_SPI_DIR_SHIFT));
+ max3421_hcd->tx->data[1] = val;
+
+ transfer.tx_buf = max3421_hcd->tx->data;
+ transfer.len = 2;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+}
+
+static void
+spi_rd_buf(struct usb_hcd *hcd, unsigned int reg, void *buf, size_t len)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct spi_transfer transfer[2];
+ struct spi_message msg;
+
+ memset(transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ max3421_hcd->tx->data[0] =
+ (field(reg, MAX3421_SPI_REG_SHIFT) |
+ field(MAX3421_SPI_DIR_RD, MAX3421_SPI_DIR_SHIFT));
+ transfer[0].tx_buf = max3421_hcd->tx->data;
+ transfer[0].len = 1;
+
+ transfer[1].rx_buf = buf;
+ transfer[1].len = len;
+
+ spi_message_add_tail(&transfer[0], &msg);
+ spi_message_add_tail(&transfer[1], &msg);
+ spi_sync(spi, &msg);
+}
+
+static void
+spi_wr_buf(struct usb_hcd *hcd, unsigned int reg, void *buf, size_t len)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct spi_transfer transfer[2];
+ struct spi_message msg;
+
+ memset(transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ max3421_hcd->tx->data[0] =
+ (field(reg, MAX3421_SPI_REG_SHIFT) |
+ field(MAX3421_SPI_DIR_WR, MAX3421_SPI_DIR_SHIFT));
+
+ transfer[0].tx_buf = max3421_hcd->tx->data;
+ transfer[0].len = 1;
+
+ transfer[1].tx_buf = buf;
+ transfer[1].len = len;
+
+ spi_message_add_tail(&transfer[0], &msg);
+ spi_message_add_tail(&transfer[1], &msg);
+ spi_sync(spi, &msg);
+}
+
+/*
+ * Figure out the correct setting for the LOWSPEED and HUBPRE mode
+ * bits. The HUBPRE bit needs to be set when MAX3421E operates at
+ * full speed, but it's talking to a low-speed device (i.e., through a
+ * hub). Setting that bit ensures that every low-speed packet is
+ * preceded by a full-speed PRE PID. Possible configurations:
+ *
+ * Hub speed: Device speed: => LOWSPEED bit: HUBPRE bit:
+ * FULL FULL => 0 0
+ * FULL LOW => 1 1
+ * LOW LOW => 1 0
+ * LOW FULL => 1 0
+ */
+static void
+max3421_set_speed(struct usb_hcd *hcd, struct usb_device *dev)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ u8 mode_lowspeed, mode_hubpre, mode = max3421_hcd->mode;
+
+ mode_lowspeed = BIT(MAX3421_MODE_LOWSPEED_BIT);
+ mode_hubpre = BIT(MAX3421_MODE_HUBPRE_BIT);
+ if (max3421_hcd->port_status & USB_PORT_STAT_LOW_SPEED) {
+ mode |= mode_lowspeed;
+ mode &= ~mode_hubpre;
+ } else if (dev->speed == USB_SPEED_LOW) {
+ mode |= mode_lowspeed | mode_hubpre;
+ } else {
+ mode &= ~(mode_lowspeed | mode_hubpre);
+ }
+ if (mode != max3421_hcd->mode) {
+ max3421_hcd->mode = mode;
+ spi_wr8(hcd, MAX3421_REG_MODE, max3421_hcd->mode);
+ }
+
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_set_address(struct usb_hcd *hcd, struct usb_device *dev, int epnum,
+ int force_toggles)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ int old_epnum, same_ep, rcvtog, sndtog;
+ struct usb_device *old_dev;
+ u8 hctl;
+
+ old_dev = max3421_hcd->loaded_dev;
+ old_epnum = max3421_hcd->loaded_epnum;
+
+ same_ep = (dev == old_dev && epnum == old_epnum);
+ if (same_ep && !force_toggles)
+ return;
+
+ if (old_dev && !same_ep) {
+ /* save the old end-points toggles: */
+ u8 hrsl = spi_rd8(hcd, MAX3421_REG_HRSL);
+
+ rcvtog = (hrsl >> MAX3421_HRSL_RCVTOGRD_BIT) & 1;
+ sndtog = (hrsl >> MAX3421_HRSL_SNDTOGRD_BIT) & 1;
+
+ /* no locking: HCD (i.e., we) own toggles, don't we? */
+ usb_settoggle(old_dev, old_epnum, 0, rcvtog);
+ usb_settoggle(old_dev, old_epnum, 1, sndtog);
+ }
+ /* setup new endpoint's toggle bits: */
+ rcvtog = usb_gettoggle(dev, epnum, 0);
+ sndtog = usb_gettoggle(dev, epnum, 1);
+ hctl = (BIT(rcvtog + MAX3421_HCTL_RCVTOG0_BIT) |
+ BIT(sndtog + MAX3421_HCTL_SNDTOG0_BIT));
+
+ max3421_hcd->loaded_epnum = epnum;
+ spi_wr8(hcd, MAX3421_REG_HCTL, hctl);
+
+ /*
+ * Note: devnum for one and the same device can change during
+ * address-assignment so it's best to just always load the
+ * address whenever the end-point changed/was forced.
+ */
+ max3421_hcd->loaded_dev = dev;
+ spi_wr8(hcd, MAX3421_REG_PERADDR, dev->devnum);
+}
+
+static int
+max3421_ctrl_setup(struct usb_hcd *hcd, struct urb *urb)
+{
+ spi_wr_buf(hcd, MAX3421_REG_SUDFIFO, urb->setup_packet, 8);
+ return MAX3421_HXFR_SETUP;
+}
+
+static int
+max3421_transfer_in(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ int epnum = usb_pipeendpoint(urb->pipe);
+
+ max3421_hcd->curr_len = 0;
+ max3421_hcd->hien |= BIT(MAX3421_HI_RCVDAV_BIT);
+ return MAX3421_HXFR_BULK_IN(epnum);
+}
+
+static int
+max3421_transfer_out(struct usb_hcd *hcd, struct urb *urb, int fast_retransmit)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ int epnum = usb_pipeendpoint(urb->pipe);
+ u32 max_packet;
+ void *src;
+
+ src = urb->transfer_buffer + urb->actual_length;
+
+ if (fast_retransmit) {
+ if (max3421_hcd->rev == 0x12) {
+ /* work around rev 0x12 bug: */
+ spi_wr8(hcd, MAX3421_REG_SNDBC, 0);
+ spi_wr8(hcd, MAX3421_REG_SNDFIFO, ((u8 *) src)[0]);
+ spi_wr8(hcd, MAX3421_REG_SNDBC, max3421_hcd->curr_len);
+ }
+ return MAX3421_HXFR_BULK_OUT(epnum);
+ }
+
+ max_packet = usb_maxpacket(urb->dev, urb->pipe, 1);
+
+ if (max_packet > MAX3421_FIFO_SIZE) {
+ /*
+ * We do not support isochronous transfers at this
+ * time.
+ */
+ dev_err(&spi->dev,
+ "%s: packet-size of %u too big (limit is %u bytes)",
+ __func__, max_packet, MAX3421_FIFO_SIZE);
+ max3421_hcd->urb_done = -EMSGSIZE;
+ return -EMSGSIZE;
+ }
+ max3421_hcd->curr_len = min((urb->transfer_buffer_length -
+ urb->actual_length), max_packet);
+
+ spi_wr_buf(hcd, MAX3421_REG_SNDFIFO, src, max3421_hcd->curr_len);
+ spi_wr8(hcd, MAX3421_REG_SNDBC, max3421_hcd->curr_len);
+ return MAX3421_HXFR_BULK_OUT(epnum);
+}
+
+/*
+ * Issue the next host-transfer command.
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_next_transfer(struct usb_hcd *hcd, int fast_retransmit)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct urb *urb = max3421_hcd->curr_urb;
+ struct max3421_ep *max3421_ep;
+ int cmd = -EINVAL;
+
+ if (!urb)
+ return; /* nothing to do */
+
+ max3421_ep = urb->ep->hcpriv;
+
+ switch (max3421_ep->pkt_state) {
+ case PKT_STATE_SETUP:
+ cmd = max3421_ctrl_setup(hcd, urb);
+ break;
+
+ case PKT_STATE_TRANSFER:
+ if (usb_urb_dir_in(urb))
+ cmd = max3421_transfer_in(hcd, urb);
+ else
+ cmd = max3421_transfer_out(hcd, urb, fast_retransmit);
+ break;
+
+ case PKT_STATE_TERMINATE:
+ /*
+ * IN transfers are terminated with HS_OUT token,
+ * OUT transfers with HS_IN:
+ */
+ if (usb_urb_dir_in(urb))
+ cmd = MAX3421_HXFR_HS_OUT;
+ else
+ cmd = MAX3421_HXFR_HS_IN;
+ break;
+ }
+
+ if (cmd < 0)
+ return;
+
+ /* issue the command and wait for host-xfer-done interrupt: */
+
+ spi_wr8(hcd, MAX3421_REG_HXFR, cmd);
+ max3421_hcd->hien |= BIT(MAX3421_HI_HXFRDN_BIT);
+}
+
+/*
+ * Find the next URB to process and start its execution.
+ *
+ * At this time, we do not anticipate ever connecting a USB hub to the
+ * MAX3421 chip, so at most USB device can be connected and we can use
+ * a simplistic scheduler: at the start of a frame, schedule all
+ * periodic transfers. Once that is done, use the remainder of the
+ * frame to process non-periodic (bulk & control) transfers.
+ *
+ * Preconditions:
+ * o Caller must NOT hold HCD spinlock.
+ * o max3421_hcd->curr_urb MUST BE NULL.
+ * o MAX3421E chip must be idle.
+ */
+static int
+max3421_select_and_start_urb(struct usb_hcd *hcd)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct urb *urb, *curr_urb = NULL;
+ struct max3421_ep *max3421_ep;
+ int epnum, force_toggles = 0;
+ struct usb_host_endpoint *ep;
+ struct list_head *pos;
+ unsigned long flags;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ for (;
+ max3421_hcd->sched_pass < SCHED_PASS_DONE;
+ ++max3421_hcd->sched_pass)
+ list_for_each(pos, &max3421_hcd->ep_list) {
+ urb = NULL;
+ max3421_ep = container_of(pos, struct max3421_ep,
+ ep_list);
+ ep = max3421_ep->ep;
+
+ switch (usb_endpoint_type(&ep->desc)) {
+ case USB_ENDPOINT_XFER_ISOC:
+ case USB_ENDPOINT_XFER_INT:
+ if (max3421_hcd->sched_pass !=
+ SCHED_PASS_PERIODIC)
+ continue;
+ break;
+
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ if (max3421_hcd->sched_pass !=
+ SCHED_PASS_NON_PERIODIC)
+ continue;
+ break;
+ }
+
+ if (list_empty(&ep->urb_list))
+ continue; /* nothing to do */
+ urb = list_first_entry(&ep->urb_list, struct urb,
+ urb_list);
+ if (urb->unlinked) {
+ dev_dbg(&spi->dev, "%s: URB %p unlinked=%d",
+ __func__, urb, urb->unlinked);
+ max3421_hcd->curr_urb = urb;
+ max3421_hcd->urb_done = 1;
+ spin_unlock_irqrestore(&max3421_hcd->lock,
+ flags);
+ return 1;
+ }
+
+ switch (usb_endpoint_type(&ep->desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /*
+ * Allow one control transaction per
+ * frame per endpoint:
+ */
+ if (frame_diff(max3421_ep->last_active,
+ max3421_hcd->frame_number) == 0)
+ continue;
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ if (max3421_ep->retransmit
+ && (frame_diff(max3421_ep->last_active,
+ max3421_hcd->frame_number)
+ == 0))
+ /*
+ * We already tried this EP
+ * during this frame and got a
+ * NAK or error; wait for next frame
+ */
+ continue;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ case USB_ENDPOINT_XFER_INT:
+ if (frame_diff(max3421_hcd->frame_number,
+ max3421_ep->last_active)
+ < urb->interval)
+ /*
+ * We already processed this
+ * end-point in the current
+ * frame
+ */
+ continue;
+ break;
+ }
+
+ /* move current ep to tail: */
+ list_move_tail(pos, &max3421_hcd->ep_list);
+ curr_urb = urb;
+ goto done;
+ }
+done:
+ if (!curr_urb) {
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return 0;
+ }
+
+ urb = max3421_hcd->curr_urb = curr_urb;
+ epnum = usb_endpoint_num(&urb->ep->desc);
+ if (max3421_ep->retransmit)
+ /* restart (part of) a USB transaction: */
+ max3421_ep->retransmit = 0;
+ else {
+ /* start USB transaction: */
+ if (usb_endpoint_xfer_control(&ep->desc)) {
+ /*
+ * See USB 2.0 spec section 8.6.1
+ * Initialization via SETUP Token:
+ */
+ usb_settoggle(urb->dev, epnum, 0, 1);
+ usb_settoggle(urb->dev, epnum, 1, 1);
+ max3421_ep->pkt_state = PKT_STATE_SETUP;
+ force_toggles = 1;
+ } else
+ max3421_ep->pkt_state = PKT_STATE_TRANSFER;
+ }
+
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+
+ max3421_ep->last_active = max3421_hcd->frame_number;
+ max3421_set_address(hcd, urb->dev, epnum, force_toggles);
+ max3421_set_speed(hcd, urb->dev);
+ max3421_next_transfer(hcd, 0);
+ return 1;
+}
+
+/*
+ * Check all endpoints for URBs that got unlinked.
+ *
+ * Caller must NOT hold HCD spinlock.
+ */
+static int
+max3421_check_unlink(struct usb_hcd *hcd)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct list_head *pos, *upos, *next_upos;
+ struct max3421_ep *max3421_ep;
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ list_for_each(pos, &max3421_hcd->ep_list) {
+ max3421_ep = container_of(pos, struct max3421_ep, ep_list);
+ ep = max3421_ep->ep;
+ list_for_each_safe(upos, next_upos, &ep->urb_list) {
+ urb = container_of(upos, struct urb, urb_list);
+ if (urb->unlinked) {
+ retval = 1;
+ dev_dbg(&spi->dev, "%s: URB %p unlinked=%d",
+ __func__, urb, urb->unlinked);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&max3421_hcd->lock,
+ flags);
+ usb_hcd_giveback_urb(hcd, urb, 0);
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return retval;
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_slow_retransmit(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct urb *urb = max3421_hcd->curr_urb;
+ struct max3421_ep *max3421_ep;
+
+ max3421_ep = urb->ep->hcpriv;
+ max3421_ep->retransmit = 1;
+ max3421_hcd->curr_urb = NULL;
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_recv_data_available(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct urb *urb = max3421_hcd->curr_urb;
+ size_t remaining, transfer_size;
+ u8 rcvbc;
+
+ rcvbc = spi_rd8(hcd, MAX3421_REG_RCVBC);
+
+ if (rcvbc > MAX3421_FIFO_SIZE)
+ rcvbc = MAX3421_FIFO_SIZE;
+ if (urb->actual_length >= urb->transfer_buffer_length)
+ remaining = 0;
+ else
+ remaining = urb->transfer_buffer_length - urb->actual_length;
+ transfer_size = rcvbc;
+ if (transfer_size > remaining)
+ transfer_size = remaining;
+ if (transfer_size > 0) {
+ void *dst = urb->transfer_buffer + urb->actual_length;
+
+ spi_rd_buf(hcd, MAX3421_REG_RCVFIFO, dst, transfer_size);
+ urb->actual_length += transfer_size;
+ max3421_hcd->curr_len = transfer_size;
+ }
+
+ /* ack the RCVDAV irq now that the FIFO has been read: */
+ spi_wr8(hcd, MAX3421_REG_HIRQ, BIT(MAX3421_HI_RCVDAV_BIT));
+}
+
+static void
+max3421_handle_error(struct usb_hcd *hcd, u8 hrsl)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ u8 result_code = hrsl & MAX3421_HRSL_RESULT_MASK;
+ struct urb *urb = max3421_hcd->curr_urb;
+ struct max3421_ep *max3421_ep = urb->ep->hcpriv;
+ int switch_sndfifo;
+
+ /*
+ * If an OUT command results in any response other than OK
+ * (i.e., error or NAK), we have to perform a dummy-write to
+ * SNDBC so the FIFO gets switched back to us. Otherwise, we
+ * get out of sync with the SNDFIFO double buffer.
+ */
+ switch_sndfifo = (max3421_ep->pkt_state == PKT_STATE_TRANSFER &&
+ usb_urb_dir_out(urb));
+
+ switch (result_code) {
+ case MAX3421_HRSL_OK:
+ return; /* this shouldn't happen */
+
+ case MAX3421_HRSL_WRONGPID: /* received wrong PID */
+ case MAX3421_HRSL_BUSY: /* SIE busy */
+ case MAX3421_HRSL_BADREQ: /* bad val in HXFR */
+ case MAX3421_HRSL_UNDEF: /* reserved */
+ case MAX3421_HRSL_KERR: /* K-state instead of response */
+ case MAX3421_HRSL_JERR: /* J-state instead of response */
+ /*
+ * packet experienced an error that we cannot recover
+ * from; report error
+ */
+ max3421_hcd->urb_done = hrsl_to_error[result_code];
+ dev_dbg(&spi->dev, "%s: unexpected error HRSL=0x%02x",
+ __func__, hrsl);
+ break;
+
+ case MAX3421_HRSL_TOGERR:
+ if (usb_urb_dir_in(urb))
+ ; /* don't do anything (device will switch toggle) */
+ else {
+ /* flip the send toggle bit: */
+ int sndtog = (hrsl >> MAX3421_HRSL_SNDTOGRD_BIT) & 1;
+
+ sndtog ^= 1;
+ spi_wr8(hcd, MAX3421_REG_HCTL,
+ BIT(sndtog + MAX3421_HCTL_SNDTOG0_BIT));
+ }
+ /* FALL THROUGH */
+ case MAX3421_HRSL_BADBC: /* bad byte count */
+ case MAX3421_HRSL_PIDERR: /* received PID is corrupted */
+ case MAX3421_HRSL_PKTERR: /* packet error (stuff, EOP) */
+ case MAX3421_HRSL_CRCERR: /* CRC error */
+ case MAX3421_HRSL_BABBLE: /* device talked too long */
+ case MAX3421_HRSL_TIMEOUT:
+ if (max3421_ep->retries++ < USB_MAX_RETRIES)
+ /* retry the packet again in the next frame */
+ max3421_slow_retransmit(hcd);
+ else {
+ /* Based on ohci.h cc_to_err[]: */
+ max3421_hcd->urb_done = hrsl_to_error[result_code];
+ dev_dbg(&spi->dev, "%s: unexpected error HRSL=0x%02x",
+ __func__, hrsl);
+ }
+ break;
+
+ case MAX3421_HRSL_STALL:
+ dev_dbg(&spi->dev, "%s: unexpected error HRSL=0x%02x",
+ __func__, hrsl);
+ max3421_hcd->urb_done = hrsl_to_error[result_code];
+ break;
+
+ case MAX3421_HRSL_NAK:
+ /*
+ * Device wasn't ready for data or has no data
+ * available: retry the packet again.
+ */
+ if (max3421_ep->naks++ < NAK_MAX_FAST_RETRANSMITS) {
+ max3421_next_transfer(hcd, 1);
+ switch_sndfifo = 0;
+ } else
+ max3421_slow_retransmit(hcd);
+ break;
+ }
+ if (switch_sndfifo)
+ spi_wr8(hcd, MAX3421_REG_SNDBC, 0);
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static int
+max3421_transfer_in_done(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ u32 max_packet;
+
+ if (urb->actual_length >= urb->transfer_buffer_length)
+ return 1; /* read is complete, so we're done */
+
+ /*
+ * USB 2.0 Section 5.3.2 Pipes: packets must be full size
+ * except for last one.
+ */
+ max_packet = usb_maxpacket(urb->dev, urb->pipe, 0);
+ if (max_packet > MAX3421_FIFO_SIZE) {
+ /*
+ * We do not support isochronous transfers at this
+ * time...
+ */
+ dev_err(&spi->dev,
+ "%s: packet-size of %u too big (limit is %u bytes)",
+ __func__, max_packet, MAX3421_FIFO_SIZE);
+ return -EINVAL;
+ }
+
+ if (max3421_hcd->curr_len < max_packet) {
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ /*
+ * remaining > 0 and received an
+ * unexpected partial packet ->
+ * error
+ */
+ return -EREMOTEIO;
+ } else
+ /* short read, but it's OK */
+ return 1;
+ }
+ return 0; /* not done */
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static int
+max3421_transfer_out_done(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+
+ urb->actual_length += max3421_hcd->curr_len;
+ if (urb->actual_length < urb->transfer_buffer_length)
+ return 0;
+ if (urb->transfer_flags & URB_ZERO_PACKET) {
+ /*
+ * Some hardware needs a zero-size packet at the end
+ * of a bulk-out transfer if the last transfer was a
+ * full-sized packet (i.e., such hardware use <
+ * max_packet as an indicator that the end of the
+ * packet has been reached).
+ */
+ u32 max_packet = usb_maxpacket(urb->dev, urb->pipe, 1);
+
+ if (max3421_hcd->curr_len == max_packet)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_host_transfer_done(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct urb *urb = max3421_hcd->curr_urb;
+ struct max3421_ep *max3421_ep;
+ u8 result_code, hrsl;
+ int urb_done = 0;
+
+ max3421_hcd->hien &= ~(BIT(MAX3421_HI_HXFRDN_BIT) |
+ BIT(MAX3421_HI_RCVDAV_BIT));
+
+ hrsl = spi_rd8(hcd, MAX3421_REG_HRSL);
+ result_code = hrsl & MAX3421_HRSL_RESULT_MASK;
+
+#ifdef DEBUG
+ ++max3421_hcd->err_stat[result_code];
+#endif
+
+ max3421_ep = urb->ep->hcpriv;
+
+ if (unlikely(result_code != MAX3421_HRSL_OK)) {
+ max3421_handle_error(hcd, hrsl);
+ return;
+ }
+
+ max3421_ep->naks = 0;
+ max3421_ep->retries = 0;
+ switch (max3421_ep->pkt_state) {
+
+ case PKT_STATE_SETUP:
+ if (urb->transfer_buffer_length > 0)
+ max3421_ep->pkt_state = PKT_STATE_TRANSFER;
+ else
+ max3421_ep->pkt_state = PKT_STATE_TERMINATE;
+ break;
+
+ case PKT_STATE_TRANSFER:
+ if (usb_urb_dir_in(urb))
+ urb_done = max3421_transfer_in_done(hcd, urb);
+ else
+ urb_done = max3421_transfer_out_done(hcd, urb);
+ if (urb_done > 0 && usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+ /*
+ * We aren't really done - we still need to
+ * terminate the control transfer:
+ */
+ max3421_hcd->urb_done = urb_done = 0;
+ max3421_ep->pkt_state = PKT_STATE_TERMINATE;
+ }
+ break;
+
+ case PKT_STATE_TERMINATE:
+ urb_done = 1;
+ break;
+ }
+
+ if (urb_done)
+ max3421_hcd->urb_done = urb_done;
+ else
+ max3421_next_transfer(hcd, 0);
+}
+
+/*
+ * Caller must NOT hold HCD spinlock.
+ */
+static void
+max3421_detect_conn(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ unsigned int jk, have_conn = 0;
+ u32 old_port_status, chg;
+ unsigned long flags;
+ u8 hrsl, mode;
+
+ hrsl = spi_rd8(hcd, MAX3421_REG_HRSL);
+
+ jk = ((((hrsl >> MAX3421_HRSL_JSTATUS_BIT) & 1) << 0) |
+ (((hrsl >> MAX3421_HRSL_KSTATUS_BIT) & 1) << 1));
+
+ mode = max3421_hcd->mode;
+
+ switch (jk) {
+ case 0x0: /* SE0: disconnect */
+ /*
+ * Turn off SOFKAENAB bit to avoid getting interrupt
+ * every milli-second:
+ */
+ mode &= ~BIT(MAX3421_MODE_SOFKAENAB_BIT);
+ break;
+
+ case 0x1: /* J=0,K=1: low-speed (in full-speed or vice versa) */
+ case 0x2: /* J=1,K=0: full-speed (in full-speed or vice versa) */
+ if (jk == 0x2)
+ /* need to switch to the other speed: */
+ mode ^= BIT(MAX3421_MODE_LOWSPEED_BIT);
+ /* turn on SOFKAENAB bit: */
+ mode |= BIT(MAX3421_MODE_SOFKAENAB_BIT);
+ have_conn = 1;
+ break;
+
+ case 0x3: /* illegal */
+ break;
+ }
+
+ max3421_hcd->mode = mode;
+ spi_wr8(hcd, MAX3421_REG_MODE, max3421_hcd->mode);
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ old_port_status = max3421_hcd->port_status;
+ if (have_conn)
+ max3421_hcd->port_status |= USB_PORT_STAT_CONNECTION;
+ else
+ max3421_hcd->port_status &= ~USB_PORT_STAT_CONNECTION;
+ if (mode & BIT(MAX3421_MODE_LOWSPEED_BIT))
+ max3421_hcd->port_status |= USB_PORT_STAT_LOW_SPEED;
+ else
+ max3421_hcd->port_status &= ~USB_PORT_STAT_LOW_SPEED;
+ chg = (old_port_status ^ max3421_hcd->port_status);
+ max3421_hcd->port_status |= chg << 16;
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+}
+
+static irqreturn_t
+max3421_irq_handler(int irq, void *dev_id)
+{
+ struct usb_hcd *hcd = dev_id;
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+
+ if (max3421_hcd->spi_thread &&
+ max3421_hcd->spi_thread->state != TASK_RUNNING)
+ wake_up_process(max3421_hcd->spi_thread);
+ if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo))
+ disable_irq_nosync(spi->irq);
+ return IRQ_HANDLED;
+}
+
+#ifdef DEBUG
+
+static void
+dump_eps(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct max3421_ep *max3421_ep;
+ struct usb_host_endpoint *ep;
+ struct list_head *pos, *upos;
+ char ubuf[512], *dp, *end;
+ unsigned long flags;
+ struct urb *urb;
+ int epnum, ret;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ list_for_each(pos, &max3421_hcd->ep_list) {
+ max3421_ep = container_of(pos, struct max3421_ep, ep_list);
+ ep = max3421_ep->ep;
+
+ dp = ubuf;
+ end = dp + sizeof(ubuf);
+ *dp = '\0';
+ list_for_each(upos, &ep->urb_list) {
+ urb = container_of(upos, struct urb, urb_list);
+ ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb,
+ usb_pipetype(urb->pipe),
+ usb_urb_dir_in(urb) ? "IN" : "OUT",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ if (ret < 0 || ret >= end - dp)
+ break; /* error or buffer full */
+ dp += ret;
+ }
+
+ epnum = usb_endpoint_num(&ep->desc);
+ pr_info("EP%0u %u lst %04u rtr %u nak %6u rxmt %u: %s\n",
+ epnum, max3421_ep->pkt_state, max3421_ep->last_active,
+ max3421_ep->retries, max3421_ep->naks,
+ max3421_ep->retransmit, ubuf);
+ }
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+}
+
+#endif /* DEBUG */
+
+/* Return zero if no work was performed, 1 otherwise. */
+static int
+max3421_handle_irqs(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ u32 chg, old_port_status;
+ unsigned long flags;
+ u8 hirq;
+
+ /*
+ * Read and ack pending interrupts (CPU must never
+ * clear SNDBAV directly and RCVDAV must be cleared by
+ * max3421_recv_data_available()!):
+ */
+ hirq = spi_rd8(hcd, MAX3421_REG_HIRQ);
+ hirq &= max3421_hcd->hien;
+ if (!hirq)
+ return 0;
+
+ spi_wr8(hcd, MAX3421_REG_HIRQ,
+ hirq & ~(BIT(MAX3421_HI_SNDBAV_BIT) |
+ BIT(MAX3421_HI_RCVDAV_BIT)));
+
+ if (hirq & BIT(MAX3421_HI_FRAME_BIT)) {
+ max3421_hcd->frame_number = ((max3421_hcd->frame_number + 1)
+ & USB_MAX_FRAME_NUMBER);
+ max3421_hcd->sched_pass = SCHED_PASS_PERIODIC;
+ }
+
+ if (hirq & BIT(MAX3421_HI_RCVDAV_BIT))
+ max3421_recv_data_available(hcd);
+
+ if (hirq & BIT(MAX3421_HI_HXFRDN_BIT))
+ max3421_host_transfer_done(hcd);
+
+ if (hirq & BIT(MAX3421_HI_CONDET_BIT))
+ max3421_detect_conn(hcd);
+
+ /*
+ * Now process interrupts that may affect HCD state
+ * other than the end-points:
+ */
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ old_port_status = max3421_hcd->port_status;
+ if (hirq & BIT(MAX3421_HI_BUSEVENT_BIT)) {
+ if (max3421_hcd->port_status & USB_PORT_STAT_RESET) {
+ /* BUSEVENT due to completion of Bus Reset */
+ max3421_hcd->port_status &= ~USB_PORT_STAT_RESET;
+ max3421_hcd->port_status |= USB_PORT_STAT_ENABLE;
+ } else {
+ /* BUSEVENT due to completion of Bus Resume */
+ pr_info("%s: BUSEVENT Bus Resume Done\n", __func__);
+ }
+ }
+ if (hirq & BIT(MAX3421_HI_RWU_BIT))
+ pr_info("%s: RWU\n", __func__);
+ if (hirq & BIT(MAX3421_HI_SUSDN_BIT))
+ pr_info("%s: SUSDN\n", __func__);
+
+ chg = (old_port_status ^ max3421_hcd->port_status);
+ max3421_hcd->port_status |= chg << 16;
+
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+
+#ifdef DEBUG
+ {
+ static unsigned long last_time;
+ char sbuf[16 * 16], *dp, *end;
+ int i;
+
+ if (time_after(jiffies, last_time + 5*HZ)) {
+ dp = sbuf;
+ end = sbuf + sizeof(sbuf);
+ *dp = '\0';
+ for (i = 0; i < 16; ++i) {
+ int ret = snprintf(dp, end - dp, " %lu",
+ max3421_hcd->err_stat[i]);
+ if (ret < 0 || ret >= end - dp)
+ break; /* error or buffer full */
+ dp += ret;
+ }
+ pr_info("%s: hrsl_stats %s\n", __func__, sbuf);
+ memset(max3421_hcd->err_stat, 0,
+ sizeof(max3421_hcd->err_stat));
+ last_time = jiffies;
+
+ dump_eps(hcd);
+ }
+ }
+#endif
+ return 1;
+}
+
+static int
+max3421_reset_hcd(struct usb_hcd *hcd)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ int timeout;
+
+ /* perform a chip reset and wait for OSCIRQ signal to appear: */
+ spi_wr8(hcd, MAX3421_REG_USBCTL, BIT(MAX3421_USBCTL_CHIPRES_BIT));
+ /* clear reset: */
+ spi_wr8(hcd, MAX3421_REG_USBCTL, 0);
+ timeout = 1000;
+ while (1) {
+ if (spi_rd8(hcd, MAX3421_REG_USBIRQ)
+ & BIT(MAX3421_USBIRQ_OSCOKIRQ_BIT))
+ break;
+ if (--timeout < 0) {
+ dev_err(&spi->dev,
+ "timed out waiting for oscillator OK signal");
+ return 1;
+ }
+ cond_resched();
+ }
+
+ /*
+ * Turn on host mode, automatic generation of SOF packets, and
+ * enable pull-down registers on DM/DP:
+ */
+ max3421_hcd->mode = (BIT(MAX3421_MODE_HOST_BIT) |
+ BIT(MAX3421_MODE_SOFKAENAB_BIT) |
+ BIT(MAX3421_MODE_DMPULLDN_BIT) |
+ BIT(MAX3421_MODE_DPPULLDN_BIT));
+ spi_wr8(hcd, MAX3421_REG_MODE, max3421_hcd->mode);
+
+ /* reset frame-number: */
+ max3421_hcd->frame_number = USB_MAX_FRAME_NUMBER;
+ spi_wr8(hcd, MAX3421_REG_HCTL, BIT(MAX3421_HCTL_FRMRST_BIT));
+
+ /* sample the state of the D+ and D- lines */
+ spi_wr8(hcd, MAX3421_REG_HCTL, BIT(MAX3421_HCTL_SAMPLEBUS_BIT));
+ max3421_detect_conn(hcd);
+
+ /* enable frame, connection-detected, and bus-event interrupts: */
+ max3421_hcd->hien = (BIT(MAX3421_HI_FRAME_BIT) |
+ BIT(MAX3421_HI_CONDET_BIT) |
+ BIT(MAX3421_HI_BUSEVENT_BIT));
+ spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien);
+
+ /* enable interrupts: */
+ spi_wr8(hcd, MAX3421_REG_CPUCTL, BIT(MAX3421_CPUCTL_IE_BIT));
+ return 1;
+}
+
+static int
+max3421_urb_done(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ unsigned long flags;
+ struct urb *urb;
+ int status;
+
+ status = max3421_hcd->urb_done;
+ max3421_hcd->urb_done = 0;
+ if (status > 0)
+ status = 0;
+ urb = max3421_hcd->curr_urb;
+ if (urb) {
+ max3421_hcd->curr_urb = NULL;
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+
+ /* must be called without the HCD spinlock: */
+ usb_hcd_giveback_urb(hcd, urb, status);
+ }
+ return 1;
+}
+
+static int
+max3421_spi_thread(void *dev_id)
+{
+ struct usb_hcd *hcd = dev_id;
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ int i, i_worked = 1;
+
+ /* set full-duplex SPI mode, low-active interrupt pin: */
+ spi_wr8(hcd, MAX3421_REG_PINCTL,
+ (BIT(MAX3421_PINCTL_FDUPSPI_BIT) | /* full-duplex */
+ BIT(MAX3421_PINCTL_INTLEVEL_BIT))); /* low-active irq */
+
+ while (!kthread_should_stop()) {
+ max3421_hcd->rev = spi_rd8(hcd, MAX3421_REG_REVISION);
+ if (max3421_hcd->rev == 0x12 || max3421_hcd->rev == 0x13)
+ break;
+ dev_err(&spi->dev, "bad rev 0x%02x", max3421_hcd->rev);
+ msleep(10000);
+ }
+ dev_info(&spi->dev, "rev 0x%x, SPI clk %dHz, bpw %u, irq %d\n",
+ max3421_hcd->rev, spi->max_speed_hz, spi->bits_per_word,
+ spi->irq);
+
+ while (!kthread_should_stop()) {
+ if (!i_worked) {
+ /*
+ * We'll be waiting for wakeups from the hard
+ * interrupt handler, so now is a good time to
+ * sync our hien with the chip:
+ */
+ spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo))
+ enable_irq(spi->irq);
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ }
+
+ i_worked = 0;
+
+ if (max3421_hcd->urb_done)
+ i_worked |= max3421_urb_done(hcd);
+ else if (max3421_handle_irqs(hcd))
+ i_worked = 1;
+ else if (!max3421_hcd->curr_urb)
+ i_worked |= max3421_select_and_start_urb(hcd);
+
+ if (test_and_clear_bit(RESET_HCD, &max3421_hcd->todo))
+ /* reset the HCD: */
+ i_worked |= max3421_reset_hcd(hcd);
+ if (test_and_clear_bit(RESET_PORT, &max3421_hcd->todo)) {
+ /* perform a USB bus reset: */
+ spi_wr8(hcd, MAX3421_REG_HCTL,
+ BIT(MAX3421_HCTL_BUSRST_BIT));
+ i_worked = 1;
+ }
+ if (test_and_clear_bit(CHECK_UNLINK, &max3421_hcd->todo))
+ i_worked |= max3421_check_unlink(hcd);
+ if (test_and_clear_bit(IOPIN_UPDATE, &max3421_hcd->todo)) {
+ /*
+ * IOPINS1/IOPINS2 do not auto-increment, so we can't
+ * use spi_wr_buf().
+ */
+ for (i = 0; i < ARRAY_SIZE(max3421_hcd->iopins); ++i) {
+ u8 val = spi_rd8(hcd, MAX3421_REG_IOPINS1);
+
+ val = ((val & 0xf0) |
+ (max3421_hcd->iopins[i] & 0x0f));
+ spi_wr8(hcd, MAX3421_REG_IOPINS1 + i, val);
+ max3421_hcd->iopins[i] = val;
+ }
+ i_worked = 1;
+ }
+ }
+ set_current_state(TASK_RUNNING);
+ dev_info(&spi->dev, "SPI thread exiting");
+ return 0;
+}
+
+static int
+max3421_reset_port(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+
+ max3421_hcd->port_status &= ~(USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED);
+ max3421_hcd->port_status |= USB_PORT_STAT_RESET;
+ set_bit(RESET_PORT, &max3421_hcd->todo);
+ wake_up_process(max3421_hcd->spi_thread);
+ return 0;
+}
+
+static int
+max3421_reset(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+
+ hcd->self.sg_tablesize = 0;
+ hcd->speed = HCD_USB2;
+ hcd->self.root_hub->speed = USB_SPEED_FULL;
+ set_bit(RESET_HCD, &max3421_hcd->todo);
+ wake_up_process(max3421_hcd->spi_thread);
+ return 0;
+}
+
+static int
+max3421_start(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+
+ spin_lock_init(&max3421_hcd->lock);
+ max3421_hcd->rh_state = MAX3421_RH_RUNNING;
+
+ INIT_LIST_HEAD(&max3421_hcd->ep_list);
+
+ hcd->power_budget = POWER_BUDGET;
+ hcd->state = HC_STATE_RUNNING;
+ hcd->uses_new_polling = 1;
+ return 0;
+}
+
+static void
+max3421_stop(struct usb_hcd *hcd)
+{
+}
+
+static int
+max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct max3421_ep *max3421_ep;
+ unsigned long flags;
+ int retval;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ case PIPE_ISOCHRONOUS:
+ if (urb->interval < 0) {
+ dev_err(&spi->dev,
+ "%s: interval=%d for intr-/iso-pipe; expected > 0\n",
+ __func__, urb->interval);
+ return -EINVAL;
+ }
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ max3421_ep = urb->ep->hcpriv;
+ if (!max3421_ep) {
+ /* gets freed in max3421_endpoint_disable: */
+ max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC);
+ if (!max3421_ep) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ max3421_ep->ep = urb->ep;
+ max3421_ep->last_active = max3421_hcd->frame_number;
+ urb->ep->hcpriv = max3421_ep;
+
+ list_add_tail(&max3421_ep->ep_list, &max3421_hcd->ep_list);
+ }
+
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval == 0) {
+ /* Since we added to the queue, restart scheduling: */
+ max3421_hcd->sched_pass = SCHED_PASS_PERIODIC;
+ wake_up_process(max3421_hcd->spi_thread);
+ }
+
+out:
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return retval;
+}
+
+static int
+max3421_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ /*
+ * This will set urb->unlinked which in turn causes the entry
+ * to be dropped at the next opportunity.
+ */
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval == 0) {
+ set_bit(CHECK_UNLINK, &max3421_hcd->todo);
+ wake_up_process(max3421_hcd->spi_thread);
+ }
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return retval;
+}
+
+static void
+max3421_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ unsigned long flags;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ if (ep->hcpriv) {
+ struct max3421_ep *max3421_ep = ep->hcpriv;
+
+ /* remove myself from the ep_list: */
+ if (!list_empty(&max3421_ep->ep_list))
+ list_del(&max3421_ep->ep_list);
+ kfree(max3421_ep);
+ ep->hcpriv = NULL;
+ }
+
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+}
+
+static int
+max3421_get_frame_number(struct usb_hcd *hcd)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ return max3421_hcd->frame_number;
+}
+
+/*
+ * Should return a non-zero value when any port is undergoing a resume
+ * transition while the root hub is suspended.
+ */
+static int
+max3421_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ goto done;
+
+ *buf = 0;
+ if ((max3421_hcd->port_status & PORT_C_MASK) != 0) {
+ *buf = (1 << 1); /* a hub over-current condition exists */
+ dev_dbg(hcd->self.controller,
+ "port status 0x%08x has changes\n",
+ max3421_hcd->port_status);
+ retval = 1;
+ if (max3421_hcd->rh_state == MAX3421_RH_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+ }
+done:
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return retval;
+}
+
+static inline void
+hub_descriptor(struct usb_hub_descriptor *desc)
+{
+ memset(desc, 0, sizeof(*desc));
+ /*
+ * See Table 11-13: Hub Descriptor in USB 2.0 spec.
+ */
+ desc->bDescriptorType = USB_DT_HUB; /* hub descriptor */
+ desc->bDescLength = 9;
+ desc->wHubCharacteristics = cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
+ desc->bNbrPorts = 1;
+}
+
+/*
+ * Set the MAX3421E general-purpose output with number PIN_NUMBER to
+ * VALUE (0 or 1). PIN_NUMBER may be in the range from 1-8. For
+ * any other value, this function acts as a no-op.
+ */
+static void
+max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value)
+{
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ u8 mask, idx;
+
+ --pin_number;
+ if (pin_number > 7)
+ return;
+
+ mask = 1u << pin_number;
+ idx = pin_number / 4;
+
+ if (value)
+ max3421_hcd->iopins[idx] |= mask;
+ else
+ max3421_hcd->iopins[idx] &= ~mask;
+ set_bit(IOPIN_UPDATE, &max3421_hcd->todo);
+ wake_up_process(max3421_hcd->spi_thread);
+}
+
+static int
+max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
+ char *buf, u16 length)
+{
+ struct spi_device *spi = to_spi_device(hcd->self.controller);
+ struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
+ struct max3421_hcd_platform_data *pdata;
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ pdata = spi->dev.platform_data;
+
+ switch (type_req) {
+ case ClearHubFeature:
+ break;
+ case ClearPortFeature:
+ switch (value) {
+ case USB_PORT_FEAT_SUSPEND:
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller, "power-off\n");
+ max3421_gpout_set_value(hcd, pdata->vbus_gpout,
+ !pdata->vbus_active_level);
+ /* FALLS THROUGH */
+ default:
+ max3421_hcd->port_status &= ~(1 << value);
+ }
+ break;
+ case GetHubDescriptor:
+ hub_descriptor((struct usb_hub_descriptor *) buf);
+ break;
+
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ case GetPortErrorCount:
+ case SetHubDepth:
+ /* USB3 only */
+ goto error;
+
+ case GetHubStatus:
+ *(__le32 *) buf = cpu_to_le32(0);
+ break;
+
+ case GetPortStatus:
+ if (index != 1) {
+ retval = -EPIPE;
+ goto error;
+ }
+ ((__le16 *) buf)[0] = cpu_to_le16(max3421_hcd->port_status);
+ ((__le16 *) buf)[1] =
+ cpu_to_le16(max3421_hcd->port_status >> 16);
+ break;
+
+ case SetHubFeature:
+ retval = -EPIPE;
+ break;
+
+ case SetPortFeature:
+ switch (value) {
+ case USB_PORT_FEAT_LINK_STATE:
+ case USB_PORT_FEAT_U1_TIMEOUT:
+ case USB_PORT_FEAT_U2_TIMEOUT:
+ case USB_PORT_FEAT_BH_PORT_RESET:
+ goto error;
+ case USB_PORT_FEAT_SUSPEND:
+ if (max3421_hcd->active)
+ max3421_hcd->port_status |=
+ USB_PORT_STAT_SUSPEND;
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller, "power-on\n");
+ max3421_hcd->port_status |= USB_PORT_STAT_POWER;
+ max3421_gpout_set_value(hcd, pdata->vbus_gpout,
+ pdata->vbus_active_level);
+ break;
+ case USB_PORT_FEAT_RESET:
+ max3421_reset_port(hcd);
+ /* FALLS THROUGH */
+ default:
+ if ((max3421_hcd->port_status & USB_PORT_STAT_POWER)
+ != 0)
+ max3421_hcd->port_status |= (1 << value);
+ }
+ break;
+
+ default:
+ dev_dbg(hcd->self.controller,
+ "hub control req%04x v%04x i%04x l%d\n",
+ type_req, value, index, length);
+error: /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ return retval;
+}
+
+static int
+max3421_bus_suspend(struct usb_hcd *hcd)
+{
+ return -1;
+}
+
+static int
+max3421_bus_resume(struct usb_hcd *hcd)
+{
+ return -1;
+}
+
+/*
+ * The SPI driver already takes care of DMA-mapping/unmapping, so no
+ * reason to do it twice.
+ */
+static int
+max3421_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+ return 0;
+}
+
+static void
+max3421_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+}
+
+static struct hc_driver max3421_hcd_desc = {
+ .description = "max3421",
+ .product_desc = DRIVER_DESC,
+ .hcd_priv_size = sizeof(struct max3421_hcd),
+ .flags = HCD_USB11,
+ .reset = max3421_reset,
+ .start = max3421_start,
+ .stop = max3421_stop,
+ .get_frame_number = max3421_get_frame_number,
+ .urb_enqueue = max3421_urb_enqueue,
+ .urb_dequeue = max3421_urb_dequeue,
+ .map_urb_for_dma = max3421_map_urb_for_dma,
+ .unmap_urb_for_dma = max3421_unmap_urb_for_dma,
+ .endpoint_disable = max3421_endpoint_disable,
+ .hub_status_data = max3421_hub_status_data,
+ .hub_control = max3421_hub_control,
+ .bus_suspend = max3421_bus_suspend,
+ .bus_resume = max3421_bus_resume,
+};
+
+static int
+max3421_probe(struct spi_device *spi)
+{
+ struct max3421_hcd *max3421_hcd;
+ struct usb_hcd *hcd = NULL;
+ int retval = -ENOMEM;
+
+ if (spi_setup(spi) < 0) {
+ dev_err(&spi->dev, "Unable to setup SPI bus");
+ return -EFAULT;
+ }
+
+ hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev,
+ dev_name(&spi->dev));
+ if (!hcd) {
+ dev_err(&spi->dev, "failed to create HCD structure\n");
+ goto error;
+ }
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ max3421_hcd = hcd_to_max3421(hcd);
+ max3421_hcd->next = max3421_hcd_list;
+ max3421_hcd_list = max3421_hcd;
+ INIT_LIST_HEAD(&max3421_hcd->ep_list);
+
+ max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
+ if (!max3421_hcd->tx) {
+ dev_err(&spi->dev, "failed to kmalloc tx buffer\n");
+ goto error;
+ }
+ max3421_hcd->rx = kmalloc(sizeof(*max3421_hcd->rx), GFP_KERNEL);
+ if (!max3421_hcd->rx) {
+ dev_err(&spi->dev, "failed to kmalloc rx buffer\n");
+ goto error;
+ }
+
+ max3421_hcd->spi_thread = kthread_run(max3421_spi_thread, hcd,
+ "max3421_spi_thread");
+ if (max3421_hcd->spi_thread == ERR_PTR(-ENOMEM)) {
+ dev_err(&spi->dev,
+ "failed to create SPI thread (out of memory)\n");
+ goto error;
+ }
+
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval) {
+ dev_err(&spi->dev, "failed to add HCD\n");
+ goto error;
+ }
+
+ retval = request_irq(spi->irq, max3421_irq_handler,
+ IRQF_TRIGGER_LOW, "max3421", hcd);
+ if (retval < 0) {
+ dev_err(&spi->dev, "failed to request irq %d\n", spi->irq);
+ goto error;
+ }
+ return 0;
+
+error:
+ if (hcd) {
+ kfree(max3421_hcd->tx);
+ kfree(max3421_hcd->rx);
+ if (max3421_hcd->spi_thread)
+ kthread_stop(max3421_hcd->spi_thread);
+ usb_put_hcd(hcd);
+ }
+ return retval;
+}
+
+static int
+max3421_remove(struct spi_device *spi)
+{
+ struct max3421_hcd *max3421_hcd = NULL, **prev;
+ struct usb_hcd *hcd = NULL;
+ unsigned long flags;
+
+ for (prev = &max3421_hcd_list; *prev; prev = &(*prev)->next) {
+ max3421_hcd = *prev;
+ hcd = max3421_to_hcd(max3421_hcd);
+ if (hcd->self.controller == &spi->dev)
+ break;
+ }
+ if (!max3421_hcd) {
+ dev_err(&spi->dev, "no MAX3421 HCD found for SPI device %p\n",
+ spi);
+ return -ENODEV;
+ }
+
+ usb_remove_hcd(hcd);
+
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
+ kthread_stop(max3421_hcd->spi_thread);
+ *prev = max3421_hcd->next;
+
+ spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+
+ free_irq(spi->irq, hcd);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static struct spi_driver max3421_driver = {
+ .probe = max3421_probe,
+ .remove = max3421_remove,
+ .driver = {
+ .name = "max3421-hcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_spi_driver(max3421_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Mosberger <davidm@egauge.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
new file mode 100644
index 000000000..15df00cce
--- /dev/null
+++ b/drivers/usb/host/ohci-at91.c
@@ -0,0 +1,712 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 SAN People (Pty) Ltd.
+ * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ * AT91 Bus Glue
+ *
+ * Based on fragments of 2.4 driver by Rick Bronson.
+ * Based on ohci-omap.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/atmel.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <asm/gpio.h>
+
+#include "ohci.h"
+
+#define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS)
+#define at91_for_each_port(index) \
+ for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++)
+
+/* interface, function and usb clocks; sometimes also an AHB clock */
+#define hcd_to_ohci_at91_priv(h) \
+ ((struct ohci_at91_priv *)hcd_to_ohci(h)->priv)
+
+struct ohci_at91_priv {
+ struct clk *iclk;
+ struct clk *fclk;
+ struct clk *hclk;
+ bool clocked;
+ bool wakeup; /* Saved wake-up state for resume */
+};
+/* interface and function clocks; sometimes also an AHB clock */
+
+#define DRIVER_DESC "OHCI Atmel driver"
+
+static const char hcd_name[] = "ohci-atmel";
+
+static struct hc_driver __read_mostly ohci_at91_hc_driver;
+
+static const struct ohci_driver_overrides ohci_at91_drv_overrides __initconst = {
+ .extra_priv_size = sizeof(struct ohci_at91_priv),
+};
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void at91_start_clock(struct ohci_at91_priv *ohci_at91)
+{
+ if (ohci_at91->clocked)
+ return;
+
+ clk_set_rate(ohci_at91->fclk, 48000000);
+ clk_prepare_enable(ohci_at91->hclk);
+ clk_prepare_enable(ohci_at91->iclk);
+ clk_prepare_enable(ohci_at91->fclk);
+ ohci_at91->clocked = true;
+}
+
+static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
+{
+ if (!ohci_at91->clocked)
+ return;
+
+ clk_disable_unprepare(ohci_at91->fclk);
+ clk_disable_unprepare(ohci_at91->iclk);
+ clk_disable_unprepare(ohci_at91->hclk);
+ ohci_at91->clocked = false;
+}
+
+static void at91_start_hc(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_regs __iomem *regs = hcd->regs;
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+
+ dev_dbg(&pdev->dev, "start\n");
+
+ /*
+ * Start the USB clocks.
+ */
+ at91_start_clock(ohci_at91);
+
+ /*
+ * The USB host controller must remain in reset.
+ */
+ writel(0, &regs->control);
+}
+
+static void at91_stop_hc(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_regs __iomem *regs = hcd->regs;
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+
+ dev_dbg(&pdev->dev, "stop\n");
+
+ /*
+ * Put the USB host controller into reset.
+ */
+ writel(0, &regs->control);
+
+ /*
+ * Stop the USB clocks.
+ */
+ at91_stop_clock(ohci_at91);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_at91_probe - initialize AT91-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int usb_hcd_at91_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct at91_usbh_data *board;
+ struct ohci_hcd *ohci;
+ int retval;
+ struct usb_hcd *hcd;
+ struct ohci_at91_priv *ohci_at91;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_dbg(dev, "hcd probe: missing irq resource\n");
+ return irq;
+ }
+
+ hcd = usb_create_hcd(driver, dev, "at91");
+ if (!hcd)
+ return -ENOMEM;
+ ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ ohci_at91->iclk = devm_clk_get(dev, "ohci_clk");
+ if (IS_ERR(ohci_at91->iclk)) {
+ dev_err(dev, "failed to get ohci_clk\n");
+ retval = PTR_ERR(ohci_at91->iclk);
+ goto err;
+ }
+ ohci_at91->fclk = devm_clk_get(dev, "uhpck");
+ if (IS_ERR(ohci_at91->fclk)) {
+ dev_err(dev, "failed to get uhpck\n");
+ retval = PTR_ERR(ohci_at91->fclk);
+ goto err;
+ }
+ ohci_at91->hclk = devm_clk_get(dev, "hclk");
+ if (IS_ERR(ohci_at91->hclk)) {
+ dev_err(dev, "failed to get hclk\n");
+ retval = PTR_ERR(ohci_at91->hclk);
+ goto err;
+ }
+
+ board = hcd->self.controller->platform_data;
+ ohci = hcd_to_ohci(hcd);
+ ohci->num_ports = board->ports;
+ at91_start_hc(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+ }
+
+ /* Error handling */
+ at91_stop_hc(pdev);
+
+ err:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_at91_remove - shutdown processing for AT91-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_at91_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, "rmmod" or something similar.
+ *
+ */
+static void usb_hcd_at91_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ at91_stop_hc(pdev);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
+{
+ if (!valid_port(port))
+ return;
+
+ if (!gpio_is_valid(pdata->vbus_pin[port]))
+ return;
+
+ gpio_set_value(pdata->vbus_pin[port],
+ pdata->vbus_pin_active_low[port] ^ enable);
+}
+
+static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
+{
+ if (!valid_port(port))
+ return -EINVAL;
+
+ if (!gpio_is_valid(pdata->vbus_pin[port]))
+ return -EINVAL;
+
+ return gpio_get_value(pdata->vbus_pin[port]) ^
+ pdata->vbus_pin_active_low[port];
+}
+
+/*
+ * Update the status data from the hub with the over-current indicator change.
+ */
+static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+ int length = ohci_hub_status_data(hcd, buf);
+ int port;
+
+ at91_for_each_port(port) {
+ if (pdata->overcurrent_changed[port]) {
+ if (!length)
+ length = 1;
+ buf[0] |= 1 << (port + 1);
+ }
+ }
+
+ return length;
+}
+
+/*
+ * Look at the control requests to the root hub and see if we need to override.
+ */
+static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller);
+ struct usb_hub_descriptor *desc;
+ int ret = -EINVAL;
+ u32 *data = (u32 *)buf;
+
+ dev_dbg(hcd->self.controller,
+ "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n",
+ hcd, typeReq, wValue, wIndex, buf, wLength);
+
+ wIndex--;
+
+ switch (typeReq) {
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER) {
+ dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
+ if (valid_port(wIndex)) {
+ ohci_at91_usb_set_power(pdata, wIndex, 1);
+ ret = 0;
+ }
+
+ goto out;
+ }
+ break;
+
+ case ClearPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: C_OVER_CURRENT\n");
+
+ if (valid_port(wIndex)) {
+ pdata->overcurrent_changed[wIndex] = 0;
+ pdata->overcurrent_status[wIndex] = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: OVER_CURRENT\n");
+
+ if (valid_port(wIndex))
+ pdata->overcurrent_status[wIndex] = 0;
+
+ goto out;
+
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: POWER\n");
+
+ if (valid_port(wIndex)) {
+ ohci_at91_usb_set_power(pdata, wIndex, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength);
+ if (ret)
+ goto out;
+
+ switch (typeReq) {
+ case GetHubDescriptor:
+
+ /* update the hub's descriptor */
+
+ desc = (struct usb_hub_descriptor *)buf;
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ /* remove the old configurations for power-switching, and
+ * over-current protection, and insert our new configuration
+ */
+
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
+ desc->wHubCharacteristics |=
+ cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM);
+
+ if (pdata->overcurrent_supported) {
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
+ desc->wHubCharacteristics |=
+ cpu_to_le16(HUB_CHAR_INDV_PORT_OCPM);
+ }
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ return ret;
+
+ case GetPortStatus:
+ /* check port status */
+
+ dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex);
+
+ if (valid_port(wIndex)) {
+ if (!ohci_at91_usb_get_power(pdata, wIndex))
+ *data &= ~cpu_to_le32(RH_PS_PPS);
+
+ if (pdata->overcurrent_changed[wIndex])
+ *data |= cpu_to_le32(RH_PS_OCIC);
+
+ if (pdata->overcurrent_status[wIndex])
+ *data |= cpu_to_le32(RH_PS_POCI);
+ }
+ }
+
+ out:
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev);
+ int val, gpio, port;
+
+ /* From the GPIO notifying the over-current situation, find
+ * out the corresponding port */
+ at91_for_each_port(port) {
+ if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
+ gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+ gpio = pdata->overcurrent_pin[port];
+ break;
+ }
+ }
+
+ if (port == AT91_MAX_USBH_PORTS) {
+ dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n");
+ return IRQ_HANDLED;
+ }
+
+ val = gpio_get_value(gpio);
+
+ /* When notified of an over-current situation, disable power
+ on the corresponding port, and mark this port in
+ over-current. */
+ if (!val) {
+ ohci_at91_usb_set_power(pdata, port, 0);
+ pdata->overcurrent_status[port] = 1;
+ pdata->overcurrent_changed[port] = 1;
+ }
+
+ dev_dbg(& pdev->dev, "overcurrent situation %s\n",
+ val ? "exited" : "notified");
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id at91_ohci_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-ohci" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids);
+
+static int ohci_at91_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int i, gpio, ret;
+ enum of_gpio_flags flags;
+ struct at91_usbh_data *pdata;
+ u32 ports;
+
+ if (!np)
+ return 0;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (!of_property_read_u32(np, "num-ports", &ports))
+ pdata->ports = ports;
+
+ at91_for_each_port(i) {
+ gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i, &flags);
+ pdata->vbus_pin[i] = gpio;
+ if (!gpio_is_valid(gpio))
+ continue;
+ pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW;
+ }
+
+ at91_for_each_port(i)
+ pdata->overcurrent_pin[i] =
+ of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags);
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int ohci_at91_of_init(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
+{
+ struct at91_usbh_data *pdata;
+ int i;
+ int gpio;
+ int ret;
+
+ ret = ohci_at91_of_init(pdev);
+ if (ret)
+ return ret;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ if (pdata) {
+ at91_for_each_port(i) {
+ /*
+ * do not configure PIO if not in relation with
+ * real USB port on board
+ */
+ if (i >= pdata->ports) {
+ pdata->vbus_pin[i] = -EINVAL;
+ pdata->overcurrent_pin[i] = -EINVAL;
+ break;
+ }
+
+ if (!gpio_is_valid(pdata->vbus_pin[i]))
+ continue;
+ gpio = pdata->vbus_pin[i];
+
+ ret = gpio_request(gpio, "ohci_vbus");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't request vbus gpio %d\n", gpio);
+ continue;
+ }
+ ret = gpio_direction_output(gpio,
+ !pdata->vbus_pin_active_low[i]);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't put vbus gpio %d as output %d\n",
+ gpio, !pdata->vbus_pin_active_low[i]);
+ gpio_free(gpio);
+ continue;
+ }
+
+ ohci_at91_usb_set_power(pdata, i, 1);
+ }
+
+ at91_for_each_port(i) {
+ if (!gpio_is_valid(pdata->overcurrent_pin[i]))
+ continue;
+ gpio = pdata->overcurrent_pin[i];
+
+ ret = gpio_request(gpio, "ohci_overcurrent");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't request overcurrent gpio %d\n",
+ gpio);
+ continue;
+ }
+
+ ret = gpio_direction_input(gpio);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't configure overcurrent gpio %d as input\n",
+ gpio);
+ gpio_free(gpio);
+ continue;
+ }
+
+ ret = request_irq(gpio_to_irq(gpio),
+ ohci_hcd_at91_overcurrent_irq,
+ IRQF_SHARED, "ohci_overcurrent", pdev);
+ if (ret) {
+ gpio_free(gpio);
+ dev_err(&pdev->dev,
+ "can't get gpio IRQ for overcurrent\n");
+ }
+ }
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+ return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
+}
+
+static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
+{
+ struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev);
+ int i;
+
+ if (pdata) {
+ at91_for_each_port(i) {
+ if (!gpio_is_valid(pdata->vbus_pin[i]))
+ continue;
+ ohci_at91_usb_set_power(pdata, i, 0);
+ gpio_free(pdata->vbus_pin[i]);
+ }
+
+ at91_for_each_port(i) {
+ if (!gpio_is_valid(pdata->overcurrent_pin[i]))
+ continue;
+ free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev);
+ gpio_free(pdata->overcurrent_pin[i]);
+ }
+ }
+
+ device_init_wakeup(&pdev->dev, 0);
+ usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int
+ohci_hcd_at91_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+ int ret;
+
+ /*
+ * Disable wakeup if we are going to sleep with slow clock mode
+ * enabled.
+ */
+ ohci_at91->wakeup = device_may_wakeup(dev)
+ && !at91_suspend_entering_slow_clock();
+
+ if (ohci_at91->wakeup)
+ enable_irq_wake(hcd->irq);
+
+ ret = ohci_suspend(hcd, ohci_at91->wakeup);
+ if (ret) {
+ if (ohci_at91->wakeup)
+ disable_irq_wake(hcd->irq);
+ return ret;
+ }
+ /*
+ * The integrated transceivers seem unable to notice disconnect,
+ * reconnect, or wakeup without the 48 MHz clock active. so for
+ * correctness, always discard connection state (using reset).
+ *
+ * REVISIT: some boards will be able to turn VBUS off...
+ */
+ if (!ohci_at91->wakeup) {
+ ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci_writel(ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->rh_state = OHCI_RH_HALTED;
+
+ /* flush the writes */
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ at91_stop_clock(ohci_at91);
+ }
+
+ return ret;
+}
+
+static int ohci_hcd_at91_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+
+ if (ohci_at91->wakeup)
+ disable_irq_wake(hcd->irq);
+
+ at91_start_clock(ohci_at91);
+
+ ohci_resume(hcd, false);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend,
+ ohci_hcd_at91_drv_resume);
+
+static struct platform_driver ohci_hcd_at91_driver = {
+ .probe = ohci_hcd_at91_drv_probe,
+ .remove = ohci_hcd_at91_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "at91_ohci",
+ .pm = &ohci_hcd_at91_pm_ops,
+ .of_match_table = of_match_ptr(at91_ohci_dt_ids),
+ },
+};
+
+static int __init ohci_at91_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ohci_init_driver(&ohci_at91_hc_driver, &ohci_at91_drv_overrides);
+
+ /*
+ * The Atmel HW has some unusual quirks, which require Atmel-specific
+ * workarounds. We override certain hc_driver functions here to
+ * achieve that. We explicitly do not enhance ohci_driver_overrides to
+ * allow this more easily, since this is an unusual case, and we don't
+ * want to encourage others to override these functions by making it
+ * too easy.
+ */
+
+ ohci_at91_hc_driver.hub_status_data = ohci_at91_hub_status_data;
+ ohci_at91_hc_driver.hub_control = ohci_at91_hub_control;
+
+ return platform_driver_register(&ohci_hcd_at91_driver);
+}
+module_init(ohci_at91_init);
+
+static void __exit ohci_at91_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_at91_driver);
+}
+module_exit(ohci_at91_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_ohci");
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
new file mode 100644
index 000000000..e5c33bc98
--- /dev/null
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -0,0 +1,438 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * TI DA8xx (OMAP-L1x) Bus Glue
+ *
+ * Derived from: ohci-omap.c and ohci-s3c2410.c
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <mach/da8xx.h>
+#include <linux/platform_data/usb-davinci.h>
+
+#ifndef CONFIG_ARCH_DAVINCI_DA8XX
+#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX."
+#endif
+
+#define CFGCHIP2 DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)
+
+static struct clk *usb11_clk;
+static struct clk *usb20_clk;
+
+/* Over-current indicator change bitmask */
+static volatile u16 ocic_mask;
+
+static void ohci_da8xx_clock(int on)
+{
+ u32 cfgchip2;
+
+ cfgchip2 = __raw_readl(CFGCHIP2);
+ if (on) {
+ clk_enable(usb11_clk);
+
+ /*
+ * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we
+ * need to enable the USB 2.0 module clocking, start its PHY,
+ * and not allow it to stop the clock during USB 2.0 suspend.
+ */
+ if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) {
+ clk_enable(usb20_clk);
+
+ cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN);
+ cfgchip2 |= CFGCHIP2_PHY_PLLON;
+ __raw_writel(cfgchip2, CFGCHIP2);
+
+ pr_info("Waiting for USB PHY clock good...\n");
+ while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD))
+ cpu_relax();
+ }
+
+ /* Enable USB 1.1 PHY */
+ cfgchip2 |= CFGCHIP2_USB1SUSPENDM;
+ } else {
+ clk_disable(usb11_clk);
+ if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX))
+ clk_disable(usb20_clk);
+
+ /* Disable USB 1.1 PHY */
+ cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM;
+ }
+ __raw_writel(cfgchip2, CFGCHIP2);
+}
+
+/*
+ * Handle the port over-current indicator change.
+ */
+static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
+ unsigned port)
+{
+ ocic_mask |= 1 << port;
+
+ /* Once over-current is detected, the port needs to be powered down */
+ if (hub->get_oci(port) > 0)
+ hub->set_power(port, 0);
+}
+
+static int ohci_da8xx_init(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int result;
+ u32 rh_a;
+
+ dev_dbg(dev, "starting USB controller\n");
+
+ ohci_da8xx_clock(1);
+
+ /*
+ * DA8xx only have 1 port connected to the pins but the HC root hub
+ * register A reports 2 ports, thus we'll have to override it...
+ */
+ ohci->num_ports = 1;
+
+ result = ohci_init(ohci);
+ if (result < 0)
+ return result;
+
+ /*
+ * Since we're providing a board-specific root hub port power control
+ * and over-current reporting, we have to override the HC root hub A
+ * register's default value, so that ohci_hub_control() could return
+ * the correct hub descriptor...
+ */
+ rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
+ if (hub->set_power) {
+ rh_a &= ~RH_A_NPS;
+ rh_a |= RH_A_PSM;
+ }
+ if (hub->get_oci) {
+ rh_a &= ~RH_A_NOCP;
+ rh_a |= RH_A_OCPM;
+ }
+ rh_a &= ~RH_A_POTPGT;
+ rh_a |= hub->potpgt << 24;
+ ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
+
+ return result;
+}
+
+static void ohci_da8xx_stop(struct usb_hcd *hcd)
+{
+ ohci_stop(hcd);
+ ohci_da8xx_clock(0);
+}
+
+static int ohci_da8xx_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int result;
+
+ result = ohci_run(ohci);
+ if (result < 0)
+ ohci_da8xx_stop(hcd);
+
+ return result;
+}
+
+/*
+ * Update the status data from the hub with the over-current indicator change.
+ */
+static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ int length = ohci_hub_status_data(hcd, buf);
+
+ /* See if we have OCIC bit set on port 1 */
+ if (ocic_mask & (1 << 1)) {
+ dev_dbg(hcd->self.controller, "over-current indicator change "
+ "on port 1\n");
+
+ if (!length)
+ length = 1;
+
+ buf[0] |= 1 << 1;
+ }
+ return length;
+}
+
+/*
+ * Look at the control requests to the root hub and see if we need to override.
+ */
+static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+ int temp;
+
+ switch (typeReq) {
+ case GetPortStatus:
+ /* Check the port number */
+ if (wIndex != 1)
+ break;
+
+ dev_dbg(dev, "GetPortStatus(%u)\n", wIndex);
+
+ temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
+
+ /* The port power status (PPS) bit defaults to 1 */
+ if (hub->get_power && hub->get_power(wIndex) == 0)
+ temp &= ~RH_PS_PPS;
+
+ /* The port over-current indicator (POCI) bit is always 0 */
+ if (hub->get_oci && hub->get_oci(wIndex) > 0)
+ temp |= RH_PS_POCI;
+
+ /* The over-current indicator change (OCIC) bit is 0 too */
+ if (ocic_mask & (1 << wIndex))
+ temp |= RH_PS_OCIC;
+
+ put_unaligned(cpu_to_le32(temp), (__le32 *)buf);
+ return 0;
+ case SetPortFeature:
+ temp = 1;
+ goto check_port;
+ case ClearPortFeature:
+ temp = 0;
+
+check_port:
+ /* Check the port number */
+ if (wIndex != 1)
+ break;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(dev, "%sPortFeature(%u): %s\n",
+ temp ? "Set" : "Clear", wIndex, "POWER");
+
+ if (!hub->set_power)
+ return -EPIPE;
+
+ return hub->set_power(wIndex, temp) ? -EPIPE : 0;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(dev, "%sPortFeature(%u): %s\n",
+ temp ? "Set" : "Clear", wIndex,
+ "C_OVER_CURRENT");
+
+ if (temp)
+ ocic_mask |= 1 << wIndex;
+ else
+ ocic_mask &= ~(1 << wIndex);
+ return 0;
+ }
+ }
+
+ return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+}
+
+static const struct hc_driver ohci_da8xx_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "DA8xx OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_da8xx_init,
+ .start = ohci_da8xx_start,
+ .stop = ohci_da8xx_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_da8xx_hub_status_data,
+ .hub_control = ohci_da8xx_hub_control,
+
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+
+
+/**
+ * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd;
+ struct resource *mem;
+ int error, irq;
+
+ if (hub == NULL)
+ return -ENODEV;
+
+ usb11_clk = devm_clk_get(&pdev->dev, "usb11");
+ if (IS_ERR(usb11_clk))
+ return PTR_ERR(usb11_clk);
+
+ usb20_clk = devm_clk_get(&pdev->dev, "usb20");
+ if (IS_ERR(usb20_clk))
+ return PTR_ERR(usb20_clk);
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(hcd->regs)) {
+ error = PTR_ERR(hcd->regs);
+ goto err;
+ }
+ hcd->rsrc_start = mem->start;
+ hcd->rsrc_len = resource_size(mem);
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ error = -ENODEV;
+ goto err;
+ }
+ error = usb_add_hcd(hcd, irq, 0);
+ if (error)
+ goto err;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ if (hub->ocic_notify) {
+ error = hub->ocic_notify(ohci_da8xx_ocic_handler);
+ if (!error)
+ return 0;
+ }
+
+ usb_remove_hcd(hcd);
+err:
+ usb_put_hcd(hcd);
+ return error;
+}
+
+/**
+ * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_da8xx_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static inline void
+usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
+
+ hub->ocic_notify(NULL);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
+
+static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev)
+{
+ return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev);
+}
+
+static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ usb_hcd_da8xx_remove(hcd, dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ohci_da8xx_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(&pdev->dev);
+ int ret;
+
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ ohci_da8xx_clock(0);
+ hcd->state = HC_STATE_SUSPENDED;
+
+ return ret;
+}
+
+static int ohci_da8xx_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ohci_da8xx_clock(1);
+ dev->dev.power.power_state = PMSG_ON;
+ usb_hcd_resume_root_hub(hcd);
+ return 0;
+}
+#endif
+
+/*
+ * Driver definition to register with platform structure.
+ */
+static struct platform_driver ohci_hcd_da8xx_driver = {
+ .probe = ohci_hcd_da8xx_drv_probe,
+ .remove = ohci_hcd_da8xx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+#ifdef CONFIG_PM
+ .suspend = ohci_da8xx_suspend,
+ .resume = ohci_da8xx_resume,
+#endif
+ .driver = {
+ .name = "ohci",
+ },
+};
+
+MODULE_ALIAS("platform:ohci");
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
new file mode 100644
index 000000000..04f218693
--- /dev/null
+++ b/drivers/usb/host/ohci-dbg.c
@@ -0,0 +1,809 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#define edstring(ed_type) ({ char *temp; \
+ switch (ed_type) { \
+ case PIPE_CONTROL: temp = "ctrl"; break; \
+ case PIPE_BULK: temp = "bulk"; break; \
+ case PIPE_INTERRUPT: temp = "intr"; break; \
+ default: temp = "isoc"; break; \
+ } temp;})
+#define pipestring(pipe) edstring(usb_pipetype(pipe))
+
+
+#define ohci_dbg_sw(ohci, next, size, format, arg...) \
+ do { \
+ if (next != NULL) { \
+ unsigned s_len; \
+ s_len = scnprintf (*next, *size, format, ## arg ); \
+ *size -= s_len; *next += s_len; \
+ } else \
+ ohci_dbg(ohci,format, ## arg ); \
+ } while (0);
+
+/* Version for use where "next" is the address of a local variable */
+#define ohci_dbg_nosw(ohci, next, size, format, arg...) \
+ do { \
+ unsigned s_len; \
+ s_len = scnprintf(*next, *size, format, ## arg); \
+ *size -= s_len; *next += s_len; \
+ } while (0);
+
+
+static void ohci_dump_intr_mask (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 mask,
+ char **next,
+ unsigned *size)
+{
+ ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 value,
+ char **next,
+ unsigned *size)
+{
+ if (value)
+ ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+static const char *rh_state_string(struct ohci_hcd *ohci)
+{
+ switch (ohci->rh_state) {
+ case OHCI_RH_HALTED:
+ return "halted";
+ case OHCI_RH_SUSPENDED:
+ return "suspended";
+ case OHCI_RH_RUNNING:
+ return "running";
+ }
+ return "?";
+}
+
+// dump control and status registers
+static void
+ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
+{
+ struct ohci_regs __iomem *regs = controller->regs;
+ u32 temp;
+
+ temp = ohci_readl (controller, &regs->revision) & 0xff;
+ ohci_dbg_sw (controller, next, size,
+ "OHCI %d.%d, %s legacy support registers, rh state %s\n",
+ 0x03 & (temp >> 4), (temp & 0x0f),
+ (temp & 0x0100) ? "with" : "NO",
+ rh_state_string(controller));
+
+ temp = ohci_readl (controller, &regs->control);
+ ohci_dbg_sw (controller, next, size,
+ "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
+ temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = ohci_readl (controller, &regs->cmdstatus);
+ ohci_dbg_sw (controller, next, size,
+ "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask (controller, "intrstatus",
+ ohci_readl (controller, &regs->intrstatus),
+ next, size);
+ ohci_dump_intr_mask (controller, "intrenable",
+ ohci_readl (controller, &regs->intrenable),
+ next, size);
+ // intrdisable always same as intrenable
+
+ maybe_print_eds (controller, "ed_periodcurrent",
+ ohci_readl (controller, &regs->ed_periodcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "ed_controlhead",
+ ohci_readl (controller, &regs->ed_controlhead),
+ next, size);
+ maybe_print_eds (controller, "ed_controlcurrent",
+ ohci_readl (controller, &regs->ed_controlcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "ed_bulkhead",
+ ohci_readl (controller, &regs->ed_bulkhead),
+ next, size);
+ maybe_print_eds (controller, "ed_bulkcurrent",
+ ohci_readl (controller, &regs->ed_bulkcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "donehead",
+ ohci_readl (controller, &regs->donehead), next, size);
+}
+
+#define dbg_port_sw(hc,num,value,next,size) \
+ ohci_dbg_sw (hc, next, size, \
+ "roothub.portstatus [%d] " \
+ "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+
+static void
+ohci_dump_roothub (
+ struct ohci_hcd *controller,
+ int verbose,
+ char **next,
+ unsigned *size)
+{
+ u32 temp, i;
+
+ temp = roothub_a (controller);
+ if (temp == ~(u32)0)
+ return;
+
+ if (verbose) {
+ ohci_dbg_sw (controller, next, size,
+ "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d)\n", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ (temp & RH_A_NDP), controller->num_ports
+ );
+ temp = roothub_b (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.b %08x PPCM=%04x DR=%04x\n",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.status %08x%s%s%s%s%s%s\n",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < controller->num_ports; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg_port_sw (controller, i, temp, next, size);
+ }
+}
+
+static void ohci_dump(struct ohci_hcd *controller)
+{
+ ohci_dbg (controller, "OHCI controller state\n");
+
+ // dumps some of the state we know about
+ ohci_dump_status (controller, NULL, NULL);
+ if (controller->hcca)
+ ohci_dbg (controller,
+ "hcca frame #%04x\n", ohci_frame_no(controller));
+ ohci_dump_roothub (controller, 1, NULL, NULL);
+}
+
+static const char data0 [] = "DATA0";
+static const char data1 [] = "DATA1";
+
+static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
+ const struct td *td)
+{
+ u32 tmp = hc32_to_cpup (ohci, &td->hwINFO);
+
+ ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
+ label, td,
+ (tmp & TD_DONE) ? " (DONE)" : "",
+ td->urb, td->index,
+ hc32_to_cpup (ohci, &td->hwNextTD));
+ if ((tmp & TD_ISO) == 0) {
+ const char *toggle, *pid;
+ u32 cbp, be;
+
+ switch (tmp & TD_T) {
+ case TD_T_DATA0: toggle = data0; break;
+ case TD_T_DATA1: toggle = data1; break;
+ case TD_T_TOGGLE: toggle = "(CARRY)"; break;
+ default: toggle = "(?)"; break;
+ }
+ switch (tmp & TD_DP) {
+ case TD_DP_SETUP: pid = "SETUP"; break;
+ case TD_DP_IN: pid = "IN"; break;
+ case TD_DP_OUT: pid = "OUT"; break;
+ default: pid = "(bad pid)"; break;
+ }
+ ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp,
+ TD_CC_GET(tmp), /* EC, */ toggle,
+ (tmp & TD_DI) >> 21, pid,
+ (tmp & TD_R) ? "R" : "");
+ cbp = hc32_to_cpup (ohci, &td->hwCBP);
+ be = hc32_to_cpup (ohci, &td->hwBE);
+ ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be,
+ cbp ? (be + 1 - cbp) : 0);
+ } else {
+ unsigned i;
+ ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
+ TD_CC_GET(tmp),
+ (tmp >> 24) & 0x07,
+ (tmp & TD_DI) >> 21,
+ tmp & 0x0000ffff);
+ ohci_dbg (ohci, " bp0 %08x be %08x\n",
+ hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
+ hc32_to_cpup (ohci, &td->hwBE));
+ for (i = 0; i < MAXPSW; i++) {
+ u16 psw = ohci_hwPSW (ohci, td, i);
+ int cc = (psw >> 12) & 0x0f;
+ ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
+ psw, cc,
+ (cc >= 0x0e) ? "OFFSET" : "SIZE",
+ psw & 0x0fff);
+ }
+ }
+}
+
+/* caller MUST own hcd spinlock if verbose is set! */
+static void __maybe_unused
+ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
+ const struct ed *ed, int verbose)
+{
+ u32 tmp = hc32_to_cpu (ohci, ed->hwINFO);
+ char *type = "";
+
+ ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
+ label,
+ ed, ed->state, edstring (ed->type),
+ hc32_to_cpup (ohci, &ed->hwNextED));
+ switch (tmp & (ED_IN|ED_OUT)) {
+ case ED_OUT: type = "-OUT"; break;
+ case ED_IN: type = "-IN"; break;
+ /* else from TDs ... control */
+ }
+ ohci_dbg (ohci,
+ " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", tmp,
+ 0x03ff & (tmp >> 16),
+ (tmp & ED_DEQUEUE) ? " DQ" : "",
+ (tmp & ED_ISO) ? " ISO" : "",
+ (tmp & ED_SKIP) ? " SKIP" : "",
+ (tmp & ED_LOWSPEED) ? " LOW" : "",
+ 0x000f & (tmp >> 7),
+ type,
+ 0x007f & tmp);
+ tmp = hc32_to_cpup (ohci, &ed->hwHeadP);
+ ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n",
+ tmp,
+ (tmp & ED_C) ? data1 : data0,
+ (tmp & ED_H) ? " HALT" : "",
+ hc32_to_cpup (ohci, &ed->hwTailP),
+ verbose ? "" : " (not listing)");
+ if (verbose) {
+ struct list_head *tmp;
+
+ /* use ed->td_list because HC concurrently modifies
+ * hwNextTD as it accumulates ed_donelist.
+ */
+ list_for_each (tmp, &ed->td_list) {
+ struct td *td;
+ td = list_entry (tmp, struct td, td_list);
+ ohci_dump_td (ohci, " ->", td);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int debug_async_open(struct inode *, struct file *);
+static int debug_periodic_open(struct inode *, struct file *);
+static int debug_registers_open(struct inode *, struct file *);
+static int debug_async_open(struct inode *, struct file *);
+static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
+static int debug_close(struct inode *, struct file *);
+
+static const struct file_operations debug_async_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_async_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_periodic_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_periodic_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+static const struct file_operations debug_registers_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_registers_open,
+ .read = debug_output,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
+
+static struct dentry *ohci_debug_root;
+
+struct debug_buffer {
+ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
+ struct ohci_hcd *ohci;
+ struct mutex mutex; /* protect filling of buffer */
+ size_t count; /* number of characters filled into buffer */
+ char *page;
+};
+
+static ssize_t
+show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
+{
+ unsigned temp, size = count;
+
+ if (!ed)
+ return 0;
+
+ /* print first --> last */
+ while (ed->ed_prev)
+ ed = ed->ed_prev;
+
+ /* dump a snapshot of the bulk or control schedule */
+ while (ed) {
+ u32 info = hc32_to_cpu (ohci, ed->hwINFO);
+ u32 headp = hc32_to_cpu (ohci, ed->hwHeadP);
+ struct list_head *entry;
+ struct td *td;
+
+ temp = scnprintf (buf, size,
+ "ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
+ ed,
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ info & 0x7f,
+ (info >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ 0x03ff & (info >> 16),
+ info,
+ (info & ED_SKIP) ? " s" : "",
+ (headp & ED_H) ? " H" : "",
+ (headp & ED_C) ? data1 : data0);
+ size -= temp;
+ buf += temp;
+
+ list_for_each (entry, &ed->td_list) {
+ u32 cbp, be;
+
+ td = list_entry (entry, struct td, td_list);
+ info = hc32_to_cpup (ohci, &td->hwINFO);
+ cbp = hc32_to_cpup (ohci, &td->hwCBP);
+ be = hc32_to_cpup (ohci, &td->hwBE);
+ temp = scnprintf (buf, size,
+ "\n\ttd %p %s %d cc=%x urb %p (%08x)",
+ td,
+ ({ char *pid;
+ switch (info & TD_DP) {
+ case TD_DP_SETUP: pid = "setup"; break;
+ case TD_DP_IN: pid = "in"; break;
+ case TD_DP_OUT: pid = "out"; break;
+ default: pid = "(?)"; break;
+ } pid;}),
+ cbp ? (be + 1 - cbp) : 0,
+ TD_CC_GET (info), td->urb, info);
+ size -= temp;
+ buf += temp;
+ }
+
+ temp = scnprintf (buf, size, "\n");
+ size -= temp;
+ buf += temp;
+
+ ed = ed->ed_next;
+ }
+ return count - size;
+}
+
+static ssize_t fill_async_buffer(struct debug_buffer *buf)
+{
+ struct ohci_hcd *ohci;
+ size_t temp, size;
+ unsigned long flags;
+
+ ohci = buf->ohci;
+ size = PAGE_SIZE;
+
+ /* display control and bulk lists together, for simplicity */
+ spin_lock_irqsave (&ohci->lock, flags);
+ temp = show_list(ohci, buf->page, size, ohci->ed_controltail);
+ temp += show_list(ohci, buf->page + temp, size - temp,
+ ohci->ed_bulktail);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return temp;
+}
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
+{
+ struct ohci_hcd *ohci;
+ struct ed **seen, *ed;
+ unsigned long flags;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ ohci = buf->ohci;
+ next = buf->page;
+ size = PAGE_SIZE;
+
+ temp = scnprintf (next, size, "size = %d\n", NUM_INTS);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule (and load) */
+ spin_lock_irqsave (&ohci->lock, flags);
+ for (i = 0; i < NUM_INTS; i++) {
+ if (!(ed = ohci->periodic [i]))
+ continue;
+
+ temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
+ size -= temp;
+ next += temp;
+
+ do {
+ temp = scnprintf (next, size, " ed%d/%p",
+ ed->interval, ed);
+ size -= temp;
+ next += temp;
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp] == ed)
+ break;
+ }
+
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 info = hc32_to_cpu (ohci, ed->hwINFO);
+ struct list_head *entry;
+ unsigned qlen = 0;
+
+ /* qlen measured here in TDs, not urbs */
+ list_for_each (entry, &ed->td_list)
+ qlen++;
+
+ temp = scnprintf (next, size,
+ " (%cs dev%d ep%d%s-%s qlen %u"
+ " max %d %08x%s%s)",
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ info & 0x7f,
+ (info >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ (info & ED_ISO) ? "iso" : "int",
+ qlen,
+ 0x03ff & (info >> 16),
+ info,
+ (info & ED_SKIP) ? " K" : "",
+ (ed->hwHeadP &
+ cpu_to_hc32(ohci, ED_H)) ?
+ " H" : "");
+ size -= temp;
+ next += temp;
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++] = ed;
+
+ ed = ed->ed_next;
+
+ } else {
+ /* we've seen it and what's after */
+ temp = 0;
+ ed = NULL;
+ }
+
+ } while (ed);
+
+ temp = scnprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ kfree (seen);
+
+ return PAGE_SIZE - size;
+}
+#undef DBG_SCHED_LIMIT
+
+static ssize_t fill_registers_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ struct ohci_regs __iomem *regs;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ u32 rdata;
+
+ ohci = buf->ohci;
+ hcd = ohci_to_hcd(ohci);
+ regs = ohci->regs;
+ next = buf->page;
+ size = PAGE_SIZE;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* dump driver info, then registers in spec order */
+
+ ohci_dbg_nosw(ohci, &next, &size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "%s\n",
+ hcd->self.controller->bus->name,
+ dev_name(hcd->self.controller),
+ hcd->product_desc,
+ hcd_name);
+
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ size -= scnprintf (next, size,
+ "SUSPENDED (no register access)\n");
+ goto done;
+ }
+
+ ohci_dump_status(ohci, &next, &size);
+
+ /* hcca */
+ if (ohci->hcca)
+ ohci_dbg_nosw(ohci, &next, &size,
+ "hcca frame 0x%04x\n", ohci_frame_no(ohci));
+
+ /* other registers mostly affect frame timings */
+ rdata = ohci_readl (ohci, &regs->fminterval);
+ temp = scnprintf (next, size,
+ "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
+ rdata, (rdata >> 31) ? "FIT " : "",
+ (rdata >> 16) & 0xefff, rdata & 0xffff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, &regs->fmremaining);
+ temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
+ rdata, (rdata >> 31) ? "FRT " : "",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, &regs->periodicstart);
+ temp = scnprintf (next, size, "periodicstart 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, &regs->lsthresh);
+ temp = scnprintf (next, size, "lsthresh 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "hub poll timer %s\n",
+ HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off");
+ size -= temp;
+ next += temp;
+
+ /* roothub */
+ ohci_dump_roothub (ohci, 1, &next, &size);
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+
+static struct debug_buffer *alloc_buffer(struct ohci_hcd *ohci,
+ ssize_t (*fill_func)(struct debug_buffer *))
+{
+ struct debug_buffer *buf;
+
+ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+
+ if (buf) {
+ buf->ohci = ohci;
+ buf->fill_func = fill_func;
+ mutex_init(&buf->mutex);
+ }
+
+ return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+ int ret = 0;
+
+ if (!buf->page)
+ buf->page = (char *)get_zeroed_page(GFP_KERNEL);
+
+ if (!buf->page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = buf->fill_func(buf);
+
+ if (ret >= 0) {
+ buf->count = ret;
+ ret = 0;
+ }
+
+out:
+ return ret;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+ size_t len, loff_t *offset)
+{
+ struct debug_buffer *buf = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&buf->mutex);
+ if (buf->count == 0) {
+ ret = fill_buffer(buf);
+ if (ret != 0) {
+ mutex_unlock(&buf->mutex);
+ goto out;
+ }
+ }
+ mutex_unlock(&buf->mutex);
+
+ ret = simple_read_from_buffer(user_buf, len, offset,
+ buf->page, buf->count);
+
+out:
+ return ret;
+
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf = file->private_data;
+
+ if (buf) {
+ if (buf->page)
+ free_page((unsigned long)buf->page);
+ kfree(buf);
+ }
+
+ return 0;
+}
+static int debug_async_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_periodic_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_periodic_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+static int debug_registers_open(struct inode *inode, struct file *file)
+{
+ file->private_data = alloc_buffer(inode->i_private,
+ fill_registers_buffer);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+static inline void create_debug_files (struct ohci_hcd *ohci)
+{
+ struct usb_bus *bus = &ohci_to_hcd(ohci)->self;
+
+ 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, ohci,
+ &debug_async_fops);
+ if (!ohci->debug_async)
+ goto async_error;
+
+ ohci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
+ 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, ohci,
+ &debug_registers_fops);
+ if (!ohci->debug_registers)
+ goto registers_error;
+
+ ohci_dbg (ohci, "created debug files\n");
+ return;
+
+registers_error:
+ debugfs_remove(ohci->debug_periodic);
+periodic_error:
+ debugfs_remove(ohci->debug_async);
+async_error:
+ debugfs_remove(ohci->debug_dir);
+dir_error:
+ ohci->debug_periodic = NULL;
+ ohci->debug_async = NULL;
+ ohci->debug_dir = NULL;
+}
+
+static inline void remove_debug_files (struct ohci_hcd *ohci)
+{
+ debugfs_remove(ohci->debug_registers);
+ debugfs_remove(ohci->debug_periodic);
+ debugfs_remove(ohci->debug_async);
+ debugfs_remove(ohci->debug_dir);
+}
+
+/*-------------------------------------------------------------------------*/
+
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
new file mode 100644
index 000000000..2cd105be7
--- /dev/null
+++ b/drivers/usb/host/ohci-exynos.c
@@ -0,0 +1,311 @@
+/*
+ * SAMSUNG EXYNOS USB HOST OHCI Controller
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Jingoo Han <jg1.han@samsung.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI EXYNOS driver"
+
+static const char hcd_name[] = "ohci-exynos";
+static struct hc_driver __read_mostly exynos_ohci_hc_driver;
+
+#define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
+
+#define PHY_NUMBER 3
+
+struct exynos_ohci_hcd {
+ struct clk *clk;
+ struct phy *phy[PHY_NUMBER];
+};
+
+static int exynos_ohci_get_phy(struct device *dev,
+ struct exynos_ohci_hcd *exynos_ohci)
+{
+ struct device_node *child;
+ struct phy *phy;
+ int phy_number;
+ int ret;
+
+ /* Get PHYs for the controller */
+ for_each_available_child_of_node(dev->of_node, child) {
+ ret = of_property_read_u32(child, "reg", &phy_number);
+ if (ret) {
+ dev_err(dev, "Failed to parse device tree\n");
+ of_node_put(child);
+ return ret;
+ }
+
+ if (phy_number >= PHY_NUMBER) {
+ dev_err(dev, "Invalid number of PHYs\n");
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ phy = devm_of_phy_get(dev, child, NULL);
+ exynos_ohci->phy[phy_number] = phy;
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else if (ret != -ENOSYS && ret != -ENODEV) {
+ dev_err(dev,
+ "Error retrieving usb2 phy: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int exynos_ohci_phy_enable(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
+ int i;
+ int ret = 0;
+
+ for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
+ if (!IS_ERR(exynos_ohci->phy[i]))
+ ret = phy_power_on(exynos_ohci->phy[i]);
+ if (ret)
+ for (i--; i >= 0; i--)
+ if (!IS_ERR(exynos_ohci->phy[i]))
+ phy_power_off(exynos_ohci->phy[i]);
+
+ return ret;
+}
+
+static void exynos_ohci_phy_disable(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
+ int i;
+
+ for (i = 0; i < PHY_NUMBER; i++)
+ if (!IS_ERR(exynos_ohci->phy[i]))
+ phy_power_off(exynos_ohci->phy[i]);
+}
+
+static int exynos_ohci_probe(struct platform_device *pdev)
+{
+ struct exynos_ohci_hcd *exynos_ohci;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int err;
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we move to full device tree support this will vanish off.
+ */
+ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ hcd = usb_create_hcd(&exynos_ohci_hc_driver,
+ &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ return -ENOMEM;
+ }
+
+ exynos_ohci = to_exynos_ohci(hcd);
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "samsung,exynos5440-ohci"))
+ goto skip_phy;
+
+ err = exynos_ohci_get_phy(&pdev->dev, exynos_ohci);
+ if (err)
+ goto fail_clk;
+
+skip_phy:
+ exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
+
+ if (IS_ERR(exynos_ohci->clk)) {
+ dev_err(&pdev->dev, "Failed to get usbhost clock\n");
+ err = PTR_ERR(exynos_ohci->clk);
+ goto fail_clk;
+ }
+
+ err = clk_prepare_enable(exynos_ohci->clk);
+ if (err)
+ goto fail_clk;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto fail_io;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "Failed to get IRQ\n");
+ err = -ENODEV;
+ goto fail_io;
+ }
+
+ platform_set_drvdata(pdev, hcd);
+
+ err = exynos_ohci_phy_enable(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to enable USB phy\n");
+ goto fail_io;
+ }
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add USB HCD\n");
+ goto fail_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+fail_add_hcd:
+ exynos_ohci_phy_disable(&pdev->dev);
+fail_io:
+ clk_disable_unprepare(exynos_ohci->clk);
+fail_clk:
+ usb_put_hcd(hcd);
+ return err;
+}
+
+static int exynos_ohci_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
+
+ usb_remove_hcd(hcd);
+
+ exynos_ohci_phy_disable(&pdev->dev);
+
+ clk_disable_unprepare(exynos_ohci->clk);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void exynos_ohci_shutdown(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+#ifdef CONFIG_PM
+static int exynos_ohci_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(dev);
+ int rc = ohci_suspend(hcd, do_wakeup);
+
+ if (rc)
+ return rc;
+
+ exynos_ohci_phy_disable(dev);
+
+ clk_disable_unprepare(exynos_ohci->clk);
+
+ return 0;
+}
+
+static int exynos_ohci_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
+ int ret;
+
+ clk_prepare_enable(exynos_ohci->clk);
+
+ ret = exynos_ohci_phy_enable(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable USB phy\n");
+ clk_disable_unprepare(exynos_ohci->clk);
+ return ret;
+ }
+
+ ohci_resume(hcd, false);
+
+ return 0;
+}
+#else
+#define exynos_ohci_suspend NULL
+#define exynos_ohci_resume NULL
+#endif
+
+static const struct ohci_driver_overrides exynos_overrides __initconst = {
+ .extra_priv_size = sizeof(struct exynos_ohci_hcd),
+};
+
+static const struct dev_pm_ops exynos_ohci_pm_ops = {
+ .suspend = exynos_ohci_suspend,
+ .resume = exynos_ohci_resume,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_ohci_match[] = {
+ { .compatible = "samsung,exynos4210-ohci" },
+ { .compatible = "samsung,exynos5440-ohci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ohci_match);
+#endif
+
+static struct platform_driver exynos_ohci_driver = {
+ .probe = exynos_ohci_probe,
+ .remove = exynos_ohci_remove,
+ .shutdown = exynos_ohci_shutdown,
+ .driver = {
+ .name = "exynos-ohci",
+ .pm = &exynos_ohci_pm_ops,
+ .of_match_table = of_match_ptr(exynos_ohci_match),
+ }
+};
+static int __init ohci_exynos_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ohci_init_driver(&exynos_ohci_hc_driver, &exynos_overrides);
+ return platform_driver_register(&exynos_ohci_driver);
+}
+module_init(ohci_exynos_init);
+
+static void __exit ohci_exynos_cleanup(void)
+{
+ platform_driver_unregister(&exynos_ohci_driver);
+}
+module_exit(ohci_exynos_cleanup);
+
+MODULE_ALIAS("platform:exynos-ohci");
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
new file mode 100644
index 000000000..1dab9dfbc
--- /dev/null
+++ b/drivers/usb/host/ohci-hcd.c
@@ -0,0 +1,1384 @@
+/*
+ * Open Host Controller Interface (OHCI) driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ *
+ * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
+ * interfaces (though some non-x86 Intel chips use it). It supports
+ * smarter hardware than UHCI. A download link for the spec available
+ * through the http://www.usb.org website.
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/hcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+
+#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
+#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+/*-------------------------------------------------------------------------*/
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT \
+ (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+ | OHCI_INTR_RD | OHCI_INTR_WDH)
+
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define IR_DISABLE
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ohci_hcd";
+
+#define STATECHANGE_DELAY msecs_to_jiffies(300)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
+
+#include "ohci.h"
+#include "pci-quirks.h"
+
+static void ohci_dump(struct ohci_hcd *ohci);
+static void ohci_stop(struct usb_hcd *hcd);
+static void io_watchdog_func(unsigned long _ohci);
+
+#include "ohci-hub.c"
+#include "ohci-dbg.c"
+#include "ohci-mem.c"
+#include "ohci-q.c"
+
+
+/*
+ * On architectures with edge-triggered interrupts we must never return
+ * IRQ_NONE.
+ */
+#if defined(CONFIG_SA1111) /* ... or other edge-triggered systems */
+#define IRQ_NOTMINE IRQ_HANDLED
+#else
+#define IRQ_NOTMINE IRQ_NONE
+#endif
+
+
+/* Some boards misreport power switching/overcurrent */
+static bool distrust_firmware = 1;
+module_param (distrust_firmware, bool, 0);
+MODULE_PARM_DESC (distrust_firmware,
+ "true to distrust firmware power/overcurrent setup");
+
+/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
+static bool no_handshake = 0;
+module_param (no_handshake, bool, 0);
+MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
+
+/*-------------------------------------------------------------------------*/
+
+static int number_of_tds(struct urb *urb)
+{
+ int len, i, num, this_sg_len;
+ struct scatterlist *sg;
+
+ len = urb->transfer_buffer_length;
+ i = urb->num_mapped_sgs;
+
+ if (len > 0 && i > 0) { /* Scatter-gather transfer */
+ num = 0;
+ sg = urb->sg;
+ for (;;) {
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ num += DIV_ROUND_UP(this_sg_len, 4096);
+ len -= this_sg_len;
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ }
+
+ } else { /* Non-SG transfer */
+ /* one TD for every 4096 Bytes (could be up to 8K) */
+ num = DIV_ROUND_UP(len, 4096);
+ }
+ return num;
+}
+
+/*
+ * queue up an urb for anything except the root hub
+ */
+static int ohci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ed *ed;
+ urb_priv_t *urb_priv;
+ unsigned int pipe = urb->pipe;
+ int i, size = 0;
+ unsigned long flags;
+ int retval = 0;
+
+ /* every endpoint has a ed, locate and maybe (re)initialize it */
+ if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval)))
+ return -ENOMEM;
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ /* td_submit_urb() doesn't yet handle these */
+ if (urb->transfer_buffer_length > 4096)
+ return -EMSGSIZE;
+
+ /* 1 TD for setup, 1 for ACK, plus ... */
+ size = 2;
+ /* FALLTHROUGH */
+ // case PIPE_INTERRUPT:
+ // case PIPE_BULK:
+ default:
+ size += number_of_tds(urb);
+ /* maybe a zero-length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket (urb->dev, pipe,
+ usb_pipeout (pipe))) == 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS: /* number of packets from URB */
+ size = urb->number_of_packets;
+ break;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kzalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
+ mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+ INIT_LIST_HEAD (&urb_priv->pending);
+ urb_priv->length = size;
+ urb_priv->ed = ed;
+
+ /* allocate the TDs (deferring hash chain updates) */
+ for (i = 0; i < size; i++) {
+ urb_priv->td [i] = td_alloc (ohci, mem_flags);
+ if (!urb_priv->td [i]) {
+ urb_priv->length = i;
+ urb_free_priv (ohci, urb_priv);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* don't submit to a dead HC */
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ retval = -ENODEV;
+ goto fail;
+ }
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+ retval = -ENODEV;
+ goto fail;
+ }
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval)
+ goto fail;
+
+ /* schedule the ed if needed */
+ if (ed->state == ED_IDLE) {
+ retval = ed_schedule (ohci, ed);
+ if (retval < 0) {
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ goto fail;
+ }
+
+ /* Start up the I/O watchdog timer, if it's not running */
+ if (!timer_pending(&ohci->io_watchdog) &&
+ list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = ohci_frame_no(ohci);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ list_add(&ed->in_use_list, &ohci->eds_in_use);
+
+ if (ed->type == PIPE_ISOCHRONOUS) {
+ u16 frame = ohci_frame_no(ohci);
+
+ /* delay a few frames before the first TD */
+ frame += max_t (u16, 8, ed->interval);
+ frame &= ~(ed->interval - 1);
+ frame |= ed->branch;
+ urb->start_frame = frame;
+ ed->last_iso = frame + ed->interval * (size - 1);
+ }
+ } else if (ed->type == PIPE_ISOCHRONOUS) {
+ u16 next = ohci_frame_no(ohci) + 1;
+ u16 frame = ed->last_iso + ed->interval;
+ u16 length = ed->interval * (size - 1);
+
+ /* Behind the scheduling threshold? */
+ if (unlikely(tick_before(frame, next))) {
+
+ /* URB_ISO_ASAP: Round up to the first available slot */
+ if (urb->transfer_flags & URB_ISO_ASAP) {
+ frame += (next - frame + ed->interval - 1) &
+ -ed->interval;
+
+ /*
+ * Not ASAP: Use the next slot in the stream,
+ * no matter what.
+ */
+ } else {
+ /*
+ * Some OHCI hardware doesn't handle late TDs
+ * correctly. After retiring them it proceeds
+ * to the next ED instead of the next TD.
+ * Therefore we have to omit the late TDs
+ * entirely.
+ */
+ urb_priv->td_cnt = DIV_ROUND_UP(
+ (u16) (next - frame),
+ ed->interval);
+ if (urb_priv->td_cnt >= urb_priv->length) {
+ ++urb_priv->td_cnt; /* Mark it */
+ ohci_dbg(ohci, "iso underrun %p (%u+%u < %u)\n",
+ urb, frame, length,
+ next);
+ }
+ }
+ }
+ urb->start_frame = frame;
+ ed->last_iso = frame + length;
+ }
+
+ /* fill the TDs and link them to the ed; and
+ * enable that part of the schedule, if needed
+ * and update count of queued periodic urbs
+ */
+ urb->hcpriv = urb_priv;
+ td_submit_urb (ohci, urb);
+
+fail:
+ if (retval)
+ urb_free_priv (ohci, urb_priv);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return retval;
+}
+
+/*
+ * decouple the URB from the HC queues (TDs, urb_priv).
+ * reporting is always done
+ * asynchronously, and we might be dealing with an urb that's
+ * partially transferred, or an ED with other urbs being unlinked.
+ */
+static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ int rc;
+ urb_priv_t *urb_priv;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc == 0) {
+
+ /* Unless an IRQ completed the unlink while it was being
+ * handed to us, flag it for unlink and giveback, and force
+ * some upcoming INTR_SF to call finish_unlinks()
+ */
+ urb_priv = urb->hcpriv;
+ if (urb_priv->ed->state == ED_OPER)
+ start_ed_unlink(ohci, urb_priv->ed);
+
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+ /* With HC dead, we can clean up right away */
+ ohci_work(ohci);
+ }
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* frees config/altsetting state for endpoints,
+ * including ED memory, dummy TD, and bulk/intr data toggle
+ */
+
+static void
+ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ struct ed *ed = ep->hcpriv;
+ unsigned limit = 1000;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+ if (!ed)
+ return;
+
+rescan:
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+sanitize:
+ ed->state = ED_IDLE;
+ ohci_work(ohci);
+ }
+
+ switch (ed->state) {
+ case ED_UNLINK: /* wait for hw to finish? */
+ /* major IRQ delivery trouble loses INTR_SF too... */
+ if (limit-- == 0) {
+ ohci_warn(ohci, "ED unlink timeout\n");
+ goto sanitize;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ case ED_IDLE: /* fully unlinked */
+ if (list_empty (&ed->td_list)) {
+ td_free (ohci, ed->dummy);
+ ed_free (ohci, ed);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. can't recover; must leak ed.
+ */
+ ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",
+ ed, ep->desc.bEndpointAddress, ed->state,
+ list_empty (&ed->td_list) ? "" : " (has tds)");
+ td_free (ohci, ed->dummy);
+ break;
+ }
+ ep->hcpriv = NULL;
+ spin_unlock_irqrestore (&ohci->lock, flags);
+}
+
+static int ohci_get_frame (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ return ohci_frame_no(ohci);
+}
+
+static void ohci_usb_reset (struct ohci_hcd *ohci)
+{
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->rh_state = OHCI_RH_HALTED;
+}
+
+/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
+ * other cases where the next software may expect clean state from the
+ * "firmware". this is bus-neutral, unlike shutdown() methods.
+ */
+static void
+ohci_shutdown (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci;
+
+ ohci = hcd_to_ohci (hcd);
+ ohci_writel(ohci, (u32) ~0, &ohci->regs->intrdisable);
+
+ /* Software reset, after which the controller goes into SUSPEND */
+ ohci_writel(ohci, OHCI_HCR, &ohci->regs->cmdstatus);
+ ohci_readl(ohci, &ohci->regs->cmdstatus); /* flush the writes */
+ udelay(10);
+
+ ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
+ ohci->rh_state = OHCI_RH_HALTED;
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* init memory, and kick BIOS/SMM off */
+
+static int ohci_init (struct ohci_hcd *ohci)
+{
+ int ret;
+ struct usb_hcd *hcd = ohci_to_hcd(ohci);
+
+ /* Accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
+
+ if (distrust_firmware)
+ ohci->flags |= OHCI_QUIRK_HUB_POWER;
+
+ ohci->rh_state = OHCI_RH_HALTED;
+ ohci->regs = hcd->regs;
+
+ /* REVISIT this BIOS handshake is now moved into PCI "quirks", and
+ * was never needed for most non-PCI systems ... remove the code?
+ */
+
+#ifndef IR_DISABLE
+ /* SMM owns the HC? not for long! */
+ if (!no_handshake && ohci_readl (ohci,
+ &ohci->regs->control) & OHCI_CTRL_IR) {
+ u32 temp;
+
+ ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
+
+ /* this timeout is arbitrary. we make it long, so systems
+ * depending on usb keyboards may be usable even if the
+ * BIOS/SMM code seems pretty broken.
+ */
+ temp = 500; /* arbitrary: five seconds */
+
+ ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
+ ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
+ while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
+ msleep (10);
+ if (--temp == 0) {
+ ohci_err (ohci, "USB HC takeover failed!"
+ " (BIOS/SMM bug)\n");
+ return -EBUSY;
+ }
+ }
+ ohci_usb_reset (ohci);
+ }
+#endif
+
+ /* Disable HC interrupts */
+ ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ /* flush the writes, and save key bits like RWC */
+ if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC)
+ ohci->hc_control |= OHCI_CTRL_RWC;
+
+ /* Read the number of ports unless overridden */
+ if (ohci->num_ports == 0)
+ ohci->num_ports = roothub_a(ohci) & RH_A_NDP;
+
+ if (ohci->hcca)
+ return 0;
+
+ setup_timer(&ohci->io_watchdog, io_watchdog_func,
+ (unsigned long) ohci);
+ set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20));
+
+ ohci->hcca = dma_alloc_coherent (hcd->self.controller,
+ sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ if ((ret = ohci_mem_init (ohci)) < 0)
+ ohci_stop (hcd);
+ else {
+ create_debug_files (ohci);
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * resets USB and controller
+ * enable interrupts
+ */
+static int ohci_run (struct ohci_hcd *ohci)
+{
+ u32 mask, val;
+ int first = ohci->fminterval == 0;
+ struct usb_hcd *hcd = ohci_to_hcd(ohci);
+
+ ohci->rh_state = OHCI_RH_HALTED;
+
+ /* boot firmware should have set this up (5.1.1.3.1) */
+ if (first) {
+
+ 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);
+ ohci->fminterval |= FSMP (ohci->fminterval) << 16;
+ /* also: power/overcurrent flags in roothub.a */
+ }
+
+ /* Reset USB nearly "by the book". RemoteWakeupConnected has
+ * to be checked in case boot firmware (BIOS/SMM/...) has set up
+ * wakeup in a way the bus isn't aware of (e.g., legacy PCI PM).
+ * If the bus glue detected wakeup capability then it should
+ * already be enabled; if so we'll just enable it again.
+ */
+ if ((ohci->hc_control & OHCI_CTRL_RWC) != 0)
+ device_set_wakeup_capable(hcd->self.controller, 1);
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ val = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESUME;
+ val = 10 /* msec wait */;
+ break;
+ // case OHCI_USB_RESET:
+ default:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESET;
+ 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(val);
+
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+ /* 2msec timelimit here means no irqs/preempt */
+ spin_lock_irq (&ohci->lock);
+
+retry:
+ /* HC Reset requires max 10 us delay */
+ ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
+ val = 30; /* ... allow extra time */
+ while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--val == 0) {
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "USB HC reset timed out!\n");
+ return -1;
+ }
+ udelay (1);
+ }
+
+ /* now we're in the SUSPEND state ... must go OPERATIONAL
+ * within 2msec else HC enters RESUME
+ *
+ * ... but some hardware won't init fmInterval "by the book"
+ * (SiS, OPTi ...), so reset again instead. SiS doesn't need
+ * this if we write fmInterval after we're OPERATIONAL.
+ * Unclear about ALi, ServerWorks, and others ... this could
+ * easily be a longstanding bug in chip init on Linux.
+ */
+ if (ohci->flags & OHCI_QUIRK_INITRESET) {
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+
+ /* Tell the controller where the control and bulk lists are
+ * The lists are empty now. */
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
+
+ /* a reset clears this */
+ ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
+
+ periodic_reinit (ohci);
+
+ /* some OHCI implementations are finicky about how they init.
+ * bogus values here mean not even enumeration could work.
+ */
+ if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
+ || !ohci_readl (ohci, &ohci->regs->periodicstart)) {
+ if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
+ ohci->flags |= OHCI_QUIRK_INITRESET;
+ ohci_dbg (ohci, "enabling initreset quirk\n");
+ goto retry;
+ }
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "init err (%08x %04x)\n",
+ ohci_readl (ohci, &ohci->regs->fminterval),
+ ohci_readl (ohci, &ohci->regs->periodicstart));
+ return -EOVERFLOW;
+ }
+
+ /* use rhsc irqs after hub_wq is allocated */
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ hcd->uses_new_polling = 1;
+
+ /* start controller operations */
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->rh_state = OHCI_RH_RUNNING;
+
+ /* wake on ConnectStatusChange, matching external hubs */
+ ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_INIT;
+ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
+ ohci_writel (ohci, mask, &ohci->regs->intrenable);
+
+ /* handle root hub init quirks ... */
+ val = roothub_a (ohci);
+ val &= ~(RH_A_PSM | RH_A_OCPM);
+ if (ohci->flags & OHCI_QUIRK_SUPERIO) {
+ /* NSC 87560 and maybe others */
+ 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.
+ */
+ 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, (val & RH_A_NPS) ? 0 : RH_B_PPCM,
+ &ohci->regs->roothub.b);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ spin_unlock_irq (&ohci->lock);
+
+ // POTPGT delay is bits 24-31, in 2 ms units.
+ mdelay ((val >> 23) & 0x1fe);
+
+ ohci_dump(ohci);
+
+ return 0;
+}
+
+/* ohci_setup routine for generic controller initialization */
+
+int ohci_setup(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci_hcd_init(ohci);
+
+ return ohci_init(ohci);
+}
+EXPORT_SYMBOL_GPL(ohci_setup);
+
+/* ohci_start routine for generic controller start of all OHCI bus glue */
+static int ohci_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ ohci_err(ohci, "can't start\n");
+ ohci_stop(hcd);
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Some OHCI controllers are known to lose track of completed TDs. They
+ * don't add the TDs to the hardware done queue, which means we never see
+ * them as being completed.
+ *
+ * This watchdog routine checks for such problems. Without some way to
+ * tell when those TDs have completed, we would never take their EDs off
+ * the unlink list. As a result, URBs could never be dequeued and
+ * endpoints could never be released.
+ */
+static void io_watchdog_func(unsigned long _ohci)
+{
+ struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
+ bool takeback_all_pending = false;
+ u32 status;
+ u32 head;
+ struct ed *ed;
+ struct td *td, *td_start, *td_next;
+ unsigned frame_no;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ /*
+ * One way to lose track of completed TDs is if the controller
+ * never writes back the done queue head. If it hasn't been
+ * written back since the last time this function ran and if it
+ * was non-empty at that time, something is badly wrong with the
+ * hardware.
+ */
+ status = ohci_readl(ohci, &ohci->regs->intrstatus);
+ if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
+ if (ohci->prev_donehead) {
+ ohci_err(ohci, "HcDoneHead not written back; disabled\n");
+ died:
+ usb_hc_died(ohci_to_hcd(ohci));
+ ohci_dump(ohci);
+ ohci_shutdown(ohci_to_hcd(ohci));
+ goto done;
+ } else {
+ /* No write back because the done queue was empty */
+ takeback_all_pending = true;
+ }
+ }
+
+ /* Check every ED which might have pending TDs */
+ list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) {
+ if (ed->pending_td) {
+ if (takeback_all_pending ||
+ OKAY_TO_TAKEBACK(ohci, ed)) {
+ unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO);
+
+ ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n",
+ 0x007f & tmp,
+ (0x000f & (tmp >> 7)) +
+ ((tmp & ED_IN) >> 5));
+ add_to_done_list(ohci, ed->pending_td);
+ }
+ }
+
+ /* Starting from the latest pending TD, */
+ td = ed->pending_td;
+
+ /* or the last TD on the done list, */
+ if (!td) {
+ list_for_each_entry(td_next, &ed->td_list, td_list) {
+ if (!td_next->next_dl_td)
+ break;
+ td = td_next;
+ }
+ }
+
+ /* find the last TD processed by the controller. */
+ head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK;
+ td_start = td;
+ td_next = list_prepare_entry(td, &ed->td_list, td_list);
+ list_for_each_entry_continue(td_next, &ed->td_list, td_list) {
+ if (head == (u32) td_next->td_dma)
+ break;
+ td = td_next; /* head pointer has passed this TD */
+ }
+ if (td != td_start) {
+ /*
+ * In case a WDH cycle is in progress, we will wait
+ * for the next two cycles to complete before assuming
+ * this TD will never get on the done queue.
+ */
+ ed->takeback_wdh_cnt = ohci->wdh_cnt + 2;
+ ed->pending_td = td;
+ }
+ }
+
+ ohci_work(ohci);
+
+ if (ohci->rh_state == OHCI_RH_RUNNING) {
+
+ /*
+ * Sometimes a controller just stops working. We can tell
+ * by checking that the frame counter has advanced since
+ * the last time we ran.
+ *
+ * But be careful: Some controllers violate the spec by
+ * stopping their frame counter when no ports are active.
+ */
+ frame_no = ohci_frame_no(ohci);
+ if (frame_no == ohci->prev_frame_no) {
+ int active_cnt = 0;
+ int i;
+ unsigned tmp;
+
+ for (i = 0; i < ohci->num_ports; ++i) {
+ tmp = roothub_portstatus(ohci, i);
+ /* Enabled and not suspended? */
+ if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
+ ++active_cnt;
+ }
+
+ if (active_cnt > 0) {
+ ohci_err(ohci, "frame counter not updating; disabled\n");
+ goto died;
+ }
+ }
+ if (!list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = frame_no;
+ ohci->prev_wdh_cnt = ohci->wdh_cnt;
+ ohci->prev_donehead = ohci_readl(ohci,
+ &ohci->regs->donehead);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ }
+
+ done:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+}
+
+/* an interrupt happens */
+
+static irqreturn_t ohci_irq (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ohci_regs __iomem *regs = ohci->regs;
+ int ints;
+
+ /* Read interrupt status (and flush pending writes). We ignore the
+ * optimization of checking the LSB of hcca->done_head; it doesn't
+ * work on all systems (edge triggering for OHCI can be a factor).
+ */
+ ints = ohci_readl(ohci, &regs->intrstatus);
+
+ /* Check for an all 1's result which is a typical consequence
+ * of dead, unclocked, or unplugged (CardBus...) devices
+ */
+ if (ints == ~(u32)0) {
+ ohci->rh_state = OHCI_RH_HALTED;
+ ohci_dbg (ohci, "device removed!\n");
+ usb_hc_died(hcd);
+ return IRQ_HANDLED;
+ }
+
+ /* We only care about interrupts that are enabled */
+ ints &= ohci_readl(ohci, &regs->intrenable);
+
+ /* interrupt for some other device? */
+ if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED))
+ return IRQ_NOTMINE;
+
+ if (ints & OHCI_INTR_UE) {
+ // e.g. due to PCI Master/Target Abort
+ if (quirk_nec(ohci)) {
+ /* Workaround for a silicon bug in some NEC chips used
+ * in Apple's PowerBooks. Adapted from Darwin code.
+ */
+ ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");
+
+ ohci_writel (ohci, OHCI_INTR_UE, &regs->intrdisable);
+
+ schedule_work (&ohci->nec_work);
+ } else {
+ ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
+ ohci->rh_state = OHCI_RH_HALTED;
+ usb_hc_died(hcd);
+ }
+
+ ohci_dump(ohci);
+ ohci_usb_reset (ohci);
+ }
+
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_dbg(ohci, "rhsc\n");
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,
+ &regs->intrstatus);
+
+ /* NOTE: Vendors didn't always make the same implementation
+ * choices for RHSC. Many followed the spec; RHSC triggers
+ * on an edge, like setting and maybe clearing a port status
+ * change bit. With others it's level-triggered, active
+ * until hub_wq clears all the port status change bits. We'll
+ * always disable it here and rely on polling until hub_wq
+ * re-enables it.
+ */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+ usb_hcd_poll_rh_status(hcd);
+ }
+
+ /* For connect and disconnect events, we expect the controller
+ * to turn on RHSC along with RD. But for remote wakeup events
+ * this might not happen.
+ */
+ else if (ints & OHCI_INTR_RD) {
+ ohci_dbg(ohci, "resume detect\n");
+ ohci_writel(ohci, OHCI_INTR_RD, &regs->intrstatus);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ if (ohci->autostop) {
+ spin_lock (&ohci->lock);
+ ohci_rh_resume (ohci);
+ spin_unlock (&ohci->lock);
+ } else
+ usb_hcd_resume_root_hub(hcd);
+ }
+
+ spin_lock(&ohci->lock);
+ if (ints & OHCI_INTR_WDH)
+ update_done_list(ohci);
+
+ /* could track INTR_SO to reduce available PCI/... bandwidth */
+
+ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
+ * when there's still unlinking to be done (next frame).
+ */
+ ohci_work(ohci);
+ if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
+ && ohci->rh_state == OHCI_RH_RUNNING)
+ ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
+
+ if (ohci->rh_state == OHCI_RH_RUNNING) {
+ ohci_writel (ohci, ints, &regs->intrstatus);
+ if (ints & OHCI_INTR_WDH)
+ ++ohci->wdh_cnt;
+
+ ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+ spin_unlock(&ohci->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_stop (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_dump(ohci);
+
+ if (quirk_nec(ohci))
+ flush_work(&ohci->nec_work);
+ del_timer_sync(&ohci->io_watchdog);
+
+ ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ ohci_usb_reset(ohci);
+ free_irq(hcd->irq, hcd);
+ hcd->irq = 0;
+
+ if (quirk_amdiso(ohci))
+ usb_amd_dev_put();
+
+ remove_debug_files (ohci);
+ ohci_mem_cleanup (ohci);
+ if (ohci->hcca) {
+ dma_free_coherent (hcd->self.controller,
+ sizeof *ohci->hcca,
+ ohci->hcca, ohci->hcca_dma);
+ ohci->hcca = NULL;
+ ohci->hcca_dma = 0;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_PM) || defined(CONFIG_PCI)
+
+/* must not be called from interrupt context */
+int ohci_restart(struct ohci_hcd *ohci)
+{
+ int temp;
+ int i;
+ struct urb_priv *priv;
+
+ ohci_init(ohci);
+ spin_lock_irq(&ohci->lock);
+ ohci->rh_state = OHCI_RH_HALTED;
+
+ /* Recycle any "live" eds/tds (and urbs). */
+ if (!list_empty (&ohci->pending))
+ ohci_dbg(ohci, "abort schedule...\n");
+ list_for_each_entry (priv, &ohci->pending, pending) {
+ struct urb *urb = priv->td[0]->urb;
+ struct ed *ed = priv->ed;
+
+ switch (ed->state) {
+ case ED_OPER:
+ ed->state = ED_UNLINK;
+ ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);
+ ed_deschedule (ohci, ed);
+
+ ed->ed_next = ohci->ed_rm_list;
+ ed->ed_prev = NULL;
+ ohci->ed_rm_list = ed;
+ /* FALLTHROUGH */
+ case ED_UNLINK:
+ break;
+ default:
+ ohci_dbg(ohci, "bogus ed %p state %d\n",
+ ed, ed->state);
+ }
+
+ if (!urb->unlinked)
+ urb->unlinked = -ESHUTDOWN;
+ }
+ ohci_work(ohci);
+ spin_unlock_irq(&ohci->lock);
+
+ /* paranoia, in case that didn't work: */
+
+ /* empty the interrupt branches */
+ for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
+
+ /* no EDs to remove */
+ ohci->ed_rm_list = NULL;
+
+ /* empty control and bulk lists */
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ if ((temp = ohci_run (ohci)) < 0) {
+ ohci_err (ohci, "can't restart, %d\n", temp);
+ return temp;
+ }
+ ohci_dbg(ohci, "restart complete\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ohci_restart);
+
+#endif
+
+#ifdef CONFIG_PM
+
+int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ int rc = 0;
+
+ /* Disable irq emission and mark HW unaccessible. Use
+ * the spinlock to properly synchronize with possible pending
+ * RH suspend or resume activity.
+ */
+ spin_lock_irqsave (&ohci->lock, flags);
+ ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ (void)ohci_readl(ohci, &ohci->regs->intrdisable);
+
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ synchronize_irq(hcd->irq);
+
+ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ ohci_resume(hcd, false);
+ rc = -EBUSY;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ohci_suspend);
+
+
+int ohci_resume(struct usb_hcd *hcd, bool hibernated)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int port;
+ bool need_reinit = false;
+
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ /* Make sure resume from hibernation re-enumerates everything */
+ if (hibernated)
+ ohci_usb_reset(ohci);
+
+ /* See if the controller is already running or has been reset */
+ ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+ if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+ need_reinit = true;
+ } else {
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ case OHCI_USB_RESET:
+ need_reinit = true;
+ }
+ }
+
+ /* If needed, reinitialize and suspend the root hub */
+ if (need_reinit) {
+ spin_lock_irq(&ohci->lock);
+ ohci_rh_resume(ohci);
+ ohci_rh_suspend(ohci, 0);
+ spin_unlock_irq(&ohci->lock);
+ }
+
+ /* Normally just turn on port power and enable interrupts */
+ else {
+ ohci_dbg(ohci, "powerup ports\n");
+ for (port = 0; port < ohci->num_ports; port++)
+ ohci_writel(ohci, RH_PS_PPS,
+ &ohci->regs->roothub.portstatus[port]);
+
+ ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable);
+ ohci_readl(ohci, &ohci->regs->intrenable);
+ msleep(20);
+ }
+
+ usb_hcd_resume_root_hub(hcd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ohci_resume);
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic structure: This gets copied for platform drivers so that
+ * individual entries can be overridden as needed.
+ */
+
+static const struct hc_driver ohci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_setup,
+ .start = ohci_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+void ohci_init_driver(struct hc_driver *drv,
+ const struct ohci_driver_overrides *over)
+{
+ /* Copy the generic table to drv and then apply the overrides */
+ *drv = ohci_hc_driver;
+
+ if (over) {
+ drv->product_desc = over->product_desc;
+ drv->hcd_priv_size += over->extra_priv_size;
+ if (over->reset)
+ drv->reset = over->reset;
+ }
+}
+EXPORT_SYMBOL_GPL(ohci_init_driver);
+
+/*-------------------------------------------------------------------------*/
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111)
+#include "ohci-sa1111.c"
+#define SA1111_DRIVER ohci_hcd_sa1111_driver
+#endif
+
+#ifdef CONFIG_USB_OHCI_HCD_DAVINCI
+#include "ohci-da8xx.c"
+#define DAVINCI_PLATFORM_DRIVER ohci_hcd_da8xx_driver
+#endif
+
+#ifdef CONFIG_USB_OHCI_HCD_PPC_OF
+#include "ohci-ppc-of.c"
+#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver
+#endif
+
+#ifdef CONFIG_PPC_PS3
+#include "ohci-ps3.c"
+#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
+#endif
+
+#ifdef CONFIG_MFD_SM501
+#include "ohci-sm501.c"
+#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver
+#endif
+
+#ifdef CONFIG_MFD_TC6393XB
+#include "ohci-tmio.c"
+#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
+#endif
+
+#ifdef CONFIG_MACH_JZ4740
+#include "ohci-jz4740.c"
+#define PLATFORM_DRIVER ohci_hcd_jz4740_driver
+#endif
+
+#ifdef CONFIG_TILE_USB
+#include "ohci-tilegx.c"
+#define PLATFORM_DRIVER ohci_hcd_tilegx_driver
+#endif
+
+static int __init ohci_hcd_mod_init(void)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
+ pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+ sizeof (struct ed), sizeof (struct td));
+ set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
+
+ ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
+ if (!ohci_debug_root) {
+ retval = -ENOENT;
+ goto error_debug;
+ }
+
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0)
+ goto error_ps3;
+#endif
+
+#ifdef PLATFORM_DRIVER
+ retval = platform_driver_register(&PLATFORM_DRIVER);
+ if (retval < 0)
+ goto error_platform;
+#endif
+
+#ifdef OF_PLATFORM_DRIVER
+ retval = platform_driver_register(&OF_PLATFORM_DRIVER);
+ if (retval < 0)
+ goto error_of_platform;
+#endif
+
+#ifdef SA1111_DRIVER
+ retval = sa1111_driver_register(&SA1111_DRIVER);
+ if (retval < 0)
+ goto error_sa1111;
+#endif
+
+#ifdef SM501_OHCI_DRIVER
+ retval = platform_driver_register(&SM501_OHCI_DRIVER);
+ if (retval < 0)
+ goto error_sm501;
+#endif
+
+#ifdef TMIO_OHCI_DRIVER
+ retval = platform_driver_register(&TMIO_OHCI_DRIVER);
+ if (retval < 0)
+ goto error_tmio;
+#endif
+
+#ifdef DAVINCI_PLATFORM_DRIVER
+ retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER);
+ if (retval < 0)
+ goto error_davinci;
+#endif
+
+ return retval;
+
+ /* Error path */
+#ifdef DAVINCI_PLATFORM_DRIVER
+ platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
+ error_davinci:
+#endif
+#ifdef TMIO_OHCI_DRIVER
+ platform_driver_unregister(&TMIO_OHCI_DRIVER);
+ error_tmio:
+#endif
+#ifdef SM501_OHCI_DRIVER
+ platform_driver_unregister(&SM501_OHCI_DRIVER);
+ error_sm501:
+#endif
+#ifdef SA1111_DRIVER
+ sa1111_driver_unregister(&SA1111_DRIVER);
+ error_sa1111:
+#endif
+#ifdef OF_PLATFORM_DRIVER
+ platform_driver_unregister(&OF_PLATFORM_DRIVER);
+ error_of_platform:
+#endif
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+ error_platform:
+#endif
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ error_ps3:
+#endif
+ debugfs_remove(ohci_debug_root);
+ ohci_debug_root = NULL;
+ error_debug:
+
+ clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
+ return retval;
+}
+module_init(ohci_hcd_mod_init);
+
+static void __exit ohci_hcd_mod_exit(void)
+{
+#ifdef DAVINCI_PLATFORM_DRIVER
+ platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
+#endif
+#ifdef TMIO_OHCI_DRIVER
+ platform_driver_unregister(&TMIO_OHCI_DRIVER);
+#endif
+#ifdef SM501_OHCI_DRIVER
+ platform_driver_unregister(&SM501_OHCI_DRIVER);
+#endif
+#ifdef SA1111_DRIVER
+ sa1111_driver_unregister(&SA1111_DRIVER);
+#endif
+#ifdef OF_PLATFORM_DRIVER
+ platform_driver_unregister(&OF_PLATFORM_DRIVER);
+#endif
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PS3_SYSTEM_BUS_DRIVER
+ ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+#endif
+ debugfs_remove(ohci_debug_root);
+ clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
+}
+module_exit(ohci_hcd_mod_exit);
+
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
new file mode 100644
index 000000000..ed678c17c
--- /dev/null
+++ b/drivers/usb/host/ohci-hub.c
@@ -0,0 +1,796 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI Root Hub ... the nonsharable stuff
+ */
+
+#define dbg_port(hc,label,num,value) \
+ ohci_dbg (hc, \
+ "%s roothub.portstatus [%d] " \
+ "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ label, num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+/*-------------------------------------------------------------------------*/
+
+#define OHCI_SCHED_ENABLES \
+ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
+
+static void update_done_list(struct ohci_hcd *);
+static void ohci_work(struct ohci_hcd *);
+
+#ifdef CONFIG_PM
+static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
+__releases(ohci->lock)
+__acquires(ohci->lock)
+{
+ int status = 0;
+
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESUME:
+ ohci_dbg (ohci, "resume/suspend?\n");
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci->hc_control |= OHCI_USB_RESET;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ /* FALL THROUGH */
+ case OHCI_USB_RESET:
+ status = -EBUSY;
+ ohci_dbg (ohci, "needs reinit!\n");
+ goto done;
+ case OHCI_USB_SUSPEND:
+ if (!ohci->autostop) {
+ ohci_dbg (ohci, "already suspended\n");
+ goto done;
+ }
+ }
+ ohci_dbg (ohci, "%s root hub\n",
+ autostop ? "auto-stop" : "suspend");
+
+ /* First stop any processing */
+ if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) {
+ ohci->hc_control &= ~OHCI_SCHED_ENABLES;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
+
+ /* sched disables take effect on the next frame,
+ * then the last WDH could take 6+ msec
+ */
+ ohci_dbg (ohci, "stopping schedules ...\n");
+ ohci->autostop = 0;
+ spin_unlock_irq (&ohci->lock);
+ msleep (8);
+ spin_lock_irq (&ohci->lock);
+ }
+ update_done_list(ohci);
+ ohci_work(ohci);
+
+ /*
+ * Some controllers don't handle "global" suspend properly if
+ * there are unsuspended ports. For these controllers, put all
+ * the enabled ports into suspend before suspending the root hub.
+ */
+ if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) {
+ __hc32 __iomem *portstat = ohci->regs->roothub.portstatus;
+ int i;
+ unsigned temp;
+
+ for (i = 0; i < ohci->num_ports; (++i, ++portstat)) {
+ temp = ohci_readl(ohci, portstat);
+ if ((temp & (RH_PS_PES | RH_PS_PSS)) ==
+ RH_PS_PES)
+ ohci_writel(ohci, RH_PS_PSS, portstat);
+ }
+ }
+
+ /* maybe resume can wake root hub */
+ if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
+ ohci->hc_control |= OHCI_CTRL_RWE;
+ } else {
+ ohci_writel(ohci, OHCI_INTR_RHSC | OHCI_INTR_RD,
+ &ohci->regs->intrdisable);
+ ohci->hc_control &= ~OHCI_CTRL_RWE;
+ }
+
+ /* Suspend hub ... this is the "global (to this bus) suspend" mode,
+ * which doesn't imply ports will first be individually suspended.
+ */
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci->hc_control |= OHCI_USB_SUSPEND;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* no resumes until devices finish suspending */
+ if (!autostop) {
+ ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ ohci->autostop = 0;
+ ohci->rh_state = OHCI_RH_SUSPENDED;
+ }
+
+done:
+ return status;
+}
+
+static inline struct ed *find_head (struct ed *ed)
+{
+ /* for bulk and control lists */
+ while (ed->ed_prev)
+ ed = ed->ed_prev;
+ return ed;
+}
+
+/* caller has locked the root hub */
+static int ohci_rh_resume (struct ohci_hcd *ohci)
+__releases(ohci->lock)
+__acquires(ohci->lock)
+{
+ struct usb_hcd *hcd = ohci_to_hcd (ohci);
+ u32 temp, enables;
+ int status = -EINPROGRESS;
+ int autostopped = ohci->autostop;
+
+ ohci->autostop = 0;
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+
+ if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+ /* this can happen after resuming a swsusp snapshot */
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+ ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
+ ohci->hc_control);
+ status = -EBUSY;
+ /* this happens when pmcore resumes HC then root */
+ } else {
+ ohci_dbg (ohci, "duplicate resume\n");
+ status = 0;
+ }
+ } else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_SUSPEND:
+ ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
+ ohci->hc_control |= OHCI_USB_RESUME;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ ohci_dbg (ohci, "%s root hub\n",
+ autostopped ? "auto-start" : "resume");
+ break;
+ case OHCI_USB_RESUME:
+ /* HCFS changes sometime after INTR_RD */
+ ohci_dbg(ohci, "%swakeup root hub\n",
+ autostopped ? "auto-" : "");
+ break;
+ case OHCI_USB_OPER:
+ /* this can happen after resuming a swsusp snapshot */
+ ohci_dbg (ohci, "snapshot resume? reinit\n");
+ status = -EBUSY;
+ break;
+ default: /* RESET, we lost power */
+ ohci_dbg (ohci, "lost power\n");
+ status = -EBUSY;
+ }
+ if (status == -EBUSY) {
+ if (!autostopped) {
+ spin_unlock_irq (&ohci->lock);
+ status = ohci_restart (ohci);
+
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
+ spin_lock_irq (&ohci->lock);
+ }
+ return status;
+ }
+ if (status != -EINPROGRESS)
+ return status;
+ if (autostopped)
+ goto skip_resume;
+ spin_unlock_irq (&ohci->lock);
+
+ /* Some controllers (lucent erratum) need extra-long delays */
+ msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
+
+ temp = ohci_readl (ohci, &ohci->regs->control);
+ temp &= OHCI_CTRL_HCFS;
+ if (temp != OHCI_USB_RESUME) {
+ ohci_err (ohci, "controller won't resume\n");
+ spin_lock_irq(&ohci->lock);
+ return -EBUSY;
+ }
+
+ /* disable old schedule state, reinit from scratch */
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
+ ohci_writel (ohci, 0, &ohci->regs->ed_periodcurrent);
+ ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
+
+ /* Sometimes PCI D3 suspend trashes frame timings ... */
+ periodic_reinit (ohci);
+
+ /*
+ * The following code is executed with ohci->lock held and
+ * irqs disabled if and only if autostopped is true. This
+ * will cause sparse to warn about a "context imbalance".
+ */
+skip_resume:
+ /* interrupts might have been disabled */
+ ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
+ if (ohci->ed_rm_list)
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
+
+ /* Then re-enable operations */
+ ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ if (!autostopped)
+ msleep (3);
+
+ temp = ohci->hc_control;
+ temp &= OHCI_CTRL_RWC;
+ temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->hc_control = temp;
+ ohci_writel (ohci, temp, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* TRSMRCY */
+ if (!autostopped) {
+ msleep (10);
+ spin_lock_irq (&ohci->lock);
+ }
+ /* now ohci->lock is always held and irqs are always disabled */
+
+ /* keep it alive for more than ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+
+ /* maybe turn schedules back on */
+ enables = 0;
+ temp = 0;
+ if (!ohci->ed_rm_list) {
+ if (ohci->ed_controltail) {
+ ohci_writel (ohci,
+ find_head (ohci->ed_controltail)->dma,
+ &ohci->regs->ed_controlhead);
+ enables |= OHCI_CTRL_CLE;
+ temp |= OHCI_CLF;
+ }
+ if (ohci->ed_bulktail) {
+ ohci_writel (ohci, find_head (ohci->ed_bulktail)->dma,
+ &ohci->regs->ed_bulkhead);
+ enables |= OHCI_CTRL_BLE;
+ temp |= OHCI_BLF;
+ }
+ }
+ if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs)
+ enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
+ if (enables) {
+ ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
+ ohci->hc_control |= enables;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ if (temp)
+ ohci_writel (ohci, temp, &ohci->regs->cmdstatus);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+
+ ohci->rh_state = OHCI_RH_RUNNING;
+ return 0;
+}
+
+static int ohci_bus_suspend (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_suspend (ohci, 0);
+ spin_unlock_irq (&ohci->lock);
+
+ if (rc == 0)
+ del_timer_sync(&ohci->io_watchdog);
+ return rc;
+}
+
+static int ohci_bus_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep(5);
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_resume (ohci);
+ spin_unlock_irq (&ohci->lock);
+
+ /* poll until we know a device is connected or we autostop */
+ if (rc == 0)
+ usb_hcd_poll_rh_status(hcd);
+ return rc;
+}
+
+/* Carry out polling-, autostop-, and autoresume-related state changes */
+static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
+ int any_connected, int rhsc_status)
+{
+ int poll_rh = 1;
+ int rhsc_enable;
+
+ /* Some broken controllers never turn off RHSC in the interrupt
+ * status register. For their sake we won't re-enable RHSC
+ * interrupts if the interrupt bit is already active.
+ */
+ rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) &
+ OHCI_INTR_RHSC;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ /* If no status changes are pending, enable RHSC interrupts. */
+ if (!rhsc_enable && !rhsc_status && !changed) {
+ rhsc_enable = OHCI_INTR_RHSC;
+ ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable);
+ }
+
+ /* Keep on polling until we know a device is connected
+ * and RHSC is enabled, or until we autostop.
+ */
+ if (!ohci->autostop) {
+ if (any_connected ||
+ !device_may_wakeup(&ohci_to_hcd(ohci)
+ ->self.root_hub->dev)) {
+ if (rhsc_enable)
+ poll_rh = 0;
+ } else {
+ ohci->autostop = 1;
+ ohci->next_statechange = jiffies + HZ;
+ }
+
+ /* if no devices have been attached for one second, autostop */
+ } else {
+ if (changed || any_connected) {
+ ohci->autostop = 0;
+ ohci->next_statechange = jiffies +
+ STATECHANGE_DELAY;
+ } else if (time_after_eq(jiffies,
+ ohci->next_statechange)
+ && !ohci->ed_rm_list
+ && !(ohci->hc_control &
+ OHCI_SCHED_ENABLES)) {
+ ohci_rh_suspend(ohci, 1);
+ if (rhsc_enable)
+ poll_rh = 0;
+ }
+ }
+ break;
+
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ /* if there is a port change, autostart or ask to be resumed */
+ if (changed) {
+ if (ohci->autostop)
+ ohci_rh_resume(ohci);
+ else
+ usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
+
+ /* If remote wakeup is disabled, stop polling */
+ } else if (!ohci->autostop &&
+ !ohci_to_hcd(ohci)->self.root_hub->
+ do_remote_wakeup) {
+ poll_rh = 0;
+
+ } else {
+ /* If no status changes are pending,
+ * enable RHSC interrupts
+ */
+ if (!rhsc_enable && !rhsc_status) {
+ rhsc_enable = OHCI_INTR_RHSC;
+ ohci_writel(ohci, rhsc_enable,
+ &ohci->regs->intrenable);
+ }
+ /* Keep polling until RHSC is enabled */
+ if (rhsc_enable)
+ poll_rh = 0;
+ }
+ break;
+ }
+ return poll_rh;
+}
+
+#else /* CONFIG_PM */
+
+static inline int ohci_rh_resume(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+
+/* Carry out polling-related state changes.
+ * autostop isn't used when CONFIG_PM is turned off.
+ */
+static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
+ int any_connected, int rhsc_status)
+{
+ /* If RHSC is enabled, don't poll */
+ if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
+ return 0;
+
+ /* If status changes are pending, continue polling.
+ * Conversely, if no status changes are pending but the RHSC
+ * status bit was set, then RHSC may be broken so continue polling.
+ */
+ if (changed || rhsc_status)
+ return 1;
+
+ /* It's safe to re-enable RHSC interrupts */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+int ohci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int i, changed = 0, length = 1;
+ int any_connected = 0;
+ int rhsc_status;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ goto done;
+
+ /* undocumented erratum seen on at least rev D */
+ if ((ohci->flags & OHCI_QUIRK_AMD756)
+ && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
+ ohci_warn (ohci, "bogus NDP, rereads as NDP=%d\n",
+ ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
+ /* retry later; "should not happen" */
+ goto done;
+ }
+
+ /* init status */
+ if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+ buf [0] = changed = 1;
+ else
+ buf [0] = 0;
+ if (ohci->num_ports > 7) {
+ buf [1] = 0;
+ length++;
+ }
+
+ /* Clear the RHSC status flag before reading the port statuses */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
+ rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) &
+ OHCI_INTR_RHSC;
+
+ /* look at each port */
+ for (i = 0; i < ohci->num_ports; i++) {
+ u32 status = roothub_portstatus (ohci, i);
+
+ /* can't autostop if ports are connected */
+ any_connected |= (status & RH_PS_CCS);
+
+ if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+ | RH_PS_OCIC | RH_PS_PRSC)) {
+ changed = 1;
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
+ }
+ }
+
+ if (ohci_root_hub_state_changes(ohci, changed,
+ any_connected, rhsc_status))
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return changed ? length : 0;
+}
+EXPORT_SYMBOL_GPL(ohci_hub_status_data);
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_hub_descriptor (
+ struct ohci_hcd *ohci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 rh = roothub_a (ohci);
+ u16 temp;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ohci->num_ports;
+ temp = 1 + (ohci->num_ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
+ if (rh & RH_A_NPS) /* no power switching? */
+ temp |= HUB_CHAR_NO_LPSM;
+ if (rh & RH_A_PSM) /* per-port power switching? */
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
+ if (rh & RH_A_NOCP) /* no overcurrent reporting? */
+ temp |= HUB_CHAR_NO_OCPM;
+ else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+
+ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ rh = roothub_b (ohci);
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ desc->u.hs.DeviceRemovable[0] = rh & RH_B_DR;
+ if (ohci->num_ports > 7) {
+ desc->u.hs.DeviceRemovable[1] = (rh & RH_B_DR) >> 8;
+ desc->u.hs.DeviceRemovable[2] = 0xff;
+ } else
+ desc->u.hs.DeviceRemovable[1] = 0xff;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_OTG
+
+static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ u32 status;
+
+ if (!port)
+ return -EINVAL;
+ port--;
+
+ /* start port reset before HNP protocol times out */
+ status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
+ if (!(status & RH_PS_CCS))
+ return -ENODEV;
+
+ /* hub_wq will finish the reset later */
+ ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
+ return 0;
+}
+
+#else
+
+#define ohci_start_port_reset NULL
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+
+/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
+ * not necessarily continuous ... to guard against resume signaling.
+ */
+#define PORT_RESET_MSEC 50
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+
+/* called from some task, normally hub_wq */
+static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
+{
+ __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
+ u32 temp = 0;
+ u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
+ u16 reset_done = now + PORT_RESET_MSEC;
+ int limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC);
+
+ /* build a "continuous enough" reset signal, with up to
+ * 3msec gap between pulses. scheduler HZ==100 must work;
+ * this might need to be deadline-scheduled.
+ */
+ do {
+ int limit_2;
+
+ /* spin until any current reset finishes */
+ limit_2 = PORT_RESET_HW_MSEC * 2;
+ while (--limit_2 >= 0) {
+ temp = ohci_readl (ohci, portstat);
+ /* handle e.g. CardBus eject */
+ if (temp == ~(u32)0)
+ return -ESHUTDOWN;
+ if (!(temp & RH_PS_PRS))
+ break;
+ udelay (500);
+ }
+
+ /* timeout (a hardware error) has been observed when
+ * EHCI sets CF while this driver is resetting a port;
+ * presumably other disconnect paths might do it too.
+ */
+ if (limit_2 < 0) {
+ ohci_dbg(ohci,
+ "port[%d] reset timeout, stat %08x\n",
+ port, temp);
+ break;
+ }
+
+ if (!(temp & RH_PS_CCS))
+ break;
+ if (temp & RH_PS_PRSC)
+ ohci_writel (ohci, RH_PS_PRSC, portstat);
+
+ /* start the next reset, sleep till it's probably done */
+ ohci_writel (ohci, RH_PS_PRS, portstat);
+ msleep(PORT_RESET_HW_MSEC);
+ now = ohci_readl(ohci, &ohci->regs->fmnumber);
+ } while (tick_before(now, reset_done) && --limit_1 >= 0);
+
+ /* caller synchronizes using PRSC ... and handles PRS
+ * still being set when this returns.
+ */
+
+ return 0;
+}
+
+int ohci_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports = ohci->num_ports;
+ u32 temp;
+ int retval = 0;
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
+ return -ESHUTDOWN;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ ohci_writel (ohci, RH_HS_OCIC,
+ &ohci->regs->roothub.status);
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+ ohci_writel (ohci, temp,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ // ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case GetHubDescriptor:
+ ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
+ put_unaligned_le32(temp, buf);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = roothub_portstatus (ohci, wIndex);
+ put_unaligned_le32(temp, buf);
+
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+ dbg_port(ohci, "GetStatus", wIndex, temp);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ // FIXME: this can be cleared, yes?
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+#ifdef CONFIG_USB_OTG
+ if (hcd->self.otg_port == (wIndex + 1)
+ && hcd->self.b_hnp_enable)
+ ohci->start_hnp(ohci);
+ else
+#endif
+ ohci_writel (ohci, RH_PS_PSS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ ohci_writel (ohci, RH_PS_PPS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ retval = root_port_reset (ohci, wIndex);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+ return retval;
+}
+EXPORT_SYMBOL_GPL(ohci_hub_control);
diff --git a/drivers/usb/host/ohci-jz4740.c b/drivers/usb/host/ohci-jz4740.c
new file mode 100644
index 000000000..4db78f169
--- /dev/null
+++ b/drivers/usb/host/ohci-jz4740.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * 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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+struct jz4740_ohci_hcd {
+ struct ohci_hcd ohci_hcd;
+
+ struct regulator *vbus;
+ bool vbus_enabled;
+ struct clk *clk;
+};
+
+static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
+{
+ return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
+{
+ return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
+}
+
+static int ohci_jz4740_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_init(ohci);
+ if (ret < 0)
+ return ret;
+
+ ohci->num_ports = 1;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ dev_err(hcd->self.controller, "Can not start %s",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+ return 0;
+}
+
+static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
+ bool enabled)
+{
+ int ret = 0;
+
+ if (!jz4740_ohci->vbus)
+ return 0;
+
+ if (enabled && !jz4740_ohci->vbus_enabled) {
+ ret = regulator_enable(jz4740_ohci->vbus);
+ if (ret)
+ dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
+ "Could not power vbus\n");
+ } else if (!enabled && jz4740_ohci->vbus_enabled) {
+ ret = regulator_disable(jz4740_ohci->vbus);
+ }
+
+ if (ret == 0)
+ jz4740_ohci->vbus_enabled = enabled;
+
+ return ret;
+}
+
+static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+ int ret = 0;
+
+ switch (typeReq) {
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER)
+ ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
+ break;
+ case ClearPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER)
+ ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+}
+
+
+static const struct hc_driver ohci_jz4740_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "JZ4740 OHCI",
+ .hcd_priv_size = sizeof(struct jz4740_ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_jz4740_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_jz4740_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+
+static int jz4740_ohci_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct usb_hcd *hcd;
+ struct jz4740_ohci_hcd *jz4740_ohci;
+ struct resource *res;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ return irq;
+ }
+
+ hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
+ if (!hcd) {
+ dev_err(&pdev->dev, "Failed to create hcd.\n");
+ return -ENOMEM;
+ }
+
+ jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto err_free;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ jz4740_ohci->clk = devm_clk_get(&pdev->dev, "uhc");
+ if (IS_ERR(jz4740_ohci->clk)) {
+ ret = PTR_ERR(jz4740_ohci->clk);
+ dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+ goto err_free;
+ }
+
+ jz4740_ohci->vbus = devm_regulator_get(&pdev->dev, "vbus");
+ if (IS_ERR(jz4740_ohci->vbus))
+ jz4740_ohci->vbus = NULL;
+
+
+ clk_set_rate(jz4740_ohci->clk, 48000000);
+ clk_enable(jz4740_ohci->clk);
+ if (jz4740_ohci->vbus)
+ ohci_jz4740_set_vbus_power(jz4740_ohci, true);
+
+ platform_set_drvdata(pdev, hcd);
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
+ goto err_disable;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return 0;
+
+err_disable:
+ if (jz4740_ohci->vbus)
+ regulator_disable(jz4740_ohci->vbus);
+ clk_disable(jz4740_ohci->clk);
+
+err_free:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int jz4740_ohci_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+
+ usb_remove_hcd(hcd);
+
+ if (jz4740_ohci->vbus)
+ regulator_disable(jz4740_ohci->vbus);
+
+ clk_disable(jz4740_ohci->clk);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static struct platform_driver ohci_hcd_jz4740_driver = {
+ .probe = jz4740_ohci_probe,
+ .remove = jz4740_ohci_remove,
+ .driver = {
+ .name = "jz4740-ohci",
+ },
+};
+
+MODULE_ALIAS("platform:jz4740-ohci");
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
new file mode 100644
index 000000000..c9e315c68
--- /dev/null
+++ b/drivers/usb/host/ohci-mem.c
@@ -0,0 +1,139 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI deals with three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... the hcd glue or the
+ * device driver provides us with dma addresses
+ *
+ * There's also "register" data, which is memory mapped.
+ * No memory seen by this driver (or any HCD) may be paged out.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_hcd_init (struct ohci_hcd *ohci)
+{
+ ohci->next_statechange = jiffies;
+ spin_lock_init (&ohci->lock);
+ INIT_LIST_HEAD (&ohci->pending);
+ INIT_LIST_HEAD(&ohci->eds_in_use);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_mem_init (struct ohci_hcd *ohci)
+{
+ ohci->td_cache = dma_pool_create ("ohci_td",
+ ohci_to_hcd(ohci)->self.controller,
+ sizeof (struct td),
+ 32 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->td_cache)
+ return -ENOMEM;
+ ohci->ed_cache = dma_pool_create ("ohci_ed",
+ ohci_to_hcd(ohci)->self.controller,
+ sizeof (struct ed),
+ 16 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->ed_cache) {
+ dma_pool_destroy (ohci->td_cache);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci_hcd *ohci)
+{
+ if (ohci->td_cache) {
+ dma_pool_destroy (ohci->td_cache);
+ ohci->td_cache = NULL;
+ }
+ if (ohci->ed_cache) {
+ dma_pool_destroy (ohci->ed_cache);
+ ohci->ed_cache = NULL;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ohci "done list" processing needs this mapping */
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+ struct td *td;
+
+ td_dma &= TD_MASK;
+ td = hc->td_hash [TD_HASH_FUNC(td_dma)];
+ while (td && td->td_dma != td_dma)
+ td = td->td_hash;
+ return td;
+}
+
+/* TDs ... */
+static struct td *
+td_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
+{
+ dma_addr_t dma;
+ struct td *td;
+
+ td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
+ if (td) {
+ /* in case hc fetches it, make it look dead */
+ memset (td, 0, sizeof *td);
+ td->hwNextTD = cpu_to_hc32 (hc, dma);
+ td->td_dma = dma;
+ /* hashed in td_fill */
+ }
+ return td;
+}
+
+static void
+td_free (struct ohci_hcd *hc, struct td *td)
+{
+ struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
+
+ while (*prev && *prev != td)
+ prev = &(*prev)->td_hash;
+ if (*prev)
+ *prev = td->td_hash;
+ else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
+ ohci_dbg (hc, "no hash for td %p\n", td);
+ dma_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EDs ... */
+static struct ed *
+ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
+{
+ dma_addr_t dma;
+ struct ed *ed;
+
+ ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ if (ed) {
+ memset (ed, 0, sizeof (*ed));
+ INIT_LIST_HEAD (&ed->td_list);
+ ed->dma = dma;
+ }
+ return ed;
+}
+
+static void
+ed_free (struct ohci_hcd *hc, struct ed *ed)
+{
+ dma_pool_free (hc->ed_cache, ed, ed->dma);
+}
+
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
new file mode 100644
index 000000000..d9f0481d7
--- /dev/null
+++ b/drivers/usb/host/ohci-nxp.c
@@ -0,0 +1,350 @@
+/*
+ * driver for NXP USB Host devices
+ *
+ * Currently supported OHCI host devices:
+ * - NXP LPC32xx
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>
+ * Vitaly Wool <vitalywool@gmail.com>
+ *
+ * register initialization is based on code examples provided by Philips
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * NOTE: This driver does not have suspend/resume functionality
+ * This driver is intended for engineering development purposes only
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/usb/isp1301.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <mach/platform.h>
+#include <mach/irqs.h>
+
+#define USB_CONFIG_BASE 0x31020000
+#define PWRMAN_BASE 0x40004000
+
+#define USB_CTRL IO_ADDRESS(PWRMAN_BASE + 0x64)
+
+/* USB_CTRL bit defines */
+#define USB_SLAVE_HCLK_EN (1 << 24)
+#define USB_DEV_NEED_CLK_EN (1 << 22)
+#define USB_HOST_NEED_CLK_EN (1 << 21)
+#define PAD_CONTROL_LAST_DRIVEN (1 << 19)
+
+#define USB_OTG_STAT_CONTROL IO_ADDRESS(USB_CONFIG_BASE + 0x110)
+
+/* USB_OTG_STAT_CONTROL bit defines */
+#define TRANSPARENT_I2C_EN (1 << 7)
+#define HOST_EN (1 << 0)
+
+/* On LPC32xx, those are undefined */
+#ifndef start_int_set_falling_edge
+#define start_int_set_falling_edge(irq)
+#define start_int_set_rising_edge(irq)
+#define start_int_ack(irq)
+#define start_int_mask(irq)
+#define start_int_umask(irq)
+#endif
+
+#define DRIVER_DESC "OHCI NXP driver"
+
+static const char hcd_name[] = "ohci-nxp";
+static struct hc_driver __read_mostly ohci_nxp_hc_driver;
+
+static struct i2c_client *isp1301_i2c_client;
+
+extern int usb_disabled(void);
+
+static struct clk *usb_pll_clk;
+static struct clk *usb_dev_clk;
+static struct clk *usb_otg_clk;
+
+static void isp1301_configure_lpc32xx(void)
+{
+ /* LPC32XX only supports DAT_SE0 USB mode */
+ /* This sequence is important */
+
+ /* Disable transparent UART mode first */
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+ MC1_UART_EN);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+ ~MC1_SPEED_REG);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_MODE_CONTROL_1, MC1_SPEED_REG);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ (ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR),
+ ~0);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_MODE_CONTROL_2,
+ (MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL));
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_MODE_CONTROL_1, MC1_DAT_SE0);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_OTG_CONTROL_1,
+ (OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+ (OTG1_DM_PULLUP | OTG1_DP_PULLUP));
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR,
+ ~0);
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+
+ /* Enable usb_need_clk clock after transceiver is initialized */
+ __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+ printk(KERN_INFO "ISP1301 Vendor ID : 0x%04x\n",
+ i2c_smbus_read_word_data(isp1301_i2c_client, 0x00));
+ printk(KERN_INFO "ISP1301 Product ID : 0x%04x\n",
+ i2c_smbus_read_word_data(isp1301_i2c_client, 0x02));
+ printk(KERN_INFO "ISP1301 Version ID : 0x%04x\n",
+ i2c_smbus_read_word_data(isp1301_i2c_client, 0x14));
+}
+
+static void isp1301_configure(void)
+{
+ isp1301_configure_lpc32xx();
+}
+
+static inline void isp1301_vbus_on(void)
+{
+ i2c_smbus_write_byte_data(isp1301_i2c_client, ISP1301_I2C_OTG_CONTROL_1,
+ OTG1_VBUS_DRV);
+}
+
+static inline void isp1301_vbus_off(void)
+{
+ i2c_smbus_write_byte_data(isp1301_i2c_client,
+ ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR,
+ OTG1_VBUS_DRV);
+}
+
+static void ohci_nxp_start_hc(void)
+{
+ unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+ isp1301_vbus_on();
+}
+
+static void ohci_nxp_stop_hc(void)
+{
+ unsigned long tmp;
+ isp1301_vbus_off();
+ tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+}
+
+static int ohci_hcd_nxp_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = 0;
+ const struct hc_driver *driver = &ohci_nxp_hc_driver;
+ struct resource *res;
+ int ret = 0, irq;
+ struct device_node *isp1301_node;
+
+ if (pdev->dev.of_node) {
+ isp1301_node = of_parse_phandle(pdev->dev.of_node,
+ "transceiver", 0);
+ } else {
+ isp1301_node = NULL;
+ }
+
+ isp1301_i2c_client = isp1301_get_client(isp1301_node);
+ if (!isp1301_i2c_client) {
+ return -EPROBE_DEFER;
+ }
+
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto fail_disable;
+
+ dev_dbg(&pdev->dev, "%s: " DRIVER_DESC " (nxp)\n", hcd_name);
+ if (usb_disabled()) {
+ dev_err(&pdev->dev, "USB is disabled\n");
+ ret = -ENODEV;
+ goto fail_disable;
+ }
+
+ /* Enable AHB slave USB clock, needed for further USB clock control */
+ __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL);
+
+ /* Enable USB PLL */
+ usb_pll_clk = devm_clk_get(&pdev->dev, "ck_pll5");
+ if (IS_ERR(usb_pll_clk)) {
+ dev_err(&pdev->dev, "failed to acquire USB PLL\n");
+ ret = PTR_ERR(usb_pll_clk);
+ goto fail_disable;
+ }
+
+ ret = clk_enable(usb_pll_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to start USB PLL\n");
+ goto fail_disable;
+ }
+
+ ret = clk_set_rate(usb_pll_clk, 48000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set USB clock rate\n");
+ goto fail_rate;
+ }
+
+ /* Enable USB device clock */
+ usb_dev_clk = devm_clk_get(&pdev->dev, "ck_usbd");
+ if (IS_ERR(usb_dev_clk)) {
+ dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
+ ret = PTR_ERR(usb_dev_clk);
+ goto fail_rate;
+ }
+
+ ret = clk_enable(usb_dev_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
+ goto fail_rate;
+ }
+
+ /* Enable USB otg clocks */
+ usb_otg_clk = devm_clk_get(&pdev->dev, "ck_usb_otg");
+ if (IS_ERR(usb_otg_clk)) {
+ dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
+ ret = PTR_ERR(usb_otg_clk);
+ goto fail_otg;
+ }
+
+ __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+ ret = clk_enable(usb_otg_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
+ goto fail_otg;
+ }
+
+ isp1301_configure();
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
+ ret = -ENOMEM;
+ goto fail_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto fail_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENXIO;
+ goto fail_resource;
+ }
+
+ ohci_nxp_start_hc();
+ platform_set_drvdata(pdev, hcd);
+
+ dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return ret;
+ }
+
+ ohci_nxp_stop_hc();
+fail_resource:
+ usb_put_hcd(hcd);
+fail_hcd:
+ clk_disable(usb_otg_clk);
+fail_otg:
+ clk_disable(usb_dev_clk);
+fail_rate:
+ clk_disable(usb_pll_clk);
+fail_disable:
+ isp1301_i2c_client = NULL;
+ return ret;
+}
+
+static int ohci_hcd_nxp_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ ohci_nxp_stop_hc();
+ usb_put_hcd(hcd);
+ clk_disable(usb_pll_clk);
+ clk_disable(usb_dev_clk);
+ i2c_unregister_device(isp1301_i2c_client);
+ isp1301_i2c_client = NULL;
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:usb-ohci");
+
+#ifdef CONFIG_OF
+static const struct of_device_id ohci_hcd_nxp_match[] = {
+ { .compatible = "nxp,ohci-nxp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ohci_hcd_nxp_match);
+#endif
+
+static struct platform_driver ohci_hcd_nxp_driver = {
+ .driver = {
+ .name = "usb-ohci",
+ .of_match_table = of_match_ptr(ohci_hcd_nxp_match),
+ },
+ .probe = ohci_hcd_nxp_probe,
+ .remove = ohci_hcd_nxp_remove,
+};
+
+static int __init ohci_nxp_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_nxp_hc_driver, NULL);
+ return platform_driver_register(&ohci_hcd_nxp_driver);
+}
+module_init(ohci_nxp_init);
+
+static void __exit ohci_nxp_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_nxp_driver);
+}
+module_exit(ohci_nxp_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
new file mode 100644
index 000000000..de7c68602
--- /dev/null
+++ b/drivers/usb/host/ohci-omap.c
@@ -0,0 +1,513 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2005 David Brownell
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * OMAP Bus Glue
+ *
+ * Modified for OMAP by Tony Lindgren <tony@atomide.com>
+ * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
+ * and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/mux.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/usb.h>
+
+
+/* OMAP-1510 OHCI has its own MMU for DMA */
+#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */
+#define OMAP1510_LB_CLOCK_DIV 0xfffec10c
+#define OMAP1510_LB_MMU_CTL 0xfffec208
+#define OMAP1510_LB_MMU_LCK 0xfffec224
+#define OMAP1510_LB_MMU_LD_TLB 0xfffec228
+#define OMAP1510_LB_MMU_CAM_H 0xfffec22c
+#define OMAP1510_LB_MMU_CAM_L 0xfffec230
+#define OMAP1510_LB_MMU_RAM_H 0xfffec234
+#define OMAP1510_LB_MMU_RAM_L 0xfffec238
+
+#define DRIVER_DESC "OHCI OMAP driver"
+
+#ifdef CONFIG_TPS65010
+#include <linux/i2c/tps65010.h>
+#else
+
+#define LOW 0
+#define HIGH 1
+
+#define GPIO1 1
+
+static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+ return 0;
+}
+
+#endif
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+
+static const char hcd_name[] = "ohci-omap";
+static struct hc_driver __read_mostly ohci_omap_hc_driver;
+
+static void omap_ohci_clock_power(int on)
+{
+ if (on) {
+ clk_enable(usb_dc_ck);
+ clk_enable(usb_host_ck);
+ /* guesstimate for T5 == 1x 32K clock + APLL lock time */
+ udelay(100);
+ } else {
+ clk_disable(usb_host_ck);
+ clk_disable(usb_dc_ck);
+ }
+}
+
+/*
+ * Board specific gang-switched transceiver power on/off.
+ * NOTE: OSK supplies power from DC, not battery.
+ */
+static int omap_ohci_transceiver_power(int on)
+{
+ if (on) {
+ if (machine_is_omap_innovator() && cpu_is_omap1510())
+ __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL)
+ | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ INNOVATOR_FPGA_CAM_USB_CONTROL);
+ else if (machine_is_omap_osk())
+ tps65010_set_gpio_out_value(GPIO1, LOW);
+ } else {
+ if (machine_is_omap_innovator() && cpu_is_omap1510())
+ __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL)
+ & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ INNOVATOR_FPGA_CAM_USB_CONTROL);
+ else if (machine_is_omap_osk())
+ tps65010_set_gpio_out_value(GPIO1, HIGH);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ARCH_OMAP15XX
+/*
+ * OMAP-1510 specific Local Bus clock on/off
+ */
+static int omap_1510_local_bus_power(int on)
+{
+ if (on) {
+ omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL);
+ udelay(200);
+ } else {
+ omap_writel(0, OMAP1510_LB_MMU_CTL);
+ }
+
+ return 0;
+}
+
+/*
+ * OMAP-1510 specific Local Bus initialization
+ * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
+ * See also arch/mach-omap/memory.h for __virt_to_dma() and
+ * __dma_to_virt() which need to match with the physical
+ * Local Bus address below.
+ */
+static int omap_1510_local_bus_init(void)
+{
+ unsigned int tlb;
+ unsigned long lbaddr, physaddr;
+
+ omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
+ OMAP1510_LB_CLOCK_DIV);
+
+ /* Configure the Local Bus MMU table */
+ for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) {
+ lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
+ physaddr = tlb * 0x00100000 + PHYS_OFFSET;
+ omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
+ omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
+ OMAP1510_LB_MMU_CAM_L);
+ omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
+ omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
+ omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK);
+ omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB);
+ }
+
+ /* Enable the walking table */
+ omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL);
+ udelay(200);
+
+ return 0;
+}
+#else
+#define omap_1510_local_bus_power(x) {}
+#define omap_1510_local_bus_init() {}
+#endif
+
+#ifdef CONFIG_USB_OTG
+
+static void start_hnp(struct ohci_hcd *ohci)
+{
+ struct usb_hcd *hcd = ohci_to_hcd(ohci);
+ const unsigned port = hcd->self.otg_port - 1;
+ unsigned long flags;
+ u32 l;
+
+ otg_start_hnp(hcd->usb_phy->otg);
+
+ local_irq_save(flags);
+ hcd->usb_phy->otg->state = OTG_STATE_A_SUSPEND;
+ writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
+ l = omap_readl(OTG_CTRL);
+ l &= ~OTG_A_BUSREQ;
+ omap_writel(l, OTG_CTRL);
+ local_irq_restore(flags);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_omap_reset(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct omap_usb_config *config = dev_get_platdata(hcd->self.controller);
+ int need_transceiver = (config->otg != 0);
+ int ret;
+
+ dev_dbg(hcd->self.controller, "starting USB Controller\n");
+
+ if (config->otg) {
+ hcd->self.otg_port = config->otg;
+ /* default/minimum OTG power budget: 8 mA */
+ hcd->power_budget = 8;
+ }
+
+ /* boards can use OTG transceivers in non-OTG modes */
+ need_transceiver = need_transceiver
+ || machine_is_omap_h2() || machine_is_omap_h3();
+
+ /* XXX OMAP16xx only */
+ if (config->ocpi_enable)
+ config->ocpi_enable();
+
+#ifdef CONFIG_USB_OTG
+ if (need_transceiver) {
+ hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
+ int status = otg_set_host(hcd->usb_phy->otg,
+ &ohci_to_hcd(ohci)->self);
+ dev_dbg(hcd->self.controller, "init %s phy, status %d\n",
+ hcd->usb_phy->label, status);
+ if (status) {
+ usb_put_phy(hcd->usb_phy);
+ return status;
+ }
+ } else {
+ dev_err(hcd->self.controller, "can't find phy\n");
+ return -ENODEV;
+ }
+ ohci->start_hnp = start_hnp;
+ }
+#endif
+
+ omap_ohci_clock_power(1);
+
+ if (cpu_is_omap15xx()) {
+ omap_1510_local_bus_power(1);
+ omap_1510_local_bus_init();
+ }
+
+ ret = ohci_setup(hcd);
+ if (ret < 0)
+ return ret;
+
+ if (config->otg || config->rwc) {
+ ohci->hc_control = OHCI_CTRL_RWC;
+ writel(OHCI_CTRL_RWC, &ohci->regs->control);
+ }
+
+ /* board-specific power switching and overcurrent support */
+ if (machine_is_omap_osk() || machine_is_omap_innovator()) {
+ u32 rh = roothub_a (ohci);
+
+ /* power switching (ganged by default) */
+ rh &= ~RH_A_NPS;
+
+ /* TPS2045 switch for internal transceiver (port 1) */
+ if (machine_is_omap_osk()) {
+ ohci_to_hcd(ohci)->power_budget = 250;
+
+ rh &= ~RH_A_NOCP;
+
+ /* gpio9 for overcurrent detction */
+ omap_cfg_reg(W8_1610_GPIO9);
+ gpio_request(9, "OHCI overcurrent");
+ gpio_direction_input(9);
+
+ /* for paranoia's sake: disable USB.PUEN */
+ omap_cfg_reg(W4_USB_HIGHZ);
+ }
+ ohci_writel(ohci, rh, &ohci->regs->roothub.a);
+ ohci->flags &= ~OHCI_QUIRK_HUB_POWER;
+ } else if (machine_is_nokia770()) {
+ /* We require a self-powered hub, which should have
+ * plenty of power. */
+ ohci_to_hcd(ohci)->power_budget = 0;
+ }
+
+ /* FIXME hub_wq hub requests should manage power switching */
+ omap_ohci_transceiver_power(1);
+
+ /* board init will have already handled HMC and mux setup.
+ * any external transceiver should already be initialized
+ * too, so all configured ports use the right signaling now.
+ */
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_omap_probe - initialize OMAP-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int usb_hcd_omap_probe (const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ int retval, irq;
+ struct usb_hcd *hcd = 0;
+
+ if (pdev->num_resources != 2) {
+ dev_err(&pdev->dev, "invalid num_resources: %i\n",
+ pdev->num_resources);
+ return -ENODEV;
+ }
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM
+ || pdev->resource[1].flags != IORESOURCE_IRQ) {
+ dev_err(&pdev->dev, "invalid resource type\n");
+ return -ENODEV;
+ }
+
+ usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck");
+ if (IS_ERR(usb_host_ck))
+ return PTR_ERR(usb_host_ck);
+
+ if (!cpu_is_omap15xx())
+ usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck");
+ else
+ usb_dc_ck = clk_get(&pdev->dev, "lb_ck");
+
+ if (IS_ERR(usb_dc_ck)) {
+ clk_put(usb_host_ck);
+ return PTR_ERR(usb_dc_ck);
+ }
+
+
+ hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err0;
+ }
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "can't ioremap OHCI HCD\n");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = -ENXIO;
+ goto err3;
+ }
+ retval = usb_add_hcd(hcd, irq, 0);
+ if (retval)
+ goto err3;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+err3:
+ iounmap(hcd->regs);
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+err0:
+ clk_put(usb_dc_ck);
+ clk_put(usb_host_ck);
+ return retval;
+}
+
+
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_omap_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static inline void
+usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ dev_dbg(hcd->self.controller, "stopping USB Controller\n");
+ usb_remove_hcd(hcd);
+ omap_ohci_clock_power(0);
+ if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
+ (void) otg_set_host(hcd->usb_phy->otg, 0);
+ usb_put_phy(hcd->usb_phy);
+ }
+ if (machine_is_omap_osk())
+ gpio_free(9);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ clk_put(usb_dc_ck);
+ clk_put(usb_host_ck);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
+{
+ return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev);
+}
+
+static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ usb_hcd_omap_remove(hcd, dev);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+static int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(&pdev->dev);
+ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ omap_ohci_clock_power(0);
+ return ret;
+}
+
+static int ohci_omap_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ omap_ohci_clock_power(1);
+ ohci_resume(hcd, false);
+ return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Driver definition to register with the OMAP bus
+ */
+static struct platform_driver ohci_hcd_omap_driver = {
+ .probe = ohci_hcd_omap_drv_probe,
+ .remove = ohci_hcd_omap_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+#ifdef CONFIG_PM
+ .suspend = ohci_omap_suspend,
+ .resume = ohci_omap_resume,
+#endif
+ .driver = {
+ .name = "ohci",
+ },
+};
+
+static const struct ohci_driver_overrides omap_overrides __initconst = {
+ .product_desc = "OMAP OHCI",
+ .reset = ohci_omap_reset
+};
+
+static int __init ohci_omap_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides);
+ return platform_driver_register(&ohci_hcd_omap_driver);
+}
+module_init(ohci_omap_init);
+
+static void __exit ohci_omap_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_omap_driver);
+}
+module_exit(ohci_omap_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:ohci");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c
new file mode 100644
index 000000000..ec15aebe8
--- /dev/null
+++ b/drivers/usb/host/ohci-omap3.c
@@ -0,0 +1,211 @@
+/*
+ * ohci-omap3.c - driver for OHCI on OMAP3 and later processors
+ *
+ * Bus Glue for OMAP3 USBHOST 3 port OHCI controller
+ * This controller is also used in later OMAPs and AM35x chips
+ *
+ * Copyright (C) 2007-2010 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ * Author: Anand Gadiyar <gadiyar@ti.com>
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ *
+ * Based on ehci-omap.c and some other ohci glue layers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO (last updated Feb 27, 2011):
+ * - add kernel-doc
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI OMAP3 driver"
+
+static const char hcd_name[] = "ohci-omap3";
+static struct hc_driver __read_mostly ohci_omap3_hc_driver;
+
+/*
+ * configure so an HC device and id are always provided
+ * always called with process context; sleeping is OK
+ */
+
+/**
+ * ohci_hcd_omap3_probe - initialize OMAP-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int ohci_hcd_omap3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ohci_hcd *ohci;
+ struct usb_hcd *hcd = NULL;
+ void __iomem *regs = NULL;
+ struct resource *res;
+ int ret;
+ int irq;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (!dev->parent) {
+ dev_err(dev, "Missing parent device\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "OHCI irq failed\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "UHH OHCI get resource failed\n");
+ return -ENOMEM;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(dev, "UHH OHCI ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto err_io;
+
+ ret = -ENODEV;
+ hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "usb_create_hcd failed\n");
+ goto err_io;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ ohci = hcd_to_ohci(hcd);
+ /*
+ * RemoteWakeupConnected has to be set explicitly before
+ * calling ohci_run. The reset value of RWC is 0.
+ */
+ ohci->hc_control = OHCI_CTRL_RWC;
+
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret) {
+ dev_dbg(dev, "failed to add hcd with err %d\n", ret);
+ goto err_add_hcd;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return 0;
+
+err_add_hcd:
+ pm_runtime_put_sync(dev);
+ usb_put_hcd(hcd);
+
+err_io:
+ iounmap(regs);
+
+ return ret;
+}
+
+/*
+ * may be called without controller electrically present
+ * may be called with controller, bus, and devices active
+ */
+
+/**
+ * ohci_hcd_omap3_remove - shutdown processing for OHCI HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of ohci_hcd_omap3_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static int ohci_hcd_omap3_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ iounmap(hcd->regs);
+ usb_remove_hcd(hcd);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static const struct of_device_id omap_ohci_dt_ids[] = {
+ { .compatible = "ti,ohci-omap3" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids);
+
+static struct platform_driver ohci_hcd_omap3_driver = {
+ .probe = ohci_hcd_omap3_probe,
+ .remove = ohci_hcd_omap3_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ohci-omap3",
+ .of_match_table = omap_ohci_dt_ids,
+ },
+};
+
+static int __init ohci_omap3_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_omap3_hc_driver, NULL);
+ return platform_driver_register(&ohci_hcd_omap3_driver);
+}
+module_init(ohci_omap3_init);
+
+static void __exit ohci_omap3_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_omap3_driver);
+}
+module_exit(ohci_omap3_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:ohci-omap3");
+MODULE_AUTHOR("Anand Gadiyar <gadiyar@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
new file mode 100644
index 000000000..bb1509675
--- /dev/null
+++ b/drivers/usb/host/ohci-pci.c
@@ -0,0 +1,315 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ * PCI Bus Glue
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+#include "pci-quirks.h"
+
+#define DRIVER_DESC "OHCI PCI platform driver"
+
+static const char hcd_name[] = "ohci-pci";
+
+
+/*-------------------------------------------------------------------------*/
+
+static int broken_suspend(struct usb_hcd *hcd)
+{
+ device_init_wakeup(&hcd->self.root_hub->dev, 0);
+ return 0;
+}
+
+/* AMD 756, for most chips (early revs), corrupts register
+ * values on read ... so enable the vendor workaround.
+ */
+static int ohci_quirk_amd756(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci->flags = OHCI_QUIRK_AMD756;
+ ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
+
+ /* also erratum 10 (suspend/resume issues) */
+ return broken_suspend(hcd);
+}
+
+/* Apple's OHCI driver has a lot of bizarre workarounds
+ * for this chip. Evidently control and bulk lists
+ * can get confused. (B&W G3 models, and ...)
+ */
+static int ohci_quirk_opti(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_dbg (ohci, "WARNING: OPTi workarounds unavailable\n");
+
+ return 0;
+}
+
+/* Check for NSC87560. We have to look at the bridge (fn1) to
+ * identify the USB (fn2). This quirk might apply to more or
+ * even all NSC stuff.
+ */
+static int ohci_quirk_ns(struct usb_hcd *hcd)
+{
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ struct pci_dev *b;
+
+ b = pci_get_slot (pdev->bus, PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
+ if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
+ && b->vendor == PCI_VENDOR_ID_NS) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci->flags |= OHCI_QUIRK_SUPERIO;
+ ohci_dbg (ohci, "Using NSC SuperIO setup\n");
+ }
+ pci_dev_put(b);
+
+ return 0;
+}
+
+/* Check for Compaq's ZFMicro chipset, which needs short
+ * delays before control or bulk queues get re-activated
+ * in finish_unlinks()
+ */
+static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci->flags |= OHCI_QUIRK_ZFMICRO;
+ ohci_dbg(ohci, "enabled Compaq ZFMicro chipset quirks\n");
+
+ return 0;
+}
+
+/* Check for Toshiba SCC OHCI which has big endian registers
+ * and little endian in memory data structures
+ */
+static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ /* That chip is only present in the southbridge of some
+ * cell based platforms which are supposed to select
+ * CONFIG_USB_OHCI_BIG_ENDIAN_MMIO. We verify here if
+ * that was the case though.
+ */
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ ohci->flags |= OHCI_QUIRK_BE_MMIO;
+ ohci_dbg (ohci, "enabled big endian Toshiba quirk\n");
+ return 0;
+#else
+ ohci_err (ohci, "unsupported big endian Toshiba quirk\n");
+ return -ENXIO;
+#endif
+}
+
+/* Check for NEC chip and apply quirk for allegedly lost interrupts.
+ */
+
+static void ohci_quirk_nec_worker(struct work_struct *work)
+{
+ struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
+ int status;
+
+ status = ohci_restart(ohci);
+ if (status != 0)
+ ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n",
+ "ohci_restart", status);
+}
+
+static int ohci_quirk_nec(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci->flags |= OHCI_QUIRK_NEC;
+ INIT_WORK(&ohci->nec_work, ohci_quirk_nec_worker);
+ ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
+
+ return 0;
+}
+
+static int ohci_quirk_amd700(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ if (usb_amd_find_chipset_info())
+ ohci->flags |= OHCI_QUIRK_AMD_PLL;
+
+ /* SB800 needs pre-fetch fix */
+ if (usb_amd_prefetch_quirk()) {
+ ohci->flags |= OHCI_QUIRK_AMD_PREFETCH;
+ ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
+ }
+
+ ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND;
+ return 0;
+}
+
+/* List of quirks for OHCI */
+static const struct pci_device_id ohci_pci_quirks[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x740c),
+ .driver_data = (unsigned long)ohci_quirk_amd756,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_OPTI, 0xc861),
+ .driver_data = (unsigned long)ohci_quirk_opti,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_ANY_ID),
+ .driver_data = (unsigned long)ohci_quirk_ns,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xa0f8),
+ .driver_data = (unsigned long)ohci_quirk_zfmicro,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
+ .driver_data = (unsigned long)ohci_quirk_toshiba_scc,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB),
+ .driver_data = (unsigned long)ohci_quirk_nec,
+ },
+ {
+ /* Toshiba portege 4000 */
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = 0x5237,
+ .subvendor = PCI_VENDOR_ID_TOSHIBA,
+ .subdevice = 0x0004,
+ .driver_data = (unsigned long) broken_suspend,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
+ .driver_data = (unsigned long) broken_suspend,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4397),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4398),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+
+ /* FIXME for some of the early AMD 760 southbridges, OHCI
+ * won't work at all. blacklist them.
+ */
+
+ {},
+};
+
+static int ohci_pci_reset (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int ret = 0;
+
+ if (hcd->self.controller) {
+ const struct pci_device_id *quirk_id;
+
+ quirk_id = pci_match_id(ohci_pci_quirks, pdev);
+ if (quirk_id != NULL) {
+ int (*quirk)(struct usb_hcd *ohci);
+ quirk = (void *)quirk_id->driver_data;
+ ret = quirk(hcd);
+ }
+ }
+
+ if (ret == 0)
+ ret = ohci_setup(hcd);
+ /*
+ * After ohci setup RWC may not be set for add-in PCI cards.
+ * This transfers PCI PM wakeup capabilities.
+ */
+ if (device_can_wakeup(&pdev->dev))
+ ohci->hc_control |= OHCI_CTRL_RWC;
+ return ret;
+}
+
+static struct hc_driver __read_mostly ohci_pci_hc_driver;
+
+static const struct ohci_driver_overrides pci_overrides __initconst = {
+ .product_desc = "OHCI PCI host controller",
+ .reset = ohci_pci_reset,
+};
+
+static const struct pci_device_id pci_ids [] = { {
+ /* handle any USB OHCI controller */
+ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0),
+ .driver_data = (unsigned long) &ohci_pci_hc_driver,
+ }, {
+ /* The device in the ConneXT I/O hub has no class reg */
+ PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI),
+ .driver_data = (unsigned long) &ohci_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 ohci_pci_driver = {
+ .name = (char *) hcd_name,
+ .id_table = pci_ids,
+
+ .probe = usb_hcd_pci_probe,
+ .remove = usb_hcd_pci_remove,
+ .shutdown = usb_hcd_pci_shutdown,
+
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &usb_hcd_pci_pm_ops
+ },
+#endif
+};
+
+static int __init ohci_pci_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides);
+
+#ifdef CONFIG_PM
+ /* Entries for the PCI suspend/resume callbacks are special */
+ ohci_pci_hc_driver.pci_suspend = ohci_suspend;
+ ohci_pci_hc_driver.pci_resume = ohci_resume;
+#endif
+
+ return pci_register_driver(&ohci_pci_driver);
+}
+module_init(ohci_pci_init);
+
+static void __exit ohci_pci_cleanup(void)
+{
+ pci_unregister_driver(&ohci_pci_driver);
+}
+module_exit(ohci_pci_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: ehci_pci");
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
new file mode 100644
index 000000000..185ceee52
--- /dev/null
+++ b/drivers/usb/host/ohci-platform.c
@@ -0,0 +1,416 @@
+/*
+ * Generic platform ohci driver
+ *
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Derived from the OCHI-SSB driver
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/usb/ohci_pdriver.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI generic platform driver"
+#define OHCI_MAX_CLKS 3
+#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
+
+struct ohci_platform_priv {
+ struct clk *clks[OHCI_MAX_CLKS];
+ struct reset_control *rst;
+ struct phy **phys;
+ int num_phys;
+};
+
+static const char hcd_name[] = "ohci-platform";
+
+static int ohci_platform_power_on(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int clk, ret, phy_num;
+
+ for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) {
+ ret = clk_prepare_enable(priv->clks[clk]);
+ if (ret)
+ goto err_disable_clks;
+ }
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ ret = phy_init(priv->phys[phy_num]);
+ if (ret)
+ goto err_exit_phy;
+ ret = phy_power_on(priv->phys[phy_num]);
+ if (ret) {
+ phy_exit(priv->phys[phy_num]);
+ goto err_exit_phy;
+ }
+ }
+ }
+
+ return 0;
+
+err_exit_phy:
+ while (--phy_num >= 0) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(priv->clks[clk]);
+
+ return ret;
+}
+
+static void ohci_platform_power_off(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int clk, phy_num;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
+
+ for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
+ if (priv->clks[clk])
+ clk_disable_unprepare(priv->clks[clk]);
+}
+
+static struct hc_driver __read_mostly ohci_platform_hc_driver;
+
+static const struct ohci_driver_overrides platform_overrides __initconst = {
+ .product_desc = "Generic Platform OHCI controller",
+ .extra_priv_size = sizeof(struct ohci_platform_priv),
+};
+
+static struct usb_ohci_pdata ohci_platform_defaults = {
+ .power_on = ohci_platform_power_on,
+ .power_suspend = ohci_platform_power_off,
+ .power_off = ohci_platform_power_off,
+};
+
+static int ohci_platform_probe(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_mem;
+ struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct ohci_platform_priv *priv;
+ struct ohci_hcd *ohci;
+ const char *phy_name;
+ int err, irq, phy_num, clk = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Use reasonable defaults so platforms don't have to provide these
+ * with DT probing on ARM.
+ */
+ if (!pdata)
+ pdata = &ohci_platform_defaults;
+
+ err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ dev_err(&dev->dev, "no irq provided");
+ return irq;
+ }
+
+ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(dev, hcd);
+ dev->dev.platform_data = pdata;
+ priv = hcd_to_ohci_priv(hcd);
+ ohci = hcd_to_ohci(hcd);
+
+ if (pdata == &ohci_platform_defaults && dev->dev.of_node) {
+ if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+ ohci->flags |= OHCI_QUIRK_BE_MMIO;
+
+ if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+ ohci->flags |= OHCI_QUIRK_BE_DESC;
+
+ if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+ ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
+
+ if (of_property_read_bool(dev->dev.of_node, "no-big-frame-no"))
+ ohci->flags |= OHCI_QUIRK_FRAME_NO;
+
+ of_property_read_u32(dev->dev.of_node, "num-ports",
+ &ohci->num_ports);
+
+ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
+ "phys", "#phy-cells");
+ priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
+
+ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
+ sizeof(struct phy *), GFP_KERNEL);
+ if (!priv->phys)
+ return -ENOMEM;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ err = of_property_read_string_index(
+ dev->dev.of_node,
+ "phy-names", phy_num,
+ &phy_name);
+
+ if (err < 0) {
+ if (priv->num_phys > 1) {
+ dev_err(&dev->dev, "phy-names not provided");
+ goto err_put_hcd;
+ } else
+ phy_name = "usb";
+ }
+
+ priv->phys[phy_num] = devm_phy_get(&dev->dev,
+ phy_name);
+ if (IS_ERR(priv->phys[phy_num])) {
+ err = PTR_ERR(priv->phys[phy_num]);
+ if ((priv->num_phys > 1) ||
+ (err == -EPROBE_DEFER))
+ goto err_put_hcd;
+ priv->phys[phy_num] = NULL;
+ }
+ }
+
+ for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
+ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+ if (IS_ERR(priv->clks[clk])) {
+ err = PTR_ERR(priv->clks[clk]);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->clks[clk] = NULL;
+ break;
+ }
+ }
+
+ }
+
+ priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ err = PTR_ERR(priv->rst);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->rst = NULL;
+ } else {
+ err = reset_control_deassert(priv->rst);
+ if (err)
+ goto err_put_clks;
+ }
+
+ if (pdata->big_endian_desc)
+ ohci->flags |= OHCI_QUIRK_BE_DESC;
+ if (pdata->big_endian_mmio)
+ ohci->flags |= OHCI_QUIRK_BE_MMIO;
+ if (pdata->no_big_frame_no)
+ ohci->flags |= OHCI_QUIRK_FRAME_NO;
+ if (pdata->num_ports)
+ ohci->num_ports = pdata->num_ports;
+
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ if (ohci->flags & OHCI_QUIRK_BE_MMIO) {
+ dev_err(&dev->dev,
+ "Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n");
+ err = -EINVAL;
+ goto err_reset;
+ }
+#endif
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
+ if (ohci->flags & OHCI_QUIRK_BE_DESC) {
+ dev_err(&dev->dev,
+ "Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n");
+ err = -EINVAL;
+ goto err_reset;
+ }
+#endif
+
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ goto err_reset;
+ }
+
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto err_power;
+ }
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_power;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+err_reset:
+ if (priv->rst)
+ reset_control_assert(priv->rst);
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(priv->clks[clk]);
+err_put_hcd:
+ if (pdata == &ohci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ usb_put_hcd(hcd);
+
+ return err;
+}
+
+static int ohci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int clk;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
+ if (priv->rst)
+ reset_control_assert(priv->rst);
+
+ for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++)
+ clk_put(priv->clks[clk]);
+
+ usb_put_hcd(hcd);
+
+ if (pdata == &ohci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ohci_platform_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ return ret;
+}
+
+static int ohci_platform_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
+
+ ohci_resume(hcd, false);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id ohci_platform_ids[] = {
+ { .compatible = "generic-ohci", },
+ { .compatible = "cavium,octeon-6335-ohci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ohci_platform_ids);
+
+static const struct platform_device_id ohci_platform_table[] = {
+ { "ohci-platform", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ohci_platform_table);
+
+static SIMPLE_DEV_PM_OPS(ohci_platform_pm_ops, ohci_platform_suspend,
+ ohci_platform_resume);
+
+static struct platform_driver ohci_platform_driver = {
+ .id_table = ohci_platform_table,
+ .probe = ohci_platform_probe,
+ .remove = ohci_platform_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ohci-platform",
+ .pm = &ohci_platform_pm_ops,
+ .of_match_table = ohci_platform_ids,
+ }
+};
+
+static int __init ohci_platform_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides);
+ return platform_driver_register(&ohci_platform_driver);
+}
+module_init(ohci_platform_init);
+
+static void __exit ohci_platform_cleanup(void)
+{
+ platform_driver_unregister(&ohci_platform_driver);
+}
+module_exit(ohci_platform_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_AUTHOR("Alan Stern");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
new file mode 100644
index 000000000..4f87a5c61
--- /dev/null
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -0,0 +1,235 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2006 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Bus glue for OHCI HC on the of_platform bus
+ *
+ * Modified for of_platform bus from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/signal.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/prom.h>
+
+
+static int
+ohci_ppc_of_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ dev_err(hcd->self.controller, "can't start %s\n",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hc_driver ohci_ppc_of_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OF OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_ppc_of_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+
+static int ohci_hcd_ppc_of_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ struct resource res;
+ int irq;
+
+ int rv;
+ int is_bigendian;
+ struct device_node *np;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ is_bigendian =
+ of_device_is_compatible(dn, "ohci-bigendian") ||
+ of_device_is_compatible(dn, "ohci-be");
+
+ dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ hcd = usb_create_hcd(&ohci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = resource_size(&res);
+
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_rmr;
+ }
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
+ __FILE__);
+ rv = -EBUSY;
+ goto err_rmr;
+ }
+
+ ohci = hcd_to_ohci(hcd);
+ if (is_bigendian) {
+ ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
+ if (of_device_is_compatible(dn, "fsl,mpc5200-ohci"))
+ ohci->flags |= OHCI_QUIRK_FRAME_NO;
+ if (of_device_is_compatible(dn, "mpc5200-ohci"))
+ ohci->flags |= OHCI_QUIRK_FRAME_NO;
+ }
+
+ ohci_hcd_init(ohci);
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+ }
+
+ /* by now, 440epx is known to show usb_23 erratum */
+ np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx");
+
+ /* Work around - At this point ohci_run has executed, the
+ * controller is running, everything, the root ports, etc., is
+ * set up. If the ehci driver is loaded, put the ohci core in
+ * the suspended state. The ehci driver will bring it out of
+ * suspended state when / if a non-high speed USB device is
+ * attached to the USB Host port. If the ehci driver is not
+ * loaded, do nothing. request_mem_region is used to test if
+ * the ehci driver is loaded.
+ */
+ if (np != NULL) {
+ if (!of_address_to_resource(np, 0, &res)) {
+ if (!request_mem_region(res.start, 0x4, hcd_name)) {
+ writel_be((readl_be(&ohci->regs->control) |
+ OHCI_USB_SUSPEND), &ohci->regs->control);
+ (void) readl_be(&ohci->regs->control);
+ } else
+ release_mem_region(res.start, 0x4);
+ } else
+ pr_debug("%s: cannot get ehci offset from fdt\n", __FILE__);
+ }
+
+ irq_dispose_mapping(irq);
+err_rmr:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+static int ohci_hcd_ppc_of_remove(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ irq_dispose_mapping(hcd->irq);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id ohci_hcd_ppc_of_match[] = {
+#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_BE
+ {
+ .name = "usb",
+ .compatible = "ohci-bigendian",
+ },
+ {
+ .name = "usb",
+ .compatible = "ohci-be",
+ },
+#endif
+#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE
+ {
+ .name = "usb",
+ .compatible = "ohci-littledian",
+ },
+ {
+ .name = "usb",
+ .compatible = "ohci-le",
+ },
+#endif
+ {},
+};
+MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match);
+
+#if !defined(CONFIG_USB_OHCI_HCD_PPC_OF_BE) && \
+ !defined(CONFIG_USB_OHCI_HCD_PPC_OF_LE)
+#error "No endianness selected for ppc-of-ohci"
+#endif
+
+
+static struct platform_driver ohci_hcd_ppc_of_driver = {
+ .probe = ohci_hcd_ppc_of_probe,
+ .remove = ohci_hcd_ppc_of_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ppc-of-ohci",
+ .of_match_table = ohci_hcd_ppc_of_match,
+ },
+};
+
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
new file mode 100644
index 000000000..71d8bc4c2
--- /dev/null
+++ b/drivers/usb/host/ohci-ps3.c
@@ -0,0 +1,251 @@
+/*
+ * PS3 OHCI Host Controller driver
+ *
+ * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright 2006 Sony Corp.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/firmware.h>
+#include <asm/ps3.h>
+
+static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci->flags |= OHCI_QUIRK_BE_MMIO;
+ ohci_hcd_init(ohci);
+ return ohci_init(ohci);
+}
+
+static int ps3_ohci_hc_start(struct usb_hcd *hcd)
+{
+ int result;
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ /* Handle root hub init quirk in spider south bridge. */
+ /* Also set PwrOn2PwrGood to 0x7f (254ms). */
+
+ ohci_writel(ohci, 0x7f000000 | RH_A_PSM | RH_A_OCPM,
+ &ohci->regs->roothub.a);
+ ohci_writel(ohci, 0x00060000, &ohci->regs->roothub.b);
+
+ result = ohci_run(ohci);
+
+ if (result < 0) {
+ dev_err(hcd->self.controller, "can't start %s\n",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ }
+
+ return result;
+}
+
+static const struct hc_driver ps3_ohci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PS3 OHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+ .reset = ps3_ohci_hc_reset,
+ .start = ps3_ohci_hc_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+ .get_frame_number = ohci_get_frame,
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+ .start_port_reset = ohci_start_port_reset,
+#if defined(CONFIG_PM)
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+};
+
+static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
+{
+ int result;
+ struct usb_hcd *hcd;
+ unsigned int virq;
+ static u64 dummy_mask = DMA_BIT_MASK(32);
+
+ if (usb_disabled()) {
+ result = -ENODEV;
+ goto fail_start;
+ }
+
+ result = ps3_open_hv_device(dev);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ result = -EPERM;
+ goto fail_open;
+ }
+
+ result = ps3_dma_region_create(dev->d_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+ "(%d)\n", __func__, __LINE__, result);
+ BUG_ON("check region type");
+ goto fail_dma_region;
+ }
+
+ result = ps3_mmio_region_create(dev->m_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
+ __func__, __LINE__);
+ result = -EPERM;
+ goto fail_mmio_region;
+ }
+
+ dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
+ __LINE__, dev->m_region->lpar_addr);
+
+ result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n",
+ __func__, __LINE__, virq);
+ result = -EPERM;
+ goto fail_irq;
+ }
+
+ dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
+
+ hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core));
+
+ if (!hcd) {
+ dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
+ __LINE__);
+ result = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ hcd->rsrc_start = dev->m_region->lpar_addr;
+ hcd->rsrc_len = dev->m_region->len;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+ dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+ __func__, __LINE__);
+
+ hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
+
+ if (!hcd->regs) {
+ dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__,
+ __LINE__);
+ result = -EPERM;
+ goto fail_ioremap;
+ }
+
+ dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->rsrc_start);
+ dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->rsrc_len);
+ dev_dbg(&dev->core, "%s:%d: hcd->regs %lxh\n", __func__, __LINE__,
+ (unsigned long)hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
+ (unsigned long)virq);
+
+ ps3_system_bus_set_drvdata(dev, hcd);
+
+ result = usb_add_hcd(hcd, virq, 0);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
+ __func__, __LINE__, result);
+ goto fail_add_hcd;
+ }
+
+ device_wakeup_enable(hcd->self.controller);
+ return result;
+
+fail_add_hcd:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ ps3_io_irq_destroy(virq);
+fail_irq:
+ ps3_free_mmio_region(dev->m_region);
+fail_mmio_region:
+ ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+ ps3_close_hv_device(dev);
+fail_open:
+fail_start:
+ return result;
+}
+
+static int ps3_ohci_remove(struct ps3_system_bus_device *dev)
+{
+ unsigned int tmp;
+ struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
+
+ BUG_ON(!hcd);
+
+ dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+ tmp = hcd->irq;
+
+ ohci_shutdown(hcd);
+ usb_remove_hcd(hcd);
+
+ ps3_system_bus_set_drvdata(dev, NULL);
+
+ BUG_ON(!hcd->regs);
+ iounmap(hcd->regs);
+
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ ps3_io_irq_destroy(tmp);
+ ps3_free_mmio_region(dev->m_region);
+
+ ps3_dma_region_free(dev->d_region);
+ ps3_close_hv_device(dev);
+
+ return 0;
+}
+
+static int __init ps3_ohci_driver_register(struct ps3_system_bus_driver *drv)
+{
+ return firmware_has_feature(FW_FEATURE_PS3_LV1)
+ ? ps3_system_bus_driver_register(drv)
+ : 0;
+}
+
+static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI);
+
+static struct ps3_system_bus_driver ps3_ohci_driver = {
+ .core.name = "ps3-ohci-driver",
+ .core.owner = THIS_MODULE,
+ .match_id = PS3_MATCH_ID_OHCI,
+ .probe = ps3_ohci_probe,
+ .remove = ps3_ohci_remove,
+ .shutdown = ps3_ohci_remove,
+};
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
new file mode 100644
index 000000000..ba1bec7db
--- /dev/null
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -0,0 +1,639 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for pxa27x
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * Modified for pxa27x from ohci-lh7a404.c
+ * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/usb-ohci-pxa27x.h>
+#include <linux/platform_data/usb-pxa3xx-ulpi.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include <mach/hardware.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI PXA27x/PXA3x driver"
+
+/*
+ * UHC: USB Host Controller (OHCI-like) register definitions
+ */
+#define UHCREV (0x0000) /* UHC HCI Spec Revision */
+#define UHCHCON (0x0004) /* UHC Host Control Register */
+#define UHCCOMS (0x0008) /* UHC Command Status Register */
+#define UHCINTS (0x000C) /* UHC Interrupt Status Register */
+#define UHCINTE (0x0010) /* UHC Interrupt Enable */
+#define UHCINTD (0x0014) /* UHC Interrupt Disable */
+#define UHCHCCA (0x0018) /* UHC Host Controller Comm. Area */
+#define UHCPCED (0x001C) /* UHC Period Current Endpt Descr */
+#define UHCCHED (0x0020) /* UHC Control Head Endpt Descr */
+#define UHCCCED (0x0024) /* UHC Control Current Endpt Descr */
+#define UHCBHED (0x0028) /* UHC Bulk Head Endpt Descr */
+#define UHCBCED (0x002C) /* UHC Bulk Current Endpt Descr */
+#define UHCDHEAD (0x0030) /* UHC Done Head */
+#define UHCFMI (0x0034) /* UHC Frame Interval */
+#define UHCFMR (0x0038) /* UHC Frame Remaining */
+#define UHCFMN (0x003C) /* UHC Frame Number */
+#define UHCPERS (0x0040) /* UHC Periodic Start */
+#define UHCLS (0x0044) /* UHC Low Speed Threshold */
+
+#define UHCRHDA (0x0048) /* UHC Root Hub Descriptor A */
+#define UHCRHDA_NOCP (1 << 12) /* No over current protection */
+#define UHCRHDA_OCPM (1 << 11) /* Over Current Protection Mode */
+#define UHCRHDA_POTPGT(x) \
+ (((x) & 0xff) << 24) /* Power On To Power Good Time */
+
+#define UHCRHDB (0x004C) /* UHC Root Hub Descriptor B */
+#define UHCRHS (0x0050) /* UHC Root Hub Status */
+#define UHCRHPS1 (0x0054) /* UHC Root Hub Port 1 Status */
+#define UHCRHPS2 (0x0058) /* UHC Root Hub Port 2 Status */
+#define UHCRHPS3 (0x005C) /* UHC Root Hub Port 3 Status */
+
+#define UHCSTAT (0x0060) /* UHC Status Register */
+#define UHCSTAT_UPS3 (1 << 16) /* USB Power Sense Port3 */
+#define UHCSTAT_SBMAI (1 << 15) /* System Bus Master Abort Interrupt*/
+#define UHCSTAT_SBTAI (1 << 14) /* System Bus Target Abort Interrupt*/
+#define UHCSTAT_UPRI (1 << 13) /* USB Port Resume Interrupt */
+#define UHCSTAT_UPS2 (1 << 12) /* USB Power Sense Port 2 */
+#define UHCSTAT_UPS1 (1 << 11) /* USB Power Sense Port 1 */
+#define UHCSTAT_HTA (1 << 10) /* HCI Target Abort */
+#define UHCSTAT_HBA (1 << 8) /* HCI Buffer Active */
+#define UHCSTAT_RWUE (1 << 7) /* HCI Remote Wake Up Event */
+
+#define UHCHR (0x0064) /* UHC Reset Register */
+#define UHCHR_SSEP3 (1 << 11) /* Sleep Standby Enable for Port3 */
+#define UHCHR_SSEP2 (1 << 10) /* Sleep Standby Enable for Port2 */
+#define UHCHR_SSEP1 (1 << 9) /* Sleep Standby Enable for Port1 */
+#define UHCHR_PCPL (1 << 7) /* Power control polarity low */
+#define UHCHR_PSPL (1 << 6) /* Power sense polarity low */
+#define UHCHR_SSE (1 << 5) /* Sleep Standby Enable */
+#define UHCHR_UIT (1 << 4) /* USB Interrupt Test */
+#define UHCHR_SSDC (1 << 3) /* Simulation Scale Down Clock */
+#define UHCHR_CGR (1 << 2) /* Clock Generation Reset */
+#define UHCHR_FHR (1 << 1) /* Force Host Controller Reset */
+#define UHCHR_FSBIR (1 << 0) /* Force System Bus Iface Reset */
+
+#define UHCHIE (0x0068) /* UHC Interrupt Enable Register*/
+#define UHCHIE_UPS3IE (1 << 14) /* Power Sense Port3 IntEn */
+#define UHCHIE_UPRIE (1 << 13) /* Port Resume IntEn */
+#define UHCHIE_UPS2IE (1 << 12) /* Power Sense Port2 IntEn */
+#define UHCHIE_UPS1IE (1 << 11) /* Power Sense Port1 IntEn */
+#define UHCHIE_TAIE (1 << 10) /* HCI Interface Transfer Abort
+ Interrupt Enable*/
+#define UHCHIE_HBAIE (1 << 8) /* HCI Buffer Active IntEn */
+#define UHCHIE_RWIE (1 << 7) /* Remote Wake-up IntEn */
+
+#define UHCHIT (0x006C) /* UHC Interrupt Test register */
+
+#define PXA_UHC_MAX_PORTNUM 3
+
+static const char hcd_name[] = "ohci-pxa27x";
+
+static struct hc_driver __read_mostly ohci_pxa27x_hc_driver;
+
+struct pxa27x_ohci {
+ struct clk *clk;
+ void __iomem *mmio_base;
+ struct regulator *vbus[3];
+ bool vbus_enabled[3];
+};
+
+#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv)
+
+/*
+ PMM_NPS_MODE -- PMM Non-power switching mode
+ Ports are powered continuously.
+
+ PMM_GLOBAL_MODE -- PMM global switching mode
+ All ports are powered at the same time.
+
+ PMM_PERPORT_MODE -- PMM per port switching mode
+ Ports are powered individually.
+ */
+static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
+{
+ uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
+ uint32_t uhcrhdb = __raw_readl(pxa_ohci->mmio_base + UHCRHDB);
+
+ switch (mode) {
+ case PMM_NPS_MODE:
+ uhcrhda |= RH_A_NPS;
+ break;
+ case PMM_GLOBAL_MODE:
+ uhcrhda &= ~(RH_A_NPS & RH_A_PSM);
+ break;
+ case PMM_PERPORT_MODE:
+ uhcrhda &= ~(RH_A_NPS);
+ uhcrhda |= RH_A_PSM;
+
+ /* Set port power control mask bits, only 3 ports. */
+ uhcrhdb |= (0x7<<17);
+ break;
+ default:
+ printk( KERN_ERR
+ "Invalid mode %d, set to non-power switch mode.\n",
+ mode );
+
+ uhcrhda |= RH_A_NPS;
+ }
+
+ __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
+ __raw_writel(uhcrhdb, pxa_ohci->mmio_base + UHCRHDB);
+ return 0;
+}
+
+static int pxa27x_ohci_set_vbus_power(struct pxa27x_ohci *pxa_ohci,
+ unsigned int port, bool enable)
+{
+ struct regulator *vbus = pxa_ohci->vbus[port];
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(vbus))
+ return 0;
+
+ if (enable && !pxa_ohci->vbus_enabled[port])
+ ret = regulator_enable(vbus);
+ else if (!enable && pxa_ohci->vbus_enabled[port])
+ ret = regulator_disable(vbus);
+
+ if (ret < 0)
+ return ret;
+
+ pxa_ohci->vbus_enabled[port] = enable;
+
+ return 0;
+}
+
+static int pxa27x_ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+ int ret;
+
+ switch (typeReq) {
+ case SetPortFeature:
+ case ClearPortFeature:
+ if (!wIndex || wIndex > 3)
+ return -EPIPE;
+
+ if (wValue != USB_PORT_FEAT_POWER)
+ break;
+
+ ret = pxa27x_ohci_set_vbus_power(pxa_ohci, wIndex - 1,
+ typeReq == SetPortFeature);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+}
+/*-------------------------------------------------------------------------*/
+
+static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci,
+ struct pxaohci_platform_data *inf)
+{
+ uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
+ uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
+
+ if (inf->flags & ENABLE_PORT1)
+ uhchr &= ~UHCHR_SSEP1;
+
+ if (inf->flags & ENABLE_PORT2)
+ uhchr &= ~UHCHR_SSEP2;
+
+ if (inf->flags & ENABLE_PORT3)
+ uhchr &= ~UHCHR_SSEP3;
+
+ if (inf->flags & POWER_CONTROL_LOW)
+ uhchr |= UHCHR_PCPL;
+
+ if (inf->flags & POWER_SENSE_LOW)
+ uhchr |= UHCHR_PSPL;
+
+ if (inf->flags & NO_OC_PROTECTION)
+ uhcrhda |= UHCRHDA_NOCP;
+ else
+ uhcrhda &= ~UHCRHDA_NOCP;
+
+ if (inf->flags & OC_MODE_PERPORT)
+ uhcrhda |= UHCRHDA_OCPM;
+ else
+ uhcrhda &= ~UHCRHDA_OCPM;
+
+ if (inf->power_on_delay) {
+ uhcrhda &= ~UHCRHDA_POTPGT(0xff);
+ uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2);
+ }
+
+ __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+ __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
+}
+
+static inline void pxa27x_reset_hc(struct pxa27x_ohci *pxa_ohci)
+{
+ uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
+
+ __raw_writel(uhchr | UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
+ udelay(11);
+ __raw_writel(uhchr & ~UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
+}
+
+#ifdef CONFIG_PXA27x
+extern void pxa27x_clear_otgph(void);
+#else
+#define pxa27x_clear_otgph() do {} while (0)
+#endif
+
+static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
+{
+ int retval = 0;
+ struct pxaohci_platform_data *inf;
+ uint32_t uhchr;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ inf = dev_get_platdata(dev);
+
+ clk_prepare_enable(pxa_ohci->clk);
+
+ pxa27x_reset_hc(pxa_ohci);
+
+ uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) | UHCHR_FSBIR;
+ __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+
+ while (__raw_readl(pxa_ohci->mmio_base + UHCHR) & UHCHR_FSBIR)
+ cpu_relax();
+
+ pxa27x_setup_hc(pxa_ohci, inf);
+
+ if (inf->init)
+ retval = inf->init(dev);
+
+ if (retval < 0)
+ return retval;
+
+ if (cpu_is_pxa3xx())
+ pxa3xx_u2d_start_hc(&hcd->self);
+
+ uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) & ~UHCHR_SSE;
+ __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+ __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, pxa_ohci->mmio_base + UHCHIE);
+
+ /* Clear any OTG Pin Hold */
+ pxa27x_clear_otgph();
+ return 0;
+}
+
+static void pxa27x_stop_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
+{
+ struct pxaohci_platform_data *inf;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ uint32_t uhccoms;
+
+ inf = dev_get_platdata(dev);
+
+ if (cpu_is_pxa3xx())
+ pxa3xx_u2d_stop_hc(&hcd->self);
+
+ if (inf->exit)
+ inf->exit(dev);
+
+ pxa27x_reset_hc(pxa_ohci);
+
+ /* Host Controller Reset */
+ uhccoms = __raw_readl(pxa_ohci->mmio_base + UHCCOMS) | 0x01;
+ __raw_writel(uhccoms, pxa_ohci->mmio_base + UHCCOMS);
+ udelay(10);
+
+ clk_disable_unprepare(pxa_ohci->clk);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ohci_dt_ids[] = {
+ { .compatible = "marvell,pxa-ohci" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pxa_ohci_dt_ids);
+
+static int ohci_pxa_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pxaohci_platform_data *pdata;
+ u32 tmp;
+ int ret;
+
+ if (!np)
+ return 0;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "marvell,enable-port1", NULL))
+ pdata->flags |= ENABLE_PORT1;
+ if (of_get_property(np, "marvell,enable-port2", NULL))
+ pdata->flags |= ENABLE_PORT2;
+ if (of_get_property(np, "marvell,enable-port3", NULL))
+ pdata->flags |= ENABLE_PORT3;
+ if (of_get_property(np, "marvell,port-sense-low", NULL))
+ pdata->flags |= POWER_SENSE_LOW;
+ if (of_get_property(np, "marvell,power-control-low", NULL))
+ pdata->flags |= POWER_CONTROL_LOW;
+ if (of_get_property(np, "marvell,no-oc-protection", NULL))
+ pdata->flags |= NO_OC_PROTECTION;
+ if (of_get_property(np, "marvell,oc-mode-perport", NULL))
+ pdata->flags |= OC_MODE_PERPORT;
+ if (!of_property_read_u32(np, "marvell,power-on-delay", &tmp))
+ pdata->power_on_delay = tmp;
+ if (!of_property_read_u32(np, "marvell,port-mode", &tmp))
+ pdata->port_mode = tmp;
+ if (!of_property_read_u32(np, "marvell,power-budget", &tmp))
+ pdata->power_budget = tmp;
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int ohci_pxa_of_init(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev)
+{
+ int retval, irq;
+ struct usb_hcd *hcd;
+ struct pxaohci_platform_data *inf;
+ struct pxa27x_ohci *pxa_ohci;
+ struct ohci_hcd *ohci;
+ struct resource *r;
+ struct clk *usb_clk;
+ unsigned int i;
+
+ retval = ohci_pxa_of_init(pdev);
+ if (retval)
+ return retval;
+
+ inf = dev_get_platdata(&pdev->dev);
+
+ if (!inf)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ pr_err("no resource of IORESOURCE_IRQ");
+ return -ENXIO;
+ }
+
+ usb_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usb_clk))
+ return PTR_ERR(usb_clk);
+
+ hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x");
+ if (!hcd)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err;
+ }
+ hcd->rsrc_start = r->start;
+ hcd->rsrc_len = resource_size(r);
+
+ /* initialize "struct pxa27x_ohci" */
+ pxa_ohci = to_pxa27x_ohci(hcd);
+ pxa_ohci->clk = usb_clk;
+ pxa_ohci->mmio_base = (void __iomem *)hcd->regs;
+
+ for (i = 0; i < 3; ++i) {
+ char name[6];
+
+ if (!(inf->flags & (ENABLE_PORT1 << i)))
+ continue;
+
+ sprintf(name, "vbus%u", i + 1);
+ pxa_ohci->vbus[i] = devm_regulator_get(&pdev->dev, name);
+ }
+
+ retval = pxa27x_start_hc(pxa_ohci, &pdev->dev);
+ if (retval < 0) {
+ pr_debug("pxa27x_start_hc failed");
+ goto err;
+ }
+
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
+
+ if (inf->power_budget)
+ hcd->power_budget = inf->power_budget;
+
+ /* The value of NDP in roothub_a is incorrect on this hardware */
+ ohci = hcd_to_ohci(hcd);
+ ohci->num_ports = 3;
+
+ retval = usb_add_hcd(hcd, irq, 0);
+ if (retval == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+ }
+
+ pxa27x_stop_hc(pxa_ohci, &pdev->dev);
+ err:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+ unsigned int i;
+
+ usb_remove_hcd(hcd);
+ pxa27x_stop_hc(pxa_ohci, &pdev->dev);
+
+ for (i = 0; i < 3; ++i)
+ pxa27x_ohci_set_vbus_power(pxa_ohci, i, false);
+
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev)
+{
+ pr_debug ("In ohci_hcd_pxa27x_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
+}
+
+static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_pxa27x_remove(hcd, pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ pxa27x_stop_hc(pxa_ohci, dev);
+ return ret;
+}
+
+static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+ struct pxaohci_platform_data *inf = dev_get_platdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int status;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ status = pxa27x_start_hc(pxa_ohci, dev);
+ if (status < 0)
+ return status;
+
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
+
+ ohci_resume(hcd, false);
+ return 0;
+}
+
+static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
+ .suspend = ohci_hcd_pxa27x_drv_suspend,
+ .resume = ohci_hcd_pxa27x_drv_resume,
+};
+#endif
+
+static struct platform_driver ohci_hcd_pxa27x_driver = {
+ .probe = ohci_hcd_pxa27x_drv_probe,
+ .remove = ohci_hcd_pxa27x_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "pxa27x-ohci",
+ .of_match_table = of_match_ptr(pxa_ohci_dt_ids),
+#ifdef CONFIG_PM
+ .pm = &ohci_hcd_pxa27x_pm_ops,
+#endif
+ },
+};
+
+static const struct ohci_driver_overrides pxa27x_overrides __initconst = {
+ .extra_priv_size = sizeof(struct pxa27x_ohci),
+};
+
+static int __init ohci_pxa27x_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides);
+ ohci_pxa27x_hc_driver.hub_control = pxa27x_ohci_hub_control;
+
+ return platform_driver_register(&ohci_hcd_pxa27x_driver);
+}
+module_init(ohci_pxa27x_init);
+
+static void __exit ohci_pxa27x_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+module_exit(ohci_pxa27x_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa27x-ohci");
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
new file mode 100644
index 000000000..fe1d5fc7d
--- /dev/null
+++ b/drivers/usb/host/ohci-q.c
@@ -0,0 +1,1229 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
+{
+ int last = urb_priv->length - 1;
+
+ if (last >= 0) {
+ int i;
+ struct td *td;
+
+ for (i = 0; i <= last; i++) {
+ td = urb_priv->td [i];
+ if (td)
+ td_free (hc, td);
+ }
+ }
+
+ list_del (&urb_priv->pending);
+ kfree (urb_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * URB goes back to driver, and isn't reissued.
+ * It's completely gone from HC data structures.
+ * PRECONDITION: ohci lock held, irqs blocked.
+ */
+static void
+finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status)
+__releases(ohci->lock)
+__acquires(ohci->lock)
+{
+ struct device *dev = ohci_to_hcd(ohci)->self.controller;
+ struct usb_host_endpoint *ep = urb->ep;
+ struct urb_priv *urb_priv;
+
+ // ASSERT (urb->hcpriv != 0);
+
+ restart:
+ urb_free_priv (ohci, urb->hcpriv);
+ urb->hcpriv = NULL;
+ if (likely(status == -EINPROGRESS))
+ status = 0;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
+ if (quirk_amdiso(ohci))
+ usb_amd_quirk_pll_enable();
+ if (quirk_amdprefetch(ohci))
+ sb800_prefetch(dev, 0);
+ }
+ break;
+ case PIPE_INTERRUPT:
+ ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
+ break;
+ }
+
+ /* urb->complete() can reenter this HCD */
+ usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb);
+ spin_unlock (&ohci->lock);
+ usb_hcd_giveback_urb(ohci_to_hcd(ohci), urb, status);
+ spin_lock (&ohci->lock);
+
+ /* stop periodic dma if it's not needed */
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0) {
+ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ }
+
+ /*
+ * An isochronous URB that is sumitted too late won't have any TDs
+ * (marked by the fact that the td_cnt value is larger than the
+ * actual number of TDs). If the next URB on this endpoint is like
+ * that, give it back now.
+ */
+ if (!list_empty(&ep->urb_list)) {
+ urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ urb_priv = urb->hcpriv;
+ if (urb_priv->td_cnt > urb_priv->length) {
+ status = 0;
+ goto restart;
+ }
+ }
+}
+
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right schedule branch to use for a periodic ed.
+ * does some load balancing; returns the branch, or negative errno.
+ */
+static int balance (struct ohci_hcd *ohci, int interval, int load)
+{
+ int i, branch = -ENOSPC;
+
+ /* iso periods can be huge; iso tds specify frame numbers */
+ if (interval > NUM_INTS)
+ interval = NUM_INTS;
+
+ /* search for the least loaded schedule branch of that period
+ * that has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < interval ; i++) {
+ if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
+ int j;
+
+ /* usb 1.1 says 90% of one frame */
+ for (j = i; j < NUM_INTS; j += interval) {
+ if ((ohci->load [j] + load) > 900)
+ break;
+ }
+ if (j < NUM_INTS)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* both iso and interrupt requests have periods; this routine puts them
+ * into the schedule tree in the apppropriate place. most iso devices use
+ * 1msec periods, but that's not required.
+ */
+static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)
+{
+ unsigned i;
+
+ ohci_dbg(ohci, "link %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed **prev = &ohci->periodic [i];
+ __hc32 *prev_p = &ohci->hcca->int_table [i];
+ struct ed *here = *prev;
+
+ /* sorting each branch by period (slow before fast)
+ * lets us share the faster parts of the tree.
+ * (plus maybe: put interrupt eds before iso)
+ */
+ while (here && ed != here) {
+ if (ed->interval > here->interval)
+ break;
+ prev = &here->ed_next;
+ prev_p = &here->hwNextED;
+ here = *prev;
+ }
+ if (ed != here) {
+ ed->ed_next = here;
+ if (here)
+ ed->hwNextED = *prev_p;
+ wmb ();
+ *prev = ed;
+ *prev_p = cpu_to_hc32(ohci, ed->dma);
+ wmb();
+ }
+ ohci->load [i] += ed->load;
+ }
+ ohci_to_hcd(ohci)->self.bandwidth_allocated += ed->load / ed->interval;
+}
+
+/* link an ed into one of the HC chains */
+
+static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int branch;
+
+ ed->state = ED_OPER;
+ ed->ed_prev = NULL;
+ ed->ed_next = NULL;
+ ed->hwNextED = 0;
+ wmb ();
+
+ /* we care about rm_list when setting CLE/BLE in case the HC was at
+ * work on some TD when CLE/BLE was turned off, and isn't quiesced
+ * yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.
+ *
+ * control and bulk EDs are doubly linked (ed_next, ed_prev), but
+ * periodic ones are singly linked (ed_next). that's because the
+ * periodic schedule encodes a tree like figure 3-5 in the ohci
+ * spec: each qh can have several "previous" nodes, and the tree
+ * doesn't have unused/idle descriptors.
+ */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ohci->ed_controltail == NULL) {
+ WARN_ON (ohci->hc_control & OHCI_CTRL_CLE);
+ ohci_writel (ohci, ed->dma,
+ &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->ed_next = ed;
+ ohci->ed_controltail->hwNextED = cpu_to_hc32 (ohci,
+ ed->dma);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list) {
+ wmb();
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ ohci->ed_controltail = ed;
+ break;
+
+ case PIPE_BULK:
+ if (ohci->ed_bulktail == NULL) {
+ WARN_ON (ohci->hc_control & OHCI_CTRL_BLE);
+ ohci_writel (ohci, ed->dma, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->ed_next = ed;
+ ohci->ed_bulktail->hwNextED = cpu_to_hc32 (ohci,
+ ed->dma);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list) {
+ wmb();
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ ohci->ed_bulktail = ed;
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ branch = balance (ohci, ed->interval, ed->load);
+ if (branch < 0) {
+ ohci_dbg (ohci,
+ "ERR %d, interval %d msecs, load %d\n",
+ branch, ed->interval, ed->load);
+ // FIXME if there are TDs queued, fail them!
+ return branch;
+ }
+ ed->branch = branch;
+ periodic_link (ohci, ed);
+ }
+
+ /* the HC may not see the schedule updates yet, but if it does
+ * then they'll be properly ordered.
+ */
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int i;
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed *temp;
+ struct ed **prev = &ohci->periodic [i];
+ __hc32 *prev_p = &ohci->hcca->int_table [i];
+
+ while (*prev && (temp = *prev) != ed) {
+ prev_p = &temp->hwNextED;
+ prev = &temp->ed_next;
+ }
+ if (*prev) {
+ *prev_p = ed->hwNextED;
+ *prev = ed->ed_next;
+ }
+ ohci->load [i] -= ed->load;
+ }
+ ohci_to_hcd(ohci)->self.bandwidth_allocated -= ed->load / ed->interval;
+
+ ohci_dbg(ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+}
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed
+ * (assuming it already started that, which needn't be true).
+ *
+ * ED_UNLINK is a transient state: the HC may still see this ED, but soon
+ * it won't. ED_SKIP means the HC will finish its current transaction,
+ * but won't start anything new. The TD queue may still grow; device
+ * drivers don't know about this HCD-internal state.
+ *
+ * When the HC can't see the ED, something changes ED_UNLINK to one of:
+ *
+ * - ED_OPER: when there's any request queued, the ED gets rescheduled
+ * immediately. HC should be working on them.
+ *
+ * - ED_IDLE: when there's no TD queue or the HC isn't running.
+ *
+ * When finish_unlinks() runs later, after SOF interrupt, it will often
+ * complete one or more URB unlinks before making that state change.
+ */
+static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_SKIP);
+ wmb ();
+ ed->state = ED_UNLINK;
+
+ /* To deschedule something from the control or bulk list, just
+ * clear CLE/BLE and wait. There's no safe way to scrub out list
+ * head/current registers until later, and "later" isn't very
+ * tightly specified. Figure 6-5 and Section 6.4.2.2 show how
+ * the HC is reading the ED queues (while we modify them).
+ *
+ * For now, ed_schedule() is "later". It might be good paranoia
+ * to scrub those registers in finish_unlinks(), in case of bugs
+ * that make the HC try to use them.
+ */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ /* remove ED from the HC's list: */
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ // a ohci_readl() later syncs CLE with the HC
+ } else
+ ohci_writel (ohci,
+ hc32_to_cpup (ohci, &ed->hwNextED),
+ &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ /* remove ED from the HCD's list: */
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ if (ohci->ed_controltail)
+ ohci->ed_controltail->ed_next = NULL;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ /* remove ED from the HC's list: */
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ // a ohci_readl() later syncs BLE with the HC
+ } else
+ ohci_writel (ohci,
+ hc32_to_cpup (ohci, &ed->hwNextED),
+ &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ /* remove ED from the HCD's list: */
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ if (ohci->ed_bulktail)
+ ohci->ed_bulktail->ed_next = NULL;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ periodic_unlink (ohci, ed);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* get and maybe (re)init an endpoint. init _should_ be done only as part
+ * of enumeration, usb_set_configuration() or usb_set_interface().
+ */
+static struct ed *ed_get (
+ struct ohci_hcd *ohci,
+ struct usb_host_endpoint *ep,
+ struct usb_device *udev,
+ unsigned int pipe,
+ int interval
+) {
+ struct ed *ed;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (!(ed = ep->hcpriv)) {
+ struct td *td;
+ int is_out;
+ u32 info;
+
+ ed = ed_alloc (ohci, GFP_ATOMIC);
+ if (!ed) {
+ /* out of memory */
+ goto done;
+ }
+
+ /* dummy td; end of td list for ed */
+ td = td_alloc (ohci, GFP_ATOMIC);
+ if (!td) {
+ /* out of memory */
+ ed_free (ohci, ed);
+ ed = NULL;
+ goto done;
+ }
+ ed->dummy = td;
+ ed->hwTailP = cpu_to_hc32 (ohci, td->td_dma);
+ ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
+ ed->state = ED_IDLE;
+
+ is_out = !(ep->desc.bEndpointAddress & USB_DIR_IN);
+
+ /* FIXME usbcore changes dev->devnum before SET_ADDRESS
+ * succeeds ... otherwise we wouldn't need "pipe".
+ */
+ info = usb_pipedevice (pipe);
+ ed->type = usb_pipetype(pipe);
+
+ info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7;
+ info |= usb_endpoint_maxp(&ep->desc) << 16;
+ if (udev->speed == USB_SPEED_LOW)
+ info |= ED_LOWSPEED;
+ /* only control transfers store pids in tds */
+ if (ed->type != PIPE_CONTROL) {
+ info |= is_out ? ED_OUT : ED_IN;
+ if (ed->type != PIPE_BULK) {
+ /* periodic transfers... */
+ if (ed->type == PIPE_ISOCHRONOUS)
+ info |= ED_ISO;
+ else if (interval > 32) /* iso can be bigger */
+ interval = 32;
+ ed->interval = interval;
+ ed->load = usb_calc_bus_time (
+ udev->speed, !is_out,
+ ed->type == PIPE_ISOCHRONOUS,
+ usb_endpoint_maxp(&ep->desc))
+ / 1000;
+ }
+ }
+ ed->hwINFO = cpu_to_hc32(ohci, info);
+
+ ep->hcpriv = ed;
+ }
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return ed;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request unlinking of an endpoint from an operational HC.
+ * put the ep on the rm_list
+ * real work is done at the next start frame (SF) hardware interrupt
+ * caller guarantees HCD is running, so hardware access is safe,
+ * and that ed->state is ED_OPER
+ */
+static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_DEQUEUE);
+ ed_deschedule (ohci, ed);
+
+ /* rm_list is just singly linked, for simplicity */
+ ed->ed_next = ohci->ed_rm_list;
+ ed->ed_prev = NULL;
+ ohci->ed_rm_list = ed;
+
+ /* enable SOF interrupt */
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
+ // flush those writes, and get latest HCCA contents
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* SF interrupt might get delayed; record the frame counter value that
+ * indicates when the HC isn't looking at it, so concurrent unlinks
+ * behave. frame_no wraps every 2^16 msec, and changes right before
+ * SF is triggered.
+ */
+ ed->tick = ohci_frame_no(ohci) + 1;
+
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (struct ohci_hcd *ohci, u32 info,
+ dma_addr_t data, int len,
+ struct urb *urb, int index)
+{
+ struct td *td, *td_pt;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ int is_iso = info & TD_ISO;
+ int hash;
+
+ // ASSERT (index < urb_priv->length);
+
+ /* aim for only one interrupt per urb. mostly applies to control
+ * and iso; other urbs rarely need more than one TD per urb.
+ * this way, only final tds (or ones with an error) cause IRQs.
+ * at least immediately; use DI=6 in case any control request is
+ * tempted to die part way through. (and to force the hc to flush
+ * its donelist soonish, even on unlink paths.)
+ *
+ * NOTE: could delay interrupts even for the last TD, and get fewer
+ * interrupts ... increasing per-urb latency by sharing interrupts.
+ * Drivers that queue bulk urbs may request that behavior.
+ */
+ if (index != (urb_priv->length - 1)
+ || (urb->transfer_flags & URB_NO_INTERRUPT))
+ info |= TD_DI_SET (6);
+
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = urb_priv->ed->dummy;
+ urb_priv->ed->dummy = td_pt;
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->urb = urb;
+ td->data_dma = data;
+ if (!len)
+ data = 0;
+
+ td->hwINFO = cpu_to_hc32 (ohci, info);
+ if (is_iso) {
+ td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
+ *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
+ (data & 0x0FFF) | 0xE000);
+ } else {
+ td->hwCBP = cpu_to_hc32 (ohci, data);
+ }
+ if (data)
+ td->hwBE = cpu_to_hc32 (ohci, data + len - 1);
+ else
+ td->hwBE = 0;
+ td->hwNextTD = cpu_to_hc32 (ohci, td_pt->td_dma);
+
+ /* append to queue */
+ list_add_tail (&td->td_list, &td->ed->td_list);
+
+ /* hash it for later reverse mapping */
+ hash = TD_HASH_FUNC (td->td_dma);
+ td->td_hash = ohci->td_hash [hash];
+ ohci->td_hash [hash] = td;
+
+ /* HC might read the TD (or cachelines) right away ... */
+ wmb ();
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare all TDs of a transfer, and queue them onto the ED.
+ * Caller guarantees HC is active.
+ * Usually the ED is already on the schedule, so TDs might be
+ * processed as soon as they're queued.
+ */
+static void td_submit_urb (
+ struct ohci_hcd *ohci,
+ struct urb *urb
+) {
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct device *dev = ohci_to_hcd(ohci)->self.controller;
+ dma_addr_t data;
+ int data_len = urb->transfer_buffer_length;
+ int cnt = 0;
+ u32 info = 0;
+ int is_out = usb_pipeout (urb->pipe);
+ int periodic = 0;
+ int i, this_sg_len, n;
+ struct scatterlist *sg;
+
+ /* OHCI handles the bulk/interrupt data toggles itself. We just
+ * use the device toggle bits for resetting, and rely on the fact
+ * that resetting toggle is meaningless if the endpoint is active.
+ */
+ if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+ is_out, 1);
+ urb_priv->ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_C);
+ }
+
+ list_add (&urb_priv->pending, &ohci->pending);
+
+ i = urb->num_mapped_sgs;
+ if (data_len > 0 && i > 0) {
+ sg = urb->sg;
+ data = sg_dma_address(sg);
+
+ /*
+ * urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), data_len);
+ } else {
+ sg = NULL;
+ if (data_len)
+ data = urb->transfer_dma;
+ else
+ data = 0;
+ this_sg_len = data_len;
+ }
+
+ /* NOTE: TD_CC is set so we can tell which TDs the HC processed by
+ * using TD_CC_GET, as well as by seeing them on the done list.
+ * (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)
+ */
+ switch (urb_priv->ed->type) {
+
+ /* Bulk and interrupt are identical except for where in the schedule
+ * their EDs live.
+ */
+ case PIPE_INTERRUPT:
+ /* ... and periodic urbs have extra accounting */
+ periodic = ohci_to_hcd(ohci)->self.bandwidth_int_reqs++ == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0;
+ /* FALLTHROUGH */
+ case PIPE_BULK:
+ info = is_out
+ ? TD_T_TOGGLE | TD_CC | TD_DP_OUT
+ : TD_T_TOGGLE | TD_CC | TD_DP_IN;
+ /* TDs _could_ transfer up to 8K each */
+ for (;;) {
+ n = min(this_sg_len, 4096);
+
+ /* maybe avoid ED halt on final TD short read */
+ if (n >= data_len || (i == 1 && n >= this_sg_len)) {
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ info |= TD_R;
+ }
+ td_fill(ohci, info, data, n, urb, cnt);
+ this_sg_len -= n;
+ data_len -= n;
+ data += n;
+ cnt++;
+
+ if (this_sg_len <= 0) {
+ if (--i <= 0 || data_len <= 0)
+ break;
+ sg = sg_next(sg);
+ data = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg),
+ data_len);
+ }
+ }
+ if ((urb->transfer_flags & URB_ZERO_PACKET)
+ && cnt < urb_priv->length) {
+ td_fill (ohci, info, 0, 0, urb, cnt);
+ cnt++;
+ }
+ /* maybe kickstart bulk list */
+ if (urb_priv->ed->type == PIPE_BULK) {
+ wmb ();
+ ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus);
+ }
+ break;
+
+ /* control manages DATA0/DATA1 toggle per-request; SETUP resets it,
+ * any DATA phase works normally, and the STATUS ack is special.
+ */
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
+ if (data_len > 0) {
+ info = TD_CC | TD_R | TD_T_DATA1;
+ info |= is_out ? TD_DP_OUT : TD_DP_IN;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len, urb, cnt++);
+ }
+ info = (is_out || data_len == 0)
+ ? TD_CC | TD_DP_IN | TD_T_DATA1
+ : TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, urb, cnt++);
+ /* maybe kickstart control list */
+ wmb ();
+ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);
+ break;
+
+ /* ISO has no retransmit, so no toggle; and it uses special TDs.
+ * Each TD could handle multiple consecutive frames (interval 1);
+ * we could often reduce the number of TDs here.
+ */
+ case PIPE_ISOCHRONOUS:
+ for (cnt = urb_priv->td_cnt; cnt < urb->number_of_packets;
+ cnt++) {
+ int frame = urb->start_frame;
+
+ // FIXME scheduling should handle frame counter
+ // roll-around ... exotic case (and OHCI has
+ // a 2^16 iso range, vs other HCs max of 2^10)
+ frame += cnt * urb->interval;
+ frame &= 0xffff;
+ td_fill (ohci, TD_CC | TD_ISO | frame,
+ data + urb->iso_frame_desc [cnt].offset,
+ urb->iso_frame_desc [cnt].length, urb, cnt);
+ }
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
+ if (quirk_amdiso(ohci))
+ usb_amd_quirk_pll_disable();
+ if (quirk_amdprefetch(ohci))
+ sb800_prefetch(dev, 1);
+ }
+ periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0;
+ break;
+ }
+
+ /* start periodic dma if needed */
+ if (periodic) {
+ wmb ();
+ ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ }
+
+ // ASSERT (urb_priv->length == cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate transfer length/status and update the urb */
+static int td_done(struct ohci_hcd *ohci, struct urb *urb, struct td *td)
+{
+ u32 tdINFO = hc32_to_cpup (ohci, &td->hwINFO);
+ int cc = 0;
+ int status = -EINPROGRESS;
+
+ list_del (&td->td_list);
+
+ /* ISO ... drivers see per-TD length/status */
+ if (tdINFO & TD_ISO) {
+ u16 tdPSW = ohci_hwPSW(ohci, td, 0);
+ int dlen = 0;
+
+ /* NOTE: assumes FC in tdINFO == 0, and that
+ * only the first of 0..MAXPSW psws is used.
+ */
+
+ cc = (tdPSW >> 12) & 0xF;
+ if (tdINFO & TD_CC) /* hc didn't touch? */
+ return status;
+
+ if (usb_pipeout (urb->pipe))
+ dlen = urb->iso_frame_desc [td->index].length;
+ else {
+ /* short reads are always OK for ISO */
+ if (cc == TD_DATAUNDERRUN)
+ cc = TD_CC_NOERROR;
+ dlen = tdPSW & 0x3ff;
+ }
+ urb->actual_length += dlen;
+ urb->iso_frame_desc [td->index].actual_length = dlen;
+ urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+
+ if (cc != TD_CC_NOERROR)
+ ohci_dbg(ohci,
+ "urb %p iso td %p (%d) len %d cc %d\n",
+ urb, td, 1 + td->index, dlen, cc);
+
+ /* BULK, INT, CONTROL ... drivers see aggregate length/status,
+ * except that "setup" bytes aren't counted and "short" transfers
+ * might not be reported as errors.
+ */
+ } else {
+ int type = usb_pipetype (urb->pipe);
+ u32 tdBE = hc32_to_cpup (ohci, &td->hwBE);
+
+ cc = TD_CC_GET (tdINFO);
+
+ /* update packet status if needed (short is normally ok) */
+ if (cc == TD_DATAUNDERRUN
+ && !(urb->transfer_flags & URB_SHORT_NOT_OK))
+ cc = TD_CC_NOERROR;
+ if (cc != TD_CC_NOERROR && cc < 0x0E)
+ status = cc_to_error[cc];
+
+ /* count all non-empty packets except control SETUP packet */
+ if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
+ if (td->hwCBP == 0)
+ urb->actual_length += tdBE - td->data_dma + 1;
+ else
+ urb->actual_length +=
+ hc32_to_cpup (ohci, &td->hwCBP)
+ - td->data_dma;
+ }
+
+ if (cc != TD_CC_NOERROR && cc < 0x0E)
+ ohci_dbg(ohci,
+ "urb %p td %p (%d) cc %d, len=%d/%d\n",
+ urb, td, 1 + td->index, cc,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc)
+{
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct ed *ed = td->ed;
+ struct list_head *tmp = td->td_list.next;
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ohci, ED_C);
+
+ /* clear ed halt; this is the td that caused it, but keep it inactive
+ * until its urb->complete() has a chance to clean up.
+ */
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_SKIP);
+ wmb ();
+ ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_H);
+
+ /* Get rid of all later tds from this urb. We don't have
+ * to be careful: no errors and nothing was transferred.
+ * Also patch the ed so it looks as if those tds completed normally.
+ */
+ while (tmp != &ed->td_list) {
+ struct td *next;
+
+ next = list_entry (tmp, struct td, td_list);
+ tmp = next->td_list.next;
+
+ if (next->urb != urb)
+ break;
+
+ /* NOTE: if multi-td control DATA segments get supported,
+ * this urb had one of them, this td wasn't the last td
+ * in that segment (TD_R clear), this ed halted because
+ * of a short read, _and_ URB_SHORT_NOT_OK is clear ...
+ * then we need to leave the control STATUS packet queued
+ * and clear ED_SKIP.
+ */
+
+ list_del(&next->td_list);
+ urb_priv->td_cnt++;
+ ed->hwHeadP = next->hwNextTD | toggle;
+ }
+
+ /* help for troubleshooting: report anything that
+ * looks odd ... that doesn't include protocol stalls
+ * (or maybe some other things)
+ */
+ switch (cc) {
+ case TD_DATAUNDERRUN:
+ if ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)
+ break;
+ /* fallthrough */
+ case TD_CC_STALL:
+ if (usb_pipecontrol (urb->pipe))
+ break;
+ /* fallthrough */
+ default:
+ ohci_dbg (ohci,
+ "urb %p path %s ep%d%s %08x cc %d --> status %d\n",
+ urb, urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ hc32_to_cpu (ohci, td->hwINFO),
+ cc, cc_to_error [cc]);
+ }
+}
+
+/* Add a TD to the done list */
+static void add_to_done_list(struct ohci_hcd *ohci, struct td *td)
+{
+ struct td *td2, *td_prev;
+ struct ed *ed;
+
+ if (td->next_dl_td)
+ return; /* Already on the list */
+
+ /* Add all the TDs going back until we reach one that's on the list */
+ ed = td->ed;
+ td2 = td_prev = td;
+ list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) {
+ if (td2->next_dl_td)
+ break;
+ td2->next_dl_td = td_prev;
+ td_prev = td2;
+ }
+
+ if (ohci->dl_end)
+ ohci->dl_end->next_dl_td = td_prev;
+ else
+ ohci->dl_start = td_prev;
+
+ /*
+ * Make td->next_dl_td point to td itself, to mark the fact
+ * that td is on the done list.
+ */
+ ohci->dl_end = td->next_dl_td = td;
+
+ /* Did we just add the latest pending TD? */
+ td2 = ed->pending_td;
+ if (td2 && td2->next_dl_td)
+ ed->pending_td = NULL;
+}
+
+/* Get the entries on the hardware done queue and put them on our list */
+static void update_done_list(struct ohci_hcd *ohci)
+{
+ u32 td_dma;
+ struct td *td = NULL;
+
+ td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);
+ ohci->hcca->done_head = 0;
+ wmb();
+
+ /* get TD from hc's singly linked list, and
+ * add to ours. ed->td_list changes later.
+ */
+ while (td_dma) {
+ int cc;
+
+ td = dma_to_td (ohci, td_dma);
+ if (!td) {
+ ohci_err (ohci, "bad entry %8x\n", td_dma);
+ break;
+ }
+
+ td->hwINFO |= cpu_to_hc32 (ohci, TD_DONE);
+ cc = TD_CC_GET (hc32_to_cpup (ohci, &td->hwINFO));
+
+ /* Non-iso endpoints can halt on error; un-halt,
+ * and dequeue any other TDs from this urb.
+ * No other TD could have caused the halt.
+ */
+ if (cc != TD_CC_NOERROR
+ && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
+ ed_halted(ohci, td, cc);
+
+ td_dma = hc32_to_cpup (ohci, &td->hwNextTD);
+ add_to_done_list(ohci, td);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
+static void finish_unlinks(struct ohci_hcd *ohci)
+{
+ unsigned tick = ohci_frame_no(ohci);
+ struct ed *ed, **last;
+
+rescan_all:
+ for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
+ struct list_head *entry, *tmp;
+ int completed, modified;
+ __hc32 *prev;
+
+ /* only take off EDs that the HC isn't using, accounting for
+ * frame counter wraps and EDs with partially retired TDs
+ */
+ if (likely(ohci->rh_state == OHCI_RH_RUNNING) &&
+ tick_before(tick, ed->tick)) {
+skip_ed:
+ last = &ed->ed_next;
+ continue;
+ }
+ if (!list_empty(&ed->td_list)) {
+ struct td *td;
+ u32 head;
+
+ td = list_first_entry(&ed->td_list, struct td, td_list);
+
+ /* INTR_WDH may need to clean up first */
+ head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK;
+ if (td->td_dma != head &&
+ ohci->rh_state == OHCI_RH_RUNNING)
+ goto skip_ed;
+
+ /* Don't mess up anything already on the done list */
+ if (td->next_dl_td)
+ goto skip_ed;
+ }
+
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
+ ed->hwNextED = 0;
+ wmb();
+ ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE);
+
+ /* reentrancy: if we drop the schedule lock, someone might
+ * have modified this list. normally it's just prepending
+ * entries (which we'd ignore), but paranoia won't hurt.
+ */
+ modified = 0;
+
+ /* unlink urbs as requested, but rescan the list after
+ * we call a completion since it might have unlinked
+ * another (earlier) urb
+ *
+ * When we get here, the HC doesn't see this ed. But it
+ * must not be rescheduled until all completed URBs have
+ * been given back to the driver.
+ */
+rescan_this:
+ completed = 0;
+ prev = &ed->hwHeadP;
+ list_for_each_safe (entry, tmp, &ed->td_list) {
+ struct td *td;
+ struct urb *urb;
+ urb_priv_t *urb_priv;
+ __hc32 savebits;
+ u32 tdINFO;
+
+ td = list_entry (entry, struct td, td_list);
+ urb = td->urb;
+ urb_priv = td->urb->hcpriv;
+
+ if (!urb->unlinked) {
+ prev = &td->hwNextTD;
+ continue;
+ }
+
+ /* patch pointer hc uses */
+ savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK);
+ *prev = td->hwNextTD | savebits;
+
+ /* If this was unlinked, the TD may not have been
+ * retired ... so manually save the data toggle.
+ * The controller ignores the value we save for
+ * control and ISO endpoints.
+ */
+ tdINFO = hc32_to_cpup(ohci, &td->hwINFO);
+ if ((tdINFO & TD_T) == TD_T_DATA0)
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C);
+ else if ((tdINFO & TD_T) == TD_T_DATA1)
+ ed->hwHeadP |= cpu_to_hc32(ohci, ED_C);
+
+ /* HC may have partly processed this TD */
+ td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* if URB is done, clean up */
+ if (urb_priv->td_cnt >= urb_priv->length) {
+ modified = completed = 1;
+ finish_urb(ohci, urb, 0);
+ }
+ }
+ if (completed && !list_empty (&ed->td_list))
+ goto rescan_this;
+
+ /*
+ * If no TDs are queued, take ED off the ed_rm_list.
+ * Otherwise, if the HC is running, reschedule.
+ * If not, leave it on the list for further dequeues.
+ */
+ if (list_empty(&ed->td_list)) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ ed->state = ED_IDLE;
+ list_del(&ed->in_use_list);
+ } else if (ohci->rh_state == OHCI_RH_RUNNING) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ ed_schedule(ohci, ed);
+ } else {
+ last = &ed->ed_next;
+ }
+
+ if (modified)
+ goto rescan_all;
+ }
+
+ /* maybe reenable control and bulk lists */
+ if (ohci->rh_state == OHCI_RH_RUNNING && !ohci->ed_rm_list) {
+ u32 command = 0, control = 0;
+
+ if (ohci->ed_controltail) {
+ command |= OHCI_CLF;
+ if (quirk_zfmicro(ohci))
+ mdelay(1);
+ if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
+ control |= OHCI_CTRL_CLE;
+ ohci_writel (ohci, 0,
+ &ohci->regs->ed_controlcurrent);
+ }
+ }
+ if (ohci->ed_bulktail) {
+ command |= OHCI_BLF;
+ if (quirk_zfmicro(ohci))
+ mdelay(1);
+ if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
+ control |= OHCI_CTRL_BLE;
+ ohci_writel (ohci, 0,
+ &ohci->regs->ed_bulkcurrent);
+ }
+ }
+
+ /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
+ if (control) {
+ ohci->hc_control |= control;
+ if (quirk_zfmicro(ohci))
+ mdelay(1);
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ if (command) {
+ if (quirk_zfmicro(ohci))
+ mdelay(1);
+ ohci_writel (ohci, command, &ohci->regs->cmdstatus);
+ }
+ }
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Take back a TD from the host controller */
+static void takeback_td(struct ohci_hcd *ohci, struct td *td)
+{
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct ed *ed = td->ed;
+ int status;
+
+ /* update URB's length and status from TD */
+ status = td_done(ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* If all this urb's TDs are done, call complete() */
+ if (urb_priv->td_cnt >= urb_priv->length)
+ finish_urb(ohci, urb, status);
+
+ /* clean schedule: unlink EDs that are no longer busy */
+ if (list_empty(&ed->td_list)) {
+ if (ed->state == ED_OPER)
+ start_ed_unlink(ohci, ed);
+
+ /* ... reenabling halted EDs only after fault cleanup */
+ } else if ((ed->hwINFO & cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE))
+ == cpu_to_hc32(ohci, ED_SKIP)) {
+ td = list_entry(ed->td_list.next, struct td, td_list);
+ if (!(td->hwINFO & cpu_to_hc32(ohci, TD_DONE))) {
+ ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP);
+ /* ... hc may need waking-up */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ohci_writel(ohci, OHCI_CLF,
+ &ohci->regs->cmdstatus);
+ break;
+ case PIPE_BULK:
+ ohci_writel(ohci, OHCI_BLF,
+ &ohci->regs->cmdstatus);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Process normal completions (error or success) and clean the schedules.
+ *
+ * This is the main path for handing urbs back to drivers. The only other
+ * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
+ * instead of scanning the (re-reversed) donelist as this does.
+ */
+static void process_done_list(struct ohci_hcd *ohci)
+{
+ struct td *td;
+
+ while (ohci->dl_start) {
+ td = ohci->dl_start;
+ if (td == ohci->dl_end)
+ ohci->dl_start = ohci->dl_end = NULL;
+ else
+ ohci->dl_start = td->next_dl_td;
+
+ takeback_td(ohci, td);
+ }
+}
+
+/*
+ * TD takeback and URB giveback must be single-threaded.
+ * This routine takes care of it all.
+ */
+static void ohci_work(struct ohci_hcd *ohci)
+{
+ if (ohci->working) {
+ ohci->restart_work = 1;
+ return;
+ }
+ ohci->working = 1;
+
+ restart:
+ process_done_list(ohci);
+ if (ohci->ed_rm_list)
+ finish_unlinks(ohci);
+
+ if (ohci->restart_work) {
+ ohci->restart_work = 0;
+ goto restart;
+ }
+ ohci->working = 0;
+}
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
new file mode 100644
index 000000000..7a1919ca5
--- /dev/null
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -0,0 +1,502 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * USB Bus Glue for Samsung S3C2410
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for S3C2410 from ohci-sa1111.c, ohci-omap.c and ohci-lh7a40.c
+ * by Ben Dooks, <ben@simtec.co.uk>
+ * Copyright (C) 2004 Simtec Electronics
+ *
+ * Thanks to basprog@mail.ru for updates to newer kernels
+ *
+ * This file is licenced under the GPL.
+*/
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb-ohci-s3c2410.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+
+#define valid_port(idx) ((idx) == 1 || (idx) == 2)
+
+/* clock device associated with the hcd */
+
+
+#define DRIVER_DESC "OHCI S3C2410 driver"
+
+static const char hcd_name[] = "ohci-s3c2410";
+
+static struct clk *clk;
+static struct clk *usb_clk;
+
+/* forward definitions */
+
+static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
+
+/* conversion functions */
+
+static struct s3c2410_hcd_info *to_s3c2410_info(struct usb_hcd *hcd)
+{
+ return dev_get_platdata(hcd->self.controller);
+}
+
+static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd)
+{
+ struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
+
+ dev_dbg(&dev->dev, "s3c2410_start_hc:\n");
+
+ clk_prepare_enable(usb_clk);
+ mdelay(2); /* let the bus clock stabilise */
+
+ clk_prepare_enable(clk);
+
+ if (info != NULL) {
+ info->hcd = hcd;
+ info->report_oc = s3c2410_hcd_oc;
+
+ if (info->enable_oc != NULL)
+ (info->enable_oc)(info, 1);
+ }
+}
+
+static void s3c2410_stop_hc(struct platform_device *dev)
+{
+ struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
+
+ dev_dbg(&dev->dev, "s3c2410_stop_hc:\n");
+
+ if (info != NULL) {
+ info->report_oc = NULL;
+ info->hcd = NULL;
+
+ if (info->enable_oc != NULL)
+ (info->enable_oc)(info, 0);
+ }
+
+ clk_disable_unprepare(clk);
+ clk_disable_unprepare(usb_clk);
+}
+
+/* ohci_s3c2410_hub_status_data
+ *
+ * update the status data from the hub with anything that
+ * has been detected by our system
+*/
+
+static int
+ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
+ struct s3c2410_hcd_port *port;
+ int orig;
+ int portno;
+
+ orig = ohci_hub_status_data(hcd, buf);
+
+ if (info == NULL)
+ return orig;
+
+ port = &info->port[0];
+
+ /* mark any changed port as changed */
+
+ for (portno = 0; portno < 2; port++, portno++) {
+ if (port->oc_changed == 1 &&
+ port->flags & S3C_HCDFLG_USED) {
+ dev_dbg(hcd->self.controller,
+ "oc change on port %d\n", portno);
+
+ if (orig < 1)
+ orig = 1;
+
+ buf[0] |= 1<<(portno+1);
+ }
+ }
+
+ return orig;
+}
+
+/* s3c2410_usb_set_power
+ *
+ * configure the power on a port, by calling the platform device
+ * routine registered with the platform device
+*/
+
+static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info,
+ int port, int to)
+{
+ if (info == NULL)
+ return;
+
+ if (info->power_control != NULL) {
+ info->port[port-1].power = to;
+ (info->power_control)(port-1, to);
+ }
+}
+
+/* ohci_s3c2410_hub_control
+ *
+ * look at control requests to the hub, and see if we need
+ * to take any action or over-ride the results from the
+ * request.
+*/
+
+static int ohci_s3c2410_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength)
+{
+ struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
+ struct usb_hub_descriptor *desc;
+ int ret = -EINVAL;
+ u32 *data = (u32 *)buf;
+
+ dev_dbg(hcd->self.controller,
+ "s3c2410_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n",
+ hcd, typeReq, wValue, wIndex, buf, wLength);
+
+ /* if we are only an humble host without any special capabilities
+ * process the request straight away and exit */
+
+ if (info == NULL) {
+ ret = ohci_hub_control(hcd, typeReq, wValue,
+ wIndex, buf, wLength);
+ goto out;
+ }
+
+ /* check the request to see if it needs handling */
+
+ switch (typeReq) {
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER) {
+ dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
+ s3c2410_usb_set_power(info, wIndex, 1);
+ goto out;
+ }
+ break;
+
+ case ClearPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: C_OVER_CURRENT\n");
+
+ if (valid_port(wIndex)) {
+ info->port[wIndex-1].oc_changed = 0;
+ info->port[wIndex-1].oc_status = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: OVER_CURRENT\n");
+
+ if (valid_port(wIndex))
+ info->port[wIndex-1].oc_status = 0;
+
+ goto out;
+
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: POWER\n");
+
+ if (valid_port(wIndex)) {
+ s3c2410_usb_set_power(info, wIndex, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ if (ret)
+ goto out;
+
+ switch (typeReq) {
+ case GetHubDescriptor:
+
+ /* update the hub's descriptor */
+
+ desc = (struct usb_hub_descriptor *)buf;
+
+ if (info->power_control == NULL)
+ return ret;
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ /* remove the old configurations for power-switching, and
+ * over-current protection, and insert our new configuration
+ */
+
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
+ desc->wHubCharacteristics |= cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM);
+
+ if (info->enable_oc) {
+ desc->wHubCharacteristics &= ~cpu_to_le16(
+ HUB_CHAR_OCPM);
+ desc->wHubCharacteristics |= cpu_to_le16(
+ HUB_CHAR_INDV_PORT_OCPM);
+ }
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ return ret;
+
+ case GetPortStatus:
+ /* check port status */
+
+ dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex);
+
+ if (valid_port(wIndex)) {
+ if (info->port[wIndex-1].oc_changed)
+ *data |= cpu_to_le32(RH_PS_OCIC);
+
+ if (info->port[wIndex-1].oc_status)
+ *data |= cpu_to_le32(RH_PS_POCI);
+ }
+ }
+
+ out:
+ return ret;
+}
+
+/* s3c2410_hcd_oc
+ *
+ * handle an over-current report
+*/
+
+static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
+{
+ struct s3c2410_hcd_port *port;
+ struct usb_hcd *hcd;
+ unsigned long flags;
+ int portno;
+
+ if (info == NULL)
+ return;
+
+ port = &info->port[0];
+ hcd = info->hcd;
+
+ local_irq_save(flags);
+
+ for (portno = 0; portno < 2; port++, portno++) {
+ if (port_oc & (1<<portno) &&
+ port->flags & S3C_HCDFLG_USED) {
+ port->oc_status = 1;
+ port->oc_changed = 1;
+
+ /* ok, once over-current is detected,
+ the port needs to be powered down */
+ s3c2410_usb_set_power(info, portno+1, 0);
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/*
+ * usb_hcd_s3c2410_remove - shutdown processing for HCD
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_3c2410_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+*/
+
+static void
+usb_hcd_s3c2410_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ s3c2410_stop_hc(dev);
+ usb_put_hcd(hcd);
+}
+
+/**
+ * usb_hcd_s3c2410_probe - initialize S3C2410-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ struct usb_hcd *hcd = NULL;
+ struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
+ int retval;
+
+ s3c2410_usb_set_power(info, 1, 1);
+ s3c2410_usb_set_power(info, 2, 1);
+
+ hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
+ if (hcd == NULL)
+ return -ENOMEM;
+
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = resource_size(&dev->resource[0]);
+
+ hcd->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err_put;
+ }
+
+ clk = devm_clk_get(&dev->dev, "usb-host");
+ if (IS_ERR(clk)) {
+ dev_err(&dev->dev, "cannot get usb-host clock\n");
+ retval = PTR_ERR(clk);
+ goto err_put;
+ }
+
+ usb_clk = devm_clk_get(&dev->dev, "usb-bus-host");
+ if (IS_ERR(usb_clk)) {
+ dev_err(&dev->dev, "cannot get usb-bus-host clock\n");
+ retval = PTR_ERR(usb_clk);
+ goto err_put;
+ }
+
+ s3c2410_start_hc(dev, hcd);
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start, 0);
+ if (retval != 0)
+ goto err_ioremap;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+ err_ioremap:
+ s3c2410_stop_hc(dev);
+
+ err_put:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver __read_mostly ohci_s3c2410_hc_driver;
+
+static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
+{
+ return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev);
+}
+
+static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_s3c2410_remove(hcd, pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ohci_hcd_s3c2410_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ bool do_wakeup = device_may_wakeup(dev);
+ int rc = 0;
+
+ rc = ohci_suspend(hcd, do_wakeup);
+ if (rc)
+ return rc;
+
+ s3c2410_stop_hc(pdev);
+
+ return rc;
+}
+
+static int ohci_hcd_s3c2410_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+
+ s3c2410_start_hc(pdev, hcd);
+
+ ohci_resume(hcd, false);
+
+ return 0;
+}
+#else
+#define ohci_hcd_s3c2410_drv_suspend NULL
+#define ohci_hcd_s3c2410_drv_resume NULL
+#endif
+
+static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = {
+ .suspend = ohci_hcd_s3c2410_drv_suspend,
+ .resume = ohci_hcd_s3c2410_drv_resume,
+};
+
+static struct platform_driver ohci_hcd_s3c2410_driver = {
+ .probe = ohci_hcd_s3c2410_drv_probe,
+ .remove = ohci_hcd_s3c2410_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "s3c2410-ohci",
+ .pm = &ohci_hcd_s3c2410_pm_ops,
+ },
+};
+
+static int __init ohci_s3c2410_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+ ohci_init_driver(&ohci_s3c2410_hc_driver, NULL);
+
+ /*
+ * The Samsung HW has some unusual quirks, which require
+ * Sumsung-specific workarounds. We override certain hc_driver
+ * functions here to achieve that. We explicitly do not enhance
+ * ohci_driver_overrides to allow this more easily, since this
+ * is an unusual case, and we don't want to encourage others to
+ * override these functions by making it too easy.
+ */
+
+ ohci_s3c2410_hc_driver.hub_status_data = ohci_s3c2410_hub_status_data;
+ ohci_s3c2410_hc_driver.hub_control = ohci_s3c2410_hub_control;
+
+ return platform_driver_register(&ohci_hcd_s3c2410_driver);
+}
+module_init(ohci_s3c2410_init);
+
+static void __exit ohci_s3c2410_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_s3c2410_driver);
+}
+module_exit(ohci_s3c2410_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c2410-ohci");
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
new file mode 100644
index 000000000..2ac266d69
--- /dev/null
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -0,0 +1,265 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * SA1111 Bus Glue
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <mach/assabet.h>
+#include <asm/hardware/sa1111.h>
+
+#ifndef CONFIG_SA1111
+#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
+#endif
+
+#define USB_STATUS 0x0118
+#define USB_RESET 0x011c
+#define USB_IRQTEST 0x0120
+
+#define USB_RESET_FORCEIFRESET (1 << 0)
+#define USB_RESET_FORCEHCRESET (1 << 1)
+#define USB_RESET_CLKGENRESET (1 << 2)
+#define USB_RESET_SIMSCALEDOWN (1 << 3)
+#define USB_RESET_USBINTTEST (1 << 4)
+#define USB_RESET_SLEEPSTBYEN (1 << 5)
+#define USB_RESET_PWRSENSELOW (1 << 6)
+#define USB_RESET_PWRCTRLLOW (1 << 7)
+
+#define USB_STATUS_IRQHCIRMTWKUP (1 << 7)
+#define USB_STATUS_IRQHCIBUFFACC (1 << 8)
+#define USB_STATUS_NIRQHCIM (1 << 9)
+#define USB_STATUS_NHCIMFCLR (1 << 10)
+#define USB_STATUS_USBPWRSENSE (1 << 11)
+
+#if 0
+static void dump_hci_status(struct usb_hcd *hcd, const char *label)
+{
+ unsigned long status = sa1111_readl(hcd->regs + USB_STATUS);
+
+ printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n", label,
+ ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
+ ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
+ ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
+ ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
+ ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
+}
+#endif
+
+static int ohci_sa1111_reset(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci_hcd_init(ohci);
+ return ohci_init(ohci);
+}
+
+static int ohci_sa1111_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ ohci_err(ohci, "can't start\n");
+ ohci_stop(hcd);
+ }
+ return ret;
+}
+
+static const struct hc_driver ohci_sa1111_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SA-1111 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_sa1111_reset,
+ .start = ohci_sa1111_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int sa1111_start_hc(struct sa1111_dev *dev)
+{
+ unsigned int usb_rst = 0;
+ int ret;
+
+ dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n");
+
+ if (machine_is_xp860() ||
+ machine_has_neponset() ||
+ machine_is_pfs168() ||
+ machine_is_badge4())
+ usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
+
+ /*
+ * Configure the power sense and control lines. Place the USB
+ * host controller in reset.
+ */
+ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ dev->mapbase + USB_RESET);
+
+ /*
+ * Now, carefully enable the USB clock, and take
+ * the USB host controller out of reset.
+ */
+ ret = sa1111_enable_device(dev);
+ if (ret == 0) {
+ udelay(11);
+ sa1111_writel(usb_rst, dev->mapbase + USB_RESET);
+ }
+
+ return ret;
+}
+
+static void sa1111_stop_hc(struct sa1111_dev *dev)
+{
+ unsigned int usb_rst;
+
+ dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n");
+
+ /*
+ * Put the USB host controller into reset.
+ */
+ usb_rst = sa1111_readl(dev->mapbase + USB_RESET);
+ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ dev->mapbase + USB_RESET);
+
+ /*
+ * Stop the USB clock.
+ */
+ sa1111_disable_device(dev);
+}
+
+/**
+ * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it.
+ */
+static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
+{
+ struct usb_hcd *hcd;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * We don't call dma_set_mask_and_coherent() here because the
+ * DMA mask has already been appropraitely setup by the core
+ * SA-1111 bus code (which includes bug workarounds.)
+ */
+
+ hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = dev->res.start;
+ hcd->rsrc_len = resource_size(&dev->res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&dev->dev, "request_mem_region failed\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = dev->mapbase;
+
+ ret = sa1111_start_hc(dev);
+ if (ret)
+ goto err2;
+
+ ret = usb_add_hcd(hcd, dev->irq[1], 0);
+ if (ret == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return ret;
+ }
+
+ sa1111_stop_hc(dev);
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+/**
+ * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
+ * @dev: USB Host Controller being removed
+ *
+ * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking
+ * the HCD's stop() method.
+ */
+static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev)
+{
+ struct usb_hcd *hcd = sa1111_get_drvdata(dev);
+
+ usb_remove_hcd(hcd);
+ sa1111_stop_hc(dev);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev)
+{
+ struct usb_hcd *hcd = sa1111_get_drvdata(dev);
+
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ hcd->driver->shutdown(hcd);
+ sa1111_stop_hc(dev);
+ }
+}
+
+static struct sa1111_driver ohci_hcd_sa1111_driver = {
+ .drv = {
+ .name = "sa1111-ohci",
+ .owner = THIS_MODULE,
+ },
+ .devid = SA1111_DEVID_USB,
+ .probe = ohci_hcd_sa1111_probe,
+ .remove = ohci_hcd_sa1111_remove,
+ .shutdown = ohci_hcd_sa1111_shutdown,
+};
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
new file mode 100644
index 000000000..a8b8d8b8d
--- /dev/null
+++ b/drivers/usb/host/ohci-sm501.c
@@ -0,0 +1,271 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2005 David Brownell
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2008 Magnus Damm
+ *
+ * SM501 Bus Glue - based on ohci-omap.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/sm501.h>
+#include <linux/sm501-regs.h>
+
+static int ohci_sm501_init(struct usb_hcd *hcd)
+{
+ return ohci_init(hcd_to_ohci(hcd));
+}
+
+static int ohci_sm501_start(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ int ret;
+
+ ret = ohci_run(hcd_to_ohci(hcd));
+ if (ret < 0) {
+ dev_err(dev, "can't start %s", hcd->self.bus_name);
+ ohci_stop(hcd);
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_sm501_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SM501 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_sm501_init,
+ .start = ohci_sm501_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
+{
+ const struct hc_driver *driver = &ohci_sm501_hc_driver;
+ struct device *dev = &pdev->dev;
+ struct resource *res, *mem;
+ int retval, irq;
+ struct usb_hcd *hcd = NULL;
+
+ irq = retval = platform_get_irq(pdev, 0);
+ if (retval < 0)
+ goto err0;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (mem == NULL) {
+ dev_err(dev, "no resource definition for memory\n");
+ retval = -ENOENT;
+ goto err0;
+ }
+
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(dev, "request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err0;
+ }
+
+ /* The sm501 chip is equipped with local memory that may be used
+ * by on-chip devices such as the video controller and the usb host.
+ * This driver uses dma_declare_coherent_memory() to make sure
+ * usb allocations with dma_alloc_coherent() allocate from
+ * this local memory. The dma_handle returned by dma_alloc_coherent()
+ * will be an offset starting from 0 for the first local memory byte.
+ *
+ * So as long as data is allocated using dma_alloc_coherent() all is
+ * fine. This is however not always the case - buffers may be allocated
+ * using kmalloc() - so the usb core needs to be told that it must copy
+ * data into our local memory if the buffers happen to be placed in
+ * regular memory. The HCD_LOCAL_MEM flag does just that.
+ */
+
+ if (!dma_declare_coherent_memory(dev, mem->start,
+ mem->start - mem->parent->start,
+ resource_size(mem),
+ DMA_MEMORY_MAP |
+ DMA_MEMORY_EXCLUSIVE)) {
+ dev_err(dev, "cannot declare coherent memory\n");
+ retval = -ENXIO;
+ goto err1;
+ }
+
+ /* allocate, reserve and remap resources for registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "no resource definition for registers\n");
+ retval = -ENOENT;
+ goto err2;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, pdev->name)) {
+ dev_err(dev, "request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err3;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_err(dev, "cannot remap registers\n");
+ retval = -ENXIO;
+ goto err4;
+ }
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto err5;
+ device_wakeup_enable(hcd->self.controller);
+
+ /* enable power and unmask interrupts */
+
+ sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1);
+ sm501_modify_reg(dev->parent, SM501_IRQ_MASK, 1 << 6, 0);
+
+ return 0;
+err5:
+ iounmap(hcd->regs);
+err4:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err3:
+ usb_put_hcd(hcd);
+err2:
+ dma_release_declared_memory(dev);
+err1:
+ release_mem_region(mem->start, resource_size(mem));
+err0:
+ return retval;
+}
+
+static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ usb_remove_hcd(hcd);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ dma_release_declared_memory(&pdev->dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (mem)
+ release_mem_region(mem->start, resource_size(mem));
+
+ /* mask interrupts and disable power */
+
+ sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6);
+ sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0);
+ return ret;
+}
+
+static int ohci_sm501_resume(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1);
+ ohci_resume(hcd, false);
+ return 0;
+}
+#else
+#define ohci_sm501_suspend NULL
+#define ohci_sm501_resume NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Driver definition to register with the SM501 bus
+ */
+static struct platform_driver ohci_hcd_sm501_driver = {
+ .probe = ohci_hcd_sm501_drv_probe,
+ .remove = ohci_hcd_sm501_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .suspend = ohci_sm501_suspend,
+ .resume = ohci_sm501_resume,
+ .driver = {
+ .name = "sm501-usb",
+ },
+};
+MODULE_ALIAS("platform:sm501-usb");
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
new file mode 100644
index 000000000..707437c88
--- /dev/null
+++ b/drivers/usb/host/ohci-spear.c
@@ -0,0 +1,203 @@
+/*
+* OHCI HCD (Host Controller Driver) for USB.
+*
+* Copyright (C) 2010 ST Microelectronics.
+* Deepak Sikri<deepak.sikri@st.com>
+*
+* Based on various ohci-*.c drivers
+*
+* This file is licensed under the terms of the GNU General Public
+* License version 2. This program is licensed "as is" without any
+* warranty of any kind, whether express or implied.
+*/
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI SPEAr driver"
+
+static const char hcd_name[] = "SPEAr-ohci";
+struct spear_ohci {
+ struct clk *clk;
+};
+
+#define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
+
+static struct hc_driver __read_mostly ohci_spear_hc_driver;
+
+static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
+{
+ const struct hc_driver *driver = &ohci_spear_hc_driver;
+ struct ohci_hcd *ohci;
+ struct usb_hcd *hcd = NULL;
+ struct clk *usbh_clk;
+ struct spear_ohci *sohci_p;
+ struct resource *res;
+ int retval, irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = irq;
+ goto fail;
+ }
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval)
+ goto fail;
+
+ usbh_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usbh_clk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = PTR_ERR(usbh_clk);
+ goto fail;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err_put_hcd;
+ }
+
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = resource_size(res);
+
+ sohci_p = to_spear_ohci(hcd);
+ sohci_p->clk = usbh_clk;
+
+ clk_prepare_enable(sohci_p->clk);
+
+ ohci = hcd_to_ohci(hcd);
+
+ retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
+ if (retval == 0) {
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+ }
+
+ clk_disable_unprepare(sohci_p->clk);
+err_put_hcd:
+ usb_put_hcd(hcd);
+fail:
+ dev_err(&pdev->dev, "init fail, %d\n", retval);
+
+ return retval;
+}
+
+static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct spear_ohci *sohci_p = to_spear_ohci(hcd);
+
+ usb_remove_hcd(hcd);
+ if (sohci_p->clk)
+ clk_disable_unprepare(sohci_p->clk);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct spear_ohci *sohci_p = to_spear_ohci(hcd);
+ bool do_wakeup = device_may_wakeup(&pdev->dev);
+ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(sohci_p->clk);
+
+ return ret;
+}
+
+static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct spear_ohci *sohci_p = to_spear_ohci(hcd);
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ clk_prepare_enable(sohci_p->clk);
+ ohci_resume(hcd, false);
+ return 0;
+}
+#endif
+
+static const struct of_device_id spear_ohci_id_table[] = {
+ { .compatible = "st,spear600-ohci", },
+ { },
+};
+
+/* Driver definition to register with the platform bus */
+static struct platform_driver spear_ohci_hcd_driver = {
+ .probe = spear_ohci_hcd_drv_probe,
+ .remove = spear_ohci_hcd_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = spear_ohci_hcd_drv_suspend,
+ .resume = spear_ohci_hcd_drv_resume,
+#endif
+ .driver = {
+ .name = "spear-ohci",
+ .of_match_table = spear_ohci_id_table,
+ },
+};
+
+static const struct ohci_driver_overrides spear_overrides __initconst = {
+ .extra_priv_size = sizeof(struct spear_ohci),
+};
+static int __init ohci_spear_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
+ return platform_driver_register(&spear_ohci_hcd_driver);
+}
+module_init(ohci_spear_init);
+
+static void __exit ohci_spear_cleanup(void)
+{
+ platform_driver_unregister(&spear_ohci_hcd_driver);
+}
+module_exit(ohci_spear_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Deepak Sikri");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spear-ohci");
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
new file mode 100644
index 000000000..df9028e0d
--- /dev/null
+++ b/drivers/usb/host/ohci-st.c
@@ -0,0 +1,349 @@
+/*
+ * ST OHCI driver
+ *
+ * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * Derived from ohci-platform.c
+ *
+ * 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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/usb/ohci_pdriver.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
+#define USB_MAX_CLKS 3
+
+struct st_ohci_platform_priv {
+ struct clk *clks[USB_MAX_CLKS];
+ struct clk *clk48;
+ struct reset_control *rst;
+ struct reset_control *pwr;
+ struct phy *phy;
+};
+
+#define DRIVER_DESC "OHCI STMicroelectronics driver"
+
+#define hcd_to_ohci_priv(h) \
+ ((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv)
+
+static const char hcd_name[] = "ohci-st";
+
+static int st_ohci_platform_power_on(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int clk, ret;
+
+ ret = reset_control_deassert(priv->pwr);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ goto err_assert_power;
+
+ /* some SoCs don't have a dedicated 48Mhz clock, but those that do
+ need the rate to be explicitly set */
+ if (priv->clk48) {
+ ret = clk_set_rate(priv->clk48, 48000000);
+ if (ret)
+ goto err_assert_reset;
+ }
+
+ for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) {
+ ret = clk_prepare_enable(priv->clks[clk]);
+ if (ret)
+ goto err_disable_clks;
+ }
+
+ ret = phy_init(priv->phy);
+ if (ret)
+ goto err_disable_clks;
+
+ ret = phy_power_on(priv->phy);
+ if (ret)
+ goto err_exit_phy;
+
+ return 0;
+
+err_exit_phy:
+ phy_exit(priv->phy);
+err_disable_clks:
+ while (--clk >= 0)
+ clk_disable_unprepare(priv->clks[clk]);
+err_assert_reset:
+ reset_control_assert(priv->rst);
+err_assert_power:
+ reset_control_assert(priv->pwr);
+
+ return ret;
+}
+
+static void st_ohci_platform_power_off(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+
+ int clk;
+
+ reset_control_assert(priv->pwr);
+
+ reset_control_assert(priv->rst);
+
+ phy_power_off(priv->phy);
+
+ phy_exit(priv->phy);
+
+ for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--)
+ if (priv->clks[clk])
+ clk_disable_unprepare(priv->clks[clk]);
+}
+
+static struct hc_driver __read_mostly ohci_platform_hc_driver;
+
+static const struct ohci_driver_overrides platform_overrides __initconst = {
+ .product_desc = "ST OHCI controller",
+ .extra_priv_size = sizeof(struct st_ohci_platform_priv),
+};
+
+static struct usb_ohci_pdata ohci_platform_defaults = {
+ .power_on = st_ohci_platform_power_on,
+ .power_suspend = st_ohci_platform_power_off,
+ .power_off = st_ohci_platform_power_off,
+};
+
+static int st_ohci_platform_probe(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_mem;
+ struct usb_ohci_pdata *pdata = &ohci_platform_defaults;
+ struct st_ohci_platform_priv *priv;
+ struct ohci_hcd *ohci;
+ int err, irq, clk = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ dev_err(&dev->dev, "no irq provided");
+ return irq;
+ }
+
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ dev_err(&dev->dev, "no memory resource provided");
+ return -ENXIO;
+ }
+
+ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(dev, hcd);
+ dev->dev.platform_data = pdata;
+ priv = hcd_to_ohci_priv(hcd);
+ ohci = hcd_to_ohci(hcd);
+
+ priv->phy = devm_phy_get(&dev->dev, "usb");
+ if (IS_ERR(priv->phy)) {
+ err = PTR_ERR(priv->phy);
+ goto err_put_hcd;
+ }
+
+ for (clk = 0; clk < USB_MAX_CLKS; clk++) {
+ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+ if (IS_ERR(priv->clks[clk])) {
+ err = PTR_ERR(priv->clks[clk]);
+ if (err == -EPROBE_DEFER)
+ goto err_put_clks;
+ priv->clks[clk] = NULL;
+ break;
+ }
+ }
+
+ /* some SoCs don't have a dedicated 48Mhz clock, but those that
+ do need the rate to be explicitly set */
+ priv->clk48 = devm_clk_get(&dev->dev, "clk48");
+ if (IS_ERR(priv->clk48)) {
+ dev_info(&dev->dev, "48MHz clk not found\n");
+ priv->clk48 = NULL;
+ }
+
+ priv->pwr = devm_reset_control_get_optional(&dev->dev, "power");
+ if (IS_ERR(priv->pwr)) {
+ err = PTR_ERR(priv->pwr);
+ goto err_put_clks;
+ }
+
+ priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset");
+ if (IS_ERR(priv->rst)) {
+ err = PTR_ERR(priv->rst);
+ goto err_put_clks;
+ }
+
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ goto err_power;
+ }
+
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+
+ hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto err_power;
+ }
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_power;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
+err_put_clks:
+ while (--clk >= 0)
+ clk_put(priv->clks[clk]);
+err_put_hcd:
+ if (pdata == &ohci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ usb_put_hcd(hcd);
+
+ return err;
+}
+
+static int st_ohci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int clk;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
+
+ for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++)
+ clk_put(priv->clks[clk]);
+
+ usb_put_hcd(hcd);
+
+ if (pdata == &ohci_platform_defaults)
+ dev->dev.platform_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int st_ohci_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = ohci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ return ret;
+}
+
+static int st_ohci_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ int err;
+
+ if (pdata->power_on) {
+ err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
+
+ ohci_resume(hcd, false);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume);
+
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id st_ohci_platform_ids[] = {
+ { .compatible = "st,st-ohci-300x", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, st_ohci_platform_ids);
+
+static struct platform_driver ohci_platform_driver = {
+ .probe = st_ohci_platform_probe,
+ .remove = st_ohci_platform_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "st-ohci",
+#ifdef CONFIG_PM_SLEEP
+ .pm = &st_ohci_pm_ops,
+#endif
+ .of_match_table = st_ohci_platform_ids,
+ }
+};
+
+static int __init ohci_platform_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides);
+ return platform_driver_register(&ohci_platform_driver);
+}
+module_init(ohci_platform_init);
+
+static void __exit ohci_platform_cleanup(void)
+{
+ platform_driver_unregister(&ohci_platform_driver);
+}
+module_exit(ohci_platform_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c
new file mode 100644
index 000000000..e1b208da4
--- /dev/null
+++ b/drivers/usb/host/ohci-tilegx.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2012 Tilera Corporation. All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * Tilera TILE-Gx USB OHCI host controller driver.
+ */
+
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/usb/tilegx.h>
+#include <linux/usb.h>
+
+#include <asm/homecache.h>
+
+#include <gxio/iorpc_usb_host.h>
+#include <gxio/usb_host.h>
+
+static void tilegx_start_ohc(void)
+{
+}
+
+static void tilegx_stop_ohc(void)
+{
+}
+
+static int tilegx_ohci_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_init(ohci);
+ if (ret < 0)
+ return ret;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ dev_err(hcd->self.controller, "can't start %s\n",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hc_driver ohci_tilegx_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Tile-Gx OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * Generic hardware linkage.
+ */
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11,
+
+ /*
+ * Basic lifecycle operations.
+ */
+ .start = tilegx_ohci_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * Managing I/O requests and associated device resources.
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * Scheduling support.
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * Root hub support.
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ pte_t pte = { 0 };
+ int my_cpu = smp_processor_id();
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Try to initialize our GXIO context; if we can't, the device
+ * doesn't exist.
+ */
+ if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0)
+ return -ENXIO;
+
+ hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto err_hcd;
+ }
+
+ /*
+ * We don't use rsrc_start to map in our registers, but seems like
+ * we ought to set it to something, so we use the register VA.
+ */
+ hcd->rsrc_start =
+ (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx);
+ hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx);
+ hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx);
+
+ tilegx_start_ohc();
+
+ /* Create our IRQs and register them. */
+ pdata->irq = irq_alloc_hwirq(-1);
+ if (!pdata->irq) {
+ ret = -ENXIO;
+ goto err_no_irq;
+ }
+
+ tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU);
+
+ /* Configure interrupts. */
+ ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx,
+ cpu_x(my_cpu), cpu_y(my_cpu),
+ KERNEL_PL, pdata->irq);
+ if (ret) {
+ ret = -ENXIO;
+ goto err_have_irq;
+ }
+
+ /* Register all of our memory. */
+ pte = pte_set_home(pte, PAGE_HOME_HASH);
+ ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0);
+ if (ret) {
+ ret = -ENXIO;
+ goto err_have_irq;
+ }
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED);
+ if (ret == 0) {
+ platform_set_drvdata(pdev, hcd);
+ device_wakeup_enable(hcd->self.controller);
+ return ret;
+ }
+
+err_have_irq:
+ irq_free_hwirq(pdata->irq);
+err_no_irq:
+ tilegx_stop_ohc();
+ usb_put_hcd(hcd);
+err_hcd:
+ gxio_usb_host_destroy(&pdata->usb_ctx);
+ return ret;
+}
+
+static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ tilegx_stop_ohc();
+ gxio_usb_host_destroy(&pdata->usb_ctx);
+ irq_free_hwirq(pdata->irq);
+
+ return 0;
+}
+
+static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev)
+{
+ usb_hcd_platform_shutdown(pdev);
+ ohci_hcd_tilegx_drv_remove(pdev);
+}
+
+static struct platform_driver ohci_hcd_tilegx_driver = {
+ .probe = ohci_hcd_tilegx_drv_probe,
+ .remove = ohci_hcd_tilegx_drv_remove,
+ .shutdown = ohci_hcd_tilegx_drv_shutdown,
+ .driver = {
+ .name = "tilegx-ohci",
+ }
+};
+
+MODULE_ALIAS("platform:tilegx-ohci");
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
new file mode 100644
index 000000000..e9a6eec39
--- /dev/null
+++ b/drivers/usb/host/ohci-tmio.c
@@ -0,0 +1,372 @@
+/*
+ * OHCI HCD(Host Controller Driver) for USB.
+ *
+ *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ *(C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *(C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus glue for Toshiba Mobile IO(TMIO) Controller's OHCI core
+ * (C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com>
+ * (C) Copyright 2007, 2008 Dmitry Baryshkov <dbaryshkov@gmail.com>
+ *
+ * This is known to work with the following variants:
+ * TC6393XB revision 3 (32kB SRAM)
+ *
+ * The TMIO's OHCI core DMAs through a small internal buffer that
+ * is directly addressable by the CPU.
+ *
+ * Written from sparse documentation from Toshiba and Sharp's driver
+ * for the 2.4 kernel,
+ * usb-ohci-tc6393.c(C) Copyright 2004 Lineo Solutions, Inc.
+ *
+ * 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/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/namei.h>
+#include <linux/sched.h>*/
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/dma-mapping.h>
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Configuration Register
+ */
+#define CCR_REVID 0x08 /* b Revision ID */
+#define CCR_BASE 0x10 /* l USB Control Register Base Address Low */
+#define CCR_ILME 0x40 /* b Internal Local Memory Enable */
+#define CCR_PM 0x4c /* w Power Management */
+#define CCR_INTC 0x50 /* b INT Control */
+#define CCR_LMW1L 0x54 /* w Local Memory Window 1 LMADRS Low */
+#define CCR_LMW1H 0x56 /* w Local Memory Window 1 LMADRS High */
+#define CCR_LMW1BL 0x58 /* w Local Memory Window 1 Base Address Low */
+#define CCR_LMW1BH 0x5A /* w Local Memory Window 1 Base Address High */
+#define CCR_LMW2L 0x5C /* w Local Memory Window 2 LMADRS Low */
+#define CCR_LMW2H 0x5E /* w Local Memory Window 2 LMADRS High */
+#define CCR_LMW2BL 0x60 /* w Local Memory Window 2 Base Address Low */
+#define CCR_LMW2BH 0x62 /* w Local Memory Window 2 Base Address High */
+#define CCR_MISC 0xFC /* b MISC */
+
+#define CCR_PM_GKEN 0x0001
+#define CCR_PM_CKRNEN 0x0002
+#define CCR_PM_USBPW1 0x0004
+#define CCR_PM_USBPW2 0x0008
+#define CCR_PM_USBPW3 0x0008
+#define CCR_PM_PMEE 0x0100
+#define CCR_PM_PMES 0x8000
+
+/*-------------------------------------------------------------------------*/
+
+struct tmio_hcd {
+ void __iomem *ccr;
+ spinlock_t lock; /* protects RMW cycles */
+};
+
+#define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1))
+
+/*-------------------------------------------------------------------------*/
+
+static void tmio_write_pm(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ u16 pm;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmio->lock, flags);
+
+ pm = CCR_PM_GKEN | CCR_PM_CKRNEN |
+ CCR_PM_PMEE | CCR_PM_PMES;
+
+ tmio_iowrite16(pm, tmio->ccr + CCR_PM);
+ spin_unlock_irqrestore(&tmio->lock, flags);
+}
+
+static void tmio_stop_hc(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ u16 pm;
+
+ pm = CCR_PM_GKEN | CCR_PM_CKRNEN;
+ switch (ohci->num_ports) {
+ default:
+ dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports);
+ case 3:
+ pm |= CCR_PM_USBPW3;
+ case 2:
+ pm |= CCR_PM_USBPW2;
+ case 1:
+ pm |= CCR_PM_USBPW1;
+ }
+ tmio_iowrite8(0, tmio->ccr + CCR_INTC);
+ tmio_iowrite8(0, tmio->ccr + CCR_ILME);
+ tmio_iowrite16(0, tmio->ccr + CCR_BASE);
+ tmio_iowrite16(0, tmio->ccr + CCR_BASE + 2);
+ tmio_iowrite16(pm, tmio->ccr + CCR_PM);
+}
+
+static void tmio_start_hc(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ unsigned long base = hcd->rsrc_start;
+
+ tmio_write_pm(dev);
+ tmio_iowrite16(base, tmio->ccr + CCR_BASE);
+ tmio_iowrite16(base >> 16, tmio->ccr + CCR_BASE + 2);
+ tmio_iowrite8(1, tmio->ccr + CCR_ILME);
+ tmio_iowrite8(2, tmio->ccr + CCR_INTC);
+
+ dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n",
+ tmio_ioread8(tmio->ccr + CCR_REVID),
+ (u64) hcd->rsrc_start, hcd->irq);
+}
+
+static int ohci_tmio_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ dev_err(hcd->self.controller, "can't start %s\n",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hc_driver ohci_tmio_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "TMIO OHCI USB Host Controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd) + sizeof (struct tmio_hcd),
+
+ /* generic hardware linkage */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM,
+
+ /* basic lifecycle operations */
+ .start = ohci_tmio_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ohci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver ohci_hcd_tmio_driver;
+
+static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct resource *regs = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ struct resource *config = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ struct resource *sram = platform_get_resource(dev, IORESOURCE_MEM, 2);
+ int irq = platform_get_irq(dev, 0);
+ struct tmio_hcd *tmio;
+ struct ohci_hcd *ohci;
+ struct usb_hcd *hcd;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (!cell)
+ return -EINVAL;
+
+ hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto err_usb_create_hcd;
+ }
+
+ hcd->rsrc_start = regs->start;
+ hcd->rsrc_len = resource_size(regs);
+
+ tmio = hcd_to_tmio(hcd);
+
+ spin_lock_init(&tmio->lock);
+
+ tmio->ccr = ioremap(config->start, resource_size(config));
+ if (!tmio->ccr) {
+ ret = -ENOMEM;
+ goto err_ioremap_ccr;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ ret = -ENOMEM;
+ goto err_ioremap_regs;
+ }
+
+ if (!dma_declare_coherent_memory(&dev->dev, sram->start,
+ sram->start,
+ resource_size(sram),
+ DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) {
+ ret = -EBUSY;
+ goto err_dma_declare;
+ }
+
+ if (cell->enable) {
+ ret = cell->enable(dev);
+ if (ret)
+ goto err_enable;
+ }
+
+ tmio_start_hc(dev);
+ ohci = hcd_to_ohci(hcd);
+ ohci_hcd_init(ohci);
+
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret)
+ goto err_add_hcd;
+
+ device_wakeup_enable(hcd->self.controller);
+ if (ret == 0)
+ return ret;
+
+ usb_remove_hcd(hcd);
+
+err_add_hcd:
+ tmio_stop_hc(dev);
+ if (cell->disable)
+ cell->disable(dev);
+err_enable:
+ dma_release_declared_memory(&dev->dev);
+err_dma_declare:
+ iounmap(hcd->regs);
+err_ioremap_regs:
+ iounmap(tmio->ccr);
+err_ioremap_ccr:
+ usb_put_hcd(hcd);
+err_usb_create_hcd:
+
+ return ret;
+}
+
+static int ohci_hcd_tmio_drv_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ const struct mfd_cell *cell = mfd_get_cell(dev);
+
+ usb_remove_hcd(hcd);
+ tmio_stop_hc(dev);
+ if (cell->disable)
+ cell->disable(dev);
+ dma_release_declared_memory(&dev->dev);
+ iounmap(hcd->regs);
+ iounmap(tmio->ccr);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state)
+{
+ const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ unsigned long flags;
+ u8 misc;
+ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ spin_lock_irqsave(&tmio->lock, flags);
+
+ misc = tmio_ioread8(tmio->ccr + CCR_MISC);
+ misc |= 1 << 3; /* USSUSP */
+ tmio_iowrite8(misc, tmio->ccr + CCR_MISC);
+
+ spin_unlock_irqrestore(&tmio->lock, flags);
+
+ if (cell->suspend) {
+ ret = cell->suspend(dev);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int ohci_hcd_tmio_drv_resume(struct platform_device *dev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ unsigned long flags;
+ u8 misc;
+ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ if (cell->resume) {
+ ret = cell->resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ tmio_start_hc(dev);
+
+ spin_lock_irqsave(&tmio->lock, flags);
+
+ misc = tmio_ioread8(tmio->ccr + CCR_MISC);
+ misc &= ~(1 << 3); /* USSUSP */
+ tmio_iowrite8(misc, tmio->ccr + CCR_MISC);
+
+ spin_unlock_irqrestore(&tmio->lock, flags);
+
+ ohci_resume(hcd, false);
+
+ return 0;
+}
+#else
+#define ohci_hcd_tmio_drv_suspend NULL
+#define ohci_hcd_tmio_drv_resume NULL
+#endif
+
+static struct platform_driver ohci_hcd_tmio_driver = {
+ .probe = ohci_hcd_tmio_drv_probe,
+ .remove = ohci_hcd_tmio_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .suspend = ohci_hcd_tmio_drv_suspend,
+ .resume = ohci_hcd_tmio_drv_resume,
+ .driver = {
+ .name = "tmio-ohci",
+ },
+};
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
new file mode 100644
index 000000000..bc462288c
--- /dev/null
+++ b/drivers/usb/host/ohci.h
@@ -0,0 +1,744 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
+ * host controller implementation.
+ */
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+
+/*
+ * OHCI Endpoint Descriptor (ED) ... holds TD queue
+ * See OHCI spec, section 4.2
+ *
+ * This is a "Queue Head" for those transfers, which is why
+ * both EHCI and UHCI call similar structures a "QH".
+ */
+struct ed {
+ /* first fields are hardware-specified */
+ __hc32 hwINFO; /* endpoint config bitmap */
+ /* info bits defined by hcd */
+#define ED_DEQUEUE (1 << 27)
+ /* info bits defined by the hardware */
+#define ED_ISO (1 << 15)
+#define ED_SKIP (1 << 14)
+#define ED_LOWSPEED (1 << 13)
+#define ED_OUT (0x01 << 11)
+#define ED_IN (0x02 << 11)
+ __hc32 hwTailP; /* tail of TD list */
+ __hc32 hwHeadP; /* head of TD list (hc r/w) */
+#define ED_C (0x02) /* toggle carry */
+#define ED_H (0x01) /* halted */
+ __hc32 hwNextED; /* next ED in list */
+
+ /* rest are purely for the driver's use */
+ dma_addr_t dma; /* addr of ED */
+ struct td *dummy; /* next TD to activate */
+
+ /* host's view of schedule */
+ struct ed *ed_next; /* on schedule or rm_list */
+ struct ed *ed_prev; /* for non-interrupt EDs */
+ struct list_head td_list; /* "shadow list" of our TDs */
+ struct list_head in_use_list;
+
+ /* create --> IDLE --> OPER --> ... --> IDLE --> destroy
+ * usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
+ */
+ u8 state; /* ED_{IDLE,UNLINK,OPER} */
+#define ED_IDLE 0x00 /* NOT linked to HC */
+#define ED_UNLINK 0x01 /* being unlinked from hc */
+#define ED_OPER 0x02 /* IS linked to hc */
+
+ u8 type; /* PIPE_{BULK,...} */
+
+ /* periodic scheduling params (for intr and iso) */
+ u8 branch;
+ u16 interval;
+ u16 load;
+ u16 last_iso; /* iso only */
+
+ /* HC may see EDs on rm_list until next frame (frame_no == tick) */
+ u16 tick;
+
+ /* Detect TDs not added to the done queue */
+ unsigned takeback_wdh_cnt;
+ struct td *pending_td;
+#define OKAY_TO_TAKEBACK(ohci, ed) \
+ ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0)
+
+} __attribute__ ((aligned(16)));
+
+#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
+
+
+/*
+ * OHCI Transfer Descriptor (TD) ... one per transfer segment
+ * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
+ * and 4.3.2 (iso)
+ */
+struct td {
+ /* first fields are hardware-specified */
+ __hc32 hwINFO; /* transfer info bitmask */
+
+ /* hwINFO bits for both general and iso tds: */
+#define TD_CC 0xf0000000 /* condition code */
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_DI 0x00E00000 /* frames before interrupt */
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+ /* these two bits are available for definition/use by HCDs in both
+ * general and iso tds ... others are available for only one type
+ */
+#define TD_DONE 0x00020000 /* retired to donelist */
+#define TD_ISO 0x00010000 /* copy of ED_ISO */
+
+ /* hwINFO bits for general tds: */
+#define TD_EC 0x0C000000 /* error count */
+#define TD_T 0x03000000 /* data toggle state */
+#define TD_T_DATA0 0x02000000 /* DATA0 */
+#define TD_T_DATA1 0x03000000 /* DATA1 */
+#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
+#define TD_DP 0x00180000 /* direction/pid */
+#define TD_DP_SETUP 0x00000000 /* SETUP pid */
+#define TD_DP_IN 0x00100000 /* IN pid */
+#define TD_DP_OUT 0x00080000 /* OUT pid */
+ /* 0x00180000 rsvd */
+#define TD_R 0x00040000 /* round: short packets OK? */
+
+ /* (no hwINFO #defines yet for iso tds) */
+
+ __hc32 hwCBP; /* Current Buffer Pointer (or 0) */
+ __hc32 hwNextTD; /* Next TD Pointer */
+ __hc32 hwBE; /* Memory Buffer End Pointer */
+
+ /* PSW is only for ISO. Only 1 PSW entry is used, but on
+ * big-endian PPC hardware that's the second entry.
+ */
+#define MAXPSW 2
+ __hc16 hwPSW [MAXPSW];
+
+ /* rest are purely for the driver's use */
+ __u8 index;
+ struct ed *ed;
+ struct td *td_hash; /* dma-->td hashtable */
+ struct td *next_dl_td;
+ struct urb *urb;
+
+ dma_addr_t td_dma; /* addr of this TD */
+ dma_addr_t data_dma; /* addr of data it points to */
+
+ struct list_head td_list; /* "shadow list", TDs on same ED */
+} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
+
+#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
+
+/*
+ * Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
+ */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+
+/* map OHCI TD status codes (CC) to errno values */
+static const int cc_to_error [16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIME,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined section 4.4.1 of the OHCI spec. The HC is
+ * told the base address of it. It must be 256-byte aligned.
+ */
+struct ohci_hcca {
+#define NUM_INTS 32
+ __hc32 int_table [NUM_INTS]; /* periodic schedule */
+
+ /*
+ * OHCI defines u16 frame_no, followed by u16 zero pad.
+ * Since some processors can't do 16 bit bus accesses,
+ * portable access must be a 32 bits wide.
+ */
+ __hc32 frame_no; /* current frame number */
+ __hc32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc [116];
+ u8 what [4]; /* spec only identifies 252 bytes :) */
+} __attribute__ ((aligned(256)));
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O region.
+ * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
+ * Layout is in section 7 (and appendix B) of the spec.
+ */
+struct ohci_regs {
+ /* control and status registers (section 7.1) */
+ __hc32 revision;
+ __hc32 control;
+ __hc32 cmdstatus;
+ __hc32 intrstatus;
+ __hc32 intrenable;
+ __hc32 intrdisable;
+
+ /* memory pointers (section 7.2) */
+ __hc32 hcca;
+ __hc32 ed_periodcurrent;
+ __hc32 ed_controlhead;
+ __hc32 ed_controlcurrent;
+ __hc32 ed_bulkhead;
+ __hc32 ed_bulkcurrent;
+ __hc32 donehead;
+
+ /* frame counters (section 7.3) */
+ __hc32 fminterval;
+ __hc32 fmremaining;
+ __hc32 fmnumber;
+ __hc32 periodicstart;
+ __hc32 lsthresh;
+
+ /* Root hub ports (section 7.4) */
+ struct ohci_roothub_regs {
+ __hc32 a;
+ __hc32 b;
+ __hc32 status;
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
+ __hc32 portstatus [MAX_ROOT_PORTS];
+ } roothub;
+
+ /* and optional "legacy support" registers (appendix B) at 0x0100 */
+
+} __attribute__ ((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+
+/* hcd-private per-urb state */
+typedef struct urb_priv {
+ struct ed *ed;
+ u16 length; // # tds in this request
+ u16 td_cnt; // tds already serviced
+ struct list_head pending;
+ struct td *td [0]; // all TDs in this request
+
+} urb_priv_t;
+
+#define TD_HASH_SIZE 64 /* power'o'two */
+// sizeof (struct td) ~= 64 == 2^6 ...
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
+
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+enum ohci_rh_state {
+ OHCI_RH_HALTED,
+ OHCI_RH_SUSPENDED,
+ OHCI_RH_RUNNING
+};
+
+struct ohci_hcd {
+ spinlock_t lock;
+
+ /*
+ * I/O memory used to communicate with the HC (dma-consistent)
+ */
+ struct ohci_regs __iomem *regs;
+
+ /*
+ * main memory used to communicate with the HC (dma-consistent).
+ * hcd adds to schedule for a live hc any time, but removals finish
+ * only at the start of the next frame.
+ */
+ struct ohci_hcca *hcca;
+ dma_addr_t hcca_dma;
+
+ struct ed *ed_rm_list; /* to be removed */
+
+ struct ed *ed_bulktail; /* last in bulk list */
+ struct ed *ed_controltail; /* last in ctrl list */
+ struct ed *periodic [NUM_INTS]; /* shadow int_table */
+
+ void (*start_hnp)(struct ohci_hcd *ohci);
+
+ /*
+ * memory management for queue data structures
+ */
+ struct dma_pool *td_cache;
+ struct dma_pool *ed_cache;
+ struct td *td_hash [TD_HASH_SIZE];
+ struct td *dl_start, *dl_end; /* the done list */
+ struct list_head pending;
+ struct list_head eds_in_use; /* all EDs with at least 1 TD */
+
+ /*
+ * driver state
+ */
+ enum ohci_rh_state rh_state;
+ int num_ports;
+ int load [NUM_INTS];
+ u32 hc_control; /* copy of hc control reg */
+ unsigned long next_statechange; /* suspend/resume */
+ u32 fminterval; /* saved register */
+ unsigned autostop:1; /* rh auto stopping/stopped */
+ unsigned working:1;
+ unsigned restart_work:1;
+
+ unsigned long flags; /* for HC bugs */
+#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
+#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
+#define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */
+#define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */
+#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
+#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
+#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
+#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
+#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
+#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
+#define OHCI_QUIRK_GLOBAL_SUSPEND 0x800 /* must suspend ports */
+
+ // there are also chip quirks/bugs in init logic
+
+ unsigned prev_frame_no;
+ unsigned wdh_cnt, prev_wdh_cnt;
+ u32 prev_donehead;
+ struct timer_list io_watchdog;
+
+ struct work_struct nec_work; /* Worker for NEC quirk */
+
+ struct dentry *debug_dir;
+ struct dentry *debug_async;
+ struct dentry *debug_periodic;
+ struct dentry *debug_registers;
+
+ /* platform-specific data -- must come last */
+ unsigned long priv[0] __aligned(sizeof(s64));
+
+};
+
+#ifdef CONFIG_PCI
+static inline int quirk_nec(struct ohci_hcd *ohci)
+{
+ return ohci->flags & OHCI_QUIRK_NEC;
+}
+static inline int quirk_zfmicro(struct ohci_hcd *ohci)
+{
+ return ohci->flags & OHCI_QUIRK_ZFMICRO;
+}
+static inline int quirk_amdiso(struct ohci_hcd *ohci)
+{
+ return ohci->flags & OHCI_QUIRK_AMD_PLL;
+}
+static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
+{
+ return ohci->flags & OHCI_QUIRK_AMD_PREFETCH;
+}
+#else
+static inline int quirk_nec(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+static inline int quirk_zfmicro(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+static inline int quirk_amdiso(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+#endif
+
+/* convert between an hcd pointer and the corresponding ohci_hcd */
+static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
+{
+ return (struct ohci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
+{
+ return container_of ((void *) ohci, struct usb_hcd, hcd_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define ohci_dbg(ohci, fmt, args...) \
+ dev_dbg (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_err(ohci, fmt, args...) \
+ dev_err (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_info(ohci, fmt, args...) \
+ dev_info (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_warn(ohci, fmt, args...) \
+ dev_warn (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * While most USB host controllers implement their registers and
+ * in-memory communication descriptors in little-endian format,
+ * a minority (notably the IBM STB04XXX and the Motorola MPC5200
+ * processors) implement them in big endian format.
+ *
+ * In addition some more exotic implementations like the Toshiba
+ * Spider (aka SCC) cell southbridge are "mixed" endian, that is,
+ * they have a different endianness for registers vs. in-memory
+ * descriptors.
+ *
+ * This attempts to support either format at compile time without a
+ * runtime penalty, or both formats with the additional overhead
+ * of checking a flag bit.
+ *
+ * That leads to some tricky Kconfig rules howevber. There are
+ * different defaults based on some arch/ppc platforms, though
+ * the basic rules are:
+ *
+ * Controller type Kconfig options needed
+ * --------------- ----------------------
+ * little endian CONFIG_USB_OHCI_LITTLE_ENDIAN
+ *
+ * fully big endian CONFIG_USB_OHCI_BIG_ENDIAN_DESC _and_
+ * CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ *
+ * mixed endian CONFIG_USB_OHCI_LITTLE_ENDIAN _and_
+ * CONFIG_USB_OHCI_BIG_ENDIAN_{MMIO,DESC}
+ *
+ * (If you have a mixed endian controller, you -must- also define
+ * CONFIG_USB_OHCI_LITTLE_ENDIAN or things will not work when building
+ * both your mixed endian and a fully big endian controller support in
+ * the same kernel image).
+ */
+
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
+#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
+#define big_endian_desc(ohci) (ohci->flags & OHCI_QUIRK_BE_DESC)
+#else
+#define big_endian_desc(ohci) 1 /* only big endian */
+#endif
+#else
+#define big_endian_desc(ohci) 0 /* only little endian */
+#endif
+
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
+#define big_endian_mmio(ohci) (ohci->flags & OHCI_QUIRK_BE_MMIO)
+#else
+#define big_endian_mmio(ohci) 1 /* only big endian */
+#endif
+#else
+#define big_endian_mmio(ohci) 0 /* only little endian */
+#endif
+
+/*
+ * Big-endian read/write functions are arch-specific.
+ * Other arches can be added if/when they're needed.
+ *
+ */
+static inline unsigned int _ohci_readl (const struct ohci_hcd *ohci,
+ __hc32 __iomem * regs)
+{
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ return big_endian_mmio(ohci) ?
+ readl_be (regs) :
+ readl (regs);
+#else
+ return readl (regs);
+#endif
+}
+
+static inline void _ohci_writel (const struct ohci_hcd *ohci,
+ const unsigned int val, __hc32 __iomem *regs)
+{
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ big_endian_mmio(ohci) ?
+ writel_be (val, regs) :
+ writel (val, regs);
+#else
+ writel (val, regs);
+#endif
+}
+
+#define ohci_readl(o,r) _ohci_readl(o,r)
+#define ohci_writel(o,v,r) _ohci_writel(o,v,r)
+
+
+/*-------------------------------------------------------------------------*/
+
+/* cpu to ohci */
+static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x)
+{
+ return big_endian_desc(ohci) ?
+ (__force __hc16)cpu_to_be16(x) :
+ (__force __hc16)cpu_to_le16(x);
+}
+
+static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x)
+{
+ return big_endian_desc(ohci) ?
+ cpu_to_be16p(x) :
+ cpu_to_le16p(x);
+}
+
+static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x)
+{
+ return big_endian_desc(ohci) ?
+ (__force __hc32)cpu_to_be32(x) :
+ (__force __hc32)cpu_to_le32(x);
+}
+
+static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x)
+{
+ return big_endian_desc(ohci) ?
+ cpu_to_be32p(x) :
+ cpu_to_le32p(x);
+}
+
+/* ohci to cpu */
+static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x)
+{
+ return big_endian_desc(ohci) ?
+ be16_to_cpu((__force __be16)x) :
+ le16_to_cpu((__force __le16)x);
+}
+
+static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x)
+{
+ return big_endian_desc(ohci) ?
+ be16_to_cpup((__force __be16 *)x) :
+ le16_to_cpup((__force __le16 *)x);
+}
+
+static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x)
+{
+ return big_endian_desc(ohci) ?
+ be32_to_cpu((__force __be32)x) :
+ le32_to_cpu((__force __le32)x);
+}
+
+static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
+{
+ return big_endian_desc(ohci) ?
+ be32_to_cpup((__force __be32 *)x) :
+ le32_to_cpup((__force __le32 *)x);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The HCCA frame number is 16 bits, but is accessed as 32 bits since not all
+ * hardware handles 16 bit reads. Depending on the SoC implementation, the
+ * frame number can wind up in either bits [31:16] (default) or
+ * [15:0] (OHCI_QUIRK_FRAME_NO) on big endian hosts.
+ *
+ * Somewhat similarly, the 16-bit PSW fields in a transfer descriptor are
+ * reordered on BE.
+ */
+
+static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+ u32 tmp;
+ if (big_endian_desc(ohci)) {
+ tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
+ if (!(ohci->flags & OHCI_QUIRK_FRAME_NO))
+ tmp >>= 16;
+ } else
+ tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
+
+ return (u16)tmp;
+}
+
+static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return (__hc16 *)(big_endian_desc(ohci) ?
+ &td->hwPSW[index ^ 1] : &td->hwPSW[index]);
+}
+
+static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define FI 0x2edf /* 12000 bits per frame (-1) */
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
+#define FIT (1 << 31)
+#define LSTHRESH 0x628 /* lowspeed bit threshold */
+
+static inline void periodic_reinit (struct ohci_hcd *ohci)
+{
+ u32 fi = ohci->fminterval & 0x03fff;
+ u32 fit = ohci_readl(ohci, &ohci->regs->fminterval) & FIT;
+
+ ohci_writel (ohci, (fit ^ FIT) | ohci->fminterval,
+ &ohci->regs->fminterval);
+ ohci_writel (ohci, ((9 * fi) / 10) & 0x3fff,
+ &ohci->regs->periodicstart);
+}
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = ohci_readl (hc, &hc->regs->roothub.register); \
+ if (temp == -1) \
+ hc->rh_state = OHCI_RH_HALTED; \
+ else if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = ohci_readl (hc, &hc->regs->roothub.register); \
+ temp; })
+
+static inline u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return ohci_readl (hc, &hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return ohci_readl (hc, &hc->regs->roothub.status); }
+static inline u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+/* Declarations of things exported for use by ohci platform drivers */
+
+struct ohci_driver_overrides {
+ const char *product_desc;
+ size_t extra_priv_size;
+ int (*reset)(struct usb_hcd *hcd);
+};
+
+extern void ohci_init_driver(struct hc_driver *drv,
+ const struct ohci_driver_overrides *over);
+extern int ohci_restart(struct ohci_hcd *ohci);
+extern int ohci_setup(struct usb_hcd *hcd);
+#ifdef CONFIG_PM
+extern int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup);
+extern int ohci_resume(struct usb_hcd *hcd, bool hibernated);
+#endif
+extern int ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength);
+extern int ohci_hub_status_data(struct usb_hcd *hcd, char *buf);
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
new file mode 100644
index 000000000..6352f54e6
--- /dev/null
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -0,0 +1,3928 @@
+/*
+ * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (c) 2008 Eurotech S.p.A. <info@eurtech.it>
+ *
+ * This code is *strongly* based on EHCI-HCD code by David Brownell since
+ * the chip is a quasi-EHCI compatible.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+
+#include "oxu210hp.h"
+
+#define DRIVER_VERSION "0.0.50"
+
+/*
+ * Main defines
+ */
+
+#define oxu_dbg(oxu, fmt, args...) \
+ dev_dbg(oxu_to_hcd(oxu)->self.controller , fmt , ## args)
+#define oxu_err(oxu, fmt, args...) \
+ dev_err(oxu_to_hcd(oxu)->self.controller , fmt , ## args)
+#define oxu_info(oxu, fmt, args...) \
+ dev_info(oxu_to_hcd(oxu)->self.controller , fmt , ## args)
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define DEBUG
+#endif
+
+static inline struct usb_hcd *oxu_to_hcd(struct oxu_hcd *oxu)
+{
+ return container_of((void *) oxu, struct usb_hcd, hcd_priv);
+}
+
+static inline struct oxu_hcd *hcd_to_oxu(struct usb_hcd *hcd)
+{
+ return (struct oxu_hcd *) (hcd->hcd_priv);
+}
+
+/*
+ * Debug stuff
+ */
+
+#undef OXU_URB_TRACE
+#undef OXU_VERBOSE_DEBUG
+
+#ifdef OXU_VERBOSE_DEBUG
+#define oxu_vdbg oxu_dbg
+#else
+#define oxu_vdbg(oxu, fmt, args...) /* Nop */
+#endif
+
+#ifdef DEBUG
+
+static int __attribute__((__unused__))
+dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
+{
+ return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ label, label[0] ? " " : "", status,
+ (status & STS_ASS) ? " Async" : "",
+ (status & STS_PSS) ? " Periodic" : "",
+ (status & STS_RECL) ? " Recl" : "",
+ (status & STS_HALT) ? " Halt" : "",
+ (status & STS_IAA) ? " IAA" : "",
+ (status & STS_FATAL) ? " FATAL" : "",
+ (status & STS_FLR) ? " FLR" : "",
+ (status & STS_PCD) ? " PCD" : "",
+ (status & STS_ERR) ? " ERR" : "",
+ (status & STS_INT) ? " INT" : ""
+ );
+}
+
+static int __attribute__((__unused__))
+dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
+{
+ return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s",
+ label, label[0] ? " " : "", enable,
+ (enable & STS_IAA) ? " IAA" : "",
+ (enable & STS_FATAL) ? " FATAL" : "",
+ (enable & STS_FLR) ? " FLR" : "",
+ (enable & STS_PCD) ? " PCD" : "",
+ (enable & STS_ERR) ? " ERR" : "",
+ (enable & STS_INT) ? " INT" : ""
+ );
+}
+
+static const char *const fls_strings[] =
+ { "1024", "512", "256", "??" };
+
+static int dbg_command_buf(char *buf, unsigned len,
+ const char *label, u32 command)
+{
+ return scnprintf(buf, len,
+ "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
+ label, label[0] ? " " : "", command,
+ (command & CMD_PARK) ? "park" : "(park)",
+ CMD_PARK_CNT(command),
+ (command >> 16) & 0x3f,
+ (command & CMD_LRESET) ? " LReset" : "",
+ (command & CMD_IAAD) ? " IAAD" : "",
+ (command & CMD_ASE) ? " Async" : "",
+ (command & CMD_PSE) ? " Periodic" : "",
+ fls_strings[(command >> 2) & 0x3],
+ (command & CMD_RESET) ? " Reset" : "",
+ (command & CMD_RUN) ? "RUN" : "HALT"
+ );
+}
+
+static int dbg_port_buf(char *buf, unsigned len, const char *label,
+ int port, u32 status)
+{
+ char *sig;
+
+ /* signaling state */
+ switch (status & (3 << 10)) {
+ case 0 << 10:
+ sig = "se0";
+ break;
+ case 1 << 10:
+ sig = "k"; /* low speed */
+ break;
+ case 2 << 10:
+ sig = "j";
+ break;
+ default:
+ sig = "?";
+ break;
+ }
+
+ return scnprintf(buf, len,
+ "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s",
+ label, label[0] ? " " : "", port, status,
+ (status & PORT_POWER) ? " POWER" : "",
+ (status & PORT_OWNER) ? " OWNER" : "",
+ sig,
+ (status & PORT_RESET) ? " RESET" : "",
+ (status & PORT_SUSPEND) ? " SUSPEND" : "",
+ (status & PORT_RESUME) ? " RESUME" : "",
+ (status & PORT_OCC) ? " OCC" : "",
+ (status & PORT_OC) ? " OC" : "",
+ (status & PORT_PEC) ? " PEC" : "",
+ (status & PORT_PE) ? " PE" : "",
+ (status & PORT_CSC) ? " CSC" : "",
+ (status & PORT_CONNECT) ? " CONNECT" : ""
+ );
+}
+
+#else
+
+static inline int __attribute__((__unused__))
+dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
+{ return 0; }
+
+#endif /* DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(oxu, label, status) { \
+ char _buf[80]; \
+ dbg_status_buf(_buf, sizeof _buf, label, status); \
+ oxu_dbg(oxu, "%s\n", _buf); \
+}
+
+#define dbg_cmd(oxu, label, command) { \
+ char _buf[80]; \
+ dbg_command_buf(_buf, sizeof _buf, label, command); \
+ oxu_dbg(oxu, "%s\n", _buf); \
+}
+
+#define dbg_port(oxu, label, port, status) { \
+ char _buf[80]; \
+ dbg_port_buf(_buf, sizeof _buf, label, port, status); \
+ oxu_dbg(oxu, "%s\n", _buf); \
+}
+
+/*
+ * Module parameters
+ */
+
+/* Initial IRQ latency: faster than hw default */
+static int log2_irq_thresh; /* 0 to 6 */
+module_param(log2_irq_thresh, int, S_IRUGO);
+MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* Initial park setting: slower than hw default */
+static unsigned park;
+module_param(park, uint, S_IRUGO);
+MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets");
+
+/* For flakey hardware, ignore overcurrent indicators */
+static bool ignore_oc;
+module_param(ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC(ignore_oc, "ignore bogus hardware overcurrent indications");
+
+
+static void ehci_work(struct oxu_hcd *oxu);
+static int oxu_hub_control(struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+
+/*
+ * Local functions
+ */
+
+/* Low level read/write registers functions */
+static inline u32 oxu_readl(void *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void oxu_writel(void *base, u32 reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+static inline void timer_action_done(struct oxu_hcd *oxu,
+ enum ehci_timer_action action)
+{
+ clear_bit(action, &oxu->actions);
+}
+
+static inline void timer_action(struct oxu_hcd *oxu,
+ enum ehci_timer_action action)
+{
+ if (!test_and_set_bit(action, &oxu->actions)) {
+ unsigned long t;
+
+ switch (action) {
+ case TIMER_IAA_WATCHDOG:
+ t = EHCI_IAA_JIFFIES;
+ break;
+ case TIMER_IO_WATCHDOG:
+ t = EHCI_IO_JIFFIES;
+ break;
+ case TIMER_ASYNC_OFF:
+ t = EHCI_ASYNC_JIFFIES;
+ break;
+ case TIMER_ASYNC_SHRINK:
+ default:
+ t = EHCI_SHRINK_JIFFIES;
+ break;
+ }
+ t += jiffies;
+ /* all timings except IAA watchdog can be overridden.
+ * async queue SHRINK often precedes IAA. while it's ready
+ * to go OFF neither can matter, and afterwards the IO
+ * watchdog stops unless there's still periodic traffic.
+ */
+ if (action != TIMER_IAA_WATCHDOG
+ && t > oxu->watchdog.expires
+ && timer_pending(&oxu->watchdog))
+ return;
+ mod_timer(&oxu->watchdog, t);
+ }
+}
+
+/*
+ * 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).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown: shutting down the bridge before the devices using it.
+ */
+static int handshake(struct oxu_hcd *oxu, void __iomem *ptr,
+ u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = readl(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 to halt state from unknown (EHCI spec section 2.3) */
+static int ehci_halt(struct oxu_hcd *oxu)
+{
+ u32 temp = readl(&oxu->regs->status);
+
+ /* disable any irqs left enabled by previous code */
+ writel(0, &oxu->regs->intr_enable);
+
+ if ((temp & STS_HALT) != 0)
+ return 0;
+
+ temp = readl(&oxu->regs->command);
+ temp &= ~CMD_RUN;
+ writel(temp, &oxu->regs->command);
+ return handshake(oxu, &oxu->regs->status,
+ STS_HALT, STS_HALT, 16 * 125);
+}
+
+/* Put TDI/ARC silicon into EHCI mode */
+static void tdi_reset(struct oxu_hcd *oxu)
+{
+ u32 __iomem *reg_ptr;
+ u32 tmp;
+
+ reg_ptr = (u32 __iomem *)(((u8 __iomem *)oxu->regs) + 0x68);
+ tmp = readl(reg_ptr);
+ tmp |= 0x3;
+ writel(tmp, reg_ptr);
+}
+
+/* Reset a non-running (STS_HALT == 1) controller */
+static int ehci_reset(struct oxu_hcd *oxu)
+{
+ int retval;
+ u32 command = readl(&oxu->regs->command);
+
+ command |= CMD_RESET;
+ dbg_cmd(oxu, "reset", command);
+ writel(command, &oxu->regs->command);
+ oxu_to_hcd(oxu)->state = HC_STATE_HALT;
+ oxu->next_statechange = jiffies;
+ retval = handshake(oxu, &oxu->regs->command,
+ CMD_RESET, 0, 250 * 1000);
+
+ if (retval)
+ return retval;
+
+ tdi_reset(oxu);
+
+ return retval;
+}
+
+/* Idle the controller (from running) */
+static void ehci_quiesce(struct oxu_hcd *oxu)
+{
+ u32 temp;
+
+#ifdef DEBUG
+ if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))
+ BUG();
+#endif
+
+ /* wait for any schedule enables/disables to take effect */
+ temp = readl(&oxu->regs->command) << 10;
+ temp &= STS_ASS | STS_PSS;
+ if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS,
+ temp, 16 * 125) != 0) {
+ oxu_to_hcd(oxu)->state = HC_STATE_HALT;
+ return;
+ }
+
+ /* then disable anything that's still active */
+ temp = readl(&oxu->regs->command);
+ temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
+ writel(temp, &oxu->regs->command);
+
+ /* hardware can take 16 microframes to turn off ... */
+ if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS,
+ 0, 16 * 125) != 0) {
+ oxu_to_hcd(oxu)->state = HC_STATE_HALT;
+ return;
+ }
+}
+
+static int check_reset_complete(struct oxu_hcd *oxu, int index,
+ u32 __iomem *status_reg, int port_status)
+{
+ if (!(port_status & PORT_CONNECT)) {
+ oxu->reset_done[index] = 0;
+ return port_status;
+ }
+
+ /* if reset finished and it's still not enabled -- handoff */
+ if (!(port_status & PORT_PE)) {
+ oxu_dbg(oxu, "Failed to enable port %d on root hub TT\n",
+ index+1);
+ return port_status;
+ } else
+ oxu_dbg(oxu, "port %d high speed\n", index + 1);
+
+ return port_status;
+}
+
+static void ehci_hub_descriptor(struct oxu_hcd *oxu,
+ struct usb_hub_descriptor *desc)
+{
+ int ports = HCS_N_PORTS(oxu->hcs_params);
+ u16 temp;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = 10; /* oxu 1.0, 2.3.9 says 20ms max */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
+
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ if (HCS_PPC(oxu->hcs_params))
+ temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */
+ else
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
+ desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
+}
+
+
+/* Allocate an OXU210HP on-chip memory data buffer
+ *
+ * An on-chip memory data buffer is required for each OXU210HP USB transfer.
+ * Each transfer descriptor has one or more on-chip memory data buffers.
+ *
+ * Data buffers are allocated from a fix sized pool of data blocks.
+ * To minimise fragmentation and give reasonable memory utlisation,
+ * data buffers are allocated with sizes the power of 2 multiples of
+ * the block size, starting on an address a multiple of the allocated size.
+ *
+ * FIXME: callers of this function require a buffer to be allocated for
+ * len=0. This is a waste of on-chip memory and should be fix. Then this
+ * function should be changed to not allocate a buffer for len=0.
+ */
+static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len)
+{
+ int n_blocks; /* minium blocks needed to hold len */
+ int a_blocks; /* blocks allocated */
+ int i, j;
+
+ /* Don't allocte bigger than supported */
+ if (len > BUFFER_SIZE * BUFFER_NUM) {
+ oxu_err(oxu, "buffer too big (%d)\n", len);
+ return -ENOMEM;
+ }
+
+ spin_lock(&oxu->mem_lock);
+
+ /* Number of blocks needed to hold len */
+ n_blocks = (len + BUFFER_SIZE - 1) / BUFFER_SIZE;
+
+ /* Round the number of blocks up to the power of 2 */
+ for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1)
+ ;
+
+ /* Find a suitable available data buffer */
+ for (i = 0; i < BUFFER_NUM;
+ i += max(a_blocks, (int)oxu->db_used[i])) {
+
+ /* Check all the required blocks are available */
+ for (j = 0; j < a_blocks; j++)
+ if (oxu->db_used[i + j])
+ break;
+
+ if (j != a_blocks)
+ continue;
+
+ /* Allocate blocks found! */
+ qtd->buffer = (void *) &oxu->mem->db_pool[i];
+ qtd->buffer_dma = virt_to_phys(qtd->buffer);
+
+ qtd->qtd_buffer_len = BUFFER_SIZE * a_blocks;
+ oxu->db_used[i] = a_blocks;
+
+ spin_unlock(&oxu->mem_lock);
+
+ return 0;
+ }
+
+ /* Failed */
+
+ spin_unlock(&oxu->mem_lock);
+
+ return -ENOMEM;
+}
+
+static void oxu_buf_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd)
+{
+ int index;
+
+ spin_lock(&oxu->mem_lock);
+
+ index = (qtd->buffer - (void *) &oxu->mem->db_pool[0])
+ / BUFFER_SIZE;
+ oxu->db_used[index] = 0;
+ qtd->qtd_buffer_len = 0;
+ qtd->buffer_dma = 0;
+ qtd->buffer = NULL;
+
+ spin_unlock(&oxu->mem_lock);
+}
+
+static inline void ehci_qtd_init(struct ehci_qtd *qtd, dma_addr_t dma)
+{
+ memset(qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_le32(QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD(&qtd->qtd_list);
+}
+
+static inline void oxu_qtd_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd)
+{
+ int index;
+
+ if (qtd->buffer)
+ oxu_buf_free(oxu, qtd);
+
+ spin_lock(&oxu->mem_lock);
+
+ index = qtd - &oxu->mem->qtd_pool[0];
+ oxu->qtd_used[index] = 0;
+
+ spin_unlock(&oxu->mem_lock);
+}
+
+static struct ehci_qtd *ehci_qtd_alloc(struct oxu_hcd *oxu)
+{
+ int i;
+ struct ehci_qtd *qtd = NULL;
+
+ spin_lock(&oxu->mem_lock);
+
+ for (i = 0; i < QTD_NUM; i++)
+ if (!oxu->qtd_used[i])
+ break;
+
+ if (i < QTD_NUM) {
+ qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i];
+ memset(qtd, 0, sizeof *qtd);
+
+ qtd->hw_token = cpu_to_le32(QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD(&qtd->qtd_list);
+
+ qtd->qtd_dma = virt_to_phys(qtd);
+
+ oxu->qtd_used[i] = 1;
+ }
+
+ spin_unlock(&oxu->mem_lock);
+
+ return qtd;
+}
+
+static void oxu_qh_free(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ int index;
+
+ spin_lock(&oxu->mem_lock);
+
+ index = qh - &oxu->mem->qh_pool[0];
+ oxu->qh_used[index] = 0;
+
+ spin_unlock(&oxu->mem_lock);
+}
+
+static void qh_destroy(struct kref *kref)
+{
+ struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
+ struct oxu_hcd *oxu = qh->oxu;
+
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) {
+ oxu_dbg(oxu, "unused qh not empty!\n");
+ BUG();
+ }
+ if (qh->dummy)
+ oxu_qtd_free(oxu, qh->dummy);
+ oxu_qh_free(oxu, qh);
+}
+
+static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu)
+{
+ int i;
+ struct ehci_qh *qh = NULL;
+
+ spin_lock(&oxu->mem_lock);
+
+ for (i = 0; i < QHEAD_NUM; i++)
+ if (!oxu->qh_used[i])
+ break;
+
+ if (i < QHEAD_NUM) {
+ qh = (struct ehci_qh *) &oxu->mem->qh_pool[i];
+ memset(qh, 0, sizeof *qh);
+
+ kref_init(&qh->kref);
+ qh->oxu = oxu;
+ qh->qh_dma = virt_to_phys(qh);
+ INIT_LIST_HEAD(&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = ehci_qtd_alloc(oxu);
+ if (qh->dummy == NULL) {
+ oxu_dbg(oxu, "no dummy td\n");
+ oxu->qh_used[i] = 0;
+ qh = NULL;
+ goto unlock;
+ }
+
+ oxu->qh_used[i] = 1;
+ }
+unlock:
+ spin_unlock(&oxu->mem_lock);
+
+ return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_get(struct ehci_qh *qh)
+{
+ kref_get(&qh->kref);
+ return qh;
+}
+
+static inline void qh_put(struct ehci_qh *qh)
+{
+ kref_put(&qh->kref, qh_destroy);
+}
+
+static void oxu_murb_free(struct oxu_hcd *oxu, struct oxu_murb *murb)
+{
+ int index;
+
+ spin_lock(&oxu->mem_lock);
+
+ index = murb - &oxu->murb_pool[0];
+ oxu->murb_used[index] = 0;
+
+ spin_unlock(&oxu->mem_lock);
+}
+
+static struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu)
+
+{
+ int i;
+ struct oxu_murb *murb = NULL;
+
+ spin_lock(&oxu->mem_lock);
+
+ for (i = 0; i < MURB_NUM; i++)
+ if (!oxu->murb_used[i])
+ break;
+
+ if (i < MURB_NUM) {
+ murb = &(oxu->murb_pool)[i];
+
+ oxu->murb_used[i] = 1;
+ }
+
+ spin_unlock(&oxu->mem_lock);
+
+ return murb;
+}
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+static void ehci_mem_cleanup(struct oxu_hcd *oxu)
+{
+ kfree(oxu->murb_pool);
+ oxu->murb_pool = NULL;
+
+ if (oxu->async)
+ qh_put(oxu->async);
+ oxu->async = NULL;
+
+ del_timer(&oxu->urb_timer);
+
+ oxu->periodic = NULL;
+
+ /* shadow periodic table */
+ kfree(oxu->pshadow);
+ oxu->pshadow = NULL;
+}
+
+/* Remember to add cleanup code (above) if you add anything here.
+ */
+static int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags)
+{
+ int i;
+
+ for (i = 0; i < oxu->periodic_size; i++)
+ oxu->mem->frame_list[i] = EHCI_LIST_END;
+ for (i = 0; i < QHEAD_NUM; i++)
+ oxu->qh_used[i] = 0;
+ for (i = 0; i < QTD_NUM; i++)
+ oxu->qtd_used[i] = 0;
+
+ oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags);
+ if (!oxu->murb_pool)
+ goto fail;
+
+ for (i = 0; i < MURB_NUM; i++)
+ oxu->murb_used[i] = 0;
+
+ oxu->async = oxu_qh_alloc(oxu);
+ if (!oxu->async)
+ goto fail;
+
+ oxu->periodic = (__le32 *) &oxu->mem->frame_list;
+ oxu->periodic_dma = virt_to_phys(oxu->periodic);
+
+ for (i = 0; i < oxu->periodic_size; i++)
+ oxu->periodic[i] = EHCI_LIST_END;
+
+ /* software shadow of hardware table */
+ oxu->pshadow = kcalloc(oxu->periodic_size, sizeof(void *), flags);
+ if (oxu->pshadow != NULL)
+ return 0;
+
+fail:
+ oxu_dbg(oxu, "couldn't init memory\n");
+ ehci_mem_cleanup(oxu);
+ return -ENOMEM;
+}
+
+/* Fill a qtd, returning how much of the buffer we were able to queue up.
+ */
+static int qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
+ int token, int maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf[0] = cpu_to_le32((u32)addr);
+ qtd->hw_buf_hi[0] = cpu_to_le32((u32)(addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely(len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ addr = buf;
+ qtd->hw_buf[i] = cpu_to_le32((u32)addr);
+ qtd->hw_buf_hi[i] = cpu_to_le32((u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_le32((count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+static inline void qh_update(struct oxu_hcd *oxu,
+ struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ /* writes to an active overlay are unsafe */
+ BUG_ON(qh->qh_state != QH_STATE_IDLE);
+
+ qh->hw_qtd_next = QTD_NEXT(qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END;
+
+ /* 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_le32(1 << 14))) {
+ unsigned is_out, epnum;
+
+ is_out = !(qtd->hw_token & cpu_to_le32(1 << 8));
+ epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f;
+ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) {
+ qh->hw_token &= ~cpu_to_le32(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_le32(QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* If it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void qh_refresh(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ struct ehci_qtd *qtd;
+
+ if (list_empty(&qh->qtd_list))
+ qtd = qh->dummy;
+ else {
+ qtd = list_entry(qh->qtd_list.next,
+ struct ehci_qtd, qtd_list);
+ /* first qtd may already be partially processed */
+ if (cpu_to_le32(qtd->qtd_dma) == qh->hw_current)
+ qtd = NULL;
+ }
+
+ if (qtd)
+ qh_update(oxu, qh, qtd);
+}
+
+static void qtd_copy_status(struct oxu_hcd *oxu, struct urb *urb,
+ size_t length, u32 token)
+{
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely(QTD_PID(token) != 2))
+ urb->actual_length += length - QTD_LENGTH(token);
+
+ /* don't modify error codes */
+ if (unlikely(urb->status != -EINPROGRESS))
+ return;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely(IS_SHORT_READ(token)))
+ urb->status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ urb->status = -EOVERFLOW;
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ urb->status = -EPROTO;
+ } else if (token & QTD_STS_DBE) {
+ urb->status = (QTD_PID(token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ } else if (token & QTD_STS_XACT) {
+ /* timeout, bad crc, wrong PID, etc; retried */
+ if (QTD_CERR(token))
+ urb->status = -EPIPE;
+ else {
+ oxu_dbg(oxu, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out");
+ urb->status = -EPROTO;
+ }
+ /* CERR nonzero + no errors + halt --> stall */
+ } else if (QTD_CERR(token))
+ urb->status = -EPIPE;
+ else /* unknown */
+ urb->status = -EPROTO;
+
+ oxu_vdbg(oxu, "dev%d ep%d%s qtd token %08x --> status %d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ token, urb->status);
+ }
+}
+
+static void ehci_urb_done(struct oxu_hcd *oxu, struct urb *urb)
+__releases(oxu->lock)
+__acquires(oxu->lock)
+{
+ if (likely(urb->hcpriv != NULL)) {
+ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw_info2 & cpu_to_le32(QH_SMASK)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ oxu_to_hcd(oxu)->self.bandwidth_int_reqs--;
+ }
+ qh_put(qh);
+ }
+
+ urb->hcpriv = NULL;
+ switch (urb->status) {
+ case -EINPROGRESS: /* success */
+ urb->status = 0;
+ default: /* fault */
+ break;
+ case -EREMOTEIO: /* fault or normal */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ urb->status = 0;
+ break;
+ case -ECONNRESET: /* canceled */
+ case -ENOENT:
+ break;
+ }
+
+#ifdef OXU_URB_TRACE
+ oxu_dbg(oxu, "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb->status,
+ urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ /* complete() can reenter this HCD */
+ spin_unlock(&oxu->lock);
+ usb_hcd_giveback_urb(oxu_to_hcd(oxu), urb, urb->status);
+ spin_lock(&oxu->lock);
+}
+
+static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh);
+static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh);
+
+static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh);
+static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh);
+
+#define HALT_BIT cpu_to_le32(QTD_STS_HALT)
+
+/* Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ struct ehci_qtd *last = NULL, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int stopped;
+ unsigned count = 0;
+ int do_status = 0;
+ u8 state;
+ struct oxu_murb *murb = NULL;
+
+ if (unlikely(list_empty(&qh->qtd_list)))
+ return count;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe(entry, tmp, &qh->qtd_list) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ u32 token = 0;
+
+ qtd = list_entry(entry, struct ehci_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* Clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely(last->urb != urb)) {
+ if (last->urb->complete == NULL) {
+ murb = (struct oxu_murb *) last->urb;
+ last->urb = murb->main;
+ if (murb->last) {
+ ehci_urb_done(oxu, last->urb);
+ count++;
+ }
+ oxu_murb_free(oxu, murb);
+ } else {
+ ehci_urb_done(oxu, last->urb);
+ count++;
+ }
+ }
+ oxu_qtd_free(oxu, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb();
+ token = le32_to_cpu(qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ if ((token & QTD_STS_HALT) != 0) {
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ */
+ } else if (IS_SHORT_READ(token) &&
+ !(qtd->hw_alt_next & EHCI_LIST_END)) {
+ stopped = 1;
+ goto halt;
+ }
+
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely(!stopped &&
+ HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) {
+ break;
+
+ } else {
+ stopped = 1;
+
+ if (unlikely(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)))
+ urb->status = -ESHUTDOWN;
+
+ /* ignore active urbs unless some previous qtd
+ * for the urb faulted (including short read) or
+ * its urb was canceled. we may patch qh or qtds.
+ */
+ if (likely(urb->status == -EINPROGRESS))
+ continue;
+
+ /* issue status after short control reads */
+ if (unlikely(do_status != 0)
+ && QTD_PID(token) == 0 /* OUT */) {
+ do_status = 0;
+ continue;
+ }
+
+ /* token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_le32(qtd->qtd_dma)
+ == qh->hw_current)
+ token = le32_to_cpu(qh->hw_token);
+
+ /* force halt for unlinked or blocked qh, so we'll
+ * patch the qh later and so that completions can't
+ * activate it while we "know" it's stopped.
+ */
+ if ((HALT_BIT & qh->hw_token) == 0) {
+halt:
+ qh->hw_token |= HALT_BIT;
+ wmb();
+ }
+ }
+
+ /* Remove it from the queue */
+ qtd_copy_status(oxu, urb->complete ?
+ urb : ((struct oxu_murb *) urb)->main,
+ qtd->length, token);
+ if ((usb_pipein(qtd->urb->pipe)) &&
+ (NULL != qtd->transfer_buffer))
+ memcpy(qtd->transfer_buffer, qtd->buffer, qtd->length);
+ do_status = (urb->status == -EREMOTEIO)
+ && usb_pipecontrol(urb->pipe);
+
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry(qtd->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+ list_del(&qtd->qtd_list);
+ last = qtd;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely(last != NULL)) {
+ if (last->urb->complete == NULL) {
+ murb = (struct oxu_murb *) last->urb;
+ last->urb = murb->main;
+ if (murb->last) {
+ ehci_urb_done(oxu, last->urb);
+ count++;
+ }
+ oxu_murb_free(oxu, murb);
+ } else {
+ ehci_urb_done(oxu, last->urb);
+ count++;
+ }
+ oxu_qtd_free(oxu, last);
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ */
+ if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) {
+ switch (state) {
+ case QH_STATE_IDLE:
+ qh_refresh(oxu, qh);
+ break;
+ case QH_STATE_LINKED:
+ /* should be rare for periodic transfers,
+ * except maybe high bandwidth ...
+ */
+ if ((cpu_to_le32(QH_SMASK)
+ & qh->hw_info2) != 0) {
+ intr_deschedule(oxu, qh);
+ (void) qh_schedule(oxu, qh);
+ } else
+ unlink_async(oxu, qh);
+ break;
+ /* otherwise, unlink already started */
+ }
+ }
+
+ return count;
+}
+
+/* High bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+/* ... and packet size, for any kind of endpoint descriptor */
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/* Reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free(struct oxu_hcd *oxu,
+ struct urb *urb, struct list_head *qtd_list)
+{
+ struct list_head *entry, *temp;
+
+ list_for_each_safe(entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry(entry, struct ehci_qtd, qtd_list);
+ list_del(&qtd->qtd_list);
+ oxu_qtd_free(oxu, qtd);
+ }
+}
+
+/* Create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *qh_urb_transaction(struct oxu_hcd *oxu,
+ struct urb *urb,
+ struct list_head *head,
+ gfp_t flags)
+{
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, maxpacket;
+ int is_input;
+ u32 token;
+ void *transfer_buf = NULL;
+ int ret;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc(oxu);
+ if (unlikely(!qtd))
+ return NULL;
+ list_add_tail(&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein(urb->pipe);
+ if (!urb->transfer_buffer && urb->transfer_buffer_length && is_input)
+ urb->transfer_buffer = phys_to_virt(urb->transfer_dma);
+
+ if (usb_pipecontrol(urb->pipe)) {
+ /* SETUP pid */
+ ret = oxu_buf_alloc(oxu, qtd, sizeof(struct usb_ctrlrequest));
+ if (ret)
+ goto cleanup;
+
+ qtd_fill(qtd, qtd->buffer_dma, sizeof(struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+ memcpy(qtd->buffer, qtd->urb->setup_packet,
+ sizeof(struct usb_ctrlrequest));
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(oxu);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
+ }
+
+ /*
+ * Data transfer stage: buffer setup
+ */
+
+ ret = oxu_buf_alloc(oxu, qtd, len);
+ if (ret)
+ goto cleanup;
+
+ buf = qtd->buffer_dma;
+ transfer_buf = urb->transfer_buffer;
+
+ if (!is_input)
+ memcpy(qtd->buffer, qtd->urb->transfer_buffer, len);
+
+ if (is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+
+ this_qtd_len = qtd_fill(qtd, buf, len, token, maxpacket);
+ qtd->transfer_buffer = transfer_buf;
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+ transfer_buf += this_qtd_len;
+ if (is_input)
+ qtd->hw_alt_next = oxu->async->hw_alt_next;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely(len <= 0))
+ break;
+
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(oxu);
+ if (unlikely(!qtd))
+ goto cleanup;
+ if (likely(len > 0)) {
+ ret = oxu_buf_alloc(oxu, qtd, len);
+ if (ret)
+ goto cleanup;
+ }
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+ }
+
+ /* unless the bulk/interrupt caller wants a chance to clean
+ * up after short reads, hc should advance qh past this urb
+ */
+ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol(urb->pipe)))
+ qtd->hw_alt_next = EHCI_LIST_END;
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * bulk ones may need a terminating short packet (zero length).
+ */
+ if (likely(urb->transfer_buffer_length != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipebulk(urb->pipe)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(oxu);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill(qtd, 0, 0, token, 0);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ qtd->hw_token |= cpu_to_le32(QTD_IOC);
+ return head;
+
+cleanup:
+ qtd_list_free(oxu, urb, head);
+ return NULL;
+}
+
+/* Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *qh_make(struct oxu_hcd *oxu,
+ struct urb *urb, gfp_t flags)
+{
+ struct ehci_qh *qh = oxu_qh_alloc(oxu);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint(urb->pipe) << 8;
+ info1 |= usb_pipedevice(urb->pipe) << 0;
+
+ is_input = usb_pipein(urb->pipe);
+ type = usb_pipetype(urb->pipe);
+ maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+ is_input, 0,
+ hb_mult(maxp) * max_packet(maxp)));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ qh->period = urb->interval >> 3;
+ if (qh->period == 0 && urb->interval != 1) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ oxu_dbg(oxu, "intr period %d uframes, NYET!\n",
+ urb->interval);
+ goto done;
+ }
+ } else {
+ struct usb_tt *tt = urb->dev->tt;
+ int think_time;
+
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */
+ qh->c_usecs = qh->usecs + HS_USECS(0);
+ qh->usecs = HS_USECS(1);
+ } else { /* SPLIT+DATA, gap, CSPLIT */
+ qh->usecs += HS_USECS(1);
+ qh->c_usecs = HS_USECS(0);
+ }
+
+ think_time = tt ? tt->think_time : 0;
+ qh->tt_usecs = NS_TO_US(think_time +
+ usb_calc_bus_time(urb->dev->speed,
+ is_input, 0, max_packet(maxp)));
+ qh->period = urb->interval;
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->dev = urb->dev;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= (1 << 12); /* EPS "low" */
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+ info2 |= urb->dev->ttport << 23;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= 1 << 14; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 512 << 16; /* usb2 fixed maxpacket */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= hb_mult(maxp) << 30;
+ }
+ break;
+ default:
+ oxu_dbg(oxu, "bogus dev %p speed %d\n", urb->dev, urb->dev->speed);
+done:
+ qh_put(qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_info1 = cpu_to_le32(info1);
+ qh->hw_info2 = cpu_to_le32(info2);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1);
+ qh_refresh(oxu, qh);
+ return qh;
+}
+
+/* Move qh (and its qtds) onto async queue; maybe enable queue.
+ */
+static void qh_link_async(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ __le32 dma = QH_NEXT(qh->qh_dma);
+ struct ehci_qh *head;
+
+ /* (re)start the async schedule? */
+ head = oxu->async;
+ timer_action_done(oxu, TIMER_ASYNC_OFF);
+ if (!head->qh_next.qh) {
+ u32 cmd = readl(&oxu->regs->command);
+
+ if (!(cmd & CMD_ASE)) {
+ /* in case a clear of CMD_ASE didn't take yet */
+ (void)handshake(oxu, &oxu->regs->status,
+ STS_ASS, 0, 150);
+ cmd |= CMD_ASE | CMD_RUN;
+ writel(cmd, &oxu->regs->command);
+ oxu_to_hcd(oxu)->state = HC_STATE_RUNNING;
+ /* posted write need not be known to HC yet ... */
+ }
+ }
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ if (qh->qh_state == QH_STATE_IDLE)
+ qh_refresh(oxu, qh);
+
+ /* splice right after start */
+ qh->qh_next = head->qh_next;
+ qh->hw_next = head->hw_next;
+ wmb();
+
+ head->qh_next.qh = qh;
+ head->hw_next = dma;
+
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+}
+
+#define QH_ADDR_MASK cpu_to_le32(0x7f)
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds(struct oxu_hcd *oxu,
+ struct urb *urb, struct list_head *qtd_list,
+ int epnum, void **ptr)
+{
+ struct ehci_qh *qh = NULL;
+
+ qh = (struct ehci_qh *) *ptr;
+ if (unlikely(qh == NULL)) {
+ /* can't sleep here, we have oxu->lock... */
+ qh = qh_make(oxu, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely(qh != NULL)) {
+ struct ehci_qtd *qtd;
+
+ if (unlikely(list_empty(qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry(qtd_list->next, struct ehci_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely(epnum == 0)) {
+
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice(urb->pipe) == 0)
+ qh->hw_info1 &= ~QH_ADDR_MASK;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely(qtd != NULL)) {
+ struct ehci_qtd *dummy;
+ dma_addr_t dma;
+ __le32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT;
+ wmb();
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del(&qtd->qtd_list);
+ list_add(&dummy->qtd_list, qtd_list);
+ list_splice(qtd_list, qh->qtd_list.prev);
+
+ ehci_qtd_init(qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry(qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ qtd->hw_next = QTD_NEXT(dma);
+
+ /* let the hc process these next qtds */
+ dummy->hw_token = (token & ~(0x80));
+ wmb();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh_get(qh);
+ }
+ }
+ return qh;
+}
+
+static int submit_async(struct oxu_hcd *oxu, struct urb *urb,
+ struct list_head *qtd_list, gfp_t mem_flags)
+{
+ struct ehci_qtd *qtd;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = NULL;
+ int rc = 0;
+
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ epnum = urb->ep->desc.bEndpointAddress;
+
+#ifdef OXU_URB_TRACE
+ oxu_dbg(oxu, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, urb->ep->hcpriv);
+#endif
+
+ spin_lock_irqsave(&oxu->lock, flags);
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+
+ qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely(qh->qh_state == QH_STATE_IDLE))
+ qh_link_async(oxu, qh_get(qh));
+done:
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ if (unlikely(qh == NULL))
+ qtd_list_free(oxu, urb, qtd_list);
+ return rc;
+}
+
+/* The async qh for the qtds being reclaimed are now unlinked from the HC */
+
+static void end_unlink_async(struct oxu_hcd *oxu)
+{
+ struct ehci_qh *qh = oxu->reclaim;
+ struct ehci_qh *next;
+
+ timer_action_done(oxu, TIMER_IAA_WATCHDOG);
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+ qh_put(qh); /* refcount from reclaim */
+
+ /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
+ next = qh->reclaim;
+ oxu->reclaim = next;
+ oxu->reclaim_ready = 0;
+ qh->reclaim = NULL;
+
+ qh_completions(oxu, qh);
+
+ if (!list_empty(&qh->qtd_list)
+ && HC_IS_RUNNING(oxu_to_hcd(oxu)->state))
+ qh_link_async(oxu, qh);
+ else {
+ qh_put(qh); /* refcount from async list */
+
+ /* it's not free to turn the async schedule on/off; leave it
+ * active but idle for a while once it empties.
+ */
+ if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state)
+ && oxu->async->qh_next.qh == NULL)
+ timer_action(oxu, TIMER_ASYNC_OFF);
+ }
+
+ if (next) {
+ oxu->reclaim = NULL;
+ start_unlink_async(oxu, next);
+ }
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own oxu->lock */
+
+static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ int cmd = readl(&oxu->regs->command);
+ struct ehci_qh *prev;
+
+#ifdef DEBUG
+ assert_spin_locked(&oxu->lock);
+ if (oxu->reclaim || (qh->qh_state != QH_STATE_LINKED
+ && qh->qh_state != QH_STATE_UNLINK_WAIT))
+ BUG();
+#endif
+
+ /* stop async schedule right now? */
+ if (unlikely(qh == oxu->async)) {
+ /* can't get here without STS_ASS set */
+ if (oxu_to_hcd(oxu)->state != HC_STATE_HALT
+ && !oxu->reclaim) {
+ /* ... and CMD_IAAD clear */
+ writel(cmd & ~CMD_ASE, &oxu->regs->command);
+ wmb();
+ /* handshake later, if we need to */
+ timer_action_done(oxu, TIMER_ASYNC_OFF);
+ }
+ return;
+ }
+
+ qh->qh_state = QH_STATE_UNLINK;
+ oxu->reclaim = qh = qh_get(qh);
+
+ prev = oxu->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw_next = qh->hw_next;
+ prev->qh_next = qh->qh_next;
+ wmb();
+
+ if (unlikely(oxu_to_hcd(oxu)->state == HC_STATE_HALT)) {
+ /* if (unlikely(qh->reclaim != 0))
+ * this will recurse, probably not much
+ */
+ end_unlink_async(oxu);
+ return;
+ }
+
+ oxu->reclaim_ready = 0;
+ cmd |= CMD_IAAD;
+ writel(cmd, &oxu->regs->command);
+ (void) readl(&oxu->regs->command);
+ timer_action(oxu, TIMER_IAA_WATCHDOG);
+}
+
+static void scan_async(struct oxu_hcd *oxu)
+{
+ struct ehci_qh *qh;
+ enum ehci_timer_action action = TIMER_IO_WATCHDOG;
+
+ if (!++(oxu->stamp))
+ oxu->stamp++;
+ timer_action_done(oxu, TIMER_ASYNC_SHRINK);
+rescan:
+ qh = oxu->async->qh_next.qh;
+ if (likely(qh != NULL)) {
+ do {
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)
+ && qh->stamp != oxu->stamp) {
+ int temp;
+
+ /* unlinks could happen here; completion
+ * reporting drops the lock. rescan using
+ * the latest schedule, but don't rescan
+ * qhs we already finished (no looping).
+ */
+ qh = qh_get(qh);
+ qh->stamp = oxu->stamp;
+ temp = qh_completions(oxu, qh);
+ qh_put(qh);
+ if (temp != 0)
+ goto rescan;
+ }
+
+ /* unlink idle entries, reducing HC PCI usage as well
+ * as HCD schedule-scanning costs. delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ * (plus, avoids some kind of re-activation race.)
+ */
+ if (list_empty(&qh->qtd_list)) {
+ if (qh->stamp == oxu->stamp)
+ action = TIMER_ASYNC_SHRINK;
+ else if (!oxu->reclaim
+ && qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async(oxu, qh);
+ }
+
+ qh = qh->qh_next.qh;
+ } while (qh);
+ }
+ if (action == TIMER_ASYNC_SHRINK)
+ timer_action(oxu, TIMER_ASYNC_SHRINK);
+}
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd/sitd
+ * @tag: hardware tag for type of this record
+ */
+static union ehci_shadow *periodic_next_shadow(union ehci_shadow *periodic,
+ __le32 tag)
+{
+ switch (tag) {
+ default:
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ }
+}
+
+/* caller must hold oxu->lock */
+static void periodic_unlink(struct oxu_hcd *oxu, unsigned frame, void *ptr)
+{
+ union ehci_shadow *prev_p = &oxu->pshadow[frame];
+ __le32 *hw_p = &oxu->periodic[frame];
+ union ehci_shadow here = *prev_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow(prev_p, Q_NEXT_TYPE(*hw_p));
+ hw_p = here.hw_next;
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr)
+ return;
+
+ /* update shadow and hardware lists ... the old "next" pointers
+ * from ptr may still be in use, the caller updates them.
+ */
+ *prev_p = *periodic_next_shadow(&here, Q_NEXT_TYPE(*hw_p));
+ *hw_p = *here.hw_next;
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short periodic_usecs(struct oxu_hcd *oxu,
+ unsigned frame, unsigned uframe)
+{
+ __le32 *hw_p = &oxu->periodic[frame];
+ union ehci_shadow *q = &oxu->pshadow[frame];
+ unsigned usecs = 0;
+
+ while (q->ptr) {
+ switch (Q_NEXT_TYPE(*hw_p)) {
+ case Q_TYPE_QH:
+ default:
+ /* is it in the S-mask? */
+ if (q->qh->hw_info2 & cpu_to_le32(1 << uframe))
+ usecs += q->qh->usecs;
+ /* ... or C-mask? */
+ if (q->qh->hw_info2 & cpu_to_le32(1 << (8 + uframe)))
+ usecs += q->qh->c_usecs;
+ hw_p = &q->qh->hw_next;
+ q = &q->qh->qh_next;
+ break;
+ }
+ }
+#ifdef DEBUG
+ if (usecs > 100)
+ oxu_err(oxu, "uframe %d sched overrun: %d usecs\n",
+ frame * 8 + uframe, usecs);
+#endif
+ return usecs;
+}
+
+static int enable_periodic(struct oxu_hcd *oxu)
+{
+ u32 cmd;
+ int status;
+
+ /* did clearing PSE did take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125);
+ if (status != 0) {
+ oxu_to_hcd(oxu)->state = HC_STATE_HALT;
+ usb_hc_died(oxu_to_hcd(oxu));
+ return status;
+ }
+
+ cmd = readl(&oxu->regs->command) | CMD_PSE;
+ writel(cmd, &oxu->regs->command);
+ /* posted write ... PSS happens later */
+ oxu_to_hcd(oxu)->state = HC_STATE_RUNNING;
+
+ /* make sure ehci_work scans these */
+ oxu->next_uframe = readl(&oxu->regs->frame_index)
+ % (oxu->periodic_size << 3);
+ return 0;
+}
+
+static int disable_periodic(struct oxu_hcd *oxu)
+{
+ u32 cmd;
+ int status;
+
+ /* did setting PSE not take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125);
+ if (status != 0) {
+ oxu_to_hcd(oxu)->state = HC_STATE_HALT;
+ usb_hc_died(oxu_to_hcd(oxu));
+ return status;
+ }
+
+ cmd = readl(&oxu->regs->command) & ~CMD_PSE;
+ writel(cmd, &oxu->regs->command);
+ /* posted write ... */
+
+ oxu->next_uframe = -1;
+ return 0;
+}
+
+/* periodic schedule slots have iso tds (normal or split) first, then a
+ * sparse tree for active interrupt transfers.
+ *
+ * this just links in a qh; caller guarantees uframe masks are set right.
+ * no FSTN support (yet; oxu 0.96+)
+ */
+static int qh_link_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period = qh->period;
+
+ dev_dbg(&qh->dev->dev,
+ "link qh%d-%04x/%p start %d [%d/%d us]\n",
+ period, le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* high bandwidth, or otherwise every microframe */
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->start; i < oxu->periodic_size; i += period) {
+ union ehci_shadow *prev = &oxu->pshadow[i];
+ __le32 *hw_p = &oxu->periodic[i];
+ union ehci_shadow here = *prev;
+ __le32 type = 0;
+
+ /* skip the iso nodes at list head */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(*hw_p);
+ if (type == Q_TYPE_QH)
+ break;
+ prev = periodic_next_shadow(prev, type);
+ hw_p = &here.qh->hw_next;
+ here = *prev;
+ }
+
+ /* sorting each branch by period (slow-->fast)
+ * enables sharing interior tree nodes
+ */
+ while (here.ptr && qh != here.qh) {
+ if (qh->period > here.qh->period)
+ break;
+ prev = &here.qh->qh_next;
+ hw_p = &here.qh->hw_next;
+ here = *prev;
+ }
+ /* link in this qh, unless some earlier pass did that */
+ if (qh != here.qh) {
+ qh->qh_next = here;
+ if (here.qh)
+ qh->hw_next = *hw_p;
+ wmb();
+ prev->qh = qh;
+ *hw_p = QH_NEXT(qh->qh_dma);
+ }
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ qh_get(qh);
+
+ /* update per-qh bandwidth for usbfs */
+ oxu_to_hcd(oxu)->self.bandwidth_allocated += qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ /* maybe enable periodic schedule processing */
+ if (!oxu->periodic_sched++)
+ return enable_periodic(oxu);
+
+ return 0;
+}
+
+static void qh_unlink_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period;
+
+ /* FIXME:
+ * IF this isn't high speed
+ * and this qh is active in the current uframe
+ * (and overlay token SplitXstate is false?)
+ * THEN
+ * qh->hw_info1 |= cpu_to_le32(1 << 7 "ignore");
+ */
+
+ /* high bandwidth, or otherwise part of every microframe */
+ period = qh->period;
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->start; i < oxu->periodic_size; i += period)
+ periodic_unlink(oxu, i, qh);
+
+ /* update per-qh bandwidth for usbfs */
+ oxu_to_hcd(oxu)->self.bandwidth_allocated -= qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ dev_dbg(&qh->dev->dev,
+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
+ qh->period,
+ le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* qh->qh_next still "live" to HC */
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = NULL;
+ qh_put(qh);
+
+ /* maybe turn off periodic schedule */
+ oxu->periodic_sched--;
+ if (!oxu->periodic_sched)
+ (void) disable_periodic(oxu);
+}
+
+static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ unsigned wait;
+
+ qh_unlink_periodic(oxu, qh);
+
+ /* simple/paranoid: always delay, expecting the HC needs to read
+ * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and
+ * expect hub_wq to clean up after any CSPLITs we won't issue.
+ * active high speed queues may need bigger delays...
+ */
+ if (list_empty(&qh->qtd_list)
+ || (cpu_to_le32(QH_CMASK) & qh->hw_info2) != 0)
+ wait = 2;
+ else
+ wait = 55; /* worst case: 3 * 1024 */
+
+ udelay(wait);
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_next = EHCI_LIST_END;
+ wmb();
+}
+
+static int check_period(struct oxu_hcd *oxu,
+ unsigned frame, unsigned uframe,
+ unsigned period, unsigned usecs)
+{
+ int claimed;
+
+ /* complete split running into next frame?
+ * given FSTN support, we could sometimes check...
+ */
+ if (uframe >= 8)
+ return 0;
+
+ /*
+ * 80% periodic == 100 usec/uframe available
+ * convert "usecs we need" to "max already claimed"
+ */
+ usecs = 100 - usecs;
+
+ /* we "know" 2 and 4 uframe intervals were rejected; so
+ * for period 0, check _every_ microframe in the schedule.
+ */
+ if (unlikely(period == 0)) {
+ do {
+ for (uframe = 0; uframe < 7; uframe++) {
+ claimed = periodic_usecs(oxu, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ }
+ } while ((frame += 1) < oxu->periodic_size);
+
+ /* just check the specified uframe, at that period */
+ } else {
+ do {
+ claimed = periodic_usecs(oxu, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ } while ((frame += period) < oxu->periodic_size);
+ }
+
+ return 1;
+}
+
+static int check_intr_schedule(struct oxu_hcd *oxu,
+ unsigned frame, unsigned uframe,
+ const struct ehci_qh *qh, __le32 *c_maskp)
+{
+ int retval = -ENOSPC;
+
+ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
+ goto done;
+
+ if (!check_period(oxu, frame, uframe, qh->period, qh->usecs))
+ goto done;
+ if (!qh->c_usecs) {
+ retval = 0;
+ *c_maskp = 0;
+ goto done;
+ }
+
+done:
+ return retval;
+}
+
+/* "first fit" scheduling policy used the first time through,
+ * or when the previous schedule slot can't be re-used.
+ */
+static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ int status;
+ unsigned uframe;
+ __le32 c_mask;
+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+
+ qh_refresh(oxu, qh);
+ qh->hw_next = EHCI_LIST_END;
+ frame = qh->start;
+
+ /* reuse the previous schedule slots, if we can */
+ if (frame < qh->period) {
+ uframe = ffs(le32_to_cpup(&qh->hw_info2) & QH_SMASK);
+ status = check_intr_schedule(oxu, frame, --uframe,
+ qh, &c_mask);
+ } else {
+ uframe = 0;
+ c_mask = 0;
+ status = -ENOSPC;
+ }
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ if (status) {
+ /* "normal" case, uframing flexible except with splits */
+ if (qh->period) {
+ frame = qh->period - 1;
+ do {
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule(oxu,
+ frame, uframe, qh,
+ &c_mask);
+ if (status == 0)
+ break;
+ }
+ } while (status && frame--);
+
+ /* qh->period == 0 means every uframe */
+ } else {
+ frame = 0;
+ status = check_intr_schedule(oxu, 0, 0, qh, &c_mask);
+ }
+ if (status)
+ goto done;
+ qh->start = frame;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ qh->hw_info2 &= cpu_to_le32(~(QH_CMASK | QH_SMASK));
+ qh->hw_info2 |= qh->period
+ ? cpu_to_le32(1 << uframe)
+ : cpu_to_le32(QH_SMASK);
+ qh->hw_info2 |= c_mask;
+ } else
+ oxu_dbg(oxu, "reused qh %p schedule\n", qh);
+
+ /* stuff into the periodic schedule */
+ status = qh_link_periodic(oxu, qh);
+done:
+ return status;
+}
+
+static int intr_submit(struct oxu_hcd *oxu, struct urb *urb,
+ struct list_head *qtd_list, gfp_t mem_flags)
+{
+ unsigned epnum;
+ unsigned long flags;
+ struct ehci_qh *qh;
+ int status = 0;
+ struct list_head empty;
+
+ /* get endpoint and transfer/schedule data */
+ epnum = urb->ep->desc.bEndpointAddress;
+
+ spin_lock_irqsave(&oxu->lock, flags);
+
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
+ status = -ESHUTDOWN;
+ goto done;
+ }
+
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD(&empty);
+ qh = qh_append_tds(oxu, urb, &empty, epnum, &urb->ep->hcpriv);
+ if (qh == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ if (qh->qh_state == QH_STATE_IDLE) {
+ status = qh_schedule(oxu, qh);
+ if (status != 0)
+ goto done;
+ }
+
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ BUG_ON(qh == NULL);
+
+ /* ... update usbfs periodic stats */
+ oxu_to_hcd(oxu)->self.bandwidth_int_reqs++;
+
+done:
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ if (status)
+ qtd_list_free(oxu, urb, qtd_list);
+
+ return status;
+}
+
+static inline int itd_submit(struct oxu_hcd *oxu, struct urb *urb,
+ gfp_t mem_flags)
+{
+ oxu_dbg(oxu, "iso support is missing!\n");
+ return -ENOSYS;
+}
+
+static inline int sitd_submit(struct oxu_hcd *oxu, struct urb *urb,
+ gfp_t mem_flags)
+{
+ oxu_dbg(oxu, "split iso support is missing!\n");
+ return -ENOSYS;
+}
+
+static void scan_periodic(struct oxu_hcd *oxu)
+{
+ unsigned frame, clock, now_uframe, mod;
+ unsigned modified;
+
+ mod = oxu->periodic_size << 3;
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
+ * Touches as few pages as possible: cache-friendly.
+ */
+ now_uframe = oxu->next_uframe;
+ if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state))
+ clock = readl(&oxu->regs->frame_index);
+ else
+ clock = now_uframe + mod - 1;
+ clock %= mod;
+
+ for (;;) {
+ union ehci_shadow q, *q_p;
+ __le32 type, *hw_p;
+ unsigned uframes;
+
+ /* don't scan past the live uframe */
+ frame = now_uframe >> 3;
+ if (frame == (clock >> 3))
+ uframes = now_uframe & 0x07;
+ else {
+ /* safe to scan the whole frame at once */
+ now_uframe |= 0x07;
+ uframes = 8;
+ }
+
+restart:
+ /* scan each element in frame's queue for completions */
+ q_p = &oxu->pshadow[frame];
+ hw_p = &oxu->periodic[frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE(*hw_p);
+ modified = 0;
+
+ while (q.ptr != NULL) {
+ union ehci_shadow temp;
+ int live;
+
+ live = HC_IS_RUNNING(oxu_to_hcd(oxu)->state);
+ switch (type) {
+ case Q_TYPE_QH:
+ /* handle any completions */
+ temp.qh = qh_get(q.qh);
+ type = Q_NEXT_TYPE(q.qh->hw_next);
+ q = q.qh->qh_next;
+ modified = qh_completions(oxu, temp.qh);
+ if (unlikely(list_empty(&temp.qh->qtd_list)))
+ intr_deschedule(oxu, temp.qh);
+ qh_put(temp.qh);
+ break;
+ default:
+ oxu_dbg(oxu, "corrupt type %d frame %d shadow %p\n",
+ type, frame, q.ptr);
+ q.ptr = NULL;
+ }
+
+ /* assume completion callbacks modify the queue */
+ if (unlikely(modified))
+ goto restart;
+ }
+
+ /* Stop when we catch up to the HC */
+
+ /* FIXME: this assumes we won't get lapped when
+ * latencies climb; that should be rare, but...
+ * detect it, and just go all the way around.
+ * FLR might help detect this case, so long as latencies
+ * don't exceed periodic_size msec (default 1.024 sec).
+ */
+
+ /* FIXME: likewise assumes HC doesn't halt mid-scan */
+
+ if (now_uframe == clock) {
+ unsigned now;
+
+ if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))
+ break;
+ oxu->next_uframe = now_uframe;
+ now = readl(&oxu->regs->frame_index) % mod;
+ if (now_uframe == now)
+ break;
+
+ /* rescan the rest of this frame, then ... */
+ clock = now;
+ } else {
+ now_uframe++;
+ now_uframe %= mod;
+ }
+ }
+}
+
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void ehci_turn_off_all_ports(struct oxu_hcd *oxu)
+{
+ int port = HCS_N_PORTS(oxu->hcs_params);
+
+ while (port--)
+ writel(PORT_RWC_BITS, &oxu->regs->port_status[port]);
+}
+
+static void ehci_port_power(struct oxu_hcd *oxu, int is_on)
+{
+ unsigned port;
+
+ if (!HCS_PPC(oxu->hcs_params))
+ return;
+
+ oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down");
+ for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; )
+ (void) oxu_hub_control(oxu_to_hcd(oxu),
+ is_on ? SetPortFeature : ClearPortFeature,
+ USB_PORT_FEAT_POWER,
+ port--, NULL, 0);
+ msleep(20);
+}
+
+/* Called from some interrupts, timers, and so on.
+ * It calls driver completion functions, after dropping oxu->lock.
+ */
+static void ehci_work(struct oxu_hcd *oxu)
+{
+ timer_action_done(oxu, TIMER_IO_WATCHDOG);
+ if (oxu->reclaim_ready)
+ end_unlink_async(oxu);
+
+ /* another CPU may drop oxu->lock during a schedule scan while
+ * it reports urb completions. this flag guards against bogus
+ * attempts at re-entrant schedule scanning.
+ */
+ if (oxu->scanning)
+ return;
+ oxu->scanning = 1;
+ scan_async(oxu);
+ if (oxu->next_uframe != -1)
+ scan_periodic(oxu);
+ oxu->scanning = 0;
+
+ /* the IO watchdog guards against hardware or driver bugs that
+ * misplace IRQs, and should let us run completely without IRQs.
+ * such lossage has been observed on both VT6202 and VT8235.
+ */
+ if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) &&
+ (oxu->async->qh_next.ptr != NULL ||
+ oxu->periodic_sched != 0))
+ timer_action(oxu, TIMER_IO_WATCHDOG);
+}
+
+static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh)
+{
+ /* if we need to use IAA and it's busy, defer */
+ if (qh->qh_state == QH_STATE_LINKED
+ && oxu->reclaim
+ && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) {
+ struct ehci_qh *last;
+
+ for (last = oxu->reclaim;
+ last->reclaim;
+ last = last->reclaim)
+ continue;
+ qh->qh_state = QH_STATE_UNLINK_WAIT;
+ last->reclaim = qh;
+
+ /* bypass IAA if the hc can't care */
+ } else if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && oxu->reclaim)
+ end_unlink_async(oxu);
+
+ /* something else might have unlinked the qh by now */
+ if (qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async(oxu, qh);
+}
+
+/*
+ * USB host controller methods
+ */
+
+static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ u32 status, pcd_status = 0;
+ int bh;
+
+ spin_lock(&oxu->lock);
+
+ status = readl(&oxu->regs->status);
+
+ /* e.g. cardbus physical eject */
+ if (status == ~(u32) 0) {
+ oxu_dbg(oxu, "device removed\n");
+ goto dead;
+ }
+
+ /* Shared IRQ? */
+ status &= INTR_MASK;
+ if (!status || unlikely(hcd->state == HC_STATE_HALT)) {
+ spin_unlock(&oxu->lock);
+ return IRQ_NONE;
+ }
+
+ /* clear (just) interrupts */
+ writel(status, &oxu->regs->status);
+ readl(&oxu->regs->command); /* unblock posted write */
+ bh = 0;
+
+#ifdef OXU_VERBOSE_DEBUG
+ /* unrequested/ignored: Frame List Rollover */
+ dbg_status(oxu, "irq", status);
+#endif
+
+ /* INT, ERR, and IAA interrupt rates can be throttled */
+
+ /* normal [4.15.1.2] or error [4.15.1.1] completion */
+ if (likely((status & (STS_INT|STS_ERR)) != 0))
+ bh = 1;
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+ oxu->reclaim_ready = 1;
+ bh = 1;
+ }
+
+ /* remote wakeup [4.3.1] */
+ if (status & STS_PCD) {
+ unsigned i = HCS_N_PORTS(oxu->hcs_params);
+ pcd_status = status;
+
+ /* resume root hub? */
+ if (!(readl(&oxu->regs->command) & CMD_RUN))
+ usb_hcd_resume_root_hub(hcd);
+
+ while (i--) {
+ int pstatus = readl(&oxu->regs->port_status[i]);
+
+ if (pstatus & PORT_OWNER)
+ continue;
+ if (!(pstatus & PORT_RESUME)
+ || oxu->reset_done[i] != 0)
+ continue;
+
+ /* start USB_RESUME_TIMEOUT resume signaling from this
+ * port, and make hub_wq collect PORT_STAT_C_SUSPEND to
+ * stop that signaling.
+ */
+ oxu->reset_done[i] = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ oxu_dbg(oxu, "port %d remote wakeup\n", i + 1);
+ mod_timer(&hcd->rh_timer, oxu->reset_done[i]);
+ }
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely((status & STS_FATAL) != 0)) {
+ /* bogus "fatal" IRQs appear on some chips... why? */
+ status = readl(&oxu->regs->status);
+ dbg_cmd(oxu, "fatal", readl(&oxu->regs->command));
+ dbg_status(oxu, "fatal", status);
+ if (status & STS_HALT) {
+ oxu_err(oxu, "fatal error\n");
+dead:
+ ehci_reset(oxu);
+ writel(0, &oxu->regs->configured_flag);
+ usb_hc_died(hcd);
+ /* generic layer kills/unlinks all urbs, then
+ * uses oxu_stop to clean up the rest
+ */
+ bh = 1;
+ }
+ }
+
+ if (bh)
+ ehci_work(oxu);
+ spin_unlock(&oxu->lock);
+ if (pcd_status & STS_PCD)
+ usb_hcd_poll_rh_status(hcd);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t oxu_irq(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int ret = IRQ_HANDLED;
+
+ u32 status = oxu_readl(hcd->regs, OXU_CHIPIRQSTATUS);
+ u32 enable = oxu_readl(hcd->regs, OXU_CHIPIRQEN_SET);
+
+ /* Disable all interrupt */
+ oxu_writel(hcd->regs, OXU_CHIPIRQEN_CLR, enable);
+
+ if ((oxu->is_otg && (status & OXU_USBOTGI)) ||
+ (!oxu->is_otg && (status & OXU_USBSPHI)))
+ oxu210_hcd_irq(hcd);
+ else
+ ret = IRQ_NONE;
+
+ /* Enable all interrupt back */
+ oxu_writel(hcd->regs, OXU_CHIPIRQEN_SET, enable);
+
+ return ret;
+}
+
+static void oxu_watchdog(unsigned long param)
+{
+ struct oxu_hcd *oxu = (struct oxu_hcd *) param;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxu->lock, flags);
+
+ /* lost IAA irqs wedge things badly; seen with a vt8235 */
+ if (oxu->reclaim) {
+ u32 status = readl(&oxu->regs->status);
+ if (status & STS_IAA) {
+ oxu_vdbg(oxu, "lost IAA\n");
+ writel(STS_IAA, &oxu->regs->status);
+ oxu->reclaim_ready = 1;
+ }
+ }
+
+ /* stop async processing after it's idled a bit */
+ if (test_bit(TIMER_ASYNC_OFF, &oxu->actions))
+ start_unlink_async(oxu, oxu->async);
+
+ /* oxu could run by timer, without IRQs ... */
+ ehci_work(oxu);
+
+ spin_unlock_irqrestore(&oxu->lock, flags);
+}
+
+/* One-time init, only for memory state.
+ */
+static int oxu_hcd_init(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ u32 temp;
+ int retval;
+ u32 hcc_params;
+
+ spin_lock_init(&oxu->lock);
+
+ setup_timer(&oxu->watchdog, oxu_watchdog, (unsigned long)oxu);
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ oxu->periodic_size = DEFAULT_I_TDPS;
+ retval = ehci_mem_init(oxu, GFP_KERNEL);
+ if (retval < 0)
+ return retval;
+
+ /* controllers may cache some of the periodic schedule ... */
+ hcc_params = readl(&oxu->caps->hcc_params);
+ if (HCC_ISOC_CACHE(hcc_params)) /* full frame cache */
+ oxu->i_thresh = 8;
+ else /* N microframes cached */
+ oxu->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
+
+ oxu->reclaim = NULL;
+ oxu->reclaim_ready = 0;
+ oxu->next_uframe = -1;
+
+ /*
+ * dedicate a qh for the async ring head, since we couldn't unlink
+ * a 'real' qh without stopping the async schedule [4.8]. use it
+ * as the 'reclamation list head' too.
+ * its dummy is used in hw_alt_next of many tds, to prevent the qh
+ * from automatically advancing to the next td after short reads.
+ */
+ oxu->async->qh_next.qh = NULL;
+ oxu->async->hw_next = QH_NEXT(oxu->async->qh_dma);
+ oxu->async->hw_info1 = cpu_to_le32(QH_HEAD);
+ oxu->async->hw_token = cpu_to_le32(QTD_STS_HALT);
+ oxu->async->hw_qtd_next = EHCI_LIST_END;
+ oxu->async->qh_state = QH_STATE_LINKED;
+ oxu->async->hw_alt_next = QTD_NEXT(oxu->async->dummy->qtd_dma);
+
+ /* clear interrupt enables, set irq latency */
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_CANPARK(hcc_params)) {
+ /* HW default park == 3, on hardware that supports it (like
+ * NVidia and ALI silicon), maximizes throughput on the async
+ * schedule by avoiding QH fetches between transfers.
+ *
+ * With fast usb storage devices and NForce2, "park" seems to
+ * make problems: throughput reduction (!), data errors...
+ */
+ if (park) {
+ park = min(park, (unsigned) 3);
+ temp |= CMD_PARK;
+ temp |= park << 8;
+ }
+ oxu_dbg(oxu, "park %d\n", park);
+ }
+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (EHCI_TUNE_FLS << 2);
+ }
+ oxu->command = temp;
+
+ return 0;
+}
+
+/* Called during probe() after chip reset completes.
+ */
+static int oxu_reset(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int ret;
+
+ spin_lock_init(&oxu->mem_lock);
+ INIT_LIST_HEAD(&oxu->urb_list);
+ oxu->urb_len = 0;
+
+ /* FIMXE */
+ hcd->self.controller->dma_mask = NULL;
+
+ if (oxu->is_otg) {
+ oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET;
+ oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \
+ HC_LENGTH(readl(&oxu->caps->hc_capbase));
+
+ oxu->mem = hcd->regs + OXU_SPH_MEM;
+ } else {
+ oxu->caps = hcd->regs + OXU_SPH_CAP_OFFSET;
+ oxu->regs = hcd->regs + OXU_SPH_CAP_OFFSET + \
+ HC_LENGTH(readl(&oxu->caps->hc_capbase));
+
+ oxu->mem = hcd->regs + OXU_OTG_MEM;
+ }
+
+ oxu->hcs_params = readl(&oxu->caps->hcs_params);
+ oxu->sbrn = 0x20;
+
+ ret = oxu_hcd_init(hcd);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int oxu_run(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int retval;
+ u32 temp, hcc_params;
+
+ hcd->uses_new_polling = 1;
+
+ /* EHCI spec section 4.1 */
+ retval = ehci_reset(oxu);
+ if (retval != 0) {
+ ehci_mem_cleanup(oxu);
+ return retval;
+ }
+ writel(oxu->periodic_dma, &oxu->regs->frame_list);
+ writel((u32) oxu->async->qh_dma, &oxu->regs->async_next);
+
+ /* hcc_params controls whether oxu->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * pci_pool consistent memory always uses segment zero.
+ * streaming mappings for I/O buffers, like pci_map_single(),
+ * can return segments above 4GB, if the device allows.
+ *
+ * NOTE: the dma mask is visible through dma_supported(), so
+ * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+ * Scsi_Host.highmem_io, and so forth. It's readonly to all
+ * host side drivers though.
+ */
+ hcc_params = readl(&oxu->caps->hcc_params);
+ if (HCC_64BIT_ADDR(hcc_params))
+ writel(0, &oxu->regs->segment);
+
+ oxu->command &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE |
+ CMD_ASE | CMD_RESET);
+ oxu->command |= CMD_RUN;
+ writel(oxu->command, &oxu->regs->command);
+ dbg_cmd(oxu, "init", oxu->command);
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub. (Except where one is integrated,
+ * and there's no companion controller unless maybe for USB OTG.)
+ */
+ hcd->state = HC_STATE_RUNNING;
+ writel(FLAG_CF, &oxu->regs->configured_flag);
+ readl(&oxu->regs->command); /* unblock posted writes */
+
+ temp = HC_VERSION(readl(&oxu->caps->hc_capbase));
+ oxu_info(oxu, "USB %x.%x started, quasi-EHCI %x.%02x, driver %s%s\n",
+ ((oxu->sbrn & 0xf0)>>4), (oxu->sbrn & 0x0f),
+ temp >> 8, temp & 0xff, DRIVER_VERSION,
+ ignore_oc ? ", overcurrent ignored" : "");
+
+ writel(INTR_MASK, &oxu->regs->intr_enable); /* Turn On Interrupts */
+
+ return 0;
+}
+
+static void oxu_stop(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+
+ /* Turn off port power on all root hub ports. */
+ ehci_port_power(oxu, 0);
+
+ /* no more interrupts ... */
+ del_timer_sync(&oxu->watchdog);
+
+ spin_lock_irq(&oxu->lock);
+ if (HC_IS_RUNNING(hcd->state))
+ ehci_quiesce(oxu);
+
+ ehci_reset(oxu);
+ writel(0, &oxu->regs->intr_enable);
+ spin_unlock_irq(&oxu->lock);
+
+ /* let companion controllers work when we aren't */
+ writel(0, &oxu->regs->configured_flag);
+
+ /* root hub is shut down separately (first, when possible) */
+ spin_lock_irq(&oxu->lock);
+ if (oxu->async)
+ ehci_work(oxu);
+ spin_unlock_irq(&oxu->lock);
+ ehci_mem_cleanup(oxu);
+
+ dbg_status(oxu, "oxu_stop completed", readl(&oxu->regs->status));
+}
+
+/* Kick in for silicon on any bus (not just pci, etc).
+ * This forcibly disables dma and IRQs, helping kexec and other cases
+ * where the next system software may expect clean state.
+ */
+static void oxu_shutdown(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+
+ (void) ehci_halt(oxu);
+ ehci_turn_off_all_ports(oxu);
+
+ /* make BIOS/etc use companion controller during reboot */
+ writel(0, &oxu->regs->configured_flag);
+
+ /* unblock posted writes */
+ readl(&oxu->regs->configured_flag);
+}
+
+/* Non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd.self.controller.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int __oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ struct list_head qtd_list;
+
+ INIT_LIST_HEAD(&qtd_list);
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return submit_async(oxu, urb, &qtd_list, mem_flags);
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit(oxu, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+ if (urb->dev->speed == USB_SPEED_HIGH)
+ return itd_submit(oxu, urb, mem_flags);
+ else
+ return sitd_submit(oxu, urb, mem_flags);
+ }
+}
+
+/* This function is responsible for breaking URBs with big data size
+ * into smaller size and processing small urbs in sequence.
+ */
+static int oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int num, rem;
+ int transfer_buffer_length;
+ void *transfer_buffer;
+ struct urb *murb;
+ int i, ret;
+
+ /* If not bulk pipe just enqueue the URB */
+ if (!usb_pipebulk(urb->pipe))
+ return __oxu_urb_enqueue(hcd, urb, mem_flags);
+
+ /* Otherwise we should verify the USB transfer buffer size! */
+ transfer_buffer = urb->transfer_buffer;
+ transfer_buffer_length = urb->transfer_buffer_length;
+
+ num = urb->transfer_buffer_length / 4096;
+ rem = urb->transfer_buffer_length % 4096;
+ if (rem != 0)
+ num++;
+
+ /* If URB is smaller than 4096 bytes just enqueue it! */
+ if (num == 1)
+ return __oxu_urb_enqueue(hcd, urb, mem_flags);
+
+ /* Ok, we have more job to do! :) */
+
+ for (i = 0; i < num - 1; i++) {
+ /* Get free micro URB poll till a free urb is received */
+
+ do {
+ murb = (struct urb *) oxu_murb_alloc(oxu);
+ if (!murb)
+ schedule();
+ } while (!murb);
+
+ /* Coping the urb */
+ memcpy(murb, urb, sizeof(struct urb));
+
+ murb->transfer_buffer_length = 4096;
+ murb->transfer_buffer = transfer_buffer + i * 4096;
+
+ /* Null pointer for the encodes that this is a micro urb */
+ murb->complete = NULL;
+
+ ((struct oxu_murb *) murb)->main = urb;
+ ((struct oxu_murb *) murb)->last = 0;
+
+ /* This loop is to guarantee urb to be processed when there's
+ * not enough resources at a particular time by retrying.
+ */
+ do {
+ ret = __oxu_urb_enqueue(hcd, murb, mem_flags);
+ if (ret)
+ schedule();
+ } while (ret);
+ }
+
+ /* Last urb requires special handling */
+
+ /* Get free micro URB poll till a free urb is received */
+ do {
+ murb = (struct urb *) oxu_murb_alloc(oxu);
+ if (!murb)
+ schedule();
+ } while (!murb);
+
+ /* Coping the urb */
+ memcpy(murb, urb, sizeof(struct urb));
+
+ murb->transfer_buffer_length = rem > 0 ? rem : 4096;
+ murb->transfer_buffer = transfer_buffer + (num - 1) * 4096;
+
+ /* Null pointer for the encodes that this is a micro urb */
+ murb->complete = NULL;
+
+ ((struct oxu_murb *) murb)->main = urb;
+ ((struct oxu_murb *) murb)->last = 1;
+
+ do {
+ ret = __oxu_urb_enqueue(hcd, murb, mem_flags);
+ if (ret)
+ schedule();
+ } while (ret);
+
+ return ret;
+}
+
+/* Remove from hardware lists.
+ * Completions normally happen asynchronously
+ */
+static int oxu_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ struct ehci_qh *qh;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxu->lock, flags);
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ qh = (struct ehci_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ unlink_async(oxu, qh);
+ break;
+
+ case PIPE_INTERRUPT:
+ qh = (struct ehci_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ intr_deschedule(oxu, qh);
+ /* FALL THROUGH */
+ case QH_STATE_IDLE:
+ qh_completions(oxu, qh);
+ break;
+ default:
+ oxu_dbg(oxu, "bogus qh %p state %d\n",
+ qh, qh->qh_state);
+ goto done;
+ }
+
+ /* reschedule QH iff another request is queued */
+ if (!list_empty(&qh->qtd_list)
+ && HC_IS_RUNNING(hcd->state)) {
+ int status;
+
+ status = qh_schedule(oxu, qh);
+ spin_unlock_irqrestore(&oxu->lock, flags);
+
+ if (status != 0) {
+ /* shouldn't happen often, but ...
+ * FIXME kill those tds' urbs
+ */
+ dev_err(hcd->self.controller,
+ "can't reschedule qh %p, err %d\n", qh,
+ status);
+ }
+ return status;
+ }
+ break;
+ }
+done:
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ return 0;
+}
+
+/* Bulk qh holds the data toggle */
+static void oxu_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ unsigned long flags;
+ struct ehci_qh *qh, *tmp;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+rescan:
+ spin_lock_irqsave(&oxu->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh)
+ goto done;
+
+ /* endpoints can be iso streams. for now, we don't
+ * accelerate iso completions ... so spin a while.
+ */
+ if (qh->hw_info1 == 0) {
+ oxu_vdbg(oxu, "iso delay\n");
+ goto idle_timeout;
+ }
+
+ if (!HC_IS_RUNNING(hcd->state))
+ qh->qh_state = QH_STATE_IDLE;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ for (tmp = oxu->async->qh_next.qh;
+ tmp && tmp != qh;
+ tmp = tmp->qh_next.qh)
+ continue;
+ /* periodic qh self-unlinks on empty */
+ if (!tmp)
+ goto nogood;
+ unlink_async(oxu, qh);
+ /* FALL THROUGH */
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+idle_timeout:
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ case QH_STATE_IDLE: /* fully unlinked */
+ if (list_empty(&qh->qtd_list)) {
+ qh_put(qh);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+nogood:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ oxu_err(oxu, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty(&qh->qtd_list) ? "" : "(has tds)");
+ break;
+ }
+ ep->hcpriv = NULL;
+done:
+ spin_unlock_irqrestore(&oxu->lock, flags);
+}
+
+static int oxu_get_frame(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+
+ return (readl(&oxu->regs->frame_index) >> 3) %
+ oxu->periodic_size;
+}
+
+/* Build "status change" packet (one or two bytes) from HC registers */
+static int oxu_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ u32 temp, mask, status = 0;
+ int ports, i, retval = 1;
+ unsigned long flags;
+
+ /* if !PM, root hub timers won't get shut down ... */
+ if (!HC_IS_RUNNING(hcd->state))
+ return 0;
+
+ /* init status to no-changes */
+ buf[0] = 0;
+ ports = HCS_N_PORTS(oxu->hcs_params);
+ if (ports > 7) {
+ buf[1] = 0;
+ retval++;
+ }
+
+ /* Some boards (mostly VIA?) report bogus overcurrent indications,
+ * causing massive log spam unless we completely ignore them. It
+ * may be relevant that VIA VT8235 controllers, where PORT_POWER is
+ * always set, seem to clear PORT_OCC and PORT_CSC when writing to
+ * PORT_POWER; that's surprising, but maybe within-spec.
+ */
+ if (!ignore_oc)
+ mask = PORT_CSC | PORT_PEC | PORT_OCC;
+ else
+ mask = PORT_CSC | PORT_PEC;
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave(&oxu->lock, flags);
+ for (i = 0; i < ports; i++) {
+ temp = readl(&oxu->regs->port_status[i]);
+
+ /*
+ * Return status information even for ports with OWNER set.
+ * Otherwise hub_wq wouldn't see the disconnect event when a
+ * high-speed device is switched over to the companion
+ * controller by the user.
+ */
+
+ if (!(temp & PORT_CONNECT))
+ oxu->reset_done[i] = 0;
+ if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 &&
+ time_after_eq(jiffies, oxu->reset_done[i]))) {
+ if (i < 7)
+ buf[0] |= 1 << (i + 1);
+ else
+ buf[1] |= 1 << (i - 7);
+ status = STS_PCD;
+ }
+ }
+ /* FIXME autosuspend idle root hubs */
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ return status ? retval : 0;
+}
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int oxu_port_speed(struct oxu_hcd *oxu,
+ unsigned int portsc)
+{
+ switch ((portsc >> 26) & 3) {
+ case 0:
+ return 0;
+ case 1:
+ return USB_PORT_STAT_LOW_SPEED;
+ case 2:
+ default:
+ return USB_PORT_STAT_HIGH_SPEED;
+ }
+}
+
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq,
+ u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int ports = HCS_N_PORTS(oxu->hcs_params);
+ u32 __iomem *status_reg = &oxu->regs->port_status[wIndex - 1];
+ u32 temp, status;
+ unsigned long flags;
+ int retval = 0;
+ unsigned selector;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave(&oxu->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl(status_reg);
+
+ /*
+ * Even if OWNER is set, so the port is owned by the
+ * companion controller, hub_wq needs to be able to clear
+ * the port-change status bits (especially
+ * USB_PORT_STAT_C_CONNECTION).
+ */
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ writel(temp & ~PORT_PE, status_reg);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ writel((temp & ~PORT_RWC_BITS) | PORT_PEC, status_reg);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (temp & PORT_RESET)
+ goto error;
+ if (temp & PORT_SUSPEND) {
+ if ((temp & PORT_PE) == 0)
+ goto error;
+ /* resume signaling for 20 msec */
+ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+ writel(temp | PORT_RESUME, status_reg);
+ oxu->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* we auto-clear this feature */
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC(oxu->hcs_params))
+ writel(temp & ~(PORT_RWC_BITS | PORT_POWER),
+ status_reg);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ writel((temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ writel((temp & ~PORT_RWC_BITS) | PORT_OCC, status_reg);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ readl(&oxu->regs->command); /* unblock posted write */
+ break;
+ case GetHubDescriptor:
+ ehci_hub_descriptor(oxu, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset(buf, 0, 4);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ status = 0;
+ temp = readl(status_reg);
+
+ /* wPortChange bits */
+ if (temp & PORT_CSC)
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
+ if (temp & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+ if ((temp & PORT_OCC) && !ignore_oc)
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+
+ /* whoever resumes must GetPortStatus to complete it!! */
+ if (temp & PORT_RESUME) {
+
+ /* Remote Wakeup received? */
+ if (!oxu->reset_done[wIndex]) {
+ /* resume signaling for 20 msec */
+ oxu->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ /* check the port again */
+ mod_timer(&oxu_to_hcd(oxu)->rh_timer,
+ oxu->reset_done[wIndex]);
+ }
+
+ /* resume completed? */
+ else if (time_after_eq(jiffies,
+ oxu->reset_done[wIndex])) {
+ status |= USB_PORT_STAT_C_SUSPEND << 16;
+ oxu->reset_done[wIndex] = 0;
+
+ /* stop resume signaling */
+ temp = readl(status_reg);
+ writel(temp & ~(PORT_RWC_BITS | PORT_RESUME),
+ status_reg);
+ retval = handshake(oxu, status_reg,
+ PORT_RESUME, 0, 2000 /* 2msec */);
+ if (retval != 0) {
+ oxu_err(oxu,
+ "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
+ }
+ }
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && time_after_eq(jiffies,
+ oxu->reset_done[wIndex])) {
+ status |= USB_PORT_STAT_C_RESET << 16;
+ oxu->reset_done[wIndex] = 0;
+
+ /* force reset to complete */
+ writel(temp & ~(PORT_RWC_BITS | PORT_RESET),
+ status_reg);
+ /* REVISIT: some hardware needs 550+ usec to clear
+ * this bit; seems too long to spin routinely...
+ */
+ retval = handshake(oxu, status_reg,
+ PORT_RESET, 0, 750);
+ if (retval != 0) {
+ oxu_err(oxu, "port %d reset error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+
+ /* see what we found out */
+ temp = check_reset_complete(oxu, wIndex, status_reg,
+ readl(status_reg));
+ }
+
+ /* transfer dedicated ports to the companion hc */
+ if ((temp & PORT_CONNECT) &&
+ test_bit(wIndex, &oxu->companion_ports)) {
+ temp &= ~PORT_RWC_BITS;
+ temp |= PORT_OWNER;
+ writel(temp, status_reg);
+ oxu_dbg(oxu, "port %d --> companion\n", wIndex + 1);
+ temp = readl(status_reg);
+ }
+
+ /*
+ * Even if OWNER is set, there's no harm letting hub_wq
+ * see the wPortStatus values (they should all be 0 except
+ * for PORT_POWER anyway).
+ */
+
+ if (temp & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ /* status may be from integrated TT */
+ status |= oxu_port_speed(oxu, temp);
+ }
+ if (temp & PORT_PE)
+ status |= USB_PORT_STAT_ENABLE;
+ if (temp & (PORT_SUSPEND|PORT_RESUME))
+ status |= USB_PORT_STAT_SUSPEND;
+ if (temp & PORT_OC)
+ status |= USB_PORT_STAT_OVERCURRENT;
+ if (temp & PORT_RESET)
+ status |= USB_PORT_STAT_RESET;
+ if (temp & PORT_POWER)
+ status |= USB_PORT_STAT_POWER;
+
+#ifndef OXU_VERBOSE_DEBUG
+ if (status & ~0xffff) /* only if wPortChange is interesting */
+#endif
+ dbg_port(oxu, "GetStatus", wIndex + 1, temp);
+ put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ selector = wIndex >> 8;
+ wIndex &= 0xff;
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl(status_reg);
+ if (temp & PORT_OWNER)
+ break;
+
+ temp &= ~PORT_RWC_BITS;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_RESET) != 0)
+ goto error;
+ if (device_may_wakeup(&hcd->self.root_hub->dev))
+ temp |= PORT_WAKE_BITS;
+ writel(temp | PORT_SUSPEND, status_reg);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC(oxu->hcs_params))
+ writel(temp | PORT_POWER, status_reg);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (temp & PORT_RESUME)
+ goto error;
+ /* line status bits may report this as low speed,
+ * which can be fine if this root hub has a
+ * transaction translator built in.
+ */
+ oxu_vdbg(oxu, "port %d reset\n", wIndex + 1);
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ oxu->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(50);
+ writel(temp, status_reg);
+ break;
+
+ /* For downstream facing ports (these): one hub port is put
+ * into test mode according to USB2 11.24.2.13, then the hub
+ * must be reset (which for root hub now means rmmod+modprobe,
+ * or else system reboot). See EHCI 2.3.9 and 4.14 for info
+ * about the EHCI-specific stuff.
+ */
+ case USB_PORT_FEAT_TEST:
+ if (!selector || selector > 5)
+ goto error;
+ ehci_quiesce(oxu);
+ ehci_halt(oxu);
+ temp |= selector << 16;
+ writel(temp, status_reg);
+ break;
+
+ default:
+ goto error;
+ }
+ readl(&oxu->regs->command); /* unblock posted writes */
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore(&oxu->lock, flags);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int oxu_bus_suspend(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ int port;
+ int mask;
+
+ oxu_dbg(oxu, "suspend root hub\n");
+
+ if (time_before(jiffies, oxu->next_statechange))
+ msleep(5);
+
+ port = HCS_N_PORTS(oxu->hcs_params);
+ spin_lock_irq(&oxu->lock);
+
+ /* stop schedules, clean any completed work */
+ if (HC_IS_RUNNING(hcd->state)) {
+ ehci_quiesce(oxu);
+ hcd->state = HC_STATE_QUIESCING;
+ }
+ oxu->command = readl(&oxu->regs->command);
+ if (oxu->reclaim)
+ oxu->reclaim_ready = 1;
+ ehci_work(oxu);
+
+ /* Unlike other USB host controller types, EHCI doesn't have
+ * any notion of "global" or bus-wide suspend. The driver has
+ * to manually suspend all the active unsuspended ports, and
+ * then manually resume them in the bus_resume() routine.
+ */
+ oxu->bus_suspended = 0;
+ while (port--) {
+ u32 __iomem *reg = &oxu->regs->port_status[port];
+ u32 t1 = readl(reg) & ~PORT_RWC_BITS;
+ u32 t2 = t1;
+
+ /* keep track of which ports we suspend */
+ if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) &&
+ !(t1 & PORT_SUSPEND)) {
+ t2 |= PORT_SUSPEND;
+ set_bit(port, &oxu->bus_suspended);
+ }
+
+ /* enable remote wakeup on all ports */
+ if (device_may_wakeup(&hcd->self.root_hub->dev))
+ t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
+ else
+ t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+
+ if (t1 != t2) {
+ oxu_vdbg(oxu, "port %d, %08x -> %08x\n",
+ port + 1, t1, t2);
+ writel(t2, reg);
+ }
+ }
+
+ /* turn off now-idle HC */
+ del_timer_sync(&oxu->watchdog);
+ ehci_halt(oxu);
+ hcd->state = HC_STATE_SUSPENDED;
+
+ /* allow remote wakeup */
+ mask = INTR_MASK;
+ if (!device_may_wakeup(&hcd->self.root_hub->dev))
+ mask &= ~STS_PCD;
+ writel(mask, &oxu->regs->intr_enable);
+ readl(&oxu->regs->intr_enable);
+
+ oxu->next_statechange = jiffies + msecs_to_jiffies(10);
+ spin_unlock_irq(&oxu->lock);
+ return 0;
+}
+
+/* Caller has locked the root hub, and should reset/reinit on error */
+static int oxu_bus_resume(struct usb_hcd *hcd)
+{
+ struct oxu_hcd *oxu = hcd_to_oxu(hcd);
+ u32 temp;
+ int i;
+
+ if (time_before(jiffies, oxu->next_statechange))
+ msleep(5);
+ spin_lock_irq(&oxu->lock);
+
+ /* Ideally and we've got a real resume here, and no port's power
+ * was lost. (For PCI, that means Vaux was maintained.) But we
+ * could instead be restoring a swsusp snapshot -- so that BIOS was
+ * the last user of the controller, not reset/pm hardware keeping
+ * state we gave to it.
+ */
+ temp = readl(&oxu->regs->intr_enable);
+ oxu_dbg(oxu, "resume root hub%s\n", temp ? "" : " after power loss");
+
+ /* at least some APM implementations will try to deliver
+ * IRQs right away, so delay them until we're ready.
+ */
+ writel(0, &oxu->regs->intr_enable);
+
+ /* re-init operational registers */
+ writel(0, &oxu->regs->segment);
+ writel(oxu->periodic_dma, &oxu->regs->frame_list);
+ writel((u32) oxu->async->qh_dma, &oxu->regs->async_next);
+
+ /* restore CMD_RUN, framelist size, and irq threshold */
+ writel(oxu->command, &oxu->regs->command);
+
+ /* Some controller/firmware combinations need a delay during which
+ * they set up the port statuses. See Bugzilla #8190. */
+ mdelay(8);
+
+ /* manually resume the ports we suspended during bus_suspend() */
+ i = HCS_N_PORTS(oxu->hcs_params);
+ while (i--) {
+ temp = readl(&oxu->regs->port_status[i]);
+ temp &= ~(PORT_RWC_BITS
+ | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
+ if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) {
+ oxu->reset_done[i] = jiffies + msecs_to_jiffies(20);
+ temp |= PORT_RESUME;
+ }
+ writel(temp, &oxu->regs->port_status[i]);
+ }
+ i = HCS_N_PORTS(oxu->hcs_params);
+ mdelay(20);
+ while (i--) {
+ temp = readl(&oxu->regs->port_status[i]);
+ if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) {
+ temp &= ~(PORT_RWC_BITS | PORT_RESUME);
+ writel(temp, &oxu->regs->port_status[i]);
+ oxu_vdbg(oxu, "resumed port %d\n", i + 1);
+ }
+ }
+ (void) readl(&oxu->regs->command);
+
+ /* maybe re-activate the schedule(s) */
+ temp = 0;
+ if (oxu->async->qh_next.qh)
+ temp |= CMD_ASE;
+ if (oxu->periodic_sched)
+ temp |= CMD_PSE;
+ if (temp) {
+ oxu->command |= temp;
+ writel(oxu->command, &oxu->regs->command);
+ }
+
+ oxu->next_statechange = jiffies + msecs_to_jiffies(5);
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Now we can safely re-enable irqs */
+ writel(INTR_MASK, &oxu->regs->intr_enable);
+
+ spin_unlock_irq(&oxu->lock);
+ return 0;
+}
+
+#else
+
+static int oxu_bus_suspend(struct usb_hcd *hcd)
+{
+ return 0;
+}
+
+static int oxu_bus_resume(struct usb_hcd *hcd)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static const struct hc_driver oxu_hc_driver = {
+ .description = "oxu210hp_hcd",
+ .product_desc = "oxu210hp HCD",
+ .hcd_priv_size = sizeof(struct oxu_hcd),
+
+ /*
+ * Generic hardware linkage
+ */
+ .irq = oxu_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * Basic lifecycle operations
+ */
+ .reset = oxu_reset,
+ .start = oxu_run,
+ .stop = oxu_stop,
+ .shutdown = oxu_shutdown,
+
+ /*
+ * Managing i/o requests and associated device resources
+ */
+ .urb_enqueue = oxu_urb_enqueue,
+ .urb_dequeue = oxu_urb_dequeue,
+ .endpoint_disable = oxu_endpoint_disable,
+
+ /*
+ * Scheduling support
+ */
+ .get_frame_number = oxu_get_frame,
+
+ /*
+ * Root hub support
+ */
+ .hub_status_data = oxu_hub_status_data,
+ .hub_control = oxu_hub_control,
+ .bus_suspend = oxu_bus_suspend,
+ .bus_resume = oxu_bus_resume,
+};
+
+/*
+ * Module stuff
+ */
+
+static void oxu_configuration(struct platform_device *pdev, void *base)
+{
+ u32 tmp;
+
+ /* Initialize top level registers.
+ * First write ever
+ */
+ oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D);
+ oxu_writel(base, OXU_SOFTRESET, OXU_SRESET);
+ oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D);
+
+ tmp = oxu_readl(base, OXU_PIOBURSTREADCTRL);
+ oxu_writel(base, OXU_PIOBURSTREADCTRL, tmp | 0x0040);
+
+ oxu_writel(base, OXU_ASO, OXU_SPHPOEN | OXU_OVRCCURPUPDEN |
+ OXU_COMPARATOR | OXU_ASO_OP);
+
+ tmp = oxu_readl(base, OXU_CLKCTRL_SET);
+ oxu_writel(base, OXU_CLKCTRL_SET, tmp | OXU_SYSCLKEN | OXU_USBOTGCLKEN);
+
+ /* Clear all top interrupt enable */
+ oxu_writel(base, OXU_CHIPIRQEN_CLR, 0xff);
+
+ /* Clear all top interrupt status */
+ oxu_writel(base, OXU_CHIPIRQSTATUS, 0xff);
+
+ /* Enable all needed top interrupt except OTG SPH core */
+ oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI);
+}
+
+static int oxu_verify_id(struct platform_device *pdev, void *base)
+{
+ u32 id;
+ static const char * const bo[] = {
+ "reserved",
+ "128-pin LQFP",
+ "84-pin TFBGA",
+ "reserved",
+ };
+
+ /* Read controller signature register to find a match */
+ id = oxu_readl(base, OXU_DEVICEID);
+ dev_info(&pdev->dev, "device ID %x\n", id);
+ if ((id & OXU_REV_MASK) != (OXU_REV_2100 << OXU_REV_SHIFT))
+ return -1;
+
+ dev_info(&pdev->dev, "found device %x %s (%04x:%04x)\n",
+ id >> OXU_REV_SHIFT,
+ bo[(id & OXU_BO_MASK) >> OXU_BO_SHIFT],
+ (id & OXU_MAJ_REV_MASK) >> OXU_MAJ_REV_SHIFT,
+ (id & OXU_MIN_REV_MASK) >> OXU_MIN_REV_SHIFT);
+
+ return 0;
+}
+
+static const struct hc_driver oxu_hc_driver;
+static struct usb_hcd *oxu_create(struct platform_device *pdev,
+ unsigned long memstart, unsigned long memlen,
+ void *base, int irq, int otg)
+{
+ struct device *dev = &pdev->dev;
+
+ struct usb_hcd *hcd;
+ struct oxu_hcd *oxu;
+ int ret;
+
+ /* Set endian mode and host mode */
+ oxu_writel(base + (otg ? OXU_OTG_CORE_OFFSET : OXU_SPH_CORE_OFFSET),
+ OXU_USBMODE,
+ OXU_CM_HOST_ONLY | OXU_ES_LITTLE | OXU_VBPS);
+
+ hcd = usb_create_hcd(&oxu_hc_driver, dev,
+ otg ? "oxu210hp_otg" : "oxu210hp_sph");
+ if (!hcd)
+ return ERR_PTR(-ENOMEM);
+
+ hcd->rsrc_start = memstart;
+ hcd->rsrc_len = memlen;
+ hcd->regs = base;
+ hcd->irq = irq;
+ hcd->state = HC_STATE_HALT;
+
+ oxu = hcd_to_oxu(hcd);
+ oxu->is_otg = otg;
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ device_wakeup_enable(hcd->self.controller);
+ return hcd;
+}
+
+static int oxu_init(struct platform_device *pdev,
+ unsigned long memstart, unsigned long memlen,
+ void *base, int irq)
+{
+ struct oxu_info *info = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd;
+ int ret;
+
+ /* First time configuration at start up */
+ oxu_configuration(pdev, base);
+
+ ret = oxu_verify_id(pdev, base);
+ if (ret) {
+ dev_err(&pdev->dev, "no devices found!\n");
+ return -ENODEV;
+ }
+
+ /* Create the OTG controller */
+ hcd = oxu_create(pdev, memstart, memlen, base, irq, 1);
+ if (IS_ERR(hcd)) {
+ dev_err(&pdev->dev, "cannot create OTG controller!\n");
+ ret = PTR_ERR(hcd);
+ goto error_create_otg;
+ }
+ info->hcd[0] = hcd;
+
+ /* Create the SPH host controller */
+ hcd = oxu_create(pdev, memstart, memlen, base, irq, 0);
+ if (IS_ERR(hcd)) {
+ dev_err(&pdev->dev, "cannot create SPH controller!\n");
+ ret = PTR_ERR(hcd);
+ goto error_create_sph;
+ }
+ info->hcd[1] = hcd;
+
+ oxu_writel(base, OXU_CHIPIRQEN_SET,
+ oxu_readl(base, OXU_CHIPIRQEN_SET) | 3);
+
+ return 0;
+
+error_create_sph:
+ usb_remove_hcd(info->hcd[0]);
+ usb_put_hcd(info->hcd[0]);
+
+error_create_otg:
+ return ret;
+}
+
+static int oxu_drv_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void *base;
+ unsigned long memstart, memlen;
+ int irq, ret;
+ struct oxu_info *info;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Get the platform resources
+ */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "no IRQ! Check %s setup!\n", dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+ irq = res->start;
+ dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto error;
+ }
+ memstart = res->start;
+ memlen = resource_size(res);
+
+ ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING);
+ if (ret) {
+ dev_err(&pdev->dev, "error setting irq type\n");
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* Allocate a driver data struct to hold useful info for both
+ * SPH & OTG devices
+ */
+ info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL);
+ if (!info) {
+ ret = -EFAULT;
+ goto error;
+ }
+ platform_set_drvdata(pdev, info);
+
+ ret = oxu_init(pdev, memstart, memlen, base, irq);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "cannot init USB devices\n");
+ goto error;
+ }
+
+ dev_info(&pdev->dev, "devices enabled and running\n");
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+
+error:
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
+ return ret;
+}
+
+static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd)
+{
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
+
+static int oxu_drv_remove(struct platform_device *pdev)
+{
+ struct oxu_info *info = platform_get_drvdata(pdev);
+
+ oxu_remove(pdev, info->hcd[0]);
+ oxu_remove(pdev, info->hcd[1]);
+
+ return 0;
+}
+
+static void oxu_drv_shutdown(struct platform_device *pdev)
+{
+ oxu_drv_remove(pdev);
+}
+
+#if 0
+/* FIXME: TODO */
+static int oxu_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+
+static int oxu_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+#else
+#define oxu_drv_suspend NULL
+#define oxu_drv_resume NULL
+#endif
+
+static struct platform_driver oxu_driver = {
+ .probe = oxu_drv_probe,
+ .remove = oxu_drv_remove,
+ .shutdown = oxu_drv_shutdown,
+ .suspend = oxu_drv_suspend,
+ .resume = oxu_drv_resume,
+ .driver = {
+ .name = "oxu210hp-hcd",
+ .bus = &platform_bus_type
+ }
+};
+
+module_platform_driver(oxu_driver);
+
+MODULE_DESCRIPTION("Oxford OXU210HP HCD driver - ver. " DRIVER_VERSION);
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h
new file mode 100644
index 000000000..1c216ad9a
--- /dev/null
+++ b/drivers/usb/host/oxu210hp.h
@@ -0,0 +1,447 @@
+/*
+ * Host interface registers
+ */
+
+#define OXU_DEVICEID 0x00
+ #define OXU_REV_MASK 0xffff0000
+ #define OXU_REV_SHIFT 16
+ #define OXU_REV_2100 0x2100
+ #define OXU_BO_SHIFT 8
+ #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT)
+ #define OXU_MAJ_REV_SHIFT 4
+ #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT)
+ #define OXU_MIN_REV_SHIFT 0
+ #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT)
+#define OXU_HOSTIFCONFIG 0x04
+#define OXU_SOFTRESET 0x08
+ #define OXU_SRESET (1 << 0)
+
+#define OXU_PIOBURSTREADCTRL 0x0C
+
+#define OXU_CHIPIRQSTATUS 0x10
+#define OXU_CHIPIRQEN_SET 0x14
+#define OXU_CHIPIRQEN_CLR 0x18
+ #define OXU_USBSPHLPWUI 0x00000080
+ #define OXU_USBOTGLPWUI 0x00000040
+ #define OXU_USBSPHI 0x00000002
+ #define OXU_USBOTGI 0x00000001
+
+#define OXU_CLKCTRL_SET 0x1C
+ #define OXU_SYSCLKEN 0x00000008
+ #define OXU_USBSPHCLKEN 0x00000002
+ #define OXU_USBOTGCLKEN 0x00000001
+
+#define OXU_ASO 0x68
+ #define OXU_SPHPOEN 0x00000100
+ #define OXU_OVRCCURPUPDEN 0x00000800
+ #define OXU_ASO_OP (1 << 10)
+ #define OXU_COMPARATOR 0x000004000
+
+#define OXU_USBMODE 0x1A8
+ #define OXU_VBPS 0x00000020
+ #define OXU_ES_LITTLE 0x00000000
+ #define OXU_CM_HOST_ONLY 0x00000003
+
+/*
+ * Proper EHCI structs & defines
+ */
+
+/* Magic numbers that can affect system performance */
+#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
+#define EHCI_TUNE_RL_TT 0
+#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define EHCI_TUNE_MULT_TT 1
+#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
+
+struct oxu_hcd;
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
+#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+ u8 portroute[8]; /* nibbles for routing - offset 0xC */
+} __attribute__ ((packed));
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+ /* USBCMD: offset 0x00 */
+ u32 command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved[9];
+
+ /* CONFIGFLAG: offset 0x40 */
+ u32 configured_flag;
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+ /* PORTSC: offset 0x44 */
+ u32 port_status[0]; /* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+#define PORT_LED_OFF (0<<14)
+#define PORT_LED_AMBER (1<<14)
+#define PORT_LED_GREEN (2<<14)
+#define PORT_LED_MASK (3<<14)
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
+} __attribute__ ((packed));
+
+/* Appendix C, Debug port ... intended for use with special "debug devices"
+ * that can help if there's no serial console. (nonstandard enumeration.)
+ */
+struct ehci_dbg_port {
+ u32 control;
+#define DBGP_OWNER (1<<30)
+#define DBGP_ENABLED (1<<28)
+#define DBGP_DONE (1<<16)
+#define DBGP_INUSE (1<<10)
+#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
+# define DBGP_ERR_BAD 1
+# define DBGP_ERR_SIGNAL 2
+#define DBGP_ERROR (1<<6)
+#define DBGP_GO (1<<5)
+#define DBGP_OUT (1<<4)
+#define DBGP_LEN(x) (((x)>>0)&0x0f)
+ u32 pids;
+#define DBGP_PID_GET(x) (((x)>>16)&0xff)
+#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
+ u32 data03;
+ u32 data47;
+ u32 address;
+#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
+} __attribute__ ((packed));
+
+
+#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+ /* first part defined by EHCI spec */
+ __le32 hw_next; /* see EHCI 3.5.1 */
+ __le32 hw_alt_next; /* see EHCI 3.5.2 */
+ __le32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+ __le32 hw_buf[5]; /* see EHCI 3.5.4 */
+ __le32 hw_buf_hi[5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+
+ u32 qtd_buffer_len;
+ void *buffer;
+ dma_addr_t buffer_dma;
+ void *transfer_buffer;
+ void *transfer_dma;
+} __attribute__ ((aligned(32)));
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK cpu_to_le32 (~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
+
+/* Type tag from {qh, itd, sitd, fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_QH cpu_to_le32 (1 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+ struct ehci_qh *qh; /* Q_TYPE_QH */
+ __le32 *hw_next; /* (all types) */
+ void *ptr;
+};
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+struct ehci_qh {
+ /* first part defined by EHCI spec */
+ __le32 hw_next; /* see EHCI 3.6.1 */
+ __le32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_HEAD 0x00008000
+ __le32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_SMASK 0x000000ff
+#define QH_CMASK 0x0000ff00
+#define QH_HUBADDR 0x007f0000
+#define QH_HUBPORT 0x3f800000
+#define QH_MULT 0xc0000000
+ __le32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ __le32 hw_qtd_next;
+ __le32 hw_alt_next;
+ __le32 hw_token;
+ __le32 hw_buf[5];
+ __le32 hw_buf_hi[5];
+
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union ehci_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+ struct ehci_qtd *dummy;
+ struct ehci_qh *reclaim; /* next to reclaim */
+
+ struct oxu_hcd *oxu;
+ struct kref kref;
+ unsigned stamp;
+
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ /* periodic schedule info */
+ u8 usecs; /* intr bandwidth */
+ u8 gap_uf; /* uframes split/csplit gap */
+ u8 c_usecs; /* ... split completion bw */
+ u16 tt_usecs; /* tt downstream bandwidth */
+ unsigned short period; /* polling interval */
+ unsigned short start; /* where polling starts */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+ struct usb_device *dev; /* access to TT */
+} __attribute__ ((aligned(32)));
+
+/*
+ * Proper OXU210HP structs
+ */
+
+#define OXU_OTG_CORE_OFFSET 0x00400
+#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100)
+#define OXU_SPH_CORE_OFFSET 0x00800
+#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100)
+
+#define OXU_OTG_MEM 0xE000
+#define OXU_SPH_MEM 0x16000
+
+/* Only how many elements & element structure are specifies here. */
+/* 2 host controllers are enabled - total size <= 28 kbytes */
+#define DEFAULT_I_TDPS 1024
+#define QHEAD_NUM 16
+#define QTD_NUM 32
+#define SITD_NUM 8
+#define MURB_NUM 8
+
+#define BUFFER_NUM 8
+#define BUFFER_SIZE 512
+
+struct oxu_info {
+ struct usb_hcd *hcd[2];
+};
+
+struct oxu_buf {
+ u8 buffer[BUFFER_SIZE];
+} __attribute__ ((aligned(BUFFER_SIZE)));
+
+struct oxu_onchip_mem {
+ struct oxu_buf db_pool[BUFFER_NUM];
+
+ u32 frame_list[DEFAULT_I_TDPS];
+ struct ehci_qh qh_pool[QHEAD_NUM];
+ struct ehci_qtd qtd_pool[QTD_NUM];
+} __attribute__ ((aligned(4 << 10)));
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+struct oxu_murb {
+ struct urb urb;
+ struct urb *main;
+ u8 last;
+};
+
+struct oxu_hcd { /* one per controller */
+ unsigned int is_otg:1;
+
+ u8 qh_used[QHEAD_NUM];
+ u8 qtd_used[QTD_NUM];
+ u8 db_used[BUFFER_NUM];
+ u8 murb_used[MURB_NUM];
+
+ struct oxu_onchip_mem __iomem *mem;
+ spinlock_t mem_lock;
+
+ struct timer_list urb_timer;
+
+ struct ehci_caps __iomem *caps;
+ struct ehci_regs __iomem *regs;
+
+ __u32 hcs_params; /* cached register copy */
+ spinlock_t lock;
+
+ /* async schedule support */
+ struct ehci_qh *async;
+ struct ehci_qh *reclaim;
+ unsigned reclaim_ready:1;
+ unsigned scanning:1;
+
+ /* periodic schedule support */
+ unsigned periodic_size;
+ __le32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union ehci_shadow *pshadow; /* mirror hw periodic table */
+ int next_uframe; /* scan periodic, start here */
+ unsigned periodic_sched; /* periodic activity count */
+
+ /* per root hub port */
+ unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
+ /* bit vectors (one bit per port) */
+ unsigned long bus_suspended; /* which ports were
+ * already suspended at the
+ * start of a bus suspend
+ */
+ unsigned long companion_ports;/* which ports are dedicated
+ * to the companion controller
+ */
+
+ struct timer_list watchdog;
+ unsigned long actions;
+ unsigned stamp;
+ unsigned long next_statechange;
+ u32 command;
+
+ /* SILICON QUIRKS */
+ struct list_head urb_list; /* this is the head to urb
+ * queue that didn't get enough
+ * resources
+ */
+ struct oxu_murb *murb_pool; /* murb per split big urb */
+ unsigned urb_len;
+
+ u8 sbrn; /* packed release number */
+};
+
+#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
+#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
+#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
+#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
+
+enum ehci_timer_action {
+ TIMER_IO_WATCHDOG,
+ TIMER_IAA_WATCHDOG,
+ TIMER_ASYNC_SHRINK,
+ TIMER_ASYNC_OFF,
+};
+
+#include <linux/oxu210hp.h>
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
new file mode 100644
index 000000000..f9400564c
--- /dev/null
+++ b/drivers/usb/host/pci-quirks.c
@@ -0,0 +1,1097 @@
+/*
+ * This file contains code to reset and initialize USB host controllers.
+ * Some of it includes work-arounds for PCI hardware and BIOS quirks.
+ * It may need to run early during booting -- before USB would normally
+ * initialize -- to ensure that Linux doesn't use any legacy modes.
+ *
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ * (and others)
+ */
+
+#include <linux/types.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include "pci-quirks.h"
+#include "xhci-ext-caps.h"
+
+
+#define UHCI_USBLEGSUP 0xc0 /* legacy support */
+#define UHCI_USBCMD 0 /* command register */
+#define UHCI_USBINTR 4 /* interrupt register */
+#define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
+#define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
+#define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */
+#define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */
+#define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */
+#define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+
+#define OHCI_CONTROL 0x04
+#define OHCI_CMDSTATUS 0x08
+#define OHCI_INTRSTATUS 0x0c
+#define OHCI_INTRENABLE 0x10
+#define OHCI_INTRDISABLE 0x14
+#define OHCI_FMINTERVAL 0x34
+#define OHCI_HCFS (3 << 6) /* hc functional state */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+
+#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
+#define EHCI_USBCMD 0 /* command register */
+#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
+#define EHCI_USBSTS 4 /* status register */
+#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
+#define EHCI_USBINTR 8 /* interrupt register */
+#define EHCI_CONFIGFLAG 0x40 /* configured flag register */
+#define EHCI_USBLEGSUP 0 /* legacy support register */
+#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
+#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
+#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
+#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
+
+/* AMD quirk use */
+#define AB_REG_BAR_LOW 0xe0
+#define AB_REG_BAR_HIGH 0xe1
+#define AB_REG_BAR_SB700 0xf0
+#define AB_INDX(addr) ((addr) + 0x00)
+#define AB_DATA(addr) ((addr) + 0x04)
+#define AX_INDXC 0x30
+#define AX_DATAC 0x34
+
+#define NB_PCIE_INDX_ADDR 0xe0
+#define NB_PCIE_INDX_DATA 0xe4
+#define PCIE_P_CNTL 0x10040
+#define BIF_NB 0x10002
+#define NB_PIF0_PWRDOWN_0 0x01100012
+#define NB_PIF0_PWRDOWN_1 0x01100013
+
+#define USB_INTEL_XUSB2PR 0xD0
+#define USB_INTEL_USB2PRM 0xD4
+#define USB_INTEL_USB3_PSSEN 0xD8
+#define USB_INTEL_USB3PRM 0xDC
+
+/*
+ * amd_chipset_gen values represent AMD different chipset generations
+ */
+enum amd_chipset_gen {
+ NOT_AMD_CHIPSET = 0,
+ AMD_CHIPSET_SB600,
+ AMD_CHIPSET_SB700,
+ AMD_CHIPSET_SB800,
+ AMD_CHIPSET_HUDSON2,
+ AMD_CHIPSET_BOLTON,
+ AMD_CHIPSET_YANGTZE,
+ AMD_CHIPSET_UNKNOWN,
+};
+
+struct amd_chipset_type {
+ enum amd_chipset_gen gen;
+ u8 rev;
+};
+
+static struct amd_chipset_info {
+ struct pci_dev *nb_dev;
+ struct pci_dev *smbus_dev;
+ int nb_type;
+ struct amd_chipset_type sb_type;
+ int isoc_reqs;
+ int probe_count;
+ int probe_result;
+} amd_chipset;
+
+static DEFINE_SPINLOCK(amd_lock);
+
+/*
+ * amd_chipset_sb_type_init - initialize amd chipset southbridge type
+ *
+ * AMD FCH/SB generation and revision is identified by SMBus controller
+ * vendor, device and revision IDs.
+ *
+ * Returns: 1 if it is an AMD chipset, 0 otherwise.
+ */
+static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo)
+{
+ u8 rev = 0;
+ pinfo->sb_type.gen = AMD_CHIPSET_UNKNOWN;
+
+ pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+ if (pinfo->smbus_dev) {
+ rev = pinfo->smbus_dev->revision;
+ if (rev >= 0x10 && rev <= 0x1f)
+ pinfo->sb_type.gen = AMD_CHIPSET_SB600;
+ else if (rev >= 0x30 && rev <= 0x3f)
+ pinfo->sb_type.gen = AMD_CHIPSET_SB700;
+ else if (rev >= 0x40 && rev <= 0x4f)
+ pinfo->sb_type.gen = AMD_CHIPSET_SB800;
+ } else {
+ pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+
+ if (!pinfo->smbus_dev) {
+ pinfo->sb_type.gen = NOT_AMD_CHIPSET;
+ return 0;
+ }
+
+ rev = pinfo->smbus_dev->revision;
+ if (rev >= 0x11 && rev <= 0x14)
+ pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
+ else if (rev >= 0x15 && rev <= 0x18)
+ pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
+ else if (rev >= 0x39 && rev <= 0x3a)
+ pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
+ }
+
+ pinfo->sb_type.rev = rev;
+ return 1;
+}
+
+void sb800_prefetch(struct device *dev, int on)
+{
+ u16 misc;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ pci_read_config_word(pdev, 0x50, &misc);
+ if (on == 0)
+ pci_write_config_word(pdev, 0x50, misc & 0xfcff);
+ else
+ pci_write_config_word(pdev, 0x50, misc | 0x0300);
+}
+EXPORT_SYMBOL_GPL(sb800_prefetch);
+
+int usb_amd_find_chipset_info(void)
+{
+ unsigned long flags;
+ struct amd_chipset_info info;
+ int ret;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ /* probe only once */
+ if (amd_chipset.probe_count > 0) {
+ amd_chipset.probe_count++;
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return amd_chipset.probe_result;
+ }
+ memset(&info, 0, sizeof(info));
+ spin_unlock_irqrestore(&amd_lock, flags);
+
+ if (!amd_chipset_sb_type_init(&info)) {
+ ret = 0;
+ goto commit;
+ }
+
+ /* Below chipset generations needn't enable AMD PLL quirk */
+ if (info.sb_type.gen == AMD_CHIPSET_UNKNOWN ||
+ info.sb_type.gen == AMD_CHIPSET_SB600 ||
+ info.sb_type.gen == AMD_CHIPSET_YANGTZE ||
+ (info.sb_type.gen == AMD_CHIPSET_SB700 &&
+ info.sb_type.rev > 0x3b)) {
+ if (info.smbus_dev) {
+ pci_dev_put(info.smbus_dev);
+ info.smbus_dev = NULL;
+ }
+ ret = 0;
+ goto commit;
+ }
+
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 1;
+ } else {
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 2;
+ } else {
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x9600, NULL);
+ if (info.nb_dev)
+ info.nb_type = 3;
+ }
+ }
+
+ ret = info.probe_result = 1;
+ printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
+
+commit:
+
+ spin_lock_irqsave(&amd_lock, flags);
+ if (amd_chipset.probe_count > 0) {
+ /* race - someone else was faster - drop devices */
+
+ /* Mark that we where here */
+ amd_chipset.probe_count++;
+ ret = amd_chipset.probe_result;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+
+ pci_dev_put(info.nb_dev);
+ pci_dev_put(info.smbus_dev);
+
+ } else {
+ /* no race - commit the result */
+ info.probe_count++;
+ amd_chipset = info;
+ spin_unlock_irqrestore(&amd_lock, flags);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
+
+int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev)
+{
+ /* Make sure amd chipset type has already been initialized */
+ usb_amd_find_chipset_info();
+ if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE)
+ return 0;
+
+ dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
+ return 1;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk);
+
+bool usb_amd_hang_symptom_quirk(void)
+{
+ u8 rev;
+
+ usb_amd_find_chipset_info();
+ rev = amd_chipset.sb_type.rev;
+ /* SB600 and old version of SB700 have hang symptom bug */
+ return amd_chipset.sb_type.gen == AMD_CHIPSET_SB600 ||
+ (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+ rev >= 0x3a && rev <= 0x3b);
+}
+EXPORT_SYMBOL_GPL(usb_amd_hang_symptom_quirk);
+
+bool usb_amd_prefetch_quirk(void)
+{
+ usb_amd_find_chipset_info();
+ /* SB800 needs pre-fetch fix */
+ return amd_chipset.sb_type.gen == AMD_CHIPSET_SB800;
+}
+EXPORT_SYMBOL_GPL(usb_amd_prefetch_quirk);
+
+/*
+ * The hardware normally enables the A-link power management feature, which
+ * lets the system lower the power consumption in idle states.
+ *
+ * This USB quirk prevents the link going into that lower power state
+ * during isochronous transfers.
+ *
+ * Without this quirk, isochronous stream on OHCI/EHCI/xHCI controllers of
+ * some AMD platforms may stutter or have breaks occasionally.
+ */
+static void usb_amd_quirk_pll(int disable)
+{
+ u32 addr, addr_low, addr_high, val;
+ u32 bit = disable ? 0 : 1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ if (disable) {
+ amd_chipset.isoc_reqs++;
+ if (amd_chipset.isoc_reqs > 1) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ } else {
+ amd_chipset.isoc_reqs--;
+ if (amd_chipset.isoc_reqs > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ }
+
+ if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB800 ||
+ amd_chipset.sb_type.gen == AMD_CHIPSET_HUDSON2 ||
+ amd_chipset.sb_type.gen == AMD_CHIPSET_BOLTON) {
+ outb_p(AB_REG_BAR_LOW, 0xcd6);
+ addr_low = inb_p(0xcd7);
+ outb_p(AB_REG_BAR_HIGH, 0xcd6);
+ addr_high = inb_p(0xcd7);
+ addr = addr_high << 8 | addr_low;
+
+ outl_p(0x30, AB_INDX(addr));
+ outl_p(0x40, AB_DATA(addr));
+ outl_p(0x34, AB_INDX(addr));
+ val = inl_p(AB_DATA(addr));
+ } else if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+ amd_chipset.sb_type.rev <= 0x3b) {
+ pci_read_config_dword(amd_chipset.smbus_dev,
+ AB_REG_BAR_SB700, &addr);
+ outl(AX_INDXC, AB_INDX(addr));
+ outl(0x40, AB_DATA(addr));
+ outl(AX_DATAC, AB_INDX(addr));
+ val = inl(AB_DATA(addr));
+ } else {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (disable) {
+ val &= ~0x08;
+ val |= (1 << 4) | (1 << 9);
+ } else {
+ val |= 0x08;
+ val &= ~((1 << 4) | (1 << 9));
+ }
+ outl_p(val, AB_DATA(addr));
+
+ if (!amd_chipset.nb_dev) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (amd_chipset.nb_type == 1 || amd_chipset.nb_type == 3) {
+ addr = PCIE_P_CNTL;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+
+ val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
+ val |= bit | (bit << 3) | (bit << 12);
+ val |= ((!bit) << 4) | ((!bit) << 9);
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = BIF_NB;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ val &= ~(1 << 8);
+ val |= bit << 8;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ } else if (amd_chipset.nb_type == 2) {
+ addr = NB_PIF0_PWRDOWN_0;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = NB_PIF0_PWRDOWN_1;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ }
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+}
+
+void usb_amd_quirk_pll_disable(void)
+{
+ usb_amd_quirk_pll(1);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);
+
+void usb_amd_quirk_pll_enable(void)
+{
+ usb_amd_quirk_pll(0);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);
+
+void usb_amd_dev_put(void)
+{
+ struct pci_dev *nb, *smbus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ amd_chipset.probe_count--;
+ if (amd_chipset.probe_count > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ /* save them to pci_dev_put outside of spinlock */
+ nb = amd_chipset.nb_dev;
+ smbus = amd_chipset.smbus_dev;
+
+ amd_chipset.nb_dev = NULL;
+ amd_chipset.smbus_dev = NULL;
+ amd_chipset.nb_type = 0;
+ memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type));
+ amd_chipset.isoc_reqs = 0;
+ amd_chipset.probe_result = 0;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+
+ pci_dev_put(nb);
+ pci_dev_put(smbus);
+}
+EXPORT_SYMBOL_GPL(usb_amd_dev_put);
+
+/*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+ */
+void uhci_reset_hc(struct pci_dev *pdev, unsigned long base)
+{
+ /* Turn off PIRQ enable and SMI enable. (This also turns off the
+ * BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
+ */
+ pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC);
+
+ /* Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD);
+ mb();
+ udelay(5);
+ if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET)
+ dev_warn(&pdev->dev, "HCRESET not completed yet!\n");
+
+ /* Just to be safe, disable interrupt requests and
+ * make sure the controller is stopped.
+ */
+ outw(0, base + UHCI_USBINTR);
+ outw(0, base + UHCI_USBCMD);
+}
+EXPORT_SYMBOL_GPL(uhci_reset_hc);
+
+/*
+ * Initialize a controller that was newly discovered or has just been
+ * resumed. In either case we can't be sure of its previous state.
+ *
+ * Returns: 1 if the controller was reset, 0 otherwise.
+ */
+int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
+{
+ u16 legsup;
+ unsigned int cmd, intr;
+
+ /*
+ * When restarting a suspended controller, we expect all the
+ * settings to be the same as we left them:
+ *
+ * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
+ * Controller is stopped and configured with EGSM set;
+ * No interrupts enabled except possibly Resume Detect.
+ *
+ * If any of these conditions are violated we do a complete reset.
+ */
+ pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
+ if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
+ dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n",
+ __func__, legsup);
+ goto reset_needed;
+ }
+
+ cmd = inw(base + UHCI_USBCMD);
+ if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
+ !(cmd & UHCI_USBCMD_EGSM)) {
+ dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n",
+ __func__, cmd);
+ goto reset_needed;
+ }
+
+ intr = inw(base + UHCI_USBINTR);
+ if (intr & (~UHCI_USBINTR_RESUME)) {
+ dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n",
+ __func__, intr);
+ goto reset_needed;
+ }
+ return 0;
+
+reset_needed:
+ dev_dbg(&pdev->dev, "Performing full reset\n");
+ uhci_reset_hc(pdev, base);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
+
+static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
+{
+ u16 cmd;
+ return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask);
+}
+
+#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO)
+#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY)
+
+static void quirk_usb_handoff_uhci(struct pci_dev *pdev)
+{
+ unsigned long base = 0;
+ int i;
+
+ if (!pio_enabled(pdev))
+ return;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
+ base = pci_resource_start(pdev, i);
+ break;
+ }
+
+ if (base)
+ uhci_check_and_reset_hc(pdev, base);
+}
+
+static int mmio_resource_enabled(struct pci_dev *pdev, int idx)
+{
+ return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
+}
+
+static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
+{
+ void __iomem *base;
+ u32 control;
+ u32 fminterval = 0;
+ bool no_fminterval = false;
+ int cnt;
+
+ if (!mmio_resource_enabled(pdev, 0))
+ return;
+
+ base = pci_ioremap_bar(pdev, 0);
+ if (base == NULL)
+ return;
+
+ /*
+ * ULi M5237 OHCI controller locks the whole system when accessing
+ * the OHCI_FMINTERVAL offset.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_AL && pdev->device == 0x5237)
+ no_fminterval = true;
+
+ control = readl(base + OHCI_CONTROL);
+
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#ifdef __hppa__
+#define OHCI_CTRL_MASK (OHCI_CTRL_RWC | OHCI_CTRL_IR)
+#else
+#define OHCI_CTRL_MASK OHCI_CTRL_RWC
+
+ if (control & OHCI_CTRL_IR) {
+ int wait_time = 500; /* arbitrary; 5 seconds */
+ writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
+ writel(OHCI_OCR, base + OHCI_CMDSTATUS);
+ while (wait_time > 0 &&
+ readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
+ wait_time -= 10;
+ msleep(10);
+ }
+ if (wait_time <= 0)
+ dev_warn(&pdev->dev,
+ "OHCI: BIOS handoff failed (BIOS bug?) %08x\n",
+ readl(base + OHCI_CONTROL));
+ }
+#endif
+
+ /* disable interrupts */
+ writel((u32) ~0, base + OHCI_INTRDISABLE);
+
+ /* Reset the USB bus, if the controller isn't already in RESET */
+ if (control & OHCI_HCFS) {
+ /* Go into RESET, preserving RWC (and possibly IR) */
+ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
+ readl(base + OHCI_CONTROL);
+
+ /* drive bus reset for at least 50 ms (7.1.7.5) */
+ msleep(50);
+ }
+
+ /* software reset of the controller, preserving HcFmInterval */
+ if (!no_fminterval)
+ fminterval = readl(base + OHCI_FMINTERVAL);
+
+ writel(OHCI_HCR, base + OHCI_CMDSTATUS);
+
+ /* reset requires max 10 us delay */
+ for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */
+ if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0)
+ break;
+ udelay(1);
+ }
+
+ if (!no_fminterval)
+ writel(fminterval, base + OHCI_FMINTERVAL);
+
+ /* Now the controller is safely in SUSPEND and nothing can wake it up */
+ iounmap(base);
+}
+
+static const struct dmi_system_id ehci_dmi_nohandoff_table[] = {
+ {
+ /* Pegatron Lucid (ExoPC) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "EXOPG06411"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-CE-133"),
+ },
+ },
+ {
+ /* Pegatron Lucid (Ordissimo AIRIS) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "M11JB"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
+ },
+ },
+ {
+ /* Pegatron Lucid (Ordissimo) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "Ordissimo"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
+ },
+ },
+ {
+ /* HASEE E200 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HASEE"),
+ DMI_MATCH(DMI_BOARD_NAME, "E210"),
+ DMI_MATCH(DMI_BIOS_VERSION, "6.00"),
+ },
+ },
+ { }
+};
+
+static void ehci_bios_handoff(struct pci_dev *pdev,
+ void __iomem *op_reg_base,
+ u32 cap, u8 offset)
+{
+ int try_handoff = 1, tried_handoff = 0;
+
+ /*
+ * The Pegatron Lucid tablet sporadically waits for 98 seconds trying
+ * the handoff on its unused controller. Skip it.
+ *
+ * The HASEE E200 hangs when the semaphore is set (bugzilla #77021).
+ */
+ if (pdev->vendor == 0x8086 && (pdev->device == 0x283a ||
+ pdev->device == 0x27cc)) {
+ if (dmi_check_system(ehci_dmi_nohandoff_table))
+ try_handoff = 0;
+ }
+
+ if (try_handoff && (cap & EHCI_USBLEGSUP_BIOS)) {
+ dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n");
+
+#if 0
+/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on,
+ * but that seems dubious in general (the BIOS left it off intentionally)
+ * and is known to prevent some systems from booting. so we won't do this
+ * unless maybe we can determine when we're on a system that needs SMI forced.
+ */
+ /* BIOS workaround (?): be sure the pre-Linux code
+ * receives the SMI
+ */
+ pci_read_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, &val);
+ pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS,
+ val | EHCI_USBLEGCTLSTS_SOOE);
+#endif
+
+ /* some systems get upset if this semaphore is
+ * set for any other reason than forcing a BIOS
+ * handoff..
+ */
+ pci_write_config_byte(pdev, offset + 3, 1);
+ }
+
+ /* if boot firmware now owns EHCI, spin till it hands it over. */
+ if (try_handoff) {
+ int msec = 1000;
+ while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
+ tried_handoff = 1;
+ msleep(10);
+ msec -= 10;
+ pci_read_config_dword(pdev, offset, &cap);
+ }
+ }
+
+ if (cap & EHCI_USBLEGSUP_BIOS) {
+ /* well, possibly buggy BIOS... try to shut it down,
+ * and hope nothing goes too wrong
+ */
+ if (try_handoff)
+ dev_warn(&pdev->dev,
+ "EHCI: BIOS handoff failed (BIOS bug?) %08x\n",
+ cap);
+ pci_write_config_byte(pdev, offset + 2, 0);
+ }
+
+ /* just in case, always disable EHCI SMIs */
+ pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, 0);
+
+ /* If the BIOS ever owned the controller then we can't expect
+ * any power sessions to remain intact.
+ */
+ if (tried_handoff)
+ writel(0, op_reg_base + EHCI_CONFIGFLAG);
+}
+
+static void quirk_usb_disable_ehci(struct pci_dev *pdev)
+{
+ void __iomem *base, *op_reg_base;
+ u32 hcc_params, cap, val;
+ u8 offset, cap_length;
+ int wait_time, count = 256/4;
+
+ if (!mmio_resource_enabled(pdev, 0))
+ return;
+
+ base = pci_ioremap_bar(pdev, 0);
+ if (base == NULL)
+ return;
+
+ cap_length = readb(base);
+ op_reg_base = base + cap_length;
+
+ /* EHCI 0.96 and later may have "extended capabilities"
+ * spec section 5.1 explains the bios handoff, e.g. for
+ * booting from USB disk or using a usb keyboard
+ */
+ hcc_params = readl(base + EHCI_HCC_PARAMS);
+ offset = (hcc_params >> 8) & 0xff;
+ while (offset && --count) {
+ pci_read_config_dword(pdev, offset, &cap);
+
+ switch (cap & 0xff) {
+ case 1:
+ ehci_bios_handoff(pdev, op_reg_base, cap, offset);
+ break;
+ case 0: /* Illegal reserved cap, set cap=0 so we exit */
+ cap = 0; /* then fallthrough... */
+ default:
+ dev_warn(&pdev->dev,
+ "EHCI: unrecognized capability %02x\n",
+ cap & 0xff);
+ }
+ offset = (cap >> 8) & 0xff;
+ }
+ if (!count)
+ dev_printk(KERN_DEBUG, &pdev->dev, "EHCI: capability loop?\n");
+
+ /*
+ * halt EHCI & disable its interrupts in any case
+ */
+ val = readl(op_reg_base + EHCI_USBSTS);
+ if ((val & EHCI_USBSTS_HALTED) == 0) {
+ val = readl(op_reg_base + EHCI_USBCMD);
+ val &= ~EHCI_USBCMD_RUN;
+ writel(val, op_reg_base + EHCI_USBCMD);
+
+ wait_time = 2000;
+ do {
+ writel(0x3f, op_reg_base + EHCI_USBSTS);
+ udelay(100);
+ wait_time -= 100;
+ val = readl(op_reg_base + EHCI_USBSTS);
+ if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) {
+ break;
+ }
+ } while (wait_time > 0);
+ }
+ writel(0, op_reg_base + EHCI_USBINTR);
+ writel(0x3f, op_reg_base + EHCI_USBSTS);
+
+ iounmap(base);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Intel's Panther Point chipset has two host controllers (EHCI and xHCI) that
+ * share some number of ports. These ports can be switched between either
+ * controller. Not all of the ports under the EHCI host controller may be
+ * switchable.
+ *
+ * The ports should be switched over to xHCI before PCI probes for any device
+ * start. This avoids active devices under EHCI being disconnected during the
+ * port switchover, which could cause loss of data on USB storage devices, or
+ * failed boot when the root file system is on a USB mass storage device and is
+ * enumerated under EHCI first.
+ *
+ * We write into the xHC's PCI configuration space in some Intel-specific
+ * registers to switch the ports over. The USB 3.0 terminations and the USB
+ * 2.0 data wires are switched separately. We want to enable the SuperSpeed
+ * terminations before switching the USB 2.0 wires over, so that USB 3.0
+ * devices connect at SuperSpeed, rather than at USB 2.0 speeds.
+ */
+void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
+{
+ u32 ports_available;
+ bool ehci_found = false;
+ struct pci_dev *companion = NULL;
+
+ /* Sony VAIO t-series with subsystem device ID 90a8 is not capable of
+ * switching ports from EHCI to xHCI
+ */
+ if (xhci_pdev->subsystem_vendor == PCI_VENDOR_ID_SONY &&
+ xhci_pdev->subsystem_device == 0x90a8)
+ return;
+
+ /* make sure an intel EHCI controller exists */
+ for_each_pci_dev(companion) {
+ if (companion->class == PCI_CLASS_SERIAL_USB_EHCI &&
+ companion->vendor == PCI_VENDOR_ID_INTEL) {
+ ehci_found = true;
+ break;
+ }
+ }
+
+ if (!ehci_found)
+ return;
+
+ /* Don't switchover the ports if the user hasn't compiled the xHCI
+ * driver. Otherwise they will see "dead" USB ports that don't power
+ * the devices.
+ */
+ if (!IS_ENABLED(CONFIG_USB_XHCI_HCD)) {
+ dev_warn(&xhci_pdev->dev,
+ "CONFIG_USB_XHCI_HCD is turned off, defaulting to EHCI.\n");
+ dev_warn(&xhci_pdev->dev,
+ "USB 3.0 devices will work at USB 2.0 speeds.\n");
+ usb_disable_xhci_ports(xhci_pdev);
+ return;
+ }
+
+ /* Read USB3PRM, the USB 3.0 Port Routing Mask Register
+ * Indicate the ports that can be changed from OS.
+ */
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB3PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable ports to enable SuperSpeed: 0x%x\n",
+ ports_available);
+
+ /* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
+ * Register, to turn on SuperSpeed terminations for the
+ * switchable ports.
+ */
+ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
+ ports_available);
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
+ &ports_available);
+ dev_dbg(&xhci_pdev->dev,
+ "USB 3.0 ports that are now enabled under xHCI: 0x%x\n",
+ ports_available);
+
+ /* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
+ * Indicate the USB 2.0 ports to be controlled by the xHCI host.
+ */
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB2PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable USB 2.0 ports to hand over to xCHI: 0x%x\n",
+ ports_available);
+
+ /* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to
+ * switch the USB 2.0 power and data lines over to the xHCI
+ * host.
+ */
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ ports_available);
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ &ports_available);
+ dev_dbg(&xhci_pdev->dev,
+ "USB 2.0 ports that are now switched over to xHCI: 0x%x\n",
+ ports_available);
+}
+EXPORT_SYMBOL_GPL(usb_enable_intel_xhci_ports);
+
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+{
+ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, 0x0);
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, 0x0);
+}
+EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+
+/**
+ * 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 quirk_usb_handoff_xhci(struct pci_dev *pdev)
+{
+ void __iomem *base;
+ int ext_cap_offset;
+ void __iomem *op_reg_base;
+ u32 val;
+ int timeout;
+ int len = pci_resource_len(pdev, 0);
+
+ if (!mmio_resource_enabled(pdev, 0))
+ return;
+
+ base = ioremap_nocache(pci_resource_start(pdev, 0), len);
+ 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 + sizeof(val)) > len) {
+ /* We're reading garbage from the controller */
+ dev_warn(&pdev->dev,
+ "xHCI controller failing to respond");
+ return;
+ }
+
+ 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);
+ }
+ }
+
+ val = readl(base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
+ /* Mask off (turn off) any enabled SMIs */
+ val &= XHCI_LEGACY_DISABLE_SMI;
+ /* Mask all SMI events bits, RW1C */
+ val |= XHCI_LEGACY_SMI_EVENTS;
+ /* Disable any BIOS SMIs and clear all SMI events*/
+ writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
+
+hc_init:
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ usb_enable_intel_xhci_ports(pdev);
+
+ 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 quirk_usb_early_handoff(struct pci_dev *pdev)
+{
+ /* Skip Netlogic mips SoC's internal PCI USB controller.
+ * This device does not need/support EHCI/OHCI handoff
+ */
+ if (pdev->vendor == 0x184e) /* vendor Netlogic */
+ return;
+ if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI &&
+ pdev->class != PCI_CLASS_SERIAL_USB_OHCI &&
+ pdev->class != PCI_CLASS_SERIAL_USB_EHCI &&
+ pdev->class != PCI_CLASS_SERIAL_USB_XHCI)
+ return;
+
+ if (pci_enable_device(pdev) < 0) {
+ dev_warn(&pdev->dev,
+ "Can't enable PCI device, BIOS handoff failed.\n");
+ return;
+ }
+ if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI)
+ quirk_usb_handoff_uhci(pdev);
+ else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI)
+ 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);
+ pci_disable_device(pdev);
+}
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
new file mode 100644
index 000000000..c622ddf21
--- /dev/null
+++ b/drivers/usb/host/pci-quirks.h
@@ -0,0 +1,26 @@
+#ifndef __LINUX_USB_PCI_QUIRKS_H
+#define __LINUX_USB_PCI_QUIRKS_H
+
+#ifdef CONFIG_PCI
+void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
+int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+int usb_amd_find_chipset_info(void);
+int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
+bool usb_amd_hang_symptom_quirk(void);
+bool usb_amd_prefetch_quirk(void);
+void usb_amd_dev_put(void);
+void usb_amd_quirk_pll_disable(void);
+void usb_amd_quirk_pll_enable(void);
+void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
+void sb800_prefetch(struct device *dev, int on);
+#else
+struct pci_dev;
+static inline void usb_amd_quirk_pll_disable(void) {}
+static inline void usb_amd_quirk_pll_enable(void) {}
+static inline void usb_amd_dev_put(void) {}
+static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
+static inline void sb800_prefetch(struct device *dev, int on) {}
+#endif /* CONFIG_PCI */
+
+#endif /* __LINUX_USB_PCI_QUIRKS_H */
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
new file mode 100644
index 000000000..4cbd0633c
--- /dev/null
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -0,0 +1,2541 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@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
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+
+#include "r8a66597.h"
+
+MODULE_DESCRIPTION("R8A66597 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_ALIAS("platform:r8a66597_hcd");
+
+#define DRIVER_VERSION "2009-05-26"
+
+static const char hcd_name[] = "r8a66597_hcd";
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum);
+static int r8a66597_get_frame(struct usb_hcd *hcd);
+
+/* this function must be called with interrupt disabled */
+static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bset(r8a66597, 1 << pipenum, reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bclr(r8a66597, 1 << pipenum, reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address,
+ u16 usbspd, u8 upphub, u8 hubport, int port)
+{
+ u16 val;
+ unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
+
+ val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
+ r8a66597_write(r8a66597, val, devadd_reg);
+}
+
+static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+ int i = 0;
+
+ if (r8a66597->pdata->on_chip) {
+ clk_prepare_enable(r8a66597->clk);
+ do {
+ r8a66597_write(r8a66597, SCKE, SYSCFG0);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 1000) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & SCKE) != SCKE);
+ r8a66597_write(r8a66597, 0x04, 0x02);
+ } else {
+ do {
+ r8a66597_write(r8a66597, USBE, SYSCFG0);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 1000) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & USBE) != USBE);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata),
+ XTAL, SYSCFG0);
+
+ i = 0;
+ r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+ do {
+ msleep(1);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 500) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & SCKE) != SCKE);
+ }
+
+ return 0;
+}
+
+static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
+{
+ r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+ udelay(1);
+
+ if (r8a66597->pdata->on_chip) {
+ clk_disable_unprepare(r8a66597->clk);
+ } else {
+ r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+ r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ }
+}
+
+static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
+{
+ u16 val;
+
+ val = port ? DRPD : DCFM | DRPD;
+ r8a66597_bset(r8a66597, val, get_syscfg_reg(port));
+ r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
+
+ r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port));
+ r8a66597_bclr(r8a66597, DTCHE, get_intenb_reg(port));
+ r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+}
+
+static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
+{
+ u16 val, tmp;
+
+ r8a66597_write(r8a66597, 0, get_intenb_reg(port));
+ r8a66597_write(r8a66597, 0, get_intsts_reg(port));
+
+ r8a66597_port_power(r8a66597, port, 0);
+
+ do {
+ tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
+ udelay(640);
+ } while (tmp == EDGESTS);
+
+ val = port ? DRPD : DCFM | DRPD;
+ r8a66597_bclr(r8a66597, val, get_syscfg_reg(port));
+ r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(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)
+ return ret;
+
+ r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
+ r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bset(r8a66597, irq_sense & INTL, SOFCFG);
+ r8a66597_bset(r8a66597, BRDY0, BRDYENB);
+ r8a66597_bset(r8a66597, BEMP0, BEMPENB);
+
+ r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
+ r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
+ r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
+ r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
+
+ r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++)
+ r8a66597_enable_port(r8a66597, port);
+
+ return 0;
+}
+
+static void disable_controller(struct r8a66597 *r8a66597)
+{
+ int port;
+
+ /* disable interrupts */
+ r8a66597_write(r8a66597, 0, INTENB0);
+ r8a66597_write(r8a66597, 0, INTENB1);
+ r8a66597_write(r8a66597, 0, BRDYENB);
+ r8a66597_write(r8a66597, 0, BEMPENB);
+ r8a66597_write(r8a66597, 0, NRDYENB);
+
+ /* clear status */
+ r8a66597_write(r8a66597, 0, BRDYSTS);
+ r8a66597_write(r8a66597, 0, NRDYSTS);
+ r8a66597_write(r8a66597, 0, BEMPSTS);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++)
+ r8a66597_disable_port(r8a66597, port);
+
+ r8a66597_clock_disable(r8a66597);
+}
+
+static int get_parent_r8a66597_address(struct r8a66597 *r8a66597,
+ struct usb_device *udev)
+{
+ struct r8a66597_device *dev;
+
+ if (udev->parent && udev->parent->devnum != 1)
+ udev = udev->parent;
+
+ dev = dev_get_drvdata(&udev->dev);
+ if (dev)
+ return dev->address;
+ else
+ return 0;
+}
+
+static int is_child_device(char *devpath)
+{
+ return (devpath[2] ? 1 : 0);
+}
+
+static int is_hub_limit(char *devpath)
+{
+ return ((strlen(devpath) >= 4) ? 1 : 0);
+}
+
+static void get_port_number(struct r8a66597 *r8a66597,
+ char *devpath, u16 *root_port, u16 *hub_port)
+{
+ if (root_port) {
+ *root_port = (devpath[0] & 0x0F) - 1;
+ if (*root_port >= r8a66597->max_root_hub)
+ printk(KERN_ERR "r8a66597: Illegal root port number.\n");
+ }
+ if (hub_port)
+ *hub_port = devpath[2] & 0x0F;
+}
+
+static u16 get_r8a66597_usb_speed(enum usb_device_speed speed)
+{
+ u16 usbspd = 0;
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ usbspd = LSMODE;
+ break;
+ case USB_SPEED_FULL:
+ usbspd = FSMODE;
+ break;
+ case USB_SPEED_HIGH:
+ usbspd = HSMODE;
+ break;
+ default:
+ printk(KERN_ERR "r8a66597: unknown speed\n");
+ break;
+ }
+
+ return usbspd;
+}
+
+static void set_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+ int idx;
+
+ idx = address / 32;
+ r8a66597->child_connect_map[idx] |= 1 << (address % 32);
+}
+
+static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+ int idx;
+
+ idx = address / 32;
+ r8a66597->child_connect_map[idx] &= ~(1 << (address % 32));
+}
+
+static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
+{
+ u16 pipenum = pipe->info.pipenum;
+ const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
+ const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
+ const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
+
+ if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
+ dma_ch = R8A66597_PIPE_NO_DMA;
+
+ pipe->fifoaddr = fifoaddr[dma_ch];
+ pipe->fifosel = fifosel[dma_ch];
+ pipe->fifoctr = fifoctr[dma_ch];
+
+ if (pipenum == 0)
+ pipe->pipectr = DCPCTR;
+ else
+ pipe->pipectr = get_pipectr_addr(pipenum);
+
+ if (check_bulk_or_isoc(pipenum)) {
+ pipe->pipetre = get_pipetre_addr(pipenum);
+ pipe->pipetrn = get_pipetrn_addr(pipenum);
+ } else {
+ pipe->pipetre = 0;
+ pipe->pipetrn = 0;
+ }
+}
+
+static struct r8a66597_device *
+get_urb_to_r8a66597_dev(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ if (usb_pipedevice(urb->pipe) == 0)
+ return &r8a66597->device0;
+
+ return dev_get_drvdata(&urb->dev->dev);
+}
+
+static int make_r8a66597_device(struct r8a66597 *r8a66597,
+ struct urb *urb, u8 addr)
+{
+ struct r8a66597_device *dev;
+ int usb_address = urb->setup_packet[2]; /* urb->pipe is address 0 */
+
+ dev = kzalloc(sizeof(struct r8a66597_device), GFP_ATOMIC);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&urb->dev->dev, dev);
+ dev->udev = urb->dev;
+ dev->address = addr;
+ dev->usb_address = usb_address;
+ dev->state = USB_STATE_ADDRESS;
+ dev->ep_in_toggle = 0;
+ dev->ep_out_toggle = 0;
+ INIT_LIST_HEAD(&dev->device_list);
+ list_add_tail(&dev->device_list, &r8a66597->child_device);
+
+ get_port_number(r8a66597, urb->dev->devpath,
+ &dev->root_port, &dev->hub_port);
+ if (!is_child_device(urb->dev->devpath))
+ r8a66597->root_hub[dev->root_port].dev = dev;
+
+ set_devadd_reg(r8a66597, dev->address,
+ get_r8a66597_usb_speed(urb->dev->speed),
+ get_parent_r8a66597_address(r8a66597, urb->dev),
+ dev->hub_port, dev->root_port);
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ u8 addr; /* R8A66597's address */
+ struct r8a66597_device *dev;
+
+ if (is_hub_limit(urb->dev->devpath)) {
+ dev_err(&urb->dev->dev, "External hub limit reached.\n");
+ return 0;
+ }
+
+ dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ if (dev && dev->state >= USB_STATE_ADDRESS)
+ return dev->address;
+
+ for (addr = 1; addr <= R8A66597_MAX_DEVICE; addr++) {
+ if (r8a66597->address_map & (1 << addr))
+ continue;
+
+ dev_dbg(&urb->dev->dev, "alloc_address: r8a66597_addr=%d\n", addr);
+ r8a66597->address_map |= 1 << addr;
+
+ if (make_r8a66597_device(r8a66597, urb, addr) < 0)
+ return 0;
+
+ return addr;
+ }
+
+ dev_err(&urb->dev->dev,
+ "cannot communicate with a USB device more than 10.(%x)\n",
+ r8a66597->address_map);
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void free_usb_address(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev, int reset)
+{
+ int port;
+
+ if (!dev)
+ return;
+
+ dev_dbg(&dev->udev->dev, "free_addr: addr=%d\n", dev->address);
+
+ dev->state = USB_STATE_DEFAULT;
+ r8a66597->address_map &= ~(1 << dev->address);
+ dev->address = 0;
+ /*
+ * Only when resetting USB, it is necessary to erase drvdata. When
+ * a usb device with usb hub is disconnect, "dev->udev" is already
+ * freed on usb_desconnect(). So we cannot access the data.
+ */
+ if (reset)
+ dev_set_drvdata(&dev->udev->dev, NULL);
+ list_del(&dev->device_list);
+ kfree(dev);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
+ if (r8a66597->root_hub[port].dev == dev) {
+ r8a66597->root_hub[port].dev = NULL;
+ break;
+ }
+ }
+}
+
+static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
+ u16 mask, u16 loop)
+{
+ u16 tmp;
+ int i = 0;
+
+ do {
+ tmp = r8a66597_read(r8a66597, reg);
+ if (i++ > 1000000) {
+ printk(KERN_ERR "r8a66597: register%lx, loop %x "
+ "is timeout\n", reg, loop);
+ break;
+ }
+ ndelay(1);
+ } while ((tmp & mask) != loop);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_start(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+ if ((pipe->info.pipenum != 0) & ((tmp & PID_STALL) != 0)) /* stall? */
+ r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+ r8a66597_mdfy(r8a66597, PID_BUF, PID, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_stop(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+ if ((tmp & PID_STALL11) != PID_STALL11) /* force stall? */
+ r8a66597_mdfy(r8a66597, PID_STALL, PID, pipe->pipectr);
+ r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+ r8a66597_reg_wait(r8a66597, pipe->pipectr, PBUSY, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void clear_all_buffer(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ if (!pipe || pipe->info.pipenum == 0)
+ return;
+
+ pipe_stop(r8a66597, pipe);
+ r8a66597_bset(r8a66597, ACLRM, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ r8a66597_bclr(r8a66597, ACLRM, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_pipe_toggle(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe, int toggle)
+{
+ if (toggle)
+ r8a66597_bset(r8a66597, SQSET, pipe->pipectr);
+ else
+ r8a66597_bset(r8a66597, SQCLR, pipe->pipectr);
+}
+
+static inline unsigned short mbw_value(struct r8a66597 *r8a66597)
+{
+ if (r8a66597->pdata->on_chip)
+ return MBW_32;
+ else
+ return MBW_16;
+}
+
+/* this function must be called with interrupt disabled */
+static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ unsigned short mbw = mbw_value(r8a66597);
+
+ r8a66597_mdfy(r8a66597, mbw | pipenum, mbw | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
+}
+
+/* this function must be called with interrupt disabled */
+static inline void fifo_change_from_pipe(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe)
+{
+ unsigned short mbw = mbw_value(r8a66597);
+
+ cfifo_change(r8a66597, 0);
+ r8a66597_mdfy(r8a66597, mbw | 0, mbw | CURPIPE, D0FIFOSEL);
+ r8a66597_mdfy(r8a66597, mbw | 0, mbw | CURPIPE, D1FIFOSEL);
+
+ r8a66597_mdfy(r8a66597, mbw | pipe->info.pipenum, mbw | CURPIPE,
+ pipe->fifosel);
+ r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, pipe->info.pipenum);
+}
+
+static u16 r8a66597_get_pipenum(struct urb *urb, struct usb_host_endpoint *hep)
+{
+ struct r8a66597_pipe *pipe = hep->hcpriv;
+
+ if (usb_pipeendpoint(urb->pipe) == 0)
+ return 0;
+ else
+ return pipe->info.pipenum;
+}
+
+static u16 get_urb_to_r8a66597_addr(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+ return (usb_pipedevice(urb->pipe) == 0) ? 0 : dev->address;
+}
+
+static unsigned short *get_toggle_pointer(struct r8a66597_device *dev,
+ int urb_pipe)
+{
+ if (!dev)
+ return NULL;
+
+ return usb_pipein(urb_pipe) ? &dev->ep_in_toggle : &dev->ep_out_toggle;
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_set(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb, int set)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+ unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+ if (!toggle)
+ return;
+
+ if (set)
+ *toggle |= 1 << endpoint;
+ else
+ *toggle &= ~(1 << endpoint);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_save(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ if (r8a66597_read(r8a66597, pipe->pipectr) & SQMON)
+ pipe_toggle_set(r8a66597, pipe, urb, 1);
+ else
+ pipe_toggle_set(r8a66597, pipe, urb, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_restore(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+ unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+ if (!toggle)
+ return;
+
+ r8a66597_pipe_toggle(r8a66597, pipe, *toggle & (1 << endpoint));
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_buffer_setting(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe_info *info)
+{
+ u16 val = 0;
+
+ if (info->pipenum == 0)
+ return;
+
+ r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+ r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+ r8a66597_write(r8a66597, info->pipenum, PIPESEL);
+ if (!info->dir_in)
+ val |= R8A66597_DIR;
+ if (info->type == R8A66597_BULK && info->dir_in)
+ val |= R8A66597_DBLB | R8A66597_SHTNAK;
+ val |= info->type | info->epnum;
+ r8a66597_write(r8a66597, val, PIPECFG);
+
+ r8a66597_write(r8a66597, (info->buf_bsize << 10) | (info->bufnum),
+ PIPEBUF);
+ r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket,
+ PIPEMAXP);
+ r8a66597_write(r8a66597, info->interval, PIPEPERI);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ struct r8a66597_pipe_info *info;
+ struct urb *urb = td->urb;
+
+ if (td->pipenum > 0) {
+ info = &td->pipe->info;
+ cfifo_change(r8a66597, 0);
+ pipe_buffer_setting(r8a66597, info);
+
+ if (!usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)) &&
+ !usb_pipecontrol(urb->pipe)) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 0);
+ pipe_toggle_set(r8a66597, td->pipe, urb, 0);
+ clear_all_buffer(r8a66597, td->pipe);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ }
+ pipe_toggle_restore(r8a66597, td->pipe, urb);
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static u16 get_empty_pipenum(struct r8a66597 *r8a66597,
+ struct usb_endpoint_descriptor *ep)
+{
+ u16 array[R8A66597_MAX_NUM_PIPE], i = 0, min;
+
+ memset(array, 0, sizeof(array));
+ switch (usb_endpoint_type(ep)) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (usb_endpoint_dir_in(ep))
+ array[i++] = 4;
+ else {
+ array[i++] = 3;
+ array[i++] = 5;
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (usb_endpoint_dir_in(ep)) {
+ array[i++] = 6;
+ array[i++] = 7;
+ array[i++] = 8;
+ } else
+ array[i++] = 9;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (usb_endpoint_dir_in(ep))
+ array[i++] = 2;
+ else
+ array[i++] = 1;
+ break;
+ default:
+ printk(KERN_ERR "r8a66597: Illegal type\n");
+ return 0;
+ }
+
+ i = 1;
+ min = array[0];
+ while (array[i] != 0) {
+ if (r8a66597->pipe_cnt[min] > r8a66597->pipe_cnt[array[i]])
+ min = array[i];
+ i++;
+ }
+
+ return min;
+}
+
+static u16 get_r8a66597_type(__u8 type)
+{
+ u16 r8a66597_type;
+
+ switch (type) {
+ case USB_ENDPOINT_XFER_BULK:
+ r8a66597_type = R8A66597_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ r8a66597_type = R8A66597_INT;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ r8a66597_type = R8A66597_ISO;
+ break;
+ default:
+ printk(KERN_ERR "r8a66597: Illegal type\n");
+ r8a66597_type = 0x0000;
+ break;
+ }
+
+ return r8a66597_type;
+}
+
+static u16 get_bufnum(u16 pipenum)
+{
+ u16 bufnum = 0;
+
+ if (pipenum == 0)
+ bufnum = 0;
+ else if (check_bulk_or_isoc(pipenum))
+ bufnum = 8 + (pipenum - 1) * R8A66597_BUF_BSIZE*2;
+ else if (check_interrupt(pipenum))
+ bufnum = 4 + (pipenum - 6);
+ else
+ printk(KERN_ERR "r8a66597: Illegal pipenum (%d)\n", pipenum);
+
+ return bufnum;
+}
+
+static u16 get_buf_bsize(u16 pipenum)
+{
+ u16 buf_bsize = 0;
+
+ if (pipenum == 0)
+ buf_bsize = 3;
+ else if (check_bulk_or_isoc(pipenum))
+ buf_bsize = R8A66597_BUF_BSIZE - 1;
+ else if (check_interrupt(pipenum))
+ buf_bsize = 0;
+ else
+ printk(KERN_ERR "r8a66597: Illegal pipenum (%d)\n", pipenum);
+
+ return buf_bsize;
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ int i;
+ struct r8a66597_pipe_info *info = &pipe->info;
+ unsigned short mbw = mbw_value(r8a66597);
+
+ /* pipe dma is only for external controlles */
+ if (r8a66597->pdata->on_chip)
+ return;
+
+ if ((pipe->info.pipenum != 0) && (info->type != R8A66597_INT)) {
+ for (i = 0; i < R8A66597_MAX_DMA_CHANNEL; i++) {
+ if ((r8a66597->dma_map & (1 << i)) != 0)
+ continue;
+
+ dev_info(&dev->udev->dev,
+ "address %d, EndpointAddress 0x%02x use "
+ "DMA FIFO\n", usb_pipedevice(urb->pipe),
+ info->dir_in ?
+ USB_ENDPOINT_DIR_MASK + info->epnum
+ : info->epnum);
+
+ r8a66597->dma_map |= 1 << i;
+ dev->dma_map |= 1 << i;
+ set_pipe_reg_addr(pipe, i);
+
+ cfifo_change(r8a66597, 0);
+ r8a66597_mdfy(r8a66597, mbw | pipe->info.pipenum,
+ mbw | CURPIPE, pipe->fifosel);
+
+ r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE,
+ pipe->info.pipenum);
+ r8a66597_bset(r8a66597, BCLR, pipe->fifoctr);
+ break;
+ }
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe(struct r8a66597 *r8a66597, struct urb *urb,
+ struct usb_host_endpoint *hep,
+ struct r8a66597_pipe_info *info)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ struct r8a66597_pipe *pipe = hep->hcpriv;
+
+ dev_dbg(&dev->udev->dev, "enable_pipe:\n");
+
+ pipe->info = *info;
+ set_pipe_reg_addr(pipe, R8A66597_PIPE_NO_DMA);
+ r8a66597->pipe_cnt[pipe->info.pipenum]++;
+ dev->pipe_cnt[pipe->info.pipenum]++;
+
+ enable_r8a66597_pipe_dma(r8a66597, dev, pipe, urb);
+}
+
+static void r8a66597_urb_done(struct r8a66597 *r8a66597, struct urb *urb,
+ int status)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) {
+ void *ptr;
+
+ for (ptr = urb->transfer_buffer;
+ ptr < urb->transfer_buffer + urb->transfer_buffer_length;
+ ptr += PAGE_SIZE)
+ flush_dcache_page(virt_to_page(ptr));
+ }
+
+ usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb);
+ spin_unlock(&r8a66597->lock);
+ usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb, status);
+ spin_lock(&r8a66597->lock);
+}
+
+/* this function must be called with interrupt disabled */
+static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address)
+{
+ struct r8a66597_td *td, *next;
+ struct urb *urb;
+ struct list_head *list = &r8a66597->pipe_queue[pipenum];
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry_safe(td, next, list, queue) {
+ if (td->address != address)
+ continue;
+
+ urb = td->urb;
+ list_del(&td->queue);
+ kfree(td);
+
+ if (urb)
+ r8a66597_urb_done(r8a66597, urb, -ENODEV);
+
+ break;
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev)
+{
+ int check_ep0 = 0;
+ u16 pipenum;
+
+ if (!dev)
+ return;
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ if (!dev->pipe_cnt[pipenum])
+ continue;
+
+ if (!check_ep0) {
+ check_ep0 = 1;
+ force_dequeue(r8a66597, 0, dev->address);
+ }
+
+ r8a66597->pipe_cnt[pipenum] -= dev->pipe_cnt[pipenum];
+ dev->pipe_cnt[pipenum] = 0;
+ force_dequeue(r8a66597, pipenum, dev->address);
+ }
+
+ dev_dbg(&dev->udev->dev, "disable_pipe\n");
+
+ r8a66597->dma_map &= ~(dev->dma_map);
+ dev->dma_map = 0;
+}
+
+static u16 get_interval(struct urb *urb, __u8 interval)
+{
+ u16 time = 1;
+ int i;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ if (interval > IITV)
+ time = IITV;
+ else
+ time = interval ? interval - 1 : 0;
+ } else {
+ if (interval > 128) {
+ time = IITV;
+ } else {
+ /* calculate the nearest value for PIPEPERI */
+ for (i = 0; i < 7; i++) {
+ if ((1 << i) < interval &&
+ (1 << (i + 1) > interval))
+ time = 1 << i;
+ }
+ }
+ }
+
+ return time;
+}
+
+static unsigned long get_timer_interval(struct urb *urb, __u8 interval)
+{
+ __u8 i;
+ unsigned long time = 1;
+
+ if (usb_pipeisoc(urb->pipe))
+ return 0;
+
+ if (get_r8a66597_usb_speed(urb->dev->speed) == HSMODE) {
+ for (i = 0; i < (interval - 1); i++)
+ time *= 2;
+ time = time * 125 / 1000; /* uSOF -> msec */
+ } else {
+ time = interval;
+ }
+
+ return time;
+}
+
+/* this function must be called with interrupt disabled */
+static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
+ struct usb_host_endpoint *hep,
+ struct usb_endpoint_descriptor *ep)
+{
+ struct r8a66597_pipe_info info;
+
+ info.pipenum = get_empty_pipenum(r8a66597, ep);
+ info.address = get_urb_to_r8a66597_addr(r8a66597, urb);
+ info.epnum = usb_endpoint_num(ep);
+ info.maxpacket = usb_endpoint_maxp(ep);
+ info.type = get_r8a66597_type(usb_endpoint_type(ep));
+ info.bufnum = get_bufnum(info.pipenum);
+ info.buf_bsize = get_buf_bsize(info.pipenum);
+ if (info.type == R8A66597_BULK) {
+ info.interval = 0;
+ info.timer_interval = 0;
+ } else {
+ info.interval = get_interval(urb, ep->bInterval);
+ info.timer_interval = get_timer_interval(urb, ep->bInterval);
+ }
+ if (usb_endpoint_dir_in(ep))
+ info.dir_in = 1;
+ else
+ info.dir_in = 0;
+
+ enable_r8a66597_pipe(r8a66597, urb, hep, &info);
+}
+
+static void init_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev;
+
+ dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ dev->state = USB_STATE_CONFIGURED;
+}
+
+static void pipe_irq_enable(struct r8a66597 *r8a66597, struct urb *urb,
+ u16 pipenum)
+{
+ if (pipenum == 0 && usb_pipeout(urb->pipe))
+ enable_irq_empty(r8a66597, pipenum);
+ else
+ enable_irq_ready(r8a66597, pipenum);
+
+ if (!usb_pipeisoc(urb->pipe))
+ enable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ disable_irq_ready(r8a66597, pipenum);
+ disable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void r8a66597_root_hub_start_polling(struct r8a66597 *r8a66597)
+{
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(R8A66597_RH_POLL_TIME));
+}
+
+static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port,
+ int connect)
+{
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+ rh->scount = R8A66597_MAX_SAMPLING;
+ if (connect)
+ rh->port |= USB_PORT_STAT_CONNECTION;
+ else
+ rh->port &= ~USB_PORT_STAT_CONNECTION;
+ rh->port |= USB_PORT_STAT_C_CONNECTION << 16;
+
+ r8a66597_root_hub_start_polling(r8a66597);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port,
+ u16 syssts)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ if (syssts == SE0) {
+ r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port));
+ r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+ } else {
+ if (syssts == FS_JSTS)
+ r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
+ else if (syssts == LS_JSTS)
+ r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
+
+ r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port));
+ r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));
+
+ if (r8a66597->bus_suspended)
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
+ }
+
+ spin_unlock(&r8a66597->lock);
+ usb_hcd_poll_rh_status(r8a66597_to_hcd(r8a66597));
+ spin_lock(&r8a66597->lock);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port)
+{
+ u16 speed = get_rh_usb_speed(r8a66597, port);
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ rh->port &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED);
+ if (speed == HSMODE)
+ rh->port |= USB_PORT_STAT_HIGH_SPEED;
+ else if (speed == LSMODE)
+ rh->port |= USB_PORT_STAT_LOW_SPEED;
+
+ rh->port &= ~USB_PORT_STAT_RESET;
+ rh->port |= USB_PORT_STAT_ENABLE;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597, int port)
+{
+ struct r8a66597_device *dev = r8a66597->root_hub[port].dev;
+
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev, 0);
+
+ start_root_hub_sampling(r8a66597, port, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_setup_packet(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ int i;
+ __le16 *p = (__le16 *)td->urb->setup_packet;
+ unsigned long setup_addr = USBREQ;
+
+ r8a66597_write(r8a66597, make_devsel(td->address) | td->maxpacket,
+ DCPMAXP);
+ r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
+
+ for (i = 0; i < 4; i++) {
+ r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr);
+ setup_addr += 2;
+ }
+ r8a66597_write(r8a66597, SUREQ, DCPCTR);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_read(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ struct urb *urb = td->urb;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ if (urb->actual_length == 0) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ }
+ pipe_irq_disable(r8a66597, td->pipenum);
+ pipe_start(r8a66597, td->pipe);
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ } else {
+ if (urb->actual_length == 0) {
+ pipe_irq_disable(r8a66597, td->pipenum);
+ pipe_setting(r8a66597, td);
+ pipe_stop(r8a66597, td->pipe);
+ r8a66597_write(r8a66597, ~(1 << td->pipenum), BRDYSTS);
+
+ if (td->pipe->pipetre) {
+ r8a66597_write(r8a66597, TRCLR,
+ td->pipe->pipetre);
+ r8a66597_write(r8a66597,
+ DIV_ROUND_UP
+ (urb->transfer_buffer_length,
+ td->maxpacket),
+ td->pipe->pipetrn);
+ r8a66597_bset(r8a66597, TRENB,
+ td->pipe->pipetre);
+ }
+
+ pipe_start(r8a66597, td->pipe);
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ }
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_write(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ u16 tmp;
+ struct urb *urb = td->urb;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ pipe_stop(r8a66597, td->pipe);
+ r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ if (urb->actual_length == 0) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ }
+ } else {
+ if (urb->actual_length == 0)
+ pipe_setting(r8a66597, td);
+ if (td->pipe->pipetre)
+ r8a66597_bclr(r8a66597, TRENB, td->pipe->pipetre);
+ }
+ r8a66597_write(r8a66597, ~(1 << td->pipenum), BRDYSTS);
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0))
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ else
+ packet_write(r8a66597, td->pipenum);
+ pipe_start(r8a66597, td->pipe);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_status_packet(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ struct urb *urb = td->urb;
+
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+ pipe_stop(r8a66597, td->pipe);
+
+ if (urb->setup_packet[0] & USB_ENDPOINT_DIR_MASK) {
+ r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+ r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR);
+ enable_irq_empty(r8a66597, 0);
+ } else {
+ r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ enable_irq_ready(r8a66597, 0);
+ }
+ enable_irq_nrdy(r8a66597, 0);
+ pipe_start(r8a66597, td->pipe);
+}
+
+static int is_set_address(unsigned char *setup_packet)
+{
+ if (((setup_packet[0] & USB_TYPE_MASK) == USB_TYPE_STANDARD) &&
+ setup_packet[1] == USB_REQ_SET_ADDRESS)
+ return 1;
+ else
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ BUG_ON(!td);
+
+ switch (td->type) {
+ case USB_PID_SETUP:
+ if (is_set_address(td->urb->setup_packet)) {
+ td->set_address = 1;
+ td->urb->setup_packet[2] = alloc_usb_address(r8a66597,
+ td->urb);
+ if (td->urb->setup_packet[2] == 0)
+ return -EPIPE;
+ }
+ prepare_setup_packet(r8a66597, td);
+ break;
+ case USB_PID_IN:
+ prepare_packet_read(r8a66597, td);
+ break;
+ case USB_PID_OUT:
+ prepare_packet_write(r8a66597, td);
+ break;
+ case USB_PID_ACK:
+ prepare_status_packet(r8a66597, td);
+ break;
+ default:
+ printk(KERN_ERR "r8a66597: invalid type.\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int check_transfer_finish(struct r8a66597_td *td, struct urb *urb)
+{
+ if (usb_pipeisoc(urb->pipe)) {
+ if (urb->number_of_packets == td->iso_cnt)
+ return 1;
+ }
+
+ /* control or bulk or interrupt */
+ if ((urb->transfer_buffer_length <= urb->actual_length) ||
+ (td->short_packet) || (td->zero_packet))
+ return 1;
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ unsigned long time;
+
+ BUG_ON(!td);
+
+ if (!list_empty(&r8a66597->pipe_queue[td->pipenum]) &&
+ !usb_pipecontrol(td->urb->pipe) && usb_pipein(td->urb->pipe)) {
+ r8a66597->timeout_map |= 1 << td->pipenum;
+ switch (usb_pipetype(td->urb->pipe)) {
+ case PIPE_INTERRUPT:
+ case PIPE_ISOCHRONOUS:
+ time = 30;
+ break;
+ default:
+ time = 300;
+ break;
+ }
+
+ mod_timer(&r8a66597->td_timer[td->pipenum],
+ jiffies + msecs_to_jiffies(time));
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td,
+ u16 pipenum, struct urb *urb, int status)
+__releases(r8a66597->lock) __acquires(r8a66597->lock)
+{
+ int restart = 0;
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+ r8a66597->timeout_map &= ~(1 << pipenum);
+
+ if (likely(td)) {
+ if (td->set_address && (status != 0 || urb->unlinked))
+ r8a66597->address_map &= ~(1 << urb->setup_packet[2]);
+
+ pipe_toggle_save(r8a66597, td->pipe, urb);
+ list_del(&td->queue);
+ kfree(td);
+ }
+
+ if (!list_empty(&r8a66597->pipe_queue[pipenum]))
+ restart = 1;
+
+ if (likely(urb)) {
+ if (usb_pipeisoc(urb->pipe))
+ urb->start_frame = r8a66597_get_frame(hcd);
+
+ r8a66597_urb_done(r8a66597, urb, status);
+ }
+
+ if (restart) {
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ return;
+
+ start_transfer(r8a66597, td);
+ set_td_timer(r8a66597, td);
+ }
+}
+
+static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 tmp;
+ int rcv_len, bufsize, urb_len, size;
+ u16 *buf;
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+ struct urb *urb;
+ int finish = 0;
+ int status = 0;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ printk(KERN_ERR "r8a66597: in fifo not ready (%d)\n", pipenum);
+ finish_request(r8a66597, td, pipenum, td->urb, -EPIPE);
+ return;
+ }
+
+ /* prepare parameters */
+ rcv_len = tmp & DTLN;
+ if (usb_pipeisoc(urb->pipe)) {
+ buf = (u16 *)(urb->transfer_buffer +
+ urb->iso_frame_desc[td->iso_cnt].offset);
+ urb_len = urb->iso_frame_desc[td->iso_cnt].length;
+ } else {
+ buf = (void *)urb->transfer_buffer + urb->actual_length;
+ urb_len = urb->transfer_buffer_length - urb->actual_length;
+ }
+ bufsize = min(urb_len, (int) td->maxpacket);
+ if (rcv_len <= bufsize) {
+ size = rcv_len;
+ } else {
+ size = bufsize;
+ status = -EOVERFLOW;
+ finish = 1;
+ }
+
+ /* update parameters */
+ urb->actual_length += size;
+ if (rcv_len == 0)
+ td->zero_packet = 1;
+ if (rcv_len < bufsize) {
+ td->short_packet = 1;
+ }
+ if (usb_pipeisoc(urb->pipe)) {
+ urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+ urb->iso_frame_desc[td->iso_cnt].status = status;
+ td->iso_cnt++;
+ finish = 0;
+ }
+
+ /* check transfer finish */
+ if (finish || check_transfer_finish(td, urb)) {
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ finish = 1;
+ }
+
+ /* read fifo */
+ if (urb->transfer_buffer) {
+ if (size == 0)
+ r8a66597_write(r8a66597, BCLR, td->pipe->fifoctr);
+ else
+ r8a66597_read_fifo(r8a66597, td->pipe->fifoaddr,
+ buf, size);
+ }
+
+ if (finish && pipenum != 0)
+ finish_request(r8a66597, td, pipenum, urb, status);
+}
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 tmp;
+ int bufsize, size;
+ u16 *buf;
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+ struct urb *urb;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ printk(KERN_ERR "r8a66597: out fifo not ready (%d)\n", pipenum);
+ finish_request(r8a66597, td, pipenum, urb, -EPIPE);
+ return;
+ }
+
+ /* prepare parameters */
+ bufsize = td->maxpacket;
+ if (usb_pipeisoc(urb->pipe)) {
+ buf = (u16 *)(urb->transfer_buffer +
+ urb->iso_frame_desc[td->iso_cnt].offset);
+ size = min(bufsize,
+ (int)urb->iso_frame_desc[td->iso_cnt].length);
+ } else {
+ buf = (u16 *)(urb->transfer_buffer + urb->actual_length);
+ size = min_t(u32, bufsize,
+ urb->transfer_buffer_length - urb->actual_length);
+ }
+
+ /* write fifo */
+ if (pipenum > 0)
+ r8a66597_write(r8a66597, ~(1 << pipenum), BEMPSTS);
+ if (urb->transfer_buffer) {
+ r8a66597_write_fifo(r8a66597, td->pipe, buf, size);
+ if (!usb_pipebulk(urb->pipe) || td->maxpacket != size)
+ r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr);
+ }
+
+ /* update parameters */
+ urb->actual_length += size;
+ if (usb_pipeisoc(urb->pipe)) {
+ urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+ urb->iso_frame_desc[td->iso_cnt].status = 0;
+ td->iso_cnt++;
+ }
+
+ /* check transfer finish */
+ if (check_transfer_finish(td, urb)) {
+ disable_irq_ready(r8a66597, pipenum);
+ enable_irq_empty(r8a66597, pipenum);
+ if (!usb_pipeisoc(urb->pipe))
+ enable_irq_nrdy(r8a66597, pipenum);
+ } else
+ pipe_irq_enable(r8a66597, urb, pipenum);
+}
+
+
+static void check_next_phase(struct r8a66597 *r8a66597, int status)
+{
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0);
+ struct urb *urb;
+ u8 finish = 0;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ switch (td->type) {
+ case USB_PID_IN:
+ case USB_PID_OUT:
+ if (check_transfer_finish(td, urb))
+ td->type = USB_PID_ACK;
+ break;
+ case USB_PID_SETUP:
+ if (urb->transfer_buffer_length == urb->actual_length)
+ td->type = USB_PID_ACK;
+ else if (usb_pipeout(urb->pipe))
+ td->type = USB_PID_OUT;
+ else
+ td->type = USB_PID_IN;
+ break;
+ case USB_PID_ACK:
+ finish = 1;
+ break;
+ }
+
+ if (finish || status != 0 || urb->unlinked)
+ finish_request(r8a66597, td, 0, urb, status);
+ else
+ start_transfer(r8a66597, td);
+}
+
+static int get_urb_error(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+
+ if (td) {
+ u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID;
+
+ if (pid == PID_NAK)
+ return -ECONNRESET;
+ else
+ return -EPIPE;
+ }
+ return 0;
+}
+
+static void irq_pipe_ready(struct r8a66597 *r8a66597)
+{
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+ struct r8a66597_td *td;
+
+ mask = r8a66597_read(r8a66597, BRDYSTS)
+ & r8a66597_read(r8a66597, BRDYENB);
+ r8a66597_write(r8a66597, ~mask, BRDYSTS);
+ if (mask & BRDY0) {
+ td = r8a66597_get_td(r8a66597, 0);
+ if (td && td->type == USB_PID_IN)
+ packet_read(r8a66597, 0);
+ else
+ pipe_irq_disable(r8a66597, 0);
+ check_next_phase(r8a66597, 0);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ if (td->type == USB_PID_IN)
+ packet_read(r8a66597, pipenum);
+ else if (td->type == USB_PID_OUT)
+ packet_write(r8a66597, pipenum);
+ }
+ }
+}
+
+static void irq_pipe_empty(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+ struct r8a66597_td *td;
+
+ mask = r8a66597_read(r8a66597, BEMPSTS)
+ & r8a66597_read(r8a66597, BEMPENB);
+ r8a66597_write(r8a66597, ~mask, BEMPSTS);
+ if (mask & BEMP0) {
+ cfifo_change(r8a66597, 0);
+ td = r8a66597_get_td(r8a66597, 0);
+ if (td && td->type != USB_PID_OUT)
+ disable_irq_empty(r8a66597, 0);
+ check_next_phase(r8a66597, 0);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ struct r8a66597_td *td;
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ tmp = r8a66597_read(r8a66597, td->pipe->pipectr);
+ if ((tmp & INBUFM) == 0) {
+ disable_irq_empty(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ finish_request(r8a66597, td, pipenum, td->urb,
+ 0);
+ }
+ }
+ }
+}
+
+static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
+{
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+ int status;
+
+ mask = r8a66597_read(r8a66597, NRDYSTS)
+ & r8a66597_read(r8a66597, NRDYENB);
+ r8a66597_write(r8a66597, ~mask, NRDYSTS);
+ if (mask & NRDY0) {
+ cfifo_change(r8a66597, 0);
+ status = get_urb_error(r8a66597, 0);
+ pipe_irq_disable(r8a66597, 0);
+ check_next_phase(r8a66597, status);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ struct r8a66597_td *td;
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ status = get_urb_error(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ pipe_stop(r8a66597, td->pipe);
+ finish_request(r8a66597, td, pipenum, td->urb, status);
+ }
+ }
+}
+
+static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ u16 intsts0, intsts1, intsts2;
+ u16 intenb0, intenb1, intenb2;
+ u16 mask0, mask1, mask2;
+ int status;
+
+ spin_lock(&r8a66597->lock);
+
+ intsts0 = r8a66597_read(r8a66597, INTSTS0);
+ intsts1 = r8a66597_read(r8a66597, INTSTS1);
+ intsts2 = r8a66597_read(r8a66597, INTSTS2);
+ intenb0 = r8a66597_read(r8a66597, INTENB0);
+ intenb1 = r8a66597_read(r8a66597, INTENB1);
+ intenb2 = r8a66597_read(r8a66597, INTENB2);
+
+ mask2 = intsts2 & intenb2;
+ mask1 = intsts1 & intenb1;
+ mask0 = intsts0 & intenb0 & (BEMP | NRDY | BRDY);
+ if (mask2) {
+ if (mask2 & ATTCH) {
+ r8a66597_write(r8a66597, ~ATTCH, INTSTS2);
+ r8a66597_bclr(r8a66597, ATTCHE, INTENB2);
+
+ /* start usb bus sampling */
+ start_root_hub_sampling(r8a66597, 1, 1);
+ }
+ if (mask2 & DTCH) {
+ r8a66597_write(r8a66597, ~DTCH, INTSTS2);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB2);
+ r8a66597_usb_disconnect(r8a66597, 1);
+ }
+ if (mask2 & BCHG) {
+ r8a66597_write(r8a66597, ~BCHG, INTSTS2);
+ r8a66597_bclr(r8a66597, BCHGE, INTENB2);
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
+ }
+ }
+
+ if (mask1) {
+ if (mask1 & ATTCH) {
+ r8a66597_write(r8a66597, ~ATTCH, INTSTS1);
+ r8a66597_bclr(r8a66597, ATTCHE, INTENB1);
+
+ /* start usb bus sampling */
+ start_root_hub_sampling(r8a66597, 0, 1);
+ }
+ if (mask1 & DTCH) {
+ r8a66597_write(r8a66597, ~DTCH, INTSTS1);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB1);
+ r8a66597_usb_disconnect(r8a66597, 0);
+ }
+ if (mask1 & BCHG) {
+ r8a66597_write(r8a66597, ~BCHG, INTSTS1);
+ r8a66597_bclr(r8a66597, BCHGE, INTENB1);
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
+ }
+
+ if (mask1 & SIGN) {
+ r8a66597_write(r8a66597, ~SIGN, INTSTS1);
+ status = get_urb_error(r8a66597, 0);
+ check_next_phase(r8a66597, status);
+ }
+ if (mask1 & SACK) {
+ r8a66597_write(r8a66597, ~SACK, INTSTS1);
+ check_next_phase(r8a66597, 0);
+ }
+ }
+ if (mask0) {
+ if (mask0 & BRDY)
+ irq_pipe_ready(r8a66597);
+ if (mask0 & BEMP)
+ irq_pipe_empty(r8a66597);
+ if (mask0 & NRDY)
+ irq_pipe_nrdy(r8a66597);
+ }
+
+ spin_unlock(&r8a66597->lock);
+ return IRQ_HANDLED;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
+{
+ u16 tmp;
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ if (rh->port & USB_PORT_STAT_RESET) {
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ tmp = r8a66597_read(r8a66597, dvstctr_reg);
+ if ((tmp & USBRST) == USBRST) {
+ r8a66597_mdfy(r8a66597, UACT, USBRST | UACT,
+ dvstctr_reg);
+ r8a66597_root_hub_start_polling(r8a66597);
+ } else
+ r8a66597_usb_connect(r8a66597, port);
+ }
+
+ if (!(rh->port & USB_PORT_STAT_CONNECTION)) {
+ r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port));
+ r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+ }
+
+ if (rh->scount > 0) {
+ tmp = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+ if (tmp == rh->old_syssts) {
+ rh->scount--;
+ if (rh->scount == 0)
+ r8a66597_check_syssts(r8a66597, port, tmp);
+ else
+ r8a66597_root_hub_start_polling(r8a66597);
+ } else {
+ rh->scount = R8A66597_MAX_SAMPLING;
+ rh->old_syssts = tmp;
+ r8a66597_root_hub_start_polling(r8a66597);
+ }
+ }
+}
+
+static void r8a66597_interval_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+ u16 pipenum;
+ struct r8a66597_td *td;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+
+ for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ if (!(r8a66597->interval_map & (1 << pipenum)))
+ continue;
+ if (timer_pending(&r8a66597->interval_timer[pipenum]))
+ continue;
+
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (td)
+ start_transfer(r8a66597, td);
+ }
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static void r8a66597_td_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+ u16 pipenum;
+ struct r8a66597_td *td, *new_td = NULL;
+ struct r8a66597_pipe *pipe;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ if (!(r8a66597->timeout_map & (1 << pipenum)))
+ continue;
+ if (timer_pending(&r8a66597->td_timer[pipenum]))
+ continue;
+
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (!td) {
+ r8a66597->timeout_map &= ~(1 << pipenum);
+ continue;
+ }
+
+ if (td->urb->actual_length) {
+ set_td_timer(r8a66597, td);
+ break;
+ }
+
+ pipe = td->pipe;
+ pipe_stop(r8a66597, pipe);
+
+ new_td = td;
+ do {
+ list_move_tail(&new_td->queue,
+ &r8a66597->pipe_queue[pipenum]);
+ new_td = r8a66597_get_td(r8a66597, pipenum);
+ if (!new_td) {
+ new_td = td;
+ break;
+ }
+ } while (td != new_td && td->address == new_td->address);
+
+ start_transfer(r8a66597, new_td);
+
+ if (td == new_td)
+ r8a66597->timeout_map &= ~(1 << pipenum);
+ else
+ set_td_timer(r8a66597, new_td);
+ break;
+ }
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static void r8a66597_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+ int port;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++)
+ r8a66597_root_hub_control(r8a66597, port);
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int check_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+ if (dev && dev->address && dev->state != USB_STATE_CONFIGURED &&
+ (urb->dev->state == USB_STATE_CONFIGURED))
+ return 1;
+ else
+ return 0;
+}
+
+static int r8a66597_start(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+
+ hcd->state = HC_STATE_RUNNING;
+ return enable_controller(r8a66597);
+}
+
+static void r8a66597_stop(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+
+ disable_controller(r8a66597);
+}
+
+static void set_address_zero(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ unsigned int usb_address = usb_pipedevice(urb->pipe);
+ u16 root_port, hub_port;
+
+ if (usb_address == 0) {
+ get_port_number(r8a66597, urb->dev->devpath,
+ &root_port, &hub_port);
+ set_devadd_reg(r8a66597, 0,
+ get_r8a66597_usb_speed(urb->dev->speed),
+ get_parent_r8a66597_address(r8a66597, urb->dev),
+ hub_port, root_port);
+ }
+}
+
+static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
+ struct urb *urb,
+ struct usb_host_endpoint *hep)
+{
+ struct r8a66597_td *td;
+ u16 pipenum;
+
+ td = kzalloc(sizeof(struct r8a66597_td), GFP_ATOMIC);
+ if (td == NULL)
+ return NULL;
+
+ pipenum = r8a66597_get_pipenum(urb, hep);
+ td->pipenum = pipenum;
+ td->pipe = hep->hcpriv;
+ td->urb = urb;
+ td->address = get_urb_to_r8a66597_addr(r8a66597, urb);
+ td->maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+ !usb_pipein(urb->pipe));
+ if (usb_pipecontrol(urb->pipe))
+ td->type = USB_PID_SETUP;
+ else if (usb_pipein(urb->pipe))
+ td->type = USB_PID_IN;
+ else
+ td->type = USB_PID_OUT;
+ INIT_LIST_HEAD(&td->queue);
+
+ return td;
+}
+
+static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct usb_host_endpoint *hep = urb->ep;
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_td *td = NULL;
+ int ret, request = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ if (!get_urb_to_r8a66597_dev(r8a66597, urb)) {
+ ret = -ENODEV;
+ goto error_not_linked;
+ }
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto error_not_linked;
+
+ if (!hep->hcpriv) {
+ hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe),
+ GFP_ATOMIC);
+ if (!hep->hcpriv) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ set_pipe_reg_addr(hep->hcpriv, R8A66597_PIPE_NO_DMA);
+ if (usb_pipeendpoint(urb->pipe))
+ init_pipe_info(r8a66597, urb, hep, &hep->desc);
+ }
+
+ if (unlikely(check_pipe_config(r8a66597, urb)))
+ init_pipe_config(r8a66597, urb);
+
+ set_address_zero(r8a66597, urb);
+ td = r8a66597_make_td(r8a66597, urb, hep);
+ if (td == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (list_empty(&r8a66597->pipe_queue[td->pipenum]))
+ request = 1;
+ list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]);
+ urb->hcpriv = td;
+
+ if (request) {
+ if (td->pipe->info.timer_interval) {
+ r8a66597->interval_map |= 1 << td->pipenum;
+ mod_timer(&r8a66597->interval_timer[td->pipenum],
+ jiffies + msecs_to_jiffies(
+ td->pipe->info.timer_interval));
+ } else {
+ ret = start_transfer(r8a66597, td);
+ if (ret < 0) {
+ list_del(&td->queue);
+ kfree(td);
+ }
+ }
+ } else
+ set_td_timer(r8a66597, td);
+
+error:
+ if (ret)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+error_not_linked:
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return ret;
+}
+
+static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_td *td;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ if (urb->hcpriv) {
+ td = urb->hcpriv;
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, td->pipenum);
+ disable_irq_empty(r8a66597, td->pipenum);
+ finish_request(r8a66597, td, td->pipenum, urb, status);
+ }
+ done:
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return rc;
+}
+
+static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv;
+ struct r8a66597_td *td;
+ struct urb *urb = NULL;
+ u16 pipenum;
+ unsigned long flags;
+
+ if (pipe == NULL)
+ return;
+ pipenum = pipe->info.pipenum;
+
+ if (pipenum == 0) {
+ kfree(hep->hcpriv);
+ hep->hcpriv = NULL;
+ return;
+ }
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ pipe_stop(r8a66597, pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ disable_irq_empty(r8a66597, pipenum);
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (td)
+ urb = td->urb;
+ finish_request(r8a66597, td, pipenum, urb, -ESHUTDOWN);
+ kfree(hep->hcpriv);
+ hep->hcpriv = NULL;
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int r8a66597_get_frame(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ return r8a66597_read(r8a66597, FRMNUM) & 0x03FF;
+}
+
+static void collect_usb_address_map(struct usb_device *udev, unsigned long *map)
+{
+ int chix;
+ struct usb_device *childdev;
+
+ if (udev->state == USB_STATE_CONFIGURED &&
+ udev->parent && udev->parent->devnum > 1 &&
+ udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB)
+ map[udev->devnum/32] |= (1 << (udev->devnum % 32));
+
+ usb_hub_for_each_child(udev, chix, childdev)
+ collect_usb_address_map(childdev, map);
+}
+
+/* this function must be called with interrupt disabled */
+static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597,
+ int addr)
+{
+ struct r8a66597_device *dev;
+ struct list_head *list = &r8a66597->child_device;
+
+ list_for_each_entry(dev, list, device_list) {
+ if (dev->usb_address != addr)
+ continue;
+
+ return dev;
+ }
+
+ printk(KERN_ERR "r8a66597: get_r8a66597_device fail.(%d)\n", addr);
+ return NULL;
+}
+
+static void update_usb_address_map(struct r8a66597 *r8a66597,
+ struct usb_device *root_hub,
+ unsigned long *map)
+{
+ int i, j, addr;
+ unsigned long diff;
+ unsigned long flags;
+
+ for (i = 0; i < 4; i++) {
+ diff = r8a66597->child_connect_map[i] ^ map[i];
+ if (!diff)
+ continue;
+
+ for (j = 0; j < 32; j++) {
+ if (!(diff & (1 << j)))
+ continue;
+
+ addr = i * 32 + j;
+ if (map[i] & (1 << j))
+ set_child_connect_map(r8a66597, addr);
+ else {
+ struct r8a66597_device *dev;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ dev = get_r8a66597_device(r8a66597, addr);
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev, 0);
+ put_child_connect_map(r8a66597, addr);
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ }
+ }
+ }
+}
+
+static void r8a66597_check_detect_child(struct r8a66597 *r8a66597,
+ struct usb_hcd *hcd)
+{
+ struct usb_bus *bus;
+ unsigned long now_map[4];
+
+ memset(now_map, 0, sizeof(now_map));
+
+ list_for_each_entry(bus, &usb_bus_list, bus_list) {
+ if (!bus->root_hub)
+ continue;
+
+ if (bus->busnum != hcd->self.busnum)
+ continue;
+
+ collect_usb_address_map(bus->root_hub, now_map);
+ update_usb_address_map(r8a66597, bus->root_hub, now_map);
+ }
+}
+
+static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ unsigned long flags;
+ int i;
+
+ r8a66597_check_detect_child(r8a66597, hcd);
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+
+ *buf = 0; /* initialize (no change) */
+
+ for (i = 0; i < r8a66597->max_root_hub; i++) {
+ if (r8a66597->root_hub[i].port & 0xffff0000)
+ *buf |= 1 << (i + 1);
+ }
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+ return (*buf != 0);
+}
+
+static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
+ struct usb_hub_descriptor *desc)
+{
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = r8a66597->max_root_hub;
+ desc->bDescLength = 9;
+ desc->bPwrOn2PwrGood = 0;
+ desc->wHubCharacteristics =
+ cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
+ desc->u.hs.DeviceRemovable[0] =
+ ((1 << r8a66597->max_root_hub) - 1) << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+}
+
+static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ int ret;
+ int port = (wIndex & 0x00FF) - 1;
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+ unsigned long flags;
+
+ ret = 0;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex > r8a66597->max_root_hub)
+ goto error;
+ if (wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ rh->port &= ~USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ break;
+ case USB_PORT_FEAT_POWER:
+ r8a66597_port_power(r8a66597, port, 0);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ break;
+ default:
+ goto error;
+ }
+ rh->port &= ~(1 << wValue);
+ break;
+ case GetHubDescriptor:
+ r8a66597_hub_descriptor(r8a66597,
+ (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ *buf = 0x00;
+ break;
+ case GetPortStatus:
+ if (wIndex > r8a66597->max_root_hub)
+ goto error;
+ *(__le32 *)buf = cpu_to_le32(rh->port);
+ break;
+ case SetPortFeature:
+ if (wIndex > r8a66597->max_root_hub)
+ goto error;
+ if (wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ break;
+ case USB_PORT_FEAT_POWER:
+ r8a66597_port_power(r8a66597, port, 1);
+ rh->port |= USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_RESET: {
+ struct r8a66597_device *dev = rh->dev;
+
+ rh->port |= USB_PORT_STAT_RESET;
+
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev, 1);
+
+ r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT,
+ get_dvstctr_reg(port));
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ break;
+ default:
+ goto error;
+ }
+ rh->port |= 1 << wValue;
+ break;
+ default:
+error:
+ ret = -EPIPE;
+ break;
+ }
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return ret;
+}
+
+#if defined(CONFIG_PM)
+static int r8a66597_bus_suspend(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ int port;
+
+ dev_dbg(&r8a66597->device0.udev->dev, "%s\n", __func__);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ if (!(rh->port & USB_PORT_STAT_ENABLE))
+ continue;
+
+ dev_dbg(&rh->dev->udev->dev, "suspend port = %d\n", port);
+ r8a66597_bclr(r8a66597, UACT, dvstctr_reg); /* suspend */
+ rh->port |= USB_PORT_STAT_SUSPEND;
+
+ if (rh->dev->udev->do_remote_wakeup) {
+ msleep(3); /* waiting last SOF */
+ r8a66597_bset(r8a66597, RWUPE, dvstctr_reg);
+ r8a66597_write(r8a66597, ~BCHG, get_intsts_reg(port));
+ r8a66597_bset(r8a66597, BCHGE, get_intenb_reg(port));
+ }
+ }
+
+ r8a66597->bus_suspended = 1;
+
+ return 0;
+}
+
+static int r8a66597_bus_resume(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ int port;
+
+ dev_dbg(&r8a66597->device0.udev->dev, "%s\n", __func__);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ if (!(rh->port & USB_PORT_STAT_SUSPEND))
+ continue;
+
+ dev_dbg(&rh->dev->udev->dev, "resume port = %d\n", port);
+ rh->port &= ~USB_PORT_STAT_SUSPEND;
+ rh->port |= USB_PORT_STAT_C_SUSPEND << 16;
+ r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg);
+ msleep(USB_RESUME_TIMEOUT);
+ r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg);
+ }
+
+ return 0;
+
+}
+#else
+#define r8a66597_bus_suspend NULL
+#define r8a66597_bus_resume NULL
+#endif
+
+static struct hc_driver r8a66597_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct r8a66597),
+ .irq = r8a66597_irq,
+
+ /*
+ * generic hardware linkage
+ */
+ .flags = HCD_USB2,
+
+ .start = r8a66597_start,
+ .stop = r8a66597_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = r8a66597_urb_enqueue,
+ .urb_dequeue = r8a66597_urb_dequeue,
+ .endpoint_disable = r8a66597_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = r8a66597_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = r8a66597_hub_status_data,
+ .hub_control = r8a66597_hub_control,
+ .bus_suspend = r8a66597_bus_suspend,
+ .bus_resume = r8a66597_bus_resume,
+};
+
+#if defined(CONFIG_PM)
+static int r8a66597_suspend(struct device *dev)
+{
+ struct r8a66597 *r8a66597 = dev_get_drvdata(dev);
+ int port;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ disable_controller(r8a66597);
+
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ rh->port = 0x00000000;
+ }
+
+ return 0;
+}
+
+static int r8a66597_resume(struct device *dev)
+{
+ struct r8a66597 *r8a66597 = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ enable_controller(r8a66597);
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
+ return 0;
+}
+
+static const struct dev_pm_ops r8a66597_dev_pm_ops = {
+ .suspend = r8a66597_suspend,
+ .resume = r8a66597_resume,
+ .poweroff = r8a66597_suspend,
+ .restore = r8a66597_resume,
+};
+
+#define R8A66597_DEV_PM_OPS (&r8a66597_dev_pm_ops)
+#else /* if defined(CONFIG_PM) */
+#define R8A66597_DEV_PM_OPS NULL
+#endif
+
+static int r8a66597_remove(struct platform_device *pdev)
+{
+ struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+ del_timer_sync(&r8a66597->rh_timer);
+ usb_remove_hcd(hcd);
+ iounmap(r8a66597->reg);
+ if (r8a66597->pdata->on_chip)
+ clk_put(r8a66597->clk);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static int r8a66597_probe(struct platform_device *pdev)
+{
+ char clk_name[8];
+ struct resource *res = NULL, *ires;
+ int irq = -1;
+ void __iomem *reg = NULL;
+ struct usb_hcd *hcd = NULL;
+ struct r8a66597 *r8a66597;
+ int ret = 0;
+ int i;
+ unsigned long irq_trigger;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (pdev->dev.dma_mask) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "dma not supported\n");
+ goto clean_up;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "platform_get_resource error.\n");
+ goto clean_up;
+ }
+
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!ires) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev,
+ "platform_get_resource IORESOURCE_IRQ error.\n");
+ goto clean_up;
+ }
+
+ irq = ires->start;
+ irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
+
+ reg = ioremap(res->start, resource_size(res));
+ if (reg == NULL) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "ioremap error.\n");
+ 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) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Failed to create hcd\n");
+ goto clean_up;
+ }
+ r8a66597 = hcd_to_r8a66597(hcd);
+ memset(r8a66597, 0, sizeof(struct r8a66597));
+ platform_set_drvdata(pdev, r8a66597);
+ r8a66597->pdata = dev_get_platdata(&pdev->dev);
+ r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
+
+ if (r8a66597->pdata->on_chip) {
+ snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+ r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(r8a66597->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
+ clk_name);
+ ret = PTR_ERR(r8a66597->clk);
+ goto clean_up2;
+ }
+ r8a66597->max_root_hub = 1;
+ } else
+ r8a66597->max_root_hub = 2;
+
+ spin_lock_init(&r8a66597->lock);
+ setup_timer(&r8a66597->rh_timer, r8a66597_timer,
+ (unsigned long)r8a66597);
+ r8a66597->reg = reg;
+
+ /* make sure no interrupts are pending */
+ ret = r8a66597_clock_enable(r8a66597);
+ if (ret < 0)
+ goto clean_up3;
+ disable_controller(r8a66597);
+
+ for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
+ INIT_LIST_HEAD(&r8a66597->pipe_queue[i]);
+ setup_timer(&r8a66597->td_timer[i], r8a66597_td_timer,
+ (unsigned long)r8a66597);
+ setup_timer(&r8a66597->interval_timer[i],
+ r8a66597_interval_timer,
+ (unsigned long)r8a66597);
+ }
+ INIT_LIST_HEAD(&r8a66597->child_device);
+
+ hcd->rsrc_start = res->start;
+ hcd->has_tt = 1;
+
+ ret = usb_add_hcd(hcd, irq, irq_trigger);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to add hcd\n");
+ goto clean_up3;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ return 0;
+
+clean_up3:
+ if (r8a66597->pdata->on_chip)
+ clk_put(r8a66597->clk);
+clean_up2:
+ usb_put_hcd(hcd);
+
+clean_up:
+ if (reg)
+ iounmap(reg);
+
+ return ret;
+}
+
+static struct platform_driver r8a66597_driver = {
+ .probe = r8a66597_probe,
+ .remove = r8a66597_remove,
+ .driver = {
+ .name = hcd_name,
+ .pm = R8A66597_DEV_PM_OPS,
+ },
+};
+
+module_platform_driver(r8a66597_driver);
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
new file mode 100644
index 000000000..672cea307
--- /dev/null
+++ b/drivers/usb/host/r8a66597.h
@@ -0,0 +1,345 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * 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 __R8A66597_H__
+#define __R8A66597_H__
+
+#include <linux/clk.h>
+#include <linux/usb/r8a66597.h>
+
+#define R8A66597_MAX_NUM_PIPE 10
+#define R8A66597_BUF_BSIZE 8
+#define R8A66597_MAX_DEVICE 10
+#define R8A66597_MAX_ROOT_HUB 2
+#define R8A66597_MAX_SAMPLING 5
+#define R8A66597_RH_POLL_TIME 10
+#define R8A66597_MAX_DMA_CHANNEL 2
+#define R8A66597_PIPE_NO_DMA R8A66597_MAX_DMA_CHANNEL
+#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5))
+#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9))
+#define make_devsel(addr) (addr << 12)
+
+struct r8a66597_pipe_info {
+ unsigned long timer_interval;
+ u16 pipenum;
+ u16 address; /* R8A66597 HCD usb address */
+ u16 epnum;
+ u16 maxpacket;
+ u16 type;
+ u16 bufnum;
+ u16 buf_bsize;
+ u16 interval;
+ u16 dir_in;
+};
+
+struct r8a66597_pipe {
+ struct r8a66597_pipe_info info;
+
+ unsigned long fifoaddr;
+ unsigned long fifosel;
+ unsigned long fifoctr;
+ unsigned long pipectr;
+ unsigned long pipetre;
+ unsigned long pipetrn;
+};
+
+struct r8a66597_td {
+ struct r8a66597_pipe *pipe;
+ struct urb *urb;
+ struct list_head queue;
+
+ u16 type;
+ u16 pipenum;
+ int iso_cnt;
+
+ u16 address; /* R8A66597's USB address */
+ u16 maxpacket;
+
+ unsigned zero_packet:1;
+ unsigned short_packet:1;
+ unsigned set_address:1;
+};
+
+struct r8a66597_device {
+ u16 address; /* R8A66597's USB address */
+ u16 hub_port;
+ u16 root_port;
+
+ unsigned short ep_in_toggle;
+ unsigned short ep_out_toggle;
+ unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+ unsigned char dma_map;
+
+ enum usb_device_state state;
+
+ struct usb_device *udev;
+ int usb_address;
+ struct list_head device_list;
+};
+
+struct r8a66597_root_hub {
+ u32 port;
+ u16 old_syssts;
+ int scount;
+
+ struct r8a66597_device *dev;
+};
+
+struct r8a66597 {
+ spinlock_t lock;
+ void __iomem *reg;
+ struct clk *clk;
+ 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];
+
+ struct timer_list rh_timer;
+ struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
+ struct timer_list interval_timer[R8A66597_MAX_NUM_PIPE];
+
+ unsigned short address_map;
+ unsigned short timeout_map;
+ unsigned short interval_map;
+ unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+ unsigned char dma_map;
+ unsigned int max_root_hub;
+
+ struct list_head child_device;
+ 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)
+{
+ return (struct r8a66597 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597)
+{
+ return container_of((void *)r8a66597, struct usb_hcd, hcd_priv);
+}
+
+static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597,
+ u16 pipenum)
+{
+ if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum])))
+ return NULL;
+
+ return list_entry(r8a66597->pipe_queue[pipenum].next,
+ struct r8a66597_td, queue);
+}
+
+static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597,
+ u16 pipenum)
+{
+ struct r8a66597_td *td;
+
+ td = r8a66597_get_td(r8a66597, pipenum);
+ return (td ? td->urb : NULL);
+}
+
+static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
+{
+ return ioread16(r8a66597->reg + offset);
+}
+
+static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
+ unsigned long offset, u16 *buf,
+ int len)
+{
+ void __iomem *fifoaddr = r8a66597->reg + offset;
+ unsigned long count;
+
+ if (r8a66597->pdata->on_chip) {
+ count = len / 4;
+ ioread32_rep(fifoaddr, buf, count);
+
+ if (len & 0x00000003) {
+ unsigned long tmp = ioread32(fifoaddr);
+ memcpy((unsigned char *)buf + count * 4, &tmp,
+ len & 0x03);
+ }
+ } else {
+ len = (len + 1) / 2;
+ ioread16_rep(fifoaddr, buf, len);
+ }
+}
+
+static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
+ unsigned long offset)
+{
+ iowrite16(val, r8a66597->reg + offset);
+}
+
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+ u16 val, u16 pat, unsigned long offset)
+{
+ u16 tmp;
+ tmp = r8a66597_read(r8a66597, offset);
+ tmp = tmp & (~pat);
+ tmp = tmp | val;
+ r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, val, 0, offset)
+
+static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe, u16 *buf,
+ int len)
+{
+ void __iomem *fifoaddr = r8a66597->reg + pipe->fifoaddr;
+ unsigned long count;
+ unsigned char *pb;
+ int i;
+
+ if (r8a66597->pdata->on_chip) {
+ count = len / 4;
+ iowrite32_rep(fifoaddr, buf, count);
+
+ if (len & 0x00000003) {
+ pb = (unsigned char *)buf + count * 4;
+ for (i = 0; i < (len & 0x00000003); i++) {
+ if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
+ iowrite8(pb[i], fifoaddr + i);
+ else
+ iowrite8(pb[i], fifoaddr + 3 - i);
+ }
+ }
+ } else {
+ int odd = len & 0x0001;
+
+ len = len / 2;
+ iowrite16_rep(fifoaddr, buf, len);
+ if (unlikely(odd)) {
+ buf = &buf[len];
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bclr(r8a66597, MBW_16, pipe->fifosel);
+ iowrite8((unsigned char)*buf, fifoaddr);
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bset(r8a66597, MBW_16, pipe->fifosel);
+ }
+ }
+}
+
+static inline unsigned long get_syscfg_reg(int port)
+{
+ return port == 0 ? SYSCFG0 : SYSCFG1;
+}
+
+static inline unsigned long get_syssts_reg(int port)
+{
+ return port == 0 ? SYSSTS0 : SYSSTS1;
+}
+
+static inline unsigned long get_dvstctr_reg(int port)
+{
+ return port == 0 ? DVSTCTR0 : DVSTCTR1;
+}
+
+static inline unsigned long get_dmacfg_reg(int port)
+{
+ return port == 0 ? DMA0CFG : DMA1CFG;
+}
+
+static inline unsigned long get_intenb_reg(int port)
+{
+ return port == 0 ? INTENB1 : INTENB2;
+}
+
+static inline unsigned long get_intsts_reg(int port)
+{
+ return port == 0 ? INTSTS1 : INTSTS2;
+}
+
+static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
+{
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
+}
+
+static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
+ int power)
+{
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ 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)
+#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
+#define get_devadd_addr(address) (DEVADD0 + address * 2)
+
+#define enable_irq_ready(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define disable_irq_ready(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define enable_irq_empty(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define disable_irq_empty(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define enable_irq_nrdy(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, NRDYENB)
+#define disable_irq_nrdy(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, NRDYENB)
+
+#endif /* __R8A66597_H__ */
+
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
new file mode 100644
index 000000000..fd2a11473
--- /dev/null
+++ b/drivers/usb/host/sl811-hcd.c
@@ -0,0 +1,1816 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * Periodic scheduling is based on Roman's OHCI code
+ * Copyright (C) 1999 Roman Weissgaerber
+ *
+ * The SL811HS controller handles host side USB (like the SL11H, but with
+ * another register set and SOF generation) as well as peripheral side USB
+ * (like the SL811S). This driver version doesn't implement the Gadget API
+ * for the peripheral role; or OTG (that'd need much external circuitry).
+ *
+ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
+ * document (providing significant pieces missing from that spec); plus
+ * the SL811S spec if you want peripheral side info.
+ */
+
+/*
+ * Status: Passed basic stress testing, works with hubs, mice, keyboards,
+ * and usb-storage.
+ *
+ * TODO:
+ * - usb suspend/resume triggered by sl811
+ * - various issues noted in the code
+ * - performance work; use both register banks; ...
+ * - use urb->iso_frame_desc[] with ISO transfers
+ */
+
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/sl811.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+#include <linux/prefetch.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include "sl811.h"
+
+
+MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sl811-hcd");
+
+#define DRIVER_VERSION "19 May 2005"
+
+/* for now, use only one transfer register bank */
+#undef USE_B
+
+// #define QUIRK2
+#define QUIRK3
+
+static const char hcd_name[] = "sl811-hcd";
+
+/*-------------------------------------------------------------------------*/
+
+static void port_power(struct sl811 *sl811, int is_on)
+{
+ struct usb_hcd *hcd = sl811_to_hcd(sl811);
+
+ /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (sl811->port1 & USB_PORT_STAT_POWER)
+ return;
+
+ sl811->port1 = USB_PORT_STAT_POWER;
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ } else {
+ sl811->port1 = 0;
+ sl811->irq_enable = 0;
+ hcd->state = HC_STATE_HALT;
+ }
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_IRQ_STATUS, ~0);
+
+ if (sl811->board && sl811->board->port_power) {
+ /* switch VBUS, at 500mA unless hub power budget gets set */
+ dev_dbg(hcd->self.controller, "power %s\n",
+ is_on ? "on" : "off");
+ sl811->board->port_power(hcd->self.controller, is_on);
+ }
+
+ /* reset as thoroughly as we can */
+ if (sl811->board && sl811->board->reset)
+ sl811->board->reset(hcd->self.controller);
+ else {
+ sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);
+ mdelay(20);
+ }
+
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ // if !is_on, put into lowpower mode now
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue,
+ * and may start I/O. Endpoint queues are scanned during completion irq
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation.
+ *
+ * Using an external DMA engine to copy a packet at a time could work,
+ * though setup/teardown costs may be too big to make it worthwhile.
+ */
+
+/* SETUP starts a new control request. Devices are not allowed to
+ * STALL or NAK these; they must cancel any pending control requests.
+ */
+static void setup_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ len = sizeof(struct usb_ctrlrequest);
+ data_reg = sl811->data_reg;
+ sl811_write_buf(sl811, addr, urb->setup_packet, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_SETUP /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always OUT/data0 */
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = 0;
+ PACKET("SETUP qh%p\n", ep);
+}
+
+/* STATUS finishes control requests, often after IN or OUT data packets */
+static void status_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ int do_out;
+ void __iomem *data_reg;
+
+ do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);
+ writeb(0, data_reg);
+ writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always data1; sometimes IN */
+ control |= SL11H_HCTLMASK_TOGGLE;
+ if (do_out)
+ control |= SL11H_HCTLMASK_OUT;
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = 0;
+ PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
+ do_out ? "out" : "in", ep);
+}
+
+/* IN packets can be used with any type of endpoint. here we just
+ * start the transfer, data from the peripheral may arrive later.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void in_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ /* avoid losing data on overflow */
+ len = ep->maxpacket;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 0))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_IN | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = min_t(u32, len,
+ urb->transfer_buffer_length - urb->actual_length);
+ PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);
+}
+
+/* OUT packets can be used with any type of endpoint.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void out_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ void *buf;
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetch(buf);
+
+ len = min_t(u32, ep->maxpacket,
+ urb->transfer_buffer_length - urb->actual_length);
+
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 1))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ data_reg = sl811->data_reg;
+
+ sl811_write_buf(sl811, addr, buf, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_OUT | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = len;
+ PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* caller updates on-chip enables later */
+
+static inline void sofirq_on(struct sl811 *sl811)
+{
+ if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)
+ return;
+ dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq on\n");
+ sl811->irq_enable |= SL11H_INTMASK_SOFINTR;
+}
+
+static inline void sofirq_off(struct sl811 *sl811)
+{
+ if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))
+ return;
+ dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq off\n");
+ sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* pick the next endpoint for a transaction, and issue it.
+ * frames start with periodic transfers (after whatever is pending
+ * from the previous frame), and the rest of the time is async
+ * transfers, scheduled round-robin.
+ */
+static struct sl811h_ep *start(struct sl811 *sl811, u8 bank)
+{
+ struct sl811h_ep *ep;
+ struct urb *urb;
+ int fclock;
+ u8 control;
+
+ /* use endpoint at schedule head */
+ if (sl811->next_periodic) {
+ ep = sl811->next_periodic;
+ sl811->next_periodic = ep->next;
+ } else {
+ if (sl811->next_async)
+ ep = sl811->next_async;
+ else if (!list_empty(&sl811->async))
+ ep = container_of(sl811->async.next,
+ struct sl811h_ep, schedule);
+ else {
+ /* could set up the first fullspeed periodic
+ * transfer for the next frame ...
+ */
+ return NULL;
+ }
+
+#ifdef USE_B
+ if ((bank && sl811->active_b == ep) || sl811->active_a == ep)
+ return NULL;
+#endif
+
+ if (ep->schedule.next == &sl811->async)
+ sl811->next_async = NULL;
+ else
+ sl811->next_async = container_of(ep->schedule.next,
+ struct sl811h_ep, schedule);
+ }
+
+ if (unlikely(list_empty(&ep->hep->urb_list))) {
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "empty %p queue?\n", ep);
+ return NULL;
+ }
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+ control = ep->defctrl;
+
+ /* if this frame doesn't have enough time left to transfer this
+ * packet, wait till the next frame. too-simple algorithm...
+ */
+ fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
+ fclock -= 100; /* setup takes not much time */
+ if (urb->dev->speed == USB_SPEED_LOW) {
+ if (control & SL11H_HCTLMASK_PREAMBLE) {
+ /* also note erratum 1: some hubs won't work */
+ fclock -= 800;
+ }
+ fclock -= ep->maxpacket << 8;
+
+ /* erratum 2: AFTERSOF only works for fullspeed */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ sofirq_on(sl811);
+ return NULL;
+ }
+ } else {
+ fclock -= 12000 / 19; /* 19 64byte packets/msec */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ control |= SL11H_HCTLMASK_AFTERSOF;
+
+ /* throttle bulk/control irq noise */
+ } else if (ep->nak_count)
+ control |= SL11H_HCTLMASK_AFTERSOF;
+ }
+
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ in_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_OUT:
+ out_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_SETUP:
+ setup_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_ACK: /* for control status */
+ status_packet(sl811, ep, urb, bank, control);
+ break;
+ default:
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "bad ep%p pid %02x\n", ep, ep->nextpid);
+ ep = NULL;
+ }
+ return ep;
+}
+
+#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)
+
+static inline void start_transfer(struct sl811 *sl811)
+{
+ if (sl811->port1 & USB_PORT_STAT_SUSPEND)
+ return;
+ if (sl811->active_a == NULL) {
+ sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
+ if (sl811->active_a != NULL)
+ sl811->jiffies_a = jiffies + MIN_JIFFIES;
+ }
+#ifdef USE_B
+ if (sl811->active_b == NULL) {
+ sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));
+ if (sl811->active_b != NULL)
+ sl811->jiffies_b = jiffies + MIN_JIFFIES;
+ }
+#endif
+}
+
+static void finish_request(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ int status
+) __releases(sl811->lock) __acquires(sl811->lock)
+{
+ unsigned i;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb);
+ spin_unlock(&sl811->lock);
+ usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, status);
+ spin_lock(&sl811->lock);
+
+ /* leave active endpoints in the schedule */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule? */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ if (ep == sl811->next_async)
+ sl811->next_async = NULL;
+ return;
+ }
+
+ /* periodic deschedule */
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep *temp;
+ struct sl811h_ep **prev = &sl811->periodic[i];
+
+ while (*prev && ((temp = *prev) != ep))
+ prev = &temp->next;
+ if (*prev)
+ *prev = ep->next;
+ sl811->load[i] -= ep->load;
+ }
+ ep->branch = PERIODIC_SIZE;
+ sl811->periodic_count--;
+ sl811_to_hcd(sl811)->self.bandwidth_allocated
+ -= ep->load / ep->period;
+ if (ep == sl811->next_periodic)
+ sl811->next_periodic = ep->next;
+
+ /* we might turn SOFs back on again for the async schedule */
+ if (sl811->periodic_count == 0)
+ sofirq_off(sl811);
+}
+
+static void
+done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank)
+{
+ u8 status;
+ struct urb *urb;
+ int urbstat = -EINPROGRESS;
+
+ if (unlikely(!ep))
+ return;
+
+ status = sl811_read(sl811, bank + SL11H_PKTSTATREG);
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+
+ /* we can safely ignore NAKs */
+ if (status & SL11H_STATMASK_NAK) {
+ // PACKET("...NAK_%02x qh%p\n", bank, ep);
+ if (!ep->period)
+ ep->nak_count++;
+ ep->error_count = 0;
+
+ /* ACK advances transfer, toggle, and maybe queue */
+ } else if (status & SL11H_STATMASK_ACK) {
+ struct usb_device *udev = urb->dev;
+ int len;
+ unsigned char *buf;
+
+ /* urb->iso_frame_desc is currently ignored here... */
+
+ ep->nak_count = ep->error_count = 0;
+ switch (ep->nextpid) {
+ case USB_PID_OUT:
+ // PACKET("...ACK/out_%02x qh%p\n", bank, ep);
+ urb->actual_length += ep->length;
+ usb_dotoggle(udev, ep->epnum, 1);
+ if (urb->actual_length
+ == urb->transfer_buffer_length) {
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_ACK;
+
+ /* some bulk protocols terminate OUT transfers
+ * by a short packet, using ZLPs not padding.
+ */
+ else if (ep->length < ep->maxpacket
+ || !(urb->transfer_flags
+ & URB_ZERO_PACKET))
+ urbstat = 0;
+ }
+ break;
+ case USB_PID_IN:
+ // PACKET("...ACK/in_%02x qh%p\n", bank, ep);
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetchw(buf);
+ len = ep->maxpacket - sl811_read(sl811,
+ bank + SL11H_XFERCNTREG);
+ if (len > ep->length) {
+ len = ep->length;
+ urbstat = -EOVERFLOW;
+ }
+ urb->actual_length += len;
+ sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
+ buf, len);
+ usb_dotoggle(udev, ep->epnum, 0);
+ if (urbstat == -EINPROGRESS &&
+ (len < ep->maxpacket ||
+ urb->actual_length ==
+ urb->transfer_buffer_length)) {
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_ACK;
+ else
+ urbstat = 0;
+ }
+ break;
+ case USB_PID_SETUP:
+ // PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
+ if (urb->transfer_buffer_length == urb->actual_length)
+ ep->nextpid = USB_PID_ACK;
+ else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ // PACKET("...ACK/status_%02x qh%p\n", bank, ep);
+ urbstat = 0;
+ break;
+ }
+
+ /* STALL stops all transfers */
+ } else if (status & SL11H_STATMASK_STALL) {
+ PACKET("...STALL_%02x qh%p\n", bank, ep);
+ ep->nak_count = ep->error_count = 0;
+ urbstat = -EPIPE;
+
+ /* error? retry, until "3 strikes" */
+ } else if (++ep->error_count >= 3) {
+ if (status & SL11H_STATMASK_TMOUT)
+ urbstat = -ETIME;
+ else if (status & SL11H_STATMASK_OVF)
+ urbstat = -EOVERFLOW;
+ else
+ urbstat = -EPROTO;
+ ep->error_count = 0;
+ PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",
+ bank, status, ep, urbstat);
+ }
+
+ if (urbstat != -EINPROGRESS || urb->unlinked)
+ finish_request(sl811, ep, urb, urbstat);
+}
+
+static inline u8 checkdone(struct sl811 *sl811)
+{
+ u8 ctl;
+ u8 irqstat = 0;
+
+ if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "%s DONE_A: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#ifdef USE_B
+ if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "%s DONE_B: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#endif
+ return irqstat;
+}
+
+static irqreturn_t sl811h_irq(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ u8 irqstat;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned retries = 5;
+
+ spin_lock(&sl811->lock);
+
+retry:
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;
+ if (irqstat) {
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+ irqstat &= sl811->irq_enable;
+ }
+
+#ifdef QUIRK2
+ /* this may no longer be necessary ... */
+ if (irqstat == 0) {
+ irqstat = checkdone(sl811);
+ if (irqstat)
+ sl811->stat_lost++;
+ }
+#endif
+
+ /* USB packets, not necessarily handled in the order they're
+ * issued ... that's fine if they're different endpoints.
+ */
+ if (irqstat & SL11H_INTMASK_DONE_A) {
+ done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF));
+ sl811->active_a = NULL;
+ sl811->stat_a++;
+ }
+#ifdef USE_B
+ if (irqstat & SL11H_INTMASK_DONE_B) {
+ done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF));
+ sl811->active_b = NULL;
+ sl811->stat_b++;
+ }
+#endif
+ if (irqstat & SL11H_INTMASK_SOFINTR) {
+ unsigned index;
+
+ index = sl811->frame++ % (PERIODIC_SIZE - 1);
+ sl811->stat_sof++;
+
+ /* be graceful about almost-inevitable periodic schedule
+ * overruns: continue the previous frame's transfers iff
+ * this one has nothing scheduled.
+ */
+ if (sl811->next_periodic) {
+ // dev_err(hcd->self.controller, "overrun to slot %d\n", index);
+ sl811->stat_overrun++;
+ }
+ if (sl811->periodic[index])
+ sl811->next_periodic = sl811->periodic[index];
+ }
+
+ /* hub_wq manages debouncing and wakeup */
+ if (irqstat & SL11H_INTMASK_INSRMV) {
+ sl811->stat_insrmv++;
+
+ /* most stats are reset for each VBUS session */
+ sl811->stat_wake = 0;
+ sl811->stat_sof = 0;
+ sl811->stat_a = 0;
+ sl811->stat_b = 0;
+ sl811->stat_lost = 0;
+
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ /* usbcore nukes other pending transactions on disconnect */
+ if (sl811->active_a) {
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_a,
+ container_of(sl811->active_a
+ ->hep->urb_list.next,
+ struct urb, urb_list),
+ -ESHUTDOWN);
+ sl811->active_a = NULL;
+ }
+#ifdef USE_B
+ if (sl811->active_b) {
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_b,
+ container_of(sl811->active_b
+ ->hep->urb_list.next,
+ struct urb, urb_list),
+ NULL, -ESHUTDOWN);
+ sl811->active_b = NULL;
+ }
+#endif
+
+ /* port status seems weird until after reset, so
+ * force the reset and make hub_wq clean up later.
+ */
+ if (irqstat & SL11H_INTMASK_RD)
+ sl811->port1 &= ~USB_PORT_STAT_CONNECTION;
+ else
+ sl811->port1 |= USB_PORT_STAT_CONNECTION;
+
+ sl811->port1 |= USB_PORT_STAT_C_CONNECTION << 16;
+
+ } else if (irqstat & SL11H_INTMASK_RD) {
+ if (sl811->port1 & USB_PORT_STAT_SUSPEND) {
+ dev_dbg(hcd->self.controller, "wakeup\n");
+ sl811->port1 |= USB_PORT_STAT_C_SUSPEND << 16;
+ sl811->stat_wake++;
+ } else
+ irqstat &= ~SL11H_INTMASK_RD;
+ }
+
+ if (irqstat) {
+ if (sl811->port1 & USB_PORT_STAT_ENABLE)
+ start_transfer(sl811);
+ ret = IRQ_HANDLED;
+ if (retries--)
+ goto retry;
+ }
+
+ if (sl811->periodic_count == 0 && list_empty(&sl811->async))
+ sofirq_off(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ spin_unlock(&sl811->lock);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ *
+ * NOTE: the periodic schedule is a sparse tree, with the load for
+ * each branch minimized. see fig 3.5 in the OHCI spec for example.
+ */
+#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */
+
+static int balance(struct sl811 *sl811, u16 period, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that period
+ * which has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < period ; i++) {
+ if (branch < 0 || sl811->load[branch] > sl811->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += period) {
+ if ((sl811->load[j] + load)
+ > MAX_PERIODIC_LOAD)
+ break;
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sl811h_urb_enqueue(
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct sl811h_ep *ep = NULL;
+ unsigned long flags;
+ int i;
+ int retval;
+ struct usb_host_endpoint *hep = urb->ep;
+
+#ifndef CONFIG_USB_SL811_HCD_ISO
+ if (type == PIPE_ISOCHRONOUS)
+ return -ENOSPC;
+#endif
+
+ /* avoid all allocations within spinlocks */
+ if (!hep->hcpriv) {
+ ep = kzalloc(sizeof *ep, mem_flags);
+ if (ep == NULL)
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* don't submit to a dead or disabled port */
+ if (!(sl811->port1 & USB_PORT_STAT_ENABLE)
+ || !HC_IS_RUNNING(hcd->state)) {
+ retval = -ENODEV;
+ kfree(ep);
+ goto fail_not_linked;
+ }
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval) {
+ kfree(ep);
+ goto fail_not_linked;
+ }
+
+ if (hep->hcpriv) {
+ kfree(ep);
+ ep = hep->hcpriv;
+ } else if (!ep) {
+ retval = -ENOMEM;
+ goto fail;
+
+ } else {
+ INIT_LIST_HEAD(&ep->schedule);
+ ep->udev = udev;
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL)
+ ep->nextpid = USB_PID_SETUP;
+ else if (is_out)
+ ep->nextpid = USB_PID_OUT;
+ else
+ ep->nextpid = USB_PID_IN;
+
+ if (ep->maxpacket > H_MAXPACKET) {
+ /* iso packets up to 240 bytes could work... */
+ dev_dbg(hcd->self.controller,
+ "dev %d ep%d maxpacket %d\n", udev->devnum,
+ epnum, ep->maxpacket);
+ retval = -EINVAL;
+ kfree(ep);
+ goto fail;
+ }
+
+ if (udev->speed == USB_SPEED_LOW) {
+ /* send preamble for external hub? */
+ if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
+ ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
+ }
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ if (urb->interval > PERIODIC_SIZE)
+ urb->interval = PERIODIC_SIZE;
+ ep->period = urb->interval;
+ ep->branch = PERIODIC_SIZE;
+ if (type == PIPE_ISOCHRONOUS)
+ ep->defctrl |= SL11H_HCTLMASK_ISOCH;
+ ep->load = usb_calc_bus_time(udev->speed, !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe, is_out))
+ / 1000;
+ break;
+ }
+
+ ep->hep = hep;
+ hep->hcpriv = ep;
+ }
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule))
+ list_add_tail(&ep->schedule, &sl811->async);
+ break;
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ urb->interval = ep->period;
+ if (ep->branch < PERIODIC_SIZE) {
+ /* NOTE: the phase is correct here, but the value
+ * needs offsetting by the transfer queue depth.
+ * All current drivers ignore start_frame, so this
+ * is unlikely to ever matter...
+ */
+ urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ + ep->branch;
+ break;
+ }
+
+ retval = balance(sl811, ep->period, ep->load);
+ if (retval < 0)
+ goto fail;
+ ep->branch = retval;
+ retval = 0;
+ urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ + ep->branch;
+
+ /* sort each schedule branch by period (slow before fast)
+ * to share the faster parts of the tree without needing
+ * dummy/placeholder nodes
+ */
+ dev_dbg(hcd->self.controller, "schedule qh%d/%p branch %d\n",
+ ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep **prev = &sl811->periodic[i];
+ struct sl811h_ep *here = *prev;
+
+ while (here && ep != here) {
+ if (ep->period > here->period)
+ break;
+ prev = &here->next;
+ here = *prev;
+ }
+ if (ep != here) {
+ ep->next = here;
+ *prev = ep;
+ }
+ sl811->load[i] += ep->load;
+ }
+ sl811->periodic_count++;
+ hcd->self.bandwidth_allocated += ep->load / ep->period;
+ sofirq_on(sl811);
+ }
+
+ urb->hcpriv = hep;
+ start_transfer(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+fail:
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+fail_not_linked:
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_host_endpoint *hep;
+ unsigned long flags;
+ struct sl811h_ep *ep;
+ int retval;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval)
+ goto fail;
+
+ hep = urb->hcpriv;
+ ep = hep->hcpriv;
+ if (ep) {
+ /* finish right away if this urb can't be active ...
+ * note that some drivers wrongly expect delays
+ */
+ if (ep->hep->urb_list.next != &urb->urb_list) {
+ /* not front of queue? never active */
+
+ /* for active transfers, we expect an IRQ */
+ } else if (sl811->active_a == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ dev_dbg(hcd->self.controller,
+ "giveup on DONE_A: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_a = NULL;
+ } else
+ urb = NULL;
+#ifdef USE_B
+ } else if (sl811->active_b == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ dev_dbg(hcd->self.controller,
+ "giveup on DONE_B: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_b = NULL;
+ } else
+ urb = NULL;
+#endif
+ } else {
+ /* front of queue for inactive endpoint */
+ }
+
+ if (urb)
+ finish_request(sl811, ep, urb, 0);
+ else
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "dequeue, urb %p active %s; wait4irq\n", urb,
+ (sl811->active_a == ep) ? "A" : "B");
+ } else
+ retval = -EINVAL;
+ fail:
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static void
+sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+ struct sl811h_ep *ep = hep->hcpriv;
+
+ if (!ep)
+ return;
+
+ /* assume we'd just wait for the irq */
+ if (!list_empty(&hep->urb_list))
+ msleep(3);
+ if (!list_empty(&hep->urb_list))
+ dev_warn(hcd->self.controller, "ep %p not empty?\n", ep);
+
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int
+sl811h_get_frame(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* wrong except while periodic transfers are scheduled;
+ * never matches the on-the-wire frame;
+ * subject to overruns.
+ */
+ return sl811->frame;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* the virtual root hub timer IRQ checks for hub status */
+static int
+sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+#ifdef QUIRK3
+ unsigned long flags;
+
+ /* non-SMP HACK: use root hub timer as i/o watchdog
+ * this seems essential when SOF IRQs aren't in use...
+ */
+ local_irq_save(flags);
+ if (!timer_pending(&sl811->timer)) {
+ if (sl811h_irq( /* ~0, */ hcd) != IRQ_NONE)
+ sl811->stat_lost++;
+ }
+ local_irq_restore(flags);
+#endif
+
+ if (!(sl811->port1 & (0xffff << 16)))
+ return 0;
+
+ /* tell hub_wq port 1 changed */
+ *buf = (1 << 1);
+ return 1;
+}
+
+static void
+sl811h_hub_descriptor (
+ struct sl811 *sl811,
+ struct usb_hub_descriptor *desc
+) {
+ u16 temp = 0;
+
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = 1;
+ desc->bDescLength = 9;
+
+ /* per-port power switching (gang of one!), or none */
+ desc->bPwrOn2PwrGood = 0;
+ if (sl811->board && sl811->board->port_power) {
+ desc->bPwrOn2PwrGood = sl811->board->potpg;
+ if (!desc->bPwrOn2PwrGood)
+ desc->bPwrOn2PwrGood = 10;
+ temp = HUB_CHAR_INDV_PORT_LPSM;
+ } else
+ temp = HUB_CHAR_NO_LPSM;
+
+ /* no overcurrent errors detection/handling */
+ temp |= HUB_CHAR_NO_OCPM;
+
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = 0 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+}
+
+static void
+sl811h_timer(unsigned long _sl811)
+{
+ struct sl811 *sl811 = (void *) _sl811;
+ unsigned long flags;
+ u8 irqstat;
+ u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
+ const u32 mask = USB_PORT_STAT_CONNECTION
+ | USB_PORT_STAT_ENABLE
+ | USB_PORT_STAT_LOW_SPEED;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* stop special signaling */
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ udelay(3);
+
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS);
+
+ switch (signaling) {
+ case SL11H_CTL1MASK_SE0:
+ dev_dbg(sl811_to_hcd(sl811)->self.controller, "end reset\n");
+ sl811->port1 = (USB_PORT_STAT_C_RESET << 16)
+ | USB_PORT_STAT_POWER;
+ sl811->ctrl1 = 0;
+ /* don't wrongly ack RD */
+ if (irqstat & SL11H_INTMASK_INSRMV)
+ irqstat &= ~SL11H_INTMASK_RD;
+ break;
+ case SL11H_CTL1MASK_K:
+ dev_dbg(sl811_to_hcd(sl811)->self.controller, "end resume\n");
+ sl811->port1 &= ~USB_PORT_STAT_SUSPEND;
+ break;
+ default:
+ dev_dbg(sl811_to_hcd(sl811)->self.controller,
+ "odd timer signaling: %02x\n", signaling);
+ break;
+ }
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+
+ if (irqstat & SL11H_INTMASK_RD) {
+ /* usbcore nukes all pending transactions on disconnect */
+ if (sl811->port1 & USB_PORT_STAT_CONNECTION)
+ sl811->port1 |= (USB_PORT_STAT_C_CONNECTION << 16)
+ | (USB_PORT_STAT_C_ENABLE << 16);
+ sl811->port1 &= ~mask;
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ } else {
+ sl811->port1 |= mask;
+ if (irqstat & SL11H_INTMASK_DP)
+ sl811->port1 &= ~USB_PORT_STAT_LOW_SPEED;
+ sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
+ }
+
+ if (sl811->port1 & USB_PORT_STAT_CONNECTION) {
+ u8 ctrl2 = SL811HS_CTL2_INIT;
+
+ sl811->irq_enable |= SL11H_INTMASK_DONE_A;
+#ifdef USE_B
+ sl811->irq_enable |= SL11H_INTMASK_DONE_B;
+#endif
+ if (sl811->port1 & USB_PORT_STAT_LOW_SPEED) {
+ sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
+ ctrl2 |= SL811HS_CTL2MASK_DSWAP;
+ }
+
+ /* start SOFs flowing, kickstarting with A registers */
+ sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_SOFLOWREG, 0xe0);
+ sl811_write(sl811, SL811HS_CTLREG2, ctrl2);
+
+ /* autoincrementing */
+ sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0);
+ writeb(SL_SOF, sl811->data_reg);
+ writeb(0, sl811->data_reg);
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ SL11H_HCTLMASK_ARM);
+
+ /* hub_wq provides debounce delay */
+ } else {
+ sl811->ctrl1 = 0;
+ }
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ /* reenable irqs */
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ sl811->port1 &= USB_PORT_STAT_POWER;
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(sl811->port1 & USB_PORT_STAT_SUSPEND))
+ break;
+
+ /* 20 msec of resume/K signaling, other irqs blocked */
+ dev_dbg(hcd->self.controller, "start resume...\n");
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 |= SL11H_CTL1MASK_K;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT));
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 0);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 &= ~(1 << wValue);
+ break;
+ case GetHubDescriptor:
+ sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ put_unaligned_le32(0, buf);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+ put_unaligned_le32(sl811->port1, buf);
+
+#ifndef VERBOSE
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dev_dbg(hcd->self.controller, "GetPortStatus %08x\n",
+ sl811->port1);
+ break;
+ case SetPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (sl811->port1 & USB_PORT_STAT_RESET)
+ goto error;
+ if (!(sl811->port1 & USB_PORT_STAT_ENABLE))
+ goto error;
+
+ dev_dbg(hcd->self.controller,"suspend...\n");
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 1);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (sl811->port1 & USB_PORT_STAT_SUSPEND)
+ goto error;
+ if (!(sl811->port1 & USB_PORT_STAT_POWER))
+ break;
+
+ /* 50 msec of reset/SE0 signaling, irqs blocked */
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 = SL11H_CTL1MASK_SE0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->port1 |= USB_PORT_STAT_RESET;
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(50));
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 |= 1 << wValue;
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int
+sl811h_bus_suspend(struct usb_hcd *hcd)
+{
+ // SOFs off
+ dev_dbg(hcd->self.controller, "%s\n", __func__);
+ return 0;
+}
+
+static int
+sl811h_bus_resume(struct usb_hcd *hcd)
+{
+ // SOFs on
+ dev_dbg(hcd->self.controller, "%s\n", __func__);
+ return 0;
+}
+
+#else
+
+#define sl811h_bus_suspend NULL
+#define sl811h_bus_resume NULL
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+static void dump_irq(struct seq_file *s, char *label, u8 mask)
+{
+ seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask,
+ (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "",
+ (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "",
+ (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "",
+ (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "",
+ (mask & SL11H_INTMASK_RD) ? " rd" : "",
+ (mask & SL11H_INTMASK_DP) ? " dp" : "");
+}
+
+static int sl811h_show(struct seq_file *s, void *unused)
+{
+ struct sl811 *sl811 = s->private;
+ struct sl811h_ep *ep;
+ unsigned i;
+
+ seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n",
+ sl811_to_hcd(sl811)->product_desc,
+ hcd_name, DRIVER_VERSION,
+ sl811->port1);
+
+ seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv);
+ seq_printf(s, "current session: done_a %ld done_b %ld "
+ "wake %ld sof %ld overrun %ld lost %ld\n\n",
+ sl811->stat_a, sl811->stat_b,
+ sl811->stat_wake, sl811->stat_sof,
+ sl811->stat_overrun, sl811->stat_lost);
+
+ spin_lock_irq(&sl811->lock);
+
+ if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND)
+ seq_printf(s, "(suspended)\n\n");
+ else {
+ u8 t = sl811_read(sl811, SL11H_CTLREG1);
+
+ seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t,
+ (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "",
+ ({char *s; switch (t & SL11H_CTL1MASK_FORCE) {
+ case SL11H_CTL1MASK_NORMAL: s = ""; break;
+ case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
+ case SL11H_CTL1MASK_K: s = " k/resume"; break;
+ default: s = "j"; break;
+ } s; }),
+ (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
+ (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
+
+ dump_irq(s, "irq_enable",
+ sl811_read(sl811, SL11H_IRQ_ENABLE));
+ dump_irq(s, "irq_status",
+ sl811_read(sl811, SL11H_IRQ_STATUS));
+ seq_printf(s, "frame clocks remaining: %d\n",
+ sl811_read(sl811, SL11H_SOFTMRREG) << 6);
+ }
+
+ seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a,
+ sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b,
+ sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ seq_printf(s, "\n");
+ list_for_each_entry (ep, &sl811->async, schedule) {
+ struct urb *urb;
+
+ seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d"
+ " nak %d err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep, ep->epnum,
+ ({ char *s; switch (ep->nextpid) {
+ case USB_PID_IN: s = "in"; break;
+ case USB_PID_OUT: s = "out"; break;
+ case USB_PID_SETUP: s = "setup"; break;
+ case USB_PID_ACK: s = "status"; break;
+ default: s = "?"; break;
+ } s;}),
+ ep->maxpacket,
+ ep->nak_count, ep->error_count);
+ list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
+ seq_printf(s, " urb%p, %d/%d\n", urb,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ }
+ if (!list_empty(&sl811->async))
+ seq_printf(s, "\n");
+
+ seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+ for (i = 0; i < PERIODIC_SIZE; i++) {
+ ep = sl811->periodic[i];
+ if (!ep)
+ continue;
+ seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]);
+
+ /* DUMB: prints shared entries multiple times */
+ do {
+ seq_printf(s,
+ " %s%sqh%d/%p (%sdev%d ep%d%s max %d) "
+ "err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep->period, ep,
+ (ep->udev->speed == USB_SPEED_FULL)
+ ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? ""
+ : ((ep->nextpid == USB_PID_IN)
+ ? "in"
+ : "out"),
+ ep->maxpacket, ep->error_count);
+ ep = ep->next;
+ } while (ep);
+ }
+
+ spin_unlock_irq(&sl811->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int sl811h_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sl811h_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+ .open = sl811h_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* expect just one sl811 per system */
+static void create_debug_file(struct sl811 *sl811)
+{
+ sl811->debug_file = debugfs_create_file("sl811h", S_IRUGO,
+ usb_debug_root, sl811,
+ &debug_ops);
+}
+
+static void remove_debug_file(struct sl811 *sl811)
+{
+ debugfs_remove(sl811->debug_file);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+sl811h_stop(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ unsigned long flags;
+
+ del_timer_sync(&hcd->rh_timer);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ port_power(sl811, 0);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_start(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* chip has been reset, VBUS power is off */
+ hcd->state = HC_STATE_RUNNING;
+
+ if (sl811->board) {
+ if (!device_can_wakeup(hcd->self.controller))
+ device_init_wakeup(hcd->self.controller,
+ sl811->board->can_wakeup);
+ hcd->power_budget = sl811->board->power * 2;
+ }
+
+ /* enable power and interrupts */
+ port_power(sl811, 1);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver sl811h_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct sl811),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = sl811h_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /* Basic lifecycle operations */
+ .start = sl811h_start,
+ .stop = sl811h_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = sl811h_urb_enqueue,
+ .urb_dequeue = sl811h_urb_dequeue,
+ .endpoint_disable = sl811h_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = sl811h_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = sl811h_hub_status_data,
+ .hub_control = sl811h_hub_control,
+ .bus_suspend = sl811h_bus_suspend,
+ .bus_resume = sl811h_bus_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+sl811h_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct resource *res;
+
+ remove_debug_file(sl811);
+ usb_remove_hcd(hcd);
+
+ /* some platforms may use IORESOURCE_IO */
+ res = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ if (res)
+ iounmap(sl811->data_reg);
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (res)
+ iounmap(sl811->addr_reg);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static int
+sl811h_probe(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct sl811 *sl811;
+ struct resource *addr, *data, *ires;
+ int irq;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int retval;
+ u8 tmp, ioaddr = 0;
+ unsigned long irqflags;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* basic sanity checks first. board-specific init logic should
+ * have initialized these three resources and probably board
+ * specific platform_data. we don't probe for IRQs, and do only
+ * minimal sanity checking.
+ */
+ ires = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (dev->num_resources < 3 || !ires)
+ return -ENODEV;
+
+ irq = ires->start;
+ irqflags = ires->flags & IRQF_TRIGGER_MASK;
+
+ /* refuse to confuse usbcore */
+ if (dev->dev.dma_mask) {
+ dev_dbg(&dev->dev, "no we won't dma\n");
+ return -EINVAL;
+ }
+
+ /* the chip may be wired for either kind of addressing */
+ addr = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ data = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ retval = -EBUSY;
+ if (!addr || !data) {
+ addr = platform_get_resource(dev, IORESOURCE_IO, 0);
+ data = platform_get_resource(dev, IORESOURCE_IO, 1);
+ if (!addr || !data)
+ return -ENODEV;
+ ioaddr = 1;
+ /*
+ * NOTE: 64-bit resource->start is getting truncated
+ * to avoid compiler warning, assuming that ->start
+ * is always 32-bit for this case
+ */
+ addr_reg = (void __iomem *) (unsigned long) addr->start;
+ data_reg = (void __iomem *) (unsigned long) data->start;
+ } else {
+ addr_reg = ioremap(addr->start, 1);
+ if (addr_reg == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ data_reg = ioremap(data->start, 1);
+ if (data_reg == NULL) {
+ retval = -ENOMEM;
+ goto err4;
+ }
+ }
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev_name(&dev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err5;
+ }
+ hcd->rsrc_start = addr->start;
+ sl811 = hcd_to_sl811(hcd);
+
+ spin_lock_init(&sl811->lock);
+ INIT_LIST_HEAD(&sl811->async);
+ sl811->board = dev_get_platdata(&dev->dev);
+ setup_timer(&sl811->timer, sl811h_timer, (unsigned long)sl811);
+ sl811->addr_reg = addr_reg;
+ sl811->data_reg = data_reg;
+
+ spin_lock_irq(&sl811->lock);
+ port_power(sl811, 0);
+ spin_unlock_irq(&sl811->lock);
+ msleep(200);
+
+ tmp = sl811_read(sl811, SL11H_HWREVREG);
+ switch (tmp >> 4) {
+ case 1:
+ hcd->product_desc = "SL811HS v1.2";
+ break;
+ case 2:
+ hcd->product_desc = "SL811HS v1.5";
+ break;
+ default:
+ /* reject case 0, SL11S is less functional */
+ dev_dbg(&dev->dev, "chiprev %02x\n", tmp);
+ retval = -ENXIO;
+ goto err6;
+ }
+
+ /* The chip's IRQ is level triggered, active high. A requirement
+ * for platform device setup is to cope with things like signal
+ * inverters (e.g. CF is active low) or working only with edge
+ * triggers (e.g. most ARM CPUs). Initial driver stress testing
+ * was on a system with single edge triggering, so most sorts of
+ * triggering arrangement should work.
+ *
+ * Use resource IRQ flags if set by platform device setup.
+ */
+ irqflags |= IRQF_SHARED;
+ retval = usb_add_hcd(hcd, irq, irqflags);
+ if (retval != 0)
+ goto err6;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ create_debug_file(sl811);
+ return retval;
+
+ err6:
+ usb_put_hcd(hcd);
+ err5:
+ if (!ioaddr)
+ iounmap(data_reg);
+ err4:
+ if (!ioaddr)
+ iounmap(addr_reg);
+ err2:
+ dev_dbg(&dev->dev, "init error, %d\n", retval);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+/* for this device there's no useful distinction between the controller
+ * and its root hub.
+ */
+
+static int
+sl811h_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+
+ switch (state.event) {
+ case PM_EVENT_FREEZE:
+ retval = sl811h_bus_suspend(hcd);
+ break;
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_HIBERNATE:
+ case PM_EVENT_PRETHAW: /* explicitly discard hw state */
+ port_power(sl811, 0);
+ break;
+ }
+ return retval;
+}
+
+static int
+sl811h_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* with no "check to see if VBUS is still powered" board hook,
+ * let's assume it'd only be powered to enable remote wakeup.
+ */
+ if (!sl811->port1 || !device_can_wakeup(&hcd->self.root_hub->dev)) {
+ sl811->port1 = 0;
+ port_power(sl811, 1);
+ usb_root_hub_lost_power(hcd->self.root_hub);
+ return 0;
+ }
+
+ return sl811h_bus_resume(hcd);
+}
+
+#else
+
+#define sl811h_suspend NULL
+#define sl811h_resume NULL
+
+#endif
+
+
+/* this driver is exported so sl811_cs can depend on it */
+struct platform_driver sl811h_driver = {
+ .probe = sl811h_probe,
+ .remove = sl811h_remove,
+
+ .suspend = sl811h_suspend,
+ .resume = sl811h_resume,
+ .driver = {
+ .name = (char *) hcd_name,
+ },
+};
+EXPORT_SYMBOL(sl811h_driver);
+
+module_platform_driver(sl811h_driver);
diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
new file mode 100644
index 000000000..1e23ef49b
--- /dev/null
+++ b/drivers/usb/host/sl811.h
@@ -0,0 +1,249 @@
+/*
+ * SL811HS register declarations and HCD data structures
+ *
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2001 Cypress Semiconductor Inc.
+ */
+
+/*
+ * SL811HS has transfer registers, and control registers. In host/master
+ * mode one set of registers is used; in peripheral/slave mode, another.
+ * - SL11H only has some "A" transfer registers from 0x00-0x04
+ * - SL811HS also has "B" registers from 0x08-0x0c
+ * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
+ */
+
+#define SL811_EP_A(base) ((base) + 0)
+#define SL811_EP_B(base) ((base) + 8)
+
+#define SL811_HOST_BUF 0x00
+#define SL811_PERIPH_EP0 0x00
+#define SL811_PERIPH_EP1 0x10
+#define SL811_PERIPH_EP2 0x20
+#define SL811_PERIPH_EP3 0x30
+
+
+/* TRANSFER REGISTERS: host and peripheral sides are similar
+ * except for the control models (master vs slave).
+ */
+#define SL11H_HOSTCTLREG 0
+# define SL11H_HCTLMASK_ARM 0x01
+# define SL11H_HCTLMASK_ENABLE 0x02
+# define SL11H_HCTLMASK_IN 0x00
+# define SL11H_HCTLMASK_OUT 0x04
+# define SL11H_HCTLMASK_ISOCH 0x10
+# define SL11H_HCTLMASK_AFTERSOF 0x20
+# define SL11H_HCTLMASK_TOGGLE 0x40
+# define SL11H_HCTLMASK_PREAMBLE 0x80
+#define SL11H_BUFADDRREG 1
+#define SL11H_BUFLNTHREG 2
+#define SL11H_PKTSTATREG 3 /* read */
+# define SL11H_STATMASK_ACK 0x01
+# define SL11H_STATMASK_ERROR 0x02
+# define SL11H_STATMASK_TMOUT 0x04
+# define SL11H_STATMASK_SEQ 0x08
+# define SL11H_STATMASK_SETUP 0x10
+# define SL11H_STATMASK_OVF 0x20
+# define SL11H_STATMASK_NAK 0x40
+# define SL11H_STATMASK_STALL 0x80
+#define SL11H_PIDEPREG 3 /* write */
+# define SL_SETUP 0xd0
+# define SL_IN 0x90
+# define SL_OUT 0x10
+# define SL_SOF 0x50
+# define SL_PREAMBLE 0xc0
+# define SL_NAK 0xa0
+# define SL_STALL 0xe0
+# define SL_DATA0 0x30
+# define SL_DATA1 0xb0
+#define SL11H_XFERCNTREG 4 /* read */
+#define SL11H_DEVADDRREG 4 /* write */
+
+
+/* CONTROL REGISTERS: host and peripheral are very different.
+ */
+#define SL11H_CTLREG1 5
+# define SL11H_CTL1MASK_SOF_ENA 0x01
+# define SL11H_CTL1MASK_FORCE 0x18
+# define SL11H_CTL1MASK_NORMAL 0x00
+# define SL11H_CTL1MASK_SE0 0x08 /* reset */
+# define SL11H_CTL1MASK_J 0x10
+# define SL11H_CTL1MASK_K 0x18 /* resume */
+# define SL11H_CTL1MASK_LSPD 0x20
+# define SL11H_CTL1MASK_SUSPEND 0x40
+#define SL11H_IRQ_ENABLE 6
+# define SL11H_INTMASK_DONE_A 0x01
+# define SL11H_INTMASK_DONE_B 0x02
+# define SL11H_INTMASK_SOFINTR 0x10
+# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */
+# define SL11H_INTMASK_RD 0x40
+# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */
+#define SL11S_ADDRESS 7
+
+/* 0x08-0x0c are for the B buffer (not in SL11) */
+
+#define SL11H_IRQ_STATUS 0x0D /* write to ack */
+#define SL11H_HWREVREG 0x0E /* read */
+# define SL11H_HWRMASK_HWREV 0xF0
+#define SL11H_SOFLOWREG 0x0E /* write */
+#define SL11H_SOFTMRREG 0x0F /* read */
+
+/* a write to this register enables SL811HS features.
+ * HOST flag presumably overrides the chip input signal?
+ */
+#define SL811HS_CTLREG2 0x0F
+# define SL811HS_CTL2MASK_SOF_MASK 0x3F
+# define SL811HS_CTL2MASK_DSWAP 0x40
+# define SL811HS_CTL2MASK_HOST 0x80
+
+#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e)
+
+
+/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
+ * that's 240 bytes, which we'll split evenly between A and B sides.
+ * Only ISO can use more than 64 bytes per packet.
+ * (The SL11S has 0x40..0xff for buffers.)
+ */
+#define H_MAXPACKET 120 /* bytes in A or B fifos */
+
+#define SL11H_DATA_START 0x10
+#define SL811HS_PACKET_BUF(is_a) ((is_a) \
+ ? SL11H_DATA_START \
+ : (SL11H_DATA_START + H_MAXPACKET))
+
+/*-------------------------------------------------------------------------*/
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct sl811 {
+ spinlock_t lock;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ struct sl811_platform_data *board;
+ struct dentry *debug_file;
+
+ unsigned long stat_insrmv;
+ unsigned long stat_wake;
+ unsigned long stat_sof;
+ unsigned long stat_a;
+ unsigned long stat_b;
+ unsigned long stat_lost;
+ unsigned long stat_overrun;
+
+ /* sw model */
+ struct timer_list timer;
+ struct sl811h_ep *next_periodic;
+ struct sl811h_ep *next_async;
+
+ struct sl811h_ep *active_a;
+ unsigned long jiffies_a;
+ struct sl811h_ep *active_b;
+ unsigned long jiffies_b;
+
+ u32 port1;
+ u8 ctrl1, ctrl2, irq_enable;
+ u16 frame;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: interrupt, iso */
+ u16 load[PERIODIC_SIZE];
+ struct sl811h_ep *periodic[PERIODIC_SIZE];
+ unsigned periodic_count;
+};
+
+static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
+{
+ return (struct sl811 *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
+{
+ return container_of((void *) sl811, struct usb_hcd, hcd_priv);
+}
+
+struct sl811h_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+
+ u8 defctrl;
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+
+ u16 error_count;
+ u16 nak_count;
+ u16 length; /* of current packet */
+
+ /* periodic schedule */
+ u16 period;
+ u16 branch;
+ u16 load;
+ struct sl811h_ep *next;
+
+ /* async schedule */
+ struct list_head schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* These register utilities should work for the SL811S register API too
+ * NOTE: caller must hold sl811->lock.
+ */
+
+static inline u8 sl811_read(struct sl811 *sl811, int reg)
+{
+ writeb(reg, sl811->addr_reg);
+ return readb(sl811->data_reg);
+}
+
+static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
+{
+ writeb(reg, sl811->addr_reg);
+ writeb(val, sl811->data_reg);
+}
+
+static inline void
+sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
+{
+ const u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ writeb(*data++, data_reg);
+ } while (--count);
+}
+
+static inline void
+sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
+{
+ u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ *data++ = readb(data_reg);
+ } while (--count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef PACKET_TRACE
+# define PACKET pr_debug("sl811: "stuff)
+#else
+# define PACKET(stuff...) do{}while(0)
+#endif
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
new file mode 100644
index 000000000..88a9bffe9
--- /dev/null
+++ b/drivers/usb/host/sl811_cs.c
@@ -0,0 +1,202 @@
+/*
+ * PCMCIA driver for SL811HS (as found in REX-CFU1U)
+ * Filename: sl811_cs.c
+ * Author: Yukio Yamamoto
+ *
+ * Port to sl811-hcd and 2.6.x by
+ * Botond Botyanszki <boti@rocketmail.com>
+ * Simon Pickering
+ *
+ * Last update: 2005-05-12
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/usb/sl811.h>
+
+MODULE_AUTHOR("Botond Botyanszki");
+MODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+/* MACROS */
+/*====================================================================*/
+
+#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
+
+/*====================================================================*/
+/* VARIABLES */
+/*====================================================================*/
+
+typedef struct local_info_t {
+ struct pcmcia_device *p_dev;
+} local_info_t;
+
+static void sl811_cs_release(struct pcmcia_device * link);
+
+/*====================================================================*/
+
+static void release_platform_dev(struct device * dev)
+{
+ dev_dbg(dev, "sl811_cs platform_dev release\n");
+ dev->parent = NULL;
+}
+
+static struct sl811_platform_data platform_data = {
+ .potpg = 100,
+ .power = 50, /* == 100mA */
+ // .reset = ... FIXME: invoke CF reset on the card
+};
+
+static struct resource resources[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ // .name = "address",
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ // .name = "data",
+ .flags = IORESOURCE_IO,
+ },
+};
+
+extern struct platform_driver sl811h_driver;
+
+static struct platform_device platform_dev = {
+ .id = -1,
+ .dev = {
+ .platform_data = &platform_data,
+ .release = release_platform_dev,
+ },
+ .resource = resources,
+ .num_resources = ARRAY_SIZE(resources),
+};
+
+static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
+ int irq)
+{
+ if (platform_dev.dev.parent)
+ return -EBUSY;
+ platform_dev.dev.parent = parent;
+
+ /* finish seting up the platform device */
+ resources[0].start = irq;
+
+ resources[1].start = base_addr;
+ resources[1].end = base_addr;
+
+ resources[2].start = base_addr + 1;
+ resources[2].end = base_addr + 1;
+
+ /* The driver core will probe for us. We know sl811-hcd has been
+ * initialized already because of the link order dependency created
+ * by referencing "sl811h_driver".
+ */
+ platform_dev.name = sl811h_driver.driver.name;
+ return platform_device_register(&platform_dev);
+}
+
+/*====================================================================*/
+
+static void sl811_cs_detach(struct pcmcia_device *link)
+{
+ dev_dbg(&link->dev, "sl811_cs_detach\n");
+
+ sl811_cs_release(link);
+
+ /* This points to the parent local_info_t struct */
+ kfree(link->priv);
+}
+
+static void sl811_cs_release(struct pcmcia_device * link)
+{
+ dev_dbg(&link->dev, "sl811_cs_release\n");
+
+ pcmcia_disable_device(link);
+ platform_device_unregister(&platform_dev);
+}
+
+static int sl811_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ return pcmcia_request_io(p_dev);
+}
+
+
+static int sl811_cs_config(struct pcmcia_device *link)
+{
+ struct device *parent = &link->dev;
+ int ret;
+
+ dev_dbg(&link->dev, "sl811_cs_config\n");
+
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+ CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO;
+
+ if (pcmcia_loop_config(link, sl811_cs_config_check, NULL))
+ goto failed;
+
+ /* require an IRQ and two registers */
+ if (resource_size(link->resource[0]) < 2)
+ goto failed;
+
+ if (!link->irq)
+ goto failed;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+ if (sl811_hc_init(parent, link->resource[0]->start, link->irq)
+ < 0) {
+failed:
+ printk(KERN_WARNING "sl811_cs_config failed\n");
+ sl811_cs_release(link);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int sl811_cs_probe(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->p_dev = link;
+ link->priv = local;
+
+ return sl811_cs_config(link);
+}
+
+static const struct pcmcia_device_id sl811_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0xc015, 0x0001), /* RATOC USB HOST CF+ Card */
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, sl811_ids);
+
+static struct pcmcia_driver sl811_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "sl811_cs",
+ .probe = sl811_cs_probe,
+ .remove = sl811_cs_detach,
+ .id_table = sl811_ids,
+};
+module_pcmcia_driver(sl811_cs_driver);
diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c
new file mode 100644
index 000000000..ffc32f4b1
--- /dev/null
+++ b/drivers/usb/host/ssb-hcd.c
@@ -0,0 +1,278 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom USB-core driver (SSB bus glue)
+ *
+ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/ssb/ssb.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/usb/ohci_pdriver.h>
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Common USB driver for SSB Bus");
+MODULE_LICENSE("GPL");
+
+#define SSB_HCD_TMSLOW_HOSTMODE (1 << 29)
+
+struct ssb_hcd_device {
+ struct platform_device *ehci_dev;
+ struct platform_device *ohci_dev;
+
+ u32 enable_flags;
+};
+
+static void ssb_hcd_5354wa(struct ssb_device *dev)
+{
+#ifdef CONFIG_SSB_DRIVER_MIPS
+ /* Work around for 5354 failures */
+ if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
+ /* Change syn01 reg */
+ ssb_write32(dev, 0x894, 0x00fe00fe);
+
+ /* Change syn03 reg */
+ ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1);
+ }
+#endif
+}
+
+static void ssb_hcd_usb20wa(struct ssb_device *dev)
+{
+ if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+ /*
+ * USB 2.0 special considerations:
+ *
+ * In addition to the standard SSB reset sequence, the Host
+ * Control Register must be programmed to bring the USB core
+ * and various phy components out of reset.
+ */
+ ssb_write32(dev, 0x200, 0x7ff);
+
+ /* Change Flush control reg */
+ ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8);
+ ssb_read32(dev, 0x400);
+
+ /* Change Shim control reg */
+ ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100);
+ ssb_read32(dev, 0x304);
+
+ udelay(1);
+
+ ssb_hcd_5354wa(dev);
+ }
+}
+
+/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
+static u32 ssb_hcd_init_chip(struct ssb_device *dev)
+{
+ u32 flags = 0;
+
+ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
+ /* Put the device into host-mode. */
+ flags |= SSB_HCD_TMSLOW_HOSTMODE;
+
+ ssb_device_enable(dev, flags);
+
+ ssb_hcd_usb20wa(dev);
+
+ return flags;
+}
+
+static const struct usb_ehci_pdata ehci_pdata = {
+};
+
+static const struct usb_ohci_pdata ohci_pdata = {
+};
+
+static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len)
+{
+ struct platform_device *hci_dev;
+ struct resource hci_res[2];
+ int ret = -ENOMEM;
+
+ memset(hci_res, 0, sizeof(hci_res));
+
+ hci_res[0].start = addr;
+ hci_res[0].end = hci_res[0].start + len - 1;
+ hci_res[0].flags = IORESOURCE_MEM;
+
+ hci_res[1].start = dev->irq;
+ hci_res[1].flags = IORESOURCE_IRQ;
+
+ hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
+ "ehci-platform" , 0);
+ if (!hci_dev)
+ return NULL;
+
+ hci_dev->dev.parent = dev->dev;
+ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
+
+ ret = platform_device_add_resources(hci_dev, hci_res,
+ ARRAY_SIZE(hci_res));
+ if (ret)
+ goto err_alloc;
+ if (ohci)
+ ret = platform_device_add_data(hci_dev, &ohci_pdata,
+ sizeof(ohci_pdata));
+ else
+ ret = platform_device_add_data(hci_dev, &ehci_pdata,
+ sizeof(ehci_pdata));
+ if (ret)
+ goto err_alloc;
+ ret = platform_device_add(hci_dev);
+ if (ret)
+ goto err_alloc;
+
+ return hci_dev;
+
+err_alloc:
+ platform_device_put(hci_dev);
+ return ERR_PTR(ret);
+}
+
+static int ssb_hcd_probe(struct ssb_device *dev,
+ const struct ssb_device_id *id)
+{
+ int err, tmp;
+ int start, len;
+ u16 chipid_top;
+ u16 coreid = dev->id.coreid;
+ struct ssb_hcd_device *usb_dev;
+
+ /* USBcores are only connected on embedded devices. */
+ chipid_top = (dev->bus->chip_id & 0xFF00);
+ if (chipid_top != 0x4700 && chipid_top != 0x5300)
+ return -ENODEV;
+
+ /* TODO: Probably need checks here; is the core connected? */
+
+ if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
+ return -EOPNOTSUPP;
+
+ usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
+ if (!usb_dev)
+ return -ENOMEM;
+
+ /* We currently always attach SSB_DEV_USB11_HOSTDEV
+ * as HOST OHCI. If we want to attach it as Client device,
+ * we must branch here and call into the (yet to
+ * be written) Client mode driver. Same for remove(). */
+ usb_dev->enable_flags = ssb_hcd_init_chip(dev);
+
+ tmp = ssb_read32(dev, SSB_ADMATCH0);
+
+ start = ssb_admatch_base(tmp);
+ len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp);
+ usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len);
+ if (IS_ERR(usb_dev->ohci_dev)) {
+ err = PTR_ERR(usb_dev->ohci_dev);
+ goto err_free_usb_dev;
+ }
+
+ if (coreid == SSB_DEV_USB20_HOST) {
+ start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
+ usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len);
+ if (IS_ERR(usb_dev->ehci_dev)) {
+ err = PTR_ERR(usb_dev->ehci_dev);
+ goto err_unregister_ohci_dev;
+ }
+ }
+
+ ssb_set_drvdata(dev, usb_dev);
+ return 0;
+
+err_unregister_ohci_dev:
+ platform_device_unregister(usb_dev->ohci_dev);
+err_free_usb_dev:
+ kfree(usb_dev);
+ return err;
+}
+
+static void ssb_hcd_remove(struct ssb_device *dev)
+{
+ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
+ struct platform_device *ohci_dev = usb_dev->ohci_dev;
+ struct platform_device *ehci_dev = usb_dev->ehci_dev;
+
+ if (ohci_dev)
+ platform_device_unregister(ohci_dev);
+ if (ehci_dev)
+ platform_device_unregister(ehci_dev);
+
+ ssb_device_disable(dev, 0);
+}
+
+static void ssb_hcd_shutdown(struct ssb_device *dev)
+{
+ ssb_device_disable(dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state)
+{
+ ssb_device_disable(dev, 0);
+
+ return 0;
+}
+
+static int ssb_hcd_resume(struct ssb_device *dev)
+{
+ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
+
+ ssb_device_enable(dev, usb_dev->enable_flags);
+
+ return 0;
+}
+
+#else /* !CONFIG_PM */
+#define ssb_hcd_suspend NULL
+#define ssb_hcd_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct ssb_device_id ssb_hcd_table[] = {
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
+ {},
+};
+MODULE_DEVICE_TABLE(ssb, ssb_hcd_table);
+
+static struct ssb_driver ssb_hcd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ssb_hcd_table,
+ .probe = ssb_hcd_probe,
+ .remove = ssb_hcd_remove,
+ .shutdown = ssb_hcd_shutdown,
+ .suspend = ssb_hcd_suspend,
+ .resume = ssb_hcd_resume,
+};
+
+static int __init ssb_hcd_init(void)
+{
+ return ssb_driver_register(&ssb_hcd_driver);
+}
+module_init(ssb_hcd_init);
+
+static void __exit ssb_hcd_exit(void)
+{
+ ssb_driver_unregister(&ssb_hcd_driver);
+}
+module_exit(ssb_hcd_exit);
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
new file mode 100644
index 000000000..d51687780
--- /dev/null
+++ b/drivers/usb/host/u132-hcd.c
@@ -0,0 +1,3260 @@
+/*
+* Host Controller Driver for the Elan Digital Systems U132 adapter
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.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.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB host drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci_ids.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+ /* FIXME ohci.h is ONLY for internal use by the OHCI driver.
+ * If you're going to try stuff like this, you need to split
+ * out shareable stuff (register declarations?) into its own
+ * file, maybe name <linux/usb/ohci.h>
+ */
+
+#include "ohci.h"
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+ OHCI_INTR_WDH)
+MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
+MODULE_DESCRIPTION("U132 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+INT_MODULE_PARM(testing, 0);
+/* Some boards misreport power switching/overcurrent*/
+static bool distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+ "t setup");
+static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+/*
+* u132_module_lock exists to protect access to global variables
+*
+*/
+static struct mutex u132_module_lock;
+static int u132_exiting;
+static int u132_instances;
+static struct list_head u132_static_list;
+/*
+* end of the global variables protected by u132_module_lock
+*/
+static struct workqueue_struct *workqueue;
+#define MAX_U132_PORTS 7
+#define MAX_U132_ADDRS 128
+#define MAX_U132_UDEVS 4
+#define MAX_U132_ENDPS 100
+#define MAX_U132_RINGS 4
+static const char *cc_to_text[16] = {
+ "No Error ",
+ "CRC Error ",
+ "Bit Stuff ",
+ "Data Togg ",
+ "Stall ",
+ "DevNotResp ",
+ "PIDCheck ",
+ "UnExpPID ",
+ "DataOver ",
+ "DataUnder ",
+ "(for hw) ",
+ "(for hw) ",
+ "BufferOver ",
+ "BuffUnder ",
+ "(for HCD) ",
+ "(for HCD) "
+};
+struct u132_port {
+ struct u132 *u132;
+ int reset;
+ int enable;
+ int power;
+ int Status;
+};
+struct u132_addr {
+ u8 address;
+};
+struct u132_udev {
+ struct kref kref;
+ struct usb_device *usb_device;
+ u8 enumeration;
+ u8 udev_number;
+ u8 usb_addr;
+ u8 portnumber;
+ u8 endp_number_in[16];
+ u8 endp_number_out[16];
+};
+#define ENDP_QUEUE_SHIFT 3
+#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
+#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
+struct u132_urbq {
+ struct list_head urb_more;
+ struct urb *urb;
+};
+struct u132_spin {
+ spinlock_t slock;
+};
+struct u132_endp {
+ struct kref kref;
+ u8 udev_number;
+ u8 endp_number;
+ u8 usb_addr;
+ u8 usb_endp;
+ struct u132 *u132;
+ struct list_head endp_ring;
+ struct u132_ring *ring;
+ unsigned toggle_bits:2;
+ unsigned active:1;
+ unsigned delayed:1;
+ unsigned input:1;
+ unsigned output:1;
+ unsigned pipetype:2;
+ unsigned dequeueing:1;
+ unsigned edset_flush:1;
+ unsigned spare_bits:14;
+ unsigned long jiffies;
+ struct usb_host_endpoint *hep;
+ struct u132_spin queue_lock;
+ u16 queue_size;
+ u16 queue_last;
+ u16 queue_next;
+ struct urb *urb_list[ENDP_QUEUE_SIZE];
+ struct list_head urb_more;
+ struct delayed_work scheduler;
+};
+struct u132_ring {
+ unsigned in_use:1;
+ unsigned length:7;
+ u8 number;
+ struct u132 *u132;
+ struct u132_endp *curr_endp;
+ struct delayed_work scheduler;
+};
+struct u132 {
+ struct kref kref;
+ struct list_head u132_list;
+ struct mutex sw_lock;
+ struct mutex scheduler_lock;
+ struct u132_platform_data *board;
+ struct platform_device *platform_dev;
+ struct u132_ring ring[MAX_U132_RINGS];
+ int sequence_num;
+ int going;
+ int power;
+ int reset;
+ int num_ports;
+ u32 hc_control;
+ u32 hc_fminterval;
+ u32 hc_roothub_status;
+ u32 hc_roothub_a;
+ u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
+ int flags;
+ unsigned long next_statechange;
+ struct delayed_work monitor;
+ int num_endpoints;
+ struct u132_addr addr[MAX_U132_ADDRS];
+ struct u132_udev udev[MAX_U132_UDEVS];
+ struct u132_port port[MAX_U132_PORTS];
+ struct u132_endp *endp[MAX_U132_ENDPS];
+};
+
+/*
+* these cannot be inlines because we need the structure offset!!
+* Does anyone have a better way?????
+*/
+#define ftdi_read_pcimem(pdev, member, data) usb_ftdi_elan_read_pcimem(pdev, \
+ offsetof(struct ohci_regs, member), 0, data);
+#define ftdi_write_pcimem(pdev, member, data) usb_ftdi_elan_write_pcimem(pdev, \
+ offsetof(struct ohci_regs, member), 0, data);
+#define u132_read_pcimem(u132, member, data) \
+ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+#define u132_write_pcimem(u132, member, data) \
+ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+static inline struct u132 *udev_to_u132(struct u132_udev *udev)
+{
+ u8 udev_number = udev->udev_number;
+ return container_of(udev, struct u132, udev[udev_number]);
+}
+
+static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
+{
+ return (struct u132 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
+{
+ return container_of((void *)u132, struct usb_hcd, hcd_priv);
+}
+
+static inline void u132_disable(struct u132 *u132)
+{
+ u132_to_hcd(u132)->state = HC_STATE_HALT;
+}
+
+
+#define kref_to_u132(d) container_of(d, struct u132, kref)
+#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
+#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
+#include "../misc/usb_u132.h"
+static const char hcd_name[] = "u132_hcd";
+#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \
+ USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \
+ USB_PORT_STAT_C_RESET) << 16)
+static void u132_hcd_delete(struct kref *kref)
+{
+ struct u132 *u132 = kref_to_u132(kref);
+ struct platform_device *pdev = u132->platform_dev;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132->going += 1;
+ mutex_lock(&u132_module_lock);
+ list_del_init(&u132->u132_list);
+ u132_instances -= 1;
+ mutex_unlock(&u132_module_lock);
+ dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
+ "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
+ usb_put_hcd(hcd);
+}
+
+static inline void u132_u132_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static inline void u132_u132_init_kref(struct u132 *u132)
+{
+ kref_init(&u132->kref);
+}
+
+static void u132_udev_delete(struct kref *kref)
+{
+ struct u132_udev *udev = kref_to_u132_udev(kref);
+ udev->udev_number = 0;
+ udev->usb_device = NULL;
+ udev->usb_addr = 0;
+ udev->enumeration = 0;
+}
+
+static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_put(&udev->kref, u132_udev_delete);
+}
+
+static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_get(&udev->kref);
+}
+
+static inline void u132_udev_init_kref(struct u132 *u132,
+ struct u132_udev *udev)
+{
+ kref_init(&udev->kref);
+}
+
+static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &ring->scheduler, delta))
+ return;
+ } else if (queue_delayed_work(workqueue, &ring->scheduler, 0))
+ return;
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ kref_get(&u132->kref);
+ u132_ring_requeue_work(u132, ring, delta);
+}
+
+static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
+{
+ if (cancel_delayed_work(&ring->scheduler))
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_endp_delete(struct kref *kref)
+{
+ struct u132_endp *endp = kref_to_u132_endp(kref);
+ struct u132 *u132 = endp->u132;
+ u8 usb_addr = endp->usb_addr;
+ u8 usb_endp = endp->usb_endp;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ u8 endp_number = endp->endp_number;
+ struct usb_host_endpoint *hep = endp->hep;
+ struct u132_ring *ring = endp->ring;
+ struct list_head *head = &endp->endp_ring;
+ ring->length -= 1;
+ if (endp == ring->curr_endp) {
+ if (list_empty(head)) {
+ ring->curr_endp = NULL;
+ list_del(head);
+ } else {
+ struct u132_endp *next_endp = list_entry(head->next,
+ struct u132_endp, endp_ring);
+ ring->curr_endp = next_endp;
+ list_del(head);
+ }
+ } else
+ list_del(head);
+ if (endp->input) {
+ udev->endp_number_in[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ if (endp->output) {
+ udev->endp_number_out[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ u132->endp[endp_number - 1] = NULL;
+ hep->hcpriv = NULL;
+ kfree(endp);
+ u132_u132_put_kref(u132);
+}
+
+static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_get(&endp->kref);
+}
+
+static inline void u132_endp_init_kref(struct u132 *u132,
+ struct u132_endp *endp)
+{
+ kref_init(&endp->kref);
+ kref_get(&u132->kref);
+}
+
+static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
+ unsigned int delta)
+{
+ if (queue_delayed_work(workqueue, &endp->scheduler, delta))
+ kref_get(&endp->kref);
+}
+
+static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
+{
+ if (cancel_delayed_work(&endp->scheduler))
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_monitor_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
+{
+ if (queue_delayed_work(workqueue, &u132->monitor, delta))
+ kref_get(&u132->kref);
+}
+
+static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
+{
+ if (!queue_delayed_work(workqueue, &u132->monitor, delta))
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_cancel_work(struct u132 *u132)
+{
+ if (cancel_delayed_work(&u132->monitor))
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static int read_roothub_info(struct u132 *u132)
+{
+ u32 revision;
+ int retval;
+ retval = u132_read_pcimem(u132, revision, &revision);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ } else if ((revision & 0xFF) == 0x10) {
+ } else if ((revision & 0xFF) == 0x11) {
+ } else {
+ dev_err(&u132->platform_dev->dev, "device revision is not valid"
+ " %08X\n", revision);
+ return -ENODEV;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.status,
+ &u132->hc_roothub_status);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.status\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.a\n", retval);
+ return retval;
+ }
+ {
+ int I = u132->num_ports;
+ int i = 0;
+ while (I-- > 0) {
+ retval = u132_read_pcimem(u132, roothub.portstatus[i],
+ &u132->hc_roothub_portstatus[i]);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d acc"
+ "essing device roothub.portstatus[%d]\n"
+ , retval, i);
+ return retval;
+ } else
+ i += 1;
+ }
+ }
+ return 0;
+}
+
+static void u132_hcd_monitor_work(struct work_struct *work)
+{
+ struct u132 *u132 = container_of(work, struct u132, monitor.work);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ u132_monitor_put_kref(u132);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ int retval;
+ mutex_lock(&u132->sw_lock);
+ retval = read_roothub_info(u132);
+ if (retval) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132_disable(u132);
+ u132->going = 1;
+ mutex_unlock(&u132->sw_lock);
+ usb_hc_died(hcd);
+ ftdi_elan_gone_away(u132->platform_dev);
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ u132_monitor_requeue_work(u132, 500);
+ mutex_unlock(&u132->sw_lock);
+ return;
+ }
+ }
+}
+
+static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ }
+ mutex_lock(&u132->scheduler_lock);
+ ring = endp->ring;
+ ring->in_use = 0;
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ usb_hcd_giveback_urb(hcd, urb, status);
+}
+
+static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ u132_endp_put_kref(u132, endp);
+}
+
+static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ }
+ usb_hcd_giveback_urb(hcd, urb, status);
+}
+
+static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+
+/*
+* must not LOCK sw_lock
+*
+*/
+static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+
+ while (L-- > 0)
+ *u++ = *b++;
+
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ if (urb->actual_length > 0) {
+ int retval;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_interrupt_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ } else {
+ ring->in_use = 0;
+ endp->active = 0;
+ endp->jiffies = jiffies +
+ msecs_to_jiffies(urb->interval);
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ }
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 1 & toggle_bits);
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p givin"
+ "g back INTERRUPT %s\n", urb,
+ cc_to_text[condition_code]);
+ }
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ struct u132_ring *ring = endp->ring;
+ urb->actual_length += len;
+ endp->toggle_bits = toggle_bits;
+ if (urb->transfer_buffer_length > urb->actual_length) {
+ int retval;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_bulk_output_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+
+ while (L-- > 0)
+ *u++ = *b++;
+
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ int retval;
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_DATAUNDERRUN) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
+ ") giving back BULK IN %s\n", urb,
+ cc_to_text[condition_code]);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
+ "ULK IN code=%d %s\n", urb, condition_code,
+ cc_to_text[condition_code]);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+
+ while (L-- > 0)
+ *u++ = *b++;
+
+ urb->actual_length = len;
+ if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
+ TD_DATAUNDERRUN) && ((urb->transfer_flags &
+ URB_SHORT_NOT_OK) == 0))) {
+ int retval;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0x3,
+ u132_hcd_configure_empty_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ mutex_unlock(&u132->scheduler_lock);
+ dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
+ "NPUT STALL urb %p\n", urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ mutex_unlock(&u132->scheduler_lock);
+ dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
+ "PUT %s urb %p\n", cc_to_text[condition_code],
+ urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ if (usb_pipein(urb->pipe)) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_input_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_empty_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ u132->addr[0].address = 0;
+ endp->usb_addr = udev->usb_addr;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, 0, endp->usb_endp, 0,
+ u132_hcd_enumeration_empty_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+
+ while (L-- > 0)
+ *u++ = *b++;
+
+ urb->actual_length = len;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0x3,
+ u132_hcd_initial_empty_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ mutex_lock(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (!urb->unlinked) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0,
+ u132_hcd_initial_input_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
+ "unlinked=%d\n", urb, urb->unlinked);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+}
+
+/*
+* this work function is only executed from the work queue
+*
+*/
+static void u132_hcd_ring_work_scheduler(struct work_struct *work)
+{
+ struct u132_ring *ring =
+ container_of(work, struct u132_ring, scheduler.work);
+ struct u132 *u132 = ring->u132;
+ mutex_lock(&u132->scheduler_lock);
+ if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else if (ring->curr_endp) {
+ struct u132_endp *last_endp = ring->curr_endp;
+ struct list_head *scan;
+ struct list_head *head = &last_endp->endp_ring;
+ unsigned long wakeup = 0;
+ list_for_each(scan, head) {
+ struct u132_endp *endp = list_entry(scan,
+ struct u132_endp, endp_ring);
+ if (endp->queue_next == endp->queue_last) {
+ } else if ((endp->delayed == 0)
+ || time_after_eq(jiffies, endp->jiffies)) {
+ ring->curr_endp = endp;
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ }
+ if (last_endp->queue_next == last_endp->queue_last) {
+ } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
+ last_endp->jiffies)) {
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = last_endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ if (wakeup > 0) {
+ u132_ring_requeue_work(u132, ring, wakeup);
+ mutex_unlock(&u132->scheduler_lock);
+ return;
+ } else {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+ } else {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+}
+
+static void u132_hcd_endp_work_scheduler(struct work_struct *work)
+{
+ struct u132_ring *ring;
+ struct u132_endp *endp =
+ container_of(work, struct u132_endp, scheduler.work);
+ struct u132 *u132 = endp->u132;
+ mutex_lock(&u132->scheduler_lock);
+ ring = endp->ring;
+ if (endp->edset_flush) {
+ endp->edset_flush = 0;
+ if (endp->dequeueing)
+ usb_ftdi_elan_edset_flush(u132->platform_dev,
+ ring->number, endp);
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->active) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->queue_next == endp->queue_last) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->pipetype == PIPE_INTERRUPT) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_interrupt_recv);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else if (endp->pipetype == PIPE_CONTROL) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (address == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_initial_setup_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (endp->usb_addr == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
+ u132_hcd_enumeration_address_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ address = u132->addr[endp->usb_addr].address;
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_configure_setup_sent);
+ if (retval != 0)
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ if (endp->input) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_input(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ } else { /* output pipe */
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ mutex_unlock(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ mutex_unlock(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_output_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ }
+ }
+}
+#ifdef CONFIG_PM
+
+static void port_power(struct u132 *u132, int pn, int is_on)
+{
+ u132->port[pn].power = is_on;
+}
+
+#endif
+
+static void u132_power(struct u132 *u132, int is_on)
+{
+ struct usb_hcd *hcd = u132_to_hcd(u132)
+ ; /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (u132->power)
+ return;
+ u132->power = 1;
+ } else {
+ u132->power = 0;
+ hcd->state = HC_STATE_HALT;
+ }
+}
+
+static int u132_periodic_reinit(struct u132 *u132)
+{
+ int retval;
+ u32 fi = u132->hc_fminterval & 0x03fff;
+ u32 fit;
+ u32 fminterval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ fit = fminterval & FIT;
+ retval = u132_write_pcimem(u132, fminterval,
+ (fit ^ FIT) | u132->hc_fminterval);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, periodicstart,
+ ((9 * fi) / 10) & 0x3fff);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static char *hcfs2string(int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET:
+ return "reset";
+ case OHCI_USB_RESUME:
+ return "resume";
+ case OHCI_USB_OPER:
+ return "operational";
+ case OHCI_USB_SUSPEND:
+ return "suspend";
+ }
+ return "?";
+}
+
+static int u132_init(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u132_disable(u132);
+ u132->next_statechange = jiffies;
+ retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ if (u132->num_ports == 0) {
+ u32 rh_a = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ u132->num_ports = rh_a & RH_A_NDP;
+ retval = read_roothub_info(u132);
+ if (retval)
+ return retval;
+ }
+ if (u132->num_ports > MAX_U132_PORTS)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/* Start an OHCI controller, set the BUS operational
+* resets USB and controller
+* enable interrupts
+*/
+static int u132_run(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u32 status;
+ u32 fminterval;
+ u32 periodicstart;
+ u32 cmdstatus;
+ u32 roothub_a;
+ int mask = OHCI_INTR_INIT;
+ int first = u132->hc_fminterval == 0;
+ int sleep_time = 0;
+ int reset_timeout = 30; /* ... allow extra time */
+ u132_disable(u132);
+ if (first) {
+ u32 temp;
+ retval = u132_read_pcimem(u132, fminterval, &temp);
+ if (retval)
+ return retval;
+ u132->hc_fminterval = temp & 0x3fff;
+ u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval)
+ return retval;
+ dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
+ "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
+ u132->hc_control);
+ switch (u132->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ sleep_time = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESUME;
+ sleep_time = 10;
+ break;
+ default:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESET;
+ sleep_time = 50;
+ break;
+ }
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ msleep(sleep_time);
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ if (!(roothub_a & RH_A_NPS)) {
+ int temp; /* power down each port */
+ for (temp = 0; temp < u132->num_ports; temp++) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[temp], RH_PS_LSDA);
+ if (retval)
+ return retval;
+ }
+ }
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+retry:
+ retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, cmdstatus, OHCI_HCR);
+ if (retval)
+ return retval;
+extra: {
+ retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ if (0 != (status & OHCI_HCR)) {
+ if (--reset_timeout == 0) {
+ dev_err(&u132->platform_dev->dev, "USB HC reset"
+ " timed out!\n");
+ return -ENODEV;
+ } else {
+ msleep(5);
+ goto extra;
+ }
+ }
+ }
+ if (u132->flags & OHCI_QUIRK_INITRESET) {
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, hcca, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_periodic_reinit(u132);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
+ if (retval)
+ return retval;
+ if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+ if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
+ u132->flags |= OHCI_QUIRK_INITRESET;
+ goto retry;
+ } else
+ dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
+ "\n", fminterval, periodicstart);
+ } /* start controller operations */
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, cmdstatus, OHCI_BLF);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrstatus, mask);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrdisable,
+ OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+ OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+ OHCI_INTR_SO);
+ if (retval)
+ return retval; /* handle root hub init quirks ... */
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+ if (u132->flags & OHCI_QUIRK_SUPERIO) {
+ roothub_a |= RH_A_NOCP;
+ roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+ roothub_a |= RH_A_NPS;
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, roothub.b,
+ (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ mdelay((roothub_a >> 23) & 0x1fe);
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+static void u132_hcd_stop(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "u132 device %p(hcd=%p) has b"
+ "een removed %d\n", u132, hcd, u132->going);
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ } else {
+ mutex_lock(&u132->sw_lock);
+ msleep(100);
+ u132_power(u132, 0);
+ mutex_unlock(&u132->sw_lock);
+ }
+}
+
+static int u132_hcd_start(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else if (hcd->self.controller) {
+ int retval;
+ struct platform_device *pdev =
+ to_platform_device(hcd->self.controller);
+ u16 vendor = ((struct u132_platform_data *)
+ dev_get_platdata(&pdev->dev))->vendor;
+ u16 device = ((struct u132_platform_data *)
+ dev_get_platdata(&pdev->dev))->device;
+ mutex_lock(&u132->sw_lock);
+ msleep(10);
+ if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
+ u132->flags = OHCI_QUIRK_AMD756;
+ } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
+ dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
+ "ounds unavailable\n");
+ } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
+ u132->flags |= OHCI_QUIRK_ZFMICRO;
+ retval = u132_run(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ msleep(100);
+ mutex_unlock(&u132->sw_lock);
+ return retval;
+ } else {
+ dev_err(&u132->platform_dev->dev, "platform_device missing\n");
+ return -ENODEV;
+ }
+}
+
+static int u132_hcd_reset(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval;
+ mutex_lock(&u132->sw_lock);
+ retval = u132_init(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ mutex_unlock(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int create_endpoint_and_queue_int(struct u132 *u132,
+ struct u132_udev *udev, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ int rc;
+ u8 endp_number;
+ struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
+
+ if (!endp)
+ return -ENOMEM;
+
+ spin_lock_init(&endp->queue_lock.slock);
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
+ if (rc) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(endp);
+ return rc;
+ }
+
+ endp_number = ++u132->num_endpoints;
+ urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
+ INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = urb->ep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ urb->hcpriv = u132;
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
+ return 0;
+}
+
+static int queue_int_on_old_endpoint(struct u132 *u132,
+ struct u132_udev *udev, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_bulk(struct u132 *u132,
+ struct u132_udev *udev, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ int ring_number;
+ struct u132_ring *ring;
+ unsigned long irqs;
+ int rc;
+ u8 endp_number;
+ struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
+
+ if (!endp)
+ return -ENOMEM;
+
+ spin_lock_init(&endp->queue_lock.slock);
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
+ if (rc) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(endp);
+ return rc;
+ }
+
+ endp_number = ++u132->num_endpoints;
+ urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
+ INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
+ INIT_LIST_HEAD(&endp->urb_more);
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = urb->ep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ ring_number = 3;
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ ring_number = 2;
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ ring = endp->ring = &u132->ring[ring_number - 1];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ urb->hcpriv = u132;
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+}
+
+static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+ struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_control(struct u132 *u132,
+ struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ int rc;
+ u8 endp_number;
+ struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
+
+ if (!endp)
+ return -ENOMEM;
+
+ spin_lock_init(&endp->queue_lock.slock);
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
+ if (rc) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(endp);
+ return rc;
+ }
+
+ endp_number = ++u132->num_endpoints;
+ urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
+ INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = urb->ep;
+ u132_endp_init_kref(u132, endp);
+ u132_endp_get_kref(u132, endp);
+ if (usb_addr == 0) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_init_kref(u132, udev);
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ } else { /*(usb_addr > 0) */
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_get_kref(u132, udev);
+ udev->enumeration = 2;
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+}
+
+static int queue_control_on_old_endpoint(struct u132 *u132,
+ struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp)
+{
+ if (usb_addr == 0) {
+ if (usb_pipein(urb->pipe)) {
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ } else { /* usb_pipeout(urb->pipe) */
+ struct u132_addr *addr = &u132->addr[usb_dev->devnum];
+ int I = MAX_U132_UDEVS;
+ int i = 0;
+ while (--I > 0) {
+ struct u132_udev *udev = &u132->udev[++i];
+ if (udev->usb_device) {
+ continue;
+ } else {
+ udev->enumeration = 1;
+ u132->addr[0].address = i;
+ endp->udev_number = i;
+ udev->udev_number = i;
+ udev->usb_addr = usb_dev->devnum;
+ u132_udev_init_kref(u132, udev);
+ udev->endp_number_in[usb_endp] =
+ endp->endp_number;
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_out[usb_endp] =
+ endp->endp_number;
+ udev->usb_device = usb_dev;
+ ((u8 *) (urb->setup_packet))[2] =
+ addr->address = i;
+ u132_udev_get_kref(u132, udev);
+ break;
+ }
+ }
+ if (I == 0) {
+ dev_err(&u132->platform_dev->dev, "run out of d"
+ "evice space\n");
+ return -EINVAL;
+ }
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+ } else { /*(usb_addr > 0) */
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ urb->hcpriv = u132;
+ if (udev->enumeration != 2)
+ udev->enumeration = 2;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+}
+
+static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (irqs_disabled()) {
+ if (__GFP_WAIT & mem_flags) {
+ printk(KERN_ERR "invalid context for function that migh"
+ "t sleep\n");
+ return -EINVAL;
+ }
+ }
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed "
+ "urb=%p\n", urb);
+ return -ESHUTDOWN;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ struct usb_device *usb_dev = urb->dev;
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = urb->ep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval == 0) {
+ retval = queue_int_on_old_endpoint(
+ u132, udev, urb,
+ usb_dev, endp,
+ usb_addr, usb_endp,
+ address);
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(
+ hcd, urb);
+ }
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp,
+ msecs_to_jiffies(urb->interval))
+ ;
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else { /*(endp == NULL) */
+ return create_endpoint_and_queue_int(u132, udev,
+ urb, usb_dev, usb_addr,
+ usb_endp, address, mem_flags);
+ }
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ dev_err(&u132->platform_dev->dev, "the hardware does no"
+ "t support PIPE_ISOCHRONOUS\n");
+ return -EINVAL;
+ } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = urb->ep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval == 0) {
+ retval = queue_bulk_on_old_endpoint(
+ u132, udev, urb,
+ usb_dev, endp,
+ usb_addr, usb_endp,
+ address);
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(
+ hcd, urb);
+ }
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_bulk(u132,
+ udev, urb, usb_dev, usb_addr,
+ usb_endp, address, mem_flags);
+ } else {
+ struct u132_endp *endp = urb->ep->hcpriv;
+ u16 urb_size = 8;
+ u8 *b = urb->setup_packet;
+ int i = 0;
+ char data[30 * 3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int l = 0;
+ data[0] = 0;
+ while (urb_size-- > 0) {
+ if (i > m) {
+ } else if (i++ < m) {
+ int w = sprintf(d, " %02X", *b++);
+ d += w;
+ l += w;
+ } else
+ d += sprintf(d, " ..");
+ }
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval == 0) {
+ retval = queue_control_on_old_endpoint(
+ u132, urb, usb_dev,
+ endp, usb_addr,
+ usb_endp);
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(
+ hcd, urb);
+ }
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_control(u132,
+ urb, usb_dev, usb_addr, usb_endp,
+ mem_flags);
+ }
+ }
+}
+
+static int dequeue_from_overflow_chain(struct u132 *u132,
+ struct u132_endp *endp, struct urb *urb)
+{
+ struct list_head *scan;
+ struct list_head *head = &endp->urb_more;
+ list_for_each(scan, head) {
+ struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
+ urb_more);
+ if (urbq->urb == urb) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ list_del(scan);
+ endp->queue_size -= 1;
+ urb->error_count = 0;
+ usb_hcd_giveback_urb(hcd, urb, 0);
+ return 0;
+ } else
+ continue;
+ }
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
+ "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
+ "\n", urb, endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ return -EINVAL;
+}
+
+static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ unsigned long irqs;
+ int rc;
+
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ rc = usb_hcd_check_unlink_urb(u132_to_hcd(u132), urb, status);
+ if (rc) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return rc;
+ }
+ if (endp->queue_size == 0) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
+ "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ }
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
+ if (endp->active) {
+ endp->dequeueing = 1;
+ endp->edset_flush = 1;
+ u132_endp_queue_work(u132, endp, 0);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return 0;
+ } else {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_hcd_abandon_urb(u132, endp, urb, status);
+ return 0;
+ }
+ } else {
+ u16 queue_list = 0;
+ u16 queue_size = endp->queue_size;
+ u16 queue_scan = endp->queue_next;
+ struct urb **urb_slot = NULL;
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan]) {
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ break;
+ } else
+ continue;
+ }
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan];
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ }
+ if (urb_slot) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ endp->queue_size -= 1;
+ if (list_empty(&endp->urb_more)) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next,
+ struct u132_urbq, urb_more);
+ list_del(next);
+ *urb_slot = urbq->urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ kfree(urbq);
+ } urb->error_count = 0;
+ usb_hcd_giveback_urb(hcd, urb, status);
+ return 0;
+ } else if (list_empty(&endp->urb_more)) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in "
+ "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
+ "=%d size=%d next=%04X last=%04X\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ',
+ endp->output ? 'O' : ' ', endp->usb_endp,
+ endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ } else {
+ int retval;
+
+ usb_hcd_unlink_urb_from_ep(u132_to_hcd(u132), urb);
+ retval = dequeue_from_overflow_chain(u132, endp,
+ urb);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return retval;
+ }
+ }
+}
+
+static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ if (usb_pipein(urb->pipe)) {
+ u8 endp_number = udev->endp_number_in[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb, status);
+ } else {
+ u8 endp_number = udev->endp_number_out[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb, status);
+ }
+ }
+}
+
+static void u132_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "u132 device %p(hcd=%p hep=%p"
+ ") has been removed %d\n", u132, hcd, hep,
+ u132->going);
+ } else {
+ struct u132_endp *endp = hep->hcpriv;
+ if (endp)
+ u132_endp_put_kref(u132, endp);
+ }
+}
+
+static int u132_get_frame(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int frame = 0;
+ dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
+ msleep(100);
+ return frame;
+ }
+}
+
+static int u132_roothub_descriptor(struct u132 *u132,
+ struct usb_hub_descriptor *desc)
+{
+ int retval;
+ u16 temp;
+ u32 rh_a = -1;
+ u32 rh_b = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ desc->bDescriptorType = USB_DT_HUB;
+ desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = u132->num_ports;
+ temp = 1 + (u132->num_ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+ temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
+ if (rh_a & RH_A_NPS)
+ temp |= HUB_CHAR_NO_LPSM;
+ if (rh_a & RH_A_PSM)
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
+ if (rh_a & RH_A_NOCP)
+ temp |= HUB_CHAR_NO_OCPM;
+ else if (rh_a & RH_A_OCPM)
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+ retval = u132_read_pcimem(u132, roothub.b, &rh_b);
+ if (retval)
+ return retval;
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ desc->u.hs.DeviceRemovable[0] = rh_b & RH_B_DR;
+ if (u132->num_ports > 7) {
+ desc->u.hs.DeviceRemovable[1] = (rh_b & RH_B_DR) >> 8;
+ desc->u.hs.DeviceRemovable[2] = 0xff;
+ } else
+ desc->u.hs.DeviceRemovable[1] = 0xff;
+ return 0;
+}
+
+static int u132_roothub_status(struct u132 *u132, __le32 *desc)
+{
+ u32 rh_status = -1;
+ int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
+ *desc = cpu_to_le32(rh_status);
+ return ret_status;
+}
+
+static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port = wIndex - 1;
+ u32 rh_portstatus = -1;
+ int ret_portstatus = u132_read_pcimem(u132,
+ roothub.portstatus[port], &rh_portstatus);
+ *desc = cpu_to_le32(rh_portstatus);
+ if (*(u16 *) (desc + 2)) {
+ dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
+ "ge = %08X\n", port, *desc);
+ }
+ return ret_portstatus;
+ }
+}
+
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+#define PORT_RESET_MSEC 10
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+static int u132_roothub_portreset(struct u132 *u132, int port_index)
+{
+ int retval;
+ u32 fmnumber;
+ u16 now;
+ u16 reset_done;
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ reset_done = now + PORT_RESET_MSEC;
+ do {
+ u32 portstat;
+ do {
+ retval = u132_read_pcimem(u132,
+ roothub.portstatus[port_index], &portstat);
+ if (retval)
+ return retval;
+ if (RH_PS_PRS & portstat)
+ continue;
+ else
+ break;
+ } while (tick_before(now, reset_done));
+ if (RH_PS_PRS & portstat)
+ return -ENODEV;
+ if (RH_PS_CCS & portstat) {
+ if (RH_PS_PRSC & portstat) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index],
+ RH_PS_PRSC);
+ if (retval)
+ return retval;
+ }
+ } else
+ break; /* start the next reset,
+ sleep till it's probably done */
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ RH_PS_PRS);
+ if (retval)
+ return retval;
+ msleep(PORT_RESET_HW_MSEC);
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ } while (tick_before(now, reset_done));
+ return 0;
+}
+
+static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int retval;
+ int port_index = wIndex - 1;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PSS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_POWER:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PPS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_RESET:
+ retval = u132_roothub_portreset(u132, port_index);
+ if (retval)
+ return retval;
+ return 0;
+ default:
+ return -EPIPE;
+ }
+ }
+}
+
+static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port_index = wIndex - 1;
+ u32 temp;
+ int retval;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ if ((u132->hc_control & OHCI_CTRL_HCFS)
+ != OHCI_USB_OPER) {
+ dev_err(&u132->platform_dev->dev, "TODO resume_"
+ "root_hub\n");
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ return -EPIPE;
+ }
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ temp);
+ if (retval)
+ return retval;
+ return 0;
+ }
+}
+
+
+/* the virtual root hub timer IRQ checks for hub status*/
+static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
+ "ed %d\n", hcd, u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ return -ESHUTDOWN;
+ } else {
+ int i, changed = 0, length = 1;
+ if (u132->flags & OHCI_QUIRK_AMD756) {
+ if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
+ dev_err(&u132->platform_dev->dev, "bogus NDP, r"
+ "ereads as NDP=%d\n",
+ u132->hc_roothub_a & RH_A_NDP);
+ goto done;
+ }
+ }
+ if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC))
+ buf[0] = changed = 1;
+ else
+ buf[0] = 0;
+ if (u132->num_ports > 7) {
+ buf[1] = 0;
+ length++;
+ }
+ for (i = 0; i < u132->num_ports; i++) {
+ if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
+ RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+ RH_PS_PRSC)) {
+ changed = 1;
+ if (i < 7)
+ buf[0] |= 1 << (i + 1);
+ else
+ buf[1] |= 1 << (i - 7);
+ continue;
+ }
+ if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS))
+ continue;
+
+ if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS))
+ continue;
+ }
+done:
+ return changed ? length : 0;
+ }
+}
+
+static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ mutex_lock(&u132->sw_lock);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case ClearPortFeature:{
+ retval = u132_roothub_clearportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubDescriptor:{
+ retval = u132_roothub_descriptor(u132,
+ (struct usb_hub_descriptor *)buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubStatus:{
+ retval = u132_roothub_status(u132,
+ (__le32 *) buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetPortStatus:{
+ retval = u132_roothub_portstatus(u132,
+ (__le32 *) buf, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case SetPortFeature:{
+ retval = u132_roothub_setportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ default:
+ goto stall;
+ error:
+ u132_disable(u132);
+ u132->going = 1;
+ break;
+ stall:
+ retval = -EPIPE;
+ break;
+ }
+ mutex_unlock(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int u132_bus_suspend(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_bus_resume(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+#else
+#define u132_bus_suspend NULL
+#define u132_bus_resume NULL
+#endif
+static struct hc_driver u132_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct u132),
+ .irq = NULL,
+ .flags = HCD_USB11 | HCD_MEMORY,
+ .reset = u132_hcd_reset,
+ .start = u132_hcd_start,
+ .stop = u132_hcd_stop,
+ .urb_enqueue = u132_urb_enqueue,
+ .urb_dequeue = u132_urb_dequeue,
+ .endpoint_disable = u132_endpoint_disable,
+ .get_frame_number = u132_get_frame,
+ .hub_status_data = u132_hub_status_data,
+ .hub_control = u132_hub_control,
+ .bus_suspend = u132_bus_suspend,
+ .bus_resume = u132_bus_resume,
+ .start_port_reset = u132_start_port_reset,
+};
+
+/*
+* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
+* is held for writing, thus this module must not call usb_remove_hcd()
+* synchronously - but instead should immediately stop activity to the
+* device and asynchronously call usb_remove_hcd()
+*/
+static int u132_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ if (hcd) {
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going++ > 1) {
+ dev_err(&u132->platform_dev->dev, "already being remove"
+ "d\n");
+ return -ENODEV;
+ } else {
+ int rings = MAX_U132_RINGS;
+ int endps = MAX_U132_ENDPS;
+ dev_err(&u132->platform_dev->dev, "removing device u132"
+ ".%d\n", u132->sequence_num);
+ msleep(100);
+ mutex_lock(&u132->sw_lock);
+ u132_monitor_cancel_work(u132);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ u132_ring_cancel_work(u132, ring);
+ } while (endps-- > 0) {
+ struct u132_endp *endp = u132->endp[endps];
+ if (endp)
+ u132_endp_cancel_work(u132, endp);
+ }
+ u132->going += 1;
+ printk(KERN_INFO "removing device u132.%d\n",
+ u132->sequence_num);
+ mutex_unlock(&u132->sw_lock);
+ usb_remove_hcd(hcd);
+ u132_u132_put_kref(u132);
+ return 0;
+ }
+ } else
+ return 0;
+}
+
+static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
+{
+ int rings = MAX_U132_RINGS;
+ int ports = MAX_U132_PORTS;
+ int addrs = MAX_U132_ADDRS;
+ int udevs = MAX_U132_UDEVS;
+ int endps = MAX_U132_ENDPS;
+ u132->board = dev_get_platdata(&pdev->dev);
+ u132->platform_dev = pdev;
+ u132->power = 0;
+ u132->reset = 0;
+ mutex_init(&u132->sw_lock);
+ mutex_init(&u132->scheduler_lock);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ ring->u132 = u132;
+ ring->number = rings + 1;
+ ring->length = 0;
+ ring->curr_endp = NULL;
+ INIT_DELAYED_WORK(&ring->scheduler,
+ u132_hcd_ring_work_scheduler);
+ }
+ mutex_lock(&u132->sw_lock);
+ INIT_DELAYED_WORK(&u132->monitor, u132_hcd_monitor_work);
+ while (ports-- > 0) {
+ struct u132_port *port = &u132->port[ports];
+ port->u132 = u132;
+ port->reset = 0;
+ port->enable = 0;
+ port->power = 0;
+ port->Status = 0;
+ }
+ while (addrs-- > 0) {
+ struct u132_addr *addr = &u132->addr[addrs];
+ addr->address = 0;
+ }
+ while (udevs-- > 0) {
+ struct u132_udev *udev = &u132->udev[udevs];
+ int i = ARRAY_SIZE(udev->endp_number_in);
+ int o = ARRAY_SIZE(udev->endp_number_out);
+ udev->usb_device = NULL;
+ udev->udev_number = 0;
+ udev->usb_addr = 0;
+ udev->portnumber = 0;
+ while (i-- > 0)
+ udev->endp_number_in[i] = 0;
+
+ while (o-- > 0)
+ udev->endp_number_out[o] = 0;
+
+ }
+ while (endps-- > 0)
+ u132->endp[endps] = NULL;
+
+ mutex_unlock(&u132->sw_lock);
+}
+
+static int u132_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ int retval;
+ u32 control;
+ u32 rh_a = -1;
+ u32 num_ports;
+
+ msleep(100);
+ if (u132_exiting > 0)
+ return -ENODEV;
+
+ retval = ftdi_write_pcimem(pdev, intrdisable, OHCI_INTR_MIE);
+ if (retval)
+ return retval;
+ retval = ftdi_read_pcimem(pdev, control, &control);
+ if (retval)
+ return retval;
+ retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ num_ports = rh_a & RH_A_NDP; /* refuse to confuse usbcore */
+ if (pdev->dev.dma_mask)
+ return -EINVAL;
+
+ hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
+ );
+ ftdi_elan_gone_away(pdev);
+ return -ENOMEM;
+ } else {
+ struct u132 *u132 = hcd_to_u132(hcd);
+ retval = 0;
+ hcd->rsrc_start = 0;
+ mutex_lock(&u132_module_lock);
+ list_add_tail(&u132->u132_list, &u132_static_list);
+ u132->sequence_num = ++u132_instances;
+ mutex_unlock(&u132_module_lock);
+ u132_u132_init_kref(u132);
+ u132_initialise(u132, pdev);
+ hcd->product_desc = "ELAN U132 Host Controller";
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval != 0) {
+ dev_err(&u132->platform_dev->dev, "init error %d\n",
+ retval);
+ u132_u132_put_kref(u132);
+ return retval;
+ } else {
+ device_wakeup_enable(hcd->self.controller);
+ u132_monitor_queue_work(u132, 100);
+ return 0;
+ }
+ }
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * for this device there's no useful distinction between the controller
+ * and its root hub.
+ */
+static int u132_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0, ports;
+
+ switch (state.event) {
+ case PM_EVENT_FREEZE:
+ retval = u132_bus_suspend(hcd);
+ break;
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_HIBERNATE:
+ ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 0);
+ }
+ break;
+ }
+ return retval;
+ }
+}
+
+static int u132_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ if (!u132->port[0].power) {
+ int ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 1);
+ }
+ retval = 0;
+ } else {
+ retval = u132_bus_resume(hcd);
+ }
+ return retval;
+ }
+}
+
+#else
+#define u132_suspend NULL
+#define u132_resume NULL
+#endif
+/*
+* this driver is loaded explicitly by ftdi_u132
+*
+* the platform_driver struct is static because it is per type of module
+*/
+static struct platform_driver u132_platform_driver = {
+ .probe = u132_probe,
+ .remove = u132_remove,
+ .suspend = u132_suspend,
+ .resume = u132_resume,
+ .driver = {
+ .name = hcd_name,
+ },
+};
+static int __init u132_hcd_init(void)
+{
+ int retval;
+ INIT_LIST_HEAD(&u132_static_list);
+ u132_instances = 0;
+ u132_exiting = 0;
+ mutex_init(&u132_module_lock);
+ if (usb_disabled())
+ return -ENODEV;
+ printk(KERN_INFO "driver %s\n", hcd_name);
+ workqueue = create_singlethread_workqueue("u132");
+ retval = platform_driver_register(&u132_platform_driver);
+ return retval;
+}
+
+
+module_init(u132_hcd_init);
+static void __exit u132_hcd_exit(void)
+{
+ struct u132 *u132;
+ struct u132 *temp;
+ mutex_lock(&u132_module_lock);
+ u132_exiting += 1;
+ mutex_unlock(&u132_module_lock);
+ list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
+ platform_device_unregister(u132->platform_dev);
+ }
+ platform_driver_unregister(&u132_platform_driver);
+ printk(KERN_INFO "u132-hcd driver deregistered\n");
+ wait_event(u132_hcd_wait, u132_instances == 0);
+ flush_workqueue(workqueue);
+ destroy_workqueue(workqueue);
+}
+
+
+module_exit(u132_hcd_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:u132_hcd");
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
new file mode 100644
index 000000000..1b28a000d
--- /dev/null
+++ b/drivers/usb/host/uhci-debug.c
@@ -0,0 +1,655 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * Kernel visible pointers are surrounded in []s and bus
+ * visible pointers are surrounded in ()s
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2001 Johannes Erdfelt
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <asm/io.h>
+
+#include "uhci-hcd.h"
+
+#define EXTRA_SPACE 1024
+
+static struct dentry *uhci_debugfs_root;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+/* Handle REALLY large printks so we don't overflow buffers */
+static void lprintk(char *buf)
+{
+ char *p;
+
+ /* Just write one line at a time */
+ while (buf) {
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+ printk(KERN_DEBUG "%s\n", buf);
+ buf = p;
+ if (buf)
+ buf++;
+ }
+}
+
+static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf,
+ int len, int space)
+{
+ char *out = buf;
+ char *spid;
+ u32 status, token;
+
+ status = td_status(uhci, td);
+ out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td,
+ hc32_to_cpu(uhci, td->link));
+ out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+ ((status >> 27) & 3),
+ (status & TD_CTRL_SPD) ? "SPD " : "",
+ (status & TD_CTRL_LS) ? "LS " : "",
+ (status & TD_CTRL_IOC) ? "IOC " : "",
+ (status & TD_CTRL_ACTIVE) ? "Active " : "",
+ (status & TD_CTRL_STALLED) ? "Stalled " : "",
+ (status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+ (status & TD_CTRL_BABBLE) ? "Babble " : "",
+ (status & TD_CTRL_NAK) ? "NAK " : "",
+ (status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+ (status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
+ status & 0x7ff);
+ if (out - buf > len)
+ goto done;
+
+ token = td_token(uhci, td);
+ switch (uhci_packetid(token)) {
+ case USB_PID_SETUP:
+ spid = "SETUP";
+ break;
+ case USB_PID_OUT:
+ spid = "OUT";
+ break;
+ case USB_PID_IN:
+ spid = "IN";
+ break;
+ default:
+ spid = "?";
+ break;
+ }
+
+ out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+ token >> 21,
+ ((token >> 19) & 1),
+ (token >> 15) & 15,
+ (token >> 8) & 127,
+ (token & 0xff),
+ spid);
+ out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer));
+
+done:
+ if (out - buf > len)
+ out += sprintf(out, " ...\n");
+ return out - buf;
+}
+
+static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp,
+ char *buf, int len, int space)
+{
+ char *out = buf;
+ struct uhci_td *td;
+ int i, nactive, ninactive;
+ char *ptype;
+
+
+ out += sprintf(out, "urb_priv [%p] ", urbp);
+ out += sprintf(out, "urb [%p] ", urbp->urb);
+ out += sprintf(out, "qh [%p] ", urbp->qh);
+ out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
+ out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe),
+ (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
+ if (out - buf > len)
+ goto done;
+
+ switch (usb_pipetype(urbp->urb->pipe)) {
+ case PIPE_ISOCHRONOUS: ptype = "ISO"; break;
+ case PIPE_INTERRUPT: ptype = "INT"; break;
+ case PIPE_BULK: ptype = "BLK"; break;
+ default:
+ case PIPE_CONTROL: ptype = "CTL"; break;
+ }
+
+ out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
+ out += sprintf(out, " Actlen=%d%s", urbp->urb->actual_length,
+ (urbp->qh->type == USB_ENDPOINT_XFER_CONTROL ?
+ "-8" : ""));
+
+ if (urbp->urb->unlinked)
+ out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked);
+ out += sprintf(out, "\n");
+
+ if (out - buf > len)
+ goto done;
+
+ i = nactive = ninactive = 0;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
+ (++i <= 10 || debug > 2)) {
+ out += sprintf(out, "%*s%d: ", space + 2, "", i);
+ out += uhci_show_td(uhci, td, out,
+ len - (out - buf), 0);
+ if (out - buf > len)
+ goto tail;
+ } else {
+ if (td_status(uhci, td) & TD_CTRL_ACTIVE)
+ ++nactive;
+ else
+ ++ninactive;
+ }
+ }
+ if (nactive + ninactive > 0)
+ out += sprintf(out,
+ "%*s[skipped %d inactive and %d active TDs]\n",
+ space, "", ninactive, nactive);
+done:
+ if (out - buf > len)
+ out += sprintf(out, " ...\n");
+tail:
+ return out - buf;
+}
+
+static int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
+{
+ char *out = buf;
+ int i, nurbs;
+ __hc32 element = qh_element(qh);
+ char *qtype;
+
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_ISOC: qtype = "ISO"; break;
+ case USB_ENDPOINT_XFER_INT: qtype = "INT"; break;
+ case USB_ENDPOINT_XFER_BULK: qtype = "BLK"; break;
+ case USB_ENDPOINT_XFER_CONTROL: qtype = "CTL"; break;
+ default: qtype = "Skel" ; break;
+ }
+
+ out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
+ space, "", qh, qtype,
+ hc32_to_cpu(uhci, qh->link),
+ hc32_to_cpu(uhci, element));
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ out += sprintf(out,
+ "%*s period %d phase %d load %d us, frame %x desc [%p]\n",
+ space, "", qh->period, qh->phase, qh->load,
+ qh->iso_frame, qh->iso_packet_desc);
+ else if (qh->type == USB_ENDPOINT_XFER_INT)
+ out += sprintf(out, "%*s period %d phase %d load %d us\n",
+ space, "", qh->period, qh->phase, qh->load);
+ if (out - buf > len)
+ goto done;
+
+ if (element & UHCI_PTR_QH(uhci))
+ out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
+
+ if (element & UHCI_PTR_DEPTH(uhci))
+ out += sprintf(out, "%*s Depth traverse\n", space, "");
+
+ if (element & cpu_to_hc32(uhci, 8))
+ out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
+
+ if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci))))
+ out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
+
+ if (out - buf > len)
+ goto done;
+
+ if (list_empty(&qh->queue)) {
+ out += sprintf(out, "%*s queue is empty\n", space, "");
+ if (qh == uhci->skel_async_qh) {
+ out += uhci_show_td(uhci, uhci->term_td, out,
+ len - (out - buf), 0);
+ if (out - buf > len)
+ goto tail;
+ }
+ } else {
+ struct urb_priv *urbp = list_entry(qh->queue.next,
+ struct urb_priv, node);
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+
+ if (element != LINK_TO_TD(uhci, td))
+ out += sprintf(out, "%*s Element != First TD\n",
+ space, "");
+ i = nurbs = 0;
+ list_for_each_entry(urbp, &qh->queue, node) {
+ if (++i <= 10) {
+ out += uhci_show_urbp(uhci, urbp, out,
+ len - (out - buf), space + 2);
+ if (out - buf > len)
+ goto tail;
+ }
+ else
+ ++nurbs;
+ }
+ if (nurbs > 0)
+ out += sprintf(out, "%*s Skipped %d URBs\n",
+ space, "", nurbs);
+ }
+
+ if (out - buf > len)
+ goto done;
+
+ if (qh->dummy_td) {
+ out += sprintf(out, "%*s Dummy TD\n", space, "");
+ out += uhci_show_td(uhci, qh->dummy_td, out,
+ len - (out - buf), 0);
+ if (out - buf > len)
+ goto tail;
+ }
+
+done:
+ if (out - buf > len)
+ out += sprintf(out, " ...\n");
+tail:
+ return out - buf;
+}
+
+static int uhci_show_sc(int port, unsigned short status, char *buf)
+{
+ return sprintf(buf, " stat%d = %04x %s%s%s%s%s%s%s%s%s%s\n",
+ port,
+ status,
+ (status & USBPORTSC_SUSP) ? " Suspend" : "",
+ (status & USBPORTSC_OCC) ? " OverCurrentChange" : "",
+ (status & USBPORTSC_OC) ? " OverCurrent" : "",
+ (status & USBPORTSC_PR) ? " Reset" : "",
+ (status & USBPORTSC_LSDA) ? " LowSpeed" : "",
+ (status & USBPORTSC_RD) ? " ResumeDetect" : "",
+ (status & USBPORTSC_PEC) ? " EnableChange" : "",
+ (status & USBPORTSC_PE) ? " Enabled" : "",
+ (status & USBPORTSC_CSC) ? " ConnectChange" : "",
+ (status & USBPORTSC_CCS) ? " Connected" : "");
+}
+
+static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf)
+{
+ char *rh_state;
+
+ switch (uhci->rh_state) {
+ case UHCI_RH_RESET:
+ rh_state = "reset"; break;
+ case UHCI_RH_SUSPENDED:
+ rh_state = "suspended"; break;
+ case UHCI_RH_AUTO_STOPPED:
+ rh_state = "auto-stopped"; break;
+ case UHCI_RH_RESUMING:
+ rh_state = "resuming"; break;
+ case UHCI_RH_SUSPENDING:
+ rh_state = "suspending"; break;
+ case UHCI_RH_RUNNING:
+ rh_state = "running"; break;
+ case UHCI_RH_RUNNING_NODEVS:
+ rh_state = "running, no devs"; break;
+ default:
+ rh_state = "?"; break;
+ }
+ return sprintf(buf, "Root-hub state: %s FSBR: %d\n",
+ rh_state, uhci->fsbr_is_on);
+}
+
+static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
+{
+ char *out = buf;
+ unsigned short usbcmd, usbstat, usbint, usbfrnum;
+ unsigned int flbaseadd;
+ unsigned char sof;
+ unsigned short portsc1, portsc2;
+
+
+ usbcmd = uhci_readw(uhci, USBCMD);
+ usbstat = uhci_readw(uhci, USBSTS);
+ usbint = uhci_readw(uhci, USBINTR);
+ usbfrnum = uhci_readw(uhci, USBFRNUM);
+ flbaseadd = uhci_readl(uhci, USBFLBASEADD);
+ sof = uhci_readb(uhci, USBSOF);
+ portsc1 = uhci_readw(uhci, USBPORTSC1);
+ portsc2 = uhci_readw(uhci, USBPORTSC2);
+
+ out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n",
+ usbcmd,
+ (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
+ (usbcmd & USBCMD_CF) ? "CF " : "",
+ (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
+ (usbcmd & USBCMD_FGR) ? "FGR " : "",
+ (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
+ (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
+ (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+ (usbcmd & USBCMD_RS) ? "RS " : "");
+ if (out - buf > len)
+ goto done;
+
+ out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n",
+ usbstat,
+ (usbstat & USBSTS_HCH) ? "HCHalted " : "",
+ (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
+ (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
+ (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
+ (usbstat & USBSTS_ERROR) ? "USBError " : "",
+ (usbstat & USBSTS_USBINT) ? "USBINT " : "");
+ if (out - buf > len)
+ goto done;
+
+ out += sprintf(out, " usbint = %04x\n", usbint);
+ out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
+ 0xfff & (4*(unsigned int)usbfrnum));
+ out += sprintf(out, " flbaseadd = %08x\n", flbaseadd);
+ out += sprintf(out, " sof = %02x\n", sof);
+ if (out - buf > len)
+ goto done;
+
+ out += uhci_show_sc(1, portsc1, out);
+ if (out - buf > len)
+ goto done;
+
+ out += uhci_show_sc(2, portsc2, out);
+ if (out - buf > len)
+ goto done;
+
+ out += sprintf(out,
+ "Most recent frame: %x (%d) Last ISO frame: %x (%d)\n",
+ uhci->frame_number, uhci->frame_number & 1023,
+ uhci->last_iso_frame, uhci->last_iso_frame & 1023);
+
+done:
+ if (out - buf > len)
+ out += sprintf(out, " ...\n");
+ return out - buf;
+}
+
+static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
+{
+ char *out = buf;
+ int i, j;
+ struct uhci_qh *qh;
+ struct uhci_td *td;
+ struct list_head *tmp, *head;
+ int nframes, nerrs;
+ __hc32 link;
+ __hc32 fsbr_link;
+
+ static const char * const qh_names[] = {
+ "unlink", "iso", "int128", "int64", "int32", "int16",
+ "int8", "int4", "int2", "async", "term"
+ };
+
+ out += uhci_show_root_hub_state(uhci, out);
+ if (out - buf > len)
+ goto done;
+ out += sprintf(out, "HC status\n");
+ out += uhci_show_status(uhci, out, len - (out - buf));
+ if (out - buf > len)
+ goto tail;
+
+ out += sprintf(out, "Periodic load table\n");
+ for (i = 0; i < MAX_PHASE; ++i) {
+ out += sprintf(out, "\t%d", uhci->load[i]);
+ if (i % 8 == 7)
+ *out++ = '\n';
+ }
+ out += sprintf(out, "Total: %d, #INT: %d, #ISO: %d\n",
+ uhci->total_load,
+ uhci_to_hcd(uhci)->self.bandwidth_int_reqs,
+ uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs);
+ if (debug <= 1)
+ goto tail;
+
+ out += sprintf(out, "Frame List\n");
+ nframes = 10;
+ nerrs = 0;
+ for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+ __hc32 qh_dma;
+
+ if (out - buf > len)
+ goto done;
+ j = 0;
+ td = uhci->frame_cpu[i];
+ link = uhci->frame[i];
+ if (!td)
+ goto check_link;
+
+ if (nframes > 0) {
+ out += sprintf(out, "- Frame %d -> (%08x)\n",
+ i, hc32_to_cpu(uhci, link));
+ j = 1;
+ }
+
+ head = &td->fl_list;
+ tmp = head;
+ do {
+ td = list_entry(tmp, struct uhci_td, fl_list);
+ tmp = tmp->next;
+ if (link != LINK_TO_TD(uhci, td)) {
+ if (nframes > 0) {
+ out += sprintf(out,
+ " link does not match list entry!\n");
+ if (out - buf > len)
+ goto done;
+ } else
+ ++nerrs;
+ }
+ if (nframes > 0) {
+ out += uhci_show_td(uhci, td, out,
+ len - (out - buf), 4);
+ if (out - buf > len)
+ goto tail;
+ }
+ link = td->link;
+ } while (tmp != head);
+
+check_link:
+ qh_dma = uhci_frame_skel_link(uhci, i);
+ if (link != qh_dma) {
+ if (nframes > 0) {
+ if (!j) {
+ out += sprintf(out,
+ "- Frame %d -> (%08x)\n",
+ i, hc32_to_cpu(uhci, link));
+ j = 1;
+ }
+ out += sprintf(out,
+ " link does not match QH (%08x)!\n",
+ hc32_to_cpu(uhci, qh_dma));
+ if (out - buf > len)
+ goto done;
+ } else
+ ++nerrs;
+ }
+ nframes -= j;
+ }
+ if (nerrs > 0)
+ out += sprintf(out, "Skipped %d bad links\n", nerrs);
+
+ out += sprintf(out, "Skeleton QHs\n");
+
+ if (out - buf > len)
+ goto done;
+
+ fsbr_link = 0;
+ for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
+ int cnt = 0;
+
+ qh = uhci->skelqh[i];
+ out += sprintf(out, "- skel_%s_qh\n", qh_names[i]);
+ out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4);
+ if (out - buf > len)
+ goto tail;
+
+ /* Last QH is the Terminating QH, it's different */
+ if (i == SKEL_TERM) {
+ if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td)) {
+ out += sprintf(out,
+ " skel_term_qh element is not set to term_td!\n");
+ if (out - buf > len)
+ goto done;
+ }
+ link = fsbr_link;
+ if (!link)
+ link = LINK_TO_QH(uhci, uhci->skel_term_qh);
+ goto check_qh_link;
+ }
+
+ head = &qh->node;
+ tmp = head->next;
+
+ while (tmp != head) {
+ qh = list_entry(tmp, struct uhci_qh, node);
+ tmp = tmp->next;
+ if (++cnt <= 10) {
+ out += uhci_show_qh(uhci, qh, out,
+ len - (out - buf), 4);
+ if (out - buf > len)
+ goto tail;
+ }
+ if (!fsbr_link && qh->skel >= SKEL_FSBR)
+ fsbr_link = LINK_TO_QH(uhci, qh);
+ }
+ if ((cnt -= 10) > 0)
+ out += sprintf(out, " Skipped %d QHs\n", cnt);
+
+ link = UHCI_PTR_TERM(uhci);
+ if (i <= SKEL_ISO)
+ ;
+ else if (i < SKEL_ASYNC)
+ link = LINK_TO_QH(uhci, uhci->skel_async_qh);
+ else if (!uhci->fsbr_is_on)
+ ;
+ else
+ link = LINK_TO_QH(uhci, uhci->skel_term_qh);
+check_qh_link:
+ if (qh->link != link)
+ out += sprintf(out,
+ " last QH not linked to next skeleton!\n");
+
+ if (out - buf > len)
+ goto done;
+ }
+
+done:
+ if (out - buf > len)
+ out += sprintf(out, " ...\n");
+tail:
+ return out - buf;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define MAX_OUTPUT (64 * 1024)
+
+struct uhci_debug {
+ int size;
+ char *data;
+};
+
+static int uhci_debug_open(struct inode *inode, struct file *file)
+{
+ struct uhci_hcd *uhci = inode->i_private;
+ struct uhci_debug *up;
+ unsigned long flags;
+
+ up = kmalloc(sizeof(*up), GFP_KERNEL);
+ if (!up)
+ return -ENOMEM;
+
+ up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+ if (!up->data) {
+ kfree(up);
+ return -ENOMEM;
+ }
+
+ up->size = 0;
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->is_initialized)
+ up->size = uhci_sprint_schedule(uhci, up->data,
+ MAX_OUTPUT - EXTRA_SPACE);
+ spin_unlock_irqrestore(&uhci->lock, flags);
+
+ file->private_data = up;
+
+ return 0;
+}
+
+static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
+{
+ struct uhci_debug *up;
+ loff_t new = -1;
+
+ up = file->private_data;
+
+ /*
+ * XXX: atomic 64bit seek access, but that needs to be fixed in the VFS
+ */
+ switch (whence) {
+ case 0:
+ new = off;
+ break;
+ case 1:
+ new = file->f_pos + off;
+ break;
+ }
+
+ if (new < 0 || new > up->size)
+ return -EINVAL;
+
+ return (file->f_pos = new);
+}
+
+static ssize_t uhci_debug_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct uhci_debug *up = file->private_data;
+ return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size);
+}
+
+static int uhci_debug_release(struct inode *inode, struct file *file)
+{
+ struct uhci_debug *up = file->private_data;
+
+ kfree(up->data);
+ kfree(up);
+
+ return 0;
+}
+
+static const struct file_operations uhci_debug_operations = {
+ .owner = THIS_MODULE,
+ .open = uhci_debug_open,
+ .llseek = uhci_debug_lseek,
+ .read = uhci_debug_read,
+ .release = uhci_debug_release,
+};
+#define UHCI_DEBUG_OPS
+
+#endif /* CONFIG_DEBUG_FS */
+
+#else /* CONFIG_DYNAMIC_DEBUG*/
+
+static inline void lprintk(char *buf)
+{}
+
+static inline int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
+{
+ return 0;
+}
+
+static inline int uhci_sprint_schedule(struct uhci_hcd *uhci,
+ char *buf, int len)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c
new file mode 100644
index 000000000..0342991c9
--- /dev/null
+++ b/drivers/usb/host/uhci-grlib.c
@@ -0,0 +1,193 @@
+/*
+ * UHCI HCD (Host Controller Driver) for GRLIB GRUSBHC
+ *
+ * Copyright (c) 2011 Jan Andersson <jan@gaisler.com>
+ *
+ * This file is based on UHCI PCI HCD:
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+#include <linux/device.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+static int uhci_grlib_init(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ /*
+ * Probe to determine the endianness of the controller.
+ * We know that bit 7 of the PORTSC1 register is always set
+ * and bit 15 is always clear. If uhci_readw() yields a value
+ * with bit 7 (0x80) turned on then the current little-endian
+ * setting is correct. Otherwise we assume the value was
+ * byte-swapped; hence the register interface and presumably
+ * also the descriptors are big-endian.
+ */
+ if (!(uhci_readw(uhci, USBPORTSC1) & 0x80)) {
+ uhci->big_endian_mmio = 1;
+ uhci->big_endian_desc = 1;
+ }
+
+ uhci->rh_numports = uhci_count_ports(hcd);
+
+ /* Set up pointers to to generic functions */
+ uhci->reset_hc = uhci_generic_reset_hc;
+ uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
+ /* No special actions need to be taken for the functions below */
+ uhci->configure_hc = NULL;
+ uhci->resume_detect_interrupts_are_broken = NULL;
+ uhci->global_suspend_mode_is_broken = NULL;
+
+ /* Reset if the controller isn't already safely quiescent. */
+ check_and_reset_hc(uhci);
+ return 0;
+}
+
+static const struct hc_driver uhci_grlib_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "GRLIB GRUSBHC UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_grlib_init,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .pci_suspend = NULL,
+ .pci_resume = NULL,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+
+static int uhci_hcd_grlib_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct usb_hcd *hcd;
+ struct uhci_hcd *uhci = NULL;
+ struct resource res;
+ int irq;
+ int rv;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ dev_dbg(&op->dev, "initializing GRUSBHC UHCI USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ /* usb_create_hcd requires dma_mask != NULL */
+ op->dev.dma_mask = &op->dev.coherent_dma_mask;
+ hcd = usb_create_hcd(&uhci_grlib_hc_driver, &op->dev,
+ "GRUSBHC UHCI USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = resource_size(&res);
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (irq == NO_IRQ) {
+ printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
+ rv = -EBUSY;
+ goto err_usb;
+ }
+
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_irq;
+ }
+
+ uhci = hcd_to_uhci(hcd);
+
+ uhci->regs = hcd->regs;
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv)
+ goto err_irq;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_irq:
+ irq_dispose_mapping(irq);
+err_usb:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+static int uhci_hcd_grlib_remove(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ dev_dbg(&op->dev, "stopping GRLIB GRUSBHC UHCI USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ irq_dispose_mapping(hcd->irq);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+/* Make sure the controller is quiescent and that we're not using it
+ * any more. This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel. Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_hcd_grlib_shutdown(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ uhci_hc_died(hcd_to_uhci(hcd));
+}
+
+static const struct of_device_id uhci_hcd_grlib_of_match[] = {
+ { .name = "GAISLER_UHCI", },
+ { .name = "01_027", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match);
+
+
+static struct platform_driver uhci_grlib_driver = {
+ .probe = uhci_hcd_grlib_probe,
+ .remove = uhci_hcd_grlib_remove,
+ .shutdown = uhci_hcd_grlib_shutdown,
+ .driver = {
+ .name = "grlib-uhci",
+ .of_match_table = uhci_hcd_grlib_of_match,
+ },
+};
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
new file mode 100644
index 000000000..a7de8e8bb
--- /dev/null
+++ b/drivers/usb/host/uhci-hcd.c
@@ -0,0 +1,945 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/pm.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/bitops.h>
+#include <linux/dmi.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "uhci-hcd.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_AUTHOR \
+ "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, " \
+ "Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, " \
+ "Roman Weissgaerber, Alan Stern"
+#define DRIVER_DESC "USB Universal Host Controller Interface driver"
+
+/* for flakey hardware, ignore overcurrent indicators */
+static bool ignore_oc;
+module_param(ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications");
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URBs except for stalls
+ * debug = 2, dump all failed URBs (including stalls)
+ * show all queues in /sys/kernel/debug/uhci/[pci_addr]
+ * debug = 3, show all TDs in URBs when dumping
+ */
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+static int debug = 1;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+
+#else
+
+#define debug 0
+#define errbuf NULL
+
+#endif
+
+
+#define ERRBUF_LEN (32 * 1024)
+
+static struct kmem_cache *uhci_up_cachep; /* urb_priv */
+
+static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state);
+static void wakeup_rh(struct uhci_hcd *uhci);
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
+
+/*
+ * Calculate the link pointer DMA value for the first Skeleton QH in a frame.
+ */
+static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
+{
+ int skelnum;
+
+ /*
+ * The interrupt queues will be interleaved as evenly as possible.
+ * There's not much to be done about period-1 interrupts; they have
+ * to occur in every frame. But we can schedule period-2 interrupts
+ * in odd-numbered frames, period-4 interrupts in frames congruent
+ * to 2 (mod 4), and so on. This way each frame only has two
+ * interrupt QHs, which will help spread out bandwidth utilization.
+ *
+ * ffs (Find First bit Set) does exactly what we need:
+ * 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8],
+ * 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
+ * ffs >= 7 => not on any high-period queue, so use
+ * period-1 QH = skelqh[9].
+ * Add in UHCI_NUMFRAMES to insure at least one bit is set.
+ */
+ skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
+ if (skelnum <= 1)
+ skelnum = 9;
+ return LINK_TO_QH(uhci, uhci->skelqh[skelnum]);
+}
+
+#include "uhci-debug.c"
+#include "uhci-q.c"
+#include "uhci-hub.c"
+
+/*
+ * Finish up a host controller reset and update the recorded state.
+ */
+static void finish_reset(struct uhci_hcd *uhci)
+{
+ int port;
+
+ /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
+ * bits in the port status and control registers.
+ * We have to clear them by hand.
+ */
+ for (port = 0; port < uhci->rh_numports; ++port)
+ uhci_writew(uhci, 0, USBPORTSC1 + (port * 2));
+
+ uhci->port_c_suspend = uhci->resuming_ports = 0;
+ uhci->rh_state = UHCI_RH_RESET;
+ uhci->is_stopped = UHCI_IS_STOPPED;
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+}
+
+/*
+ * Last rites for a defunct/nonfunctional controller
+ * or one we don't want to use any more.
+ */
+static void uhci_hc_died(struct uhci_hcd *uhci)
+{
+ uhci_get_current_frame_number(uhci);
+ uhci->reset_hc(uhci);
+ finish_reset(uhci);
+ uhci->dead = 1;
+
+ /* The current frame may already be partway finished */
+ ++uhci->frame_number;
+}
+
+/*
+ * Initialize a controller that was newly discovered or has lost power
+ * or otherwise been reset while it was suspended. In none of these cases
+ * can we be sure of its previous state.
+ */
+static void check_and_reset_hc(struct uhci_hcd *uhci)
+{
+ if (uhci->check_and_reset_hc(uhci))
+ finish_reset(uhci);
+}
+
+#if defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC)
+/*
+ * The two functions below are generic reset functions that are used on systems
+ * that do not have keyboard and mouse legacy support. We assume that we are
+ * running on such a system if CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC is defined.
+ */
+
+/*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+ */
+static void uhci_generic_reset_hc(struct uhci_hcd *uhci)
+{
+ /* Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ uhci_writew(uhci, USBCMD_HCRESET, USBCMD);
+ mb();
+ udelay(5);
+ if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET)
+ dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n");
+
+ /* Just to be safe, disable interrupt requests and
+ * make sure the controller is stopped.
+ */
+ uhci_writew(uhci, 0, USBINTR);
+ uhci_writew(uhci, 0, USBCMD);
+}
+
+/*
+ * Initialize a controller that was newly discovered or has just been
+ * resumed. In either case we can't be sure of its previous state.
+ *
+ * Returns: 1 if the controller was reset, 0 otherwise.
+ */
+static int uhci_generic_check_and_reset_hc(struct uhci_hcd *uhci)
+{
+ unsigned int cmd, intr;
+
+ /*
+ * When restarting a suspended controller, we expect all the
+ * settings to be the same as we left them:
+ *
+ * Controller is stopped and configured with EGSM set;
+ * No interrupts enabled except possibly Resume Detect.
+ *
+ * If any of these conditions are violated we do a complete reset.
+ */
+
+ cmd = uhci_readw(uhci, USBCMD);
+ if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) {
+ dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n",
+ __func__, cmd);
+ goto reset_needed;
+ }
+
+ intr = uhci_readw(uhci, USBINTR);
+ if (intr & (~USBINTR_RESUME)) {
+ dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n",
+ __func__, intr);
+ goto reset_needed;
+ }
+ return 0;
+
+reset_needed:
+ dev_dbg(uhci_dev(uhci), "Performing full reset\n");
+ uhci_generic_reset_hc(uhci);
+ return 1;
+}
+#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
+
+/*
+ * Store the basic register settings needed by the controller.
+ */
+static void configure_hc(struct uhci_hcd *uhci)
+{
+ /* Set the frame length to the default: 1 ms exactly */
+ uhci_writeb(uhci, USBSOF_DEFAULT, USBSOF);
+
+ /* Store the frame list base address */
+ uhci_writel(uhci, uhci->frame_dma_handle, USBFLBASEADD);
+
+ /* Set the current frame number */
+ uhci_writew(uhci, uhci->frame_number & UHCI_MAX_SOF_NUMBER,
+ USBFRNUM);
+
+ /* perform any arch/bus specific configuration */
+ if (uhci->configure_hc)
+ uhci->configure_hc(uhci);
+}
+
+static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
+{
+ /* If we have to ignore overcurrent events then almost by definition
+ * we can't depend on resume-detect interrupts. */
+ if (ignore_oc)
+ return 1;
+
+ return uhci->resume_detect_interrupts_are_broken ?
+ uhci->resume_detect_interrupts_are_broken(uhci) : 0;
+}
+
+static int global_suspend_mode_is_broken(struct uhci_hcd *uhci)
+{
+ return uhci->global_suspend_mode_is_broken ?
+ uhci->global_suspend_mode_is_broken(uhci) : 0;
+}
+
+static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+ int auto_stop;
+ int int_enable, egsm_enable, wakeup_enable;
+ struct usb_device *rhdev = uhci_to_hcd(uhci)->self.root_hub;
+
+ auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
+ dev_dbg(&rhdev->dev, "%s%s\n", __func__,
+ (auto_stop ? " (auto-stop)" : ""));
+
+ /* Start off by assuming Resume-Detect interrupts and EGSM work
+ * and that remote wakeups should be enabled.
+ */
+ egsm_enable = USBCMD_EGSM;
+ int_enable = USBINTR_RESUME;
+ wakeup_enable = 1;
+
+ /*
+ * In auto-stop mode, we must be able to detect new connections.
+ * The user can force us to poll by disabling remote wakeup;
+ * otherwise we will use the EGSM/RD mechanism.
+ */
+ if (auto_stop) {
+ if (!device_may_wakeup(&rhdev->dev))
+ egsm_enable = int_enable = 0;
+ }
+
+#ifdef CONFIG_PM
+ /*
+ * In bus-suspend mode, we use the wakeup setting specified
+ * for the root hub.
+ */
+ else {
+ if (!rhdev->do_remote_wakeup)
+ wakeup_enable = 0;
+ }
+#endif
+
+ /*
+ * UHCI doesn't distinguish between wakeup requests from downstream
+ * devices and local connect/disconnect events. There's no way to
+ * enable one without the other; both are controlled by EGSM. Thus
+ * if wakeups are disallowed then EGSM must be turned off -- in which
+ * case remote wakeup requests from downstream during system sleep
+ * will be lost.
+ *
+ * In addition, if EGSM is broken then we can't use it. Likewise,
+ * if Resume-Detect interrupts are broken then we can't use them.
+ *
+ * Finally, neither EGSM nor RD is useful by itself. Without EGSM,
+ * the RD status bit will never get set. Without RD, the controller
+ * won't generate interrupts to tell the system about wakeup events.
+ */
+ if (!wakeup_enable || global_suspend_mode_is_broken(uhci) ||
+ resume_detect_interrupts_are_broken(uhci))
+ egsm_enable = int_enable = 0;
+
+ uhci->RD_enable = !!int_enable;
+ uhci_writew(uhci, int_enable, USBINTR);
+ uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD);
+ mb();
+ udelay(5);
+
+ /* If we're auto-stopping then no devices have been attached
+ * for a while, so there shouldn't be any active URBs and the
+ * controller should stop after a few microseconds. Otherwise
+ * we will give the controller one frame to stop.
+ */
+ if (!auto_stop && !(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) {
+ uhci->rh_state = UHCI_RH_SUSPENDING;
+ spin_unlock_irq(&uhci->lock);
+ msleep(1);
+ spin_lock_irq(&uhci->lock);
+ if (uhci->dead)
+ return;
+ }
+ if (!(uhci_readw(uhci, USBSTS) & USBSTS_HCH))
+ dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
+
+ uhci_get_current_frame_number(uhci);
+
+ uhci->rh_state = new_state;
+ uhci->is_stopped = UHCI_IS_STOPPED;
+
+ /*
+ * If remote wakeup is enabled but either EGSM or RD interrupts
+ * doesn't work, then we won't get an interrupt when a wakeup event
+ * occurs. Thus the suspended root hub needs to be polled.
+ */
+ if (wakeup_enable && (!int_enable || !egsm_enable))
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+
+ uhci_scan_schedule(uhci);
+ uhci_fsbr_off(uhci);
+}
+
+static void start_rh(struct uhci_hcd *uhci)
+{
+ uhci->is_stopped = 0;
+
+ /* Mark it configured and running with a 64-byte max packet.
+ * All interrupts are enabled, even though RESUME won't do anything.
+ */
+ uhci_writew(uhci, USBCMD_RS | USBCMD_CF | USBCMD_MAXP, USBCMD);
+ uhci_writew(uhci, USBINTR_TIMEOUT | USBINTR_RESUME |
+ USBINTR_IOC | USBINTR_SP, USBINTR);
+ mb();
+ uhci->rh_state = UHCI_RH_RUNNING;
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+}
+
+static void wakeup_rh(struct uhci_hcd *uhci)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+ dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
+ "%s%s\n", __func__,
+ uhci->rh_state == UHCI_RH_AUTO_STOPPED ?
+ " (auto-start)" : "");
+
+ /* If we are auto-stopped then no devices are attached so there's
+ * no need for wakeup signals. Otherwise we send Global Resume
+ * for 20 ms.
+ */
+ if (uhci->rh_state == UHCI_RH_SUSPENDED) {
+ unsigned egsm;
+
+ /* Keep EGSM on if it was set before */
+ egsm = uhci_readw(uhci, USBCMD) & USBCMD_EGSM;
+ uhci->rh_state = UHCI_RH_RESUMING;
+ uhci_writew(uhci, USBCMD_FGR | USBCMD_CF | egsm, USBCMD);
+ spin_unlock_irq(&uhci->lock);
+ msleep(20);
+ spin_lock_irq(&uhci->lock);
+ if (uhci->dead)
+ return;
+
+ /* End Global Resume and wait for EOP to be sent */
+ uhci_writew(uhci, USBCMD_CF, USBCMD);
+ mb();
+ udelay(4);
+ if (uhci_readw(uhci, USBCMD) & USBCMD_FGR)
+ dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n");
+ }
+
+ start_rh(uhci);
+
+ /* Restart root hub polling */
+ mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
+}
+
+static irqreturn_t uhci_irq(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned short status;
+
+ /*
+ * Read the interrupt status, and write it back to clear the
+ * interrupt cause. Contrary to the UHCI specification, the
+ * "HC Halted" status bit is persistent: it is RO, not R/WC.
+ */
+ status = uhci_readw(uhci, USBSTS);
+ if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
+ return IRQ_NONE;
+ uhci_writew(uhci, status, USBSTS); /* Clear it */
+
+ spin_lock(&uhci->lock);
+ if (unlikely(!uhci->is_initialized)) /* not yet configured */
+ goto done;
+
+ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
+ if (status & USBSTS_HSE)
+ dev_err(uhci_dev(uhci),
+ "host system error, PCI problems?\n");
+ if (status & USBSTS_HCPE)
+ dev_err(uhci_dev(uhci),
+ "host controller process error, something bad happened!\n");
+ if (status & USBSTS_HCH) {
+ if (uhci->rh_state >= UHCI_RH_RUNNING) {
+ dev_err(uhci_dev(uhci),
+ "host controller halted, very bad!\n");
+ if (debug > 1 && errbuf) {
+ /* Print the schedule for debugging */
+ uhci_sprint_schedule(uhci, errbuf,
+ ERRBUF_LEN - EXTRA_SPACE);
+ lprintk(errbuf);
+ }
+ uhci_hc_died(uhci);
+ usb_hc_died(hcd);
+
+ /* Force a callback in case there are
+ * pending unlinks */
+ mod_timer(&hcd->rh_timer, jiffies);
+ }
+ }
+ }
+
+ if (status & USBSTS_RD) {
+ spin_unlock(&uhci->lock);
+ usb_hcd_poll_rh_status(hcd);
+ } else {
+ uhci_scan_schedule(uhci);
+ done:
+ spin_unlock(&uhci->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Store the current frame number in uhci->frame_number if the controller
+ * is running. Expand from 11 bits (of which we use only 10) to a
+ * full-sized integer.
+ *
+ * Like many other parts of the driver, this code relies on being polled
+ * more than once per second as long as the controller is running.
+ */
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
+{
+ if (!uhci->is_stopped) {
+ unsigned delta;
+
+ delta = (uhci_readw(uhci, USBFRNUM) - uhci->frame_number) &
+ (UHCI_NUMFRAMES - 1);
+ uhci->frame_number += delta;
+ }
+}
+
+/*
+ * De-allocate all resources
+ */
+static void release_uhci(struct uhci_hcd *uhci)
+{
+ int i;
+
+
+ spin_lock_irq(&uhci->lock);
+ uhci->is_initialized = 0;
+ spin_unlock_irq(&uhci->lock);
+
+ debugfs_remove(uhci->dentry);
+
+ for (i = 0; i < UHCI_NUM_SKELQH; i++)
+ uhci_free_qh(uhci, uhci->skelqh[i]);
+
+ uhci_free_td(uhci, uhci->term_td);
+
+ dma_pool_destroy(uhci->qh_pool);
+
+ dma_pool_destroy(uhci->td_pool);
+
+ kfree(uhci->frame_cpu);
+
+ dma_free_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ uhci->frame, uhci->frame_dma_handle);
+}
+
+/*
+ * Allocate a frame list, and then setup the skeleton
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order in which the queues
+ * are encountered by the hardware is:
+ *
+ * - All isochronous events are handled before any
+ * of the queues. We don't do that here, because
+ * we'll create the actual TD entries on demand.
+ * - The first queue is the high-period interrupt queue.
+ * - The second queue is the period-1 interrupt and async
+ * (low-speed control, full-speed control, then bulk) queue.
+ * - The third queue is the terminating bandwidth reclamation queue,
+ * which contains no members, loops back to itself, and is present
+ * only when FSBR is on and there are no full-speed control or bulk QHs.
+ */
+static int uhci_start(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int retval = -EBUSY;
+ int i;
+ struct dentry __maybe_unused *dentry;
+
+ hcd->uses_new_polling = 1;
+ /* Accept arbitrarily long scatter-gather lists */
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
+
+ spin_lock_init(&uhci->lock);
+ setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
+ (unsigned long) uhci);
+ INIT_LIST_HEAD(&uhci->idle_qh_list);
+ init_waitqueue_head(&uhci->waitqh);
+
+#ifdef UHCI_DEBUG_OPS
+ dentry = debugfs_create_file(hcd->self.bus_name,
+ S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
+ uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
+ return -ENOMEM;
+ }
+ uhci->dentry = dentry;
+#endif
+
+ uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ &uhci->frame_dma_handle, GFP_KERNEL);
+ if (!uhci->frame) {
+ dev_err(uhci_dev(uhci),
+ "unable to allocate consistent memory for frame list\n");
+ goto err_alloc_frame;
+ }
+ memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));
+
+ uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
+ GFP_KERNEL);
+ if (!uhci->frame_cpu) {
+ dev_err(uhci_dev(uhci),
+ "unable to allocate memory for frame pointers\n");
+ goto err_alloc_frame_cpu;
+ }
+
+ uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
+ sizeof(struct uhci_td), 16, 0);
+ if (!uhci->td_pool) {
+ dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");
+ goto err_create_td_pool;
+ }
+
+ uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),
+ sizeof(struct uhci_qh), 16, 0);
+ if (!uhci->qh_pool) {
+ dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");
+ goto err_create_qh_pool;
+ }
+
+ uhci->term_td = uhci_alloc_td(uhci);
+ if (!uhci->term_td) {
+ dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
+ goto err_alloc_term_td;
+ }
+
+ for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+ uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL);
+ if (!uhci->skelqh[i]) {
+ dev_err(uhci_dev(uhci), "unable to allocate QH\n");
+ goto err_alloc_skelqh;
+ }
+ }
+
+ /*
+ * 8 Interrupt queues; link all higher int queues to int1 = async
+ */
+ for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
+ uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh);
+ uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci);
+ uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
+
+ /* This dummy TD is to work around a bug in Intel PIIX controllers */
+ uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) |
+ (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+ uhci->term_td->link = UHCI_PTR_TERM(uhci);
+ uhci->skel_async_qh->element = uhci->skel_term_qh->element =
+ LINK_TO_TD(uhci, uhci->term_td);
+
+ /*
+ * Fill the frame list: make all entries point to the proper
+ * interrupt queue.
+ */
+ for (i = 0; i < UHCI_NUMFRAMES; i++) {
+
+ /* Only place we don't use the frame list routines */
+ uhci->frame[i] = uhci_frame_skel_link(uhci, i);
+ }
+
+ /*
+ * Some architectures require a full mb() to enforce completion of
+ * the memory writes above before the I/O transfers in configure_hc().
+ */
+ mb();
+
+ spin_lock_irq(&uhci->lock);
+ configure_hc(uhci);
+ uhci->is_initialized = 1;
+ start_rh(uhci);
+ spin_unlock_irq(&uhci->lock);
+ return 0;
+
+/*
+ * error exits:
+ */
+err_alloc_skelqh:
+ for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+ if (uhci->skelqh[i])
+ uhci_free_qh(uhci, uhci->skelqh[i]);
+ }
+
+ uhci_free_td(uhci, uhci->term_td);
+
+err_alloc_term_td:
+ dma_pool_destroy(uhci->qh_pool);
+
+err_create_qh_pool:
+ dma_pool_destroy(uhci->td_pool);
+
+err_create_td_pool:
+ kfree(uhci->frame_cpu);
+
+err_alloc_frame_cpu:
+ dma_free_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ uhci->frame, uhci->frame_dma_handle);
+
+err_alloc_frame:
+ debugfs_remove(uhci->dentry);
+
+ return retval;
+}
+
+static void uhci_stop(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ spin_lock_irq(&uhci->lock);
+ if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead)
+ uhci_hc_died(uhci);
+ uhci_scan_schedule(uhci);
+ spin_unlock_irq(&uhci->lock);
+ synchronize_irq(hcd->irq);
+
+ del_timer_sync(&uhci->fsbr_timer);
+ release_uhci(uhci);
+}
+
+#ifdef CONFIG_PM
+static int uhci_rh_suspend(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int rc = 0;
+
+ spin_lock_irq(&uhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ rc = -ESHUTDOWN;
+ else if (uhci->dead)
+ ; /* Dead controllers tell no tales */
+
+ /* Once the controller is stopped, port resumes that are already
+ * in progress won't complete. Hence if remote wakeup is enabled
+ * for the root hub and any ports are in the middle of a resume or
+ * remote wakeup, we must fail the suspend.
+ */
+ else if (hcd->self.root_hub->do_remote_wakeup &&
+ uhci->resuming_ports) {
+ dev_dbg(uhci_dev(uhci),
+ "suspend failed because a port is resuming\n");
+ rc = -EBUSY;
+ } else
+ suspend_rh(uhci, UHCI_RH_SUSPENDED);
+ spin_unlock_irq(&uhci->lock);
+ return rc;
+}
+
+static int uhci_rh_resume(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int rc = 0;
+
+ spin_lock_irq(&uhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ rc = -ESHUTDOWN;
+ else if (!uhci->dead)
+ wakeup_rh(uhci);
+ spin_unlock_irq(&uhci->lock);
+ return rc;
+}
+
+#endif
+
+/* Wait until a particular device/endpoint's QH is idle, and free it */
+static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct uhci_qh *qh;
+
+ spin_lock_irq(&uhci->lock);
+ qh = (struct uhci_qh *) hep->hcpriv;
+ if (qh == NULL)
+ goto done;
+
+ while (qh->state != QH_STATE_IDLE) {
+ ++uhci->num_waiting;
+ spin_unlock_irq(&uhci->lock);
+ wait_event_interruptible(uhci->waitqh,
+ qh->state == QH_STATE_IDLE);
+ spin_lock_irq(&uhci->lock);
+ --uhci->num_waiting;
+ }
+
+ uhci_free_qh(uhci, qh);
+done:
+ spin_unlock_irq(&uhci->lock);
+}
+
+static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned frame_number;
+ unsigned delta;
+
+ /* Minimize latency by avoiding the spinlock */
+ frame_number = uhci->frame_number;
+ barrier();
+ delta = (uhci_readw(uhci, USBFRNUM) - frame_number) &
+ (UHCI_NUMFRAMES - 1);
+ return frame_number + delta;
+}
+
+/* Determines number of ports on controller */
+static int uhci_count_ports(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned io_size = (unsigned) hcd->rsrc_len;
+ int port;
+
+ /* The UHCI spec says devices must have 2 ports, and goes on to say
+ * they may have more but gives no way to determine how many there
+ * are. However according to the UHCI spec, Bit 7 of the port
+ * status and control register is always set to 1. So we try to
+ * use this to our advantage. Another common failure mode when
+ * a nonexistent register is addressed is to return all ones, so
+ * we test for that also.
+ */
+ for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
+ unsigned int portstatus;
+
+ portstatus = uhci_readw(uhci, USBPORTSC1 + (port * 2));
+ if (!(portstatus & 0x0080) || portstatus == 0xffff)
+ break;
+ }
+ if (debug)
+ dev_info(uhci_dev(uhci), "detected %d ports\n", port);
+
+ /* Anything greater than 7 is weird so we'll ignore it. */
+ if (port > UHCI_RH_MAXCHILD) {
+ dev_info(uhci_dev(uhci),
+ "port count misdetected? forcing to 2 ports\n");
+ port = 2;
+ }
+
+ return port;
+}
+
+static const char hcd_name[] = "uhci_hcd";
+
+#ifdef CONFIG_PCI
+#include "uhci-pci.c"
+#define PCI_DRIVER uhci_pci_driver
+#endif
+
+#ifdef CONFIG_SPARC_LEON
+#include "uhci-grlib.c"
+#define PLATFORM_DRIVER uhci_grlib_driver
+#endif
+
+#ifdef CONFIG_USB_UHCI_PLATFORM
+#include "uhci-platform.c"
+#define PLATFORM_DRIVER uhci_platform_driver
+#endif
+
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
+#error "missing bus glue for uhci-hcd"
+#endif
+
+static int __init uhci_hcd_init(void)
+{
+ int retval = -ENOMEM;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ printk(KERN_INFO "uhci_hcd: " DRIVER_DESC "%s\n",
+ ignore_oc ? ", overcurrent ignored" : "");
+ set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+ if (!errbuf)
+ goto errbuf_failed;
+ uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root);
+ if (!uhci_debugfs_root)
+ goto debug_failed;
+#endif
+
+ uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+ sizeof(struct urb_priv), 0, 0, NULL);
+ if (!uhci_up_cachep)
+ goto up_failed;
+
+#ifdef PLATFORM_DRIVER
+ retval = platform_driver_register(&PLATFORM_DRIVER);
+ if (retval < 0)
+ goto clean0;
+#endif
+
+#ifdef PCI_DRIVER
+ retval = pci_register_driver(&PCI_DRIVER);
+ if (retval < 0)
+ goto clean1;
+#endif
+
+ return 0;
+
+#ifdef PCI_DRIVER
+clean1:
+#endif
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+clean0:
+#endif
+ kmem_cache_destroy(uhci_up_cachep);
+
+up_failed:
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+ debugfs_remove(uhci_debugfs_root);
+
+debug_failed:
+ kfree(errbuf);
+
+errbuf_failed:
+#endif
+
+ clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
+ return retval;
+}
+
+static void __exit uhci_hcd_cleanup(void)
+{
+#ifdef PLATFORM_DRIVER
+ platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PCI_DRIVER
+ pci_unregister_driver(&PCI_DRIVER);
+#endif
+ kmem_cache_destroy(uhci_up_cachep);
+ debugfs_remove(uhci_debugfs_root);
+#ifdef CONFIG_DYNAMIC_DEBUG
+ kfree(errbuf);
+#endif
+ clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
+}
+
+module_init(uhci_hcd_init);
+module_exit(uhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
new file mode 100644
index 000000000..6f986d824
--- /dev/null
+++ b/drivers/usb/host/uhci-hcd.h
@@ -0,0 +1,660 @@
+#ifndef __LINUX_UHCI_HCD_H
+#define __LINUX_UHCI_HCD_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+
+#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
+#define PIPE_DEVEP_MASK 0x0007ff00
+
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD 0
+#define USBCMD_RS 0x0001 /* Run/Stop */
+#define USBCMD_HCRESET 0x0002 /* Host reset */
+#define USBCMD_GRESET 0x0004 /* Global reset */
+#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define USBCMD_FGR 0x0010 /* Force Global Resume */
+#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
+#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
+#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS 2
+#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
+#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
+#define USBSTS_RD 0x0004 /* Resume Detect */
+#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error:
+ * the schedule is buggy */
+#define USBSTS_HCH 0x0020 /* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR 4
+#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
+#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
+#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
+
+#define USBFRNUM 6
+#define USBFLBASEADD 8
+#define USBSOF 12
+#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */
+
+/* USB port status and control registers */
+#define USBPORTSC1 16
+#define USBPORTSC2 18
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status
+ * ("device present") */
+#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
+#define USBPORTSC_PE 0x0004 /* Port Enable */
+#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
+#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */
+#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */
+#define USBPORTSC_RD 0x0040 /* Resume Detect */
+#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
+#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
+#define USBPORTSC_PR 0x0200 /* Port Reset */
+/* OC and OCC from Intel 430TX and later (not UHCI 1.1d spec) */
+#define USBPORTSC_OC 0x0400 /* Over Current condition */
+#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */
+#define USBPORTSC_SUSP 0x1000 /* Suspend */
+#define USBPORTSC_RES2 0x2000 /* reserved, write zeroes */
+#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
+#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
+
+/* PCI legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
+#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
+#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
+
+/* PCI Intel-specific resume-enable register */
+#define USBRES_INTEL 0xc4
+#define USBPORT1EN 0x01
+#define USBPORT2EN 0x02
+
+#define UHCI_PTR_BITS(uhci) cpu_to_hc32((uhci), 0x000F)
+#define UHCI_PTR_TERM(uhci) cpu_to_hc32((uhci), 0x0001)
+#define UHCI_PTR_QH(uhci) cpu_to_hc32((uhci), 0x0002)
+#define UHCI_PTR_DEPTH(uhci) cpu_to_hc32((uhci), 0x0004)
+#define UHCI_PTR_BREADTH(uhci) cpu_to_hc32((uhci), 0x0000)
+
+#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames
+ * can be scheduled */
+#define MAX_PHASE 32 /* Periodic scheduling length */
+
+/* When no queues need Full-Speed Bandwidth Reclamation,
+ * delay this long before turning FSBR off */
+#define FSBR_OFF_DELAY msecs_to_jiffies(10)
+
+/* If a queue hasn't advanced after this much time, assume it is stuck */
+#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
+
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given UHCI_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+#else
+#define __hc32 __le32
+#define __hc16 __le16
+#endif
+
+/*
+ * Queue Headers
+ */
+
+/*
+ * One role of a QH is to hold a queue of TDs for some endpoint. One QH goes
+ * with each endpoint, and qh->element (updated by the HC) is either:
+ * - the next unprocessed TD in the endpoint's queue, or
+ * - UHCI_PTR_TERM (when there's no more traffic for this endpoint).
+ *
+ * The other role of a QH is to serve as a "skeleton" framelist entry, so we
+ * can easily splice a QH for some endpoint into the schedule at the right
+ * place. Then qh->element is UHCI_PTR_TERM.
+ *
+ * In the schedule, qh->link maintains a list of QHs seen by the HC:
+ * skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
+ *
+ * qh->node is the software equivalent of qh->link. The differences
+ * are that the software list is doubly-linked and QHs in the UNLINKING
+ * state are on the software list but not the hardware schedule.
+ *
+ * For bookkeeping purposes we maintain QHs even for Isochronous endpoints,
+ * but they never get added to the hardware schedule.
+ */
+#define QH_STATE_IDLE 1 /* QH is not being used */
+#define QH_STATE_UNLINKING 2 /* QH has been removed from the
+ * schedule but the hardware may
+ * still be using it */
+#define QH_STATE_ACTIVE 3 /* QH is on the schedule */
+
+struct uhci_qh {
+ /* Hardware fields */
+ __hc32 link; /* Next QH in the schedule */
+ __hc32 element; /* Queue element (TD) pointer */
+
+ /* Software fields */
+ dma_addr_t dma_handle;
+
+ struct list_head node; /* Node in the list of QHs */
+ struct usb_host_endpoint *hep; /* Endpoint information */
+ struct usb_device *udev;
+ struct list_head queue; /* Queue of urbps for this QH */
+ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
+ struct uhci_td *post_td; /* Last TD completed */
+
+ struct usb_iso_packet_descriptor *iso_packet_desc;
+ /* Next urb->iso_frame_desc entry */
+ unsigned long advance_jiffies; /* Time of last queue advance */
+ unsigned int unlink_frame; /* When the QH was unlinked */
+ unsigned int period; /* For Interrupt and Isochronous QHs */
+ short phase; /* Between 0 and period-1 */
+ short load; /* Periodic time requirement, in us */
+ unsigned int iso_frame; /* Frame # for iso_packet_desc */
+
+ int state; /* QH_STATE_xxx; see above */
+ int type; /* Queue type (control, bulk, etc) */
+ int skel; /* Skeleton queue number */
+
+ unsigned int initial_toggle:1; /* Endpoint's current toggle value */
+ unsigned int needs_fixup:1; /* Must fix the TD toggle values */
+ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
+ unsigned int wait_expired:1; /* QH_WAIT_TIMEOUT has expired */
+ unsigned int bandwidth_reserved:1; /* Periodic bandwidth has
+ * been allocated */
+} __attribute__((aligned(16)));
+
+/*
+ * We need a special accessor for the element pointer because it is
+ * subject to asynchronous updates by the controller.
+ */
+#define qh_element(qh) ACCESS_ONCE((qh)->element)
+
+#define LINK_TO_QH(uhci, qh) (UHCI_PTR_QH((uhci)) | \
+ cpu_to_hc32((uhci), (qh)->dma_handle))
+
+
+/*
+ * Transfer Descriptors
+ */
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT 27
+#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
+#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
+#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
+#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
+#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
+#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
+#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
+#define TD_CTRL_NAK (1 << 19) /* NAK Received */
+#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
+
+#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
+#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000)
+#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & \
+ TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define td_token(uhci, td) hc32_to_cpu((uhci), (td)->token)
+#define TD_TOKEN_DEVADDR_SHIFT 8
+#define TD_TOKEN_TOGGLE_SHIFT 19
+#define TD_TOKEN_TOGGLE (1 << 19)
+#define TD_TOKEN_EXPLEN_SHIFT 21
+#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */
+#define TD_TOKEN_PID_MASK 0xFF
+
+#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \
+ TD_TOKEN_EXPLEN_SHIFT)
+
+#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \
+ 1) & TD_TOKEN_EXPLEN_MASK)
+#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define uhci_endpoint(token) (((token) >> 15) & 0xf)
+#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
+#define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
+#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
+#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry.
+ *
+ * td->link points to either another TD (not necessarily for the same urb or
+ * even the same endpoint), or nothing (PTR_TERM), or a QH.
+ */
+struct uhci_td {
+ /* Hardware fields */
+ __hc32 link;
+ __hc32 status;
+ __hc32 token;
+ __hc32 buffer;
+
+ /* Software fields */
+ dma_addr_t dma_handle;
+
+ struct list_head list;
+
+ int frame; /* for iso: what frame? */
+ struct list_head fl_list;
+} __attribute__((aligned(16)));
+
+/*
+ * We need a special accessor for the control/status word because it is
+ * subject to asynchronous updates by the controller.
+ */
+#define td_status(uhci, td) hc32_to_cpu((uhci), \
+ ACCESS_ONCE((td)->status))
+
+#define LINK_TO_TD(uhci, td) (cpu_to_hc32((uhci), (td)->dma_handle))
+
+
+/*
+ * Skeleton Queue Headers
+ */
+
+/*
+ * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
+ * automatic queuing. To make it easy to insert entries into the schedule,
+ * we have a skeleton of QHs for each predefined Interrupt latency.
+ * Asynchronous QHs (low-speed control, full-speed control, and bulk)
+ * go onto the period-1 interrupt list, since they all get accessed on
+ * every frame.
+ *
+ * When we want to add a new QH, we add it to the list starting from the
+ * appropriate skeleton QH. For instance, the schedule can look like this:
+ *
+ * skel int128 QH
+ * dev 1 interrupt QH
+ * dev 5 interrupt QH
+ * skel int64 QH
+ * skel int32 QH
+ * ...
+ * skel int1 + async QH
+ * dev 5 low-speed control QH
+ * dev 1 bulk QH
+ * dev 2 bulk QH
+ *
+ * There is a special terminating QH used to keep full-speed bandwidth
+ * reclamation active when no full-speed control or bulk QHs are linked
+ * into the schedule. It has an inactive TD (to work around a PIIX bug,
+ * see the Intel errata) and it points back to itself.
+ *
+ * There's a special skeleton QH for Isochronous QHs which never appears
+ * on the schedule. Isochronous TDs go on the schedule before the
+ * the skeleton QHs. The hardware accesses them directly rather than
+ * through their QH, which is used only for bookkeeping purposes.
+ * While the UHCI spec doesn't forbid the use of QHs for Isochronous,
+ * it doesn't use them either. And the spec says that queues never
+ * advance on an error completion status, which makes them totally
+ * unsuitable for Isochronous transfers.
+ *
+ * There's also a special skeleton QH used for QHs which are in the process
+ * of unlinking and so may still be in use by the hardware. It too never
+ * appears on the schedule.
+ */
+
+#define UHCI_NUM_SKELQH 11
+#define SKEL_UNLINK 0
+#define skel_unlink_qh skelqh[SKEL_UNLINK]
+#define SKEL_ISO 1
+#define skel_iso_qh skelqh[SKEL_ISO]
+ /* int128, int64, ..., int1 = 2, 3, ..., 9 */
+#define SKEL_INDEX(exponent) (9 - exponent)
+#define SKEL_ASYNC 9
+#define skel_async_qh skelqh[SKEL_ASYNC]
+#define SKEL_TERM 10
+#define skel_term_qh skelqh[SKEL_TERM]
+
+/* The following entries refer to sublists of skel_async_qh */
+#define SKEL_LS_CONTROL 20
+#define SKEL_FS_CONTROL 21
+#define SKEL_FSBR SKEL_FS_CONTROL
+#define SKEL_BULK 22
+
+/*
+ * The UHCI controller and root hub
+ */
+
+/*
+ * States for the root hub:
+ *
+ * To prevent "bouncing" in the presence of electrical noise,
+ * when there are no devices attached we delay for 1 second in the
+ * RUNNING_NODEVS state before switching to the AUTO_STOPPED state.
+ *
+ * (Note that the AUTO_STOPPED state won't be necessary once the hub
+ * driver learns to autosuspend.)
+ */
+enum uhci_rh_state {
+ /* In the following states the HC must be halted.
+ * These two must come first. */
+ UHCI_RH_RESET,
+ UHCI_RH_SUSPENDED,
+
+ UHCI_RH_AUTO_STOPPED,
+ UHCI_RH_RESUMING,
+
+ /* In this state the HC changes from running to halted,
+ * so it can legally appear either way. */
+ UHCI_RH_SUSPENDING,
+
+ /* In the following states it's an error if the HC is halted.
+ * These two must come last. */
+ UHCI_RH_RUNNING, /* The normal state */
+ UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */
+};
+
+/*
+ * The full UHCI controller information:
+ */
+struct uhci_hcd {
+
+ /* debugfs */
+ struct dentry *dentry;
+
+ /* Grabbed from PCI */
+ unsigned long io_addr;
+
+ /* Used when registers are memory mapped */
+ void __iomem *regs;
+
+ struct dma_pool *qh_pool;
+ struct dma_pool *td_pool;
+
+ struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
+ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QHs */
+ struct uhci_qh *next_qh; /* Next QH to scan */
+
+ spinlock_t lock;
+
+ dma_addr_t frame_dma_handle; /* Hardware frame list */
+ __hc32 *frame;
+ void **frame_cpu; /* CPU's frame list */
+
+ enum uhci_rh_state rh_state;
+ unsigned long auto_stop_time; /* When to AUTO_STOP */
+
+ unsigned int frame_number; /* As of last check */
+ unsigned int is_stopped;
+#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
+ unsigned int last_iso_frame; /* Frame of last scan */
+ unsigned int cur_iso_frame; /* Frame for current scan */
+
+ unsigned int scan_in_progress:1; /* Schedule scan is running */
+ unsigned int need_rescan:1; /* Redo the schedule scan */
+ unsigned int dead:1; /* Controller has died */
+ unsigned int RD_enable:1; /* Suspended root hub with
+ Resume-Detect interrupts
+ enabled */
+ unsigned int is_initialized:1; /* Data structure is usable */
+ unsigned int fsbr_is_on:1; /* FSBR is turned on */
+ unsigned int fsbr_is_wanted:1; /* Does any URB want FSBR? */
+ unsigned int fsbr_expiring:1; /* FSBR is timing out */
+
+ struct timer_list fsbr_timer; /* For turning off FBSR */
+
+ /* Silicon quirks */
+ unsigned int oc_low:1; /* OverCurrent bit active low */
+ unsigned int wait_for_hp:1; /* Wait for HP port reset */
+ unsigned int big_endian_mmio:1; /* Big endian registers */
+ unsigned int big_endian_desc:1; /* Big endian descriptors */
+
+ /* Support for port suspend/resume/reset */
+ unsigned long port_c_suspend; /* Bit-arrays of ports */
+ unsigned long resuming_ports;
+ unsigned long ports_timeout; /* Time to stop signalling */
+
+ struct list_head idle_qh_list; /* Where the idle QHs live */
+
+ int rh_numports; /* Number of root-hub ports */
+
+ wait_queue_head_t waitqh; /* endpoint_disable waiters */
+ int num_waiting; /* Number of waiters */
+
+ int total_load; /* Sum of array values */
+ short load[MAX_PHASE]; /* Periodic allocations */
+
+ /* Reset host controller */
+ void (*reset_hc) (struct uhci_hcd *uhci);
+ int (*check_and_reset_hc) (struct uhci_hcd *uhci);
+ /* configure_hc should perform arch specific settings, if needed */
+ void (*configure_hc) (struct uhci_hcd *uhci);
+ /* Check for broken resume detect interrupts */
+ int (*resume_detect_interrupts_are_broken) (struct uhci_hcd *uhci);
+ /* Check for broken global suspend */
+ int (*global_suspend_mode_is_broken) (struct uhci_hcd *uhci);
+};
+
+/* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
+static inline struct uhci_hcd *hcd_to_uhci(struct usb_hcd *hcd)
+{
+ return (struct uhci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
+{
+ return container_of((void *) uhci, struct usb_hcd, hcd_priv);
+}
+
+#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
+
+/* Utility macro for comparing frame numbers */
+#define uhci_frame_before_eq(f1, f2) (0 <= (int) ((f2) - (f1)))
+
+
+/*
+ * Private per-URB data
+ */
+struct urb_priv {
+ struct list_head node; /* Node in the QH's urbp list */
+
+ struct urb *urb;
+
+ struct uhci_qh *qh; /* QH for this URB */
+ struct list_head td_list;
+
+ unsigned fsbr:1; /* URB wants FSBR */
+};
+
+
+/* Some special IDs */
+
+#define PCI_VENDOR_ID_GENESYS 0x17a0
+#define PCI_DEVICE_ID_GL880S_UHCI 0x8083
+
+/*
+ * Functions used to access controller registers. The UCHI spec says that host
+ * controller I/O registers are mapped into PCI I/O space. For non-PCI hosts
+ * we use memory mapped registers.
+ */
+
+#ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC
+/* Support PCI only */
+static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
+{
+ return inl(uhci->io_addr + reg);
+}
+
+static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
+{
+ outl(val, uhci->io_addr + reg);
+}
+
+static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
+{
+ return inw(uhci->io_addr + reg);
+}
+
+static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
+{
+ outw(val, uhci->io_addr + reg);
+}
+
+static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
+{
+ return inb(uhci->io_addr + reg);
+}
+
+static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
+{
+ outb(val, uhci->io_addr + reg);
+}
+
+#else
+/* Support non-PCI host controllers */
+#ifdef CONFIG_PCI
+/* Support PCI and non-PCI host controllers */
+#define uhci_has_pci_registers(u) ((u)->io_addr != 0)
+#else
+/* Support non-PCI host controllers only */
+#define uhci_has_pci_registers(u) 0
+#endif
+
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+/* Support (non-PCI) big endian host controllers */
+#define uhci_big_endian_mmio(u) ((u)->big_endian_mmio)
+#else
+#define uhci_big_endian_mmio(u) 0
+#endif
+
+static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ return inl(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ return readl_be(uhci->regs + reg);
+#endif
+ else
+ return readl(uhci->regs + reg);
+}
+
+static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ outl(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ writel_be(val, uhci->regs + reg);
+#endif
+ else
+ writel(val, uhci->regs + reg);
+}
+
+static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ return inw(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ return readw_be(uhci->regs + reg);
+#endif
+ else
+ return readw(uhci->regs + reg);
+}
+
+static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ outw(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ writew_be(val, uhci->regs + reg);
+#endif
+ else
+ writew(val, uhci->regs + reg);
+}
+
+static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ return inb(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ return readb_be(uhci->regs + reg);
+#endif
+ else
+ return readb(uhci->regs + reg);
+}
+
+static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
+{
+ if (uhci_has_pci_registers(uhci))
+ outb(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+ else if (uhci_big_endian_mmio(uhci))
+ writeb_be(val, uhci->regs + reg);
+#endif
+ else
+ writeb(val, uhci->regs + reg);
+}
+#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
+
+/*
+ * The GRLIB GRUSBHC controller can use big endian format for its descriptors.
+ *
+ * UHCI controllers accessed through PCI work normally (little-endian
+ * everywhere), so we don't bother supporting a BE-only mode.
+ */
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC
+#define uhci_big_endian_desc(u) ((u)->big_endian_desc)
+
+/* cpu to uhci */
+static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
+{
+ return uhci_big_endian_desc(uhci)
+ ? (__force __hc32)cpu_to_be32(x)
+ : (__force __hc32)cpu_to_le32(x);
+}
+
+/* uhci to cpu */
+static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
+{
+ return uhci_big_endian_desc(uhci)
+ ? be32_to_cpu((__force __be32)x)
+ : le32_to_cpu((__force __le32)x);
+}
+
+#else
+/* cpu to uhci */
+static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
+{
+ return cpu_to_le32(x);
+}
+
+/* uhci to cpu */
+static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
+{
+ return le32_to_cpu(x);
+}
+#endif
+
+#endif
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
new file mode 100644
index 000000000..ece9e37e8
--- /dev/null
+++ b/drivers/usb/host/uhci-hub.c
@@ -0,0 +1,420 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ */
+
+static const __u8 root_hub_hub_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ USB_DT_HUB, /* __u8 bDescriptorType; Hub-descriptor */
+ 0x02, /* __u8 bNbrPorts; */
+ HUB_CHAR_NO_LPSM | /* __u16 wHubCharacteristics; */
+ HUB_CHAR_INDV_PORT_OCPM, /* (per-port OC, no power switching) */
+ 0x00,
+ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
+ 0x00, /* __u8 bHubContrCurrent; 0 mA */
+ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max */
+ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max */
+};
+
+#define UHCI_RH_MAXCHILD 7
+
+/* must write as zeroes */
+#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
+
+/* status change bits: nonzero writes will clear */
+#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
+
+/* suspend/resume bits: port suspended or port resuming */
+#define SUSPEND_BITS (USBPORTSC_SUSP | USBPORTSC_RD)
+
+/* A port that either is connected or has a changed-bit set will prevent
+ * us from AUTO_STOPPING.
+ */
+static int any_ports_active(struct uhci_hcd *uhci)
+{
+ int port;
+
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if ((uhci_readw(uhci, USBPORTSC1 + port * 2) &
+ (USBPORTSC_CCS | RWC_BITS)) ||
+ test_bit(port, &uhci->port_c_suspend))
+ return 1;
+ }
+ return 0;
+}
+
+static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
+{
+ int port;
+ int mask = RWC_BITS;
+
+ /* Some boards (both VIA and Intel apparently) report bogus
+ * overcurrent indications, causing massive log spam unless
+ * we completely ignore them. This doesn't seem to be a problem
+ * with the chipset so much as with the way it is connected on
+ * the motherboard; if the overcurrent input is left to float
+ * then it may constantly register false positives. */
+ if (ignore_oc)
+ mask &= ~USBPORTSC_OCC;
+
+ *buf = 0;
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) ||
+ test_bit(port, &uhci->port_c_suspend))
+ *buf |= (1 << (port + 1));
+ }
+ return !!*buf;
+}
+
+#define CLR_RH_PORTSTAT(x) \
+ status = uhci_readw(uhci, port_addr); \
+ status &= ~(RWC_BITS|WZ_BITS); \
+ status &= ~(x); \
+ status |= RWC_BITS & (x); \
+ uhci_writew(uhci, status, port_addr)
+
+#define SET_RH_PORTSTAT(x) \
+ status = uhci_readw(uhci, port_addr); \
+ status |= (x); \
+ status &= ~(RWC_BITS|WZ_BITS); \
+ uhci_writew(uhci, status, port_addr)
+
+/* UHCI controllers don't automatically stop resume signalling after 20 msec,
+ * so we have to poll and check timeouts in order to take care of it.
+ */
+static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
+ unsigned long port_addr)
+{
+ int status;
+ int i;
+
+ if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) {
+ CLR_RH_PORTSTAT(SUSPEND_BITS);
+ if (test_bit(port, &uhci->resuming_ports))
+ set_bit(port, &uhci->port_c_suspend);
+
+ /* The controller won't actually turn off the RD bit until
+ * it has had a chance to send a low-speed EOP sequence,
+ * which is supposed to take 3 bit times (= 2 microseconds).
+ * Experiments show that some controllers take longer, so
+ * we'll poll for completion. */
+ for (i = 0; i < 10; ++i) {
+ if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS))
+ break;
+ udelay(1);
+ }
+ }
+ clear_bit(port, &uhci->resuming_ports);
+ usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port);
+}
+
+/* Wait for the UHCI controller in HP's iLO2 server management chip.
+ * It can take up to 250 us to finish a reset and set the CSC bit.
+ */
+static void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr)
+{
+ int i;
+
+ for (i = 10; i < 250; i += 10) {
+ if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC)
+ return;
+ udelay(10);
+ }
+ /* Log a warning? */
+}
+
+static void uhci_check_ports(struct uhci_hcd *uhci)
+{
+ unsigned int port;
+ unsigned long port_addr;
+ int status;
+
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ port_addr = USBPORTSC1 + 2 * port;
+ status = uhci_readw(uhci, port_addr);
+ if (unlikely(status & USBPORTSC_PR)) {
+ if (time_after_eq(jiffies, uhci->ports_timeout)) {
+ CLR_RH_PORTSTAT(USBPORTSC_PR);
+ udelay(10);
+
+ /* HP's server management chip requires
+ * a longer delay. */
+ if (uhci->wait_for_hp)
+ wait_for_HP(uhci, port_addr);
+
+ /* If the port was enabled before, turning
+ * reset on caused a port enable change.
+ * Turning reset off causes a port connect
+ * status change. Clear these changes. */
+ CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
+ SET_RH_PORTSTAT(USBPORTSC_PE);
+ }
+ }
+ if (unlikely(status & USBPORTSC_RD)) {
+ if (!test_bit(port, &uhci->resuming_ports)) {
+
+ /* Port received a wakeup request */
+ set_bit(port, &uhci->resuming_ports);
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ usb_hcd_start_port_resume(
+ &uhci_to_hcd(uhci)->self, port);
+
+ /* Make sure we see the port again
+ * after the resuming period is over. */
+ mod_timer(&uhci_to_hcd(uhci)->rh_timer,
+ uhci->ports_timeout);
+ } else if (time_after_eq(jiffies,
+ uhci->ports_timeout)) {
+ uhci_finish_suspend(uhci, port, port_addr);
+ }
+ }
+ }
+}
+
+static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+
+ uhci_scan_schedule(uhci);
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
+ goto done;
+ uhci_check_ports(uhci);
+
+ status = get_hub_status_data(uhci, buf);
+
+ switch (uhci->rh_state) {
+ case UHCI_RH_SUSPENDED:
+ /* if port change, ask to be resumed */
+ if (status || uhci->resuming_ports) {
+ status = 1;
+ usb_hcd_resume_root_hub(hcd);
+ }
+ break;
+
+ case UHCI_RH_AUTO_STOPPED:
+ /* if port change, auto start */
+ if (status)
+ wakeup_rh(uhci);
+ break;
+
+ case UHCI_RH_RUNNING:
+ /* are any devices attached? */
+ if (!any_ports_active(uhci)) {
+ uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
+ uhci->auto_stop_time = jiffies + HZ;
+ }
+ break;
+
+ case UHCI_RH_RUNNING_NODEVS:
+ /* auto-stop if nothing connected for 1 second */
+ if (any_ports_active(uhci))
+ uhci->rh_state = UHCI_RH_RUNNING;
+ else if (time_after_eq(jiffies, uhci->auto_stop_time) &&
+ !uhci->wait_for_hp)
+ suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
+ break;
+
+ default:
+ break;
+ }
+
+done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return status;
+}
+
+/* size of returned buffer is part of USB spec */
+static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int status, lstatus, retval = 0;
+ unsigned int port = wIndex - 1;
+ unsigned long port_addr = USBPORTSC1 + 2 * port;
+ u16 wPortChange, wPortStatus;
+ unsigned long flags;
+
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
+ return -ETIMEDOUT;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ switch (typeReq) {
+
+ case GetHubStatus:
+ *(__le32 *)buf = cpu_to_le32(0);
+ retval = 4; /* hub power */
+ break;
+ case GetPortStatus:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ uhci_check_ports(uhci);
+ status = uhci_readw(uhci, port_addr);
+
+ /* Intel controllers report the OverCurrent bit active on.
+ * VIA controllers report it active off, so we'll adjust the
+ * bit value. (It's not standardized in the UHCI spec.)
+ */
+ if (uhci->oc_low)
+ status ^= USBPORTSC_OC;
+
+ /* UHCI doesn't support C_RESET (always false) */
+ wPortChange = lstatus = 0;
+ if (status & USBPORTSC_CSC)
+ wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ if (status & USBPORTSC_PEC)
+ wPortChange |= USB_PORT_STAT_C_ENABLE;
+ if ((status & USBPORTSC_OCC) && !ignore_oc)
+ wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
+
+ if (test_bit(port, &uhci->port_c_suspend)) {
+ wPortChange |= USB_PORT_STAT_C_SUSPEND;
+ lstatus |= 1;
+ }
+ if (test_bit(port, &uhci->resuming_ports))
+ lstatus |= 4;
+
+ /* UHCI has no power switching (always on) */
+ wPortStatus = USB_PORT_STAT_POWER;
+ if (status & USBPORTSC_CCS)
+ wPortStatus |= USB_PORT_STAT_CONNECTION;
+ if (status & USBPORTSC_PE) {
+ wPortStatus |= USB_PORT_STAT_ENABLE;
+ if (status & SUSPEND_BITS)
+ wPortStatus |= USB_PORT_STAT_SUSPEND;
+ }
+ if (status & USBPORTSC_OC)
+ wPortStatus |= USB_PORT_STAT_OVERCURRENT;
+ if (status & USBPORTSC_PR)
+ wPortStatus |= USB_PORT_STAT_RESET;
+ if (status & USBPORTSC_LSDA)
+ wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+
+ if (wPortChange)
+ dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
+ wIndex, status, lstatus);
+
+ *(__le16 *)buf = cpu_to_le16(wPortStatus);
+ *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
+ retval = 4;
+ break;
+ case SetHubFeature: /* We don't implement these */
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto err;
+ }
+ break;
+ case SetPortFeature:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ SET_RH_PORTSTAT(USBPORTSC_SUSP);
+ break;
+ case USB_PORT_FEAT_RESET:
+ SET_RH_PORTSTAT(USBPORTSC_PR);
+
+ /* Reset terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
+
+ /* USB v2.0 7.1.7.5 */
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ break;
+ case USB_PORT_FEAT_POWER:
+ /* UHCI has no power switching */
+ break;
+ default:
+ goto err;
+ }
+ break;
+ case ClearPortFeature:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ CLR_RH_PORTSTAT(USBPORTSC_PE);
+
+ /* Disable terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ CLR_RH_PORTSTAT(USBPORTSC_PEC);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) {
+
+ /* Make certain the port isn't suspended */
+ uhci_finish_suspend(uhci, port, port_addr);
+ } else if (!test_and_set_bit(port,
+ &uhci->resuming_ports)) {
+ SET_RH_PORTSTAT(USBPORTSC_RD);
+
+ /* The controller won't allow RD to be set
+ * if the port is disabled. When this happens
+ * just skip the Resume signalling.
+ */
+ if (!(uhci_readw(uhci, port_addr) &
+ USBPORTSC_RD))
+ uhci_finish_suspend(uhci, port,
+ port_addr);
+ else
+ /* USB v2.0 7.1.7.7 */
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(20);
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ clear_bit(port, &uhci->port_c_suspend);
+ break;
+ case USB_PORT_FEAT_POWER:
+ /* UHCI has no power switching */
+ goto err;
+ case USB_PORT_FEAT_C_CONNECTION:
+ CLR_RH_PORTSTAT(USBPORTSC_CSC);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ CLR_RH_PORTSTAT(USBPORTSC_OCC);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* this driver won't report these */
+ break;
+ default:
+ goto err;
+ }
+ break;
+ case GetHubDescriptor:
+ retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
+ memcpy(buf, root_hub_hub_des, retval);
+ if (retval > 2)
+ buf[2] = uhci->rh_numports;
+ break;
+ default:
+err:
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore(&uhci->lock, flags);
+
+ return retval;
+}
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
new file mode 100644
index 000000000..940304c33
--- /dev/null
+++ b/drivers/usb/host/uhci-pci.c
@@ -0,0 +1,306 @@
+/*
+ * UHCI HCD (Host Controller Driver) PCI Bus Glue.
+ *
+ * Extracted from uhci-hcd.c:
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+#include "pci-quirks.h"
+
+/*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+ */
+static void uhci_pci_reset_hc(struct uhci_hcd *uhci)
+{
+ uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
+}
+
+/*
+ * Initialize a controller that was newly discovered or has just been
+ * resumed. In either case we can't be sure of its previous state.
+ *
+ * Returns: 1 if the controller was reset, 0 otherwise.
+ */
+static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci)
+{
+ return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)),
+ uhci->io_addr);
+}
+
+/*
+ * Store the basic register settings needed by the controller.
+ * This function is called at the end of configure_hc in uhci-hcd.c.
+ */
+static void uhci_pci_configure_hc(struct uhci_hcd *uhci)
+{
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
+
+ /* Enable PIRQ */
+ pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+ /* Disable platform-specific non-PME# wakeup */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL, 0);
+}
+
+static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
+{
+ int port;
+
+ switch (to_pci_dev(uhci_dev(uhci))->vendor) {
+ default:
+ break;
+
+ case PCI_VENDOR_ID_GENESYS:
+ /* Genesys Logic's GL880S controllers don't generate
+ * resume-detect interrupts.
+ */
+ return 1;
+
+ case PCI_VENDOR_ID_INTEL:
+ /* Some of Intel's USB controllers have a bug that causes
+ * resume-detect interrupts if any port has an over-current
+ * condition. To make matters worse, some motherboards
+ * hardwire unused USB ports' over-current inputs active!
+ * To prevent problems, we will not enable resume-detect
+ * interrupts if any ports are OC.
+ */
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
+ USBPORTSC_OC)
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci)
+{
+ int port;
+ const char *sys_info;
+ static const char bad_Asus_board[] = "A7V8X";
+
+ /* One of Asus's motherboards has a bug which causes it to
+ * wake up immediately from suspend-to-RAM if any of the ports
+ * are connected. In such cases we will not set EGSM.
+ */
+ sys_info = dmi_get_system_info(DMI_BOARD_NAME);
+ if (sys_info && !strcmp(sys_info, bad_Asus_board)) {
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
+ USBPORTSC_CCS)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int uhci_pci_init(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci->io_addr = (unsigned long) hcd->rsrc_start;
+
+ uhci->rh_numports = uhci_count_ports(hcd);
+
+ /* Intel controllers report the OverCurrent bit active on.
+ * VIA controllers report it active off, so we'll adjust the
+ * bit value. (It's not standardized in the UHCI spec.)
+ */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA)
+ uhci->oc_low = 1;
+
+ /* HP's server management chip requires a longer port reset delay. */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
+ uhci->wait_for_hp = 1;
+
+ /* Set up pointers to PCI-specific functions */
+ uhci->reset_hc = uhci_pci_reset_hc;
+ uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
+ uhci->configure_hc = uhci_pci_configure_hc;
+ uhci->resume_detect_interrupts_are_broken =
+ uhci_pci_resume_detect_interrupts_are_broken;
+ uhci->global_suspend_mode_is_broken =
+ uhci_pci_global_suspend_mode_is_broken;
+
+
+ /* Kick BIOS off this hardware and reset if the controller
+ * isn't already safely quiescent.
+ */
+ check_and_reset_hc(uhci);
+ return 0;
+}
+
+/* Make sure the controller is quiescent and that we're not using it
+ * any more. This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel. Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_shutdown(struct pci_dev *pdev)
+{
+ struct usb_hcd *hcd = pci_get_drvdata(pdev);
+
+ uhci_hc_died(hcd_to_uhci(hcd));
+}
+
+#ifdef CONFIG_PM
+
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated);
+
+static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
+ int rc = 0;
+
+ dev_dbg(uhci_dev(uhci), "%s\n", __func__);
+
+ spin_lock_irq(&uhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
+ goto done_okay; /* Already suspended or dead */
+
+ /* All PCI host controllers are required to disable IRQ generation
+ * at the source, so we must turn off PIRQ.
+ */
+ pci_write_config_word(pdev, USBLEGSUP, 0);
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Enable platform-specific non-PME# wakeup */
+ if (do_wakeup) {
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL,
+ USBPORT1EN | USBPORT2EN);
+ }
+
+done_okay:
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ spin_unlock_irq(&uhci->lock);
+
+ synchronize_irq(hcd->irq);
+
+ /* Check for race with a wakeup request */
+ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ uhci_pci_resume(hcd, false);
+ rc = -EBUSY;
+ }
+ return rc;
+}
+
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ dev_dbg(uhci_dev(uhci), "%s\n", __func__);
+
+ /* Since we aren't in D3 any more, it's safe to set this flag
+ * even if the controller was dead.
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ spin_lock_irq(&uhci->lock);
+
+ /* Make sure resume from hibernation re-enumerates everything */
+ if (hibernated) {
+ uhci->reset_hc(uhci);
+ finish_reset(uhci);
+ }
+
+ /* The firmware may have changed the controller settings during
+ * a system wakeup. Check it and reconfigure to avoid problems.
+ */
+ else {
+ check_and_reset_hc(uhci);
+ }
+ configure_hc(uhci);
+
+ /* Tell the core if the controller had to be reset */
+ if (uhci->rh_state == UHCI_RH_RESET)
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
+ spin_unlock_irq(&uhci->lock);
+
+ /* If interrupts don't work and remote wakeup is enabled then
+ * the suspended root hub needs to be polled.
+ */
+ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Does the root hub have a port wakeup pending? */
+ usb_hcd_poll_rh_status(hcd);
+ return 0;
+}
+
+#endif
+
+static const struct hc_driver uhci_driver = {
+ .description = hcd_name,
+ .product_desc = "UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_pci_init,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .pci_suspend = uhci_pci_suspend,
+ .pci_resume = uhci_pci_resume,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+static const struct pci_device_id uhci_pci_ids[] = { {
+ /* handle any USB UHCI controller */
+ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0),
+ .driver_data = (unsigned long) &uhci_driver,
+ }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+
+static struct pci_driver uhci_pci_driver = {
+ .name = (char *)hcd_name,
+ .id_table = uhci_pci_ids,
+
+ .probe = usb_hcd_pci_probe,
+ .remove = usb_hcd_pci_remove,
+ .shutdown = uhci_shutdown,
+
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &usb_hcd_pci_pm_ops
+ },
+#endif
+};
+
+MODULE_SOFTDEP("pre: ehci_pci");
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
new file mode 100644
index 000000000..3a3e3eeba
--- /dev/null
+++ b/drivers/usb/host/uhci-platform.c
@@ -0,0 +1,152 @@
+/*
+ * Generic UHCI HCD (Host Controller Driver) for Platform Devices
+ *
+ * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This file is based on uhci-grlib.c
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+static int uhci_platform_init(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci->rh_numports = uhci_count_ports(hcd);
+
+ /* Set up pointers to to generic functions */
+ uhci->reset_hc = uhci_generic_reset_hc;
+ uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
+
+ /* No special actions need to be taken for the functions below */
+ uhci->configure_hc = NULL;
+ uhci->resume_detect_interrupts_are_broken = NULL;
+ uhci->global_suspend_mode_is_broken = NULL;
+
+ /* Reset if the controller isn't already safely quiescent. */
+ check_and_reset_hc(uhci);
+ return 0;
+}
+
+static const struct hc_driver uhci_platform_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Generic UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_platform_init,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .pci_suspend = NULL,
+ .pci_resume = NULL,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+static int uhci_hcd_platform_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct uhci_hcd *uhci;
+ struct resource *res;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
+ pdev->name);
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto err_rmr;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ uhci = hcd_to_uhci(hcd);
+
+ uhci->regs = hcd->regs;
+
+ ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
+ if (ret)
+ goto err_rmr;
+
+ device_wakeup_enable(hcd->self.controller);
+ return 0;
+
+err_rmr:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int uhci_hcd_platform_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+/* Make sure the controller is quiescent and that we're not using it
+ * any more. This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel. Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_hcd_platform_shutdown(struct platform_device *op)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(op);
+
+ uhci_hc_died(hcd_to_uhci(hcd));
+}
+
+static const struct of_device_id platform_uhci_ids[] = {
+ { .compatible = "generic-uhci", },
+ { .compatible = "platform-uhci", },
+ {}
+};
+
+static struct platform_driver uhci_platform_driver = {
+ .probe = uhci_hcd_platform_probe,
+ .remove = uhci_hcd_platform_remove,
+ .shutdown = uhci_hcd_platform_shutdown,
+ .driver = {
+ .name = "platform-uhci",
+ .of_match_table = platform_uhci_ids,
+ },
+};
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
new file mode 100644
index 000000000..da6f56d99
--- /dev/null
+++ b/drivers/usb/host/uhci-q.c
@@ -0,0 +1,1794 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurious interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+{
+ if (uhci->is_stopped)
+ mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
+ uhci->term_td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
+{
+ uhci->term_td->status &= ~cpu_to_hc32(uhci, TD_CTRL_IOC);
+}
+
+
+/*
+ * Full-Speed Bandwidth Reclamation (FSBR).
+ * We turn on FSBR whenever a queue that wants it is advancing,
+ * and leave it on for a short time thereafter.
+ */
+static void uhci_fsbr_on(struct uhci_hcd *uhci)
+{
+ struct uhci_qh *lqh;
+
+ /* The terminating skeleton QH always points back to the first
+ * FSBR QH. Make the last async QH point to the terminating
+ * skeleton QH. */
+ uhci->fsbr_is_on = 1;
+ lqh = list_entry(uhci->skel_async_qh->node.prev,
+ struct uhci_qh, node);
+ lqh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
+}
+
+static void uhci_fsbr_off(struct uhci_hcd *uhci)
+{
+ struct uhci_qh *lqh;
+
+ /* Remove the link from the last async QH to the terminating
+ * skeleton QH. */
+ uhci->fsbr_is_on = 0;
+ lqh = list_entry(uhci->skel_async_qh->node.prev,
+ struct uhci_qh, node);
+ lqh->link = UHCI_PTR_TERM(uhci);
+}
+
+static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+
+ if (!(urb->transfer_flags & URB_NO_FSBR))
+ urbp->fsbr = 1;
+}
+
+static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
+{
+ if (urbp->fsbr) {
+ uhci->fsbr_is_wanted = 1;
+ if (!uhci->fsbr_is_on)
+ uhci_fsbr_on(uhci);
+ else if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ del_timer(&uhci->fsbr_timer);
+ }
+ }
+}
+
+static void uhci_fsbr_timeout(unsigned long _uhci)
+{
+ struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 0;
+ uhci_fsbr_off(uhci);
+ }
+ spin_unlock_irqrestore(&uhci->lock, flags);
+}
+
+
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
+{
+ dma_addr_t dma_handle;
+ struct uhci_td *td;
+
+ td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
+ if (!td)
+ return NULL;
+
+ td->dma_handle = dma_handle;
+ td->frame = -1;
+
+ INIT_LIST_HEAD(&td->list);
+ INIT_LIST_HEAD(&td->fl_list);
+
+ return td;
+}
+
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+ if (!list_empty(&td->list))
+ dev_WARN(uhci_dev(uhci), "td %p still in list!\n", td);
+ if (!list_empty(&td->fl_list))
+ dev_WARN(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+ dma_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static inline void uhci_fill_td(struct uhci_hcd *uhci, struct uhci_td *td,
+ u32 status, u32 token, u32 buffer)
+{
+ td->status = cpu_to_hc32(uhci, status);
+ td->token = cpu_to_hc32(uhci, token);
+ td->buffer = cpu_to_hc32(uhci, buffer);
+}
+
+static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp)
+{
+ list_add_tail(&td->list, &urbp->td_list);
+}
+
+static void uhci_remove_td_from_urbp(struct uhci_td *td)
+{
+ list_del_init(&td->list);
+}
+
+/*
+ * We insert Isochronous URBs directly into the frame list at the beginning
+ */
+static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
+ struct uhci_td *td, unsigned framenum)
+{
+ framenum &= (UHCI_NUMFRAMES - 1);
+
+ td->frame = framenum;
+
+ /* Is there a TD already mapped there? */
+ if (uhci->frame_cpu[framenum]) {
+ struct uhci_td *ftd, *ltd;
+
+ ftd = uhci->frame_cpu[framenum];
+ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+ list_add_tail(&td->fl_list, &ftd->fl_list);
+
+ td->link = ltd->link;
+ wmb();
+ ltd->link = LINK_TO_TD(uhci, td);
+ } else {
+ td->link = uhci->frame[framenum];
+ wmb();
+ uhci->frame[framenum] = LINK_TO_TD(uhci, td);
+ uhci->frame_cpu[framenum] = td;
+ }
+}
+
+static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
+ struct uhci_td *td)
+{
+ /* If it's not inserted, don't remove it */
+ if (td->frame == -1) {
+ WARN_ON(!list_empty(&td->fl_list));
+ return;
+ }
+
+ if (uhci->frame_cpu[td->frame] == td) {
+ if (list_empty(&td->fl_list)) {
+ uhci->frame[td->frame] = td->link;
+ uhci->frame_cpu[td->frame] = NULL;
+ } else {
+ struct uhci_td *ntd;
+
+ ntd = list_entry(td->fl_list.next,
+ struct uhci_td,
+ fl_list);
+ uhci->frame[td->frame] = LINK_TO_TD(uhci, ntd);
+ uhci->frame_cpu[td->frame] = ntd;
+ }
+ } else {
+ struct uhci_td *ptd;
+
+ ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+ ptd->link = td->link;
+ }
+
+ list_del_init(&td->fl_list);
+ td->frame = -1;
+}
+
+static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
+ unsigned int framenum)
+{
+ struct uhci_td *ftd, *ltd;
+
+ framenum &= (UHCI_NUMFRAMES - 1);
+
+ ftd = uhci->frame_cpu[framenum];
+ if (ftd) {
+ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+ uhci->frame[framenum] = ltd->link;
+ uhci->frame_cpu[framenum] = NULL;
+
+ while (!list_empty(&ftd->fl_list))
+ list_del_init(ftd->fl_list.prev);
+ }
+}
+
+/*
+ * Remove all the TDs for an Isochronous URB from the frame list
+ */
+static void uhci_unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+ struct uhci_td *td;
+
+ list_for_each_entry(td, &urbp->td_list, list)
+ uhci_remove_td_from_frame_list(uhci, td);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
+ struct usb_device *udev, struct usb_host_endpoint *hep)
+{
+ dma_addr_t dma_handle;
+ struct uhci_qh *qh;
+
+ qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+ if (!qh)
+ return NULL;
+
+ memset(qh, 0, sizeof(*qh));
+ qh->dma_handle = dma_handle;
+
+ qh->element = UHCI_PTR_TERM(uhci);
+ qh->link = UHCI_PTR_TERM(uhci);
+
+ INIT_LIST_HEAD(&qh->queue);
+ INIT_LIST_HEAD(&qh->node);
+
+ if (udev) { /* Normal QH */
+ 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) {
+ dma_pool_free(uhci->qh_pool, qh, dma_handle);
+ return NULL;
+ }
+ }
+ qh->state = QH_STATE_IDLE;
+ qh->hep = hep;
+ qh->udev = udev;
+ hep->hcpriv = qh;
+
+ if (qh->type == USB_ENDPOINT_XFER_INT ||
+ qh->type == USB_ENDPOINT_XFER_ISOC)
+ qh->load = usb_calc_bus_time(udev->speed,
+ usb_endpoint_dir_in(&hep->desc),
+ qh->type == USB_ENDPOINT_XFER_ISOC,
+ usb_endpoint_maxp(&hep->desc))
+ / 1000 + 1;
+
+ } else { /* Skeleton QH */
+ qh->state = QH_STATE_ACTIVE;
+ qh->type = -1;
+ }
+ return qh;
+}
+
+static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
+ if (!list_empty(&qh->queue))
+ dev_WARN(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+
+ list_del(&qh->node);
+ if (qh->udev) {
+ qh->hep->hcpriv = NULL;
+ if (qh->dummy_td)
+ uhci_free_td(uhci, qh->dummy_td);
+ }
+ dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * When a queue is stopped and a dequeued URB is given back, adjust
+ * the previous TD link (if the URB isn't first on the queue) or
+ * save its toggle value (if it is first and is currently executing).
+ *
+ * Returns 0 if the URB should not yet be given back, 1 otherwise.
+ */
+static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_td *td;
+ int ret = 1;
+
+ /* Isochronous pipes don't use toggles and their TD link pointers
+ * get adjusted during uhci_urb_dequeue(). But since their queues
+ * cannot truly be stopped, we have to watch out for dequeues
+ * occurring after the nominal unlink frame. */
+ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
+ ret = (uhci->frame_number + uhci->is_stopped !=
+ qh->unlink_frame);
+ goto done;
+ }
+
+ /* If the URB isn't first on its queue, adjust the link pointer
+ * of the last TD in the previous URB. The toggle doesn't need
+ * to be saved since this URB can't be executing yet. */
+ if (qh->queue.next != &urbp->node) {
+ struct urb_priv *purbp;
+ struct uhci_td *ptd;
+
+ purbp = list_entry(urbp->node.prev, struct urb_priv, node);
+ WARN_ON(list_empty(&purbp->td_list));
+ ptd = list_entry(purbp->td_list.prev, struct uhci_td,
+ list);
+ td = list_entry(urbp->td_list.prev, struct uhci_td,
+ list);
+ ptd->link = td->link;
+ goto done;
+ }
+
+ /* If the QH element pointer is UHCI_PTR_TERM then then currently
+ * executing URB has already been unlinked, so this one isn't it. */
+ if (qh_element(qh) == UHCI_PTR_TERM(uhci))
+ goto done;
+ qh->element = UHCI_PTR_TERM(uhci);
+
+ /* Control pipes don't have to worry about toggles */
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL)
+ goto done;
+
+ /* Save the next toggle value */
+ WARN_ON(list_empty(&urbp->td_list));
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ qh->needs_fixup = 1;
+ qh->initial_toggle = uhci_toggle(td_token(uhci, td));
+
+done:
+ return ret;
+}
+
+/*
+ * Fix up the data toggles for URBs in a queue, when one of them
+ * terminates early (short transfer, error, or dequeued).
+ */
+static void uhci_fixup_toggles(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ int skip_first)
+{
+ struct urb_priv *urbp = NULL;
+ struct uhci_td *td;
+ unsigned int toggle = qh->initial_toggle;
+ unsigned int pipe;
+
+ /* Fixups for a short transfer start with the second URB in the
+ * queue (the short URB is the first). */
+ if (skip_first)
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+
+ /* When starting with the first URB, if the QH element pointer is
+ * still valid then we know the URB's toggles are okay. */
+ else if (qh_element(qh) != UHCI_PTR_TERM(uhci))
+ toggle = 2;
+
+ /* Fix up the toggle for the URBs in the queue. Normally this
+ * loop won't run more than once: When an error or short transfer
+ * occurs, the queue usually gets emptied. */
+ urbp = list_prepare_entry(urbp, &qh->queue, node);
+ list_for_each_entry_continue(urbp, &qh->queue, node) {
+
+ /* If the first TD has the right toggle value, we don't
+ * need to change any toggles in this URB */
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ if (toggle > 1 || uhci_toggle(td_token(uhci, td)) == toggle) {
+ td = list_entry(urbp->td_list.prev, struct uhci_td,
+ list);
+ toggle = uhci_toggle(td_token(uhci, td)) ^ 1;
+
+ /* Otherwise all the toggles in the URB have to be switched */
+ } else {
+ list_for_each_entry(td, &urbp->td_list, list) {
+ td->token ^= cpu_to_hc32(uhci,
+ TD_TOKEN_TOGGLE);
+ toggle ^= 1;
+ }
+ }
+ }
+
+ wmb();
+ pipe = list_entry(qh->queue.next, struct urb_priv, node)->urb->pipe;
+ usb_settoggle(qh->udev, usb_pipeendpoint(pipe),
+ usb_pipeout(pipe), toggle);
+ qh->needs_fixup = 0;
+}
+
+/*
+ * Link an Isochronous QH into its skeleton's list
+ */
+static inline void link_iso(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ list_add_tail(&qh->node, &uhci->skel_iso_qh->node);
+
+ /* Isochronous QHs aren't linked by the hardware */
+}
+
+/*
+ * Link a high-period interrupt QH into the schedule at the end of its
+ * skeleton's list
+ */
+static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+
+ list_add_tail(&qh->node, &uhci->skelqh[qh->skel]->node);
+
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ qh->link = pqh->link;
+ wmb();
+ pqh->link = LINK_TO_QH(uhci, qh);
+}
+
+/*
+ * Link a period-1 interrupt or async QH into the schedule at the
+ * correct spot in the async skeleton's list, and update the FSBR link
+ */
+static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+ __hc32 link_to_new_qh;
+
+ /* Find the predecessor QH for our new one and insert it in the list.
+ * The list of QHs is expected to be short, so linear search won't
+ * take too long. */
+ list_for_each_entry_reverse(pqh, &uhci->skel_async_qh->node, node) {
+ if (pqh->skel <= qh->skel)
+ break;
+ }
+ list_add(&qh->node, &pqh->node);
+
+ /* Link it into the schedule */
+ qh->link = pqh->link;
+ wmb();
+ link_to_new_qh = LINK_TO_QH(uhci, qh);
+ pqh->link = link_to_new_qh;
+
+ /* If this is now the first FSBR QH, link the terminating skeleton
+ * QH to it. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_new_qh;
+}
+
+/*
+ * Put a QH on the schedule in both hardware and software
+ */
+static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ WARN_ON(list_empty(&qh->queue));
+
+ /* Set the element pointer if it isn't set already.
+ * This isn't needed for Isochronous queues, but it doesn't hurt. */
+ if (qh_element(qh) == UHCI_PTR_TERM(uhci)) {
+ struct urb_priv *urbp = list_entry(qh->queue.next,
+ struct urb_priv, node);
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+
+ qh->element = LINK_TO_TD(uhci, td);
+ }
+
+ /* Treat the queue as if it has just advanced */
+ qh->wait_expired = 0;
+ qh->advance_jiffies = jiffies;
+
+ if (qh->state == QH_STATE_ACTIVE)
+ return;
+ qh->state = QH_STATE_ACTIVE;
+
+ /* Move the QH from its old list to the correct spot in the appropriate
+ * skeleton's list */
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_del(&qh->node);
+
+ if (qh->skel == SKEL_ISO)
+ link_iso(uhci, qh);
+ else if (qh->skel < SKEL_ASYNC)
+ link_interrupt(uhci, qh);
+ else
+ link_async(uhci, qh);
+}
+
+/*
+ * Unlink a high-period interrupt QH from the schedule
+ */
+static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ pqh->link = qh->link;
+ mb();
+}
+
+/*
+ * Unlink a period-1 interrupt or async QH from the schedule
+ */
+static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+ __hc32 link_to_next_qh = qh->link;
+
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ pqh->link = link_to_next_qh;
+
+ /* If this was the old first FSBR QH, link the terminating skeleton
+ * QH to the next (new first FSBR) QH. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_next_qh;
+ mb();
+}
+
+/*
+ * Take a QH off the hardware schedule
+ */
+static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ if (qh->state == QH_STATE_UNLINKING)
+ return;
+ WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
+ qh->state = QH_STATE_UNLINKING;
+
+ /* Unlink the QH from the schedule and record when we did it */
+ if (qh->skel == SKEL_ISO)
+ ;
+ else if (qh->skel < SKEL_ASYNC)
+ unlink_interrupt(uhci, qh);
+ else
+ unlink_async(uhci, qh);
+
+ uhci_get_current_frame_number(uhci);
+ qh->unlink_frame = uhci->frame_number;
+
+ /* Force an interrupt so we know when the QH is fully unlinked */
+ if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped)
+ uhci_set_next_interrupt(uhci);
+
+ /* Move the QH from its old list to the end of the unlinking list */
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_move_tail(&qh->node, &uhci->skel_unlink_qh->node);
+}
+
+/*
+ * When we and the controller are through with a QH, it becomes IDLE.
+ * This happens when a QH has been off the schedule (on the unlinking
+ * list) for more than one frame, or when an error occurs while adding
+ * the first URB onto a new QH.
+ */
+static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ WARN_ON(qh->state == QH_STATE_ACTIVE);
+
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_move(&qh->node, &uhci->idle_qh_list);
+ qh->state = QH_STATE_IDLE;
+
+ /* Now that the QH is idle, its post_td isn't being used */
+ if (qh->post_td) {
+ uhci_free_td(uhci, qh->post_td);
+ qh->post_td = NULL;
+ }
+
+ /* If anyone is waiting for a QH to become idle, wake them up */
+ if (uhci->num_waiting)
+ wake_up_all(&uhci->waitqh);
+}
+
+/*
+ * Find the highest existing bandwidth load for a given phase and period.
+ */
+static int uhci_highest_load(struct uhci_hcd *uhci, int phase, int period)
+{
+ int highest_load = uhci->load[phase];
+
+ for (phase += period; phase < MAX_PHASE; phase += period)
+ highest_load = max_t(int, highest_load, uhci->load[phase]);
+ return highest_load;
+}
+
+/*
+ * Set qh->phase to the optimal phase for a periodic transfer and
+ * check whether the bandwidth requirement is acceptable.
+ */
+static int uhci_check_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ int minimax_load;
+
+ /* Find the optimal phase (unless it is already set) and get
+ * its load value. */
+ if (qh->phase >= 0)
+ minimax_load = uhci_highest_load(uhci, qh->phase, qh->period);
+ else {
+ int phase, load;
+ int max_phase = min_t(int, MAX_PHASE, qh->period);
+
+ qh->phase = 0;
+ minimax_load = uhci_highest_load(uhci, qh->phase, qh->period);
+ for (phase = 1; phase < max_phase; ++phase) {
+ load = uhci_highest_load(uhci, phase, qh->period);
+ if (load < minimax_load) {
+ minimax_load = load;
+ qh->phase = phase;
+ }
+ }
+ }
+
+ /* Maximum allowable periodic bandwidth is 90%, or 900 us per frame */
+ if (minimax_load + qh->load > 900) {
+ dev_dbg(uhci_dev(uhci), "bandwidth allocation failed: "
+ "period %d, phase %d, %d + %d us\n",
+ qh->period, qh->phase, minimax_load, qh->load);
+ return -ENOSPC;
+ }
+ return 0;
+}
+
+/*
+ * Reserve a periodic QH's bandwidth in the schedule
+ */
+static void uhci_reserve_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ int i;
+ int load = qh->load;
+ char *p = "??";
+
+ for (i = qh->phase; i < MAX_PHASE; i += qh->period) {
+ uhci->load[i] += load;
+ uhci->total_load += load;
+ }
+ uhci_to_hcd(uhci)->self.bandwidth_allocated =
+ uhci->total_load / MAX_PHASE;
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_INT:
+ ++uhci_to_hcd(uhci)->self.bandwidth_int_reqs;
+ p = "INT";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ ++uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs;
+ p = "ISO";
+ break;
+ }
+ qh->bandwidth_reserved = 1;
+ dev_dbg(uhci_dev(uhci),
+ "%s dev %d ep%02x-%s, period %d, phase %d, %d us\n",
+ "reserve", qh->udev->devnum,
+ qh->hep->desc.bEndpointAddress, p,
+ qh->period, qh->phase, load);
+}
+
+/*
+ * Release a periodic QH's bandwidth reservation
+ */
+static void uhci_release_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ int i;
+ int load = qh->load;
+ char *p = "??";
+
+ for (i = qh->phase; i < MAX_PHASE; i += qh->period) {
+ uhci->load[i] -= load;
+ uhci->total_load -= load;
+ }
+ uhci_to_hcd(uhci)->self.bandwidth_allocated =
+ uhci->total_load / MAX_PHASE;
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_INT:
+ --uhci_to_hcd(uhci)->self.bandwidth_int_reqs;
+ p = "INT";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ --uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs;
+ p = "ISO";
+ break;
+ }
+ qh->bandwidth_reserved = 0;
+ dev_dbg(uhci_dev(uhci),
+ "%s dev %d ep%02x-%s, period %d, phase %d, %d us\n",
+ "release", qh->udev->devnum,
+ qh->hep->desc.bEndpointAddress, p,
+ qh->period, qh->phase, load);
+}
+
+static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci,
+ struct urb *urb)
+{
+ struct urb_priv *urbp;
+
+ urbp = kmem_cache_zalloc(uhci_up_cachep, GFP_ATOMIC);
+ if (!urbp)
+ return NULL;
+
+ urbp->urb = urb;
+ urb->hcpriv = urbp;
+
+ INIT_LIST_HEAD(&urbp->node);
+ INIT_LIST_HEAD(&urbp->td_list);
+
+ return urbp;
+}
+
+static void uhci_free_urb_priv(struct uhci_hcd *uhci,
+ struct urb_priv *urbp)
+{
+ struct uhci_td *td, *tmp;
+
+ if (!list_empty(&urbp->node))
+ dev_WARN(uhci_dev(uhci), "urb %p still on QH's list!\n",
+ urbp->urb);
+
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ }
+
+ kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td_status(uhci, td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(uhci, td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+ if (!status)
+ return 0;
+ if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */
+ return -EPROTO;
+ if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */
+ if (dir_out)
+ return -EPROTO;
+ else
+ return -EILSEQ;
+ }
+ if (status & TD_CTRL_BABBLE) /* Babble */
+ return -EOVERFLOW;
+ if (status & TD_CTRL_DBUFERR) /* Buffer error */
+ return -ENOSR;
+ if (status & TD_CTRL_STALLED) /* Stalled */
+ return -EPIPE;
+ return 0;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+{
+ struct uhci_td *td;
+ unsigned long destination, status;
+ int maxsze = usb_endpoint_maxp(&qh->hep->desc);
+ int len = urb->transfer_buffer_length;
+ dma_addr_t data = urb->transfer_dma;
+ __hc32 *plink;
+ struct urb_priv *urbp = urb->hcpriv;
+ int skel;
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+ /* 3 errors, dummy TD remains inactive */
+ status = uhci_maxerr(3);
+ if (urb->dev->speed == USB_SPEED_LOW)
+ status |= TD_CTRL_LS;
+
+ /*
+ * Build the TD for the control request setup packet
+ */
+ td = qh->dummy_td;
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status, destination | uhci_explen(8),
+ urb->setup_dma);
+ plink = &td->link;
+ status |= TD_CTRL_ACTIVE;
+
+ /*
+ * If direction is "send", change the packet ID from SETUP (0x2D)
+ * to OUT (0xE1). Else change it from SETUP to IN (0x69) and
+ * set Short Packet Detect (SPD) for all data packets.
+ *
+ * 0-length transfers always get treated as "send".
+ */
+ if (usb_pipeout(urb->pipe) || len == 0)
+ destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
+ else {
+ destination ^= (USB_PID_SETUP ^ USB_PID_IN);
+ status |= TD_CTRL_SPD;
+ }
+
+ /*
+ * Build the DATA TDs
+ */
+ while (len > 0) {
+ int pktsze = maxsze;
+
+ if (len <= pktsze) { /* The last data packet */
+ pktsze = len;
+ status &= ~TD_CTRL_SPD;
+ }
+
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+
+ /* Alternate Data0/1 (start with Data1) */
+ destination ^= TD_TOKEN_TOGGLE;
+
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status,
+ destination | uhci_explen(pktsze), data);
+ plink = &td->link;
+
+ data += pktsze;
+ len -= pktsze;
+ }
+
+ /*
+ * Build the final TD for control status
+ */
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+
+ /* Change direction for the status transaction */
+ destination ^= (USB_PID_IN ^ USB_PID_OUT);
+ destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
+
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status | TD_CTRL_IOC,
+ destination | uhci_explen(0), 0);
+ plink = &td->link;
+
+ /*
+ * Build the new dummy TD and activate the old one
+ */
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+
+ uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0);
+ wmb();
+ qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE);
+ qh->dummy_td = td;
+
+ /* Low-speed transfers get a different queue, and won't hog the bus.
+ * Also, some devices enumerate better without FSBR; the easiest way
+ * to do that is to put URBs on the low-speed queue while the device
+ * isn't in the CONFIGURED state. */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->state != USB_STATE_CONFIGURED)
+ skel = SKEL_LS_CONTROL;
+ else {
+ skel = SKEL_FS_CONTROL;
+ uhci_add_fsbr(uhci, urb);
+ }
+ if (qh->state != QH_STATE_ACTIVE)
+ qh->skel = skel;
+ return 0;
+
+nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+ uhci_remove_td_from_urbp(qh->dummy_td);
+ return -ENOMEM;
+}
+
+/*
+ * Common submit for bulk and interrupt
+ */
+static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+{
+ struct uhci_td *td;
+ unsigned long destination, status;
+ int maxsze = usb_endpoint_maxp(&qh->hep->desc);
+ int len = urb->transfer_buffer_length;
+ int this_sg_len;
+ dma_addr_t data;
+ __hc32 *plink;
+ struct urb_priv *urbp = urb->hcpriv;
+ unsigned int toggle;
+ struct scatterlist *sg;
+ int i;
+
+ if (len < 0)
+ return -EINVAL;
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+
+ /* 3 errors, dummy TD remains inactive */
+ status = uhci_maxerr(3);
+ if (urb->dev->speed == USB_SPEED_LOW)
+ status |= TD_CTRL_LS;
+ if (usb_pipein(urb->pipe))
+ status |= TD_CTRL_SPD;
+
+ i = urb->num_mapped_sgs;
+ if (len > 0 && i > 0) {
+ sg = urb->sg;
+ data = sg_dma_address(sg);
+
+ /* urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ } else {
+ sg = NULL;
+ data = urb->transfer_dma;
+ this_sg_len = len;
+ }
+ /*
+ * Build the DATA TDs
+ */
+ plink = NULL;
+ td = qh->dummy_td;
+ for (;;) { /* Allow zero length packets */
+ int pktsze = maxsze;
+
+ if (len <= pktsze) { /* The last packet */
+ pktsze = len;
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ status &= ~TD_CTRL_SPD;
+ }
+
+ if (plink) {
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+ }
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status,
+ destination | uhci_explen(pktsze) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+ plink = &td->link;
+ status |= TD_CTRL_ACTIVE;
+
+ toggle ^= 1;
+ data += pktsze;
+ this_sg_len -= pktsze;
+ len -= maxsze;
+ if (this_sg_len <= 0) {
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ data = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ }
+ }
+
+ /*
+ * URB_ZERO_PACKET means adding a 0-length packet, if direction
+ * is OUT and the transfer_length was an exact multiple of maxsze,
+ * hence (len = transfer_length - N * maxsze) == 0
+ * however, if transfer_length == 0, the zero packet was already
+ * prepared above.
+ */
+ if ((urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && len == 0 &&
+ urb->transfer_buffer_length > 0) {
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status,
+ destination | uhci_explen(0) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+ plink = &td->link;
+
+ toggle ^= 1;
+ }
+
+ /* Set the interrupt-on-completion flag on the last packet.
+ * A more-or-less typical 4 KB URB (= size of one memory page)
+ * will require about 3 ms to transfer; that's a little on the
+ * fast side but not enough to justify delaying an interrupt
+ * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
+ * flag setting. */
+ td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
+
+ /*
+ * Build the new dummy TD and activate the old one
+ */
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = LINK_TO_TD(uhci, td);
+
+ uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0);
+ wmb();
+ qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE);
+ qh->dummy_td = td;
+
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+ return 0;
+
+nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+ uhci_remove_td_from_urbp(qh->dummy_td);
+ return -ENOMEM;
+}
+
+static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+{
+ int ret;
+
+ /* Can't have low-speed bulk transfers */
+ if (urb->dev->speed == USB_SPEED_LOW)
+ return -EINVAL;
+
+ if (qh->state != QH_STATE_ACTIVE)
+ qh->skel = SKEL_BULK;
+ ret = uhci_submit_common(uhci, urb, qh);
+ if (ret == 0)
+ uhci_add_fsbr(uhci, urb);
+ return ret;
+}
+
+static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+{
+ int ret;
+
+ /* USB 1.1 interrupt transfers only involve one packet per interval.
+ * Drivers can submit URBs of any length, but longer ones will need
+ * multiple intervals to complete.
+ */
+
+ if (!qh->bandwidth_reserved) {
+ int exponent;
+
+ /* Figure out which power-of-two queue to use */
+ for (exponent = 7; exponent >= 0; --exponent) {
+ if ((1 << exponent) <= urb->interval)
+ break;
+ }
+ if (exponent < 0)
+ return -EINVAL;
+
+ /* If the slot is full, try a lower period */
+ do {
+ qh->period = 1 << exponent;
+ qh->skel = SKEL_INDEX(exponent);
+
+ /* For now, interrupt phase is fixed by the layout
+ * of the QH lists.
+ */
+ qh->phase = (qh->period / 2) & (MAX_PHASE - 1);
+ ret = uhci_check_bandwidth(uhci, qh);
+ } while (ret != 0 && --exponent >= 0);
+ if (ret)
+ return ret;
+ } else if (qh->period > urb->interval)
+ return -EINVAL; /* Can't decrease the period */
+
+ ret = uhci_submit_common(uhci, urb, qh);
+ if (ret == 0) {
+ urb->interval = qh->period;
+ if (!qh->bandwidth_reserved)
+ uhci_reserve_bandwidth(uhci, qh);
+ }
+ return ret;
+}
+
+/*
+ * Fix up the data structures following a short transfer
+ */
+static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, struct urb_priv *urbp)
+{
+ struct uhci_td *td;
+ struct list_head *tmp;
+ int ret;
+
+ td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+
+ /* When a control transfer is short, we have to restart
+ * the queue at the status stage transaction, which is
+ * the last TD. */
+ WARN_ON(list_empty(&urbp->td_list));
+ qh->element = LINK_TO_TD(uhci, td);
+ tmp = td->list.prev;
+ ret = -EINPROGRESS;
+
+ } else {
+
+ /* When a bulk/interrupt transfer is short, we have to
+ * fix up the toggles of the following URBs on the queue
+ * before restarting the queue at the next URB. */
+ qh->initial_toggle =
+ uhci_toggle(td_token(uhci, qh->post_td)) ^ 1;
+ uhci_fixup_toggles(uhci, qh, 1);
+
+ if (list_empty(&urbp->td_list))
+ td = qh->post_td;
+ qh->element = td->link;
+ tmp = urbp->td_list.prev;
+ ret = 0;
+ }
+
+ /* Remove all the TDs we skipped over, from tmp back to the start */
+ while (tmp != &urbp->td_list) {
+ td = list_entry(tmp, struct uhci_td, list);
+ tmp = tmp->prev;
+
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ }
+ return ret;
+}
+
+/*
+ * Common result for control, bulk, and interrupt
+ */
+static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
+ struct uhci_td *td, *tmp;
+ unsigned status;
+ int ret = 0;
+
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int len;
+
+ ctrlstat = td_status(uhci, td);
+ status = uhci_status_bits(ctrlstat);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ len = uhci_actual_length(ctrlstat);
+ urb->actual_length += len;
+
+ if (status) {
+ ret = uhci_map_status(status,
+ uhci_packetout(td_token(uhci, td)));
+ if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+ /* Some debugging code */
+ dev_dbg(&urb->dev->dev,
+ "%s: failed with status %x\n",
+ __func__, status);
+
+ if (debug > 1 && errbuf) {
+ /* Print the chain for debugging */
+ uhci_show_qh(uhci, urbp->qh, errbuf,
+ ERRBUF_LEN - EXTRA_SPACE, 0);
+ lprintk(errbuf);
+ }
+ }
+
+ /* Did we receive a short packet? */
+ } else if (len < uhci_expected_length(td_token(uhci, td))) {
+
+ /* For control transfers, go to the status TD if
+ * this isn't already the last data TD */
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+ if (td->list.next != urbp->td_list.prev)
+ ret = 1;
+ }
+
+ /* For bulk and interrupt, this may be an error */
+ else if (urb->transfer_flags & URB_SHORT_NOT_OK)
+ ret = -EREMOTEIO;
+
+ /* Fixup needed only if this isn't the URB's last TD */
+ else if (&td->list != urbp->td_list.prev)
+ ret = 1;
+ }
+
+ uhci_remove_td_from_urbp(td);
+ if (qh->post_td)
+ uhci_free_td(uhci, qh->post_td);
+ qh->post_td = td;
+
+ if (ret != 0)
+ goto err;
+ }
+ return ret;
+
+err:
+ if (ret < 0) {
+ /* Note that the queue has stopped and save
+ * the next toggle value */
+ qh->element = UHCI_PTR_TERM(uhci);
+ qh->is_stopped = 1;
+ qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
+ qh->initial_toggle = uhci_toggle(td_token(uhci, td)) ^
+ (ret == -EREMOTEIO);
+
+ } else /* Short packet received */
+ ret = uhci_fixup_short_transfer(uhci, qh, urbp);
+ return ret;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+{
+ struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */
+ int i;
+ unsigned frame, next;
+ unsigned long destination, status;
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+ /* Values must not be too big (could overflow below) */
+ if (urb->interval >= UHCI_NUMFRAMES ||
+ urb->number_of_packets >= UHCI_NUMFRAMES)
+ return -EFBIG;
+
+ uhci_get_current_frame_number(uhci);
+
+ /* Check the period and figure out the starting frame number */
+ if (!qh->bandwidth_reserved) {
+ qh->period = urb->interval;
+ qh->phase = -1; /* Find the best phase */
+ i = uhci_check_bandwidth(uhci, qh);
+ if (i)
+ return i;
+
+ /* Allow a little time to allocate the TDs */
+ next = uhci->frame_number + 10;
+ frame = qh->phase;
+
+ /* Round up to the first available slot */
+ frame += (next - frame + qh->period - 1) & -qh->period;
+
+ } else if (qh->period != urb->interval) {
+ return -EINVAL; /* Can't change the period */
+
+ } else {
+ next = uhci->frame_number + 1;
+
+ /* Find the next unused frame */
+ if (list_empty(&qh->queue)) {
+ frame = qh->iso_frame;
+ } else {
+ struct urb *lurb;
+
+ lurb = list_entry(qh->queue.prev,
+ struct urb_priv, node)->urb;
+ frame = lurb->start_frame +
+ lurb->number_of_packets *
+ lurb->interval;
+ }
+
+ /* Fell behind? */
+ if (!uhci_frame_before_eq(next, frame)) {
+
+ /* USB_ISO_ASAP: Round up to the first available slot */
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ frame += (next - frame + qh->period - 1) &
+ -qh->period;
+
+ /*
+ * Not ASAP: Use the next slot in the stream,
+ * no matter what.
+ */
+ else if (!uhci_frame_before_eq(next,
+ frame + (urb->number_of_packets - 1) *
+ qh->period))
+ dev_dbg(uhci_dev(uhci), "iso underrun %p (%u+%u < %u)\n",
+ urb, frame,
+ (urb->number_of_packets - 1) *
+ qh->period,
+ next);
+ }
+ }
+
+ /* Make sure we won't have to go too far into the future */
+ if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
+ frame + urb->number_of_packets * urb->interval))
+ return -EFBIG;
+ urb->start_frame = frame;
+
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ return -ENOMEM;
+
+ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(uhci, td, status, destination |
+ uhci_explen(urb->iso_frame_desc[i].length),
+ urb->transfer_dma +
+ urb->iso_frame_desc[i].offset);
+ }
+
+ /* Set the interrupt-on-completion flag on the last packet. */
+ td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
+
+ /* Add the TDs to the frame list */
+ frame = urb->start_frame;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ uhci_insert_td_in_frame_list(uhci, td, frame);
+ frame += qh->period;
+ }
+
+ if (list_empty(&qh->queue)) {
+ qh->iso_packet_desc = &urb->iso_frame_desc[0];
+ qh->iso_frame = urb->start_frame;
+ }
+
+ qh->skel = SKEL_ISO;
+ if (!qh->bandwidth_reserved)
+ uhci_reserve_bandwidth(uhci, qh);
+ return 0;
+}
+
+static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct uhci_td *td, *tmp;
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
+
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int status;
+ int actlength;
+
+ if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
+ return -EINPROGRESS;
+
+ uhci_remove_tds_from_frame(uhci, qh->iso_frame);
+
+ ctrlstat = td_status(uhci, td);
+ if (ctrlstat & TD_CTRL_ACTIVE) {
+ status = -EXDEV; /* TD was added too late? */
+ } else {
+ status = uhci_map_status(uhci_status_bits(ctrlstat),
+ usb_pipeout(urb->pipe));
+ actlength = uhci_actual_length(ctrlstat);
+
+ urb->actual_length += actlength;
+ qh->iso_packet_desc->actual_length = actlength;
+ qh->iso_packet_desc->status = status;
+ }
+ if (status)
+ urb->error_count++;
+
+ uhci_remove_td_from_urbp(td);
+ uhci_free_td(uhci, td);
+ qh->iso_frame += qh->period;
+ ++qh->iso_packet_desc;
+ }
+ return 0;
+}
+
+static int uhci_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
+{
+ int ret;
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ struct urb_priv *urbp;
+ struct uhci_qh *qh;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto done_not_linked;
+
+ ret = -ENOMEM;
+ urbp = uhci_alloc_urb_priv(uhci, urb);
+ if (!urbp)
+ goto done;
+
+ if (urb->ep->hcpriv)
+ qh = urb->ep->hcpriv;
+ else {
+ qh = uhci_alloc_qh(uhci, urb->dev, urb->ep);
+ if (!qh)
+ goto err_no_qh;
+ }
+ urbp->qh = qh;
+
+ switch (qh->type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ret = uhci_submit_control(uhci, urb, qh);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ret = uhci_submit_bulk(uhci, urb, qh);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ ret = uhci_submit_interrupt(uhci, urb, qh);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ urb->error_count = 0;
+ ret = uhci_submit_isochronous(uhci, urb, qh);
+ break;
+ }
+ if (ret != 0)
+ goto err_submit_failed;
+
+ /* Add this URB to the QH */
+ list_add_tail(&urbp->node, &qh->queue);
+
+ /* If the new URB is the first and only one on this QH then either
+ * the QH is new and idle or else it's unlinked and waiting to
+ * become idle, so we can activate it right away. But only if the
+ * queue isn't stopped. */
+ if (qh->queue.next == &urbp->node && !qh->is_stopped) {
+ uhci_activate_qh(uhci, qh);
+ uhci_urbp_wants_fsbr(uhci, urbp);
+ }
+ goto done;
+
+err_submit_failed:
+ if (qh->state == QH_STATE_IDLE)
+ uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */
+err_no_qh:
+ uhci_free_urb_priv(uhci, urbp);
+done:
+ if (ret)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+done_not_linked:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return ret;
+}
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ struct uhci_qh *qh;
+ int rc;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ qh = ((struct urb_priv *) urb->hcpriv)->qh;
+
+ /* Remove Isochronous TDs from the frame list ASAP */
+ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
+ uhci_unlink_isochronous_tds(uhci, urb);
+ mb();
+
+ /* If the URB has already started, update the QH unlink time */
+ uhci_get_current_frame_number(uhci);
+ if (uhci_frame_before_eq(urb->start_frame, uhci->frame_number))
+ qh->unlink_frame = uhci->frame_number;
+ }
+
+ uhci_unlink_qh(uhci, qh);
+
+done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return rc;
+}
+
+/*
+ * Finish unlinking an URB and give it back
+ */
+static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct urb *urb, int status)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+
+ /* Subtract off the length of the SETUP packet from
+ * urb->actual_length.
+ */
+ urb->actual_length -= min_t(u32, 8, urb->actual_length);
+ }
+
+ /* When giving back the first URB in an Isochronous queue,
+ * reinitialize the QH's iso-related members for the next URB. */
+ else if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+ urbp->node.prev == &qh->queue &&
+ urbp->node.next != &qh->queue) {
+ struct urb *nurb = list_entry(urbp->node.next,
+ struct urb_priv, node)->urb;
+
+ qh->iso_packet_desc = &nurb->iso_frame_desc[0];
+ qh->iso_frame = nurb->start_frame;
+ }
+
+ /* Take the URB off the QH's queue. If the queue is now empty,
+ * this is a perfect time for a toggle fixup. */
+ list_del_init(&urbp->node);
+ if (list_empty(&qh->queue) && qh->needs_fixup) {
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), qh->initial_toggle);
+ qh->needs_fixup = 0;
+ }
+
+ uhci_free_urb_priv(uhci, urbp);
+ usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb);
+
+ spin_unlock(&uhci->lock);
+ usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, status);
+ spin_lock(&uhci->lock);
+
+ /* If the queue is now empty, we can unlink the QH and give up its
+ * reserved bandwidth. */
+ if (list_empty(&qh->queue)) {
+ uhci_unlink_qh(uhci, qh);
+ if (qh->bandwidth_reserved)
+ uhci_release_bandwidth(uhci, qh);
+ }
+}
+
+/*
+ * Scan the URBs in a QH's queue
+ */
+#define QH_FINISHED_UNLINKING(qh) \
+ (qh->state == QH_STATE_UNLINKING && \
+ uhci->frame_number + uhci->is_stopped != qh->unlink_frame)
+
+static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct urb_priv *urbp;
+ struct urb *urb;
+ int status;
+
+ while (!list_empty(&qh->queue)) {
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ urb = urbp->urb;
+
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ status = uhci_result_isochronous(uhci, urb);
+ else
+ status = uhci_result_common(uhci, urb);
+ if (status == -EINPROGRESS)
+ break;
+
+ /* Dequeued but completed URBs can't be given back unless
+ * the QH is stopped or has finished unlinking. */
+ if (urb->unlinked) {
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
+ return;
+ }
+
+ uhci_giveback_urb(uhci, qh, urb, status);
+ if (status < 0)
+ break;
+ }
+
+ /* If the QH is neither stopped nor finished unlinking (normal case),
+ * our work here is done. */
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
+ return;
+
+ /* Otherwise give back each of the dequeued URBs */
+restart:
+ list_for_each_entry(urbp, &qh->queue, node) {
+ urb = urbp->urb;
+ if (urb->unlinked) {
+
+ /* Fix up the TD links and save the toggles for
+ * non-Isochronous queues. For Isochronous queues,
+ * test for too-recent dequeues. */
+ if (!uhci_cleanup_queue(uhci, qh, urb)) {
+ qh->is_stopped = 0;
+ return;
+ }
+ uhci_giveback_urb(uhci, qh, urb, 0);
+ goto restart;
+ }
+ }
+ qh->is_stopped = 0;
+
+ /* There are no more dequeued URBs. If there are still URBs on the
+ * queue, the QH can now be re-activated. */
+ if (!list_empty(&qh->queue)) {
+ if (qh->needs_fixup)
+ uhci_fixup_toggles(uhci, qh, 0);
+
+ /* If the first URB on the queue wants FSBR but its time
+ * limit has expired, set the next TD to interrupt on
+ * completion before reactivating the QH. */
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ if (urbp->fsbr && qh->wait_expired) {
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+
+ td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
+ }
+
+ uhci_activate_qh(uhci, qh);
+ }
+
+ /* The queue is empty. The QH can become idle if it is fully
+ * unlinked. */
+ else if (QH_FINISHED_UNLINKING(qh))
+ uhci_make_qh_idle(uhci, qh);
+}
+
+/*
+ * Check for queues that have made some forward progress.
+ * Returns 0 if the queue is not Isochronous, is ACTIVE, and
+ * has not advanced since last examined; 1 otherwise.
+ *
+ * Early Intel controllers have a bug which causes qh->element sometimes
+ * not to advance when a TD completes successfully. The queue remains
+ * stuck on the inactive completed TD. We detect such cases and advance
+ * the element pointer by hand.
+ */
+static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct urb_priv *urbp = NULL;
+ struct uhci_td *td;
+ int ret = 1;
+ unsigned status;
+
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ goto done;
+
+ /* Treat an UNLINKING queue as though it hasn't advanced.
+ * This is okay because reactivation will treat it as though
+ * it has advanced, and if it is going to become IDLE then
+ * this doesn't matter anyway. Furthermore it's possible
+ * for an UNLINKING queue not to have any URBs at all, or
+ * for its first URB not to have any TDs (if it was dequeued
+ * just as it completed). So it's not easy in any case to
+ * test whether such queues have advanced. */
+ if (qh->state != QH_STATE_ACTIVE) {
+ urbp = NULL;
+ status = 0;
+
+ } else {
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ status = td_status(uhci, td);
+ if (!(status & TD_CTRL_ACTIVE)) {
+
+ /* We're okay, the queue has advanced */
+ qh->wait_expired = 0;
+ qh->advance_jiffies = jiffies;
+ goto done;
+ }
+ ret = uhci->is_stopped;
+ }
+
+ /* The queue hasn't advanced; check for timeout */
+ if (qh->wait_expired)
+ goto done;
+
+ if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+
+ /* Detect the Intel bug and work around it */
+ if (qh->post_td && qh_element(qh) ==
+ LINK_TO_TD(uhci, qh->post_td)) {
+ qh->element = qh->post_td->link;
+ qh->advance_jiffies = jiffies;
+ ret = 1;
+ goto done;
+ }
+
+ qh->wait_expired = 1;
+
+ /* If the current URB wants FSBR, unlink it temporarily
+ * so that we can safely set the next TD to interrupt on
+ * completion. That way we'll know as soon as the queue
+ * starts moving again. */
+ if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
+ uhci_unlink_qh(uhci, qh);
+
+ } else {
+ /* Unmoving but not-yet-expired queues keep FSBR alive */
+ if (urbp)
+ uhci_urbp_wants_fsbr(uhci, urbp);
+ }
+
+done:
+ return ret;
+}
+
+/*
+ * Process events in the schedule, but only in one thread at a time
+ */
+static void uhci_scan_schedule(struct uhci_hcd *uhci)
+{
+ int i;
+ struct uhci_qh *qh;
+
+ /* Don't allow re-entrant calls */
+ if (uhci->scan_in_progress) {
+ uhci->need_rescan = 1;
+ return;
+ }
+ uhci->scan_in_progress = 1;
+rescan:
+ uhci->need_rescan = 0;
+ uhci->fsbr_is_wanted = 0;
+
+ uhci_clear_next_interrupt(uhci);
+ uhci_get_current_frame_number(uhci);
+ uhci->cur_iso_frame = uhci->frame_number;
+
+ /* Go through all the QH queues and process the URBs in each one */
+ for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
+ uhci->next_qh = list_entry(uhci->skelqh[i]->node.next,
+ struct uhci_qh, node);
+ while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
+ uhci->next_qh = list_entry(qh->node.next,
+ struct uhci_qh, node);
+
+ if (uhci_advance_check(uhci, qh)) {
+ uhci_scan_qh(uhci, qh);
+ if (qh->state == QH_STATE_ACTIVE) {
+ uhci_urbp_wants_fsbr(uhci,
+ list_entry(qh->queue.next, struct urb_priv, node));
+ }
+ }
+ }
+ }
+
+ uhci->last_iso_frame = uhci->cur_iso_frame;
+ if (uhci->need_rescan)
+ goto rescan;
+ uhci->scan_in_progress = 0;
+
+ if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&
+ !uhci->fsbr_expiring) {
+ uhci->fsbr_expiring = 1;
+ mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);
+ }
+
+ if (list_empty(&uhci->skel_unlink_qh->node))
+ uhci_clear_next_interrupt(uhci);
+ else
+ uhci_set_next_interrupt(uhci);
+}
diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild
new file mode 100644
index 000000000..26df01380
--- /dev/null
+++ b/drivers/usb/host/whci/Kbuild
@@ -0,0 +1,12 @@
+obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o
+
+whci-hcd-y := \
+ asl.o \
+ debug.o \
+ hcd.o \
+ hw.o \
+ init.o \
+ int.o \
+ pzl.o \
+ qset.o \
+ wusb.o
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
new file mode 100644
index 000000000..773249306
--- /dev/null
+++ b/drivers/usb/host/whci/asl.c
@@ -0,0 +1,389 @@
+/*
+ * Wireless Host Controller (WHC) asynchronous schedule management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset,
+ struct whc_qset **next, struct whc_qset **prev)
+{
+ struct list_head *n, *p;
+
+ BUG_ON(list_empty(&whc->async_list));
+
+ n = qset->list_node.next;
+ if (n == &whc->async_list)
+ n = n->next;
+ p = qset->list_node.prev;
+ if (p == &whc->async_list)
+ p = p->prev;
+
+ *next = container_of(n, struct whc_qset, list_node);
+ *prev = container_of(p, struct whc_qset, list_node);
+
+}
+
+static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset)
+{
+ list_move(&qset->list_node, &whc->async_list);
+ qset->in_sw_list = true;
+}
+
+static void asl_qset_insert(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_qset *next, *prev;
+
+ qset_clear(whc, qset);
+
+ /* Link into ASL. */
+ qset_get_next_prev(whc, qset, &next, &prev);
+ whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma);
+ whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma);
+ qset->in_hw_list = true;
+}
+
+static void asl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_qset *prev, *next;
+
+ qset_get_next_prev(whc, qset, &next, &prev);
+
+ list_move(&qset->list_node, &whc->async_removed_list);
+ qset->in_sw_list = false;
+
+ /*
+ * No more qsets in the ASL? The caller must stop the ASL as
+ * it's no longer valid.
+ */
+ if (list_empty(&whc->async_list))
+ return;
+
+ /* Remove from ASL. */
+ whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma);
+ qset->in_hw_list = false;
+}
+
+/**
+ * process_qset - process any recently inactivated or halted qTDs in a
+ * qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns any additional WUSBCMD bits for the ASL sync command (i.e.,
+ * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed).
+ */
+static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
+{
+ enum whc_update update = 0;
+ uint32_t status = 0;
+
+ while (qset->ntds) {
+ struct whc_qtd *td;
+ int t;
+
+ t = qset->td_start;
+ td = &qset->qtd[qset->td_start];
+ status = le32_to_cpu(td->status);
+
+ /*
+ * Nothing to do with a still active qTD.
+ */
+ if (status & QTD_STS_ACTIVE)
+ break;
+
+ if (status & QTD_STS_HALTED) {
+ /* Ug, an error. */
+ process_halted_qtd(whc, qset, td);
+ /* A halted qTD always triggers an update
+ because the qset was either removed or
+ reactivated. */
+ update |= WHC_UPDATE_UPDATED;
+ goto done;
+ }
+
+ /* Mmm, a completed qTD. */
+ process_inactive_qtd(whc, qset, td);
+ }
+
+ if (!qset->remove)
+ update |= qset_add_qtds(whc, qset);
+
+done:
+ /*
+ * Remove this qset from the ASL if requested, but only if has
+ * no qTDs.
+ */
+ if (qset->remove && qset->ntds == 0) {
+ asl_qset_remove(whc, qset);
+ update |= WHC_UPDATE_REMOVED;
+ }
+ return update;
+}
+
+void asl_start(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+
+ le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR);
+
+ whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED,
+ 1000, "start ASL");
+}
+
+void asl_stop(struct whc *whc)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_ASYNC_SCHED, 0,
+ 1000, "stop ASL");
+}
+
+/**
+ * asl_update - request an ASL update and wait for the hardware to be synced
+ * @whc: the WHCI HC
+ * @wusbcmd: WUSBCMD value to start the update.
+ *
+ * If the WUSB HC is inactive (i.e., the ASL is stopped) then the
+ * update must be skipped as the hardware may not respond to update
+ * requests.
+ */
+void asl_update(struct whc *whc, uint32_t wusbcmd)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+ long t;
+
+ mutex_lock(&wusbhc->mutex);
+ if (wusbhc->active) {
+ whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+ t = wait_event_timeout(
+ whc->async_list_wq,
+ (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0,
+ msecs_to_jiffies(1000));
+ if (t == 0)
+ whc_hw_error(whc, "ASL update timeout");
+ }
+ mutex_unlock(&wusbhc->mutex);
+}
+
+/**
+ * scan_async_work - scan the ASL for qsets to process.
+ *
+ * Process each qset in the ASL in turn and then signal the WHC that
+ * the ASL has been updated.
+ *
+ * Then start, stop or update the asynchronous schedule as required.
+ */
+void scan_async_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, async_work);
+ struct whc_qset *qset, *t;
+ enum whc_update update = 0;
+
+ spin_lock_irq(&whc->lock);
+
+ /*
+ * Transerve the software list backwards so new qsets can be
+ * safely inserted into the ASL without making it non-circular.
+ */
+ list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) {
+ if (!qset->in_hw_list) {
+ asl_qset_insert(whc, qset);
+ update |= WHC_UPDATE_ADDED;
+ }
+
+ update |= process_qset(whc, qset);
+ }
+
+ spin_unlock_irq(&whc->lock);
+
+ if (update) {
+ uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB;
+ if (update & WHC_UPDATE_REMOVED)
+ wusbcmd |= WUSBCMD_ASYNC_QSET_RM;
+ asl_update(whc, wusbcmd);
+ }
+
+ /*
+ * Now that the ASL is updated, complete the removal of any
+ * removed qsets.
+ *
+ * If the qset was to be reset, do so and reinsert it into the
+ * ASL if it has pending transfers.
+ */
+ spin_lock_irq(&whc->lock);
+
+ list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
+ qset_remove_complete(whc, qset);
+ if (qset->reset) {
+ qset_reset(whc, qset);
+ if (!list_empty(&qset->stds)) {
+ asl_qset_insert_begin(whc, qset);
+ queue_work(whc->workqueue, &whc->async_work);
+ }
+ }
+ }
+
+ spin_unlock_irq(&whc->lock);
+}
+
+/**
+ * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL).
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the ASL.
+ */
+int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (err < 0) {
+ spin_unlock_irqrestore(&whc->lock, flags);
+ return err;
+ }
+
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+ if (!qset->in_sw_list && !qset->remove)
+ asl_qset_insert_begin(whc, qset);
+ } else
+ usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->async_work);
+
+ return err;
+}
+
+/**
+ * asl_urb_dequeue - remove an URB (qset) from the async list.
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed from the ASL so the qTDs
+ * can be removed.
+ */
+int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+ struct whc_urb *wurb = urb->hcpriv;
+ struct whc_qset *qset = wurb->qset;
+ struct whc_std *std, *t;
+ bool has_qtd = false;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+ if (ret < 0)
+ goto out;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb) {
+ if (std->qtd)
+ has_qtd = true;
+ qset_free_std(whc, std);
+ } else
+ std->qtd = NULL; /* so this std is re-added when the qset is */
+ }
+
+ if (has_qtd) {
+ asl_qset_remove(whc, qset);
+ wurb->status = status;
+ wurb->is_async = true;
+ queue_work(whc->workqueue, &wurb->dequeue_work);
+ } else
+ qset_remove_urb(whc, qset, urb, status);
+out:
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * asl_qset_delete - delete a qset from the ASL
+ */
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ qset->remove = 1;
+ queue_work(whc->workqueue, &whc->async_work);
+ qset_delete(whc, qset);
+}
+
+/**
+ * asl_init - initialize the asynchronous schedule list
+ *
+ * A dummy qset with no qTDs is added to the ASL to simplify removing
+ * qsets (no need to stop the ASL when the last qset is removed).
+ */
+int asl_init(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ qset = qset_alloc(whc, GFP_KERNEL);
+ if (qset == NULL)
+ return -ENOMEM;
+
+ asl_qset_insert_begin(whc, qset);
+ asl_qset_insert(whc, qset);
+
+ return 0;
+}
+
+/**
+ * asl_clean_up - free ASL resources
+ *
+ * The ASL is stopped and empty except for the dummy qset.
+ */
+void asl_clean_up(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ if (!list_empty(&whc->async_list)) {
+ qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+ list_del(&qset->list_node);
+ qset_free(whc, qset);
+ }
+}
diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c
new file mode 100644
index 000000000..774b89d28
--- /dev/null
+++ b/drivers/usb/host/whci/debug.c
@@ -0,0 +1,200 @@
+/*
+ * Wireless Host Controller (WHC) debug.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/export.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+struct whc_dbg {
+ struct dentry *di_f;
+ struct dentry *asl_f;
+ struct dentry *pzl_f;
+};
+
+static void qset_print(struct seq_file *s, struct whc_qset *qset)
+{
+ static const char *qh_type[] = {
+ "ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", };
+ struct whc_std *std;
+ struct urb *urb = NULL;
+ int i;
+
+ seq_printf(s, "qset %08x", (u32)qset->qset_dma);
+ if (&qset->list_node == qset->whc->async_list.prev) {
+ seq_printf(s, " (dummy)\n");
+ } else {
+ seq_printf(s, " ep%d%s-%s maxpkt: %d\n",
+ qset->qh.info1 & 0x0f,
+ (qset->qh.info1 >> 4) & 0x1 ? "in" : "out",
+ qh_type[(qset->qh.info1 >> 5) & 0x7],
+ (qset->qh.info1 >> 16) & 0xffff);
+ }
+ seq_printf(s, " -> %08x\n", (u32)qset->qh.link);
+ seq_printf(s, " info: %08x %08x %08x\n",
+ qset->qh.info1, qset->qh.info2, qset->qh.info3);
+ seq_printf(s, " sts: %04x errs: %d curwin: %08x\n",
+ qset->qh.status, qset->qh.err_count, qset->qh.cur_window);
+ seq_printf(s, " TD: sts: %08x opts: %08x\n",
+ qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
+
+ for (i = 0; i < WHCI_QSET_TD_MAX; i++) {
+ seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n",
+ i == qset->td_start ? 'S' : ' ',
+ i == qset->td_end ? 'E' : ' ',
+ i, qset->qtd[i].status, qset->qtd[i].options,
+ (u32)qset->qtd[i].page_list_ptr);
+ }
+ seq_printf(s, " ntds: %d\n", qset->ntds);
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (urb != std->urb) {
+ urb = std->urb;
+ seq_printf(s, " urb %p transferred: %d bytes\n", urb,
+ urb->actual_length);
+ }
+ if (std->qtd)
+ seq_printf(s, " sTD[%td]: %zu bytes @ %08x\n",
+ std->qtd - &qset->qtd[0],
+ std->len, std->num_pointers ?
+ (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+ else
+ seq_printf(s, " sTD[-]: %zd bytes @ %08x\n",
+ std->len, std->num_pointers ?
+ (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+ }
+}
+
+static int di_print(struct seq_file *s, void *p)
+{
+ struct whc *whc = s->private;
+ int d;
+
+ for (d = 0; d < whc->n_devices; d++) {
+ struct di_buf_entry *di = &whc->di_buf[d];
+
+ seq_printf(s, "DI[%d]\n", d);
+ seq_printf(s, " availability: %*pb\n",
+ UWB_NUM_MAS, (unsigned long *)di->availability_info);
+ seq_printf(s, " %c%c key idx: %d dev addr: %d\n",
+ (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ',
+ (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ',
+ (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8,
+ (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK));
+ }
+ return 0;
+}
+
+static int asl_print(struct seq_file *s, void *p)
+{
+ struct whc *whc = s->private;
+ struct whc_qset *qset;
+
+ list_for_each_entry(qset, &whc->async_list, list_node) {
+ qset_print(s, qset);
+ }
+
+ return 0;
+}
+
+static int pzl_print(struct seq_file *s, void *p)
+{
+ struct whc *whc = s->private;
+ struct whc_qset *qset;
+ int period;
+
+ for (period = 0; period < 5; period++) {
+ seq_printf(s, "Period %d\n", period);
+ list_for_each_entry(qset, &whc->periodic_list[period], list_node) {
+ qset_print(s, qset);
+ }
+ }
+ return 0;
+}
+
+static int di_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, di_print, inode->i_private);
+}
+
+static int asl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, asl_print, inode->i_private);
+}
+
+static int pzl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pzl_print, inode->i_private);
+}
+
+static const struct file_operations di_fops = {
+ .open = di_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations asl_fops = {
+ .open = asl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations pzl_fops = {
+ .open = pzl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+void whc_dbg_init(struct whc *whc)
+{
+ if (whc->wusbhc.pal.debugfs_dir == NULL)
+ return;
+
+ whc->dbg = kzalloc(sizeof(struct whc_dbg), GFP_KERNEL);
+ if (whc->dbg == NULL)
+ return;
+
+ whc->dbg->di_f = debugfs_create_file("di", 0444,
+ whc->wusbhc.pal.debugfs_dir, whc,
+ &di_fops);
+ whc->dbg->asl_f = debugfs_create_file("asl", 0444,
+ whc->wusbhc.pal.debugfs_dir, whc,
+ &asl_fops);
+ whc->dbg->pzl_f = debugfs_create_file("pzl", 0444,
+ whc->wusbhc.pal.debugfs_dir, whc,
+ &pzl_fops);
+}
+
+void whc_dbg_clean_up(struct whc *whc)
+{
+ if (whc->dbg) {
+ debugfs_remove(whc->dbg->pzl_f);
+ debugfs_remove(whc->dbg->asl_f);
+ debugfs_remove(whc->dbg->di_f);
+ kfree(whc->dbg);
+ }
+}
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
new file mode 100644
index 000000000..43626c446
--- /dev/null
+++ b/drivers/usb/host/whci/hcd.c
@@ -0,0 +1,366 @@
+/*
+ * Wireless Host Controller (WHC) driver.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * One time initialization.
+ *
+ * Nothing to do here.
+ */
+static int whc_reset(struct usb_hcd *usb_hcd)
+{
+ return 0;
+}
+
+/*
+ * Start the wireless host controller.
+ *
+ * Start device notification.
+ *
+ * Put hc into run state, set DNTS parameters.
+ */
+static int whc_start(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u8 bcid;
+ int ret;
+
+ mutex_lock(&wusbhc->mutex);
+
+ le_writel(WUSBINTR_GEN_CMD_DONE
+ | WUSBINTR_HOST_ERR
+ | WUSBINTR_ASYNC_SCHED_SYNCED
+ | WUSBINTR_DNTS_INT
+ | WUSBINTR_ERR_INT
+ | WUSBINTR_INT,
+ whc->base + WUSBINTR);
+
+ /* set cluster ID */
+ bcid = wusb_cluster_id_get();
+ ret = whc_set_cluster_id(whc, bcid);
+ if (ret < 0)
+ goto out;
+ wusbhc->cluster_id = bcid;
+
+ /* start HC */
+ whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
+
+ usb_hcd->uses_new_polling = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
+ usb_hcd->state = HC_STATE_RUNNING;
+
+out:
+ mutex_unlock(&wusbhc->mutex);
+ return ret;
+}
+
+
+/*
+ * Stop the wireless host controller.
+ *
+ * Stop device notification.
+ *
+ * Wait for pending transfer to stop? Put hc into stop state?
+ */
+static void whc_stop(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ mutex_lock(&wusbhc->mutex);
+
+ /* stop HC */
+ le_writel(0, whc->base + WUSBINTR);
+ whc_write_wusbcmd(whc, WUSBCMD_RUN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_HCHALTED, WUSBSTS_HCHALTED,
+ 100, "HC to halt");
+
+ wusb_cluster_id_put(wusbhc->cluster_id);
+
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static int whc_get_frame_number(struct usb_hcd *usb_hcd)
+{
+ /* Frame numbers are not applicable to WUSB. */
+ return -ENOSYS;
+}
+
+
+/*
+ * Queue an URB to the ASL or PZL
+ */
+static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ ret = pzl_urb_enqueue(whc, urb, mem_flags);
+ break;
+ case PIPE_ISOCHRONOUS:
+ dev_err(&whc->umc->dev, "isochronous transfers unsupported\n");
+ ret = -ENOTSUPP;
+ break;
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ ret = asl_urb_enqueue(whc, urb, mem_flags);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Remove a queued URB from the ASL or PZL.
+ */
+static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ ret = pzl_urb_dequeue(whc, urb, status);
+ break;
+ case PIPE_ISOCHRONOUS:
+ ret = -ENOTSUPP;
+ break;
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ ret = asl_urb_dequeue(whc, urb, status);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Wait for all URBs to the endpoint to be completed, then delete the
+ * qset.
+ */
+static void whc_endpoint_disable(struct usb_hcd *usb_hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ struct whc_qset *qset;
+
+ qset = ep->hcpriv;
+ if (qset) {
+ ep->hcpriv = NULL;
+ if (usb_endpoint_xfer_bulk(&ep->desc)
+ || usb_endpoint_xfer_control(&ep->desc))
+ asl_qset_delete(whc, qset);
+ else
+ pzl_qset_delete(whc, qset);
+ }
+}
+
+static void whc_endpoint_reset(struct usb_hcd *usb_hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ struct whc_qset *qset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ qset = ep->hcpriv;
+ if (qset) {
+ qset->remove = 1;
+ qset->reset = 1;
+
+ if (usb_endpoint_xfer_bulk(&ep->desc)
+ || usb_endpoint_xfer_control(&ep->desc))
+ queue_work(whc->workqueue, &whc->async_work);
+ else
+ queue_work(whc->workqueue, &whc->periodic_work);
+ }
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+
+static struct hc_driver whc_hc_driver = {
+ .description = "whci-hcd",
+ .product_desc = "Wireless host controller",
+ .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd),
+ .irq = whc_int_handler,
+ .flags = HCD_USB2,
+
+ .reset = whc_reset,
+ .start = whc_start,
+ .stop = whc_stop,
+ .get_frame_number = whc_get_frame_number,
+ .urb_enqueue = whc_urb_enqueue,
+ .urb_dequeue = whc_urb_dequeue,
+ .endpoint_disable = whc_endpoint_disable,
+ .endpoint_reset = whc_endpoint_reset,
+
+ .hub_status_data = wusbhc_rh_status_data,
+ .hub_control = wusbhc_rh_control,
+ .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int whc_probe(struct umc_dev *umc)
+{
+ int ret;
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc;
+ struct whc *whc;
+ struct device *dev = &umc->dev;
+
+ usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
+ if (usb_hcd == NULL) {
+ dev_err(dev, "unable to create hcd\n");
+ return -ENOMEM;
+ }
+
+ usb_hcd->wireless = 1;
+ usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */
+
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ whc = wusbhc_to_whc(wusbhc);
+ whc->umc = umc;
+
+ ret = whc_init(whc);
+ if (ret)
+ goto error;
+
+ wusbhc->dev = dev;
+ wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent);
+ if (!wusbhc->uwb_rc) {
+ ret = -ENODEV;
+ dev_err(dev, "cannot get radio controller\n");
+ goto error;
+ }
+
+ if (whc->n_devices > USB_MAXCHILDREN) {
+ dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n",
+ whc->n_devices);
+ wusbhc->ports_max = USB_MAXCHILDREN;
+ } else
+ wusbhc->ports_max = whc->n_devices;
+ wusbhc->mmcies_max = whc->n_mmc_ies;
+ wusbhc->start = whc_wusbhc_start;
+ wusbhc->stop = whc_wusbhc_stop;
+ wusbhc->mmcie_add = whc_mmcie_add;
+ wusbhc->mmcie_rm = whc_mmcie_rm;
+ wusbhc->dev_info_set = whc_dev_info_set;
+ wusbhc->bwa_set = whc_bwa_set;
+ wusbhc->set_num_dnts = whc_set_num_dnts;
+ wusbhc->set_ptk = whc_set_ptk;
+ wusbhc->set_gtk = whc_set_gtk;
+
+ ret = wusbhc_create(wusbhc);
+ if (ret)
+ goto error_wusbhc_create;
+
+ ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED);
+ if (ret) {
+ dev_err(dev, "cannot add HCD: %d\n", ret);
+ goto error_usb_add_hcd;
+ }
+ device_wakeup_enable(usb_hcd->self.controller);
+
+ ret = wusbhc_b_create(wusbhc);
+ if (ret) {
+ dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret);
+ goto error_wusbhc_b_create;
+ }
+
+ whc_dbg_init(whc);
+
+ return 0;
+
+error_wusbhc_b_create:
+ usb_remove_hcd(usb_hcd);
+error_usb_add_hcd:
+ wusbhc_destroy(wusbhc);
+error_wusbhc_create:
+ uwb_rc_put(wusbhc->uwb_rc);
+error:
+ whc_clean_up(whc);
+ usb_put_hcd(usb_hcd);
+ return ret;
+}
+
+
+static void whc_remove(struct umc_dev *umc)
+{
+ struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev);
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ if (usb_hcd) {
+ whc_dbg_clean_up(whc);
+ wusbhc_b_destroy(wusbhc);
+ usb_remove_hcd(usb_hcd);
+ wusbhc_destroy(wusbhc);
+ uwb_rc_put(wusbhc->uwb_rc);
+ whc_clean_up(whc);
+ usb_put_hcd(usb_hcd);
+ }
+}
+
+static struct umc_driver whci_hc_driver = {
+ .name = "whci-hcd",
+ .cap_id = UMC_CAP_ID_WHCI_WUSB_HC,
+ .probe = whc_probe,
+ .remove = whc_remove,
+};
+
+static int __init whci_hc_driver_init(void)
+{
+ return umc_driver_register(&whci_hc_driver);
+}
+module_init(whci_hc_driver_init);
+
+static void __exit whci_hc_driver_exit(void)
+{
+ umc_driver_unregister(&whci_hc_driver);
+}
+module_exit(whci_hc_driver_exit);
+
+/* PCI device ID's that we handle (so it gets loaded) */
+static struct pci_device_id __used whci_hcd_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { /* empty last entry */ }
+};
+MODULE_DEVICE_TABLE(pci, whci_hcd_id_table);
+
+MODULE_DESCRIPTION("WHCI Wireless USB host controller driver");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c
new file mode 100644
index 000000000..6afa2e379
--- /dev/null
+++ b/drivers/usb/host/whci/hw.c
@@ -0,0 +1,104 @@
+/*
+ * Wireless Host Controller (WHC) hardware access helpers.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val)
+{
+ unsigned long flags;
+ u32 cmd;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ cmd = le_readl(whc->base + WUSBCMD);
+ cmd = (cmd & ~mask) | val;
+ le_writel(cmd, whc->base + WUSBCMD);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+/**
+ * whc_do_gencmd - start a generic command via the WUSBGENCMDSTS register
+ * @whc: the WHCI HC
+ * @cmd: command to start.
+ * @params: parameters for the command (the WUSBGENCMDPARAMS register value).
+ * @addr: pointer to any data for the command (may be NULL).
+ * @len: length of the data (if any).
+ */
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len)
+{
+ unsigned long flags;
+ dma_addr_t dma_addr;
+ int t;
+ int ret = 0;
+
+ mutex_lock(&whc->mutex);
+
+ /* Wait for previous command to complete. */
+ t = wait_event_timeout(whc->cmd_wq,
+ (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0,
+ WHC_GENCMD_TIMEOUT_MS);
+ if (t == 0) {
+ dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n",
+ le_readl(whc->base + WUSBGENCMDSTS),
+ le_readl(whc->base + WUSBGENCMDPARAMS));
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (addr) {
+ memcpy(whc->gen_cmd_buf, addr, len);
+ dma_addr = whc->gen_cmd_buf_dma;
+ } else
+ dma_addr = 0;
+
+ /* Poke registers to start cmd. */
+ spin_lock_irqsave(&whc->lock, flags);
+
+ le_writel(params, whc->base + WUSBGENCMDPARAMS);
+ le_writeq(dma_addr, whc->base + WUSBGENADDR);
+
+ le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd,
+ whc->base + WUSBGENCMDSTS);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+out:
+ mutex_unlock(&whc->mutex);
+
+ return ret;
+}
+
+/**
+ * whc_hw_error - recover from a hardware error
+ * @whc: the WHCI HC that broke.
+ * @reason: a description of the failure.
+ *
+ * Recover from broken hardware with a full reset.
+ */
+void whc_hw_error(struct whc *whc, const char *reason)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+
+ dev_err(&whc->umc->dev, "hardware error: %s\n", reason);
+ wusbhc_reset_all(wusbhc);
+}
diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c
new file mode 100644
index 000000000..d3e13b640
--- /dev/null
+++ b/drivers/usb/host/whci/init.c
@@ -0,0 +1,189 @@
+/*
+ * Wireless Host Controller (WHC) initialization.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * Reset the host controller.
+ */
+static void whc_hw_reset(struct whc *whc)
+{
+ le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
+ 100, "reset");
+}
+
+static void whc_hw_init_di_buf(struct whc *whc)
+{
+ int d;
+
+ /* Disable all entries in the Device Information buffer. */
+ for (d = 0; d < whc->n_devices; d++)
+ whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
+
+ le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
+}
+
+static void whc_hw_init_dn_buf(struct whc *whc)
+{
+ /* Clear the Device Notification buffer to ensure the V (valid)
+ * bits are clear. */
+ memset(whc->dn_buf, 0, 4096);
+
+ le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
+}
+
+int whc_init(struct whc *whc)
+{
+ u32 whcsparams;
+ int ret, i;
+ resource_size_t start, len;
+
+ spin_lock_init(&whc->lock);
+ mutex_init(&whc->mutex);
+ init_waitqueue_head(&whc->cmd_wq);
+ init_waitqueue_head(&whc->async_list_wq);
+ init_waitqueue_head(&whc->periodic_list_wq);
+ whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
+ if (whc->workqueue == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ INIT_WORK(&whc->dn_work, whc_dn_work);
+
+ INIT_WORK(&whc->async_work, scan_async_work);
+ INIT_LIST_HEAD(&whc->async_list);
+ INIT_LIST_HEAD(&whc->async_removed_list);
+
+ INIT_WORK(&whc->periodic_work, scan_periodic_work);
+ for (i = 0; i < 5; i++)
+ INIT_LIST_HEAD(&whc->periodic_list[i]);
+ INIT_LIST_HEAD(&whc->periodic_removed_list);
+
+ /* Map HC registers. */
+ start = whc->umc->resource.start;
+ len = whc->umc->resource.end - start + 1;
+ if (!request_mem_region(start, len, "whci-hc")) {
+ dev_err(&whc->umc->dev, "can't request HC region\n");
+ ret = -EBUSY;
+ goto error;
+ }
+ whc->base_phys = start;
+ whc->base = ioremap(start, len);
+ if (!whc->base) {
+ dev_err(&whc->umc->dev, "ioremap\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ whc_hw_reset(whc);
+
+ /* Read maximum number of devices, keys and MMC IEs. */
+ whcsparams = le_readl(whc->base + WHCSPARAMS);
+ whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
+ whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
+ whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
+
+ dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
+ whc->n_devices, whc->n_keys, whc->n_mmc_ies);
+
+ whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
+ sizeof(struct whc_qset), 64, 0);
+ if (whc->qset_pool == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = asl_init(whc);
+ if (ret < 0)
+ goto error;
+ ret = pzl_init(whc);
+ if (ret < 0)
+ goto error;
+
+ /* Allocate and initialize a buffer for generic commands, the
+ Device Information buffer, and the Device Notification
+ buffer. */
+
+ whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
+ &whc->gen_cmd_buf_dma, GFP_KERNEL);
+ if (whc->gen_cmd_buf == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
+ sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+ &whc->dn_buf_dma, GFP_KERNEL);
+ if (!whc->dn_buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ whc_hw_init_dn_buf(whc);
+
+ whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
+ sizeof(struct di_buf_entry) * whc->n_devices,
+ &whc->di_buf_dma, GFP_KERNEL);
+ if (!whc->di_buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ whc_hw_init_di_buf(whc);
+
+ return 0;
+
+error:
+ whc_clean_up(whc);
+ return ret;
+}
+
+void whc_clean_up(struct whc *whc)
+{
+ resource_size_t len;
+
+ if (whc->di_buf)
+ dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
+ whc->di_buf, whc->di_buf_dma);
+ if (whc->dn_buf)
+ dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+ whc->dn_buf, whc->dn_buf_dma);
+ if (whc->gen_cmd_buf)
+ dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
+ whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
+
+ pzl_clean_up(whc);
+ asl_clean_up(whc);
+
+ if (whc->qset_pool)
+ dma_pool_destroy(whc->qset_pool);
+
+ len = resource_size(&whc->umc->resource);
+ if (whc->base)
+ iounmap(whc->base);
+ if (whc->base_phys)
+ release_mem_region(whc->base_phys, len);
+
+ if (whc->workqueue)
+ destroy_workqueue(whc->workqueue);
+}
diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c
new file mode 100644
index 000000000..0c086b279
--- /dev/null
+++ b/drivers/usb/host/whci/int.c
@@ -0,0 +1,93 @@
+/*
+ * Wireless Host Controller (WHC) interrupt handling.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static void transfer_done(struct whc *whc)
+{
+ queue_work(whc->workqueue, &whc->async_work);
+ queue_work(whc->workqueue, &whc->periodic_work);
+}
+
+irqreturn_t whc_int_handler(struct usb_hcd *hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 sts;
+
+ sts = le_readl(whc->base + WUSBSTS);
+ if (!(sts & WUSBSTS_INT_MASK))
+ return IRQ_NONE;
+ le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS);
+
+ if (sts & WUSBSTS_GEN_CMD_DONE)
+ wake_up(&whc->cmd_wq);
+
+ if (sts & WUSBSTS_HOST_ERR)
+ dev_err(&whc->umc->dev, "FIXME: host system error\n");
+
+ if (sts & WUSBSTS_ASYNC_SCHED_SYNCED)
+ wake_up(&whc->async_list_wq);
+
+ if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED)
+ wake_up(&whc->periodic_list_wq);
+
+ if (sts & WUSBSTS_DNTS_INT)
+ queue_work(whc->workqueue, &whc->dn_work);
+
+ /*
+ * A transfer completed (see [WHCI] section 4.7.1.2 for when
+ * this occurs).
+ */
+ if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT))
+ transfer_done(whc);
+
+ return IRQ_HANDLED;
+}
+
+static int process_dn_buf(struct whc *whc)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+ struct dn_buf_entry *dn;
+ int processed = 0;
+
+ for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) {
+ if (dn->status & WHC_DN_STATUS_VALID) {
+ wusbhc_handle_dn(wusbhc, dn->src_addr,
+ (struct wusb_dn_hdr *)dn->dn_data,
+ dn->msg_size);
+ dn->status &= ~WHC_DN_STATUS_VALID;
+ processed++;
+ }
+ }
+ return processed;
+}
+
+void whc_dn_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, dn_work);
+ int processed;
+
+ do {
+ processed = process_dn_buf(whc);
+ } while (processed);
+}
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
new file mode 100644
index 000000000..33c5580b4
--- /dev/null
+++ b/drivers/usb/host/whci/pzl.c
@@ -0,0 +1,417 @@
+/*
+ * Wireless Host Controller (WHC) periodic schedule management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static void update_pzl_pointers(struct whc *whc, int period, u64 addr)
+{
+ switch (period) {
+ case 0:
+ whc_qset_set_link_ptr(&whc->pz_list[0], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[2], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[4], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[6], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[8], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[10], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[12], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[14], addr);
+ break;
+ case 1:
+ whc_qset_set_link_ptr(&whc->pz_list[1], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[5], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[9], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[13], addr);
+ break;
+ case 2:
+ whc_qset_set_link_ptr(&whc->pz_list[3], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[11], addr);
+ break;
+ case 3:
+ whc_qset_set_link_ptr(&whc->pz_list[7], addr);
+ break;
+ case 4:
+ whc_qset_set_link_ptr(&whc->pz_list[15], addr);
+ break;
+ }
+}
+
+/*
+ * Return the 'period' to use for this qset. The minimum interval for
+ * the endpoint is used so whatever urbs are submitted the device is
+ * polled often enough.
+ */
+static int qset_get_period(struct whc *whc, struct whc_qset *qset)
+{
+ uint8_t bInterval = qset->ep->desc.bInterval;
+
+ if (bInterval < 6)
+ bInterval = 6;
+ if (bInterval > 10)
+ bInterval = 10;
+ return bInterval - 6;
+}
+
+static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset)
+{
+ int period;
+
+ period = qset_get_period(whc, qset);
+
+ qset_clear(whc, qset);
+ list_move(&qset->list_node, &whc->periodic_list[period]);
+ qset->in_sw_list = true;
+}
+
+static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+ list_move(&qset->list_node, &whc->periodic_removed_list);
+ qset->in_hw_list = false;
+ qset->in_sw_list = false;
+}
+
+/**
+ * pzl_process_qset - process any recently inactivated or halted qTDs
+ * in a qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns the schedule updates required.
+ */
+static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset)
+{
+ enum whc_update update = 0;
+ uint32_t status = 0;
+
+ while (qset->ntds) {
+ struct whc_qtd *td;
+ int t;
+
+ t = qset->td_start;
+ td = &qset->qtd[qset->td_start];
+ status = le32_to_cpu(td->status);
+
+ /*
+ * Nothing to do with a still active qTD.
+ */
+ if (status & QTD_STS_ACTIVE)
+ break;
+
+ if (status & QTD_STS_HALTED) {
+ /* Ug, an error. */
+ process_halted_qtd(whc, qset, td);
+ /* A halted qTD always triggers an update
+ because the qset was either removed or
+ reactivated. */
+ update |= WHC_UPDATE_UPDATED;
+ goto done;
+ }
+
+ /* Mmm, a completed qTD. */
+ process_inactive_qtd(whc, qset, td);
+ }
+
+ if (!qset->remove)
+ update |= qset_add_qtds(whc, qset);
+
+done:
+ /*
+ * If there are no qTDs in this qset, remove it from the PZL.
+ */
+ if (qset->remove && qset->ntds == 0) {
+ pzl_qset_remove(whc, qset);
+ update |= WHC_UPDATE_REMOVED;
+ }
+
+ return update;
+}
+
+/**
+ * pzl_start - start the periodic schedule
+ * @whc: the WHCI host controller
+ *
+ * The PZL must be valid (e.g., all entries in the list should have
+ * the T bit set).
+ */
+void pzl_start(struct whc *whc)
+{
+ le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+ whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED,
+ 1000, "start PZL");
+}
+
+/**
+ * pzl_stop - stop the periodic schedule
+ * @whc: the WHCI host controller
+ */
+void pzl_stop(struct whc *whc)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_PERIODIC_SCHED, 0,
+ 1000, "stop PZL");
+}
+
+/**
+ * pzl_update - request a PZL update and wait for the hardware to be synced
+ * @whc: the WHCI HC
+ * @wusbcmd: WUSBCMD value to start the update.
+ *
+ * If the WUSB HC is inactive (i.e., the PZL is stopped) then the
+ * update must be skipped as the hardware may not respond to update
+ * requests.
+ */
+void pzl_update(struct whc *whc, uint32_t wusbcmd)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+ long t;
+
+ mutex_lock(&wusbhc->mutex);
+ if (wusbhc->active) {
+ whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+ t = wait_event_timeout(
+ whc->periodic_list_wq,
+ (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0,
+ msecs_to_jiffies(1000));
+ if (t == 0)
+ whc_hw_error(whc, "PZL update timeout");
+ }
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static void update_pzl_hw_view(struct whc *whc)
+{
+ struct whc_qset *qset, *t;
+ int period;
+ u64 tmp_qh = 0;
+
+ for (period = 0; period < 5; period++) {
+ list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+ whc_qset_set_link_ptr(&qset->qh.link, tmp_qh);
+ tmp_qh = qset->qset_dma;
+ qset->in_hw_list = true;
+ }
+ update_pzl_pointers(whc, period, tmp_qh);
+ }
+}
+
+/**
+ * scan_periodic_work - scan the PZL for qsets to process.
+ *
+ * Process each qset in the PZL in turn and then signal the WHC that
+ * the PZL has been updated.
+ *
+ * Then start, stop or update the periodic schedule as required.
+ */
+void scan_periodic_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, periodic_work);
+ struct whc_qset *qset, *t;
+ enum whc_update update = 0;
+ int period;
+
+ spin_lock_irq(&whc->lock);
+
+ for (period = 4; period >= 0; period--) {
+ list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+ if (!qset->in_hw_list)
+ update |= WHC_UPDATE_ADDED;
+ update |= pzl_process_qset(whc, qset);
+ }
+ }
+
+ if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED))
+ update_pzl_hw_view(whc);
+
+ spin_unlock_irq(&whc->lock);
+
+ if (update) {
+ uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB;
+ if (update & WHC_UPDATE_REMOVED)
+ wusbcmd |= WUSBCMD_PERIODIC_QSET_RM;
+ pzl_update(whc, wusbcmd);
+ }
+
+ /*
+ * Now that the PZL is updated, complete the removal of any
+ * removed qsets.
+ *
+ * If the qset was to be reset, do so and reinsert it into the
+ * PZL if it has pending transfers.
+ */
+ spin_lock_irq(&whc->lock);
+
+ list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
+ qset_remove_complete(whc, qset);
+ if (qset->reset) {
+ qset_reset(whc, qset);
+ if (!list_empty(&qset->stds)) {
+ qset_insert_in_sw_list(whc, qset);
+ queue_work(whc->workqueue, &whc->periodic_work);
+ }
+ }
+ }
+
+ spin_unlock_irq(&whc->lock);
+}
+
+/**
+ * pzl_urb_enqueue - queue an URB onto the periodic list (PZL)
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the PZL.
+ */
+int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (err < 0) {
+ spin_unlock_irqrestore(&whc->lock, flags);
+ return err;
+ }
+
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+ if (!qset->in_sw_list && !qset->remove)
+ qset_insert_in_sw_list(whc, qset);
+ } else
+ usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->periodic_work);
+
+ return err;
+}
+
+/**
+ * pzl_urb_dequeue - remove an URB (qset) from the periodic list
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed so the qTDs can be safely
+ * removed.
+ */
+int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+ struct whc_urb *wurb = urb->hcpriv;
+ struct whc_qset *qset = wurb->qset;
+ struct whc_std *std, *t;
+ bool has_qtd = false;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+ if (ret < 0)
+ goto out;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb) {
+ if (std->qtd)
+ has_qtd = true;
+ qset_free_std(whc, std);
+ } else
+ std->qtd = NULL; /* so this std is re-added when the qset is */
+ }
+
+ if (has_qtd) {
+ pzl_qset_remove(whc, qset);
+ update_pzl_hw_view(whc);
+ wurb->status = status;
+ wurb->is_async = false;
+ queue_work(whc->workqueue, &wurb->dequeue_work);
+ } else
+ qset_remove_urb(whc, qset, urb, status);
+out:
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * pzl_qset_delete - delete a qset from the PZL
+ */
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ qset->remove = 1;
+ queue_work(whc->workqueue, &whc->periodic_work);
+ qset_delete(whc, qset);
+}
+
+/**
+ * pzl_init - initialize the periodic zone list
+ * @whc: the WHCI host controller
+ */
+int pzl_init(struct whc *whc)
+{
+ int i;
+
+ whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16,
+ &whc->pz_list_dma, GFP_KERNEL);
+ if (whc->pz_list == NULL)
+ return -ENOMEM;
+
+ /* Set T bit on all elements in PZL. */
+ for (i = 0; i < 16; i++)
+ whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T);
+
+ le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+ return 0;
+}
+
+/**
+ * pzl_clean_up - free PZL resources
+ * @whc: the WHCI host controller
+ *
+ * The PZL is stopped and empty.
+ */
+void pzl_clean_up(struct whc *whc)
+{
+ if (whc->pz_list)
+ dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list,
+ whc->pz_list_dma);
+}
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
new file mode 100644
index 000000000..dc31c425c
--- /dev/null
+++ b/drivers/usb/host/whci/qset.c
@@ -0,0 +1,835 @@
+/*
+ * Wireless Host Controller (WHC) qset management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ dma_addr_t dma;
+
+ qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma);
+ if (qset == NULL)
+ return NULL;
+ memset(qset, 0, sizeof(struct whc_qset));
+
+ qset->qset_dma = dma;
+ qset->whc = whc;
+
+ INIT_LIST_HEAD(&qset->list_node);
+ INIT_LIST_HEAD(&qset->stds);
+
+ return qset;
+}
+
+/**
+ * qset_fill_qh - fill the static endpoint state in a qset's QHead
+ * @qset: the qset whose QH needs initializing with static endpoint
+ * state
+ * @urb: an urb for a transfer to this endpoint
+ */
+static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb)
+{
+ struct usb_device *usb_dev = urb->dev;
+ struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
+ struct usb_wireless_ep_comp_descriptor *epcd;
+ bool is_out;
+ uint8_t phy_rate;
+
+ is_out = usb_pipeout(urb->pipe);
+
+ qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize);
+
+ epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
+ if (epcd) {
+ qset->max_seq = epcd->bMaxSequence;
+ qset->max_burst = epcd->bMaxBurst;
+ } else {
+ qset->max_seq = 2;
+ qset->max_burst = 1;
+ }
+
+ /*
+ * Initial PHY rate is 53.3 Mbit/s for control endpoints or
+ * the maximum supported by the device for other endpoints
+ * (unless limited by the user).
+ */
+ if (usb_pipecontrol(urb->pipe))
+ phy_rate = UWB_PHY_RATE_53;
+ else {
+ uint16_t phy_rates;
+
+ phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates);
+ phy_rate = fls(phy_rates) - 1;
+ if (phy_rate > whc->wusbhc.phy_rate)
+ phy_rate = whc->wusbhc.phy_rate;
+ }
+
+ qset->qh.info1 = cpu_to_le32(
+ QH_INFO1_EP(usb_pipeendpoint(urb->pipe))
+ | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN)
+ | usb_pipe_to_qh_type(urb->pipe)
+ | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum))
+ | QH_INFO1_MAX_PKT_LEN(qset->max_packet)
+ );
+ qset->qh.info2 = cpu_to_le32(
+ QH_INFO2_BURST(qset->max_burst)
+ | QH_INFO2_DBP(0)
+ | QH_INFO2_MAX_COUNT(3)
+ | QH_INFO2_MAX_RETRY(3)
+ | QH_INFO2_MAX_SEQ(qset->max_seq - 1)
+ );
+ /* FIXME: where can we obtain these Tx parameters from? Why
+ * doesn't the chip know what Tx power to use? It knows the Rx
+ * strength and can presumably guess the Tx power required
+ * from that? */
+ qset->qh.info3 = cpu_to_le32(
+ QH_INFO3_TX_RATE(phy_rate)
+ | QH_INFO3_TX_PWR(0) /* 0 == max power */
+ );
+
+ qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+}
+
+/**
+ * qset_clear - clear fields in a qset so it may be reinserted into a
+ * schedule.
+ *
+ * The sequence number and current window are not cleared (see
+ * qset_reset()).
+ */
+void qset_clear(struct whc *whc, struct whc_qset *qset)
+{
+ qset->td_start = qset->td_end = qset->ntds = 0;
+
+ qset->qh.link = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T);
+ qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK;
+ qset->qh.err_count = 0;
+ qset->qh.scratch[0] = 0;
+ qset->qh.scratch[1] = 0;
+ qset->qh.scratch[2] = 0;
+
+ memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay));
+
+ init_completion(&qset->remove_complete);
+}
+
+/**
+ * qset_reset - reset endpoint state in a qset.
+ *
+ * Clears the sequence number and current window. This qset must not
+ * be in the ASL or PZL.
+ */
+void qset_reset(struct whc *whc, struct whc_qset *qset)
+{
+ qset->reset = 0;
+
+ qset->qh.status &= ~QH_STATUS_SEQ_MASK;
+ qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+}
+
+/**
+ * get_qset - get the qset for an async endpoint
+ *
+ * A new qset is created if one does not already exist.
+ */
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+
+ qset = urb->ep->hcpriv;
+ if (qset == NULL) {
+ qset = qset_alloc(whc, mem_flags);
+ if (qset == NULL)
+ return NULL;
+
+ qset->ep = urb->ep;
+ urb->ep->hcpriv = qset;
+ qset_fill_qh(whc, qset, urb);
+ }
+ return qset;
+}
+
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
+{
+ qset->remove = 0;
+ list_del_init(&qset->list_node);
+ complete(&qset->remove_complete);
+}
+
+/**
+ * qset_add_qtds - add qTDs for an URB to a qset
+ *
+ * Returns true if the list (ASL/PZL) must be updated because (for a
+ * WHCI 0.95 controller) an activated qTD was pointed to be iCur.
+ */
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_std *std;
+ enum whc_update update = 0;
+
+ list_for_each_entry(std, &qset->stds, list_node) {
+ struct whc_qtd *qtd;
+ uint32_t status;
+
+ if (qset->ntds >= WHCI_QSET_TD_MAX
+ || (qset->pause_after_urb && std->urb != qset->pause_after_urb))
+ break;
+
+ if (std->qtd)
+ continue; /* already has a qTD */
+
+ qtd = std->qtd = &qset->qtd[qset->td_end];
+
+ /* Fill in setup bytes for control transfers. */
+ if (usb_pipecontrol(std->urb->pipe))
+ memcpy(qtd->setup, std->urb->setup_packet, 8);
+
+ status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len);
+
+ if (whc_std_last(std) && usb_pipeout(std->urb->pipe))
+ status |= QTD_STS_LAST_PKT;
+
+ /*
+ * For an IN transfer the iAlt field should be set so
+ * the h/w will automatically advance to the next
+ * transfer. However, if there are 8 or more TDs
+ * remaining in this transfer then iAlt cannot be set
+ * as it could point to somewhere in this transfer.
+ */
+ if (std->ntds_remaining < WHCI_QSET_TD_MAX) {
+ int ialt;
+ ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX;
+ status |= QTD_STS_IALT(ialt);
+ } else if (usb_pipein(std->urb->pipe))
+ qset->pause_after_urb = std->urb;
+
+ if (std->num_pointers)
+ qtd->options = cpu_to_le32(QTD_OPT_IOC);
+ else
+ qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL);
+ qtd->page_list_ptr = cpu_to_le64(std->dma_addr);
+
+ qtd->status = cpu_to_le32(status);
+
+ if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end)
+ update = WHC_UPDATE_UPDATED;
+
+ if (++qset->td_end >= WHCI_QSET_TD_MAX)
+ qset->td_end = 0;
+ qset->ntds++;
+ }
+
+ return update;
+}
+
+/**
+ * qset_remove_qtd - remove the first qTD from a qset.
+ *
+ * The qTD might be still active (if it's part of a IN URB that
+ * resulted in a short read) so ensure it's deactivated.
+ */
+static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
+{
+ qset->qtd[qset->td_start].status = 0;
+
+ if (++qset->td_start >= WHCI_QSET_TD_MAX)
+ qset->td_start = 0;
+ qset->ntds--;
+}
+
+static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std)
+{
+ struct scatterlist *sg;
+ void *bounce;
+ size_t remaining, offset;
+
+ bounce = std->bounce_buf;
+ remaining = std->len;
+
+ sg = std->bounce_sg;
+ offset = std->bounce_offset;
+
+ while (remaining) {
+ size_t len;
+
+ len = min(sg->length - offset, remaining);
+ memcpy(sg_virt(sg) + offset, bounce, len);
+
+ bounce += len;
+ remaining -= len;
+
+ offset += len;
+ if (offset >= sg->length) {
+ sg = sg_next(sg);
+ offset = 0;
+ }
+ }
+
+}
+
+/**
+ * qset_free_std - remove an sTD and free it.
+ * @whc: the WHCI host controller
+ * @std: the sTD to remove and free.
+ */
+void qset_free_std(struct whc *whc, struct whc_std *std)
+{
+ list_del(&std->list_node);
+ if (std->bounce_buf) {
+ bool is_out = usb_pipeout(std->urb->pipe);
+ dma_addr_t dma_addr;
+
+ if (std->num_pointers)
+ dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr);
+ else
+ dma_addr = std->dma_addr;
+
+ dma_unmap_single(whc->wusbhc.dev, dma_addr,
+ std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (!is_out)
+ qset_copy_bounce_to_sg(whc, std);
+ kfree(std->bounce_buf);
+ }
+ if (std->pl_virt) {
+ if (std->dma_addr)
+ dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
+ std->num_pointers * sizeof(struct whc_page_list_entry),
+ DMA_TO_DEVICE);
+ kfree(std->pl_virt);
+ std->pl_virt = NULL;
+ }
+ kfree(std);
+}
+
+/**
+ * qset_remove_qtds - remove an URB's qTDs (and sTDs).
+ */
+static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb)
+{
+ struct whc_std *std, *t;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb != urb)
+ break;
+ if (std->qtd != NULL)
+ qset_remove_qtd(whc, qset);
+ qset_free_std(whc, std);
+ }
+}
+
+/**
+ * qset_free_stds - free any remaining sTDs for an URB.
+ */
+static void qset_free_stds(struct whc_qset *qset, struct urb *urb)
+{
+ struct whc_std *std, *t;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb)
+ qset_free_std(qset->whc, std);
+ }
+}
+
+static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags)
+{
+ dma_addr_t dma_addr = std->dma_addr;
+ dma_addr_t sp, ep;
+ size_t pl_len;
+ int p;
+
+ /* Short buffers don't need a page list. */
+ if (std->len <= WHCI_PAGE_SIZE) {
+ std->num_pointers = 0;
+ return 0;
+ }
+
+ sp = dma_addr & ~(WHCI_PAGE_SIZE-1);
+ ep = dma_addr + std->len;
+ std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
+
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+ std->pl_virt = kmalloc(pl_len, mem_flags);
+ if (std->pl_virt == NULL)
+ return -ENOMEM;
+ std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE);
+
+ for (p = 0; p < std->num_pointers; p++) {
+ std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
+ dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
+ }
+
+ return 0;
+}
+
+/**
+ * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system.
+ */
+static void urb_dequeue_work(struct work_struct *work)
+{
+ struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work);
+ struct whc_qset *qset = wurb->qset;
+ struct whc *whc = qset->whc;
+ unsigned long flags;
+
+ if (wurb->is_async == true)
+ asl_update(whc, WUSBCMD_ASYNC_UPDATED
+ | WUSBCMD_ASYNC_SYNCED_DB
+ | WUSBCMD_ASYNC_QSET_RM);
+ else
+ pzl_update(whc, WUSBCMD_PERIODIC_UPDATED
+ | WUSBCMD_PERIODIC_SYNCED_DB
+ | WUSBCMD_PERIODIC_QSET_RM);
+
+ spin_lock_irqsave(&whc->lock, flags);
+ qset_remove_urb(whc, qset, wurb->urb, wurb->status);
+ spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_std *std;
+
+ std = kzalloc(sizeof(struct whc_std), mem_flags);
+ if (std == NULL)
+ return NULL;
+
+ std->urb = urb;
+ std->qtd = NULL;
+
+ INIT_LIST_HEAD(&std->list_node);
+ list_add_tail(&std->list_node, &qset->stds);
+
+ return std;
+}
+
+static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags)
+{
+ size_t remaining;
+ struct scatterlist *sg;
+ int i;
+ int ntds = 0;
+ struct whc_std *std = NULL;
+ struct whc_page_list_entry *new_pl_virt;
+ dma_addr_t prev_end = 0;
+ size_t pl_len;
+ int p = 0;
+
+ remaining = urb->transfer_buffer_length;
+
+ for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
+ dma_addr_t dma_addr;
+ size_t dma_remaining;
+ dma_addr_t sp, ep;
+ int num_pointers;
+
+ if (remaining == 0) {
+ break;
+ }
+
+ dma_addr = sg_dma_address(sg);
+ dma_remaining = min_t(size_t, sg_dma_len(sg), remaining);
+
+ while (dma_remaining) {
+ size_t dma_len;
+
+ /*
+ * We can use the previous std (if it exists) provided that:
+ * - the previous one ended on a page boundary.
+ * - the current one begins on a page boundary.
+ * - the previous one isn't full.
+ *
+ * If a new std is needed but the previous one
+ * was not a whole number of packets then this
+ * sg list cannot be mapped onto multiple
+ * qTDs. Return an error and let the caller
+ * sort it out.
+ */
+ if (!std
+ || (prev_end & (WHCI_PAGE_SIZE-1))
+ || (dma_addr & (WHCI_PAGE_SIZE-1))
+ || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
+ if (std && std->len % qset->max_packet != 0)
+ return -EINVAL;
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL) {
+ return -ENOMEM;
+ }
+ ntds++;
+ p = 0;
+ }
+
+ dma_len = dma_remaining;
+
+ /*
+ * If the remainder of this element doesn't
+ * fit in a single qTD, limit the qTD to a
+ * whole number of packets. This allows the
+ * remainder to go into the next qTD.
+ */
+ if (std->len + dma_len > QTD_MAX_XFER_SIZE) {
+ dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet)
+ * qset->max_packet - std->len;
+ }
+
+ std->len += dma_len;
+ std->ntds_remaining = -1; /* filled in later */
+
+ sp = dma_addr & ~(WHCI_PAGE_SIZE-1);
+ ep = dma_addr + dma_len;
+ num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
+ std->num_pointers += num_pointers;
+
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+
+ new_pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
+ if (new_pl_virt == NULL) {
+ kfree(std->pl_virt);
+ std->pl_virt = NULL;
+ return -ENOMEM;
+ }
+ std->pl_virt = new_pl_virt;
+
+ for (;p < std->num_pointers; p++) {
+ std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
+ dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
+ }
+
+ prev_end = dma_addr = ep;
+ dma_remaining -= dma_len;
+ remaining -= dma_len;
+ }
+ }
+
+ /* Now the number of stds is know, go back and fill in
+ std->ntds_remaining. */
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (std->ntds_remaining == -1) {
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+ std->ntds_remaining = ntds--;
+ std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt,
+ pl_len, DMA_TO_DEVICE);
+ }
+ }
+ return 0;
+}
+
+/**
+ * qset_add_urb_sg_linearize - add an urb with sg list, copying the data
+ *
+ * If the URB contains an sg list whose elements cannot be directly
+ * mapped to qTDs then the data must be transferred via bounce
+ * buffers.
+ */
+static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, gfp_t mem_flags)
+{
+ bool is_out = usb_pipeout(urb->pipe);
+ size_t max_std_len;
+ size_t remaining;
+ int ntds = 0;
+ struct whc_std *std = NULL;
+ void *bounce = NULL;
+ struct scatterlist *sg;
+ int i;
+
+ /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */
+ max_std_len = qset->max_burst * qset->max_packet;
+
+ remaining = urb->transfer_buffer_length;
+
+ for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
+ size_t len;
+ size_t sg_remaining;
+ void *orig;
+
+ if (remaining == 0) {
+ break;
+ }
+
+ sg_remaining = min_t(size_t, remaining, sg->length);
+ orig = sg_virt(sg);
+
+ while (sg_remaining) {
+ if (!std || std->len == max_std_len) {
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL)
+ return -ENOMEM;
+ std->bounce_buf = kmalloc(max_std_len, mem_flags);
+ if (std->bounce_buf == NULL)
+ return -ENOMEM;
+ std->bounce_sg = sg;
+ std->bounce_offset = orig - sg_virt(sg);
+ bounce = std->bounce_buf;
+ ntds++;
+ }
+
+ len = min(sg_remaining, max_std_len - std->len);
+
+ if (is_out)
+ memcpy(bounce, orig, len);
+
+ std->len += len;
+ std->ntds_remaining = -1; /* filled in later */
+
+ bounce += len;
+ orig += len;
+ sg_remaining -= len;
+ remaining -= len;
+ }
+ }
+
+ /*
+ * For each of the new sTDs, map the bounce buffers, create
+ * page lists (if necessary), and fill in std->ntds_remaining.
+ */
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (std->ntds_remaining != -1)
+ continue;
+
+ std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len,
+ is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (qset_fill_page_list(whc, std, mem_flags) < 0)
+ return -ENOMEM;
+
+ std->ntds_remaining = ntds--;
+ }
+
+ return 0;
+}
+
+/**
+ * qset_add_urb - add an urb to the qset's queue.
+ *
+ * The URB is chopped into sTDs, one for each qTD that will required.
+ * At least one qTD (and sTD) is required even if the transfer has no
+ * data (e.g., for some control transfers).
+ */
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct whc_urb *wurb;
+ int remaining = urb->transfer_buffer_length;
+ u64 transfer_dma = urb->transfer_dma;
+ int ntds_remaining;
+ int ret;
+
+ wurb = kzalloc(sizeof(struct whc_urb), mem_flags);
+ if (wurb == NULL)
+ goto err_no_mem;
+ urb->hcpriv = wurb;
+ wurb->qset = qset;
+ wurb->urb = urb;
+ INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
+
+ if (urb->num_sgs) {
+ ret = qset_add_urb_sg(whc, qset, urb, mem_flags);
+ if (ret == -EINVAL) {
+ qset_free_stds(qset, urb);
+ ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags);
+ }
+ if (ret < 0)
+ goto err_no_mem;
+ return 0;
+ }
+
+ ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
+ if (ntds_remaining == 0)
+ ntds_remaining = 1;
+
+ while (ntds_remaining) {
+ struct whc_std *std;
+ size_t std_len;
+
+ std_len = remaining;
+ if (std_len > QTD_MAX_XFER_SIZE)
+ std_len = QTD_MAX_XFER_SIZE;
+
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL)
+ goto err_no_mem;
+
+ std->dma_addr = transfer_dma;
+ std->len = std_len;
+ std->ntds_remaining = ntds_remaining;
+
+ if (qset_fill_page_list(whc, std, mem_flags) < 0)
+ goto err_no_mem;
+
+ ntds_remaining--;
+ remaining -= std_len;
+ transfer_dma += std_len;
+ }
+
+ return 0;
+
+err_no_mem:
+ qset_free_stds(qset, urb);
+ return -ENOMEM;
+}
+
+/**
+ * qset_remove_urb - remove an URB from the urb queue.
+ *
+ * The URB is returned to the USB subsystem.
+ */
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, int status)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+ struct whc_urb *wurb = urb->hcpriv;
+
+ usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb);
+ /* Drop the lock as urb->complete() may enqueue another urb. */
+ spin_unlock(&whc->lock);
+ wusbhc_giveback_urb(wusbhc, urb, status);
+ spin_lock(&whc->lock);
+
+ kfree(wurb);
+}
+
+/**
+ * get_urb_status_from_qtd - get the completed urb status from qTD status
+ * @urb: completed urb
+ * @status: qTD status
+ */
+static int get_urb_status_from_qtd(struct urb *urb, u32 status)
+{
+ if (status & QTD_STS_HALTED) {
+ if (status & QTD_STS_DBE)
+ return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM;
+ else if (status & QTD_STS_BABBLE)
+ return -EOVERFLOW;
+ else if (status & QTD_STS_RCE)
+ return -ETIME;
+ return -EPIPE;
+ }
+ if (usb_pipein(urb->pipe)
+ && (urb->transfer_flags & URB_SHORT_NOT_OK)
+ && urb->actual_length < urb->transfer_buffer_length)
+ return -EREMOTEIO;
+ return 0;
+}
+
+/**
+ * process_inactive_qtd - process an inactive (but not halted) qTD.
+ *
+ * Update the urb with the transfer bytes from the qTD, if the urb is
+ * completely transferred or (in the case of an IN only) the LPF is
+ * set, then the transfer is complete and the urb should be returned
+ * to the system.
+ */
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd)
+{
+ struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+ struct urb *urb = std->urb;
+ uint32_t status;
+ bool complete;
+
+ status = le32_to_cpu(qtd->status);
+
+ urb->actual_length += std->len - QTD_STS_TO_LEN(status);
+
+ if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT))
+ complete = true;
+ else
+ complete = whc_std_last(std);
+
+ qset_remove_qtd(whc, qset);
+ qset_free_std(whc, std);
+
+ /*
+ * Transfers for this URB are complete? Then return it to the
+ * USB subsystem.
+ */
+ if (complete) {
+ qset_remove_qtds(whc, qset, urb);
+ qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status));
+
+ /*
+ * If iAlt isn't valid then the hardware didn't
+ * advance iCur. Adjust the start and end pointers to
+ * match iCur.
+ */
+ if (!(status & QTD_STS_IALT_VALID))
+ qset->td_start = qset->td_end
+ = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status));
+ qset->pause_after_urb = NULL;
+ }
+}
+
+/**
+ * process_halted_qtd - process a qset with a halted qtd
+ *
+ * Remove all the qTDs for the failed URB and return the failed URB to
+ * the USB subsystem. Then remove all other qTDs so the qset can be
+ * removed.
+ *
+ * FIXME: this is the point where rate adaptation can be done. If a
+ * transfer failed because it exceeded the maximum number of retries
+ * then it could be reactivated with a slower rate without having to
+ * remove the qset.
+ */
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd)
+{
+ struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+ struct urb *urb = std->urb;
+ int urb_status;
+
+ urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status));
+
+ qset_remove_qtds(whc, qset, urb);
+ qset_remove_urb(whc, qset, urb, urb_status);
+
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (qset->ntds == 0)
+ break;
+ qset_remove_qtd(whc, qset);
+ std->qtd = NULL;
+ }
+
+ qset->remove = 1;
+}
+
+void qset_free(struct whc *whc, struct whc_qset *qset)
+{
+ dma_pool_free(whc->qset_pool, qset, qset->qset_dma);
+}
+
+/**
+ * qset_delete - wait for a qset to be unused, then free it.
+ */
+void qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ wait_for_completion(&qset->remove_complete);
+ qset_free(whc, qset);
+}
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h
new file mode 100644
index 000000000..c80c7d93b
--- /dev/null
+++ b/drivers/usb/host/whci/whcd.h
@@ -0,0 +1,215 @@
+/*
+ * Wireless Host Controller (WHC) private header.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef __WHCD_H
+#define __WHCD_H
+
+#include <linux/uwb/whci.h>
+#include <linux/uwb/umc.h>
+#include <linux/workqueue.h>
+
+#include "whci-hc.h"
+
+/* Generic command timeout. */
+#define WHC_GENCMD_TIMEOUT_MS 100
+
+struct whc_dbg;
+
+struct whc {
+ struct wusbhc wusbhc;
+ struct umc_dev *umc;
+
+ resource_size_t base_phys;
+ void __iomem *base;
+ int irq;
+
+ u8 n_devices;
+ u8 n_keys;
+ u8 n_mmc_ies;
+
+ u64 *pz_list;
+ struct dn_buf_entry *dn_buf;
+ struct di_buf_entry *di_buf;
+ dma_addr_t pz_list_dma;
+ dma_addr_t dn_buf_dma;
+ dma_addr_t di_buf_dma;
+
+ spinlock_t lock;
+ struct mutex mutex;
+
+ void * gen_cmd_buf;
+ dma_addr_t gen_cmd_buf_dma;
+ wait_queue_head_t cmd_wq;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct dn_work;
+
+ struct dma_pool *qset_pool;
+
+ struct list_head async_list;
+ struct list_head async_removed_list;
+ wait_queue_head_t async_list_wq;
+ struct work_struct async_work;
+
+ struct list_head periodic_list[5];
+ struct list_head periodic_removed_list;
+ wait_queue_head_t periodic_list_wq;
+ struct work_struct periodic_work;
+
+ struct whc_dbg *dbg;
+};
+
+#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc))
+
+/**
+ * struct whc_std - a software TD.
+ * @urb: the URB this sTD is for.
+ * @offset: start of the URB's data for this TD.
+ * @len: the length of data in the associated TD.
+ * @ntds_remaining: number of TDs (starting from this one) in this transfer.
+ *
+ * @bounce_buf: a bounce buffer if the std was from an urb with a sg
+ * list that could not be mapped to qTDs directly.
+ * @bounce_sg: the first scatterlist element bounce_buf is for.
+ * @bounce_offset: the offset into bounce_sg for the start of bounce_buf.
+ *
+ * Queued URBs may require more TDs than are available in a qset so we
+ * use a list of these "software TDs" (sTDs) to hold per-TD data.
+ */
+struct whc_std {
+ struct urb *urb;
+ size_t len;
+ int ntds_remaining;
+ struct whc_qtd *qtd;
+
+ struct list_head list_node;
+ int num_pointers;
+ dma_addr_t dma_addr;
+ struct whc_page_list_entry *pl_virt;
+
+ void *bounce_buf;
+ struct scatterlist *bounce_sg;
+ unsigned bounce_offset;
+};
+
+/**
+ * struct whc_urb - per URB host controller structure.
+ * @urb: the URB this struct is for.
+ * @qset: the qset associated to the URB.
+ * @dequeue_work: the work to remove the URB when dequeued.
+ * @is_async: the URB belongs to async sheduler or not.
+ * @status: the status to be returned when calling wusbhc_giveback_urb.
+ */
+struct whc_urb {
+ struct urb *urb;
+ struct whc_qset *qset;
+ struct work_struct dequeue_work;
+ bool is_async;
+ int status;
+};
+
+/**
+ * whc_std_last - is this sTD the URB's last?
+ * @std: the sTD to check.
+ */
+static inline bool whc_std_last(struct whc_std *std)
+{
+ return std->ntds_remaining <= 1;
+}
+
+enum whc_update {
+ WHC_UPDATE_ADDED = 0x01,
+ WHC_UPDATE_REMOVED = 0x02,
+ WHC_UPDATE_UPDATED = 0x04,
+};
+
+/* init.c */
+int whc_init(struct whc *whc);
+void whc_clean_up(struct whc *whc);
+
+/* hw.c */
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val);
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len);
+void whc_hw_error(struct whc *whc, const char *reason);
+
+/* wusb.c */
+int whc_wusbhc_start(struct wusbhc *wusbhc);
+void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay);
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ u8 handle, struct wuie_hdr *wuie);
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle);
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm);
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots);
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *ptk, size_t key_size);
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *gtk, size_t key_size);
+int whc_set_cluster_id(struct whc *whc, u8 bcid);
+
+/* int.c */
+irqreturn_t whc_int_handler(struct usb_hcd *hcd);
+void whc_dn_work(struct work_struct *work);
+
+/* asl.c */
+void asl_start(struct whc *whc);
+void asl_stop(struct whc *whc);
+int asl_init(struct whc *whc);
+void asl_clean_up(struct whc *whc);
+int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_async_work(struct work_struct *work);
+
+/* pzl.c */
+int pzl_init(struct whc *whc);
+void pzl_clean_up(struct whc *whc);
+void pzl_start(struct whc *whc);
+void pzl_stop(struct whc *whc);
+int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_periodic_work(struct work_struct *work);
+
+/* qset.c */
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags);
+void qset_free(struct whc *whc, struct whc_qset *qset);
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+void qset_delete(struct whc *whc, struct whc_qset *qset);
+void qset_clear(struct whc *whc, struct whc_qset *qset);
+void qset_reset(struct whc *whc, struct whc_qset *qset);
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags);
+void qset_free_std(struct whc *whc, struct whc_std *std);
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, int status);
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd);
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd);
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset);
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset);
+void pzl_update(struct whc *whc, uint32_t wusbcmd);
+void asl_update(struct whc *whc, uint32_t wusbcmd);
+
+/* debug.c */
+void whc_dbg_init(struct whc *whc);
+void whc_dbg_clean_up(struct whc *whc);
+
+#endif /* #ifndef __WHCD_H */
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
new file mode 100644
index 000000000..4d4cbc073
--- /dev/null
+++ b/drivers/usb/host/whci/whci-hc.h
@@ -0,0 +1,414 @@
+/*
+ * Wireless Host Controller (WHC) data structures.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef _WHCI_WHCI_HC_H
+#define _WHCI_WHCI_HC_H
+
+#include <linux/list.h>
+
+/**
+ * WHCI_PAGE_SIZE - page size use by WHCI
+ *
+ * WHCI assumes that host system uses pages of 4096 octets.
+ */
+#define WHCI_PAGE_SIZE 4096
+
+
+/**
+ * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single
+ * qtd.
+ *
+ * This is 2^20 - 1.
+ */
+#define QTD_MAX_XFER_SIZE 1048575
+
+
+/**
+ * struct whc_qtd - Queue Element Transfer Descriptors (qTD)
+ *
+ * This describes the data for a bulk, control or interrupt transfer.
+ *
+ * [WHCI] section 3.2.4
+ */
+struct whc_qtd {
+ __le32 status; /*< remaining transfer len and transfer status */
+ __le32 options;
+ __le64 page_list_ptr; /*< physical pointer to data buffer page list*/
+ __u8 setup[8]; /*< setup data for control transfers */
+} __attribute__((packed));
+
+#define QTD_STS_ACTIVE (1 << 31) /* enable execution of transaction */
+#define QTD_STS_HALTED (1 << 30) /* transfer halted */
+#define QTD_STS_DBE (1 << 29) /* data buffer error */
+#define QTD_STS_BABBLE (1 << 28) /* babble detected */
+#define QTD_STS_RCE (1 << 27) /* retry count exceeded */
+#define QTD_STS_LAST_PKT (1 << 26) /* set Last Packet Flag in WUSB header */
+#define QTD_STS_INACTIVE (1 << 25) /* queue set is marked inactive */
+#define QTD_STS_IALT_VALID (1 << 23) /* iAlt field is valid */
+#define QTD_STS_IALT(i) (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */
+#define QTD_STS_LEN(l) ((l) << 0) /* transfer length */
+#define QTD_STS_TO_LEN(s) ((s) & 0x000fffff)
+
+#define QTD_OPT_IOC (1 << 1) /* page_list_ptr points to buffer directly */
+#define QTD_OPT_SMALL (1 << 0) /* interrupt on complete */
+
+/**
+ * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD)
+ *
+ * This describes the data and other parameters for an isochronous
+ * transfer.
+ *
+ * [WHCI] section 3.2.5
+ */
+struct whc_itd {
+ __le16 presentation_time; /*< presentation time for OUT transfers */
+ __u8 num_segments; /*< number of data segments in segment list */
+ __u8 status; /*< command execution status */
+ __le32 options; /*< misc transfer options */
+ __le64 page_list_ptr; /*< physical pointer to data buffer page list */
+ __le64 seg_list_ptr; /*< physical pointer to segment list */
+} __attribute__((packed));
+
+#define ITD_STS_ACTIVE (1 << 7) /* enable execution of transaction */
+#define ITD_STS_DBE (1 << 5) /* data buffer error */
+#define ITD_STS_BABBLE (1 << 4) /* babble detected */
+#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */
+
+#define ITD_OPT_IOC (1 << 1) /* interrupt on complete */
+#define ITD_OPT_SMALL (1 << 0) /* page_list_ptr points to buffer directly */
+
+/**
+ * Page list entry.
+ *
+ * A TD's page list must contain sufficient page list entries for the
+ * total data length in the TD.
+ *
+ * [WHCI] section 3.2.4.3
+ */
+struct whc_page_list_entry {
+ __le64 buf_ptr; /*< physical pointer to buffer */
+} __attribute__((packed));
+
+/**
+ * struct whc_seg_list_entry - Segment list entry.
+ *
+ * Describes a portion of the data buffer described in the containing
+ * qTD's page list.
+ *
+ * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr
+ * + qtd->seg_list_ptr[seg].offset;
+ *
+ * Segments can't cross page boundries.
+ *
+ * [WHCI] section 3.2.5.5
+ */
+struct whc_seg_list_entry {
+ __le16 len; /*< segment length */
+ __u8 idx; /*< index into page list */
+ __u8 status; /*< segment status */
+ __le16 offset; /*< 12 bit offset into page */
+} __attribute__((packed));
+
+/**
+ * struct whc_qhead - endpoint and status information for a qset.
+ *
+ * [WHCI] section 3.2.6
+ */
+struct whc_qhead {
+ __le64 link; /*< next qset in list */
+ __le32 info1;
+ __le32 info2;
+ __le32 info3;
+ __le16 status;
+ __le16 err_count; /*< transaction error count */
+ __le32 cur_window;
+ __le32 scratch[3]; /*< h/w scratch area */
+ union {
+ struct whc_qtd qtd;
+ struct whc_itd itd;
+ } overlay;
+} __attribute__((packed));
+
+#define QH_LINK_PTR_MASK (~0x03Full)
+#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK)
+#define QH_LINK_IQS (1 << 4) /* isochronous queue set */
+#define QH_LINK_NTDS(n) (((n) - 1) << 1) /* number of TDs in queue set */
+#define QH_LINK_T (1 << 0) /* last queue set in periodic schedule list */
+
+#define QH_INFO1_EP(e) ((e) << 0) /* endpoint number */
+#define QH_INFO1_DIR_IN (1 << 4) /* IN transfer */
+#define QH_INFO1_DIR_OUT (0 << 4) /* OUT transfer */
+#define QH_INFO1_TR_TYPE_CTRL (0x0 << 5) /* control transfer */
+#define QH_INFO1_TR_TYPE_ISOC (0x1 << 5) /* isochronous transfer */
+#define QH_INFO1_TR_TYPE_BULK (0x2 << 5) /* bulk transfer */
+#define QH_INFO1_TR_TYPE_INT (0x3 << 5) /* interrupt */
+#define QH_INFO1_TR_TYPE_LP_INT (0x7 << 5) /* low power interrupt */
+#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8) /* index into device info buffer */
+#define QH_INFO1_SET_INACTIVE (1 << 15) /* set inactive after transfer */
+#define QH_INFO1_MAX_PKT_LEN(l) ((l) << 16) /* maximum packet length */
+
+#define QH_INFO2_BURST(b) ((b) << 0) /* maximum burst length */
+#define QH_INFO2_DBP(p) ((p) << 5) /* data burst policy (see [WUSB] table 5-7) */
+#define QH_INFO2_MAX_COUNT(c) ((c) << 8) /* max isoc/int pkts per zone */
+#define QH_INFO2_RQS (1 << 15) /* reactivate queue set */
+#define QH_INFO2_MAX_RETRY(r) ((r) << 16) /* maximum transaction retries */
+#define QH_INFO2_MAX_SEQ(s) ((s) << 20) /* maximum sequence number */
+#define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */
+#define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */
+
+#define QH_INFO3_TX_RATE(r) ((r) << 24) /* PHY rate (see [ECMA-368] section 10.3.1.1) */
+#define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */
+
+#define QH_STATUS_FLOW_CTRL (1 << 15)
+#define QH_STATUS_ICUR(i) ((i) << 5)
+#define QH_STATUS_TO_ICUR(s) (((s) >> 5) & 0x7)
+#define QH_STATUS_SEQ_MASK 0x1f
+
+/**
+ * usb_pipe_to_qh_type - USB core pipe type to QH transfer type
+ *
+ * Returns the QH type field for a USB core pipe type.
+ */
+static inline unsigned usb_pipe_to_qh_type(unsigned pipe)
+{
+ static const unsigned type[] = {
+ [PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC,
+ [PIPE_INTERRUPT] = QH_INFO1_TR_TYPE_INT,
+ [PIPE_CONTROL] = QH_INFO1_TR_TYPE_CTRL,
+ [PIPE_BULK] = QH_INFO1_TR_TYPE_BULK,
+ };
+ return type[usb_pipetype(pipe)];
+}
+
+/**
+ * Maxiumum number of TDs in a qset.
+ */
+#define WHCI_QSET_TD_MAX 8
+
+/**
+ * struct whc_qset - WUSB data transfers to a specific endpoint
+ * @qh: the QHead of this qset
+ * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt
+ * transfers)
+ * @itd: up to 8 iTDs (for qsets for isochronous transfers)
+ * @qset_dma: DMA address for this qset
+ * @whc: WHCI HC this qset is for
+ * @ep: endpoint
+ * @stds: list of sTDs queued to this qset
+ * @ntds: number of qTDs queued (not necessarily the same as nTDs
+ * field in the QH)
+ * @td_start: index of the first qTD in the list
+ * @td_end: index of next free qTD in the list (provided
+ * ntds < WHCI_QSET_TD_MAX)
+ *
+ * Queue Sets (qsets) are added to the asynchronous schedule list
+ * (ASL) or the periodic zone list (PZL).
+ *
+ * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate).
+ * Each TD may refer to at most 1 MiB of data. If a single transfer
+ * has > 8MiB of data, TDs can be reused as they are completed since
+ * the TD list is used as a circular buffer. Similarly, several
+ * (smaller) transfers may be queued in a qset.
+ *
+ * WHCI controllers may cache portions of the qsets in the ASL and
+ * PZL, requiring the WHCD to inform the WHC that the lists have been
+ * updated (fields changed or qsets inserted or removed). For safe
+ * insertion and removal of qsets from the lists the schedule must be
+ * stopped to avoid races in updating the QH link pointers.
+ *
+ * Since the HC is free to execute qsets in any order, all transfers
+ * to an endpoint should use the same qset to ensure transfers are
+ * executed in the order they're submitted.
+ *
+ * [WHCI] section 3.2.3
+ */
+struct whc_qset {
+ struct whc_qhead qh;
+ union {
+ struct whc_qtd qtd[WHCI_QSET_TD_MAX];
+ struct whc_itd itd[WHCI_QSET_TD_MAX];
+ };
+
+ /* private data for WHCD */
+ dma_addr_t qset_dma;
+ struct whc *whc;
+ struct usb_host_endpoint *ep;
+ struct list_head stds;
+ int ntds;
+ int td_start;
+ int td_end;
+ struct list_head list_node;
+ unsigned in_sw_list:1;
+ unsigned in_hw_list:1;
+ unsigned remove:1;
+ unsigned reset:1;
+ struct urb *pause_after_urb;
+ struct completion remove_complete;
+ uint16_t max_packet;
+ uint8_t max_burst;
+ uint8_t max_seq;
+};
+
+static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)
+{
+ if (target)
+ *ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target);
+ else
+ *ptr = QH_LINK_T;
+}
+
+/**
+ * struct di_buf_entry - Device Information (DI) buffer entry.
+ *
+ * There's one of these per connected device.
+ */
+struct di_buf_entry {
+ __le32 availability_info[8]; /*< MAS availability information, one MAS per bit */
+ __le32 addr_sec_info; /*< addressing and security info */
+ __le32 reserved[7];
+} __attribute__((packed));
+
+#define WHC_DI_SECURE (1 << 31)
+#define WHC_DI_DISABLE (1 << 30)
+#define WHC_DI_KEY_IDX(k) ((k) << 8)
+#define WHC_DI_KEY_IDX_MASK 0x0000ff00
+#define WHC_DI_DEV_ADDR(a) ((a) << 0)
+#define WHC_DI_DEV_ADDR_MASK 0x000000ff
+
+/**
+ * struct dn_buf_entry - Device Notification (DN) buffer entry.
+ *
+ * [WHCI] section 3.2.8
+ */
+struct dn_buf_entry {
+ __u8 msg_size; /*< number of octets of valid DN data */
+ __u8 reserved1;
+ __u8 src_addr; /*< source address */
+ __u8 status; /*< buffer entry status */
+ __le32 tkid; /*< TKID for source device, valid if secure bit is set */
+ __u8 dn_data[56]; /*< up to 56 octets of DN data */
+} __attribute__((packed));
+
+#define WHC_DN_STATUS_VALID (1 << 7) /* buffer entry is valid */
+#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */
+
+#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry))
+
+/* The Add MMC IE WUSB Generic Command may take up to 256 bytes of
+ data. [WHCI] section 2.4.7. */
+#define WHC_GEN_CMD_DATA_LEN 256
+
+/*
+ * HC registers.
+ *
+ * [WHCI] section 2.4
+ */
+
+#define WHCIVERSION 0x00
+
+#define WHCSPARAMS 0x04
+# define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff)
+# define WHCSPARAMS_TO_N_KEYS(p) (((p) >> 8) & 0xff)
+# define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f)
+
+#define WUSBCMD 0x08
+# define WUSBCMD_BCID(b) ((b) << 16)
+# define WUSBCMD_BCID_MASK (0xff << 16)
+# define WUSBCMD_ASYNC_QSET_RM (1 << 12)
+# define WUSBCMD_PERIODIC_QSET_RM (1 << 11)
+# define WUSBCMD_WUSBSI(s) ((s) << 8)
+# define WUSBCMD_WUSBSI_MASK (0x7 << 8)
+# define WUSBCMD_ASYNC_SYNCED_DB (1 << 7)
+# define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6)
+# define WUSBCMD_ASYNC_UPDATED (1 << 5)
+# define WUSBCMD_PERIODIC_UPDATED (1 << 4)
+# define WUSBCMD_ASYNC_EN (1 << 3)
+# define WUSBCMD_PERIODIC_EN (1 << 2)
+# define WUSBCMD_WHCRESET (1 << 1)
+# define WUSBCMD_RUN (1 << 0)
+
+#define WUSBSTS 0x0c
+# define WUSBSTS_ASYNC_SCHED (1 << 15)
+# define WUSBSTS_PERIODIC_SCHED (1 << 14)
+# define WUSBSTS_DNTS_SCHED (1 << 13)
+# define WUSBSTS_HCHALTED (1 << 12)
+# define WUSBSTS_GEN_CMD_DONE (1 << 9)
+# define WUSBSTS_CHAN_TIME_ROLLOVER (1 << 8)
+# define WUSBSTS_DNTS_OVERFLOW (1 << 7)
+# define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6)
+# define WUSBSTS_HOST_ERR (1 << 5)
+# define WUSBSTS_ASYNC_SCHED_SYNCED (1 << 4)
+# define WUSBSTS_PERIODIC_SCHED_SYNCED (1 << 3)
+# define WUSBSTS_DNTS_INT (1 << 2)
+# define WUSBSTS_ERR_INT (1 << 1)
+# define WUSBSTS_INT (1 << 0)
+# define WUSBSTS_INT_MASK 0x3ff
+
+#define WUSBINTR 0x10
+# define WUSBINTR_GEN_CMD_DONE (1 << 9)
+# define WUSBINTR_CHAN_TIME_ROLLOVER (1 << 8)
+# define WUSBINTR_DNTS_OVERFLOW (1 << 7)
+# define WUSBINTR_BPST_ADJUSTMENT_CHANGED (1 << 6)
+# define WUSBINTR_HOST_ERR (1 << 5)
+# define WUSBINTR_ASYNC_SCHED_SYNCED (1 << 4)
+# define WUSBINTR_PERIODIC_SCHED_SYNCED (1 << 3)
+# define WUSBINTR_DNTS_INT (1 << 2)
+# define WUSBINTR_ERR_INT (1 << 1)
+# define WUSBINTR_INT (1 << 0)
+# define WUSBINTR_ALL 0x3ff
+
+#define WUSBGENCMDSTS 0x14
+# define WUSBGENCMDSTS_ACTIVE (1 << 31)
+# define WUSBGENCMDSTS_ERROR (1 << 24)
+# define WUSBGENCMDSTS_IOC (1 << 23)
+# define WUSBGENCMDSTS_MMCIE_ADD 0x01
+# define WUSBGENCMDSTS_MMCIE_RM 0x02
+# define WUSBGENCMDSTS_SET_MAS 0x03
+# define WUSBGENCMDSTS_CHAN_STOP 0x04
+# define WUSBGENCMDSTS_RWP_EN 0x05
+
+#define WUSBGENCMDPARAMS 0x18
+#define WUSBGENADDR 0x20
+#define WUSBASYNCLISTADDR 0x28
+#define WUSBDNTSBUFADDR 0x30
+#define WUSBDEVICEINFOADDR 0x38
+
+#define WUSBSETSECKEYCMD 0x40
+# define WUSBSETSECKEYCMD_SET (1 << 31)
+# define WUSBSETSECKEYCMD_ERASE (1 << 30)
+# define WUSBSETSECKEYCMD_GTK (1 << 8)
+# define WUSBSETSECKEYCMD_IDX(i) ((i) << 0)
+
+#define WUSBTKID 0x44
+#define WUSBSECKEY 0x48
+#define WUSBPERIODICLISTBASE 0x58
+#define WUSBMASINDEX 0x60
+
+#define WUSBDNTSCTRL 0x64
+# define WUSBDNTSCTRL_ACTIVE (1 << 31)
+# define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8)
+# define WUSBDNTSCTRL_SLOTS(s) ((s) << 0)
+
+#define WUSBTIME 0x68
+# define WUSBTIME_CHANNEL_TIME_MASK 0x00ffffff
+
+#define WUSBBPST 0x6c
+#define WUSBDIBUPDATED 0x70
+
+#endif /* #ifndef _WHCI_WHCI_HC_H */
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c
new file mode 100644
index 000000000..8d2762682
--- /dev/null
+++ b/drivers/usb/host/whci/wusb.c
@@ -0,0 +1,221 @@
+/*
+ * Wireless Host Controller (WHC) WUSB operations.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static int whc_update_di(struct whc *whc, int idx)
+{
+ int offset = idx / 32;
+ u32 bit = 1 << (idx % 32);
+
+ le_writel(bit, whc->base + WUSBDIBUPDATED + offset);
+
+ return whci_wait_for(&whc->umc->dev,
+ whc->base + WUSBDIBUPDATED + offset, bit, 0,
+ 100, "DI update");
+}
+
+/*
+ * WHCI starts MMCs based on there being a valid GTK so these need
+ * only start/stop the asynchronous and periodic schedules and send a
+ * channel stop command.
+ */
+
+int whc_wusbhc_start(struct wusbhc *wusbhc)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ asl_start(whc);
+ pzl_start(whc);
+
+ return 0;
+}
+
+void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 stop_time, now_time;
+ int ret;
+
+ pzl_stop(whc);
+ asl_stop(whc);
+
+ now_time = le_readl(whc->base + WUSBTIME) & WUSBTIME_CHANNEL_TIME_MASK;
+ stop_time = (now_time + ((delay * 8) << 7)) & 0x00ffffff;
+ ret = whc_do_gencmd(whc, WUSBGENCMDSTS_CHAN_STOP, stop_time, NULL, 0);
+ if (ret == 0)
+ msleep(delay);
+}
+
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ u8 handle, struct wuie_hdr *wuie)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 params;
+
+ params = (interval << 24)
+ | (repeat_cnt << 16)
+ | (wuie->bLength << 8)
+ | handle;
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength);
+}
+
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 params;
+
+ params = handle;
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0);
+}
+
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ if (stream_index >= 0)
+ whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index));
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm));
+}
+
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int idx = wusb_dev->port_idx;
+ struct di_buf_entry *di = &whc->di_buf[idx];
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability);
+ di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK);
+ di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr);
+
+ ret = whc_update_di(whc, idx);
+
+ mutex_unlock(&whc->mutex);
+
+ return ret;
+}
+
+/*
+ * Set the number of Device Notification Time Slots (DNTS) and enable
+ * device notifications.
+ */
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 dntsctrl;
+
+ dntsctrl = WUSBDNTSCTRL_ACTIVE
+ | WUSBDNTSCTRL_INTERVAL(interval)
+ | WUSBDNTSCTRL_SLOTS(slots);
+
+ le_writel(dntsctrl, whc->base + WUSBDNTSCTRL);
+
+ return 0;
+}
+
+static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid,
+ const void *key, size_t key_size, bool is_gtk)
+{
+ uint32_t setkeycmd;
+ uint32_t seckey[4];
+ int i;
+ int ret;
+
+ memcpy(seckey, key, key_size);
+ setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index);
+ if (is_gtk)
+ setkeycmd |= WUSBSETSECKEYCMD_GTK;
+
+ le_writel(tkid, whc->base + WUSBTKID);
+ for (i = 0; i < 4; i++)
+ le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i);
+ le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD);
+
+ ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD,
+ WUSBSETSECKEYCMD_SET, 0, 100, "set key");
+
+ return ret;
+}
+
+/**
+ * whc_set_ptk - set the PTK to use for a device.
+ *
+ * The index into the key table for this PTK is the same as the
+ * device's port index.
+ */
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *ptk, size_t key_size)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ struct di_buf_entry *di = &whc->di_buf[port_idx];
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ if (ptk) {
+ ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false);
+ if (ret)
+ goto out;
+
+ di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK;
+ di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx);
+ } else
+ di->addr_sec_info &= ~WHC_DI_SECURE;
+
+ ret = whc_update_di(whc, port_idx);
+out:
+ mutex_unlock(&whc->mutex);
+ return ret;
+}
+
+/**
+ * whc_set_gtk - set the GTK for subsequent broadcast packets
+ *
+ * The GTK is stored in the last entry in the key table (the previous
+ * N_DEVICES entries are for the per-device PTKs).
+ */
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *gtk, size_t key_size)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true);
+
+ mutex_unlock(&whc->mutex);
+
+ return ret;
+}
+
+int whc_set_cluster_id(struct whc *whc, u8 bcid)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid));
+ return 0;
+}
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
new file mode 100644
index 000000000..745717ec9
--- /dev/null
+++ b/drivers/usb/host/xhci-dbg.c
@@ -0,0 +1,597 @@
+/*
+ * 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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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 = readl(&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_HSEIE) ? "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 = readl(&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)
+{
+ __le32 __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) readl(addr));
+ addr++;
+ }
+ }
+}
+
+void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num)
+{
+ struct xhci_intr_reg __iomem *ir_set = &xhci->run_regs->ir_set[set_num];
+ void __iomem *addr;
+ u32 temp;
+ u64 temp_64;
+
+ addr = &ir_set->irq_pending;
+ temp = readl(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 = readl(addr);
+ xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr,
+ (unsigned int)temp);
+
+ addr = &ir_set->erst_size;
+ temp = readl(addr);
+ xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr,
+ (unsigned int)temp);
+
+ addr = &ir_set->rsvd;
+ temp = readl(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;
+ temp_64 = xhci_read_64(xhci, addr);
+ xhci_dbg(xhci, " %p: ir_set.erst_base = @%08llx\n",
+ addr, temp_64);
+
+ addr = &ir_set->erst_dequeue;
+ temp_64 = xhci_read_64(xhci, addr);
+ xhci_dbg(xhci, " %p: ir_set.erst_dequeue = @%08llx\n",
+ addr, temp_64);
+}
+
+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 = readl(&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 = readl(&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 = le32_to_cpu(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 = le64_to_cpu(trb->link.segment_ptr);
+ xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address);
+
+ xhci_dbg(xhci, "Interrupter target = 0x%x\n",
+ GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target)));
+ xhci_dbg(xhci, "Cycle bit = %u\n",
+ le32_to_cpu(trb->link.control) & TRB_CYCLE);
+ xhci_dbg(xhci, "Toggle cycle bit = %u\n",
+ le32_to_cpu(trb->link.control) & LINK_TOGGLE);
+ xhci_dbg(xhci, "No Snoop bit = %u\n",
+ le32_to_cpu(trb->link.control) & TRB_NO_SNOOP);
+ break;
+ case TRB_TYPE(TRB_TRANSFER):
+ address = le64_to_cpu(trb->trans_event.buffer);
+ /*
+ * 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 = le64_to_cpu(trb->event_cmd.cmd_trb);
+ xhci_dbg(xhci, "Command TRB pointer = %llu\n", address);
+ xhci_dbg(xhci, "Completion status = %u\n",
+ GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status)));
+ xhci_dbg(xhci, "Flags = 0x%x\n",
+ le32_to_cpu(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;
+ u64 addr = seg->dma;
+ union xhci_trb *trb = seg->trbs;
+
+ for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
+ trb = &seg->trbs[i];
+ xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr,
+ lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ upper_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ le32_to_cpu(trb->link.intr_target),
+ le32_to_cpu(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_ep_rings(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_virt_ep *ep)
+{
+ int i;
+ struct xhci_ring *ring;
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ for (i = 1; i < ep->stream_info->num_streams; i++) {
+ ring = ep->stream_info->stream_rings[i];
+ xhci_dbg(xhci, "Dev %d endpoint %d stream ID %d:\n",
+ slot_id, ep_index, i);
+ xhci_debug_segment(xhci, ring->deq_seg);
+ }
+ } else {
+ ring = ep->ring;
+ if (!ring)
+ return;
+ xhci_dbg(xhci, "Dev %d endpoint ring %d:\n",
+ slot_id, ep_index);
+ xhci_debug_segment(xhci, ring->deq_seg);
+ }
+}
+
+void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
+{
+ u64 addr = 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, "@%016llx %08x %08x %08x %08x\n",
+ addr,
+ lower_32_bits(le64_to_cpu(entry->seg_addr)),
+ upper_32_bits(le64_to_cpu(entry->seg_addr)),
+ le32_to_cpu(entry->seg_size),
+ le32_to_cpu(entry->rsvd));
+ addr += sizeof(*entry);
+ }
+}
+
+void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci)
+{
+ u64 val;
+
+ val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = @%08x\n",
+ lower_32_bits(val));
+ xhci_dbg(xhci, "// xHC command ring deq ptr high bits = @%08x\n",
+ upper_32_bits(val));
+}
+
+/* Print the last 32 bytes for 64-byte contexts */
+static void dbg_rsvd64(struct xhci_hcd *xhci, u64 *ctx, dma_addr_t dma)
+{
+ int i;
+ for (i = 0; i < 4; ++i) {
+ xhci_dbg(xhci, "@%p (virt) @%08llx "
+ "(dma) %#08llx - rsvd64[%d]\n",
+ &ctx[4 + i], (unsigned long long)dma,
+ ctx[4 + i], i);
+ dma += 8;
+ }
+}
+
+char *xhci_get_slot_state(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx);
+
+ switch (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state))) {
+ case SLOT_STATE_ENABLED:
+ return "enabled/disabled";
+ case SLOT_STATE_DEFAULT:
+ return "default";
+ case SLOT_STATE_ADDRESSED:
+ return "addressed";
+ case SLOT_STATE_CONFIGURED:
+ return "configured";
+ default:
+ return "reserved";
+ }
+}
+
+static void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
+{
+ /* Fields are 32 bits wide, DMA addresses are in bytes */
+ int field_size = 32 / 8;
+ int i;
+
+ struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx);
+ dma_addr_t dma = ctx->dma +
+ ((unsigned long)slot_ctx - (unsigned long)ctx->bytes);
+ int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+
+ xhci_dbg(xhci, "Slot Context:\n");
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info\n",
+ &slot_ctx->dev_info,
+ (unsigned long long)dma, slot_ctx->dev_info);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info2\n",
+ &slot_ctx->dev_info2,
+ (unsigned long long)dma, slot_ctx->dev_info2);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tt_info\n",
+ &slot_ctx->tt_info,
+ (unsigned long long)dma, slot_ctx->tt_info);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_state\n",
+ &slot_ctx->dev_state,
+ (unsigned long long)dma, slot_ctx->dev_state);
+ dma += field_size;
+ for (i = 0; i < 4; ++i) {
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
+ &slot_ctx->reserved[i], (unsigned long long)dma,
+ slot_ctx->reserved[i], i);
+ dma += field_size;
+ }
+
+ if (csz)
+ dbg_rsvd64(xhci, (u64 *)slot_ctx, dma);
+}
+
+static void xhci_dbg_ep_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx,
+ 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;
+ int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+
+ if (last_ep < 31)
+ last_ep_ctx = last_ep + 1;
+ for (i = 0; i < last_ep_ctx; ++i) {
+ unsigned int epaddr = xhci_get_endpoint_address(i);
+ struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i);
+ dma_addr_t dma = ctx->dma +
+ ((unsigned long)ep_ctx - (unsigned long)ctx->bytes);
+
+ xhci_dbg(xhci, "%s Endpoint %02d Context (ep_index %02d):\n",
+ usb_endpoint_out(epaddr) ? "OUT" : "IN",
+ epaddr & USB_ENDPOINT_NUMBER_MASK, i);
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n",
+ &ep_ctx->ep_info,
+ (unsigned long long)dma, ep_ctx->ep_info);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info2\n",
+ &ep_ctx->ep_info2,
+ (unsigned long long)dma, ep_ctx->ep_info2);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08llx - deq\n",
+ &ep_ctx->deq,
+ (unsigned long long)dma, ep_ctx->deq);
+ dma += 2*field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tx_info\n",
+ &ep_ctx->tx_info,
+ (unsigned long long)dma, ep_ctx->tx_info);
+ dma += field_size;
+ for (j = 0; j < 3; ++j) {
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
+ &ep_ctx->reserved[j],
+ (unsigned long long)dma,
+ ep_ctx->reserved[j], j);
+ dma += field_size;
+ }
+
+ if (csz)
+ dbg_rsvd64(xhci, (u64 *)ep_ctx, dma);
+ }
+}
+
+void xhci_dbg_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx,
+ unsigned int last_ep)
+{
+ int i;
+ /* Fields are 32 bits wide, DMA addresses are in bytes */
+ int field_size = 32 / 8;
+ dma_addr_t dma = ctx->dma;
+ int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+
+ if (ctx->type == XHCI_CTX_TYPE_INPUT) {
+ struct xhci_input_control_ctx *ctrl_ctx =
+ xhci_get_input_control_ctx(ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "Could not get input context, bad type.\n");
+ return;
+ }
+
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n",
+ &ctrl_ctx->drop_flags, (unsigned long long)dma,
+ ctrl_ctx->drop_flags);
+ dma += field_size;
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n",
+ &ctrl_ctx->add_flags, (unsigned long long)dma,
+ ctrl_ctx->add_flags);
+ dma += field_size;
+ for (i = 0; i < 6; ++i) {
+ xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd2[%d]\n",
+ &ctrl_ctx->rsvd2[i], (unsigned long long)dma,
+ ctrl_ctx->rsvd2[i], i);
+ dma += field_size;
+ }
+
+ if (csz)
+ dbg_rsvd64(xhci, (u64 *)ctrl_ctx, dma);
+ }
+
+ xhci_dbg_slot_ctx(xhci, ctx);
+ xhci_dbg_ep_ctx(xhci, ctx, last_ep);
+}
+
+void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ xhci_dbg(xhci, "%pV\n", &vaf);
+ trace(&vaf);
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(xhci_dbg_trace);
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
new file mode 100644
index 000000000..9fe3225e6
--- /dev/null
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -0,0 +1,156 @@
+/*
+ * 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 ms to halt an HC */
+#define XHCI_MAX_HALT_USEC (16*1000)
+/* 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:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
+#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17))
+#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29)
+
+/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */
+#define XHCI_L1C (1 << 16)
+
+/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
+#define XHCI_HLC (1 << 19)
+#define XHCI_BLC (1 << 20)
+
+/* 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);
+ ext_offset = 0;
+ } 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-hub.c b/drivers/usb/host/xhci-hub.c
new file mode 100644
index 000000000..0827d7c96
--- /dev/null
+++ b/drivers/usb/host/xhci-hub.c
@@ -0,0 +1,1265 @@
+/*
+ * 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/slab.h>
+#include <asm/unaligned.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+
+#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
+ PORT_RC | PORT_PLC | PORT_PE)
+
+/* USB 3.0 BOS descriptor and a capability descriptor, combined */
+static u8 usb_bos_descriptor [] = {
+ USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */
+ USB_DT_BOS, /* __u8 bDescriptorType */
+ 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */
+ 0x1, /* __u8 bNumDeviceCaps */
+ /* First device capability */
+ USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */
+ USB_DT_DEVICE_CAPABILITY, /* Device Capability */
+ USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */
+ 0x00, /* bmAttributes, LTM off by default */
+ USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */
+ 0x03, /* bFunctionalitySupport,
+ USB 3.0 speed only */
+ 0x00, /* bU1DevExitLat, set later. */
+ 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */
+};
+
+
+static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc, int ports)
+{
+ u16 temp;
+
+ desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 0;
+ /* Bits 1:0 - support per-port power switching, or power always on */
+ if (HCC_PPC(xhci->hcc_params))
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
+ else
+ temp |= HUB_CHAR_NO_LPSM;
+ /* Bit 2 - root hubs are not part of a compound device */
+ /* Bits 4:3 - individual port over current protection */
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
+ /* Bits 6:5 - no TTs in root ports */
+ /* Bit 7 - no port indicators */
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+/* Fill in the USB 2.0 roothub descriptor */
+static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+ int ports;
+ u16 temp;
+ __u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8];
+ u32 portsc;
+ unsigned int i;
+
+ ports = xhci->num_usb2_ports;
+
+ xhci_common_hub_descriptor(xhci, desc, ports);
+ desc->bDescriptorType = USB_DT_HUB;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp;
+
+ /* The Device Removable bits are reported on a byte granularity.
+ * If the port doesn't exist within that byte, the bit is set to 0.
+ */
+ memset(port_removable, 0, sizeof(port_removable));
+ for (i = 0; i < ports; i++) {
+ portsc = readl(xhci->usb2_ports[i]);
+ /* If a device is removable, PORTSC reports a 0, same as in the
+ * hub descriptor DeviceRemovable bits.
+ */
+ if (portsc & PORT_DEV_REMOVE)
+ /* This math is hairy because bit 0 of DeviceRemovable
+ * is reserved, and bit 1 is for port 1, etc.
+ */
+ port_removable[(i + 1) / 8] |= 1 << ((i + 1) % 8);
+ }
+
+ /* ch11.h defines a hub descriptor that has room for USB_MAXCHILDREN
+ * ports on it. The USB 2.0 specification says that there are two
+ * variable length fields at the end of the hub descriptor:
+ * DeviceRemovable and PortPwrCtrlMask. But since we can have less than
+ * USB_MAXCHILDREN ports, we may need to use the DeviceRemovable array
+ * to set PortPwrCtrlMask bits. PortPwrCtrlMask must always be set to
+ * 0xFF, so we initialize the both arrays (DeviceRemovable and
+ * PortPwrCtrlMask) to 0xFF. Then we set the DeviceRemovable for each
+ * set of ports that actually exist.
+ */
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ memset(desc->u.hs.PortPwrCtrlMask, 0xff,
+ sizeof(desc->u.hs.PortPwrCtrlMask));
+
+ for (i = 0; i < (ports + 1 + 7) / 8; i++)
+ memset(&desc->u.hs.DeviceRemovable[i], port_removable[i],
+ sizeof(__u8));
+}
+
+/* Fill in the USB 3.0 roothub descriptor */
+static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+ int ports;
+ u16 port_removable;
+ u32 portsc;
+ unsigned int i;
+
+ ports = xhci->num_usb3_ports;
+ xhci_common_hub_descriptor(xhci, desc, ports);
+ desc->bDescriptorType = USB_DT_SS_HUB;
+ desc->bDescLength = USB_DT_SS_HUB_SIZE;
+
+ /* header decode latency should be zero for roothubs,
+ * see section 4.23.5.2.
+ */
+ desc->u.ss.bHubHdrDecLat = 0;
+ desc->u.ss.wHubDelay = 0;
+
+ port_removable = 0;
+ /* bit 0 is reserved, bit 1 is for port 1, etc. */
+ for (i = 0; i < ports; i++) {
+ portsc = readl(xhci->usb3_ports[i]);
+ if (portsc & PORT_DEV_REMOVE)
+ port_removable |= 1 << (i + 1);
+ }
+
+ desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable);
+}
+
+static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+
+ if (hcd->speed == HCD_USB3)
+ xhci_usb3_hub_descriptor(hcd, xhci, desc);
+ else
+ xhci_usb2_hub_descriptor(hcd, xhci, desc);
+
+}
+
+static unsigned int xhci_port_speed(unsigned int port_status)
+{
+ if (DEV_LOWSPEED(port_status))
+ return USB_PORT_STAT_LOW_SPEED;
+ if (DEV_HIGHSPEED(port_status))
+ return USB_PORT_STAT_HIGH_SPEED;
+ /*
+ * 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_STAT_*_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.
+ */
+u32 xhci_port_state_to_neutral(u32 state)
+{
+ /* Save read-only status and port state */
+ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+}
+
+/*
+ * find slot id based on port number.
+ * @port: The one-based port number from one of the two split roothubs.
+ */
+int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 port)
+{
+ int slot_id;
+ int i;
+ enum usb_device_speed speed;
+
+ slot_id = 0;
+ for (i = 0; i < MAX_HC_SLOTS; i++) {
+ if (!xhci->devs[i])
+ continue;
+ speed = xhci->devs[i]->udev->speed;
+ if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3))
+ && xhci->devs[i]->fake_port == port) {
+ slot_id = i;
+ break;
+ }
+ }
+
+ return slot_id;
+}
+
+/*
+ * Stop device
+ * It issues stop endpoint command for EP 0 to 30. And wait the last command
+ * to complete.
+ * suspend will set to 1, if suspend bit need to set in command.
+ */
+static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
+{
+ struct xhci_virt_device *virt_dev;
+ struct xhci_command *cmd;
+ unsigned long flags;
+ int ret;
+ int i;
+
+ ret = 0;
+ virt_dev = xhci->devs[slot_id];
+ cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
+ if (!cmd) {
+ xhci_dbg(xhci, "Couldn't allocate command structure.\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ for (i = LAST_EP_INDEX; i > 0; i--) {
+ if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false,
+ GFP_NOWAIT);
+ if (!command) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_free_command(xhci, cmd);
+ return -ENOMEM;
+
+ }
+ xhci_queue_stop_endpoint(xhci, command, slot_id, i,
+ suspend);
+ }
+ }
+ xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Wait for last stop endpoint command to finish */
+ wait_for_completion(cmd->completion);
+
+ if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) {
+ xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
+ ret = -ETIME;
+ }
+ xhci_free_command(xhci, cmd);
+ return ret;
+}
+
+/*
+ * Ring device, it rings the all doorbells unconditionally.
+ */
+void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
+{
+ int i, s;
+ struct xhci_virt_ep *ep;
+
+ for (i = 0; i < LAST_EP_INDEX + 1; i++) {
+ ep = &xhci->devs[slot_id]->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ for (s = 1; s < ep->stream_info->num_streams; s++)
+ xhci_ring_ep_doorbell(xhci, slot_id, i, s);
+ } else if (ep->ring && ep->ring->dequeue) {
+ xhci_ring_ep_doorbell(xhci, slot_id, i, 0);
+ }
+ }
+
+ return;
+}
+
+static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 wIndex, __le32 __iomem *addr, u32 port_status)
+{
+ /* Don't allow the USB core to disable SuperSpeed ports. */
+ if (hcd->speed == HCD_USB3) {
+ xhci_dbg(xhci, "Ignoring request to disable "
+ "SuperSpeed port.\n");
+ return;
+ }
+
+ /* Write 1 to disable the port */
+ writel(port_status | PORT_PE, addr);
+ port_status = readl(addr);
+ xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n",
+ wIndex, port_status);
+}
+
+static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
+ u16 wIndex, __le32 __iomem *addr, u32 port_status)
+{
+ char *port_change_bit;
+ u32 status;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_C_RESET:
+ status = PORT_RC;
+ port_change_bit = "reset";
+ break;
+ case USB_PORT_FEAT_C_BH_PORT_RESET:
+ status = PORT_WRC;
+ port_change_bit = "warm(BH) 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;
+ case USB_PORT_FEAT_C_ENABLE:
+ status = PORT_PEC;
+ port_change_bit = "enable/disable";
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ status = PORT_PLC;
+ port_change_bit = "suspend/resume";
+ break;
+ case USB_PORT_FEAT_C_PORT_LINK_STATE:
+ status = PORT_PLC;
+ port_change_bit = "link state";
+ break;
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
+ status = PORT_CEC;
+ port_change_bit = "config error";
+ break;
+ default:
+ /* Should never happen */
+ return;
+ }
+ /* Change bits are all write 1 to clear */
+ writel(port_status | status, addr);
+ port_status = readl(addr);
+ xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
+ port_change_bit, wIndex, port_status);
+}
+
+static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array)
+{
+ int max_ports;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (hcd->speed == HCD_USB3) {
+ max_ports = xhci->num_usb3_ports;
+ *port_array = xhci->usb3_ports;
+ } else {
+ max_ports = xhci->num_usb2_ports;
+ *port_array = xhci->usb2_ports;
+ }
+
+ return max_ports;
+}
+
+void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 link_state)
+{
+ u32 temp;
+
+ temp = readl(port_array[port_id]);
+ temp = xhci_port_state_to_neutral(temp);
+ temp &= ~PORT_PLS_MASK;
+ temp |= PORT_LINK_STROBE | link_state;
+ writel(temp, port_array[port_id]);
+}
+
+static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
+ __le32 __iomem **port_array, int port_id, u16 wake_mask)
+{
+ u32 temp;
+
+ temp = readl(port_array[port_id]);
+ temp = xhci_port_state_to_neutral(temp);
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
+ temp |= PORT_WKCONN_E;
+ else
+ temp &= ~PORT_WKCONN_E;
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT)
+ temp |= PORT_WKDISC_E;
+ else
+ temp &= ~PORT_WKDISC_E;
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT)
+ temp |= PORT_WKOC_E;
+ else
+ temp &= ~PORT_WKOC_E;
+
+ writel(temp, port_array[port_id]);
+}
+
+/* Test and clear port RWC bit */
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 port_bit)
+{
+ u32 temp;
+
+ temp = readl(port_array[port_id]);
+ if (temp & port_bit) {
+ temp = xhci_port_state_to_neutral(temp);
+ temp |= port_bit;
+ writel(temp, port_array[port_id]);
+ }
+}
+
+/* Updates Link Status for USB 2.1 port */
+static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg)
+{
+ if ((status_reg & PORT_PLS_MASK) == XDEV_U2)
+ *status |= USB_PORT_STAT_L1;
+}
+
+/* Updates Link Status for super Speed port */
+static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
+ u32 *status, u32 status_reg)
+{
+ u32 pls = status_reg & PORT_PLS_MASK;
+
+ /* resume state is a xHCI internal state.
+ * Do not report it to usb core.
+ */
+ if (pls == XDEV_RESUME)
+ return;
+
+ /* When the CAS bit is set then warm reset
+ * should be performed on port
+ */
+ if (status_reg & PORT_CAS) {
+ /* The CAS bit can be set while the port is
+ * in any link state.
+ * Only roothubs have CAS bit, so we
+ * pretend to be in compliance mode
+ * unless we're already in compliance
+ * or the inactive state.
+ */
+ if (pls != USB_SS_PORT_LS_COMP_MOD &&
+ pls != USB_SS_PORT_LS_SS_INACTIVE) {
+ pls = USB_SS_PORT_LS_COMP_MOD;
+ }
+ /* Return also connection bit -
+ * hub state machine resets port
+ * when this bit is set.
+ */
+ pls |= USB_PORT_STAT_CONNECTION;
+ } else {
+ /*
+ * If CAS bit isn't set but the Port is already at
+ * Compliance Mode, fake a connection so the USB core
+ * notices the Compliance state and resets the port.
+ * This resolves an issue generated by the SN65LVPE502CP
+ * in which sometimes the port enters compliance mode
+ * caused by a delay on the host-device negotiation.
+ */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ (pls == USB_SS_PORT_LS_COMP_MOD))
+ pls |= USB_PORT_STAT_CONNECTION;
+ }
+
+ /* update status field */
+ *status |= pls;
+}
+
+/*
+ * Function for Compliance Mode Quirk.
+ *
+ * This Function verifies if all xhc USB3 ports have entered U0, if so,
+ * the compliance mode timer is deleted. A port won't enter
+ * compliance mode if it has previously entered U0.
+ */
+static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
+ u16 wIndex)
+{
+ u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
+ bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
+
+ if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
+ return;
+
+ if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
+ xhci->port_status_u0 |= 1 << wIndex;
+ if (xhci->port_status_u0 == all_ports_seen_u0) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "All USB3 ports have entered U0 already!");
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Compliance Mode Recovery Timer Deleted.");
+ }
+ }
+}
+
+/*
+ * Converts a raw xHCI port status into the format that external USB 2.0 or USB
+ * 3.0 hubs use.
+ *
+ * Possible side effects:
+ * - Mark a port as being done with device resume,
+ * and ring the endpoint doorbells.
+ * - Stop the Synopsys redriver Compliance Mode polling.
+ * - Drop and reacquire the xHCI lock, in order to wait for port resume.
+ */
+static u32 xhci_get_port_status(struct usb_hcd *hcd,
+ struct xhci_bus_state *bus_state,
+ __le32 __iomem **port_array,
+ u16 wIndex, u32 raw_port_status,
+ unsigned long flags)
+ __releases(&xhci->lock)
+ __acquires(&xhci->lock)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ u32 status = 0;
+ int slot_id;
+
+ /* wPortChange bits */
+ if (raw_port_status & PORT_CSC)
+ status |= USB_PORT_STAT_C_CONNECTION << 16;
+ if (raw_port_status & PORT_PEC)
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+ if ((raw_port_status & PORT_OCC))
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+ if ((raw_port_status & PORT_RC))
+ status |= USB_PORT_STAT_C_RESET << 16;
+ /* USB3.0 only */
+ if (hcd->speed == HCD_USB3) {
+ if ((raw_port_status & PORT_PLC))
+ status |= USB_PORT_STAT_C_LINK_STATE << 16;
+ if ((raw_port_status & PORT_WRC))
+ status |= USB_PORT_STAT_C_BH_RESET << 16;
+ if ((raw_port_status & PORT_CEC))
+ status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
+ }
+
+ if (hcd->speed != HCD_USB3) {
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3
+ && (raw_port_status & PORT_POWER))
+ status |= USB_PORT_STAT_SUSPEND;
+ }
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
+ !DEV_SUPERSPEED(raw_port_status)) {
+ if ((raw_port_status & PORT_RESET) ||
+ !(raw_port_status & PORT_PE))
+ return 0xffffffff;
+ if (time_after_eq(jiffies,
+ bus_state->resume_done[wIndex])) {
+ int time_left;
+
+ xhci_dbg(xhci, "Resume USB2 port %d\n",
+ wIndex + 1);
+ bus_state->resume_done[wIndex] = 0;
+ clear_bit(wIndex, &bus_state->resuming_ports);
+
+ set_bit(wIndex, &bus_state->rexit_ports);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ time_left = wait_for_completion_timeout(
+ &bus_state->rexit_done[wIndex],
+ msecs_to_jiffies(
+ XHCI_MAX_REXIT_TIMEOUT));
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (time_left) {
+ slot_id = xhci_find_slot_id_by_port(hcd,
+ xhci, wIndex + 1);
+ if (!slot_id) {
+ xhci_dbg(xhci, "slot_id is zero\n");
+ return 0xffffffff;
+ }
+ xhci_ring_device(xhci, slot_id);
+ } else {
+ int port_status = readl(port_array[wIndex]);
+ xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
+ XHCI_MAX_REXIT_TIMEOUT,
+ port_status);
+ status |= USB_PORT_STAT_SUSPEND;
+ clear_bit(wIndex, &bus_state->rexit_ports);
+ }
+
+ bus_state->port_c_suspend |= 1 << wIndex;
+ bus_state->suspended_ports &= ~(1 << wIndex);
+ } else {
+ /*
+ * The resume has been signaling for less than
+ * 20ms. Report the port status as SUSPEND,
+ * let the usbcore check port status again
+ * and clear resume signaling later.
+ */
+ status |= USB_PORT_STAT_SUSPEND;
+ }
+ }
+ if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
+ && (raw_port_status & PORT_POWER)
+ && (bus_state->suspended_ports & (1 << wIndex))) {
+ bus_state->suspended_ports &= ~(1 << wIndex);
+ if (hcd->speed != HCD_USB3)
+ bus_state->port_c_suspend |= 1 << wIndex;
+ }
+ if (raw_port_status & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ status |= xhci_port_speed(raw_port_status);
+ }
+ if (raw_port_status & PORT_PE)
+ status |= USB_PORT_STAT_ENABLE;
+ if (raw_port_status & PORT_OC)
+ status |= USB_PORT_STAT_OVERCURRENT;
+ if (raw_port_status & PORT_RESET)
+ status |= USB_PORT_STAT_RESET;
+ if (raw_port_status & PORT_POWER) {
+ if (hcd->speed == HCD_USB3)
+ status |= USB_SS_PORT_STAT_POWER;
+ else
+ status |= USB_PORT_STAT_POWER;
+ }
+ /* Update Port Link State */
+ if (hcd->speed == HCD_USB3) {
+ xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status);
+ /*
+ * Verify if all USB3 Ports Have entered U0 already.
+ * Delete Compliance Mode Timer if so.
+ */
+ xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex);
+ } else {
+ xhci_hub_report_usb2_link_state(&status, raw_port_status);
+ }
+ if (bus_state->port_c_suspend & (1 << wIndex))
+ status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+
+ return status;
+}
+
+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 max_ports;
+ unsigned long flags;
+ u32 temp, status;
+ int retval = 0;
+ __le32 __iomem **port_array;
+ int slot_id;
+ struct xhci_bus_state *bus_state;
+ u16 link_state = 0;
+ u16 wake_mask = 0;
+ u16 timeout = 0;
+
+ max_ports = xhci_get_ports(hcd, &port_array);
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+
+ 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:
+ /* Check to make sure userspace is asking for the USB 3.0 hub
+ * descriptor for the USB 3.0 roothub. If not, we stall the
+ * endpoint, like external hubs do.
+ */
+ if (hcd->speed == HCD_USB3 &&
+ (wLength < USB_DT_SS_HUB_SIZE ||
+ wValue != (USB_DT_SS_HUB << 8))) {
+ xhci_dbg(xhci, "Wrong hub descriptor type for "
+ "USB 3.0 roothub.\n");
+ goto error;
+ }
+ xhci_hub_descriptor(hcd, xhci,
+ (struct usb_hub_descriptor *) buf);
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ if ((wValue & 0xff00) != (USB_DT_BOS << 8))
+ goto error;
+
+ if (hcd->speed != HCD_USB3)
+ goto error;
+
+ /* Set the U1 and U2 exit latencies. */
+ memcpy(buf, &usb_bos_descriptor,
+ USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
+ if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
+ temp = readl(&xhci->cap_regs->hcs_params3);
+ buf[12] = HCS_U1_LATENCY(temp);
+ put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+ }
+
+ /* Indicate whether the host has LTM support. */
+ temp = readl(&xhci->cap_regs->hcc_params);
+ if (HCC_LTC(temp))
+ buf[8] |= USB_LTM_SUPPORT;
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
+ case GetPortStatus:
+ if (!wIndex || wIndex > max_ports)
+ goto error;
+ wIndex--;
+ temp = readl(port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
+ status = xhci_get_port_status(hcd, bus_state, port_array,
+ wIndex, temp, flags);
+ if (status == 0xffffffff)
+ goto error;
+
+ xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n",
+ wIndex, temp);
+ xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
+
+ put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+ break;
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_LINK_STATE)
+ link_state = (wIndex & 0xff00) >> 3;
+ if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
+ wake_mask = wIndex & 0xff00;
+ /* The MSB of wIndex is the U1/U2 timeout */
+ timeout = (wIndex & 0xff00) >> 8;
+ wIndex &= 0xff;
+ if (!wIndex || wIndex > max_ports)
+ goto error;
+ wIndex--;
+ temp = readl(port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
+ temp = xhci_port_state_to_neutral(temp);
+ /* FIXME: What new port features do we need to support? */
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ temp = readl(port_array[wIndex]);
+ if ((temp & PORT_PLS_MASK) != XDEV_U0) {
+ /* Resume the port to U0 first */
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&xhci->lock, flags);
+ }
+ /* In spec software should not attempt to suspend
+ * a port unless the port reports that it is in the
+ * enabled (PED = ‘1’,PLS < ‘3’) state.
+ */
+ temp = readl(port_array[wIndex]);
+ if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
+ || (temp & PORT_PLS_MASK) >= XDEV_U3) {
+ xhci_warn(xhci, "USB core suspending device "
+ "not in U0/U1/U2.\n");
+ goto error;
+ }
+
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ wIndex + 1);
+ if (!slot_id) {
+ xhci_warn(xhci, "slot_id is zero\n");
+ goto error;
+ }
+ /* unlock to execute stop endpoint commands */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_stop_device(xhci, slot_id, 1);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(10); /* wait device to enter */
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ temp = readl(port_array[wIndex]);
+ bus_state->suspended_ports |= 1 << wIndex;
+ break;
+ case USB_PORT_FEAT_LINK_STATE:
+ temp = readl(port_array[wIndex]);
+
+ /* Disable port */
+ if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
+ xhci_dbg(xhci, "Disable port %d\n", wIndex);
+ temp = xhci_port_state_to_neutral(temp);
+ /*
+ * Clear all change bits, so that we get a new
+ * connection event.
+ */
+ temp |= PORT_CSC | PORT_PEC | PORT_WRC |
+ PORT_OCC | PORT_RC | PORT_PLC |
+ PORT_CEC;
+ writel(temp | PORT_PE, port_array[wIndex]);
+ temp = readl(port_array[wIndex]);
+ break;
+ }
+
+ /* Put link in RxDetect (enable port) */
+ if (link_state == USB_SS_PORT_LS_RX_DETECT) {
+ xhci_dbg(xhci, "Enable port %d\n", wIndex);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ link_state);
+ temp = readl(port_array[wIndex]);
+ break;
+ }
+
+ /* Software should not attempt to set
+ * port link state above '3' (U3) and the port
+ * must be enabled.
+ */
+ if ((temp & PORT_PE) == 0 ||
+ (link_state > USB_SS_PORT_LS_U3)) {
+ xhci_warn(xhci, "Cannot set link state.\n");
+ goto error;
+ }
+
+ if (link_state == USB_SS_PORT_LS_U3) {
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ wIndex + 1);
+ if (slot_id) {
+ /* unlock to execute stop endpoint
+ * commands */
+ spin_unlock_irqrestore(&xhci->lock,
+ flags);
+ xhci_stop_device(xhci, slot_id, 1);
+ spin_lock_irqsave(&xhci->lock, flags);
+ }
+ }
+
+ xhci_set_link_state(xhci, port_array, wIndex,
+ link_state);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20); /* wait device to enter */
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ temp = readl(port_array[wIndex]);
+ if (link_state == USB_SS_PORT_LS_U3)
+ bus_state->suspended_ports |= 1 << wIndex;
+ break;
+ 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, hub_wq will ignore the roothub events until
+ * the roothub is registered.
+ */
+ writel(temp | PORT_POWER, port_array[wIndex]);
+
+ temp = readl(port_array[wIndex]);
+ xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, true);
+ spin_lock_irqsave(&xhci->lock, flags);
+ break;
+ case USB_PORT_FEAT_RESET:
+ temp = (temp | PORT_RESET);
+ writel(temp, port_array[wIndex]);
+
+ temp = readl(port_array[wIndex]);
+ xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
+ break;
+ case USB_PORT_FEAT_REMOTE_WAKE_MASK:
+ xhci_set_remote_wake_mask(xhci, port_array,
+ wIndex, wake_mask);
+ temp = readl(port_array[wIndex]);
+ xhci_dbg(xhci, "set port remote wake mask, "
+ "actual port %d status = 0x%x\n",
+ wIndex, temp);
+ break;
+ case USB_PORT_FEAT_BH_PORT_RESET:
+ temp |= PORT_WR;
+ writel(temp, port_array[wIndex]);
+
+ temp = readl(port_array[wIndex]);
+ break;
+ case USB_PORT_FEAT_U1_TIMEOUT:
+ if (hcd->speed != HCD_USB3)
+ goto error;
+ temp = readl(port_array[wIndex] + PORTPMSC);
+ temp &= ~PORT_U1_TIMEOUT_MASK;
+ temp |= PORT_U1_TIMEOUT(timeout);
+ writel(temp, port_array[wIndex] + PORTPMSC);
+ break;
+ case USB_PORT_FEAT_U2_TIMEOUT:
+ if (hcd->speed != HCD_USB3)
+ goto error;
+ temp = readl(port_array[wIndex] + PORTPMSC);
+ temp &= ~PORT_U2_TIMEOUT_MASK;
+ temp |= PORT_U2_TIMEOUT(timeout);
+ writel(temp, port_array[wIndex] + PORTPMSC);
+ break;
+ default:
+ goto error;
+ }
+ /* unblock any posted writes */
+ temp = readl(port_array[wIndex]);
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > max_ports)
+ goto error;
+ wIndex--;
+ temp = readl(port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
+ /* FIXME: What new port features do we need to support? */
+ temp = xhci_port_state_to_neutral(temp);
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ temp = readl(port_array[wIndex]);
+ xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
+ xhci_dbg(xhci, "PORTSC %04x\n", temp);
+ if (temp & PORT_RESET)
+ goto error;
+ if ((temp & PORT_PLS_MASK) == XDEV_U3) {
+ if ((temp & PORT_PE) == 0)
+ goto error;
+
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_RESUME);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&xhci->lock, flags);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
+ }
+ bus_state->port_c_suspend |= 1 << wIndex;
+
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ wIndex + 1);
+ if (!slot_id) {
+ xhci_dbg(xhci, "slot_id is zero\n");
+ goto error;
+ }
+ xhci_ring_device(xhci, slot_id);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ bus_state->port_c_suspend &= ~(1 << wIndex);
+ case USB_PORT_FEAT_C_RESET:
+ case USB_PORT_FEAT_C_BH_PORT_RESET:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_PORT_LINK_STATE:
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
+ xhci_clear_port_change_bit(xhci, wValue, wIndex,
+ port_array[wIndex], temp);
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ xhci_disable_port(hcd, xhci, wIndex,
+ port_array[wIndex], temp);
+ break;
+ case USB_PORT_FEAT_POWER:
+ writel(temp & ~PORT_POWER, port_array[wIndex]);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, false);
+ spin_lock_irqsave(&xhci->lock, flags);
+ break;
+ default:
+ goto error;
+ }
+ 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.
+ *
+ * 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;
+ u32 mask;
+ int i, retval;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int max_ports;
+ __le32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
+ bool reset_change = false;
+
+ max_ports = xhci_get_ports(hcd, &port_array);
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+
+ /* Initial status is no changes */
+ retval = (max_ports + 8) / 8;
+ memset(buf, 0, retval);
+
+ /*
+ * Inform the usbcore about resume-in-progress by returning
+ * a non-zero value even if there are no status changes.
+ */
+ status = bus_state->resuming_ports;
+
+ mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* For each port, did anything change? If so, set that bit in buf. */
+ for (i = 0; i < max_ports; i++) {
+ temp = readl(port_array[i]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
+ if ((temp & mask) != 0 ||
+ (bus_state->port_c_suspend & 1 << i) ||
+ (bus_state->resume_done[i] && time_after_eq(
+ jiffies, bus_state->resume_done[i]))) {
+ buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
+ status = 1;
+ }
+ if ((temp & PORT_RC))
+ reset_change = true;
+ }
+ if (!status && !reset_change) {
+ xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return status ? retval : 0;
+}
+
+#ifdef CONFIG_PM
+
+int xhci_bus_suspend(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int max_ports, port_index;
+ __le32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
+ unsigned long flags;
+
+ max_ports = xhci_get_ports(hcd, &port_array);
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ if (bus_state->resuming_ports) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "suspend failed because "
+ "a port is resuming\n");
+ return -EBUSY;
+ }
+ }
+
+ port_index = max_ports;
+ bus_state->bus_suspended = 0;
+ while (port_index--) {
+ /* suspend the port if the port is not suspended */
+ u32 t1, t2;
+ int slot_id;
+
+ t1 = readl(port_array[port_index]);
+ t2 = xhci_port_state_to_neutral(t1);
+
+ if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
+ xhci_dbg(xhci, "port %d not suspended\n", port_index);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ port_index + 1);
+ if (slot_id) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_stop_device(xhci, slot_id, 1);
+ spin_lock_irqsave(&xhci->lock, flags);
+ }
+ t2 &= ~PORT_PLS_MASK;
+ t2 |= PORT_LINK_STROBE | XDEV_U3;
+ set_bit(port_index, &bus_state->bus_suspended);
+ }
+ /* USB core sets remote wake mask for USB 3.0 hubs,
+ * including the USB 3.0 roothub, but only if CONFIG_PM
+ * is enabled, so also enable remote wake here.
+ */
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ if (t1 & PORT_CONNECT) {
+ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+ t2 &= ~PORT_WKCONN_E;
+ } else {
+ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
+ t2 &= ~PORT_WKDISC_E;
+ }
+ } else
+ t2 &= ~PORT_WAKE_BITS;
+
+ t1 = xhci_port_state_to_neutral(t1);
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+ hcd->state = HC_STATE_SUSPENDED;
+ bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+}
+
+int xhci_bus_resume(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int max_ports, port_index;
+ __le32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
+ u32 temp;
+ unsigned long flags;
+
+ max_ports = xhci_get_ports(hcd, &port_array);
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+
+ if (time_before(jiffies, bus_state->next_statechange))
+ msleep(5);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ESHUTDOWN;
+ }
+
+ /* delay the irqs */
+ temp = readl(&xhci->op_regs->command);
+ temp &= ~CMD_EIE;
+ writel(temp, &xhci->op_regs->command);
+
+ port_index = max_ports;
+ while (port_index--) {
+ /* Check whether need resume ports. If needed
+ resume port and disable remote wakeup */
+ u32 temp;
+ int slot_id;
+
+ temp = readl(port_array[port_index]);
+ if (DEV_SUPERSPEED(temp))
+ temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
+ else
+ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+ if (test_bit(port_index, &bus_state->bus_suspended) &&
+ (temp & PORT_PLS_MASK)) {
+ if (DEV_SUPERSPEED(temp)) {
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_U0);
+ } else {
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_RESUME);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_U0);
+ }
+ /* wait for the port to enter U0 and report port link
+ * state change.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Clear PLC */
+ xhci_test_and_clear_bit(xhci, port_array, port_index,
+ PORT_PLC);
+
+ slot_id = xhci_find_slot_id_by_port(hcd,
+ xhci, port_index + 1);
+ if (slot_id)
+ xhci_ring_device(xhci, slot_id);
+ } else
+ writel(temp, port_array[port_index]);
+ }
+
+ (void) readl(&xhci->op_regs->command);
+
+ bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
+ /* re-enable irqs */
+ temp = readl(&xhci->op_regs->command);
+ temp |= CMD_EIE;
+ writel(temp, &xhci->op_regs->command);
+ temp = readl(&xhci->op_regs->command);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
new file mode 100644
index 000000000..3e442f77a
--- /dev/null
+++ b/drivers/usb/host/xhci-mem.c
@@ -0,0 +1,2548 @@
+/*
+ * 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/slab.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include "xhci.h"
+#include "xhci-trace.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,
+ unsigned int cycle_state, gfp_t flags)
+{
+ struct xhci_segment *seg;
+ dma_addr_t dma;
+ int i;
+
+ seg = kzalloc(sizeof *seg, flags);
+ if (!seg)
+ return NULL;
+
+ seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
+ if (!seg->trbs) {
+ kfree(seg);
+ return NULL;
+ }
+
+ memset(seg->trbs, 0, TRB_SEGMENT_SIZE);
+ /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
+ if (cycle_state == 0) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++)
+ seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
+ }
+ seg->dma = dma;
+ seg->next = NULL;
+
+ return seg;
+}
+
+static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
+{
+ if (seg->trbs) {
+ dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
+ seg->trbs = NULL;
+ }
+ kfree(seg);
+}
+
+static void xhci_free_segments_for_ring(struct xhci_hcd *xhci,
+ struct xhci_segment *first)
+{
+ struct xhci_segment *seg;
+
+ seg = first->next;
+ while (seg != first) {
+ struct xhci_segment *next = seg->next;
+ xhci_segment_free(xhci, seg);
+ seg = next;
+ }
+ xhci_segment_free(xhci, first);
+}
+
+/*
+ * 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, enum xhci_ring_type type)
+{
+ u32 val;
+
+ if (!prev || !next)
+ return;
+ prev->next = next;
+ if (type != TYPE_EVENT) {
+ prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr =
+ cpu_to_le64(next->dma);
+
+ /* Set the last TRB in the segment to have a TRB type ID of Link TRB */
+ val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
+ val &= ~TRB_TYPE_BITMASK;
+ val |= TRB_TYPE(TRB_LINK);
+ /* Always set the chain bit with 0.95 hardware */
+ /* Set chain bit for isoc rings on AMD 0.96 host */
+ if (xhci_link_trb_quirk(xhci) ||
+ (type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ val |= TRB_CHAIN;
+ prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
+ }
+}
+
+/*
+ * Link the ring to the new segments.
+ * Set Toggle Cycle for the new ring if needed.
+ */
+static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ struct xhci_segment *first, struct xhci_segment *last,
+ unsigned int num_segs)
+{
+ struct xhci_segment *next;
+
+ if (!ring || !first || !last)
+ return;
+
+ next = ring->enq_seg->next;
+ xhci_link_segments(xhci, ring->enq_seg, first, ring->type);
+ xhci_link_segments(xhci, last, next, ring->type);
+ ring->num_segs += num_segs;
+ ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
+
+ if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) {
+ ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control
+ &= ~cpu_to_le32(LINK_TOGGLE);
+ last->trbs[TRBS_PER_SEGMENT-1].link.control
+ |= cpu_to_le32(LINK_TOGGLE);
+ ring->last_seg = last;
+ }
+}
+
+/*
+ * We need a radix tree for mapping physical addresses of TRBs to which stream
+ * ID they belong to. We need to do this because the host controller won't tell
+ * us which stream ring the TRB came from. We could store the stream ID in an
+ * event data TRB, but that doesn't help us for the cancellation case, since the
+ * endpoint may stop before it reaches that event data TRB.
+ *
+ * The radix tree maps the upper portion of the TRB DMA address to a ring
+ * segment that has the same upper portion of DMA addresses. For example, say I
+ * have segments of size 1KB, that are always 1KB aligned. A segment may
+ * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
+ * key to the stream ID is 0x43244. I can use the DMA address of the TRB to
+ * pass the radix tree a key to get the right stream ID:
+ *
+ * 0x10c90fff >> 10 = 0x43243
+ * 0x10c912c0 >> 10 = 0x43244
+ * 0x10c91400 >> 10 = 0x43245
+ *
+ * Obviously, only those TRBs with DMA addresses that are within the segment
+ * will make the radix tree return the stream ID for that ring.
+ *
+ * Caveats for the radix tree:
+ *
+ * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
+ * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
+ * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
+ * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
+ * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
+ * extended systems (where the DMA address can be bigger than 32-bits),
+ * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
+ */
+static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct xhci_ring *ring,
+ struct xhci_segment *seg,
+ gfp_t mem_flags)
+{
+ unsigned long key;
+ int ret;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+ /* Skip any segments that were already added. */
+ if (radix_tree_lookup(trb_address_map, key))
+ return 0;
+
+ ret = radix_tree_maybe_preload(mem_flags);
+ if (ret)
+ return ret;
+ ret = radix_tree_insert(trb_address_map,
+ key, ring);
+ radix_tree_preload_end();
+ return ret;
+}
+
+static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct xhci_segment *seg)
+{
+ unsigned long key;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+ if (radix_tree_lookup(trb_address_map, key))
+ radix_tree_delete(trb_address_map, key);
+}
+
+static int xhci_update_stream_segment_mapping(
+ struct radix_tree_root *trb_address_map,
+ struct xhci_ring *ring,
+ struct xhci_segment *first_seg,
+ struct xhci_segment *last_seg,
+ gfp_t mem_flags)
+{
+ struct xhci_segment *seg;
+ struct xhci_segment *failed_seg;
+ int ret;
+
+ if (WARN_ON_ONCE(trb_address_map == NULL))
+ return 0;
+
+ seg = first_seg;
+ do {
+ ret = xhci_insert_segment_mapping(trb_address_map,
+ ring, seg, mem_flags);
+ if (ret)
+ goto remove_streams;
+ if (seg == last_seg)
+ return 0;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return 0;
+
+remove_streams:
+ failed_seg = seg;
+ seg = first_seg;
+ do {
+ xhci_remove_segment_mapping(trb_address_map, seg);
+ if (seg == failed_seg)
+ return ret;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return ret;
+}
+
+static void xhci_remove_stream_mapping(struct xhci_ring *ring)
+{
+ struct xhci_segment *seg;
+
+ if (WARN_ON_ONCE(ring->trb_address_map == NULL))
+ return;
+
+ seg = ring->first_seg;
+ do {
+ xhci_remove_segment_mapping(ring->trb_address_map, seg);
+ seg = seg->next;
+ } while (seg != ring->first_seg);
+}
+
+static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags)
+{
+ return xhci_update_stream_segment_mapping(ring->trb_address_map, ring,
+ ring->first_seg, ring->last_seg, mem_flags);
+}
+
+/* XXX: Do we need the hcd structure in all these functions? */
+void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+ if (!ring)
+ return;
+
+ if (ring->first_seg) {
+ if (ring->type == TYPE_STREAM)
+ xhci_remove_stream_mapping(ring);
+ xhci_free_segments_for_ring(xhci, ring->first_seg);
+ }
+
+ kfree(ring);
+}
+
+static void xhci_initialize_ring_info(struct xhci_ring *ring,
+ unsigned int cycle_state)
+{
+ /* 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.
+ *
+ * New rings are initialized with cycle state equal to 1; if we are
+ * handling ring expansion, set the cycle state equal to the old ring.
+ */
+ ring->cycle_state = cycle_state;
+ /* Not necessary for new rings, but needed for re-initialized rings */
+ ring->enq_updates = 0;
+ ring->deq_updates = 0;
+
+ /*
+ * Each segment has a link TRB, and leave an extra TRB for SW
+ * accounting purpose
+ */
+ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+}
+
+/* Allocate segments and link them for a ring */
+static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
+ struct xhci_segment **first, struct xhci_segment **last,
+ unsigned int num_segs, unsigned int cycle_state,
+ enum xhci_ring_type type, gfp_t flags)
+{
+ struct xhci_segment *prev;
+
+ prev = xhci_segment_alloc(xhci, cycle_state, flags);
+ if (!prev)
+ return -ENOMEM;
+ num_segs--;
+
+ *first = prev;
+ while (num_segs > 0) {
+ struct xhci_segment *next;
+
+ next = xhci_segment_alloc(xhci, cycle_state, flags);
+ if (!next) {
+ prev = *first;
+ while (prev) {
+ next = prev->next;
+ xhci_segment_free(xhci, prev);
+ prev = next;
+ }
+ return -ENOMEM;
+ }
+ xhci_link_segments(xhci, prev, next, type);
+
+ prev = next;
+ num_segs--;
+ }
+ xhci_link_segments(xhci, prev, *first, type);
+ *last = prev;
+
+ return 0;
+}
+
+/**
+ * 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, unsigned int cycle_state,
+ enum xhci_ring_type type, gfp_t flags)
+{
+ struct xhci_ring *ring;
+ int ret;
+
+ ring = kzalloc(sizeof *(ring), flags);
+ if (!ring)
+ return NULL;
+
+ ring->num_segs = num_segs;
+ INIT_LIST_HEAD(&ring->td_list);
+ ring->type = type;
+ if (num_segs == 0)
+ return ring;
+
+ ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
+ &ring->last_seg, num_segs, cycle_state, type, flags);
+ if (ret)
+ goto fail;
+
+ /* Only event ring does not use link TRB */
+ if (type != TYPE_EVENT) {
+ /* See section 4.9.2.1 and 6.4.4.1 */
+ ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |=
+ cpu_to_le32(LINK_TOGGLE);
+ }
+ xhci_initialize_ring_info(ring, cycle_state);
+ return ring;
+
+fail:
+ kfree(ring);
+ return NULL;
+}
+
+void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ unsigned int ep_index)
+{
+ int rings_cached;
+
+ rings_cached = virt_dev->num_rings_cached;
+ if (rings_cached < XHCI_MAX_RINGS_CACHED) {
+ virt_dev->ring_cache[rings_cached] =
+ virt_dev->eps[ep_index].ring;
+ virt_dev->num_rings_cached++;
+ xhci_dbg(xhci, "Cached old ring, "
+ "%d ring%s cached\n",
+ virt_dev->num_rings_cached,
+ (virt_dev->num_rings_cached > 1) ? "s" : "");
+ } else {
+ xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
+ xhci_dbg(xhci, "Ring cache full (%d rings), "
+ "freeing ring\n",
+ virt_dev->num_rings_cached);
+ }
+ virt_dev->eps[ep_index].ring = NULL;
+}
+
+/* Zero an endpoint ring (except for link TRBs) and move the enqueue and dequeue
+ * pointers to the beginning of the ring.
+ */
+static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, unsigned int cycle_state,
+ enum xhci_ring_type type)
+{
+ struct xhci_segment *seg = ring->first_seg;
+ int i;
+
+ do {
+ memset(seg->trbs, 0,
+ sizeof(union xhci_trb)*TRBS_PER_SEGMENT);
+ if (cycle_state == 0) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++)
+ seg->trbs[i].link.control |=
+ cpu_to_le32(TRB_CYCLE);
+ }
+ /* All endpoint rings have link TRBs */
+ xhci_link_segments(xhci, seg, seg->next, type);
+ seg = seg->next;
+ } while (seg != ring->first_seg);
+ ring->type = type;
+ xhci_initialize_ring_info(ring, cycle_state);
+ /* td list should be empty since all URBs have been cancelled,
+ * but just in case...
+ */
+ INIT_LIST_HEAD(&ring->td_list);
+}
+
+/*
+ * Expand an existing ring.
+ * Look for a cached ring or allocate a new ring which has same segment numbers
+ * and link the two rings.
+ */
+int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs, gfp_t flags)
+{
+ struct xhci_segment *first;
+ struct xhci_segment *last;
+ unsigned int num_segs;
+ unsigned int num_segs_needed;
+ int ret;
+
+ num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) /
+ (TRBS_PER_SEGMENT - 1);
+
+ /* Allocate number of segments we needed, or double the ring size */
+ num_segs = ring->num_segs > num_segs_needed ?
+ ring->num_segs : num_segs_needed;
+
+ ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
+ num_segs, ring->cycle_state, ring->type, flags);
+ if (ret)
+ return -ENOMEM;
+
+ if (ring->type == TYPE_STREAM)
+ ret = xhci_update_stream_segment_mapping(ring->trb_address_map,
+ ring, first, last, flags);
+ if (ret) {
+ struct xhci_segment *next;
+ do {
+ next = first->next;
+ xhci_segment_free(xhci, first);
+ if (first == last)
+ break;
+ first = next;
+ } while (true);
+ return ret;
+ }
+
+ xhci_link_rings(xhci, ring, first, last, num_segs);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
+ "ring expansion succeed, now has %d segments",
+ ring->num_segs);
+
+ return 0;
+}
+
+#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+
+static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
+ int type, gfp_t flags)
+{
+ struct xhci_container_ctx *ctx;
+
+ if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT))
+ return NULL;
+
+ ctx = kzalloc(sizeof(*ctx), flags);
+ if (!ctx)
+ return NULL;
+
+ ctx->type = type;
+ ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024;
+ if (type == XHCI_CTX_TYPE_INPUT)
+ ctx->size += CTX_SIZE(xhci->hcc_params);
+
+ ctx->bytes = dma_pool_alloc(xhci->device_pool, flags, &ctx->dma);
+ if (!ctx->bytes) {
+ kfree(ctx);
+ return NULL;
+ }
+ memset(ctx->bytes, 0, ctx->size);
+ return ctx;
+}
+
+static void xhci_free_container_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma);
+ kfree(ctx);
+}
+
+struct xhci_input_control_ctx *xhci_get_input_control_ctx(
+ struct xhci_container_ctx *ctx)
+{
+ if (ctx->type != XHCI_CTX_TYPE_INPUT)
+ return NULL;
+
+ return (struct xhci_input_control_ctx *)ctx->bytes;
+}
+
+struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (ctx->type == XHCI_CTX_TYPE_DEVICE)
+ return (struct xhci_slot_ctx *)ctx->bytes;
+
+ return (struct xhci_slot_ctx *)
+ (ctx->bytes + CTX_SIZE(xhci->hcc_params));
+}
+
+struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx,
+ unsigned int ep_index)
+{
+ /* increment ep index by offset of start of ep ctx array */
+ ep_index++;
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ ep_index++;
+
+ return (struct xhci_ep_ctx *)
+ (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params)));
+}
+
+
+/***************** Streams structures manipulation *************************/
+
+static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
+ unsigned int num_stream_ctxs,
+ struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
+{
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
+
+ if (size > MEDIUM_STREAM_ARRAY_SIZE)
+ dma_free_coherent(dev, size,
+ stream_ctx, dma);
+ else if (size <= SMALL_STREAM_ARRAY_SIZE)
+ return dma_pool_free(xhci->small_streams_pool,
+ stream_ctx, dma);
+ else
+ return dma_pool_free(xhci->medium_streams_pool,
+ stream_ctx, dma);
+}
+
+/*
+ * The stream context array for each endpoint with bulk streams enabled can
+ * vary in size, based on:
+ * - how many streams the endpoint supports,
+ * - the maximum primary stream array size the host controller supports,
+ * - and how many streams the device driver asks for.
+ *
+ * The stream context array must be a power of 2, and can be as small as
+ * 64 bytes or as large as 1MB.
+ */
+static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
+ unsigned int num_stream_ctxs, dma_addr_t *dma,
+ gfp_t mem_flags)
+{
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
+
+ if (size > MEDIUM_STREAM_ARRAY_SIZE)
+ return dma_alloc_coherent(dev, size,
+ dma, mem_flags);
+ else if (size <= SMALL_STREAM_ARRAY_SIZE)
+ return dma_pool_alloc(xhci->small_streams_pool,
+ mem_flags, dma);
+ else
+ return dma_pool_alloc(xhci->medium_streams_pool,
+ mem_flags, dma);
+}
+
+struct xhci_ring *xhci_dma_to_transfer_ring(
+ struct xhci_virt_ep *ep,
+ u64 address)
+{
+ if (ep->ep_state & EP_HAS_STREAMS)
+ return radix_tree_lookup(&ep->stream_info->trb_address_map,
+ address >> TRB_SEGMENT_SHIFT);
+ return ep->ring;
+}
+
+struct xhci_ring *xhci_stream_id_to_ring(
+ struct xhci_virt_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct xhci_virt_ep *ep = &dev->eps[ep_index];
+
+ if (stream_id == 0)
+ return ep->ring;
+ if (!ep->stream_info)
+ return NULL;
+
+ if (stream_id > ep->stream_info->num_streams)
+ return NULL;
+ return ep->stream_info->stream_rings[stream_id];
+}
+
+/*
+ * Change an endpoint's internal structure so it supports stream IDs. The
+ * number of requested streams includes stream 0, which cannot be used by device
+ * drivers.
+ *
+ * The number of stream contexts in the stream context array may be bigger than
+ * the number of streams the driver wants to use. This is because the number of
+ * stream context array entries must be a power of two.
+ */
+struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
+ unsigned int num_stream_ctxs,
+ unsigned int num_streams, gfp_t mem_flags)
+{
+ struct xhci_stream_info *stream_info;
+ u32 cur_stream;
+ struct xhci_ring *cur_ring;
+ u64 addr;
+ int ret;
+
+ xhci_dbg(xhci, "Allocating %u streams and %u "
+ "stream context array entries.\n",
+ num_streams, num_stream_ctxs);
+ if (xhci->cmd_ring_reserved_trbs == MAX_RSVD_CMD_TRBS) {
+ xhci_dbg(xhci, "Command ring has no reserved TRBs available\n");
+ return NULL;
+ }
+ xhci->cmd_ring_reserved_trbs++;
+
+ stream_info = kzalloc(sizeof(struct xhci_stream_info), mem_flags);
+ if (!stream_info)
+ goto cleanup_trbs;
+
+ stream_info->num_streams = num_streams;
+ stream_info->num_stream_ctxs = num_stream_ctxs;
+
+ /* Initialize the array of virtual pointers to stream rings. */
+ stream_info->stream_rings = kzalloc(
+ sizeof(struct xhci_ring *)*num_streams,
+ mem_flags);
+ if (!stream_info->stream_rings)
+ goto cleanup_info;
+
+ /* Initialize the array of DMA addresses for stream rings for the HW. */
+ stream_info->stream_ctx_array = xhci_alloc_stream_ctx(xhci,
+ num_stream_ctxs, &stream_info->ctx_array_dma,
+ mem_flags);
+ if (!stream_info->stream_ctx_array)
+ goto cleanup_ctx;
+ memset(stream_info->stream_ctx_array, 0,
+ sizeof(struct xhci_stream_ctx)*num_stream_ctxs);
+
+ /* Allocate everything needed to free the stream rings later */
+ stream_info->free_streams_command =
+ xhci_alloc_command(xhci, true, true, mem_flags);
+ if (!stream_info->free_streams_command)
+ goto cleanup_ctx;
+
+ INIT_RADIX_TREE(&stream_info->trb_address_map, GFP_ATOMIC);
+
+ /* Allocate rings for all the streams that the driver will use,
+ * and add their segment DMA addresses to the radix tree.
+ * Stream 0 is reserved.
+ */
+ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
+ stream_info->stream_rings[cur_stream] =
+ xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, mem_flags);
+ cur_ring = stream_info->stream_rings[cur_stream];
+ if (!cur_ring)
+ goto cleanup_rings;
+ cur_ring->stream_id = cur_stream;
+ cur_ring->trb_address_map = &stream_info->trb_address_map;
+ /* Set deq ptr, cycle bit, and stream context type */
+ addr = cur_ring->first_seg->dma |
+ SCT_FOR_CTX(SCT_PRI_TR) |
+ cur_ring->cycle_state;
+ stream_info->stream_ctx_array[cur_stream].stream_ring =
+ cpu_to_le64(addr);
+ xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
+ cur_stream, (unsigned long long) addr);
+
+ ret = xhci_update_stream_mapping(cur_ring, mem_flags);
+ if (ret) {
+ xhci_ring_free(xhci, cur_ring);
+ stream_info->stream_rings[cur_stream] = NULL;
+ goto cleanup_rings;
+ }
+ }
+ /* Leave the other unused stream ring pointers in the stream context
+ * array initialized to zero. This will cause the xHC to give us an
+ * error if the device asks for a stream ID we don't have setup (if it
+ * was any other way, the host controller would assume the ring is
+ * "empty" and wait forever for data to be queued to that stream ID).
+ */
+
+ return stream_info;
+
+cleanup_rings:
+ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
+ cur_ring = stream_info->stream_rings[cur_stream];
+ if (cur_ring) {
+ xhci_ring_free(xhci, cur_ring);
+ stream_info->stream_rings[cur_stream] = NULL;
+ }
+ }
+ xhci_free_command(xhci, stream_info->free_streams_command);
+cleanup_ctx:
+ kfree(stream_info->stream_rings);
+cleanup_info:
+ kfree(stream_info);
+cleanup_trbs:
+ xhci->cmd_ring_reserved_trbs--;
+ return NULL;
+}
+/*
+ * Sets the MaxPStreams field and the Linear Stream Array field.
+ * Sets the dequeue pointer to the stream context array.
+ */
+void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
+ struct xhci_ep_ctx *ep_ctx,
+ struct xhci_stream_info *stream_info)
+{
+ u32 max_primary_streams;
+ /* MaxPStreams is the number of stream context array entries, not the
+ * number we're actually using. Must be in 2^(MaxPstreams + 1) format.
+ * fls(0) = 0, fls(0x1) = 1, fls(0x10) = 2, fls(0x100) = 3, etc.
+ */
+ max_primary_streams = fls(stream_info->num_stream_ctxs) - 2;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Setting number of stream ctx array entries to %u",
+ 1 << (max_primary_streams + 1));
+ ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK);
+ ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams)
+ | EP_HAS_LSA);
+ ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma);
+}
+
+/*
+ * Sets the MaxPStreams field and the Linear Stream Array field to 0.
+ * Reinstalls the "normal" endpoint ring (at its previous dequeue mark,
+ * not at the beginning of the ring).
+ */
+void xhci_setup_no_streams_ep_input_ctx(struct xhci_ep_ctx *ep_ctx,
+ struct xhci_virt_ep *ep)
+{
+ dma_addr_t addr;
+ ep_ctx->ep_info &= cpu_to_le32(~(EP_MAXPSTREAMS_MASK | EP_HAS_LSA));
+ addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue);
+ ep_ctx->deq = cpu_to_le64(addr | ep->ring->cycle_state);
+}
+
+/* Frees all stream contexts associated with the endpoint,
+ *
+ * Caller should fix the endpoint context streams fields.
+ */
+void xhci_free_stream_info(struct xhci_hcd *xhci,
+ struct xhci_stream_info *stream_info)
+{
+ int cur_stream;
+ struct xhci_ring *cur_ring;
+
+ if (!stream_info)
+ return;
+
+ for (cur_stream = 1; cur_stream < stream_info->num_streams;
+ cur_stream++) {
+ cur_ring = stream_info->stream_rings[cur_stream];
+ if (cur_ring) {
+ xhci_ring_free(xhci, cur_ring);
+ stream_info->stream_rings[cur_stream] = NULL;
+ }
+ }
+ xhci_free_command(xhci, stream_info->free_streams_command);
+ xhci->cmd_ring_reserved_trbs--;
+ if (stream_info->stream_ctx_array)
+ xhci_free_stream_ctx(xhci,
+ stream_info->num_stream_ctxs,
+ stream_info->stream_ctx_array,
+ stream_info->ctx_array_dma);
+
+ kfree(stream_info->stream_rings);
+ kfree(stream_info);
+}
+
+
+/***************** Device context manipulation *************************/
+
+static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
+ struct xhci_virt_ep *ep)
+{
+ setup_timer(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog,
+ (unsigned long)ep);
+ ep->xhci = xhci;
+}
+
+static void xhci_free_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int slot_id)
+{
+ struct list_head *tt_list_head;
+ struct xhci_tt_bw_info *tt_info, *next;
+ bool slot_found = false;
+
+ /* If the device never made it past the Set Address stage,
+ * it may not have the real_port set correctly.
+ */
+ if (virt_dev->real_port == 0 ||
+ virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) {
+ xhci_dbg(xhci, "Bad real port.\n");
+ return;
+ }
+
+ tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts);
+ list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
+ /* Multi-TT hubs will have more than one entry */
+ if (tt_info->slot_id == slot_id) {
+ slot_found = true;
+ list_del(&tt_info->tt_list);
+ kfree(tt_info);
+ } else if (slot_found) {
+ break;
+ }
+ }
+}
+
+int xhci_alloc_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags)
+{
+ struct xhci_tt_bw_info *tt_info;
+ unsigned int num_ports;
+ int i, j;
+
+ if (!tt->multi)
+ num_ports = 1;
+ else
+ num_ports = hdev->maxchild;
+
+ for (i = 0; i < num_ports; i++, tt_info++) {
+ struct xhci_interval_bw_table *bw_table;
+
+ tt_info = kzalloc(sizeof(*tt_info), mem_flags);
+ if (!tt_info)
+ goto free_tts;
+ INIT_LIST_HEAD(&tt_info->tt_list);
+ list_add(&tt_info->tt_list,
+ &xhci->rh_bw[virt_dev->real_port - 1].tts);
+ tt_info->slot_id = virt_dev->udev->slot_id;
+ if (tt->multi)
+ tt_info->ttport = i+1;
+ bw_table = &tt_info->bw_table;
+ for (j = 0; j < XHCI_MAX_INTERVAL; j++)
+ INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
+ }
+ return 0;
+
+free_tts:
+ xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id);
+ return -ENOMEM;
+}
+
+
+/* All the xhci_tds in the ring's TD list should be freed at this point.
+ * Should be called with xhci->lock held if there is any chance the TT lists
+ * will be manipulated by the configure endpoint, allocate device, or update
+ * hub functions while this function is removing the TT entries from the list.
+ */
+void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *dev;
+ int i;
+ int old_active_eps = 0;
+
+ /* Slot ID 0 is reserved */
+ if (slot_id == 0 || !xhci->devs[slot_id])
+ return;
+
+ dev = xhci->devs[slot_id];
+ xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ if (!dev)
+ return;
+
+ if (dev->tt_info)
+ old_active_eps = dev->tt_info->active_eps;
+
+ for (i = 0; i < 31; ++i) {
+ if (dev->eps[i].ring)
+ xhci_ring_free(xhci, dev->eps[i].ring);
+ if (dev->eps[i].stream_info)
+ xhci_free_stream_info(xhci,
+ dev->eps[i].stream_info);
+ /* Endpoints on the TT/root port lists should have been removed
+ * when usb_disable_device() was called for the device.
+ * We can't drop them anyway, because the udev might have gone
+ * away by this point, and we can't tell what speed it was.
+ */
+ if (!list_empty(&dev->eps[i].bw_endpoint_list))
+ xhci_warn(xhci, "Slot %u endpoint %u "
+ "not removed from BW list!\n",
+ slot_id, i);
+ }
+ /* If this is a hub, free the TT(s) from the TT list */
+ xhci_free_tt_info(xhci, dev, slot_id);
+ /* If necessary, update the number of active TTs on this root port */
+ xhci_update_tt_active_eps(xhci, dev, old_active_eps);
+
+ if (dev->ring_cache) {
+ for (i = 0; i < dev->num_rings_cached; i++)
+ xhci_ring_free(xhci, dev->ring_cache[i]);
+ kfree(dev->ring_cache);
+ }
+
+ if (dev->in_ctx)
+ xhci_free_container_ctx(xhci, dev->in_ctx);
+ if (dev->out_ctx)
+ xhci_free_container_ctx(xhci, dev->out_ctx);
+
+ kfree(xhci->devs[slot_id]);
+ xhci->devs[slot_id] = NULL;
+}
+
+int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
+ struct usb_device *udev, gfp_t flags)
+{
+ struct xhci_virt_device *dev;
+ int i;
+
+ /* 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 = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags);
+ if (!dev->out_ctx)
+ goto fail;
+
+ xhci_dbg(xhci, "Slot %d output ctx = 0x%llx (dma)\n", slot_id,
+ (unsigned long long)dev->out_ctx->dma);
+
+ /* Allocate the (input) device context for address device command */
+ dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags);
+ if (!dev->in_ctx)
+ goto fail;
+
+ xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id,
+ (unsigned long long)dev->in_ctx->dma);
+
+ /* Initialize the cancellation list and watchdog timers for each ep */
+ for (i = 0; i < 31; i++) {
+ xhci_init_endpoint_timer(xhci, &dev->eps[i]);
+ INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
+ INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
+ }
+
+ /* Allocate endpoint 0 ring */
+ dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, flags);
+ if (!dev->eps[0].ring)
+ goto fail;
+
+ /* Allocate pointers to the ring cache */
+ dev->ring_cache = kzalloc(
+ sizeof(struct xhci_ring *)*XHCI_MAX_RINGS_CACHED,
+ flags);
+ if (!dev->ring_cache)
+ goto fail;
+ dev->num_rings_cached = 0;
+
+ init_completion(&dev->cmd_completion);
+ dev->udev = udev;
+
+ /* Point to output device context in dcbaa. */
+ xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma);
+ xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
+ slot_id,
+ &xhci->dcbaa->dev_context_ptrs[slot_id],
+ le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
+
+ return 1;
+fail:
+ xhci_free_virt_device(xhci, slot_id);
+ return 0;
+}
+
+void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
+ struct usb_device *udev)
+{
+ struct xhci_virt_device *virt_dev;
+ struct xhci_ep_ctx *ep0_ctx;
+ struct xhci_ring *ep_ring;
+
+ virt_dev = xhci->devs[udev->slot_id];
+ ep0_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, 0);
+ ep_ring = virt_dev->eps[0].ring;
+ /*
+ * FIXME we don't keep track of the dequeue pointer very well after a
+ * Set TR dequeue pointer, so we're setting the dequeue pointer of the
+ * host to our enqueue pointer. This should only be called after a
+ * configured device has reset, so all control transfers should have
+ * been completed or cancelled before the reset.
+ */
+ ep0_ctx->deq = cpu_to_le64(xhci_trb_virt_to_dma(ep_ring->enq_seg,
+ ep_ring->enqueue)
+ | ep_ring->cycle_state);
+}
+
+/*
+ * The xHCI roothub may have ports of differing speeds in any order in the port
+ * status registers. xhci->port_array provides an array of the port speed for
+ * each offset into the port status registers.
+ *
+ * The xHCI hardware wants to know the roothub port number that the USB device
+ * is attached to (or the roothub port its ancestor hub is attached to). All we
+ * know is the index of that port under either the USB 2.0 or the USB 3.0
+ * roothub, but that doesn't give us the real index into the HW port status
+ * registers. Call xhci_find_raw_port_number() to get real index.
+ */
+static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
+ struct usb_device *udev)
+{
+ struct usb_device *top_dev;
+ struct usb_hcd *hcd;
+
+ if (udev->speed == USB_SPEED_SUPER)
+ hcd = xhci->shared_hcd;
+ else
+ hcd = xhci->main_hcd;
+
+ for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
+ top_dev = top_dev->parent)
+ /* Found device below root hub */;
+
+ return xhci_find_raw_port_number(hcd, top_dev->portnum);
+}
+
+/* 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 xhci_slot_ctx *slot_ctx;
+ u32 port_num;
+ u32 max_packets;
+ 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 = xhci_get_ep_ctx(xhci, dev->in_ctx, 0);
+ slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx);
+
+ /* 3) Only the control endpoint is valid - one endpoint context */
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | udev->route);
+ switch (udev->speed) {
+ case USB_SPEED_SUPER:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
+ max_packets = MAX_PACKET(512);
+ break;
+ case USB_SPEED_HIGH:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
+ max_packets = MAX_PACKET(64);
+ break;
+ /* USB core guesses at a 64-byte max packet first for FS devices */
+ case USB_SPEED_FULL:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
+ max_packets = MAX_PACKET(64);
+ break;
+ case USB_SPEED_LOW:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
+ max_packets = MAX_PACKET(8);
+ break;
+ case USB_SPEED_WIRELESS:
+ xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
+ return -EINVAL;
+ break;
+ default:
+ /* Speed was set earlier, this shouldn't happen. */
+ return -EINVAL;
+ }
+ /* Find the root hub port this device is under */
+ port_num = xhci_find_real_port_number(xhci, udev);
+ if (!port_num)
+ return -EINVAL;
+ slot_ctx->dev_info2 |= cpu_to_le32(ROOT_HUB_PORT(port_num));
+ /* Set the port number in the virtual_device to the faked port number */
+ for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
+ top_dev = top_dev->parent)
+ /* Found device below root hub */;
+ dev->fake_port = top_dev->portnum;
+ dev->real_port = port_num;
+ xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num);
+ xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port);
+
+ /* Find the right bandwidth table that this device will be a part of.
+ * If this is a full speed device attached directly to a root port (or a
+ * decendent of one), it counts as a primary bandwidth domain, not a
+ * secondary bandwidth domain under a TT. An xhci_tt_info structure
+ * will never be created for the HS root hub.
+ */
+ if (!udev->tt || !udev->tt->hub->parent) {
+ dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table;
+ } else {
+ struct xhci_root_port_bw_info *rh_bw;
+ struct xhci_tt_bw_info *tt_bw;
+
+ rh_bw = &xhci->rh_bw[port_num - 1];
+ /* Find the right TT. */
+ list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) {
+ if (tt_bw->slot_id != udev->tt->hub->slot_id)
+ continue;
+
+ if (!dev->udev->tt->multi ||
+ (udev->tt->multi &&
+ tt_bw->ttport == dev->udev->ttport)) {
+ dev->bw_table = &tt_bw->bw_table;
+ dev->tt_info = tt_bw;
+ break;
+ }
+ }
+ if (!dev->tt_info)
+ xhci_warn(xhci, "WARN: Didn't find a matching TT\n");
+ }
+
+ /* Is this a LS/FS device under an external HS hub? */
+ if (udev->tt && udev->tt->hub->parent) {
+ slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id |
+ (udev->ttport << 8));
+ if (udev->tt->multi)
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ }
+ 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 = cpu_to_le32(EP_TYPE(CTRL_EP));
+
+ /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3) |
+ max_packets);
+
+ ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma |
+ dev->eps[0].ring->cycle_state);
+
+ /* Steps 7 and 8 were done in xhci_alloc_virt_device() */
+
+ return 0;
+}
+
+/*
+ * Convert interval expressed as 2^(bInterval - 1) == interval into
+ * straight exponent value 2^n == interval.
+ *
+ */
+static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int interval;
+
+ interval = clamp_val(ep->desc.bInterval, 1, 16) - 1;
+ if (interval != ep->desc.bInterval - 1)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d %sframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval,
+ udev->speed == USB_SPEED_FULL ? "" : "micro");
+
+ if (udev->speed == USB_SPEED_FULL) {
+ /*
+ * Full speed isoc endpoints specify interval in frames,
+ * not microframes. We are using microframes everywhere,
+ * so adjust accordingly.
+ */
+ interval += 3; /* 1 frame = 2^3 uframes */
+ }
+
+ return interval;
+}
+
+/*
+ * Convert bInterval expressed in microframes (in 1-255 range) to exponent of
+ * microframes, rounded down to nearest power of 2.
+ */
+static unsigned int xhci_microframes_to_exponent(struct usb_device *udev,
+ struct usb_host_endpoint *ep, unsigned int desc_interval,
+ unsigned int min_exponent, unsigned int max_exponent)
+{
+ unsigned int interval;
+
+ interval = fls(desc_interval) - 1;
+ interval = clamp_val(interval, min_exponent, max_exponent);
+ if ((1 << interval) != desc_interval)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval,
+ desc_interval);
+
+ return interval;
+}
+
+static unsigned int xhci_parse_microframe_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ if (ep->desc.bInterval == 0)
+ return 0;
+ return xhci_microframes_to_exponent(udev, ep,
+ ep->desc.bInterval, 0, 15);
+}
+
+
+static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ return xhci_microframes_to_exponent(udev, ep,
+ ep->desc.bInterval * 8, 3, 10);
+}
+
+/* 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 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 = xhci_parse_microframe_interval(udev, ep);
+ break;
+ }
+ /* 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)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
+ }
+ break;
+
+ case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_isoc(&ep->desc)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
+ break;
+ }
+ /*
+ * Fall through for interrupt endpoint interval decoding
+ * since it uses the same rules as low speed interrupt
+ * endpoints.
+ */
+
+ case USB_SPEED_LOW:
+ if (usb_endpoint_xfer_int(&ep->desc) ||
+ usb_endpoint_xfer_isoc(&ep->desc)) {
+
+ interval = xhci_parse_frame_interval(udev, ep);
+ }
+ break;
+
+ default:
+ BUG();
+ }
+ return EP_INTERVAL(interval);
+}
+
+/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps.
+ * High speed endpoint descriptors can define "the number of additional
+ * transaction opportunities per microframe", but that goes in the Max Burst
+ * endpoint context field.
+ */
+static u32 xhci_get_endpoint_mult(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ if (udev->speed != USB_SPEED_SUPER ||
+ !usb_endpoint_xfer_isoc(&ep->desc))
+ return 0;
+ return ep->ss_ep_comp.bmAttributes;
+}
+
+static u32 xhci_get_endpoint_type(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 {
+ type = 0;
+ }
+ return type;
+}
+
+/* Return the maximum endpoint service interval time (ESIT) payload.
+ * Basically, this is the maxpacket size, multiplied by the burst size
+ * and mult size.
+ */
+static u32 xhci_get_max_esit_payload(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ int max_burst;
+ int max_packet;
+
+ /* Only applies for interrupt or isochronous endpoints */
+ if (usb_endpoint_xfer_control(&ep->desc) ||
+ usb_endpoint_xfer_bulk(&ep->desc))
+ return 0;
+
+ if (udev->speed == USB_SPEED_SUPER)
+ return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
+
+ max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
+ max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11;
+ /* A 0 in max burst means 1 transfer per ESIT */
+ return max_packet * (max_burst + 1);
+}
+
+/* Set up an endpoint with one ring segment. Do not allocate stream rings.
+ * Drivers will have to call usb_alloc_streams() to do that.
+ */
+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;
+ enum xhci_ring_type type;
+ u32 max_esit_payload;
+ u32 endpoint_type;
+
+ ep_index = xhci_get_endpoint_index(&ep->desc);
+ ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
+
+ endpoint_type = xhci_get_endpoint_type(ep);
+ if (!endpoint_type)
+ return -EINVAL;
+ ep_ctx->ep_info2 = cpu_to_le32(endpoint_type);
+
+ type = usb_endpoint_type(&ep->desc);
+ /* Set up the endpoint ring */
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 2, 1, type, mem_flags);
+ if (!virt_dev->eps[ep_index].new_ring) {
+ /* Attempt to use the ring cache */
+ if (virt_dev->num_rings_cached == 0)
+ return -ENOMEM;
+ virt_dev->num_rings_cached--;
+ virt_dev->eps[ep_index].new_ring =
+ virt_dev->ring_cache[virt_dev->num_rings_cached];
+ virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
+ xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring,
+ 1, type);
+ }
+ virt_dev->eps[ep_index].skip = false;
+ ep_ring = virt_dev->eps[ep_index].new_ring;
+ ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | ep_ring->cycle_state);
+
+ ep_ctx->ep_info = cpu_to_le32(xhci_get_endpoint_interval(udev, ep)
+ | EP_MULT(xhci_get_endpoint_mult(udev, ep)));
+
+ /* FIXME dig Mult and streams info out of ep companion desc */
+
+ /* Allow 3 retries for everything but isoc;
+ * CErr shall be set to 0 for Isoch endpoints.
+ */
+ if (!usb_endpoint_xfer_isoc(&ep->desc))
+ ep_ctx->ep_info2 |= cpu_to_le32(ERROR_COUNT(3));
+ else
+ ep_ctx->ep_info2 |= cpu_to_le32(ERROR_COUNT(0));
+
+ /* Set the max packet size and max burst */
+ max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
+ max_burst = 0;
+ switch (udev->speed) {
+ case USB_SPEED_SUPER:
+ /* dig out max burst from ep companion desc */
+ max_burst = ep->ss_ep_comp.bMaxBurst;
+ break;
+ case USB_SPEED_HIGH:
+ /* Some devices get this wrong */
+ if (usb_endpoint_xfer_bulk(&ep->desc))
+ max_packet = 512;
+ /* 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 = (usb_endpoint_maxp(&ep->desc)
+ & 0x1800) >> 11;
+ }
+ break;
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ break;
+ default:
+ BUG();
+ }
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet) |
+ MAX_BURST(max_burst));
+ max_esit_payload = xhci_get_max_esit_payload(udev, ep);
+ ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload));
+
+ /*
+ * XXX no idea how to calculate the average TRB buffer length for bulk
+ * endpoints, as the driver gives us no clue how big each scatter gather
+ * list entry (or buffer) is going to be.
+ *
+ * For isochronous and interrupt endpoints, we set it to the max
+ * available, until we have new API in the USB core to allow drivers to
+ * declare how much bandwidth they actually need.
+ *
+ * Normally, it would be calculated by taking the total of the buffer
+ * lengths in the TD and then dividing by the number of TRBs in a TD,
+ * including link TRBs, No-op TRBs, and Event data TRBs. Since we don't
+ * use Event Data TRBs, and we don't chain in a link TRB on short
+ * transfers, we're basically dividing by 1.
+ *
+ * xHCI 1.0 specification indicates that the Average TRB Length should
+ * be set to 8 for control endpoints.
+ */
+ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100)
+ ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8));
+ else
+ ep_ctx->tx_info |=
+ cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload));
+
+ /* 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 = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
+
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = 0;
+ ep_ctx->deq = 0;
+ ep_ctx->tx_info = 0;
+ /* Don't free the endpoint ring until the set interface or configuration
+ * request succeeds.
+ */
+}
+
+void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info)
+{
+ bw_info->ep_interval = 0;
+ bw_info->mult = 0;
+ bw_info->num_packets = 0;
+ bw_info->max_packet_size = 0;
+ bw_info->type = 0;
+ bw_info->max_esit_payload = 0;
+}
+
+void xhci_update_bw_info(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_input_control_ctx *ctrl_ctx,
+ struct xhci_virt_device *virt_dev)
+{
+ struct xhci_bw_info *bw_info;
+ struct xhci_ep_ctx *ep_ctx;
+ unsigned int ep_type;
+ int i;
+
+ for (i = 1; i < 31; ++i) {
+ bw_info = &virt_dev->eps[i].bw_info;
+
+ /* We can't tell what endpoint type is being dropped, but
+ * unconditionally clearing the bandwidth info for non-periodic
+ * endpoints should be harmless because the info will never be
+ * set in the first place.
+ */
+ if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) {
+ /* Dropped endpoint */
+ xhci_clear_endpoint_bw_info(bw_info);
+ continue;
+ }
+
+ if (EP_IS_ADDED(ctrl_ctx, i)) {
+ ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, i);
+ ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
+
+ /* Ignore non-periodic endpoints */
+ if (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP &&
+ ep_type != ISOC_IN_EP &&
+ ep_type != INT_IN_EP)
+ continue;
+
+ /* Added or changed endpoint */
+ bw_info->ep_interval = CTX_TO_EP_INTERVAL(
+ le32_to_cpu(ep_ctx->ep_info));
+ /* Number of packets and mult are zero-based in the
+ * input context, but we want one-based for the
+ * interval table.
+ */
+ bw_info->mult = CTX_TO_EP_MULT(
+ le32_to_cpu(ep_ctx->ep_info)) + 1;
+ bw_info->num_packets = CTX_TO_MAX_BURST(
+ le32_to_cpu(ep_ctx->ep_info2)) + 1;
+ bw_info->max_packet_size = MAX_PACKET_DECODED(
+ le32_to_cpu(ep_ctx->ep_info2));
+ bw_info->type = ep_type;
+ bw_info->max_esit_payload = CTX_TO_MAX_ESIT_PAYLOAD(
+ le32_to_cpu(ep_ctx->tx_info));
+ }
+ }
+}
+
+/* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy.
+ * Useful when you want to change one particular aspect of the endpoint and then
+ * issue a configure endpoint command.
+ */
+void xhci_endpoint_copy(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_container_ctx *out_ctx,
+ unsigned int ep_index)
+{
+ struct xhci_ep_ctx *out_ep_ctx;
+ struct xhci_ep_ctx *in_ep_ctx;
+
+ out_ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
+ in_ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
+
+ in_ep_ctx->ep_info = out_ep_ctx->ep_info;
+ in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2;
+ in_ep_ctx->deq = out_ep_ctx->deq;
+ in_ep_ctx->tx_info = out_ep_ctx->tx_info;
+}
+
+/* Copy output xhci_slot_ctx to the input xhci_slot_ctx.
+ * Useful when you want to change one particular aspect of the endpoint and then
+ * issue a configure endpoint command. Only the context entries field matters,
+ * but we'll copy the whole thing anyway.
+ */
+void xhci_slot_copy(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_container_ctx *out_ctx)
+{
+ struct xhci_slot_ctx *in_slot_ctx;
+ struct xhci_slot_ctx *out_slot_ctx;
+
+ in_slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
+ out_slot_ctx = xhci_get_slot_ctx(xhci, out_ctx);
+
+ in_slot_ctx->dev_info = out_slot_ctx->dev_info;
+ in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2;
+ in_slot_ctx->tt_info = out_slot_ctx->tt_info;
+ in_slot_ctx->dev_state = out_slot_ctx->dev_state;
+}
+
+/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
+static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
+{
+ int i;
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ int num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Allocating %d scratchpad buffers", num_sp);
+
+ if (!num_sp)
+ return 0;
+
+ xhci->scratchpad = kzalloc(sizeof(*xhci->scratchpad), flags);
+ if (!xhci->scratchpad)
+ goto fail_sp;
+
+ xhci->scratchpad->sp_array = dma_alloc_coherent(dev,
+ num_sp * sizeof(u64),
+ &xhci->scratchpad->sp_dma, flags);
+ if (!xhci->scratchpad->sp_array)
+ goto fail_sp2;
+
+ xhci->scratchpad->sp_buffers = kzalloc(sizeof(void *) * num_sp, flags);
+ if (!xhci->scratchpad->sp_buffers)
+ goto fail_sp3;
+
+ xhci->scratchpad->sp_dma_buffers =
+ kzalloc(sizeof(dma_addr_t) * num_sp, flags);
+
+ if (!xhci->scratchpad->sp_dma_buffers)
+ goto fail_sp4;
+
+ xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
+ for (i = 0; i < num_sp; i++) {
+ dma_addr_t dma;
+ void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
+ flags);
+ if (!buf)
+ goto fail_sp5;
+
+ xhci->scratchpad->sp_array[i] = dma;
+ xhci->scratchpad->sp_buffers[i] = buf;
+ xhci->scratchpad->sp_dma_buffers[i] = dma;
+ }
+
+ return 0;
+
+ fail_sp5:
+ for (i = i - 1; i >= 0; i--) {
+ dma_free_coherent(dev, xhci->page_size,
+ xhci->scratchpad->sp_buffers[i],
+ xhci->scratchpad->sp_dma_buffers[i]);
+ }
+ kfree(xhci->scratchpad->sp_dma_buffers);
+
+ fail_sp4:
+ kfree(xhci->scratchpad->sp_buffers);
+
+ fail_sp3:
+ dma_free_coherent(dev, num_sp * sizeof(u64),
+ xhci->scratchpad->sp_array,
+ xhci->scratchpad->sp_dma);
+
+ fail_sp2:
+ kfree(xhci->scratchpad);
+ xhci->scratchpad = NULL;
+
+ fail_sp:
+ return -ENOMEM;
+}
+
+static void scratchpad_free(struct xhci_hcd *xhci)
+{
+ int num_sp;
+ int i;
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+
+ if (!xhci->scratchpad)
+ return;
+
+ num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2);
+
+ for (i = 0; i < num_sp; i++) {
+ dma_free_coherent(dev, xhci->page_size,
+ xhci->scratchpad->sp_buffers[i],
+ xhci->scratchpad->sp_dma_buffers[i]);
+ }
+ kfree(xhci->scratchpad->sp_dma_buffers);
+ kfree(xhci->scratchpad->sp_buffers);
+ dma_free_coherent(dev, num_sp * sizeof(u64),
+ xhci->scratchpad->sp_array,
+ xhci->scratchpad->sp_dma);
+ kfree(xhci->scratchpad);
+ xhci->scratchpad = NULL;
+}
+
+struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
+ bool allocate_in_ctx, bool allocate_completion,
+ gfp_t mem_flags)
+{
+ struct xhci_command *command;
+
+ command = kzalloc(sizeof(*command), mem_flags);
+ if (!command)
+ return NULL;
+
+ if (allocate_in_ctx) {
+ command->in_ctx =
+ xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT,
+ mem_flags);
+ if (!command->in_ctx) {
+ kfree(command);
+ return NULL;
+ }
+ }
+
+ if (allocate_completion) {
+ command->completion =
+ kzalloc(sizeof(struct completion), mem_flags);
+ if (!command->completion) {
+ xhci_free_container_ctx(xhci, command->in_ctx);
+ kfree(command);
+ return NULL;
+ }
+ init_completion(command->completion);
+ }
+
+ command->status = 0;
+ INIT_LIST_HEAD(&command->cmd_list);
+ return command;
+}
+
+void xhci_urb_free_priv(struct urb_priv *urb_priv)
+{
+ if (urb_priv) {
+ kfree(urb_priv->td[0]);
+ kfree(urb_priv);
+ }
+}
+
+void xhci_free_command(struct xhci_hcd *xhci,
+ struct xhci_command *command)
+{
+ xhci_free_container_ctx(xhci,
+ command->in_ctx);
+ kfree(command->completion);
+ kfree(command);
+}
+
+void xhci_mem_cleanup(struct xhci_hcd *xhci)
+{
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ int size;
+ int i, j, num_ports;
+
+ del_timer_sync(&xhci->cmd_timer);
+
+ /* Free the Event Ring Segment Table and the actual Event Ring */
+ size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
+ if (xhci->erst.entries)
+ dma_free_coherent(dev, size,
+ xhci->erst.entries, xhci->erst.erst_dma_addr);
+ xhci->erst.entries = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST");
+ if (xhci->event_ring)
+ xhci_ring_free(xhci, xhci->event_ring);
+ xhci->event_ring = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring");
+
+ if (xhci->lpm_command)
+ xhci_free_command(xhci, xhci->lpm_command);
+ xhci->lpm_command = NULL;
+ if (xhci->cmd_ring)
+ xhci_ring_free(xhci, xhci->cmd_ring);
+ xhci->cmd_ring = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
+ xhci_cleanup_command_queue(xhci);
+
+ num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ for (i = 0; i < num_ports && xhci->rh_bw; i++) {
+ struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
+ for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
+ struct list_head *ep = &bwt->interval_bw[j].endpoints;
+ while (!list_empty(ep))
+ list_del_init(ep->next);
+ }
+ }
+
+ 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_trace(xhci, trace_xhci_dbg_init, "Freed segment pool");
+
+ if (xhci->device_pool)
+ dma_pool_destroy(xhci->device_pool);
+ xhci->device_pool = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed device context pool");
+
+ if (xhci->small_streams_pool)
+ dma_pool_destroy(xhci->small_streams_pool);
+ xhci->small_streams_pool = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Freed small stream array pool");
+
+ if (xhci->medium_streams_pool)
+ dma_pool_destroy(xhci->medium_streams_pool);
+ xhci->medium_streams_pool = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Freed medium stream array pool");
+
+ if (xhci->dcbaa)
+ dma_free_coherent(dev, sizeof(*xhci->dcbaa),
+ xhci->dcbaa, xhci->dcbaa->dma);
+ xhci->dcbaa = NULL;
+
+ scratchpad_free(xhci);
+
+ if (!xhci->rh_bw)
+ goto no_bw;
+
+ for (i = 0; i < num_ports; i++) {
+ struct xhci_tt_bw_info *tt, *n;
+ list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) {
+ list_del(&tt->tt_list);
+ kfree(tt);
+ }
+ }
+
+no_bw:
+ xhci->cmd_ring_reserved_trbs = 0;
+ xhci->num_usb2_ports = 0;
+ xhci->num_usb3_ports = 0;
+ xhci->num_active_eps = 0;
+ kfree(xhci->usb2_ports);
+ kfree(xhci->usb3_ports);
+ kfree(xhci->port_array);
+ kfree(xhci->rh_bw);
+ kfree(xhci->ext_caps);
+
+ xhci->page_size = 0;
+ xhci->page_shift = 0;
+ xhci->bus_state[0].bus_suspended = 0;
+ xhci->bus_state[1].bus_suspended = 0;
+}
+
+static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
+ struct xhci_segment *input_seg,
+ union xhci_trb *start_trb,
+ union xhci_trb *end_trb,
+ dma_addr_t input_dma,
+ struct xhci_segment *result_seg,
+ char *test_name, int test_number)
+{
+ unsigned long long start_dma;
+ unsigned long long end_dma;
+ struct xhci_segment *seg;
+
+ start_dma = xhci_trb_virt_to_dma(input_seg, start_trb);
+ end_dma = xhci_trb_virt_to_dma(input_seg, end_trb);
+
+ seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false);
+ if (seg != result_seg) {
+ xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n",
+ test_name, test_number);
+ xhci_warn(xhci, "Tested TRB math w/ seg %p and "
+ "input DMA 0x%llx\n",
+ input_seg,
+ (unsigned long long) input_dma);
+ xhci_warn(xhci, "starting TRB %p (0x%llx DMA), "
+ "ending TRB %p (0x%llx DMA)\n",
+ start_trb, start_dma,
+ end_trb, end_dma);
+ xhci_warn(xhci, "Expected seg %p, got seg %p\n",
+ result_seg, seg);
+ trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma,
+ true);
+ return -1;
+ }
+ return 0;
+}
+
+/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
+static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
+{
+ struct {
+ dma_addr_t input_dma;
+ struct xhci_segment *result_seg;
+ } simple_test_vector [] = {
+ /* A zeroed DMA field should fail */
+ { 0, NULL },
+ /* One TRB before the ring start should fail */
+ { xhci->event_ring->first_seg->dma - 16, NULL },
+ /* One byte before the ring start should fail */
+ { xhci->event_ring->first_seg->dma - 1, NULL },
+ /* Starting TRB should succeed */
+ { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg },
+ /* Ending TRB should succeed */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16,
+ xhci->event_ring->first_seg },
+ /* One byte after the ring end should fail */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL },
+ /* One TRB after the ring end should fail */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL },
+ /* An address of all ones should fail */
+ { (dma_addr_t) (~0), NULL },
+ };
+ struct {
+ struct xhci_segment *input_seg;
+ union xhci_trb *start_trb;
+ union xhci_trb *end_trb;
+ dma_addr_t input_dma;
+ struct xhci_segment *result_seg;
+ } complex_test_vector [] = {
+ /* Test feeding a valid DMA address from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->event_ring->first_seg->trbs,
+ .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid end TRB from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->event_ring->first_seg->trbs,
+ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid start and end TRB from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->cmd_ring->first_seg->trbs,
+ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[0],
+ .end_trb = &xhci->event_ring->first_seg->trbs[3],
+ .input_dma = xhci->event_ring->first_seg->dma + 4*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[6],
+ .input_dma = xhci->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16,
+ .result_seg = NULL,
+ },
+ /* TRB not in this ring, and we have a wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->cmd_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ };
+
+ unsigned int num_tests;
+ int i, ret;
+
+ num_tests = ARRAY_SIZE(simple_test_vector);
+ for (i = 0; i < num_tests; i++) {
+ ret = xhci_test_trb_in_td(xhci,
+ xhci->event_ring->first_seg,
+ xhci->event_ring->first_seg->trbs,
+ &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ simple_test_vector[i].input_dma,
+ simple_test_vector[i].result_seg,
+ "Simple", i);
+ if (ret < 0)
+ return ret;
+ }
+
+ num_tests = ARRAY_SIZE(complex_test_vector);
+ for (i = 0; i < num_tests; i++) {
+ ret = xhci_test_trb_in_td(xhci,
+ complex_test_vector[i].input_seg,
+ complex_test_vector[i].start_trb,
+ complex_test_vector[i].end_trb,
+ complex_test_vector[i].input_dma,
+ complex_test_vector[i].result_seg,
+ "Complex", i);
+ if (ret < 0)
+ return ret;
+ }
+ xhci_dbg(xhci, "TRB math tests passed.\n");
+ return 0;
+}
+
+static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
+{
+ u64 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_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ temp &= ERST_PTR_MASK;
+ /* Don't clear the EHB bit (which is RW1C) because
+ * there might be more events to service.
+ */
+ temp &= ~ERST_EHB;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Write event ring dequeue pointer, "
+ "preserving EHB bit");
+ xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
+ &xhci->ir_set->erst_dequeue);
+}
+
+static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
+ __le32 __iomem *addr, u8 major_revision, int max_caps)
+{
+ u32 temp, port_offset, port_count;
+ int i;
+
+ if (major_revision > 0x03) {
+ xhci_warn(xhci, "Ignoring unknown port speed, "
+ "Ext Cap %p, revision = 0x%x\n",
+ addr, major_revision);
+ /* Ignoring port protocol we can't understand. FIXME */
+ return;
+ }
+
+ /* Port offset and count in the third dword, see section 7.2 */
+ temp = readl(addr + 2);
+ port_offset = XHCI_EXT_PORT_OFF(temp);
+ port_count = XHCI_EXT_PORT_COUNT(temp);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Ext Cap %p, port offset = %u, "
+ "count = %u, revision = 0x%x",
+ addr, port_offset, port_count, major_revision);
+ /* Port count includes the current port offset */
+ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports)
+ /* WTF? "Valid values are ‘1’ to MaxPorts" */
+ return;
+
+ /* cache usb2 port capabilities */
+ if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
+ xhci->ext_caps[xhci->num_ext_caps++] = temp;
+
+ /* Check the host's USB2 LPM capability */
+ if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
+ (temp & XHCI_L1C)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xHCI 0.96: support USB2 software lpm");
+ xhci->sw_lpm_support = 1;
+ }
+
+ if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xHCI 1.0: support USB2 software lpm");
+ xhci->sw_lpm_support = 1;
+ if (temp & XHCI_HLC) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xHCI 1.0: support USB2 hardware lpm");
+ xhci->hw_lpm_support = 1;
+ }
+ }
+
+ port_offset--;
+ for (i = port_offset; i < (port_offset + port_count); i++) {
+ /* Duplicate entry. Ignore the port if the revisions differ. */
+ if (xhci->port_array[i] != 0) {
+ xhci_warn(xhci, "Duplicate port entry, Ext Cap %p,"
+ " port %u\n", addr, i);
+ xhci_warn(xhci, "Port was marked as USB %u, "
+ "duplicated as USB %u\n",
+ xhci->port_array[i], major_revision);
+ /* Only adjust the roothub port counts if we haven't
+ * found a similar duplicate.
+ */
+ if (xhci->port_array[i] != major_revision &&
+ xhci->port_array[i] != DUPLICATE_ENTRY) {
+ if (xhci->port_array[i] == 0x03)
+ xhci->num_usb3_ports--;
+ else
+ xhci->num_usb2_ports--;
+ xhci->port_array[i] = DUPLICATE_ENTRY;
+ }
+ /* FIXME: Should we disable the port? */
+ continue;
+ }
+ xhci->port_array[i] = major_revision;
+ if (major_revision == 0x03)
+ xhci->num_usb3_ports++;
+ else
+ xhci->num_usb2_ports++;
+ }
+ /* FIXME: Should we disable ports not in the Extended Capabilities? */
+}
+
+/*
+ * Scan the Extended Capabilities for the "Supported Protocol Capabilities" that
+ * specify what speeds each port is supposed to be. We can't count on the port
+ * speed bits in the PORTSC register being correct until a device is connected,
+ * but we need to set up the two fake roothubs with the correct number of USB
+ * 3.0 and USB 2.0 ports at host controller initialization time.
+ */
+static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
+{
+ __le32 __iomem *addr, *tmp_addr;
+ u32 offset, tmp_offset;
+ unsigned int num_ports;
+ int i, j, port_index;
+ int cap_count = 0;
+
+ addr = &xhci->cap_regs->hcc_params;
+ offset = XHCI_HCC_EXT_CAPS(readl(addr));
+ if (offset == 0) {
+ xhci_err(xhci, "No Extended Capability registers, "
+ "unable to set up roothub.\n");
+ return -ENODEV;
+ }
+
+ num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags);
+ if (!xhci->port_array)
+ return -ENOMEM;
+
+ xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags);
+ if (!xhci->rh_bw)
+ return -ENOMEM;
+ for (i = 0; i < num_ports; i++) {
+ struct xhci_interval_bw_table *bw_table;
+
+ INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
+ bw_table = &xhci->rh_bw[i].bw_table;
+ for (j = 0; j < XHCI_MAX_INTERVAL; j++)
+ INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
+ }
+
+ /*
+ * For whatever reason, the first capability offset is from the
+ * capability register base, not from the HCCPARAMS register.
+ * See section 5.3.6 for offset calculation.
+ */
+ addr = &xhci->cap_regs->hc_capbase + offset;
+
+ tmp_addr = addr;
+ tmp_offset = offset;
+
+ /* count extended protocol capability entries for later caching */
+ do {
+ u32 cap_id;
+ cap_id = readl(tmp_addr);
+ if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
+ cap_count++;
+ tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
+ tmp_addr += tmp_offset;
+ } while (tmp_offset);
+
+ xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
+ if (!xhci->ext_caps)
+ return -ENOMEM;
+
+ while (1) {
+ u32 cap_id;
+
+ cap_id = readl(addr);
+ if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
+ xhci_add_in_port(xhci, num_ports, addr,
+ (u8) XHCI_EXT_PORT_MAJOR(cap_id),
+ cap_count);
+ offset = XHCI_EXT_CAPS_NEXT(cap_id);
+ if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
+ == num_ports)
+ break;
+ /*
+ * Once you're into the Extended Capabilities, the offset is
+ * always relative to the register holding the offset.
+ */
+ addr += offset;
+ }
+
+ if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {
+ xhci_warn(xhci, "No ports on the roothubs?\n");
+ return -ENODEV;
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Found %u USB 2.0 ports and %u USB 3.0 ports.",
+ xhci->num_usb2_ports, xhci->num_usb3_ports);
+
+ /* Place limits on the number of roothub ports so that the hub
+ * descriptors aren't longer than the USB core will allocate.
+ */
+ if (xhci->num_usb3_ports > 15) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Limiting USB 3.0 roothub ports to 15.");
+ xhci->num_usb3_ports = 15;
+ }
+ if (xhci->num_usb2_ports > USB_MAXCHILDREN) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Limiting USB 2.0 roothub ports to %u.",
+ USB_MAXCHILDREN);
+ xhci->num_usb2_ports = USB_MAXCHILDREN;
+ }
+
+ /*
+ * Note we could have all USB 3.0 ports, or all USB 2.0 ports.
+ * Not sure how the USB core will handle a hub with no ports...
+ */
+ if (xhci->num_usb2_ports) {
+ xhci->usb2_ports = kmalloc(sizeof(*xhci->usb2_ports)*
+ xhci->num_usb2_ports, flags);
+ if (!xhci->usb2_ports)
+ return -ENOMEM;
+
+ port_index = 0;
+ for (i = 0; i < num_ports; i++) {
+ if (xhci->port_array[i] == 0x03 ||
+ xhci->port_array[i] == 0 ||
+ xhci->port_array[i] == DUPLICATE_ENTRY)
+ continue;
+
+ xhci->usb2_ports[port_index] =
+ &xhci->op_regs->port_status_base +
+ NUM_PORT_REGS*i;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "USB 2.0 port at index %u, "
+ "addr = %p", i,
+ xhci->usb2_ports[port_index]);
+ port_index++;
+ if (port_index == xhci->num_usb2_ports)
+ break;
+ }
+ }
+ if (xhci->num_usb3_ports) {
+ xhci->usb3_ports = kmalloc(sizeof(*xhci->usb3_ports)*
+ xhci->num_usb3_ports, flags);
+ if (!xhci->usb3_ports)
+ return -ENOMEM;
+
+ port_index = 0;
+ for (i = 0; i < num_ports; i++)
+ if (xhci->port_array[i] == 0x03) {
+ xhci->usb3_ports[port_index] =
+ &xhci->op_regs->port_status_base +
+ NUM_PORT_REGS*i;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "USB 3.0 port at index %u, "
+ "addr = %p", i,
+ xhci->usb3_ports[port_index]);
+ port_index++;
+ if (port_index == xhci->num_usb3_ports)
+ break;
+ }
+ }
+ return 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;
+ u64 val_64;
+ struct xhci_segment *seg;
+ u32 page_size, temp;
+ int i;
+
+ INIT_LIST_HEAD(&xhci->cmd_list);
+
+ page_size = readl(&xhci->op_regs->page_size);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Supported page size register = 0x%x", page_size);
+ for (i = 0; i < 16; i++) {
+ if ((0x1 & page_size) != 0)
+ break;
+ page_size = page_size >> 1;
+ }
+ if (i < 16)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Supported page size of %iK", (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_trace(xhci, trace_xhci_dbg_init,
+ "HCD page size set to %iK", 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(readl(&xhci->cap_regs->hcs_params1));
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// xHC can handle at most %d device slots.", val);
+ val2 = readl(&xhci->op_regs->config_reg);
+ val |= (val2 & ~HCS_SLOTS_MASK);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Setting Max device slots reg = 0x%x.", val);
+ writel(val, &xhci->op_regs->config_reg);
+
+ /*
+ * Section 5.4.8 - doorbell array must be
+ * "physically contiguous and 64-byte (cache line) aligned".
+ */
+ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
+ GFP_KERNEL);
+ if (!xhci->dcbaa)
+ goto fail;
+ memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
+ xhci->dcbaa->dma = dma;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Device context base array address = 0x%llx (DMA), %p (virt)",
+ (unsigned long long)xhci->dcbaa->dma, xhci->dcbaa);
+ xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
+
+ /*
+ * 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
+ * and our use of dma addresses in the trb_address_map radix tree needs
+ * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
+ */
+ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
+ TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
+
+ /* See Table 46 and Note on Figure 55 */
+ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
+ 2112, 64, xhci->page_size);
+ if (!xhci->segment_pool || !xhci->device_pool)
+ goto fail;
+
+ /* Linear stream context arrays don't have any boundary restrictions,
+ * and only need to be 16-byte aligned.
+ */
+ xhci->small_streams_pool =
+ dma_pool_create("xHCI 256 byte stream ctx arrays",
+ dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
+ xhci->medium_streams_pool =
+ dma_pool_create("xHCI 1KB stream ctx arrays",
+ dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
+ /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
+ * will be allocated with dma_alloc_coherent()
+ */
+
+ if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
+ goto fail;
+
+ /* Set up the command ring to have one segments for now. */
+ xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
+ if (!xhci->cmd_ring)
+ goto fail;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Allocated command ring at %p", xhci->cmd_ring);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%llx",
+ (unsigned long long)xhci->cmd_ring->first_seg->dma);
+
+ /* Set the address in the Command Ring Control register */
+ val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
+ (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
+ xhci->cmd_ring->cycle_state;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Setting command ring address to 0x%x", val);
+ xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
+ xhci_dbg_cmd_ptrs(xhci);
+
+ xhci->lpm_command = xhci_alloc_command(xhci, true, true, flags);
+ if (!xhci->lpm_command)
+ goto fail;
+
+ /* Reserve one command ring TRB for disabling LPM.
+ * Since the USB core grabs the shared usb_bus bandwidth mutex before
+ * disabling LPM, we only need to reserve one TRB for all devices.
+ */
+ xhci->cmd_ring_reserved_trbs++;
+
+ val = readl(&xhci->cap_regs->db_off);
+ val &= DBOFF_MASK;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Doorbell array is located at offset 0x%x"
+ " from cap regs base addr", val);
+ xhci->dba = (void __iomem *) xhci->cap_regs + val;
+ xhci_dbg_regs(xhci);
+ xhci_print_run_regs(xhci);
+ /* Set ir_set to interrupt register set 0 */
+ xhci->ir_set = &xhci->run_regs->ir_set[0];
+
+ /*
+ * Event ring setup: Allocate a normal ring, but also setup
+ * the event ring segment table (ERST). Section 4.9.3.
+ */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
+ xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
+ flags);
+ if (!xhci->event_ring)
+ goto fail;
+ if (xhci_check_trb_in_td_math(xhci) < 0)
+ goto fail;
+
+ xhci->erst.entries = dma_alloc_coherent(dev,
+ sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
+ GFP_KERNEL);
+ if (!xhci->erst.entries)
+ goto fail;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Allocated event ring segment table at 0x%llx",
+ (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_trace(xhci, trace_xhci_dbg_init,
+ "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx",
+ 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 = cpu_to_le64(seg->dma);
+ entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
+ entry->rsvd = 0;
+ seg = seg->next;
+ }
+
+ /* set ERST count with the number of entries in the segment table */
+ val = readl(&xhci->ir_set->erst_size);
+ val &= ERST_SIZE_MASK;
+ val |= ERST_NUM_SEGS;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Write ERST size = %i to ir_set 0 (some bits preserved)",
+ val);
+ writel(val, &xhci->ir_set->erst_size);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Set ERST entries to point to event ring.");
+ /* set the segment table base address */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Set ERST base address for ir_set 0 = 0x%llx",
+ (unsigned long long)xhci->erst.erst_dma_addr);
+ val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base);
+ val_64 &= ERST_PTR_MASK;
+ val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK);
+ xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base);
+
+ /* Set the event ring dequeue address */
+ xhci_set_hc_event_deq(xhci);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Wrote ERST address to ir_set 0.");
+ xhci_print_ir_set(xhci, 0);
+
+ /* init command timeout timer */
+ setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
+ (unsigned long)xhci);
+
+ /*
+ * 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] = NULL;
+ for (i = 0; i < USB_MAXCHILDREN; ++i) {
+ xhci->bus_state[0].resume_done[i] = 0;
+ xhci->bus_state[1].resume_done[i] = 0;
+ /* Only the USB 2.0 completions will ever be used. */
+ init_completion(&xhci->bus_state[1].rexit_done[i]);
+ }
+
+ if (scratchpad_alloc(xhci, flags))
+ goto fail;
+ if (xhci_setup_port_arrays(xhci, flags))
+ goto fail;
+
+ /* Enable USB 3.0 device notifications for function remote wake, which
+ * is necessary for allowing USB 3.0 devices to do remote wakeup from
+ * U3 (device suspend).
+ */
+ temp = readl(&xhci->op_regs->dev_notification);
+ temp &= ~DEV_NOTE_MASK;
+ temp |= DEV_NOTE_FWAKE;
+ writel(temp, &xhci->op_regs->dev_notification);
+
+ return 0;
+
+fail:
+ xhci_warn(xhci, "Couldn't initialize memory\n");
+ xhci_halt(xhci);
+ xhci_reset(xhci);
+ xhci_mem_cleanup(xhci);
+ return -ENOMEM;
+}
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
new file mode 100644
index 000000000..1eefc9881
--- /dev/null
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 Marvell
+ * Author: Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * 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/io.h>
+#include <linux/mbus.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "xhci-mvebu.h"
+
+#define USB3_MAX_WINDOWS 4
+#define USB3_WIN_CTRL(w) (0x0 + ((w) * 8))
+#define USB3_WIN_BASE(w) (0x4 + ((w) * 8))
+
+static void xhci_mvebu_mbus_config(void __iomem *base,
+ const struct mbus_dram_target_info *dram)
+{
+ int win;
+
+ /* Clear all existing windows */
+ for (win = 0; win < USB3_MAX_WINDOWS; win++) {
+ writel(0, base + USB3_WIN_CTRL(win));
+ writel(0, base + USB3_WIN_BASE(win));
+ }
+
+ /* Program each DRAM CS in a seperate window */
+ for (win = 0; win < dram->num_cs; win++) {
+ const struct mbus_dram_window *cs = dram->cs + win;
+
+ writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ base + USB3_WIN_CTRL(win));
+
+ writel((cs->base & 0xffff0000), base + USB3_WIN_BASE(win));
+ }
+}
+
+int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ const struct mbus_dram_target_info *dram;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENODEV;
+
+ /*
+ * We don't use devm_ioremap() because this mapping should
+ * only exists for the duration of this probe function.
+ */
+ base = ioremap(res->start, resource_size(res));
+ if (!base)
+ return -ENODEV;
+
+ dram = mv_mbus_dram_info();
+ xhci_mvebu_mbus_config(base, dram);
+
+ /*
+ * This memory area was only needed to configure the MBus
+ * windows, and is therefore no longer useful.
+ */
+ iounmap(base);
+
+ return 0;
+}
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
new file mode 100644
index 000000000..7ede92aa4
--- /dev/null
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory Clement <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __LINUX_XHCI_MVEBU_H
+#define __LINUX_XHCI_MVEBU_H
+#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
+int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev);
+#else
+static inline int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+#endif /* __LINUX_XHCI_MVEBU_H */
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
new file mode 100644
index 000000000..2af32e26f
--- /dev/null
+++ b/drivers/usb/host/xhci-pci.c
@@ -0,0 +1,398 @@
+/*
+ * 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 <linux/slab.h>
+#include <linux/module.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+
+/* Device for a quirk */
+#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
+#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
+#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
+
+#define PCI_VENDOR_ID_ETRON 0x1b6f
+#define PCI_DEVICE_ID_EJ168 0x7023
+
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
+
+static const char hcd_name[] = "xhci_hcd";
+
+static struct hc_driver __read_mostly xhci_pci_hc_driver;
+
+/* 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;
+}
+
+static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /* Look for vendor-specific quirks */
+ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
+ (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK ||
+ pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1400)) {
+ if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
+ pdev->revision == 0x0) {
+ xhci->quirks |= XHCI_RESET_EP_QUIRK;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Fresco Logic xHC needs configure"
+ " endpoint cmd after reset endpoint");
+ }
+ if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
+ pdev->revision == 0x4) {
+ xhci->quirks |= XHCI_SLOW_SUSPEND;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Fresco Logic xHC revision %u"
+ "must be suspended extra slowly",
+ pdev->revision);
+ }
+ if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+ /* Fresco Logic confirms: all revisions of this chip do not
+ * support MSI, even though some of them claim to in their PCI
+ * capabilities.
+ */
+ xhci->quirks |= XHCI_BROKEN_MSI;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Fresco Logic revision %u "
+ "has broken MSI implementation",
+ pdev->revision);
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ }
+
+ if (pdev->vendor == PCI_VENDOR_ID_NEC)
+ xhci->quirks |= XHCI_NEC_HOST;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96)
+ xhci->quirks |= XHCI_AMD_0x96_HOST;
+
+ /* AMD PLL quirk */
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
+ xhci->quirks |= XHCI_AMD_PLL_FIX;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AMD)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+ xhci->quirks |= XHCI_INTEL_HOST;
+ xhci->quirks |= XHCI_AVOID_BEI;
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
+ xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
+ xhci->limit_active_eps = 64;
+ xhci->quirks |= XHCI_SW_BW_CHECKING;
+ /*
+ * PPT desktop boards DH77EB and DH77DF will power back on after
+ * a few seconds of being shutdown. The fix for this is to
+ * switch the ports from xHCI to EHCI on shutdown. We can't use
+ * DMI information to find those particular boards (since each
+ * vendor will change the board name), so we have to key off all
+ * PPT chipsets.
+ */
+ xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
+ xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) {
+ xhci->quirks |= XHCI_PME_STUCK_QUIRK;
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
+ pdev->device == PCI_DEVICE_ID_EJ168) {
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
+ pdev->device == 0x0015)
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+ if (pdev->vendor == PCI_VENDOR_ID_VIA)
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+
+ /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
+ if (pdev->vendor == PCI_VENDOR_ID_VIA &&
+ pdev->device == 0x3432)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
+ if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
+ pdev->device == 0x1042)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Resetting on resume");
+}
+
+/*
+ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear
+ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
+ */
+static void xhci_pme_quirk(struct xhci_hcd *xhci)
+{
+ u32 val;
+ void __iomem *reg;
+
+ reg = (void __iomem *) xhci->cap_regs + 0x80a4;
+ val = readl(reg);
+ writel(val | BIT(28), reg);
+ readl(reg);
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_pci_setup(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci;
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int retval;
+
+ retval = xhci_gen_setup(hcd, xhci_pci_quirks);
+ if (retval)
+ return retval;
+
+ xhci = hcd_to_xhci(hcd);
+ if (!usb_hcd_is_primary_hcd(hcd))
+ return 0;
+
+ 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 */
+ retval = xhci_pci_reinit(xhci, pdev);
+ if (!retval)
+ return retval;
+
+ kfree(xhci);
+ return retval;
+}
+
+/*
+ * We need to register our own PCI probe function (instead of the USB core's
+ * function) in order to create a second roothub under xHCI.
+ */
+static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int retval;
+ struct xhci_hcd *xhci;
+ struct hc_driver *driver;
+ struct usb_hcd *hcd;
+
+ driver = (struct hc_driver *)id->driver_data;
+
+ /* Prevent runtime suspending between USB-2 and USB-3 initialization */
+ pm_runtime_get_noresume(&dev->dev);
+
+ /* Register the USB 2.0 roothub.
+ * FIXME: USB core must know to register the USB 2.0 roothub first.
+ * This is sort of silly, because we could just set the HCD driver flags
+ * to say USB 2.0, but I'm not sure what the implications would be in
+ * the other parts of the HCD code.
+ */
+ retval = usb_hcd_pci_probe(dev, id);
+
+ if (retval)
+ goto put_runtime_pm;
+
+ /* USB 2.0 roothub is stored in the PCI device now. */
+ hcd = dev_get_drvdata(&dev->dev);
+ xhci = hcd_to_xhci(hcd);
+ xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,
+ pci_name(dev), hcd);
+ if (!xhci->shared_hcd) {
+ retval = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
+
+ /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset)
+ * is called by usb_add_hcd().
+ */
+ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+
+ retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
+ IRQF_SHARED);
+ if (retval)
+ goto put_usb3_hcd;
+ /* Roothub already marked as USB 3.0 speed */
+
+ if (!(xhci->quirks & XHCI_BROKEN_STREAMS) &&
+ HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
+ /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
+ pm_runtime_put_noidle(&dev->dev);
+
+ return 0;
+
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+dealloc_usb2_hcd:
+ usb_hcd_pci_remove(dev);
+put_runtime_pm:
+ pm_runtime_put_noidle(&dev->dev);
+ return retval;
+}
+
+static void xhci_pci_remove(struct pci_dev *dev)
+{
+ struct xhci_hcd *xhci;
+
+ xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ if (xhci->shared_hcd) {
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ }
+ usb_hcd_pci_remove(dev);
+
+ /* Workaround for spurious wakeups at shutdown with HSW */
+ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+ pci_set_power_state(dev, PCI_D3hot);
+
+ kfree(xhci);
+}
+
+#ifdef CONFIG_PM
+static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ /*
+ * Systems with the TI redriver that loses port status change events
+ * need to have the registers polled during D3, so avoid D3cold.
+ */
+ if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
+ pdev->no_d3cold = true;
+
+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+ xhci_pme_quirk(xhci);
+
+ return xhci_suspend(xhci, do_wakeup);
+}
+
+static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int retval = 0;
+
+ /* The BIOS on systems with the Intel Panther Point chipset may or may
+ * not support xHCI natively. That means that during system resume, it
+ * may switch the ports back to EHCI so that users can use their
+ * keyboard to select a kernel from GRUB after resume from hibernate.
+ *
+ * The BIOS is supposed to remember whether the OS had xHCI ports
+ * enabled before resume, and switch the ports back to xHCI when the
+ * BIOS/OS semaphore is written, but we all know we can't trust BIOS
+ * writers.
+ *
+ * Unconditionally switch the ports back to xHCI after a system resume.
+ * It should not matter whether the EHCI or xHCI controller is
+ * resumed first. It's enough to do the switchover in xHCI because
+ * USB core won't notice anything as the hub driver doesn't start
+ * running again until after all the devices (including both EHCI and
+ * xHCI host controllers) have been resumed.
+ */
+
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ usb_enable_intel_xhci_ports(pdev);
+
+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+ xhci_pme_quirk(xhci);
+
+ retval = xhci_resume(xhci, hibernated);
+ return retval;
+}
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/* 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 = xhci_pci_probe,
+ .remove = xhci_pci_remove,
+ /* suspend and resume implemented later */
+
+ .shutdown = usb_hcd_pci_shutdown,
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &usb_hcd_pci_pm_ops
+ },
+#endif
+};
+
+static int __init xhci_pci_init(void)
+{
+ xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup);
+#ifdef CONFIG_PM
+ xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend;
+ xhci_pci_hc_driver.pci_resume = xhci_pci_resume;
+#endif
+ return pci_register_driver(&xhci_pci_driver);
+}
+module_init(xhci_pci_init);
+
+static void __exit xhci_pci_exit(void)
+{
+ pci_unregister_driver(&xhci_pci_driver);
+}
+module_exit(xhci_pci_exit);
+
+MODULE_DESCRIPTION("xHCI PCI Host Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
new file mode 100644
index 000000000..783e81913
--- /dev/null
+++ b/drivers/usb/host/xhci-plat.c
@@ -0,0 +1,287 @@
+/*
+ * xhci-plat.c - xHCI host controller driver platform Bus Glue.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * A lot of code borrowed from the Linux xHCI 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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+#include <linux/slab.h>
+#include <linux/usb/xhci_pdriver.h>
+
+#include "xhci.h"
+#include "xhci-mvebu.h"
+#include "xhci-rcar.h"
+
+static struct hc_driver __read_mostly xhci_plat_hc_driver;
+
+static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ /*
+ * As of now platform drivers don't provide MSI support so we ensure
+ * here that the generic code does not try to make a pci_dev from our
+ * dev struct in order to setup MSI
+ */
+ xhci->quirks |= XHCI_PLAT;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_plat_setup(struct usb_hcd *hcd)
+{
+ struct device_node *of_node = hcd->self.controller->of_node;
+ int ret;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) {
+ ret = xhci_rcar_init_quirk(hcd);
+ if (ret)
+ return ret;
+ }
+
+ return xhci_gen_setup(hcd, xhci_plat_quirks);
+}
+
+static int xhci_plat_start(struct usb_hcd *hcd)
+{
+ struct device_node *of_node = hcd->self.controller->of_node;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791"))
+ xhci_rcar_start(hcd);
+
+ return xhci_run(hcd);
+}
+
+static int xhci_plat_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
+ const struct hc_driver *driver;
+ struct xhci_hcd *xhci;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct clk *clk;
+ int ret;
+ int irq;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ driver = &xhci_plat_hc_driver;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ /* Initialize dma_mask and coherent_dma_mask to 32-bits */
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+ else
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto put_hcd;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ /*
+ * Not all platforms have a clk so it is not an error if the
+ * clock does not exists.
+ */
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto put_hcd;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-375-xhci") ||
+ of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-380-xhci")) {
+ ret = xhci_mvebu_mbus_init_quirk(pdev);
+ if (ret)
+ goto disable_clk;
+ }
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto disable_clk;
+
+ device_wakeup_enable(hcd->self.controller);
+
+ /* USB 2.0 roothub is stored in the platform_device now. */
+ hcd = platform_get_drvdata(pdev);
+ xhci = hcd_to_xhci(hcd);
+ xhci->clk = clk;
+ xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
+ dev_name(&pdev->dev), hcd);
+ if (!xhci->shared_hcd) {
+ ret = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
+
+ if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
+ (pdata && pdata->usb3_lpm_capable))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+ /*
+ * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
+ * is called by usb_add_hcd().
+ */
+ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
+ hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
+ if (IS_ERR(hcd->usb_phy)) {
+ ret = PTR_ERR(hcd->usb_phy);
+ if (ret == -EPROBE_DEFER)
+ goto put_usb3_hcd;
+ hcd->usb_phy = NULL;
+ } else {
+ ret = usb_phy_init(hcd->usb_phy);
+ if (ret)
+ goto put_usb3_hcd;
+ }
+
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto disable_usb_phy;
+
+ return 0;
+
+disable_usb_phy:
+ usb_phy_shutdown(hcd->usb_phy);
+
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+
+dealloc_usb2_hcd:
+ usb_remove_hcd(hcd);
+
+disable_clk:
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
+
+put_hcd:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int xhci_plat_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct clk *clk = xhci->clk;
+
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_phy_shutdown(hcd->usb_phy);
+ usb_put_hcd(xhci->shared_hcd);
+
+ usb_remove_hcd(hcd);
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
+ usb_put_hcd(hcd);
+ kfree(xhci);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int xhci_plat_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ /*
+ * xhci_suspend() needs `do_wakeup` to know whether host is allowed
+ * to do wakeup during suspend. Since xhci_plat_suspend is currently
+ * only designed for system suspend, device_may_wakeup() is enough
+ * to dertermine whether host is allowed to do wakeup. Need to
+ * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
+ * also applies to runtime suspend.
+ */
+ return xhci_suspend(xhci, device_may_wakeup(dev));
+}
+
+static int xhci_plat_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ return xhci_resume(xhci, 0);
+}
+
+static const struct dev_pm_ops xhci_plat_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
+};
+#define DEV_PM_OPS (&xhci_plat_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+static const struct of_device_id usb_xhci_of_match[] = {
+ { .compatible = "generic-xhci" },
+ { .compatible = "xhci-platform" },
+ { .compatible = "marvell,armada-375-xhci"},
+ { .compatible = "marvell,armada-380-xhci"},
+ { .compatible = "renesas,xhci-r8a7790"},
+ { .compatible = "renesas,xhci-r8a7791"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
+#endif
+
+static struct platform_driver usb_xhci_driver = {
+ .probe = xhci_plat_probe,
+ .remove = xhci_plat_remove,
+ .driver = {
+ .name = "xhci-hcd",
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(usb_xhci_of_match),
+ },
+};
+MODULE_ALIAS("platform:xhci-hcd");
+
+static int __init xhci_plat_init(void)
+{
+ xhci_init_driver(&xhci_plat_hc_driver, xhci_plat_setup);
+ xhci_plat_hc_driver.start = xhci_plat_start;
+ return platform_driver_register(&usb_xhci_driver);
+}
+module_init(xhci_plat_init);
+
+static void __exit xhci_plat_exit(void)
+{
+ platform_driver_unregister(&usb_xhci_driver);
+}
+module_exit(xhci_plat_exit);
+
+MODULE_DESCRIPTION("xHCI Platform Host Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
new file mode 100644
index 000000000..d1fa59e78
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.c
@@ -0,0 +1,148 @@
+/*
+ * xHCI host controller driver for R-Car SoCs
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * 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/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+
+#include "xhci.h"
+#include "xhci-rcar.h"
+
+#define FIRMWARE_NAME "/*(DEBLOBBED)*/"
+/*(DEBLOBBED)*/
+
+/*** Register Offset ***/
+#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */
+#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */
+#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */
+
+#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */
+#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */
+#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */
+#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */
+#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */
+#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */
+
+/*** Register Settings ***/
+/* Interrupt Enable */
+#define RCAR_USB3_INT_XHC_ENA 0x00000001
+#define RCAR_USB3_INT_PME_ENA 0x00000002
+#define RCAR_USB3_INT_HSE_ENA 0x00000004
+#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \
+ RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA)
+
+/* FW Download Control & Status */
+#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001
+#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010
+#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100
+
+/* LCLK Select */
+#define RCAR_USB3_LCLK_ENA_VAL 0x01030001
+
+/* USB3.0 Configuration */
+#define RCAR_USB3_CONF1_VAL 0x00030204
+#define RCAR_USB3_CONF2_VAL 0x00030300
+#define RCAR_USB3_CONF3_VAL 0x13802007
+
+/* USB3.0 Polarity */
+#define RCAR_USB3_RX_POL_VAL BIT(21)
+#define RCAR_USB3_TX_POL_VAL BIT(4)
+
+void xhci_rcar_start(struct usb_hcd *hcd)
+{
+ u32 temp;
+
+ if (hcd->regs != NULL) {
+ /* Interrupt Enable */
+ temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
+ temp |= RCAR_USB3_INT_ENA_VAL;
+ writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
+ /* LCLK Select */
+ writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
+ /* USB3.0 Configuration */
+ writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
+ writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
+ writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
+ /* USB3.0 Polarity */
+ writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
+ writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
+ }
+}
+
+static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs)
+{
+ const struct firmware *fw;
+ int retval, index, j, time;
+ int timeout = 10000;
+ u32 data, val, temp;
+
+ /* request R-Car USB3.0 firmware */
+ retval = reject_firmware(&fw, FIRMWARE_NAME, dev);
+ if (retval)
+ return retval;
+
+ /* download R-Car USB3.0 firmware */
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (index = 0; index < fw->size; index += 4) {
+ /* to avoid reading beyond the end of the buffer */
+ for (data = 0, j = 3; j >= 0; j--) {
+ if ((j + index) < fw->size)
+ data |= fw->data[index + j] << (8 * j);
+ }
+ writel(data, regs + RCAR_USB3_FW_DATA0);
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0)
+ break;
+ udelay(1);
+ }
+ if (time == timeout) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp &= ~RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) {
+ retval = 0;
+ break;
+ }
+ udelay(1);
+ }
+ if (time == timeout)
+ retval = -ETIMEDOUT;
+
+ release_firmware(fw);
+
+ return retval;
+}
+
+/* This function needs to initialize a "phy" of usb before */
+int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ /* If hcd->regs is NULL, we don't just call the following function */
+ if (!hcd->regs)
+ return 0;
+
+ return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs);
+}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
new file mode 100644
index 000000000..585012567
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/usb/host/xhci-rcar.h
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * 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 _XHCI_RCAR_H
+#define _XHCI_RCAR_H
+
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+void xhci_rcar_start(struct usb_hcd *hcd);
+int xhci_rcar_init_quirk(struct usb_hcd *hcd);
+#else
+static inline void xhci_rcar_start(struct usb_hcd *hcd)
+{
+}
+
+static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ return 0;
+}
+#endif
+#endif /* _XHCI_RCAR_H */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
new file mode 100644
index 000000000..7d34cbfaf
--- /dev/null
+++ b/drivers/usb/host/xhci-ring.c
@@ -0,0 +1,3962 @@
+/*
+ * 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 <linux/slab.h>
+#include "xhci.h"
+#include "xhci-trace.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 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 le32_to_cpu(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 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_TYPE_LINK_LE32(trb->link.control);
+}
+
+static int enqueue_is_link_trb(struct xhci_ring *ring)
+{
+ struct xhci_link_trb *link = &ring->enqueue->link;
+ return TRB_TYPE_LINK_LE32(link->control);
+}
+
+/* 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)++;
+ }
+}
+
+/*
+ * 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)
+{
+ ring->deq_updates++;
+
+ /*
+ * If this is not event ring, and the dequeue pointer
+ * is not on a link TRB, there is one more usable TRB
+ */
+ if (ring->type != TYPE_EVENT &&
+ !last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
+ ring->num_trbs_free++;
+
+ do {
+ /*
+ * 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)
+ */
+ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
+ if (ring->type == TYPE_EVENT &&
+ last_trb_on_last_seg(xhci, ring,
+ ring->deq_seg, ring->dequeue)) {
+ ring->cycle_state ^= 1;
+ }
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ } else {
+ ring->dequeue++;
+ }
+ } while (last_trb(xhci, ring, ring->deq_seg, 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. This was
+ * fixed in the 0.96 specification errata, but we have to assume that all 0.95
+ * xHCI hardware can't handle the chain bit being cleared on a link TRB.
+ *
+ * @more_trbs_coming: Will you enqueue more TRBs before calling
+ * prepare_transfer()?
+ */
+static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ bool more_trbs_coming)
+{
+ u32 chain;
+ union xhci_trb *next;
+
+ chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
+ /* If this is not event ring, there is one less usable TRB */
+ if (ring->type != TYPE_EVENT &&
+ !last_trb(xhci, ring, ring->enq_seg, ring->enqueue))
+ ring->num_trbs_free--;
+ 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 (ring->type != TYPE_EVENT) {
+ /*
+ * If the caller doesn't plan on enqueueing more
+ * TDs before ringing the doorbell, then we
+ * don't want to give the link TRB to the
+ * hardware just yet. We'll give the link TRB
+ * back in prepare_ring() just before we enqueue
+ * the TD at the top of the ring.
+ */
+ if (!chain && !more_trbs_coming)
+ break;
+
+ /* If we're not dealing with 0.95 hardware or
+ * isoc rings on AMD 0.96 host,
+ * carry over the chain bit of the previous TRB
+ * (which may mean the chain bit is cleared).
+ */
+ if (!(ring->type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST))
+ && !xhci_link_trb_quirk(xhci)) {
+ next->link.control &=
+ cpu_to_le32(~TRB_CHAIN);
+ next->link.control |=
+ cpu_to_le32(chain);
+ }
+ /* Give this link TRB to the hardware */
+ wmb();
+ next->link.control ^= cpu_to_le32(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 ^= 1;
+ }
+ }
+ 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 and make sure
+ * enqueue pointer will not advance into dequeue segment. See rules above.
+ */
+static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs)
+{
+ int num_trbs_in_deq_seg;
+
+ if (ring->num_trbs_free < num_trbs)
+ return 0;
+
+ if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) {
+ num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs;
+ if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Ring the host controller doorbell after placing a command on the ring */
+void xhci_ring_cmd_db(struct xhci_hcd *xhci)
+{
+ if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING))
+ return;
+
+ xhci_dbg(xhci, "// Ding dong!\n");
+ writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]);
+ /* Flush PCI posted writes */
+ readl(&xhci->dba->doorbell[0]);
+}
+
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+{
+ u64 temp_64;
+ int ret;
+
+ xhci_dbg(xhci, "Abort command ring\n");
+
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
+ &xhci->op_regs->cmd_ring);
+
+ /* Section 4.6.1.2 of xHCI 1.0 spec says software should
+ * time the completion od all xHCI commands, including
+ * the Command Abort operation. If software doesn't see
+ * CRR negated in a timely manner (e.g. longer than 5
+ * seconds), then it should assume that the there are
+ * larger problems with the xHC and assert HCRST.
+ */
+ ret = xhci_handshake(&xhci->op_regs->cmd_ring,
+ CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+
+ return 0;
+}
+
+void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
+ unsigned int slot_id,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ __le32 __iomem *db_addr = &xhci->dba->doorbell[slot_id];
+ struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
+ unsigned int ep_state = ep->ep_state;
+
+ /* Don't ring the doorbell for this endpoint if there are pending
+ * cancellations because we don't want to interrupt processing.
+ * We don't want to restart any stream rings if there's a set dequeue
+ * pointer command pending because the device can choose to start any
+ * stream once the endpoint is on the HW schedule.
+ */
+ if ((ep_state & EP_HALT_PENDING) || (ep_state & SET_DEQ_PENDING) ||
+ (ep_state & EP_HALTED))
+ return;
+ writel(DB_VALUE(ep_index, stream_id), db_addr);
+ /* The CPU has better things to do at this point than wait for a
+ * write-posting flush. It'll get there soon enough.
+ */
+}
+
+/* Ring the doorbell for any rings with pending URBs */
+static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
+ unsigned int slot_id,
+ unsigned int ep_index)
+{
+ unsigned int stream_id;
+ struct xhci_virt_ep *ep;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+
+ /* A ring has pending URBs if its TD list is not empty */
+ if (!(ep->ep_state & EP_HAS_STREAMS)) {
+ if (ep->ring && !(list_empty(&ep->ring->td_list)))
+ xhci_ring_ep_doorbell(xhci, slot_id, ep_index, 0);
+ return;
+ }
+
+ for (stream_id = 1; stream_id < ep->stream_info->num_streams;
+ stream_id++) {
+ struct xhci_stream_info *stream_info = ep->stream_info;
+ if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
+ xhci_ring_ep_doorbell(xhci, slot_id, ep_index,
+ stream_id);
+ }
+}
+
+static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct xhci_virt_ep *ep;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
+ xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
+}
+
+/*
+ * 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.
+ *
+ * Some of the uses of xhci_generic_trb are grotty, but if they're done
+ * with correct __le32 accesses they should work fine. Only users of this are
+ * in here.
+ */
+void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id, struct xhci_td *cur_td,
+ struct xhci_dequeue_state *state)
+{
+ struct xhci_virt_device *dev = xhci->devs[slot_id];
+ struct xhci_virt_ep *ep = &dev->eps[ep_index];
+ struct xhci_ring *ep_ring;
+ struct xhci_segment *new_seg;
+ union xhci_trb *new_deq;
+ dma_addr_t addr;
+ u64 hw_dequeue;
+ bool cycle_found = false;
+ bool td_last_trb_found = false;
+
+ ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
+ ep_index, stream_id);
+ if (!ep_ring) {
+ xhci_warn(xhci, "WARN can't find new dequeue state "
+ "for invalid stream ID %u.\n",
+ stream_id);
+ return;
+ }
+
+ /* Dig out the cycle state saved by the xHC during the stop ep cmd */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Finding endpoint context");
+ /* 4.6.9 the css flag is written to the stream context for streams */
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ struct xhci_stream_ctx *ctx =
+ &ep->stream_info->stream_ctx_array[stream_id];
+ hw_dequeue = le64_to_cpu(ctx->stream_ring);
+ } else {
+ struct xhci_ep_ctx *ep_ctx
+ = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
+ hw_dequeue = le64_to_cpu(ep_ctx->deq);
+ }
+
+ new_seg = ep_ring->deq_seg;
+ new_deq = ep_ring->dequeue;
+ state->new_cycle_state = hw_dequeue & 0x1;
+
+ /*
+ * We want to find the pointer, segment and cycle state of the new trb
+ * (the one after current TD's last_trb). We know the cycle state at
+ * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are
+ * found.
+ */
+ do {
+ if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
+ == (dma_addr_t)(hw_dequeue & ~0xf)) {
+ cycle_found = true;
+ if (td_last_trb_found)
+ break;
+ }
+ if (new_deq == cur_td->last_trb)
+ td_last_trb_found = true;
+
+ if (cycle_found &&
+ TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) &&
+ new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE))
+ state->new_cycle_state ^= 0x1;
+
+ next_trb(xhci, ep_ring, &new_seg, &new_deq);
+
+ /* Search wrapped around, bail out */
+ if (new_deq == ep->ring->dequeue) {
+ xhci_err(xhci, "Error: Failed finding new dequeue state\n");
+ state->new_deq_seg = NULL;
+ state->new_deq_ptr = NULL;
+ return;
+ }
+
+ } while (!cycle_found || !td_last_trb_found);
+
+ state->new_deq_seg = new_seg;
+ state->new_deq_ptr = new_deq;
+
+ /* Don't update the ring cycle state for the producer (us). */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Cycle state = 0x%x", state->new_cycle_state);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "New dequeue segment = %p (virtual)",
+ state->new_deq_seg);
+ addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "New dequeue pointer = 0x%llx (DMA)",
+ (unsigned long long) addr);
+}
+
+/* flip_cycle means flip the cycle bit of all but the first and last TRB.
+ * (The last TRB actually points to the ring enqueue pointer, which is not part
+ * of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
+ */
+static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
+ struct xhci_td *cur_td, bool flip_cycle)
+{
+ 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 (TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) {
+ /* Unchain any chained Link TRBs, but
+ * leave the pointers intact.
+ */
+ cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN);
+ /* Flip the cycle bit (link TRBs can't be the first
+ * or last TRB).
+ */
+ if (flip_cycle)
+ cur_trb->generic.field[3] ^=
+ cpu_to_le32(TRB_CYCLE);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Cancel (unchain) link TRB");
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Address = %p (0x%llx dma); "
+ "in seg %p (0x%llx dma)",
+ 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] &= cpu_to_le32(TRB_CYCLE);
+ /* Flip the cycle bit except on the first or last TRB */
+ if (flip_cycle && cur_trb != cur_td->first_trb &&
+ cur_trb != cur_td->last_trb)
+ cur_trb->generic.field[3] ^=
+ cpu_to_le32(TRB_CYCLE);
+ cur_trb->generic.field[3] |= cpu_to_le32(
+ TRB_TYPE(TRB_TR_NOOP));
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "TRB to noop at offset 0x%llx",
+ (unsigned long long)
+ xhci_trb_virt_to_dma(cur_seg, cur_trb));
+ }
+ if (cur_trb == cur_td->last_trb)
+ break;
+ }
+}
+
+static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
+ struct xhci_virt_ep *ep)
+{
+ ep->ep_state &= ~EP_HALT_PENDING;
+ /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the
+ * timer is running on another CPU, we don't decrement stop_cmds_pending
+ * (since we didn't successfully stop the watchdog timer).
+ */
+ if (del_timer(&ep->stop_cmd_timer))
+ ep->stop_cmds_pending--;
+}
+
+/* Must be called with xhci->lock held in interrupt context */
+static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
+ struct xhci_td *cur_td, int status)
+{
+ struct usb_hcd *hcd;
+ struct urb *urb;
+ struct urb_priv *urb_priv;
+
+ urb = cur_td->urb;
+ urb_priv = urb->hcpriv;
+ urb_priv->td_cnt++;
+ hcd = bus_to_hcd(urb->dev->bus);
+
+ /* Only giveback urb when this is the last td in urb */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ xhci_urb_free_priv(urb_priv);
+ spin_lock(&xhci->lock);
+ }
+}
+
+/*
+ * 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 xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
+ union xhci_trb *trb, struct xhci_event_cmd *event)
+{
+ unsigned int ep_index;
+ struct xhci_ring *ep_ring;
+ struct xhci_virt_ep *ep;
+ struct list_head *entry;
+ struct xhci_td *cur_td = NULL;
+ struct xhci_td *last_unlinked_td;
+
+ struct xhci_dequeue_state deq_state;
+
+ if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
+ if (!xhci->devs[slot_id])
+ xhci_warn(xhci, "Stop endpoint command "
+ "completion for disabled slot %u\n",
+ slot_id);
+ return;
+ }
+
+ memset(&deq_state, 0, sizeof(deq_state));
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+
+ if (list_empty(&ep->cancelled_td_list)) {
+ xhci_stop_watchdog_timer_in_irq(xhci, ep);
+ ep->stopped_td = NULL;
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+ 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->cancelled_td_list) {
+ cur_td = list_entry(entry, struct xhci_td, cancelled_td_list);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Removing canceled TD starting at 0x%llx (dma).",
+ (unsigned long long)xhci_trb_virt_to_dma(
+ cur_td->start_seg, cur_td->first_trb));
+ ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
+ if (!ep_ring) {
+ /* This shouldn't happen unless a driver is mucking
+ * with the stream ID after submission. This will
+ * leave the TD on the hardware ring, and the hardware
+ * will try to execute it, and may access a buffer
+ * that has already been freed. In the best case, the
+ * hardware will execute it, and the event handler will
+ * ignore the completion event for that TD, since it was
+ * removed from the td_list for that endpoint. In
+ * short, don't muck with the stream ID after
+ * submission.
+ */
+ xhci_warn(xhci, "WARN Cancelled URB %p "
+ "has invalid stream ID %u.\n",
+ cur_td->urb,
+ cur_td->urb->stream_id);
+ goto remove_finished_td;
+ }
+ /*
+ * 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->stopped_td)
+ xhci_find_new_dequeue_state(xhci, slot_id, ep_index,
+ cur_td->urb->stream_id,
+ cur_td, &deq_state);
+ else
+ td_to_noop(xhci, ep_ring, cur_td, false);
+remove_finished_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_init(&cur_td->td_list);
+ }
+ last_unlinked_td = cur_td;
+ xhci_stop_watchdog_timer_in_irq(xhci, ep);
+
+ /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
+ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
+ xhci_queue_new_dequeue_state(xhci, slot_id, ep_index,
+ ep->stopped_td->urb->stream_id, &deq_state);
+ xhci_ring_cmd_db(xhci);
+ } else {
+ /* Otherwise ring the doorbell(s) to restart queued transfers */
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+ }
+
+ ep->stopped_td = NULL;
+
+ /*
+ * 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->cancelled_td_list.next,
+ struct xhci_td, cancelled_td_list);
+ list_del_init(&cur_td->cancelled_td_list);
+
+ /* Clean up the cancelled URB */
+ /* Doesn't matter what we pass for status, since the core will
+ * just overwrite it (because the URB has been unlinked).
+ */
+ xhci_giveback_urb_in_irq(xhci, cur_td, 0);
+
+ /* Stop processing the cancelled list if the watchdog timer is
+ * running.
+ */
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return;
+ } while (cur_td != last_unlinked_td);
+
+ /* Return to the event handler with xhci->lock re-acquired */
+}
+
+static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+ struct xhci_td *cur_td;
+
+ while (!list_empty(&ring->td_list)) {
+ cur_td = list_first_entry(&ring->td_list,
+ struct xhci_td, td_list);
+ list_del_init(&cur_td->td_list);
+ if (!list_empty(&cur_td->cancelled_td_list))
+ list_del_init(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ }
+}
+
+static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
+ int slot_id, int ep_index)
+{
+ struct xhci_td *cur_td;
+ struct xhci_virt_ep *ep;
+ struct xhci_ring *ring;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ if ((ep->ep_state & EP_HAS_STREAMS) ||
+ (ep->ep_state & EP_GETTING_NO_STREAMS)) {
+ int stream_id;
+
+ for (stream_id = 0; stream_id < ep->stream_info->num_streams;
+ stream_id++) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Killing URBs for slot ID %u, ep index %u, stream %u",
+ slot_id, ep_index, stream_id + 1);
+ xhci_kill_ring_urbs(xhci,
+ ep->stream_info->stream_rings[stream_id]);
+ }
+ } else {
+ ring = ep->ring;
+ if (!ring)
+ return;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Killing URBs for slot ID %u, ep index %u",
+ slot_id, ep_index);
+ xhci_kill_ring_urbs(xhci, ring);
+ }
+ while (!list_empty(&ep->cancelled_td_list)) {
+ cur_td = list_first_entry(&ep->cancelled_td_list,
+ struct xhci_td, cancelled_td_list);
+ list_del_init(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ }
+}
+
+/* Watchdog timer function for when a stop endpoint command fails to complete.
+ * In this case, we assume the host controller is broken or dying or dead. The
+ * host may still be completing some other events, so we have to be careful to
+ * let the event ring handler and the URB dequeueing/enqueueing functions know
+ * through xhci->state.
+ *
+ * The timer may also fire if the host takes a very long time to respond to the
+ * command, and the stop endpoint command completion handler cannot delete the
+ * timer before the timer function is called. Another endpoint cancellation may
+ * sneak in before the timer function can grab the lock, and that may queue
+ * another stop endpoint command and add the timer back. So we cannot use a
+ * simple flag to say whether there is a pending stop endpoint command for a
+ * particular endpoint.
+ *
+ * Instead we use a combination of that flag and a counter for the number of
+ * pending stop endpoint commands. If the timer is the tail end of the last
+ * stop endpoint command, and the endpoint's command is still pending, we assume
+ * the host is dying.
+ */
+void xhci_stop_endpoint_command_watchdog(unsigned long arg)
+{
+ struct xhci_hcd *xhci;
+ struct xhci_virt_ep *ep;
+ int ret, i, j;
+ unsigned long flags;
+
+ ep = (struct xhci_virt_ep *) arg;
+ xhci = ep->xhci;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ ep->stop_cmds_pending--;
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Stop EP timer ran, but another timer marked "
+ "xHCI as DYING, exiting.");
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
+ }
+ if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Stop EP timer ran, but no command pending, "
+ "exiting.");
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
+ }
+
+ xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
+ xhci_warn(xhci, "Assuming host is dying, halting host.\n");
+ /* Oops, HC is dead or dying or at least not responding to the stop
+ * endpoint command.
+ */
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ /* Disable interrupts from the host controller and start halting it */
+ xhci_quiesce(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ ret = xhci_halt(xhci);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (ret < 0) {
+ /* This is bad; the host is not responding to commands and it's
+ * not allowing itself to be halted. At least interrupts are
+ * disabled. If we call usb_hc_died(), it will attempt to
+ * disconnect all device drivers under this host. Those
+ * disconnect() methods will wait for all URBs to be unlinked,
+ * so we must complete them.
+ */
+ xhci_warn(xhci, "Non-responsive xHCI host is not halting.\n");
+ xhci_warn(xhci, "Completing active URBs anyway.\n");
+ /* We could turn all TDs on the rings to no-ops. This won't
+ * help if the host has cached part of the ring, and is slow if
+ * we want to preserve the cycle bit. Skip it and hope the host
+ * doesn't touch the memory.
+ */
+ }
+ for (i = 0; i < MAX_HC_SLOTS; i++) {
+ if (!xhci->devs[i])
+ continue;
+ for (j = 0; j < 31; j++)
+ xhci_kill_endpoint_urbs(xhci, i, j);
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Calling usb_hc_died()");
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "xHCI host controller is dead.");
+}
+
+
+static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
+ struct xhci_virt_device *dev,
+ struct xhci_ring *ep_ring,
+ unsigned int ep_index)
+{
+ union xhci_trb *dequeue_temp;
+ int num_trbs_free_temp;
+ bool revert = false;
+
+ num_trbs_free_temp = ep_ring->num_trbs_free;
+ dequeue_temp = ep_ring->dequeue;
+
+ /* If we get two back-to-back stalls, and the first stalled transfer
+ * ends just before a link TRB, the dequeue pointer will be left on
+ * the link TRB by the code in the while loop. So we have to update
+ * the dequeue pointer one segment further, or we'll jump off
+ * the segment into la-la-land.
+ */
+ if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) {
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+
+ while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
+ /* We have more usable TRBs */
+ ep_ring->num_trbs_free++;
+ ep_ring->dequeue++;
+ if (last_trb(xhci, ep_ring, ep_ring->deq_seg,
+ ep_ring->dequeue)) {
+ if (ep_ring->dequeue ==
+ dev->eps[ep_index].queued_deq_ptr)
+ break;
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+ if (ep_ring->dequeue == dequeue_temp) {
+ revert = true;
+ break;
+ }
+ }
+
+ if (revert) {
+ xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
+ ep_ring->num_trbs_free = num_trbs_free_temp;
+ }
+}
+
+/*
+ * 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 xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
+ union xhci_trb *trb, u32 cmd_comp_code)
+{
+ unsigned int ep_index;
+ unsigned int stream_id;
+ struct xhci_ring *ep_ring;
+ struct xhci_virt_device *dev;
+ struct xhci_virt_ep *ep;
+ struct xhci_ep_ctx *ep_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
+ dev = xhci->devs[slot_id];
+ ep = &dev->eps[ep_index];
+
+ ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
+ if (!ep_ring) {
+ xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n",
+ stream_id);
+ /* XXX: Harmless??? */
+ goto cleanup;
+ }
+
+ ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
+ slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
+
+ if (cmd_comp_code != COMP_SUCCESS) {
+ unsigned int ep_state;
+ unsigned int slot_state;
+
+ switch (cmd_comp_code) {
+ 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 = le32_to_cpu(ep_ctx->ep_info);
+ ep_state &= EP_STATE_MASK;
+ slot_state = le32_to_cpu(slot_ctx->dev_state);
+ slot_state = GET_SLOT_STATE(slot_state);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Slot state = %u, EP state = %u",
+ 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",
+ cmd_comp_code);
+ 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 {
+ u64 deq;
+ /* 4.6.10 deq ptr is written to the stream ctx for streams */
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ struct xhci_stream_ctx *ctx =
+ &ep->stream_info->stream_ctx_array[stream_id];
+ deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
+ } else {
+ deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
+ if (xhci_trb_virt_to_dma(ep->queued_deq_seg,
+ ep->queued_deq_ptr) == deq) {
+ /* Update the ring's dequeue segment and dequeue pointer
+ * to reflect the new position.
+ */
+ update_ring_for_set_deq_completion(xhci, dev,
+ ep_ring, ep_index);
+ } else {
+ xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
+ xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
+ ep->queued_deq_seg, ep->queued_deq_ptr);
+ }
+ }
+
+cleanup:
+ dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
+ dev->eps[ep_index].queued_deq_seg = NULL;
+ dev->eps[ep_index].queued_deq_ptr = NULL;
+ /* Restart any rings with pending URBs */
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+}
+
+static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
+ union xhci_trb *trb, u32 cmd_comp_code)
+{
+ unsigned int ep_index;
+
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ /* This command will only fail if the endpoint wasn't halted,
+ * but we don't care.
+ */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
+ "Ignoring reset ep completion code of %u", cmd_comp_code);
+
+ /* HW with the reset endpoint quirk needs to have a configure endpoint
+ * command complete before the endpoint can be used. Queue that here
+ * because the HW can't handle two commands being queued in a row.
+ */
+ if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n");
+ return;
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Queueing configure endpoint command");
+ xhci_queue_configure_endpoint(xhci, command,
+ xhci->devs[slot_id]->in_ctx->dma, slot_id,
+ false);
+ xhci_ring_cmd_db(xhci);
+ } else {
+ /* Clear our internal halted state */
+ xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
+ }
+}
+
+static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
+ u32 cmd_comp_code)
+{
+ if (cmd_comp_code == COMP_SUCCESS)
+ xhci->slot_id = slot_id;
+ else
+ xhci->slot_id = 0;
+}
+
+static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *virt_dev;
+
+ virt_dev = xhci->devs[slot_id];
+ if (!virt_dev)
+ return;
+ if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
+ /* Delete default control endpoint resources */
+ xhci_free_device_endpoint_resources(xhci, virt_dev, true);
+ xhci_free_virt_device(xhci, slot_id);
+}
+
+static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
+ struct xhci_event_cmd *event, u32 cmd_comp_code)
+{
+ struct xhci_virt_device *virt_dev;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ unsigned int ep_index;
+ unsigned int ep_state;
+ u32 add_flags, drop_flags;
+
+ /*
+ * Configure endpoint commands can come from the USB core
+ * configuration or alt setting changes, or because the HW
+ * needed an extra configure endpoint command after a reset
+ * endpoint command or streams were being configured.
+ * If the command was for a halted endpoint, the xHCI driver
+ * is not waiting on the configure endpoint command.
+ */
+ virt_dev = xhci->devs[slot_id];
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "Could not get input context, bad type.\n");
+ return;
+ }
+
+ add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+ drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+ /* Input ctx add_flags are the endpoint index plus one */
+ ep_index = xhci_last_valid_endpoint(add_flags) - 1;
+
+ /* A usb_set_interface() call directly after clearing a halted
+ * condition may race on this quirky hardware. Not worth
+ * worrying about, since this is prototype hardware. Not sure
+ * if this will work for streams, but streams support was
+ * untested on this prototype.
+ */
+ if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
+ ep_index != (unsigned int) -1 &&
+ add_flags - SLOT_FLAG == drop_flags) {
+ ep_state = virt_dev->eps[ep_index].ep_state;
+ if (!(ep_state & EP_HALTED))
+ return;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Completed config ep cmd - "
+ "last ep index = %d, state = %d",
+ ep_index, ep_state);
+ /* Clear internal halted state and restart ring(s) */
+ virt_dev->eps[ep_index].ep_state &= ~EP_HALTED;
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+ return;
+ }
+ return;
+}
+
+static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id,
+ struct xhci_event_cmd *event)
+{
+ xhci_dbg(xhci, "Completed reset device command.\n");
+ if (!xhci->devs[slot_id])
+ xhci_warn(xhci, "Reset device command completion "
+ "for disabled slot %u\n", slot_id);
+}
+
+static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
+ struct xhci_event_cmd *event)
+{
+ if (!(xhci->quirks & XHCI_NEC_HOST)) {
+ xhci->error_bitmask |= 1 << 6;
+ return;
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "NEC firmware version %2x.%02x",
+ NEC_FW_MAJOR(le32_to_cpu(event->status)),
+ NEC_FW_MINOR(le32_to_cpu(event->status)));
+}
+
+static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 status)
+{
+ list_del(&cmd->cmd_list);
+
+ if (cmd->completion) {
+ cmd->status = status;
+ complete(cmd->completion);
+ } else {
+ kfree(cmd);
+ }
+}
+
+void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
+{
+ struct xhci_command *cur_cmd, *tmp_cmd;
+ list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
+ xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd, *tmp_cmd;
+ u32 cycle_state;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
+ cmd_list) {
+
+ if (i_cmd->status != COMP_CMD_ABORT)
+ continue;
+
+ i_cmd->status = COMP_CMD_STOP;
+
+ xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
+ i_cmd->command_trb);
+ /* get cycle state from the original cmd trb */
+ cycle_state = le32_to_cpu(
+ i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
+ /* modify the command trb to no-op command */
+ i_cmd->command_trb->generic.field[0] = 0;
+ i_cmd->command_trb->generic.field[1] = 0;
+ i_cmd->command_trb->generic.field[2] = 0;
+ i_cmd->command_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+ return;
+}
+
+
+void xhci_handle_command_timeout(unsigned long data)
+{
+ struct xhci_hcd *xhci;
+ int ret;
+ unsigned long flags;
+ u64 hw_ring_state;
+ struct xhci_command *cur_cmd = NULL;
+ xhci = (struct xhci_hcd *) data;
+
+ /* mark this command to be cancelled */
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->current_cmd) {
+ cur_cmd = xhci->current_cmd;
+ cur_cmd->status = COMP_CMD_ABORT;
+ }
+
+
+ /* Make sure command ring is running before aborting it */
+ hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
+ (hw_ring_state & CMD_RING_RUNNING)) {
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "Command timeout\n");
+ ret = xhci_abort_cmd_ring(xhci);
+ if (unlikely(ret == -ESHUTDOWN)) {
+ xhci_err(xhci, "Abort command ring failed\n");
+ xhci_cleanup_command_queue(xhci);
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ xhci_dbg(xhci, "xHCI host controller is dead.\n");
+ }
+ return;
+ }
+ /* command timeout on stopped ring, ring can't be aborted */
+ xhci_dbg(xhci, "Command timeout on stopped ring\n");
+ xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
+}
+
+static void handle_cmd_completion(struct xhci_hcd *xhci,
+ struct xhci_event_cmd *event)
+{
+ int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ u64 cmd_dma;
+ dma_addr_t cmd_dequeue_dma;
+ u32 cmd_comp_code;
+ union xhci_trb *cmd_trb;
+ struct xhci_command *cmd;
+ u32 cmd_type;
+
+ cmd_dma = le64_to_cpu(event->cmd_trb);
+ cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
+ cmd_trb);
+ /* 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;
+ }
+
+ cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
+
+ if (cmd->command_trb != xhci->cmd_ring->dequeue) {
+ xhci_err(xhci,
+ "Command completion event does not match command\n");
+ return;
+ }
+
+ del_timer(&xhci->cmd_timer);
+
+ trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
+
+ cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
+
+ /* If CMD ring stopped we own the trbs between enqueue and dequeue */
+ if (cmd_comp_code == COMP_CMD_STOP) {
+ xhci_handle_stopped_cmd_ring(xhci, cmd);
+ return;
+ }
+ /*
+ * Host aborted the command ring, check if the current command was
+ * supposed to be aborted, otherwise continue normally.
+ * The command ring is stopped now, but the xHC will issue a Command
+ * Ring Stopped event which will cause us to restart it.
+ */
+ if (cmd_comp_code == COMP_CMD_ABORT) {
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ if (cmd->status == COMP_CMD_ABORT)
+ goto event_handled;
+ }
+
+ cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
+ switch (cmd_type) {
+ case TRB_ENABLE_SLOT:
+ xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code);
+ break;
+ case TRB_DISABLE_SLOT:
+ xhci_handle_cmd_disable_slot(xhci, slot_id);
+ break;
+ case TRB_CONFIG_EP:
+ if (!cmd->completion)
+ xhci_handle_cmd_config_ep(xhci, slot_id, event,
+ cmd_comp_code);
+ break;
+ case TRB_EVAL_CONTEXT:
+ break;
+ case TRB_ADDR_DEV:
+ break;
+ case TRB_STOP_RING:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
+ break;
+ case TRB_SET_DEQ:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code);
+ break;
+ case TRB_CMD_NOOP:
+ /* Is this an aborted command turned to NO-OP? */
+ if (cmd->status == COMP_CMD_STOP)
+ cmd_comp_code = COMP_CMD_STOP;
+ break;
+ case TRB_RESET_EP:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ xhci_handle_cmd_reset_ep(xhci, slot_id, cmd_trb, cmd_comp_code);
+ break;
+ case TRB_RESET_DEV:
+ /* SLOT_ID field in reset device cmd completion event TRB is 0.
+ * Use the SLOT_ID from the command TRB instead (xhci 4.6.11)
+ */
+ slot_id = TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3]));
+ xhci_handle_cmd_reset_dev(xhci, slot_id, event);
+ break;
+ case TRB_NEC_GET_FW:
+ xhci_handle_cmd_nec_get_fw(xhci, event);
+ break;
+ default:
+ /* Skip over unknown commands on the event ring */
+ xhci->error_bitmask |= 1 << 6;
+ break;
+ }
+
+ /* restart timer if this wasn't the last command */
+ if (cmd->cmd_list.next != &xhci->cmd_list) {
+ xhci->current_cmd = list_entry(cmd->cmd_list.next,
+ struct xhci_command, cmd_list);
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ }
+
+event_handled:
+ xhci_complete_del_and_free_cmd(cmd, cmd_comp_code);
+
+ inc_deq(xhci, xhci->cmd_ring);
+}
+
+static void handle_vendor_event(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+{
+ u32 trb_type;
+
+ trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3]));
+ xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type);
+ if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST))
+ handle_cmd_completion(xhci, &event->event_cmd);
+}
+
+/* @port_id: the one-based port ID from the hardware (indexed from array of all
+ * port registers -- USB 3.0 and USB 2.0).
+ *
+ * Returns a zero-based port number, which is suitable for indexing into each of
+ * the split roothubs' port arrays and bus state arrays.
+ * Add one to it in order to call xhci_find_slot_id_by_port.
+ */
+static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
+ struct xhci_hcd *xhci, u32 port_id)
+{
+ unsigned int i;
+ unsigned int num_similar_speed_ports = 0;
+
+ /* port_id from the hardware is 1-based, but port_array[], usb3_ports[],
+ * and usb2_ports are 0-based indexes. Count the number of similar
+ * speed ports, up to 1 port before this port.
+ */
+ for (i = 0; i < (port_id - 1); i++) {
+ u8 port_speed = xhci->port_array[i];
+
+ /*
+ * Skip ports that don't have known speeds, or have duplicate
+ * Extended Capabilities port speed entries.
+ */
+ if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
+ continue;
+
+ /*
+ * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and
+ * 1.1 ports are under the USB 2.0 hub. If the port speed
+ * matches the device speed, it's a similar speed port.
+ */
+ if ((port_speed == 0x03) == (hcd->speed == HCD_USB3))
+ num_similar_speed_ports++;
+ }
+ return num_similar_speed_ports;
+}
+
+static void handle_device_notification(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+{
+ u32 slot_id;
+ struct usb_device *udev;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->generic.field[3]));
+ if (!xhci->devs[slot_id]) {
+ xhci_warn(xhci, "Device Notification event for "
+ "unused slot %u\n", slot_id);
+ return;
+ }
+
+ xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n",
+ slot_id);
+ udev = xhci->devs[slot_id]->udev;
+ if (udev && udev->parent)
+ usb_wakeup_notification(udev->parent, udev->portnum);
+}
+
+static void handle_port_status(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+{
+ struct usb_hcd *hcd;
+ u32 port_id;
+ u32 temp, temp1;
+ int max_ports;
+ int slot_id;
+ unsigned int faked_port_index;
+ u8 major_revision;
+ struct xhci_bus_state *bus_state;
+ __le32 __iomem **port_array;
+ bool bogus_port_status = false;
+
+ /* Port status change events always have a successful completion code */
+ if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) {
+ xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
+ xhci->error_bitmask |= 1 << 8;
+ }
+ port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
+ xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
+
+ max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ if ((port_id <= 0) || (port_id > max_ports)) {
+ xhci_warn(xhci, "Invalid port id %d\n", port_id);
+ inc_deq(xhci, xhci->event_ring);
+ return;
+ }
+
+ /* Figure out which usb_hcd this port is attached to:
+ * is it a USB 3.0 port or a USB 2.0/1.1 port?
+ */
+ major_revision = xhci->port_array[port_id - 1];
+
+ /* Find the right roothub. */
+ hcd = xhci_to_hcd(xhci);
+ if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
+ hcd = xhci->shared_hcd;
+
+ if (major_revision == 0) {
+ xhci_warn(xhci, "Event for port %u not in "
+ "Extended Capabilities, ignoring.\n",
+ port_id);
+ bogus_port_status = true;
+ goto cleanup;
+ }
+ if (major_revision == DUPLICATE_ENTRY) {
+ xhci_warn(xhci, "Event for port %u duplicated in"
+ "Extended Capabilities, ignoring.\n",
+ port_id);
+ bogus_port_status = true;
+ goto cleanup;
+ }
+
+ /*
+ * Hardware port IDs reported by a Port Status Change Event include USB
+ * 3.0 and USB 2.0 ports. We want to check if the port has reported a
+ * resume event, but we first need to translate the hardware port ID
+ * into the index into the ports on the correct split roothub, and the
+ * correct bus_state structure.
+ */
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+ if (hcd->speed == HCD_USB3)
+ port_array = xhci->usb3_ports;
+ else
+ port_array = xhci->usb2_ports;
+ /* Find the faked port hub number */
+ faked_port_index = find_faked_portnum_from_hw_portnum(hcd, xhci,
+ port_id);
+
+ temp = readl(port_array[faked_port_index]);
+ if (hcd->state == HC_STATE_SUSPENDED) {
+ xhci_dbg(xhci, "resume root hub\n");
+ usb_hcd_resume_root_hub(hcd);
+ }
+
+ if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) {
+ xhci_dbg(xhci, "port resume event for port %d\n", port_id);
+
+ temp1 = readl(&xhci->op_regs->command);
+ if (!(temp1 & CMD_RUN)) {
+ xhci_warn(xhci, "xHC is not running.\n");
+ goto cleanup;
+ }
+
+ if (DEV_SUPERSPEED(temp)) {
+ xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
+ /* Set a flag to say the port signaled remote wakeup,
+ * so we can tell the difference between the end of
+ * device and host initiated resume.
+ */
+ bus_state->port_remote_wakeup |= 1 << faked_port_index;
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
+ xhci_set_link_state(xhci, port_array, faked_port_index,
+ XDEV_U0);
+ /* Need to wait until the next link state change
+ * indicates the device is actually in U0.
+ */
+ bogus_port_status = true;
+ goto cleanup;
+ } else {
+ xhci_dbg(xhci, "resume HS port %d\n", port_id);
+ bus_state->resume_done[faked_port_index] = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ set_bit(faked_port_index, &bus_state->resuming_ports);
+ mod_timer(&hcd->rh_timer,
+ bus_state->resume_done[faked_port_index]);
+ /* Do the rest in GetPortStatus */
+ }
+ }
+
+ if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
+ DEV_SUPERSPEED(temp)) {
+ xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
+ /* We've just brought the device into U0 through either the
+ * Resume state after a device remote wakeup, or through the
+ * U3Exit state after a host-initiated resume. If it's a device
+ * initiated remote wake, don't pass up the link state change,
+ * so the roothub behavior is consistent with external
+ * USB 3.0 hub behavior.
+ */
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ faked_port_index + 1);
+ if (slot_id && xhci->devs[slot_id])
+ xhci_ring_device(xhci, slot_id);
+ if (bus_state->port_remote_wakeup & (1 << faked_port_index)) {
+ bus_state->port_remote_wakeup &=
+ ~(1 << faked_port_index);
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
+ usb_wakeup_notification(hcd->self.root_hub,
+ faked_port_index + 1);
+ bogus_port_status = true;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Check to see if xhci-hub.c is waiting on RExit to U0 transition (or
+ * RExit to a disconnect state). If so, let the the driver know it's
+ * out of the RExit state.
+ */
+ if (!DEV_SUPERSPEED(temp) &&
+ test_and_clear_bit(faked_port_index,
+ &bus_state->rexit_ports)) {
+ complete(&bus_state->rexit_done[faked_port_index]);
+ bogus_port_status = true;
+ goto cleanup;
+ }
+
+ if (hcd->speed != HCD_USB3)
+ xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
+ PORT_PLC);
+
+cleanup:
+ /* Update event ring dequeue pointer before dropping the lock */
+ inc_deq(xhci, xhci->event_ring);
+
+ /* Don't make the USB core poll the roothub if we got a bad port status
+ * change event. Besides, at that point we can't tell which roothub
+ * (USB 2.0 or USB 3.0) to kick.
+ */
+ if (bogus_port_status)
+ return;
+
+ /*
+ * xHCI port-status-change events occur when the "or" of all the
+ * status-change bits in the portsc register changes from 0 to 1.
+ * New status changes won't cause an event if any other change
+ * bits are still set. When an event occurs, switch over to
+ * polling to avoid losing status changes.
+ */
+ xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ spin_unlock(&xhci->lock);
+ /* Pass this up to the core */
+ usb_hcd_poll_rh_status(hcd);
+ 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.
+ */
+struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
+ struct xhci_segment *start_seg,
+ union xhci_trb *start_trb,
+ union xhci_trb *end_trb,
+ dma_addr_t suspect_dma,
+ bool debug)
+{
+ 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 {
+ if (start_dma == 0)
+ return NULL;
+ /* 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,
+ &cur_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 (debug)
+ xhci_warn(xhci,
+ "Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n",
+ (unsigned long long)suspect_dma,
+ (unsigned long long)start_dma,
+ (unsigned long long)end_trb_dma,
+ (unsigned long long)cur_seg->dma,
+ (unsigned long long)end_seg_dma);
+
+ 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 NULL;
+ } 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 (cur_seg != start_seg);
+
+ return NULL;
+}
+
+static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id,
+ struct xhci_td *td, union xhci_trb *event_trb)
+{
+ struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command)
+ return;
+
+ ep->ep_state |= EP_HALTED;
+ ep->stopped_stream = stream_id;
+
+ xhci_queue_reset_ep(xhci, command, slot_id, ep_index);
+ xhci_cleanup_stalled_ring(xhci, ep_index, td);
+
+ ep->stopped_stream = 0;
+
+ xhci_ring_cmd_db(xhci);
+}
+
+/* Check if an error has halted the endpoint ring. The class driver will
+ * cleanup the halt for a non-default control endpoint if we indicate a stall.
+ * However, a babble and other errors also halt the endpoint ring, and the class
+ * driver won't clear the halt in that case, so we need to issue a Set Transfer
+ * Ring Dequeue Pointer command manually.
+ */
+static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
+ struct xhci_ep_ctx *ep_ctx,
+ unsigned int trb_comp_code)
+{
+ /* TRB completion codes that may require a manual halt cleanup */
+ if (trb_comp_code == COMP_TX_ERR ||
+ trb_comp_code == COMP_BABBLE ||
+ trb_comp_code == COMP_SPLIT_ERR)
+ /* The 0.96 spec says a babbling control endpoint
+ * is not halted. The 0.96 spec says it is. Some HW
+ * claims to be 0.95 compliant, but it halts the control
+ * endpoint anyway. Check if a babble halted the
+ * endpoint.
+ */
+ if ((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) ==
+ cpu_to_le32(EP_STATE_HALTED))
+ return 1;
+
+ return 0;
+}
+
+int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
+{
+ if (trb_comp_code >= 224 && trb_comp_code <= 255) {
+ /* Vendor defined "informational" completion code,
+ * treat as not-an-error.
+ */
+ xhci_dbg(xhci, "Vendor defined info completion code %u\n",
+ trb_comp_code);
+ xhci_dbg(xhci, "Treating code as success.\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Finish the td processing, remove the td from td list;
+ * Return 1 if the urb can be given back.
+ */
+static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status, bool skip)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct urb *urb = NULL;
+ struct xhci_ep_ctx *ep_ctx;
+ int ret = 0;
+ struct urb_priv *urb_priv;
+ u32 trb_comp_code;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ xdev = xhci->devs[slot_id];
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+
+ if (skip)
+ goto td_cleanup;
+
+ if (trb_comp_code == COMP_STOP_INVAL || trb_comp_code == COMP_STOP) {
+ /* 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.
+ */
+ ep->stopped_td = td;
+ return 0;
+ }
+ if (trb_comp_code == COMP_STALL ||
+ xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+ trb_comp_code)) {
+ /* Issue a reset endpoint command to clear the host side
+ * halt, followed by a set dequeue command to move the
+ * dequeue pointer past the TD.
+ * The class driver clears the device side halt later.
+ */
+ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+ ep_ring->stream_id, td, event_trb);
+ } else {
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring);
+ inc_deq(xhci, ep_ring);
+ }
+
+td_cleanup:
+ /* Clean up the endpoint's TD list */
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+
+ /* Do one last check of the actual transfer length.
+ * If the host controller said we transferred more data than the buffer
+ * length, urb->actual_length will be a very big number (since it's
+ * unsigned). Play it safe and say we didn't transfer anything.
+ */
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ xhci_warn(xhci, "URB transfer length is wrong, xHC issue? req. len = %u, act. len = %u\n",
+ urb->transfer_buffer_length,
+ urb->actual_length);
+ urb->actual_length = 0;
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ list_del_init(&td->td_list);
+ /* Was this TD slated to be cancelled but completed anyway? */
+ if (!list_empty(&td->cancelled_td_list))
+ list_del_init(&td->cancelled_td_list);
+
+ urb_priv->td_cnt++;
+ /* Giveback the urb when all the tds are completed */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ ret = 1;
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Process control tds, update urb status and actual_length.
+ */
+static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ xdev = xhci->devs[slot_id];
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+
+ switch (trb_comp_code) {
+ 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 {
+ *status = 0;
+ }
+ break;
+ case COMP_SHORT_TX:
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ break;
+ case COMP_STOP_INVAL:
+ case COMP_STOP:
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+ default:
+ if (!xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code))
+ break;
+ xhci_dbg(xhci, "TRB error code %u, "
+ "halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
+ /* else fall through */
+ case COMP_STALL:
+ /* Did we transfer part of the data (middle) phase? */
+ if (event_trb != ep_ring->dequeue &&
+ event_trb != td->last_trb)
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ else
+ td->urb->actual_length = 0;
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+ }
+ /*
+ * 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) {
+ if (td->urb_length_set) {
+ /* Don't overwrite a previously set error code
+ */
+ if ((*status == -EINPROGRESS || *status == 0) &&
+ (td->urb->transfer_flags
+ & URB_SHORT_NOT_OK))
+ /* Did we already see a short data
+ * stage? */
+ *status = -EREMOTEIO;
+ } else {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length;
+ }
+ } else {
+ /*
+ * Maybe the event was for the data stage? If so, update
+ * already the actual_length of the URB and flag it as
+ * set, so that it is not overwritten in the event for
+ * the last TRB.
+ */
+ td->urb_length_set = true;
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ xhci_dbg(xhci, "Waiting for status "
+ "stage event\n");
+ return 0;
+ }
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * Process isochronous tds, update urb packet status and actual_length.
+ */
+static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ int idx;
+ int len = 0;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ struct usb_iso_packet_descriptor *frame;
+ u32 trb_comp_code;
+ bool skip_td = false;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
+
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
+ frame->status = 0;
+ break;
+ }
+ if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
+ trb_comp_code = COMP_SHORT_TX;
+ case COMP_SHORT_TX:
+ frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
+ -EREMOTEIO : 0;
+ break;
+ case COMP_BW_OVER:
+ frame->status = -ECOMM;
+ skip_td = true;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ frame->status = -EOVERFLOW;
+ skip_td = true;
+ break;
+ case COMP_DEV_ERR:
+ case COMP_STALL:
+ frame->status = -EPROTO;
+ skip_td = true;
+ break;
+ case COMP_TX_ERR:
+ frame->status = -EPROTO;
+ if (event_trb != td->last_trb)
+ return 0;
+ skip_td = true;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ frame->status = -1;
+ break;
+ }
+
+ if (trb_comp_code == COMP_SUCCESS || skip_td) {
+ frame->actual_length = frame->length;
+ td->urb->actual_length += frame->length;
+ } else {
+ 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_NOOP_LE32(cur_trb->generic.field[3]) &&
+ !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
+ len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
+ }
+ len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
+ if (trb_comp_code != COMP_STOP_INVAL) {
+ frame->actual_length = len;
+ td->urb->actual_length += len;
+ }
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ struct usb_iso_packet_descriptor *frame;
+ int idx;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
+
+ /* The transfer is partly done. */
+ frame->status = -EXDEV;
+
+ /* calc actual length */
+ frame->actual_length = 0;
+
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring);
+ inc_deq(xhci, ep_ring);
+
+ return finish_td(xhci, td, NULL, event, ep, status, true);
+}
+
+/*
+ * Process bulk and interrupt tds, update urb status and actual_length.
+ */
+static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ u32 trb_comp_code;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ /* Double check that the HW transferred everything. */
+ if (event_trb != td->last_trb ||
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
+ xhci_warn(xhci, "WARN Successful completion "
+ "on short TX\n");
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
+ trb_comp_code = COMP_SHORT_TX;
+ } else {
+ *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;
+ }
+ if (trb_comp_code == COMP_SHORT_TX)
+ xhci_dbg(xhci, "ep %#x - asked for %d bytes, "
+ "%d bytes untransferred\n",
+ td->urb->ep->desc.bEndpointAddress,
+ td->urb->transfer_buffer_length,
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
+ /* Fast path - was this the last TRB in the TD for this URB? */
+ if (event_trb == td->last_trb) {
+ if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ if (td->urb->transfer_buffer_length <
+ td->urb->actual_length) {
+ xhci_warn(xhci, "HC gave bad length "
+ "of %d bytes left\n",
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
+ td->urb->actual_length = 0;
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ /* Don't overwrite a previously set error code */
+ if (*status == -EINPROGRESS) {
+ 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.
+ */
+ if (*status == -EREMOTEIO)
+ *status = 0;
+ }
+ } else {
+ /* Slow path - walk the list, starting from the dequeue
+ * pointer, to get the actual length transferred.
+ */
+ 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_NOOP_LE32(cur_trb->generic.field[3]) &&
+ !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
+ td->urb->actual_length +=
+ TRB_LEN(le32_to_cpu(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 (trb_comp_code != COMP_STOP_INVAL)
+ td->urb->actual_length +=
+ TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
+ EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * 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)
+ __releases(&xhci->lock)
+ __acquires(&xhci->lock)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_virt_ep *ep;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct xhci_td *td = NULL;
+ dma_addr_t event_dma;
+ struct xhci_segment *event_seg;
+ union xhci_trb *event_trb;
+ struct urb *urb = NULL;
+ int status = -EINPROGRESS;
+ struct urb_priv *urb_priv;
+ struct xhci_ep_ctx *ep_ctx;
+ struct list_head *tmp;
+ u32 trb_comp_code;
+ int ret = 0;
+ int td_num = 0;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ xdev = xhci->devs[slot_id];
+ if (!xdev) {
+ xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
+ xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
+ (unsigned long long) xhci_trb_virt_to_dma(
+ xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue),
+ lower_32_bits(le64_to_cpu(event->buffer)),
+ upper_32_bits(le64_to_cpu(event->buffer)),
+ le32_to_cpu(event->transfer_len),
+ le32_to_cpu(event->flags));
+ xhci_dbg(xhci, "Event ring:\n");
+ xhci_debug_segment(xhci, xhci->event_ring->deq_seg);
+ return -ENODEV;
+ }
+
+ /* Endpoint ID is 1 based, our index is zero based */
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep = &xdev->eps[ep_index];
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ if (!ep_ring ||
+ (le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
+ EP_STATE_DISABLED) {
+ xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
+ "or incorrect stream ring\n");
+ xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
+ (unsigned long long) xhci_trb_virt_to_dma(
+ xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue),
+ lower_32_bits(le64_to_cpu(event->buffer)),
+ upper_32_bits(le64_to_cpu(event->buffer)),
+ le32_to_cpu(event->transfer_len),
+ le32_to_cpu(event->flags));
+ xhci_dbg(xhci, "Event ring:\n");
+ xhci_debug_segment(xhci, xhci->event_ring->deq_seg);
+ return -ENODEV;
+ }
+
+ /* Count current td numbers if ep->skip is set */
+ if (ep->skip) {
+ list_for_each(tmp, &ep_ring->td_list)
+ td_num++;
+ }
+
+ event_dma = le64_to_cpu(event->buffer);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ /* Look for common error cases */
+ switch (trb_comp_code) {
+ /* Skip codes that require special handling depending on
+ * transfer type
+ */
+ case COMP_SUCCESS:
+ if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
+ break;
+ if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
+ trb_comp_code = COMP_SHORT_TX;
+ else
+ xhci_warn_ratelimited(xhci,
+ "WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk?\n");
+ 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_dbg(xhci, "Stalled endpoint\n");
+ ep->ep_state |= EP_HALTED;
+ status = -EPIPE;
+ break;
+ case COMP_TRB_ERR:
+ xhci_warn(xhci, "WARN: TRB error on endpoint\n");
+ status = -EILSEQ;
+ break;
+ case COMP_SPLIT_ERR:
+ case COMP_TX_ERR:
+ xhci_dbg(xhci, "Transfer error on endpoint\n");
+ status = -EPROTO;
+ break;
+ case COMP_BABBLE:
+ xhci_dbg(xhci, "Babble error on endpoint\n");
+ status = -EOVERFLOW;
+ break;
+ case COMP_DB_ERR:
+ xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
+ status = -ENOSR;
+ break;
+ case COMP_BW_OVER:
+ xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n");
+ break;
+ case COMP_BUFF_OVER:
+ xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n");
+ break;
+ case COMP_UNDERRUN:
+ /*
+ * When the Isoch ring is empty, the xHC will generate
+ * a Ring Overrun Event for IN Isoch endpoint or Ring
+ * Underrun Event for OUT Isoch endpoint.
+ */
+ xhci_dbg(xhci, "underrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
+ goto cleanup;
+ case COMP_OVERRUN:
+ xhci_dbg(xhci, "overrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
+ goto cleanup;
+ case COMP_DEV_ERR:
+ xhci_warn(xhci, "WARN: detect an incompatible device");
+ status = -EPROTO;
+ break;
+ case COMP_MISSED_INT:
+ /*
+ * When encounter missed service error, one or more isoc tds
+ * may be missed by xHC.
+ * Set skip flag of the ep_ring; Complete the missed tds as
+ * short transfer when process the ep_ring next time.
+ */
+ ep->skip = true;
+ xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
+ goto cleanup;
+ default:
+ if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
+ status = 0;
+ break;
+ }
+ xhci_warn(xhci, "ERROR Unknown event condition %u, HC probably busted\n",
+ trb_comp_code);
+ goto cleanup;
+ }
+
+ do {
+ /* This TRB should be in the TD at the head of this ring's
+ * TD list.
+ */
+ if (list_empty(&ep_ring->td_list)) {
+ /*
+ * A stopped endpoint may generate an extra completion
+ * event if the device was suspended. Don't print
+ * warnings.
+ */
+ if (!(trb_comp_code == COMP_STOP ||
+ trb_comp_code == COMP_STOP_INVAL)) {
+ xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
+ xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
+ (le32_to_cpu(event->flags) &
+ TRB_TYPE_BITMASK)>>10);
+ xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
+ }
+ if (ep->skip) {
+ ep->skip = false;
+ xhci_dbg(xhci, "td_list is empty while skip "
+ "flag set. Clear skip flag.\n");
+ }
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* We've skipped all the TDs on the ep ring when ep->skip set */
+ if (ep->skip && td_num == 0) {
+ ep->skip = false;
+ xhci_dbg(xhci, "All tds on the ep_ring skipped. "
+ "Clear skip flag.\n");
+ ret = 0;
+ goto cleanup;
+ }
+
+ td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+ if (ep->skip)
+ td_num--;
+
+ /* Is this a TRB in the currently executing TD? */
+ event_seg = trb_in_td(xhci, ep_ring->deq_seg, ep_ring->dequeue,
+ td->last_trb, event_dma, false);
+
+ /*
+ * Skip the Force Stopped Event. The event_trb(event_dma) of FSE
+ * is not in the current TD pointed by ep_ring->dequeue because
+ * that the hardware dequeue pointer still at the previous TRB
+ * of the current TD. The previous TRB maybe a Link TD or the
+ * last TRB of the previous TD. The command completion handle
+ * will take care the rest.
+ */
+ if (!event_seg && (trb_comp_code == COMP_STOP ||
+ trb_comp_code == COMP_STOP_INVAL)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!event_seg) {
+ if (!ep->skip ||
+ !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+ /* Some host controllers give a spurious
+ * successful event after a short transfer.
+ * Ignore it.
+ */
+ if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
+ ep_ring->last_td_was_short) {
+ ep_ring->last_td_was_short = false;
+ ret = 0;
+ goto cleanup;
+ }
+ /* HC is busted, give up! */
+ xhci_err(xhci,
+ "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD ep_index %d "
+ "comp_code %u\n", ep_index,
+ trb_comp_code);
+ trb_in_td(xhci, ep_ring->deq_seg,
+ ep_ring->dequeue, td->last_trb,
+ event_dma, true);
+ return -ESHUTDOWN;
+ }
+
+ ret = skip_isoc_td(xhci, td, event, ep, &status);
+ goto cleanup;
+ }
+ if (trb_comp_code == COMP_SHORT_TX)
+ ep_ring->last_td_was_short = true;
+ else
+ ep_ring->last_td_was_short = false;
+
+ if (ep->skip) {
+ xhci_dbg(xhci, "Found td. Clear skip flag.\n");
+ ep->skip = false;
+ }
+
+ event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
+ sizeof(*event_trb)];
+ /*
+ * No-op TRB should not trigger interrupts.
+ * If event_trb is a no-op TRB, it means the
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
+ */
+ if (TRB_TYPE_NOOP_LE32(event_trb->generic.field[3])) {
+ xhci_dbg(xhci,
+ "event_trb is a no-op TRB. Skip it\n");
+ goto cleanup;
+ }
+
+ /* Now update the urb's actual_length and give back to
+ * the core
+ */
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ ret = process_ctrl_td(xhci, td, event_trb, event, ep,
+ &status);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ ret = process_isoc_td(xhci, td, event_trb, event, ep,
+ &status);
+ else
+ ret = process_bulk_intr_td(xhci, td, event_trb, event,
+ ep, &status);
+
+cleanup:
+ /*
+ * Do not update event ring dequeue pointer if ep->skip is set.
+ * Will roll back to continue process missed tds.
+ */
+ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
+ inc_deq(xhci, xhci->event_ring);
+ }
+
+ if (ret) {
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+
+ xhci_urb_free_priv(urb_priv);
+
+ usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
+ if ((urb->actual_length != urb->transfer_buffer_length &&
+ (urb->transfer_flags &
+ URB_SHORT_NOT_OK)) ||
+ (status != 0 &&
+ !usb_endpoint_xfer_isoc(&urb->ep->desc)))
+ xhci_dbg(xhci, "Giveback URB %p, len = %d, "
+ "expected = %d, status = %d\n",
+ urb, urb->actual_length,
+ urb->transfer_buffer_length,
+ status);
+ spin_unlock(&xhci->lock);
+ /* EHCI, UHCI, and OHCI always unconditionally set the
+ * urb->status of an isochronous endpoint to 0.
+ */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ status = 0;
+ usb_hcd_giveback_urb(bus_to_hcd(urb->dev->bus), urb, status);
+ spin_lock(&xhci->lock);
+ }
+
+ /*
+ * If ep->skip is set, it means there are missed tds on the
+ * endpoint ring need to take care of.
+ * Process them as short transfer until reach the td pointed by
+ * the event.
+ */
+ } while (ep->skip && trb_comp_code != COMP_MISSED_INT);
+
+ 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).
+ * Returns >0 for "possibly more events to process" (caller should call again),
+ * otherwise 0 if done. In future, <0 returns should indicate error code.
+ */
+static int 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 0;
+ }
+
+ event = xhci->event_ring->dequeue;
+ /* Does the HC or OS own the TRB? */
+ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
+ xhci->event_ring->cycle_state) {
+ xhci->error_bitmask |= 1 << 2;
+ return 0;
+ }
+
+ /*
+ * Barrier between reading the TRB_CYCLE (valid) flag above and any
+ * speculative reads of the event's flags/data below.
+ */
+ rmb();
+ /* FIXME: Handle more event types. */
+ switch ((le32_to_cpu(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;
+ case TRB_TYPE(TRB_DEV_NOTE):
+ handle_device_notification(xhci, event);
+ break;
+ default:
+ if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
+ TRB_TYPE(48))
+ handle_vendor_event(xhci, event);
+ else
+ xhci->error_bitmask |= 1 << 3;
+ }
+ /* Any of the above functions may drop and re-acquire the lock, so check
+ * to make sure a watchdog timer didn't mark the host as non-responsive.
+ */
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI host dying, returning from "
+ "event handler.\n");
+ return 0;
+ }
+
+ if (update_ptrs)
+ /* Update SW event ring dequeue pointer */
+ inc_deq(xhci, xhci->event_ring);
+
+ /* Are there more items on the event ring? Caller will call us again to
+ * check.
+ */
+ return 1;
+}
+
+/*
+ * 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 status;
+ u64 temp_64;
+ union xhci_trb *event_ring_deq;
+ dma_addr_t deq;
+
+ spin_lock(&xhci->lock);
+ /* Check if the xHC generated the interrupt, or the irq is shared */
+ status = readl(&xhci->op_regs->status);
+ if (status == 0xffffffff)
+ goto hw_died;
+
+ if (!(status & STS_EINT)) {
+ spin_unlock(&xhci->lock);
+ return IRQ_NONE;
+ }
+ if (status & STS_FATAL) {
+ xhci_warn(xhci, "WARNING: Host System Error\n");
+ xhci_halt(xhci);
+hw_died:
+ spin_unlock(&xhci->lock);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Clear the op reg interrupt status first,
+ * so we can receive interrupts from other MSI-X interrupters.
+ * Write 1 to clear the interrupt status.
+ */
+ status |= STS_EINT;
+ writel(status, &xhci->op_regs->status);
+ /* FIXME when MSI-X is supported and there are multiple vectors */
+ /* Clear the MSI-X event interrupt status */
+
+ if (hcd->irq) {
+ u32 irq_pending;
+ /* Acknowledge the PCI interrupt */
+ irq_pending = readl(&xhci->ir_set->irq_pending);
+ irq_pending |= IMAN_IP;
+ writel(irq_pending, &xhci->ir_set->irq_pending);
+ }
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ /* Clear the event handler busy flag (RW1C);
+ * the event ring should be empty.
+ */
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ xhci_write_64(xhci, temp_64 | ERST_EHB,
+ &xhci->ir_set->erst_dequeue);
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+ }
+
+ event_ring_deq = xhci->event_ring->dequeue;
+ /* FIXME this should be a delayed service routine
+ * that clears the EHB.
+ */
+ while (xhci_handle_event(xhci) > 0) {}
+
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ /* If necessary, update the HW's version of the event ring deq ptr. */
+ if (event_ring_deq != xhci->event_ring->dequeue) {
+ deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue);
+ if (deq == 0)
+ xhci_warn(xhci, "WARN something wrong with SW event "
+ "ring dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ temp_64 &= ERST_PTR_MASK;
+ temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+ }
+
+ /* Clear the event handler busy flag (RW1C); event ring is empty. */
+ temp_64 |= ERST_EHB;
+ xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t xhci_msi_irq(int irq, void *hcd)
+{
+ return xhci_irq(hcd);
+}
+
+/**** 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.
+ *
+ * @more_trbs_coming: Will you enqueue more TRBs before calling
+ * prepare_transfer()?
+ */
+static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ bool more_trbs_coming,
+ u32 field1, u32 field2, u32 field3, u32 field4)
+{
+ struct xhci_generic_trb *trb;
+
+ trb = &ring->enqueue->generic;
+ trb->field[0] = cpu_to_le32(field1);
+ trb->field[1] = cpu_to_le32(field2);
+ trb->field[2] = cpu_to_le32(field3);
+ trb->field[3] = cpu_to_le32(field4);
+ inc_enq(xhci, ring, more_trbs_coming);
+}
+
+/*
+ * 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)
+{
+ unsigned int num_trbs_needed;
+
+ /* Make sure the endpoint has been added to xHC schedule */
+ 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_ERROR:
+ xhci_warn(xhci, "WARN waiting for 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_HALTED:
+ xhci_dbg(xhci, "WARN halted endpoint, queueing URB anyway.\n");
+ 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;
+ }
+
+ while (1) {
+ if (room_on_ring(xhci, ep_ring, num_trbs))
+ break;
+
+ if (ep_ring == xhci->cmd_ring) {
+ xhci_err(xhci, "Do not support expand command ring\n");
+ return -ENOMEM;
+ }
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
+ "ERROR no room on ep ring, try ring expansion");
+ num_trbs_needed = num_trbs - ep_ring->num_trbs_free;
+ if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed,
+ mem_flags)) {
+ xhci_err(xhci, "Ring expansion failed\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (enqueue_is_link_trb(ep_ring)) {
+ struct xhci_ring *ring = ep_ring;
+ union xhci_trb *next;
+
+ next = ring->enqueue;
+
+ while (last_trb(xhci, ring, ring->enq_seg, next)) {
+ /* If we're not dealing with 0.95 hardware or isoc rings
+ * on AMD 0.96 host, clear the chain bit.
+ */
+ if (!xhci_link_trb_quirk(xhci) &&
+ !(ring->type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ next->link.control &= cpu_to_le32(~TRB_CHAIN);
+ else
+ next->link.control |= cpu_to_le32(TRB_CHAIN);
+
+ wmb();
+ next->link.control ^= cpu_to_le32(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 ^= 1;
+ }
+ ring->enq_seg = ring->enq_seg->next;
+ ring->enqueue = ring->enq_seg->trbs;
+ next = ring->enqueue;
+ }
+ }
+
+ return 0;
+}
+
+static int prepare_transfer(struct xhci_hcd *xhci,
+ struct xhci_virt_device *xdev,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ unsigned int num_trbs,
+ struct urb *urb,
+ unsigned int td_index,
+ gfp_t mem_flags)
+{
+ int ret;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+ struct xhci_ring *ep_ring;
+ struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+
+ ep_ring = xhci_stream_id_to_ring(xdev, ep_index, stream_id);
+ if (!ep_ring) {
+ xhci_dbg(xhci, "Can't prepare ring for bad stream ID %u\n",
+ stream_id);
+ return -EINVAL;
+ }
+
+ ret = prepare_ring(xhci, ep_ring,
+ le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ num_trbs, mem_flags);
+ if (ret)
+ return ret;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[td_index];
+
+ INIT_LIST_HEAD(&td->td_list);
+ INIT_LIST_HEAD(&td->cancelled_td_list);
+
+ if (td_index == 0) {
+ ret = usb_hcd_link_urb_to_ep(bus_to_hcd(urb->dev->bus), urb);
+ if (unlikely(ret))
+ return ret;
+ }
+
+ td->urb = urb;
+ /* Add this TD to the tail of the endpoint ring's TD list */
+ list_add_tail(&td->td_list, &ep_ring->td_list);
+ td->start_seg = ep_ring->enq_seg;
+ td->first_trb = ep_ring->enqueue;
+
+ urb_priv->td[td_index] = td;
+
+ 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_mapped_sgs;
+ temp = urb->transfer_buffer_length;
+
+ num_trbs = 0;
+ for_each_sg(urb->sg, sg, num_sgs, i) {
+ 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) & (TRB_MAX_BUFF_SIZE - 1));
+ running_total &= TRB_MAX_BUFF_SIZE - 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) && running_total < temp) {
+ num_trbs++;
+ running_total += TRB_MAX_BUFF_SIZE;
+ }
+ len = min_t(int, len, temp);
+ temp -= len;
+ if (temp == 0)
+ break;
+ }
+ return num_trbs;
+}
+
+static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
+{
+ if (num_trbs != 0)
+ dev_err(&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_err(&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, unsigned int stream_id, int start_cycle,
+ struct xhci_generic_trb *start_trb)
+{
+ /*
+ * Pass all the TRBs to the hardware at once and make sure this write
+ * isn't reordered.
+ */
+ wmb();
+ if (start_cycle)
+ start_trb->field[3] |= cpu_to_le32(start_cycle);
+ else
+ start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
+ xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
+}
+
+/*
+ * xHCI uses normal TRBs for both bulk and interrupt. When the interrupt
+ * endpoint is to be serviced, the xHC will consume (at most) one TD. A TD
+ * (comprised of sg list entries) can take several service intervals to
+ * transmit.
+ */
+int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci,
+ xhci->devs[slot_id]->out_ctx, ep_index);
+ int xhci_interval;
+ int ep_interval;
+
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
+ ep_interval = urb->interval;
+ /* Convert to microframes */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ ep_interval *= 8;
+ /* FIXME change this to a warning and a suggestion to use the new API
+ * to set the polling interval (once the API is added).
+ */
+ if (xhci_interval != ep_interval) {
+ dev_dbg_ratelimited(&urb->dev->dev,
+ "Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n",
+ ep_interval, ep_interval == 1 ? "" : "s",
+ xhci_interval, xhci_interval == 1 ? "" : "s");
+ urb->interval = xhci_interval;
+ /* Convert back to frames for LS/FS devices */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->interval /= 8;
+ }
+ return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
+}
+
+/*
+ * The TD size is the number of bytes remaining in the TD (including this TRB),
+ * right shifted by 10.
+ * It must fit in bits 21:17, so it can't be bigger than 31.
+ */
+static u32 xhci_td_remainder(unsigned int remainder)
+{
+ u32 max = (1 << (21 - 17 + 1)) - 1;
+
+ if ((remainder >> 10) >= max)
+ return max << 17;
+ else
+ return (remainder >> 10) << 17;
+}
+
+/*
+ * For xHCI 1.0 host controllers, TD size is the number of max packet sized
+ * packets remaining in the TD (*not* including this TRB).
+ *
+ * Total TD packet count = total_packet_count =
+ * DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
+ *
+ * Packets transferred up to and including this TRB = packets_transferred =
+ * rounddown(total bytes transferred including this TRB / wMaxPacketSize)
+ *
+ * TD size = total_packet_count - packets_transferred
+ *
+ * It must fit in bits 21:17, so it can't be bigger than 31.
+ * The last TRB in a TD must have the TD size set to zero.
+ */
+static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
+ unsigned int total_packet_count, struct urb *urb,
+ unsigned int num_trbs_left)
+{
+ int packets_transferred;
+
+ /* One TRB with a zero-length data packet. */
+ if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0))
+ return 0;
+
+ /* All the TRB queueing functions don't count the current TRB in
+ * running_total.
+ */
+ packets_transferred = (running_total + trb_buff_len) /
+ GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+
+ if ((total_packet_count - packets_transferred) > 31)
+ return 31 << 17;
+ return (total_packet_count - packets_transferred) << 17;
+}
+
+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 urb_priv *urb_priv;
+ struct xhci_td *td;
+ struct scatterlist *sg;
+ int num_sgs;
+ int trb_buff_len, this_sg_len, running_total;
+ unsigned int total_packet_count;
+ bool first_trb;
+ u64 addr;
+ bool more_trbs_coming;
+
+ struct xhci_generic_trb *start_trb;
+ int start_cycle;
+
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring)
+ return -EINVAL;
+
+ num_trbs = count_sg_trbs_needed(xhci, urb);
+ num_sgs = urb->num_mapped_sgs;
+ total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
+ usb_endpoint_maxp(&urb->ep->desc));
+
+ trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
+ ep_index, urb->stream_id,
+ num_trbs, urb, 0, mem_flags);
+ if (trb_buff_len < 0)
+ return trb_buff_len;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
+ /*
+ * 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;
+ addr = (u64) sg_dma_address(sg);
+ this_sg_len = sg_dma_len(sg);
+ trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 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;
+
+ first_trb = true;
+ /* Queue the first TRB, even if it's zero-length */
+ do {
+ u32 field = 0;
+ u32 length_field = 0;
+ u32 remainder = 0;
+
+ /* Don't change the cycle bit of the first TRB until later */
+ if (first_trb) {
+ first_trb = false;
+ if (start_cycle == 0)
+ field |= 0x1;
+ } 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;
+ }
+
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ field |= TRB_ISP;
+
+ if (TRB_MAX_BUFF_SIZE -
+ (addr & (TRB_MAX_BUFF_SIZE - 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);
+ }
+
+ /* Set the TRB length, TD size, and interrupter fields. */
+ if (xhci->hci_version < 0x100) {
+ remainder = xhci_td_remainder(
+ urb->transfer_buffer_length -
+ running_total);
+ } else {
+ remainder = xhci_v1_0_td_remainder(running_total,
+ trb_buff_len, total_packet_count, urb,
+ num_trbs - 1);
+ }
+ length_field = TRB_LEN(trb_buff_len) |
+ remainder |
+ TRB_INTR_TARGET(0);
+
+ if (num_trbs > 1)
+ more_trbs_coming = true;
+ else
+ more_trbs_coming = false;
+ queue_trb(xhci, ep_ring, more_trbs_coming,
+ lower_32_bits(addr),
+ upper_32_bits(addr),
+ length_field,
+ field | 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 & (TRB_MAX_BUFF_SIZE - 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, urb->stream_id,
+ start_cycle, start_trb);
+ 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 urb_priv *urb_priv;
+ struct xhci_td *td;
+ int num_trbs;
+ struct xhci_generic_trb *start_trb;
+ bool first_trb;
+ bool more_trbs_coming;
+ int start_cycle;
+ u32 field, length_field;
+
+ int running_total, trb_buff_len, ret;
+ unsigned int total_packet_count;
+ u64 addr;
+
+ if (urb->num_sgs)
+ return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
+
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring)
+ return -EINVAL;
+
+ num_trbs = 0;
+ /* How much data is (potentially) left before the 64KB boundary? */
+ running_total = TRB_MAX_BUFF_SIZE -
+ (urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
+ running_total &= TRB_MAX_BUFF_SIZE - 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 */
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id],
+ ep_index, urb->stream_id,
+ num_trbs, urb, 0, mem_flags);
+ if (ret < 0)
+ return ret;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
+ /*
+ * 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;
+ total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
+ usb_endpoint_maxp(&urb->ep->desc));
+ /* How much data is in the first TRB? */
+ addr = (u64) urb->transfer_dma;
+ trb_buff_len = TRB_MAX_BUFF_SIZE -
+ (urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
+ if (trb_buff_len > urb->transfer_buffer_length)
+ trb_buff_len = urb->transfer_buffer_length;
+
+ first_trb = true;
+
+ /* Queue the first TRB, even if it's zero-length */
+ do {
+ u32 remainder = 0;
+ field = 0;
+
+ /* Don't change the cycle bit of the first TRB until later */
+ if (first_trb) {
+ first_trb = false;
+ if (start_cycle == 0)
+ field |= 0x1;
+ } 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;
+ }
+
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ field |= TRB_ISP;
+
+ /* Set the TRB length, TD size, and interrupter fields. */
+ if (xhci->hci_version < 0x100) {
+ remainder = xhci_td_remainder(
+ urb->transfer_buffer_length -
+ running_total);
+ } else {
+ remainder = xhci_v1_0_td_remainder(running_total,
+ trb_buff_len, total_packet_count, urb,
+ num_trbs - 1);
+ }
+ length_field = TRB_LEN(trb_buff_len) |
+ remainder |
+ TRB_INTR_TARGET(0);
+
+ if (num_trbs > 1)
+ more_trbs_coming = true;
+ else
+ more_trbs_coming = false;
+ queue_trb(xhci, ep_ring, more_trbs_coming,
+ lower_32_bits(addr),
+ upper_32_bits(addr),
+ length_field,
+ field | 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, urb->stream_id,
+ start_cycle, start_trb);
+ 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, length_field;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring)
+ return -EINVAL;
+
+ /*
+ * Need to copy setup packet into setup TRB, so we can't use the setup
+ * DMA address.
+ */
+ if (!urb->setup_packet)
+ return -EINVAL;
+
+ /* 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, urb->stream_id,
+ num_trbs, urb, 0, mem_flags);
+ if (ret < 0)
+ return ret;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
+ /*
+ * 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;
+ field = 0;
+ field |= TRB_IDT | TRB_TYPE(TRB_SETUP);
+ if (start_cycle == 0)
+ field |= 0x1;
+
+ /* xHCI 1.0 6.4.1.2.1: Transfer Type field */
+ if (xhci->hci_version == 0x100) {
+ if (urb->transfer_buffer_length > 0) {
+ if (setup->bRequestType & USB_DIR_IN)
+ field |= TRB_TX_TYPE(TRB_DATA_IN);
+ else
+ field |= TRB_TX_TYPE(TRB_DATA_OUT);
+ }
+ }
+
+ queue_trb(xhci, ep_ring, true,
+ setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
+ le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
+ TRB_LEN(8) | TRB_INTR_TARGET(0),
+ /* Immediate data in pointer */
+ field);
+
+ /* If there's data, queue data TRBs */
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ field = TRB_ISP | TRB_TYPE(TRB_DATA);
+ else
+ field = TRB_TYPE(TRB_DATA);
+
+ length_field = TRB_LEN(urb->transfer_buffer_length) |
+ xhci_td_remainder(urb->transfer_buffer_length) |
+ TRB_INTR_TARGET(0);
+ if (urb->transfer_buffer_length > 0) {
+ if (setup->bRequestType & USB_DIR_IN)
+ field |= TRB_DIR_IN;
+ queue_trb(xhci, ep_ring, true,
+ lower_32_bits(urb->transfer_dma),
+ upper_32_bits(urb->transfer_dma),
+ length_field,
+ field | 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, 0,
+ start_cycle, start_trb);
+ return 0;
+}
+
+static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
+ struct urb *urb, int i)
+{
+ int num_trbs = 0;
+ u64 addr, td_len;
+
+ addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
+ td_len = urb->iso_frame_desc[i].length;
+
+ num_trbs = DIV_ROUND_UP(td_len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
+ TRB_MAX_BUFF_SIZE);
+ if (num_trbs == 0)
+ num_trbs++;
+
+ return num_trbs;
+}
+
+/*
+ * The transfer burst count field of the isochronous TRB defines the number of
+ * bursts that are required to move all packets in this TD. Only SuperSpeed
+ * devices can burst up to bMaxBurst number of packets per service interval.
+ * This field is zero based, meaning a value of zero in the field means one
+ * burst. Basically, for everything but SuperSpeed devices, this field will be
+ * zero. Only xHCI 1.0 host controllers support this field.
+ */
+static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct urb *urb, unsigned int total_packet_count)
+{
+ unsigned int max_burst;
+
+ if (xhci->hci_version < 0x100 || udev->speed != USB_SPEED_SUPER)
+ return 0;
+
+ max_burst = urb->ep->ss_ep_comp.bMaxBurst;
+ return DIV_ROUND_UP(total_packet_count, max_burst + 1) - 1;
+}
+
+/*
+ * Returns the number of packets in the last "burst" of packets. This field is
+ * valid for all speeds of devices. USB 2.0 devices can only do one "burst", so
+ * the last burst packet count is equal to the total number of packets in the
+ * TD. SuperSpeed endpoints can have up to 3 bursts. All but the last burst
+ * must contain (bMaxBurst + 1) number of packets, but the last burst can
+ * contain 1 to (bMaxBurst + 1) packets.
+ */
+static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct urb *urb, unsigned int total_packet_count)
+{
+ unsigned int max_burst;
+ unsigned int residue;
+
+ if (xhci->hci_version < 0x100)
+ return 0;
+
+ switch (udev->speed) {
+ case USB_SPEED_SUPER:
+ /* bMaxBurst is zero based: 0 means 1 packet per burst */
+ max_burst = urb->ep->ss_ep_comp.bMaxBurst;
+ residue = total_packet_count % (max_burst + 1);
+ /* If residue is zero, the last burst contains (max_burst + 1)
+ * number of packets, but the TLBPC field is zero-based.
+ */
+ if (residue == 0)
+ return max_burst;
+ return residue - 1;
+ default:
+ if (total_packet_count == 0)
+ return 0;
+ return total_packet_count - 1;
+ }
+}
+
+/* This is for isoc transfer */
+static int xhci_queue_isoc_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 urb_priv *urb_priv;
+ struct xhci_td *td;
+ int num_tds, trbs_per_td;
+ struct xhci_generic_trb *start_trb;
+ bool first_trb;
+ int start_cycle;
+ u32 field, length_field;
+ int running_total, trb_buff_len, td_len, td_remain_len, ret;
+ u64 start_addr, addr;
+ int i, j;
+ bool more_trbs_coming;
+
+ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
+
+ num_tds = urb->number_of_packets;
+ if (num_tds < 1) {
+ xhci_dbg(xhci, "Isoc URB with zero packets?\n");
+ return -EINVAL;
+ }
+
+ start_addr = (u64) urb->transfer_dma;
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+
+ urb_priv = urb->hcpriv;
+ /* Queue the first TRB, even if it's zero-length */
+ for (i = 0; i < num_tds; i++) {
+ unsigned int total_packet_count;
+ unsigned int burst_count;
+ unsigned int residue;
+
+ first_trb = true;
+ running_total = 0;
+ addr = start_addr + urb->iso_frame_desc[i].offset;
+ td_len = urb->iso_frame_desc[i].length;
+ td_remain_len = td_len;
+ total_packet_count = DIV_ROUND_UP(td_len,
+ GET_MAX_PACKET(
+ usb_endpoint_maxp(&urb->ep->desc)));
+ /* A zero-length transfer still involves at least one packet. */
+ if (total_packet_count == 0)
+ total_packet_count++;
+ burst_count = xhci_get_burst_count(xhci, urb->dev, urb,
+ total_packet_count);
+ residue = xhci_get_last_burst_packet_count(xhci,
+ urb->dev, urb, total_packet_count);
+
+ trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
+ urb->stream_id, trbs_per_td, urb, i, mem_flags);
+ if (ret < 0) {
+ if (i == 0)
+ return ret;
+ goto cleanup;
+ }
+
+ td = urb_priv->td[i];
+ for (j = 0; j < trbs_per_td; j++) {
+ u32 remainder = 0;
+ field = 0;
+
+ if (first_trb) {
+ field = TRB_TBC(burst_count) |
+ TRB_TLBPC(residue);
+ /* Queue the isoc TRB */
+ field |= TRB_TYPE(TRB_ISOC);
+ /* Assume URB_ISO_ASAP is set */
+ field |= TRB_SIA;
+ if (i == 0) {
+ if (start_cycle == 0)
+ field |= 0x1;
+ } else
+ field |= ep_ring->cycle_state;
+ first_trb = false;
+ } else {
+ /* Queue other normal TRBs */
+ field |= TRB_TYPE(TRB_NORMAL);
+ field |= ep_ring->cycle_state;
+ }
+
+ /* Only set interrupt on short packet for IN EPs */
+ if (usb_urb_dir_in(urb))
+ field |= TRB_ISP;
+
+ /* Chain all the TRBs together; clear the chain bit in
+ * the last TRB to indicate it's the last TRB in the
+ * chain.
+ */
+ if (j < trbs_per_td - 1) {
+ field |= TRB_CHAIN;
+ more_trbs_coming = true;
+ } else {
+ td->last_trb = ep_ring->enqueue;
+ field |= TRB_IOC;
+ if (xhci->hci_version == 0x100 &&
+ !(xhci->quirks &
+ XHCI_AVOID_BEI)) {
+ /* Set BEI bit except for the last td */
+ if (i < num_tds - 1)
+ field |= TRB_BEI;
+ }
+ more_trbs_coming = false;
+ }
+
+ /* Calculate TRB length */
+ trb_buff_len = TRB_MAX_BUFF_SIZE -
+ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+ if (trb_buff_len > td_remain_len)
+ trb_buff_len = td_remain_len;
+
+ /* Set the TRB length, TD size, & interrupter fields. */
+ if (xhci->hci_version < 0x100) {
+ remainder = xhci_td_remainder(
+ td_len - running_total);
+ } else {
+ remainder = xhci_v1_0_td_remainder(
+ running_total, trb_buff_len,
+ total_packet_count, urb,
+ (trbs_per_td - j - 1));
+ }
+ length_field = TRB_LEN(trb_buff_len) |
+ remainder |
+ TRB_INTR_TARGET(0);
+
+ queue_trb(xhci, ep_ring, more_trbs_coming,
+ lower_32_bits(addr),
+ upper_32_bits(addr),
+ length_field,
+ field);
+ running_total += trb_buff_len;
+
+ addr += trb_buff_len;
+ td_remain_len -= trb_buff_len;
+ }
+
+ /* Check TD length */
+ if (running_total != td_len) {
+ xhci_err(xhci, "ISOC TD length unmatch\n");
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ }
+
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_disable();
+ }
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++;
+
+ giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
+ start_cycle, start_trb);
+ return 0;
+cleanup:
+ /* Clean up a partially enqueued isoc transfer. */
+
+ for (i--; i >= 0; i--)
+ list_del_init(&urb_priv->td[i]->td_list);
+
+ /* Use the first TD as a temporary variable to turn the TDs we've queued
+ * into No-ops with a software-owned cycle bit. That way the hardware
+ * won't accidentally start executing bogus TDs when we partially
+ * overwrite them. td->first_trb and td->start_seg are already set.
+ */
+ urb_priv->td[0]->last_trb = ep_ring->enqueue;
+ /* Every TRB except the first & last will have its cycle bit flipped. */
+ td_to_noop(xhci, ep_ring, urb_priv->td[0], true);
+
+ /* Reset the ring enqueue back to the first TRB and its cycle bit. */
+ ep_ring->enqueue = urb_priv->td[0]->first_trb;
+ ep_ring->enq_seg = urb_priv->td[0]->start_seg;
+ ep_ring->cycle_state = start_cycle;
+ ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
+ usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
+ return ret;
+}
+
+/*
+ * Check transfer ring to guarantee there is enough room for the urb.
+ * Update ISO URB start_frame and interval.
+ * Update interval as xhci_queue_intr_tx does. Just use xhci frame_index to
+ * update the urb->start_frame by now.
+ * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input.
+ */
+int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ struct xhci_ep_ctx *ep_ctx;
+ int start_frame;
+ int xhci_interval;
+ int ep_interval;
+ int num_tds, num_trbs, i;
+ int ret;
+
+ xdev = xhci->devs[slot_id];
+ ep_ring = xdev->eps[ep_index].ring;
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+
+ num_trbs = 0;
+ num_tds = urb->number_of_packets;
+ for (i = 0; i < num_tds; i++)
+ num_trbs += count_isoc_trbs_needed(xhci, urb, i);
+
+ /* Check the ring to guarantee there is enough room for the whole urb.
+ * Do not insert any td of the urb to the ring if the check failed.
+ */
+ ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ num_trbs, mem_flags);
+ if (ret)
+ return ret;
+
+ start_frame = readl(&xhci->run_regs->microframe_index);
+ start_frame &= 0x3fff;
+
+ urb->start_frame = start_frame;
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->start_frame >>= 3;
+
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
+ ep_interval = urb->interval;
+ /* Convert to microframes */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ ep_interval *= 8;
+ /* FIXME change this to a warning and a suggestion to use the new API
+ * to set the polling interval (once the API is added).
+ */
+ if (xhci_interval != ep_interval) {
+ dev_dbg_ratelimited(&urb->dev->dev,
+ "Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n",
+ ep_interval, ep_interval == 1 ? "" : "s",
+ xhci_interval, xhci_interval == 1 ? "" : "s");
+ urb->interval = xhci_interval;
+ /* Convert back to frames for LS/FS devices */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->interval /= 8;
+ }
+ ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;
+
+ return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
+}
+
+/**** Command Ring Operations ****/
+
+/* Generic function for queueing a command TRB on the command ring.
+ * Check to make sure there's room on the command ring for one command TRB.
+ * Also check that there's room reserved for commands that must not fail.
+ * If this is a command that must not fail, meaning command_must_succeed = TRUE,
+ * then only check for the number of reserved spots.
+ * Don't decrement xhci->cmd_ring_reserved_trbs after we've queued the TRB
+ * because the command event handler may want to resubmit a failed command.
+ */
+static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 field1, u32 field2,
+ u32 field3, u32 field4, bool command_must_succeed)
+{
+ int reserved_trbs = xhci->cmd_ring_reserved_trbs;
+ int ret;
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return -ESHUTDOWN;
+
+ if (!command_must_succeed)
+ reserved_trbs++;
+
+ ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING,
+ reserved_trbs, GFP_ATOMIC);
+ if (ret < 0) {
+ xhci_err(xhci, "ERR: No room for command on command ring\n");
+ if (command_must_succeed)
+ xhci_err(xhci, "ERR: Reserved TRB counting for "
+ "unfailable commands failed.\n");
+ return ret;
+ }
+
+ cmd->command_trb = xhci->cmd_ring->enqueue;
+ list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
+
+ /* if there are no other commands queued we start the timeout timer */
+ if (xhci->cmd_list.next == &cmd->cmd_list &&
+ !timer_pending(&xhci->cmd_timer)) {
+ xhci->current_cmd = cmd;
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ }
+
+ queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
+ field4 | xhci->cmd_ring->cycle_state);
+ return 0;
+}
+
+/* Queue a slot enable or disable request on the command ring */
+int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 trb_type, u32 slot_id)
+{
+ return queue_command(xhci, cmd, 0, 0, 0,
+ TRB_TYPE(trb_type) | SLOT_ID_FOR_TRB(slot_id), false);
+}
+
+/* Queue an address device command TRB */
+int xhci_queue_address_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, enum xhci_setup_dev setup)
+{
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id)
+ | (setup == SETUP_CONTEXT_ONLY ? TRB_BSR : 0), false);
+}
+
+int xhci_queue_vendor_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 field1, u32 field2, u32 field3, u32 field4)
+{
+ return queue_command(xhci, cmd, field1, field2, field3, field4, false);
+}
+
+/* Queue a reset device command TRB */
+int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 slot_id)
+{
+ return queue_command(xhci, cmd, 0, 0, 0,
+ TRB_TYPE(TRB_RESET_DEV) | SLOT_ID_FOR_TRB(slot_id),
+ false);
+}
+
+/* Queue a configure endpoint command TRB */
+int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
+ u32 slot_id, bool command_must_succeed)
+{
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id),
+ command_must_succeed);
+}
+
+/* Queue an evaluate context command TRB */
+int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
+{
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_EVAL_CONTEXT) | SLOT_ID_FOR_TRB(slot_id),
+ command_must_succeed);
+}
+
+/*
+ * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
+ * activity on an endpoint that is about to be suspended.
+ */
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index, int suspend)
+{
+ 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);
+ u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
+
+ return queue_command(xhci, cmd, 0, 0, 0,
+ trb_slot_id | trb_ep_index | type | trb_suspend, false);
+}
+
+/* Set Transfer Ring Dequeue Pointer command */
+void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id,
+ struct xhci_dequeue_state *deq_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 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
+ u32 trb_sct = 0;
+ u32 type = TRB_TYPE(TRB_SET_DEQ);
+ struct xhci_virt_ep *ep;
+ struct xhci_command *cmd;
+ int ret;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), new deq ptr = %p (0x%llx dma), new cycle = %u",
+ 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);
+
+ addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg,
+ deq_state->new_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_state->new_deq_seg, deq_state->new_deq_ptr);
+ return;
+ }
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ if ((ep->ep_state & SET_DEQ_PENDING)) {
+ xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
+ xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n");
+ return;
+ }
+
+ /* This function gets called from contexts where it cannot sleep */
+ cmd = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!cmd) {
+ xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr: ENOMEM\n");
+ return;
+ }
+
+ ep->queued_deq_seg = deq_state->new_deq_seg;
+ ep->queued_deq_ptr = deq_state->new_deq_ptr;
+ if (stream_id)
+ trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+ ret = queue_command(xhci, cmd,
+ lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state,
+ upper_32_bits(addr), trb_stream_id,
+ trb_slot_id | trb_ep_index | type, false);
+ if (ret < 0) {
+ xhci_free_command(xhci, cmd);
+ return;
+ }
+
+ /* 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->ep_state |= SET_DEQ_PENDING;
+}
+
+int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ 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_RESET_EP);
+
+ return queue_command(xhci, cmd, 0, 0, 0,
+ trb_slot_id | trb_ep_index | type, false);
+}
diff --git a/drivers/usb/host/xhci-trace.c b/drivers/usb/host/xhci-trace.c
new file mode 100644
index 000000000..367b630bd
--- /dev/null
+++ b/drivers/usb/host/xhci-trace.c
@@ -0,0 +1,17 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2013 Xenia Ragiadakou
+ *
+ * Author: Xenia Ragiadakou
+ * Email : burzalodowa@gmail.com
+ *
+ * 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.
+ */
+
+#define CREATE_TRACE_POINTS
+#include "xhci-trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_dbg_quirks);
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
new file mode 100644
index 000000000..59c05653b
--- /dev/null
+++ b/drivers/usb/host/xhci-trace.h
@@ -0,0 +1,158 @@
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2013 Xenia Ragiadakou
+ *
+ * Author: Xenia Ragiadakou
+ * Email : burzalodowa@gmail.com
+ *
+ * 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xhci-hcd
+
+/*
+ * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
+ * legitimate C variable. It is not exported to user space.
+ */
+#undef TRACE_SYSTEM_VAR
+#define TRACE_SYSTEM_VAR xhci_hcd
+
+#if !defined(__XHCI_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __XHCI_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "xhci.h"
+
+#define XHCI_MSG_MAX 500
+
+DECLARE_EVENT_CLASS(xhci_log_msg,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(__dynamic_array(char, msg, XHCI_MSG_MAX)),
+ TP_fast_assign(
+ vsnprintf(__get_str(msg), XHCI_MSG_MAX, vaf->fmt, *vaf->va);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_address,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_context_change,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_quirks,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_reset_ep,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_cancel_urb,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_init,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DECLARE_EVENT_CLASS(xhci_log_ctx,
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+ unsigned int ep_num),
+ TP_ARGS(xhci, ctx, ep_num),
+ TP_STRUCT__entry(
+ __field(int, ctx_64)
+ __field(unsigned, ctx_type)
+ __field(dma_addr_t, ctx_dma)
+ __field(u8 *, ctx_va)
+ __field(unsigned, ctx_ep_num)
+ __field(int, slot_id)
+ __dynamic_array(u32, ctx_data,
+ ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) *
+ ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1))
+ ),
+ TP_fast_assign(
+ struct usb_device *udev;
+
+ udev = to_usb_device(xhci_to_hcd(xhci)->self.controller);
+ __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+ __entry->ctx_type = ctx->type;
+ __entry->ctx_dma = ctx->dma;
+ __entry->ctx_va = ctx->bytes;
+ __entry->slot_id = udev->slot_id;
+ __entry->ctx_ep_num = ep_num;
+ memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
+ ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
+ ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1));
+ ),
+ TP_printk("\nctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
+ __entry->ctx_64, __entry->ctx_type,
+ (unsigned long long) __entry->ctx_dma, __entry->ctx_va
+ )
+);
+
+DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+ unsigned int ep_num),
+ TP_ARGS(xhci, ctx, ep_num)
+);
+
+DECLARE_EVENT_CLASS(xhci_log_event,
+ TP_PROTO(void *trb_va, struct xhci_generic_trb *ev),
+ TP_ARGS(trb_va, ev),
+ TP_STRUCT__entry(
+ __field(void *, va)
+ __field(u64, dma)
+ __field(u32, status)
+ __field(u32, flags)
+ __dynamic_array(u8, trb, sizeof(struct xhci_generic_trb))
+ ),
+ TP_fast_assign(
+ __entry->va = trb_va;
+ __entry->dma = ((u64)le32_to_cpu(ev->field[1])) << 32 |
+ le32_to_cpu(ev->field[0]);
+ __entry->status = le32_to_cpu(ev->field[2]);
+ __entry->flags = le32_to_cpu(ev->field[3]);
+ memcpy(__get_dynamic_array(trb), trb_va,
+ sizeof(struct xhci_generic_trb));
+ ),
+ TP_printk("\ntrb_dma=@%llx, trb_va=@%p, status=%08x, flags=%08x",
+ (unsigned long long) __entry->dma, __entry->va,
+ __entry->status, __entry->flags
+ )
+);
+
+DEFINE_EVENT(xhci_log_event, xhci_cmd_completion,
+ TP_PROTO(void *trb_va, struct xhci_generic_trb *ev),
+ TP_ARGS(trb_va, ev)
+);
+
+#endif /* __XHCI_TRACE_H */
+
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE xhci-trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
new file mode 100644
index 000000000..36bf089b7
--- /dev/null
+++ b/drivers/usb/host/xhci.c
@@ -0,0 +1,5033 @@
+/*
+ * 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/pci.h>
+#include <linux/irq.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/dma-mapping.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+
+#define DRIVER_AUTHOR "Sarah Sharp"
+#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
+
+#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
+
+/* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
+static int link_quirk;
+module_param(link_quirk, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
+
+static unsigned int quirks;
+module_param(quirks, uint, S_IRUGO);
+MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
+
+/* TODO: copied from ehci-hcd.c - can this be refactored? */
+/*
+ * xhci_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).
+ */
+int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = readl(ptr);
+ if (result == ~(u32)0) /* card removed */
+ return -ENODEV;
+ result &= mask;
+ if (result == done)
+ return 0;
+ udelay(1);
+ usec--;
+ } while (usec > 0);
+ return -ETIMEDOUT;
+}
+
+/*
+ * Disable interrupts and begin the xHCI halting process.
+ */
+void xhci_quiesce(struct xhci_hcd *xhci)
+{
+ u32 halted;
+ u32 cmd;
+ u32 mask;
+
+ mask = ~(XHCI_IRQS);
+ halted = readl(&xhci->op_regs->status) & STS_HALT;
+ if (!halted)
+ mask &= ~CMD_RUN;
+
+ cmd = readl(&xhci->op_regs->command);
+ cmd &= mask;
+ writel(cmd, &xhci->op_regs->command);
+}
+
+/*
+ * 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 ms of the run/stop bit being cleared.
+ * Read HC Halted bit in the status register to see when the HC is finished.
+ */
+int xhci_halt(struct xhci_hcd *xhci)
+{
+ int ret;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Halt the HC");
+ xhci_quiesce(xhci);
+
+ ret = xhci_handshake(&xhci->op_regs->status,
+ STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
+ if (!ret) {
+ xhci->xhc_state |= XHCI_STATE_HALTED;
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ } else
+ xhci_warn(xhci, "Host not halted after %u microseconds.\n",
+ XHCI_MAX_HALT_USEC);
+ return ret;
+}
+
+/*
+ * Set the run bit and wait for the host to be running.
+ */
+static int xhci_start(struct xhci_hcd *xhci)
+{
+ u32 temp;
+ int ret;
+
+ temp = readl(&xhci->op_regs->command);
+ temp |= (CMD_RUN);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.",
+ temp);
+ writel(temp, &xhci->op_regs->command);
+
+ /*
+ * Wait for the HCHalted Status bit to be 0 to indicate the host is
+ * running.
+ */
+ ret = xhci_handshake(&xhci->op_regs->status,
+ STS_HALT, 0, XHCI_MAX_HALT_USEC);
+ if (ret == -ETIMEDOUT)
+ xhci_err(xhci, "Host took too long to start, "
+ "waited %u microseconds.\n",
+ XHCI_MAX_HALT_USEC);
+ if (!ret)
+ xhci->xhc_state &= ~XHCI_STATE_HALTED;
+ return ret;
+}
+
+/*
+ * Reset a halted HC.
+ *
+ * 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;
+ int ret, i;
+
+ state = readl(&xhci->op_regs->status);
+ if ((state & STS_HALT) == 0) {
+ xhci_warn(xhci, "Host controller not halted, aborting reset.\n");
+ return 0;
+ }
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Reset the HC");
+ command = readl(&xhci->op_regs->command);
+ command |= CMD_RESET;
+ writel(command, &xhci->op_regs->command);
+
+ ret = xhci_handshake(&xhci->op_regs->command,
+ CMD_RESET, 0, 10 * 1000 * 1000);
+ if (ret)
+ return ret;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Wait for controller to be ready for doorbell rings");
+ /*
+ * xHCI cannot write to any doorbells or operational registers other
+ * than status until the "Controller Not Ready" flag is cleared.
+ */
+ ret = xhci_handshake(&xhci->op_regs->status,
+ STS_CNR, 0, 10 * 1000 * 1000);
+
+ for (i = 0; i < 2; ++i) {
+ xhci->bus_state[i].port_c_suspend = 0;
+ xhci->bus_state[i].suspended_ports = 0;
+ xhci->bus_state[i].resuming_ports = 0;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PCI
+static int xhci_free_msi(struct xhci_hcd *xhci)
+{
+ int i;
+
+ if (!xhci->msix_entries)
+ return -EINVAL;
+
+ for (i = 0; i < xhci->msix_count; i++)
+ if (xhci->msix_entries[i].vector)
+ free_irq(xhci->msix_entries[i].vector,
+ xhci_to_hcd(xhci));
+ return 0;
+}
+
+/*
+ * Set up MSI
+ */
+static int xhci_setup_msi(struct xhci_hcd *xhci)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+
+ ret = pci_enable_msi(pdev);
+ if (ret) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "failed to allocate MSI entry");
+ return ret;
+ }
+
+ ret = request_irq(pdev->irq, xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "disable MSI interrupt");
+ pci_disable_msi(pdev);
+ }
+
+ return ret;
+}
+
+/*
+ * Free IRQs
+ * free all IRQs request
+ */
+static void xhci_free_irq(struct xhci_hcd *xhci)
+{
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ int ret;
+
+ /* return if using legacy interrupt */
+ if (xhci_to_hcd(xhci)->irq > 0)
+ return;
+
+ ret = xhci_free_msi(xhci);
+ if (!ret)
+ return;
+ if (pdev->irq > 0)
+ free_irq(pdev->irq, xhci_to_hcd(xhci));
+
+ return;
+}
+
+/*
+ * Set up MSI-X
+ */
+static int xhci_setup_msix(struct xhci_hcd *xhci)
+{
+ int i, ret = 0;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ /*
+ * calculate number of msi-x vectors supported.
+ * - HCS_MAX_INTRS: the max number of interrupts the host can handle,
+ * with max number of interrupters based on the xhci HCSPARAMS1.
+ * - num_online_cpus: maximum msi-x vectors per CPUs core.
+ * Add additional 1 vector to ensure always available interrupt.
+ */
+ xhci->msix_count = min(num_online_cpus() + 1,
+ HCS_MAX_INTRS(xhci->hcs_params1));
+
+ xhci->msix_entries =
+ kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
+ GFP_KERNEL);
+ if (!xhci->msix_entries) {
+ xhci_err(xhci, "Failed to allocate MSI-X entries\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < xhci->msix_count; i++) {
+ xhci->msix_entries[i].entry = i;
+ xhci->msix_entries[i].vector = 0;
+ }
+
+ ret = pci_enable_msix_exact(pdev, xhci->msix_entries, xhci->msix_count);
+ if (ret) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Failed to enable MSI-X");
+ goto free_entries;
+ }
+
+ for (i = 0; i < xhci->msix_count; i++) {
+ ret = request_irq(xhci->msix_entries[i].vector,
+ xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret)
+ goto disable_msix;
+ }
+
+ hcd->msix_enabled = 1;
+ return ret;
+
+disable_msix:
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt");
+ xhci_free_irq(xhci);
+ pci_disable_msix(pdev);
+free_entries:
+ kfree(xhci->msix_entries);
+ xhci->msix_entries = NULL;
+ return ret;
+}
+
+/* Free any IRQs and disable MSI-X */
+static void xhci_cleanup_msix(struct xhci_hcd *xhci)
+{
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ if (xhci->quirks & XHCI_PLAT)
+ return;
+
+ xhci_free_irq(xhci);
+
+ if (xhci->msix_entries) {
+ pci_disable_msix(pdev);
+ kfree(xhci->msix_entries);
+ xhci->msix_entries = NULL;
+ } else {
+ pci_disable_msi(pdev);
+ }
+
+ hcd->msix_enabled = 0;
+ return;
+}
+
+static void __maybe_unused xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+ int i;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ synchronize_irq(xhci->msix_entries[i].vector);
+ }
+}
+
+static int xhci_try_enable_msi(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev;
+ int ret;
+
+ /* The xhci platform device has set up IRQs through usb_add_hcd. */
+ if (xhci->quirks & XHCI_PLAT)
+ return 0;
+
+ pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ /*
+ * Some Fresco Logic host controllers advertise MSI, but fail to
+ * generate interrupts. Don't even try to enable MSI.
+ */
+ if (xhci->quirks & XHCI_BROKEN_MSI)
+ goto legacy_irq;
+
+ /* unregister the legacy interrupt */
+ if (hcd->irq)
+ free_irq(hcd->irq, hcd);
+ hcd->irq = 0;
+
+ ret = xhci_setup_msix(xhci);
+ if (ret)
+ /* fall back to msi*/
+ ret = xhci_setup_msi(xhci);
+
+ if (!ret)
+ /* hcd->irq is 0, we have MSI */
+ return 0;
+
+ if (!pdev->irq) {
+ xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n");
+ return -EINVAL;
+ }
+
+ legacy_irq:
+ if (!strlen(hcd->irq_descr))
+ snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
+ hcd->driver->description, hcd->self.busnum);
+
+ /* fall back to legacy interrupt*/
+ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
+ hcd->irq_descr, hcd);
+ if (ret) {
+ xhci_err(xhci, "request interrupt %d failed\n",
+ pdev->irq);
+ return ret;
+ }
+ hcd->irq = pdev->irq;
+ return 0;
+}
+
+#else
+
+static inline int xhci_try_enable_msi(struct usb_hcd *hcd)
+{
+ return 0;
+}
+
+static inline void xhci_cleanup_msix(struct xhci_hcd *xhci)
+{
+}
+
+static inline void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+}
+
+#endif
+
+static void compliance_mode_recovery(unsigned long arg)
+{
+ struct xhci_hcd *xhci;
+ struct usb_hcd *hcd;
+ u32 temp;
+ int i;
+
+ xhci = (struct xhci_hcd *)arg;
+
+ for (i = 0; i < xhci->num_usb3_ports; i++) {
+ temp = readl(xhci->usb3_ports[i]);
+ if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
+ /*
+ * Compliance Mode Detected. Letting USB Core
+ * handle the Warm Reset
+ */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Compliance mode detected->port %d",
+ i + 1);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Attempting compliance mode recovery");
+ hcd = xhci->shared_hcd;
+
+ if (hcd->state == HC_STATE_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+
+ usb_hcd_poll_rh_status(hcd);
+ }
+ }
+
+ if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1))
+ mod_timer(&xhci->comp_mode_recovery_timer,
+ jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+}
+
+/*
+ * Quirk to work around issue generated by the SN65LVPE502CP USB3.0 re-driver
+ * that causes ports behind that hardware to enter compliance mode sometimes.
+ * The quirk creates a timer that polls every 2 seconds the link state of
+ * each host controller's port and recovers it by issuing a Warm reset
+ * if Compliance mode is detected, otherwise the port will become "dead" (no
+ * device connections or disconnections will be detected anymore). Becasue no
+ * status event is generated when entering compliance mode (per xhci spec),
+ * this quirk is needed on systems that have the failing hardware installed.
+ */
+static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci)
+{
+ xhci->port_status_u0 = 0;
+ setup_timer(&xhci->comp_mode_recovery_timer,
+ compliance_mode_recovery, (unsigned long)xhci);
+ xhci->comp_mode_recovery_timer.expires = jiffies +
+ msecs_to_jiffies(COMP_MODE_RCVRY_MSECS);
+
+ set_timer_slack(&xhci->comp_mode_recovery_timer,
+ msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+ add_timer(&xhci->comp_mode_recovery_timer);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Compliance mode recovery timer initialized");
+}
+
+/*
+ * This function identifies the systems that have installed the SN65LVPE502CP
+ * USB3.0 re-driver and that need the Compliance Mode Quirk.
+ * Systems:
+ * Vendor: Hewlett-Packard -> System Models: Z420, Z620 and Z820
+ */
+static bool xhci_compliance_mode_recovery_timer_quirk_check(void)
+{
+ const char *dmi_product_name, *dmi_sys_vendor;
+
+ dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+ dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ if (!dmi_product_name || !dmi_sys_vendor)
+ return false;
+
+ if (!(strstr(dmi_sys_vendor, "Hewlett-Packard")))
+ return false;
+
+ if (strstr(dmi_product_name, "Z420") ||
+ strstr(dmi_product_name, "Z620") ||
+ strstr(dmi_product_name, "Z820") ||
+ strstr(dmi_product_name, "Z1 Workstation"))
+ return true;
+
+ return false;
+}
+
+static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
+{
+ return (xhci->port_status_u0 == ((1 << xhci->num_usb3_ports)-1));
+}
+
+
+/*
+ * 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_trace(xhci, trace_xhci_dbg_init, "xhci_init");
+ spin_lock_init(&xhci->lock);
+ if (xhci->hci_version == 0x95 && link_quirk) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Not clearing Link TRB chain bits.");
+ xhci->quirks |= XHCI_LINK_TRB_QUIRK;
+ } else {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xHCI doesn't need link TRB QUIRK");
+ }
+ retval = xhci_mem_init(xhci, GFP_KERNEL);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init");
+
+ /* Initializing Compliance Mode Recovery Data If Needed */
+ if (xhci_compliance_mode_recovery_timer_quirk_check()) {
+ xhci->quirks |= XHCI_COMP_MODE_QUIRK;
+ compliance_mode_recovery_timer_init(xhci);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+static int xhci_run_finished(struct xhci_hcd *xhci)
+{
+ if (xhci_start(xhci)) {
+ xhci_halt(xhci);
+ return -ENODEV;
+ }
+ xhci->shared_hcd->state = HC_STATE_RUNNING;
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ if (xhci->quirks & XHCI_NEC_HOST)
+ xhci_ring_cmd_db(xhci);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Finished xhci_run for USB3 roothub");
+ return 0;
+}
+
+/*
+ * 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;
+ u64 temp_64;
+ int ret;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ /* Start the xHCI host controller running only after the USB 2.0 roothub
+ * is setup.
+ */
+
+ hcd->uses_new_polling = 1;
+ if (!usb_hcd_is_primary_hcd(hcd))
+ return xhci_run_finished(xhci);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_run");
+
+ ret = xhci_try_enable_msi(hcd);
+ if (ret)
+ return ret;
+
+ 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_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ temp_64 &= ~ERST_PTR_MASK;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "ERST deq = 64'h%0lx", (long unsigned int) temp_64);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Set the interrupt modulation register");
+ temp = readl(&xhci->ir_set->irq_control);
+ temp &= ~ER_IRQ_INTERVAL_MASK;
+ temp |= (u32) 160;
+ writel(temp, &xhci->ir_set->irq_control);
+
+ /* Set the HCD state before we enable the irqs */
+ temp = readl(&xhci->op_regs->command);
+ temp |= (CMD_EIE);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Enable interrupts, cmd = 0x%x.", temp);
+ writel(temp, &xhci->op_regs->command);
+
+ temp = readl(&xhci->ir_set->irq_pending);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Enabling event ring interrupter %p by writing 0x%x to irq_pending",
+ xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp));
+ writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending);
+ xhci_print_ir_set(xhci, 0);
+
+ if (xhci->quirks & XHCI_NEC_HOST) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+ xhci_queue_vendor_command(xhci, command, 0, 0, 0,
+ TRB_TYPE(TRB_NEC_GET_FW));
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Finished xhci_run for USB2 roothub");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_run);
+
+static void xhci_only_stop_hcd(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ spin_lock_irq(&xhci->lock);
+ xhci_halt(xhci);
+
+ /* The shared_hcd is going to be deallocated shortly (the USB core only
+ * calls this function when allocation fails in usb_add_hcd(), or
+ * usb_remove_hcd() is called). So we need to unset xHCI's pointer.
+ */
+ xhci->shared_hcd = NULL;
+ spin_unlock_irq(&xhci->lock);
+}
+
+/*
+ * 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);
+
+ if (!usb_hcd_is_primary_hcd(hcd)) {
+ xhci_only_stop_hcd(xhci->shared_hcd);
+ return;
+ }
+
+ spin_lock_irq(&xhci->lock);
+ /* Make sure the xHC is halted for a USB3 roothub
+ * (xhci_stop() could be called as part of failed init).
+ */
+ xhci_halt(xhci);
+ xhci_reset(xhci);
+ spin_unlock_irq(&xhci->lock);
+
+ xhci_cleanup_msix(xhci);
+
+ /* Deleting Compliance Mode Recovery Timer */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ (!(xhci_all_ports_seen_u0(xhci)))) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "%s: compliance mode recovery timer deleted",
+ __func__);
+ }
+
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_dev_put();
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Disabling event ring interrupts");
+ temp = readl(&xhci->op_regs->status);
+ writel(temp & ~STS_EINT, &xhci->op_regs->status);
+ temp = readl(&xhci->ir_set->irq_pending);
+ writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
+ xhci_print_ir_set(xhci, 0);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
+ xhci_mem_cleanup(xhci);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xhci_stop completed - status = %x",
+ readl(&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.
+ *
+ * This will only ever be called with the main usb_hcd (the USB3 roothub).
+ */
+void xhci_shutdown(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
+ usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
+
+ spin_lock_irq(&xhci->lock);
+ xhci_halt(xhci);
+ /* Workaround for spurious wakeups at shutdown with HSW */
+ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+ xhci_reset(xhci);
+ spin_unlock_irq(&xhci->lock);
+
+ xhci_cleanup_msix(xhci);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "xhci_shutdown completed - status = %x",
+ readl(&xhci->op_regs->status));
+
+ /* Yet another workaround for spurious wakeups at shutdown with HSW */
+ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+ pci_set_power_state(to_pci_dev(hcd->self.controller), PCI_D3hot);
+}
+
+#ifdef CONFIG_PM
+static void xhci_save_registers(struct xhci_hcd *xhci)
+{
+ xhci->s3.command = readl(&xhci->op_regs->command);
+ xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification);
+ xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
+ xhci->s3.config_reg = readl(&xhci->op_regs->config_reg);
+ xhci->s3.erst_size = readl(&xhci->ir_set->erst_size);
+ xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
+ xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending);
+ xhci->s3.irq_control = readl(&xhci->ir_set->irq_control);
+}
+
+static void xhci_restore_registers(struct xhci_hcd *xhci)
+{
+ writel(xhci->s3.command, &xhci->op_regs->command);
+ writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
+ xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
+ writel(xhci->s3.config_reg, &xhci->op_regs->config_reg);
+ writel(xhci->s3.erst_size, &xhci->ir_set->erst_size);
+ xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
+ xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue);
+ writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
+ writel(xhci->s3.irq_control, &xhci->ir_set->irq_control);
+}
+
+static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
+{
+ u64 val_64;
+
+ /* step 2: initialize command ring buffer */
+ val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
+ (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
+ xhci->cmd_ring->dequeue) &
+ (u64) ~CMD_RING_RSVD_BITS) |
+ xhci->cmd_ring->cycle_state;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "// Setting command ring address to 0x%llx",
+ (long unsigned long) val_64);
+ xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
+}
+
+/*
+ * The whole command ring must be cleared to zero when we suspend the host.
+ *
+ * The host doesn't save the command ring pointer in the suspend well, so we
+ * need to re-program it on resume. Unfortunately, the pointer must be 64-byte
+ * aligned, because of the reserved bits in the command ring dequeue pointer
+ * register. Therefore, we can't just set the dequeue pointer back in the
+ * middle of the ring (TRBs are 16-byte aligned).
+ */
+static void xhci_clear_command_ring(struct xhci_hcd *xhci)
+{
+ struct xhci_ring *ring;
+ struct xhci_segment *seg;
+
+ ring = xhci->cmd_ring;
+ seg = ring->deq_seg;
+ do {
+ memset(seg->trbs, 0,
+ sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1));
+ seg->trbs[TRBS_PER_SEGMENT - 1].link.control &=
+ cpu_to_le32(~TRB_CYCLE);
+ seg = seg->next;
+ } while (seg != ring->deq_seg);
+
+ /* Reset the software enqueue and dequeue pointers */
+ ring->deq_seg = ring->first_seg;
+ ring->dequeue = ring->first_seg->trbs;
+ ring->enq_seg = ring->deq_seg;
+ ring->enqueue = ring->dequeue;
+
+ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+ /*
+ * Ring is now zeroed, so the HW should look for change of ownership
+ * when the cycle bit is set to 1.
+ */
+ ring->cycle_state = 1;
+
+ /*
+ * Reset the hardware dequeue pointer.
+ * Yes, this will need to be re-written after resume, but we're paranoid
+ * and want to make sure the hardware doesn't access bogus memory
+ * because, say, the BIOS or an SMI started the host without changing
+ * the command ring pointers.
+ */
+ xhci_set_cmd_ring_deq(xhci);
+}
+
+static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
+{
+ int port_index;
+ __le32 __iomem **port_array;
+ unsigned long flags;
+ u32 t1, t2;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* disble usb3 ports Wake bits*/
+ port_index = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ /* disble usb2 ports Wake bits*/
+ port_index = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+}
+
+/*
+ * Stop HC (not bus-specific)
+ *
+ * This is called when the machine transition into S3/S4 mode.
+ *
+ */
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
+{
+ int rc = 0;
+ unsigned int delay = XHCI_MAX_HALT_USEC;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ u32 command;
+
+ if (hcd->state != HC_STATE_SUSPENDED ||
+ xhci->shared_hcd->state != HC_STATE_SUSPENDED)
+ return -EINVAL;
+
+ /* Clear root port wake on bits if wakeup not allowed. */
+ if (!do_wakeup)
+ xhci_disable_port_wake_on_bits(xhci);
+
+ /* Don't poll the roothubs on bus suspend. */
+ xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
+ clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
+ del_timer_sync(&xhci->shared_hcd->rh_timer);
+
+ spin_lock_irq(&xhci->lock);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
+ /* step 1: stop endpoint */
+ /* skipped assuming that port suspend has done */
+
+ /* step 2: clear Run/Stop bit */
+ command = readl(&xhci->op_regs->command);
+ command &= ~CMD_RUN;
+ writel(command, &xhci->op_regs->command);
+
+ /* Some chips from Fresco Logic need an extraordinary delay */
+ delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1;
+
+ if (xhci_handshake(&xhci->op_regs->status,
+ STS_HALT, STS_HALT, delay)) {
+ xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
+ spin_unlock_irq(&xhci->lock);
+ return -ETIMEDOUT;
+ }
+ xhci_clear_command_ring(xhci);
+
+ /* step 3: save registers */
+ xhci_save_registers(xhci);
+
+ /* step 4: set CSS flag */
+ command = readl(&xhci->op_regs->command);
+ command |= CMD_CSS;
+ writel(command, &xhci->op_regs->command);
+ if (xhci_handshake(&xhci->op_regs->status,
+ STS_SAVE, 0, 10 * 1000)) {
+ xhci_warn(xhci, "WARN: xHC save state timeout\n");
+ spin_unlock_irq(&xhci->lock);
+ return -ETIMEDOUT;
+ }
+ spin_unlock_irq(&xhci->lock);
+
+ /*
+ * Deleting Compliance Mode Recovery Timer because the xHCI Host
+ * is about to be suspended.
+ */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ (!(xhci_all_ports_seen_u0(xhci)))) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "%s: compliance mode recovery timer deleted",
+ __func__);
+ }
+
+ /* step 5: remove core well power */
+ /* synchronize irq when using MSI-X */
+ xhci_msix_sync_irqs(xhci);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(xhci_suspend);
+
+/*
+ * start xHC (not bus-specific)
+ *
+ * This is called when the machine transition from S3/S4 mode.
+ *
+ */
+int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
+{
+ u32 command, temp = 0, status;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct usb_hcd *secondary_hcd;
+ int retval = 0;
+ bool comp_timer_running = false;
+
+ /* Wait a bit if either of the roothubs need to settle from the
+ * transition into bus suspend.
+ */
+ if (time_before(jiffies, xhci->bus_state[0].next_statechange) ||
+ time_before(jiffies,
+ xhci->bus_state[1].next_statechange))
+ msleep(100);
+
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
+
+ spin_lock_irq(&xhci->lock);
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ hibernated = true;
+
+ if (!hibernated) {
+ /* step 1: restore register */
+ xhci_restore_registers(xhci);
+ /* step 2: initialize command ring buffer */
+ xhci_set_cmd_ring_deq(xhci);
+ /* step 3: restore state and start state*/
+ /* step 3: set CRS flag */
+ command = readl(&xhci->op_regs->command);
+ command |= CMD_CRS;
+ writel(command, &xhci->op_regs->command);
+ if (xhci_handshake(&xhci->op_regs->status,
+ STS_RESTORE, 0, 10 * 1000)) {
+ xhci_warn(xhci, "WARN: xHC restore state timeout\n");
+ spin_unlock_irq(&xhci->lock);
+ return -ETIMEDOUT;
+ }
+ temp = readl(&xhci->op_regs->status);
+ }
+
+ /* If restore operation fails, re-initialize the HC during resume */
+ if ((temp & STS_SRE) || hibernated) {
+
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ !(xhci_all_ports_seen_u0(xhci))) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Compliance Mode Recovery Timer deleted!");
+ }
+
+ /* Let the USB core know _both_ roothubs lost power. */
+ usb_root_hub_lost_power(xhci->main_hcd->self.root_hub);
+ usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub);
+
+ xhci_dbg(xhci, "Stop HCD\n");
+ xhci_halt(xhci);
+ xhci_reset(xhci);
+ spin_unlock_irq(&xhci->lock);
+ xhci_cleanup_msix(xhci);
+
+ xhci_dbg(xhci, "// Disabling event ring interrupts\n");
+ temp = readl(&xhci->op_regs->status);
+ writel(temp & ~STS_EINT, &xhci->op_regs->status);
+ temp = readl(&xhci->ir_set->irq_pending);
+ writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
+ xhci_print_ir_set(xhci, 0);
+
+ xhci_dbg(xhci, "cleaning up memory\n");
+ xhci_mem_cleanup(xhci);
+ xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
+ readl(&xhci->op_regs->status));
+
+ /* USB core calls the PCI reinit and start functions twice:
+ * first with the primary HCD, and then with the secondary HCD.
+ * If we don't do the same, the host will never be started.
+ */
+ if (!usb_hcd_is_primary_hcd(hcd))
+ secondary_hcd = hcd;
+ else
+ secondary_hcd = xhci->shared_hcd;
+
+ xhci_dbg(xhci, "Initialize the xhci_hcd\n");
+ retval = xhci_init(hcd->primary_hcd);
+ if (retval)
+ return retval;
+ comp_timer_running = true;
+
+ xhci_dbg(xhci, "Start the primary HCD\n");
+ retval = xhci_run(hcd->primary_hcd);
+ if (!retval) {
+ xhci_dbg(xhci, "Start the secondary HCD\n");
+ retval = xhci_run(secondary_hcd);
+ }
+ hcd->state = HC_STATE_SUSPENDED;
+ xhci->shared_hcd->state = HC_STATE_SUSPENDED;
+ goto done;
+ }
+
+ /* step 4: set Run/Stop bit */
+ command = readl(&xhci->op_regs->command);
+ command |= CMD_RUN;
+ writel(command, &xhci->op_regs->command);
+ xhci_handshake(&xhci->op_regs->status, STS_HALT,
+ 0, 250 * 1000);
+
+ /* step 5: walk topology and initialize portsc,
+ * portpmsc and portli
+ */
+ /* this is done in bus_resume */
+
+ /* step 6: restart each of the previously
+ * Running endpoints by ringing their doorbells
+ */
+
+ spin_unlock_irq(&xhci->lock);
+
+ done:
+ if (retval == 0) {
+ /* Resume root hubs only when have pending events. */
+ status = readl(&xhci->op_regs->status);
+ if (status & STS_EINT) {
+ usb_hcd_resume_root_hub(hcd);
+ usb_hcd_resume_root_hub(xhci->shared_hcd);
+ }
+ }
+
+ /*
+ * If system is subject to the Quirk, Compliance Mode Timer needs to
+ * be re-initialized Always after a system resume. Ports are subject
+ * to suffer the Compliance Mode issue again. It doesn't matter if
+ * ports have entered previously to U0 before system's suspension.
+ */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
+ compliance_mode_recovery_timer_init(xhci);
+
+ /* Re-enable port polling. */
+ xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
+ set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
+ usb_hcd_poll_rh_status(xhci->shared_hcd);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(xhci_resume);
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * 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;
+}
+
+/* The reverse operation to xhci_get_endpoint_index. Calculate the USB endpoint
+ * address from the XHCI endpoint index.
+ */
+unsigned int xhci_get_endpoint_address(unsigned int ep_index)
+{
+ unsigned int number = DIV_ROUND_UP(ep_index, 2);
+ unsigned int direction = ep_index % 2 ? USB_DIR_OUT : USB_DIR_IN;
+ return direction | number;
+}
+
+/* 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);
+}
+
+/* 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_from_index(unsigned int ep_index)
+{
+ return 1 << (ep_index + 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.
+ */
+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.
+ */
+static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint *ep, int check_ep, bool check_virt_dev,
+ const char *func) {
+ struct xhci_hcd *xhci;
+ struct xhci_virt_device *virt_dev;
+
+ if (!hcd || (check_ep && !ep) || !udev) {
+ pr_debug("xHCI %s called with invalid args\n", func);
+ return -EINVAL;
+ }
+ if (!udev->parent) {
+ pr_debug("xHCI %s called for root hub\n", func);
+ return 0;
+ }
+
+ xhci = hcd_to_xhci(hcd);
+ if (check_virt_dev) {
+ if (!udev->slot_id || !xhci->devs[udev->slot_id]) {
+ xhci_dbg(xhci, "xHCI %s called with unaddressed device\n",
+ func);
+ return -EINVAL;
+ }
+
+ virt_dev = xhci->devs[udev->slot_id];
+ if (virt_dev->udev != udev) {
+ xhci_dbg(xhci, "xHCI %s called with udev and "
+ "virt_dev does not match\n", func);
+ return -EINVAL;
+ }
+ }
+
+ if (xhci->xhc_state & XHCI_STATE_HALTED)
+ return -ENODEV;
+
+ return 1;
+}
+
+static int xhci_configure_endpoint(struct xhci_hcd *xhci,
+ struct usb_device *udev, struct xhci_command *command,
+ bool ctx_change, bool must_succeed);
+
+/*
+ * Full speed devices may have a max packet size greater than 8 bytes, but the
+ * USB core doesn't know that until it reads the first 8 bytes of the
+ * descriptor. If the usb_device's max packet size changes after that point,
+ * we need to issue an evaluate context command and wait on it.
+ */
+static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
+ unsigned int ep_index, struct urb *urb)
+{
+ struct xhci_container_ctx *out_ctx;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_ep_ctx *ep_ctx;
+ struct xhci_command *command;
+ int max_packet_size;
+ int hw_max_packet_size;
+ int ret = 0;
+
+ out_ctx = xhci->devs[slot_id]->out_ctx;
+ ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
+ hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
+ max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc);
+ if (hw_max_packet_size != max_packet_size) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Max Packet Size for ep 0 changed.");
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Max packet size in usb_device = %d",
+ max_packet_size);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Max packet size in xHCI HW = %d",
+ hw_max_packet_size);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Issuing evaluate context command.");
+
+ /* Set up the input context flags for the command */
+ /* FIXME: This won't work if a non-default control endpoint
+ * changes max packet sizes.
+ */
+
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = xhci->devs[slot_id]->in_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto command_cleanup;
+ }
+ /* Set up the modified control endpoint 0 */
+ xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx,
+ xhci->devs[slot_id]->out_ctx, ep_index);
+
+ ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index);
+ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK);
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size));
+
+ ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG);
+ ctrl_ctx->drop_flags = 0;
+
+ xhci_dbg(xhci, "Slot %d input context\n", slot_id);
+ xhci_dbg_ctx(xhci, command->in_ctx, ep_index);
+ xhci_dbg(xhci, "Slot %d output context\n", slot_id);
+ xhci_dbg_ctx(xhci, out_ctx, ep_index);
+
+ ret = xhci_configure_endpoint(xhci, urb->dev, command,
+ true, false);
+
+ /* Clean up the input context for later use by bandwidth
+ * functions.
+ */
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
+ }
+ return ret;
+}
+
+/*
+ * 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);
+ struct xhci_td *buffer;
+ unsigned long flags;
+ int ret = 0;
+ unsigned int slot_id, ep_index;
+ struct urb_priv *urb_priv;
+ int size, i;
+
+ if (!urb || xhci_check_args(hcd, urb->dev, urb->ep,
+ true, true, __func__) <= 0)
+ return -EINVAL;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ if (!in_interrupt())
+ xhci_dbg(xhci, "urb submitted during PCI suspend\n");
+ ret = -ESHUTDOWN;
+ goto exit;
+ }
+
+ if (usb_endpoint_xfer_isoc(&urb->ep->desc))
+ size = urb->number_of_packets;
+ else
+ size = 1;
+
+ urb_priv = kzalloc(sizeof(struct urb_priv) +
+ size * sizeof(struct xhci_td *), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags);
+ if (!buffer) {
+ kfree(urb_priv);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < size; i++) {
+ urb_priv->td[i] = buffer;
+ buffer++;
+ }
+
+ urb_priv->length = size;
+ urb_priv->td_cnt = 0;
+ urb->hcpriv = urb_priv;
+
+ if (usb_endpoint_xfer_control(&urb->ep->desc)) {
+ /* Check to see if the max packet size for the default control
+ * endpoint changed during FS device enumeration
+ */
+ if (urb->dev->speed == USB_SPEED_FULL) {
+ ret = xhci_check_maxpacket(xhci, slot_id,
+ ep_index, urb);
+ if (ret < 0) {
+ xhci_urb_free_priv(urb_priv);
+ urb->hcpriv = NULL;
+ return ret;
+ }
+ }
+
+ /* We have a spinlock and interrupts disabled, so we must pass
+ * atomic context to this function, which may allocate memory.
+ */
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ if (ret)
+ goto free_priv;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ } else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ if (xhci->devs[slot_id]->eps[ep_index].ep_state &
+ EP_GETTING_STREAMS) {
+ xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
+ "is transitioning to using streams.\n");
+ ret = -EINVAL;
+ } else if (xhci->devs[slot_id]->eps[ep_index].ep_state &
+ EP_GETTING_NO_STREAMS) {
+ xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
+ "is transitioning to "
+ "not having streams.\n");
+ ret = -EINVAL;
+ } else {
+ ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ }
+ if (ret)
+ goto free_priv;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ } else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ if (ret)
+ goto free_priv;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ } else {
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ if (ret)
+ goto free_priv;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ }
+exit:
+ return ret;
+dying:
+ xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
+ "non-responsive xHCI host.\n",
+ urb->ep->desc.bEndpointAddress, urb);
+ ret = -ESHUTDOWN;
+free_priv:
+ xhci_urb_free_priv(urb_priv);
+ urb->hcpriv = NULL;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return ret;
+}
+
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ unsigned int slot_id;
+ unsigned int ep_index;
+ unsigned int stream_id;
+ struct xhci_virt_ep *ep;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ stream_id = urb->stream_id;
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
+/*
+ * 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, i;
+ u32 temp;
+ struct xhci_hcd *xhci;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+ unsigned int ep_index;
+ struct xhci_ring *ep_ring;
+ struct xhci_virt_ep *ep;
+ struct xhci_command *command;
+
+ 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;
+ temp = readl(&xhci->op_regs->status);
+ if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "HW died, freeing TD.");
+ urb_priv = urb->hcpriv;
+ for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
+ td = urb_priv->td[i];
+ if (!list_empty(&td->td_list))
+ list_del_init(&td->td_list);
+ if (!list_empty(&td->cancelled_td_list))
+ list_del_init(&td->cancelled_td_list);
+ }
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
+ xhci_urb_free_priv(urb_priv);
+ return ret;
+ }
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Ep 0x%x: URB %p to be canceled on "
+ "non-responsive xHCI host.",
+ urb->ep->desc.bEndpointAddress, urb);
+ /* Let the stop endpoint command watchdog timer (which set this
+ * state) finish cleaning up the endpoint TD lists. We must
+ * have caught it in the middle of dropping a lock and giving
+ * back an URB.
+ */
+ goto done;
+ }
+
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ urb_priv = urb->hcpriv;
+ i = urb_priv->td_cnt;
+ if (i < urb_priv->length)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Cancel URB %p, dev %s, ep 0x%x, "
+ "starting at offset 0x%llx",
+ urb, urb->dev->devpath,
+ urb->ep->desc.bEndpointAddress,
+ (unsigned long long) xhci_trb_virt_to_dma(
+ urb_priv->td[i]->start_seg,
+ urb_priv->td[i]->first_trb));
+
+ for (; i < urb_priv->length; i++) {
+ td = urb_priv->td[i];
+ list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+ }
+
+ /* Queue a stop endpoint command, but only if this is
+ * the first cancellation to be handled.
+ */
+ if (!(ep->ep_state & EP_HALT_PENDING)) {
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ ep->ep_state |= EP_HALT_PENDING;
+ ep->stop_cmds_pending++;
+ ep->stop_cmd_timer.expires = jiffies +
+ XHCI_STOP_EP_CMD_TIMEOUT * HZ;
+ add_timer(&ep->stop_cmd_timer);
+ xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id,
+ ep_index, 0);
+ 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_container_ctx *in_ctx, *out_ctx;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ unsigned int ep_index;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 drop_flag;
+ u32 new_add_flags, new_drop_flags;
+ int ret;
+
+ ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return -ENODEV;
+
+ 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;
+ }
+
+ in_ctx = xhci->devs[udev->slot_id]->in_ctx;
+ out_ctx = xhci->devs[udev->slot_id]->out_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return 0;
+ }
+
+ ep_index = xhci_get_endpoint_index(&ep->desc);
+ ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, 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 & cpu_to_le32(EP_STATE_MASK)) ==
+ cpu_to_le32(EP_STATE_DISABLED)) ||
+ le32_to_cpu(ctrl_ctx->drop_flags) &
+ xhci_get_endpoint_flag(&ep->desc)) {
+ /* Do not warn when called after a usb_device_reset */
+ if (xhci->devs[udev->slot_id]->eps[ep_index].ring != NULL)
+ xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
+ __func__, ep);
+ return 0;
+ }
+
+ ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag);
+ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+
+ ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
+ new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+
+ 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\n",
+ (unsigned int) ep->desc.bEndpointAddress,
+ udev->slot_id,
+ (unsigned int) new_drop_flags,
+ (unsigned int) new_add_flags);
+ 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_container_ctx *in_ctx;
+ unsigned int ep_index;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ u32 added_ctxs;
+ u32 new_add_flags, new_drop_flags;
+ struct xhci_virt_device *virt_dev;
+ int ret = 0;
+
+ ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
+ if (ret <= 0) {
+ /* So we won't queue a reset ep command for a root hub */
+ ep->hcpriv = NULL;
+ return ret;
+ }
+ xhci = hcd_to_xhci(hcd);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return -ENODEV;
+
+ added_ctxs = xhci_get_endpoint_flag(&ep->desc);
+ 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;
+ }
+
+ virt_dev = xhci->devs[udev->slot_id];
+ in_ctx = virt_dev->in_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return 0;
+ }
+
+ ep_index = xhci_get_endpoint_index(&ep->desc);
+ /* If this endpoint is already in use, and the upper layers are trying
+ * to add it again without dropping it, reject the addition.
+ */
+ if (virt_dev->eps[ep_index].ring &&
+ !(le32_to_cpu(ctrl_ctx->drop_flags) & added_ctxs)) {
+ xhci_warn(xhci, "Trying to add endpoint 0x%x "
+ "without dropping it.\n",
+ (unsigned int) ep->desc.bEndpointAddress);
+ return -EINVAL;
+ }
+
+ /* If the HCD has already noted the endpoint is enabled,
+ * ignore this request.
+ */
+ if (le32_to_cpu(ctrl_ctx->add_flags) & added_ctxs) {
+ 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, virt_dev, udev, ep, GFP_NOIO) < 0) {
+ dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n",
+ __func__, ep->desc.bEndpointAddress);
+ return -ENOMEM;
+ }
+
+ ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
+ new_add_flags = le32_to_cpu(ctrl_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 = le32_to_cpu(ctrl_ctx->drop_flags);
+
+ /* Store the usb_device pointer for later use */
+ ep->hcpriv = udev;
+
+ xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
+ (unsigned int) ep->desc.bEndpointAddress,
+ udev->slot_id,
+ (unsigned int) new_drop_flags,
+ (unsigned int) new_add_flags);
+ return 0;
+}
+
+static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev)
+{
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_ep_ctx *ep_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ int i;
+
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return;
+ }
+
+ /* 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.
+ */
+ ctrl_ctx->drop_flags = 0;
+ ctrl_ctx->add_flags = 0;
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ /* Endpoint 0 is always valid */
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1));
+ for (i = 1; i < 31; ++i) {
+ ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i);
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = 0;
+ ep_ctx->deq = 0;
+ ep_ctx->tx_info = 0;
+ }
+}
+
+static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
+ struct usb_device *udev, u32 *cmd_status)
+{
+ int ret;
+
+ switch (*cmd_status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for configure endpoint command\n");
+ ret = -ETIME;
+ break;
+ 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:
+ case COMP_2ND_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_DEV_ERR:
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for endpoint configure command.\n");
+ ret = -ENODEV;
+ break;
+ case COMP_SUCCESS:
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Successful Endpoint Configure command");
+ ret = 0;
+ break;
+ default:
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
+ struct usb_device *udev, u32 *cmd_status)
+{
+ int ret;
+ struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
+
+ switch (*cmd_status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for evaluate context command\n");
+ ret = -ETIME;
+ break;
+ case COMP_EINVAL:
+ dev_warn(&udev->dev,
+ "WARN: xHCI driver setup invalid evaluate context command.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_EBADSLT:
+ dev_warn(&udev->dev,
+ "WARN: slot not enabled for evaluate context command.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_CTX_STATE:
+ dev_warn(&udev->dev,
+ "WARN: invalid context state for evaluate context command.\n");
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
+ ret = -EINVAL;
+ break;
+ case COMP_DEV_ERR:
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for evaluate context command.\n");
+ ret = -ENODEV;
+ break;
+ case COMP_MEL_ERR:
+ /* Max Exit Latency too large error */
+ dev_warn(&udev->dev, "WARN: Max Exit Latency too large\n");
+ ret = -EINVAL;
+ break;
+ case COMP_SUCCESS:
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Successful evaluate context command");
+ ret = 0;
+ break;
+ default:
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static u32 xhci_count_num_new_endpoints(struct xhci_hcd *xhci,
+ struct xhci_input_control_ctx *ctrl_ctx)
+{
+ u32 valid_add_flags;
+ u32 valid_drop_flags;
+
+ /* Ignore the slot flag (bit 0), and the default control endpoint flag
+ * (bit 1). The default control endpoint is added during the Address
+ * Device command and is never removed until the slot is disabled.
+ */
+ valid_add_flags = le32_to_cpu(ctrl_ctx->add_flags) >> 2;
+ valid_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags) >> 2;
+
+ /* Use hweight32 to count the number of ones in the add flags, or
+ * number of endpoints added. Don't count endpoints that are changed
+ * (both added and dropped).
+ */
+ return hweight32(valid_add_flags) -
+ hweight32(valid_add_flags & valid_drop_flags);
+}
+
+static unsigned int xhci_count_num_dropped_endpoints(struct xhci_hcd *xhci,
+ struct xhci_input_control_ctx *ctrl_ctx)
+{
+ u32 valid_add_flags;
+ u32 valid_drop_flags;
+
+ valid_add_flags = le32_to_cpu(ctrl_ctx->add_flags) >> 2;
+ valid_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags) >> 2;
+
+ return hweight32(valid_drop_flags) -
+ hweight32(valid_add_flags & valid_drop_flags);
+}
+
+/*
+ * We need to reserve the new number of endpoints before the configure endpoint
+ * command completes. We can't subtract the dropped endpoints from the number
+ * of active endpoints until the command completes because we can oversubscribe
+ * the host in this case:
+ *
+ * - the first configure endpoint command drops more endpoints than it adds
+ * - a second configure endpoint command that adds more endpoints is queued
+ * - the first configure endpoint command fails, so the config is unchanged
+ * - the second command may succeed, even though there isn't enough resources
+ *
+ * Must be called with xhci->lock held.
+ */
+static int xhci_reserve_host_resources(struct xhci_hcd *xhci,
+ struct xhci_input_control_ctx *ctrl_ctx)
+{
+ u32 added_eps;
+
+ added_eps = xhci_count_num_new_endpoints(xhci, ctrl_ctx);
+ if (xhci->num_active_eps + added_eps > xhci->limit_active_eps) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Not enough ep ctxs: "
+ "%u active, need to add %u, limit is %u.",
+ xhci->num_active_eps, added_eps,
+ xhci->limit_active_eps);
+ return -ENOMEM;
+ }
+ xhci->num_active_eps += added_eps;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Adding %u ep ctxs, %u now active.", added_eps,
+ xhci->num_active_eps);
+ return 0;
+}
+
+/*
+ * The configure endpoint was failed by the xHC for some other reason, so we
+ * need to revert the resources that failed configuration would have used.
+ *
+ * Must be called with xhci->lock held.
+ */
+static void xhci_free_host_resources(struct xhci_hcd *xhci,
+ struct xhci_input_control_ctx *ctrl_ctx)
+{
+ u32 num_failed_eps;
+
+ num_failed_eps = xhci_count_num_new_endpoints(xhci, ctrl_ctx);
+ xhci->num_active_eps -= num_failed_eps;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Removing %u failed ep ctxs, %u now active.",
+ num_failed_eps,
+ xhci->num_active_eps);
+}
+
+/*
+ * Now that the command has completed, clean up the active endpoint count by
+ * subtracting out the endpoints that were dropped (but not changed).
+ *
+ * Must be called with xhci->lock held.
+ */
+static void xhci_finish_resource_reservation(struct xhci_hcd *xhci,
+ struct xhci_input_control_ctx *ctrl_ctx)
+{
+ u32 num_dropped_eps;
+
+ num_dropped_eps = xhci_count_num_dropped_endpoints(xhci, ctrl_ctx);
+ xhci->num_active_eps -= num_dropped_eps;
+ if (num_dropped_eps)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Removing %u dropped ep ctxs, %u now active.",
+ num_dropped_eps,
+ xhci->num_active_eps);
+}
+
+static unsigned int xhci_get_block_size(struct usb_device *udev)
+{
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return FS_BLOCK;
+ case USB_SPEED_HIGH:
+ return HS_BLOCK;
+ case USB_SPEED_SUPER:
+ return SS_BLOCK;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ default:
+ /* Should never happen */
+ return 1;
+ }
+}
+
+static unsigned int
+xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw)
+{
+ if (interval_bw->overhead[LS_OVERHEAD_TYPE])
+ return LS_OVERHEAD;
+ if (interval_bw->overhead[FS_OVERHEAD_TYPE])
+ return FS_OVERHEAD;
+ return HS_OVERHEAD;
+}
+
+/* If we are changing a LS/FS device under a HS hub,
+ * make sure (if we are activating a new TT) that the HS bus has enough
+ * bandwidth for this new TT.
+ */
+static int xhci_check_tt_bw_table(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ struct xhci_interval_bw_table *bw_table;
+ struct xhci_tt_bw_info *tt_info;
+
+ /* Find the bandwidth table for the root port this TT is attached to. */
+ bw_table = &xhci->rh_bw[virt_dev->real_port - 1].bw_table;
+ tt_info = virt_dev->tt_info;
+ /* If this TT already had active endpoints, the bandwidth for this TT
+ * has already been added. Removing all periodic endpoints (and thus
+ * making the TT enactive) will only decrease the bandwidth used.
+ */
+ if (old_active_eps)
+ return 0;
+ if (old_active_eps == 0 && tt_info->active_eps != 0) {
+ if (bw_table->bw_used + TT_HS_OVERHEAD > HS_BW_LIMIT)
+ return -ENOMEM;
+ return 0;
+ }
+ /* Not sure why we would have no new active endpoints...
+ *
+ * Maybe because of an Evaluate Context change for a hub update or a
+ * control endpoint 0 max packet size change?
+ * FIXME: skip the bandwidth calculation in that case.
+ */
+ return 0;
+}
+
+static int xhci_check_ss_bw(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev)
+{
+ unsigned int bw_reserved;
+
+ bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100);
+ if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved))
+ return -ENOMEM;
+
+ bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100);
+ if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * This algorithm is a very conservative estimate of the worst-case scheduling
+ * scenario for any one interval. The hardware dynamically schedules the
+ * packets, so we can't tell which microframe could be the limiting factor in
+ * the bandwidth scheduling. This only takes into account periodic endpoints.
+ *
+ * Obviously, we can't solve an NP complete problem to find the minimum worst
+ * case scenario. Instead, we come up with an estimate that is no less than
+ * the worst case bandwidth used for any one microframe, but may be an
+ * over-estimate.
+ *
+ * We walk the requirements for each endpoint by interval, starting with the
+ * smallest interval, and place packets in the schedule where there is only one
+ * possible way to schedule packets for that interval. In order to simplify
+ * this algorithm, we record the largest max packet size for each interval, and
+ * assume all packets will be that size.
+ *
+ * For interval 0, we obviously must schedule all packets for each interval.
+ * The bandwidth for interval 0 is just the amount of data to be transmitted
+ * (the sum of all max ESIT payload sizes, plus any overhead per packet times
+ * the number of packets).
+ *
+ * For interval 1, we have two possible microframes to schedule those packets
+ * in. For this algorithm, if we can schedule the same number of packets for
+ * each possible scheduling opportunity (each microframe), we will do so. The
+ * remaining number of packets will be saved to be transmitted in the gaps in
+ * the next interval's scheduling sequence.
+ *
+ * As we move those remaining packets to be scheduled with interval 2 packets,
+ * we have to double the number of remaining packets to transmit. This is
+ * because the intervals are actually powers of 2, and we would be transmitting
+ * the previous interval's packets twice in this interval. We also have to be
+ * sure that when we look at the largest max packet size for this interval, we
+ * also look at the largest max packet size for the remaining packets and take
+ * the greater of the two.
+ *
+ * The algorithm continues to evenly distribute packets in each scheduling
+ * opportunity, and push the remaining packets out, until we get to the last
+ * interval. Then those packets and their associated overhead are just added
+ * to the bandwidth used.
+ */
+static int xhci_check_bw_table(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ unsigned int bw_reserved;
+ unsigned int max_bandwidth;
+ unsigned int bw_used;
+ unsigned int block_size;
+ struct xhci_interval_bw_table *bw_table;
+ unsigned int packet_size = 0;
+ unsigned int overhead = 0;
+ unsigned int packets_transmitted = 0;
+ unsigned int packets_remaining = 0;
+ unsigned int i;
+
+ if (virt_dev->udev->speed == USB_SPEED_SUPER)
+ return xhci_check_ss_bw(xhci, virt_dev);
+
+ if (virt_dev->udev->speed == USB_SPEED_HIGH) {
+ max_bandwidth = HS_BW_LIMIT;
+ /* Convert percent of bus BW reserved to blocks reserved */
+ bw_reserved = DIV_ROUND_UP(HS_BW_RESERVED * max_bandwidth, 100);
+ } else {
+ max_bandwidth = FS_BW_LIMIT;
+ bw_reserved = DIV_ROUND_UP(FS_BW_RESERVED * max_bandwidth, 100);
+ }
+
+ bw_table = virt_dev->bw_table;
+ /* We need to translate the max packet size and max ESIT payloads into
+ * the units the hardware uses.
+ */
+ block_size = xhci_get_block_size(virt_dev->udev);
+
+ /* If we are manipulating a LS/FS device under a HS hub, double check
+ * that the HS bus has enough bandwidth if we are activing a new TT.
+ */
+ if (virt_dev->tt_info) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Recalculating BW for rootport %u",
+ virt_dev->real_port);
+ if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) {
+ xhci_warn(xhci, "Not enough bandwidth on HS bus for "
+ "newly activated TT.\n");
+ return -ENOMEM;
+ }
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Recalculating BW for TT slot %u port %u",
+ virt_dev->tt_info->slot_id,
+ virt_dev->tt_info->ttport);
+ } else {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Recalculating BW for rootport %u",
+ virt_dev->real_port);
+ }
+
+ /* Add in how much bandwidth will be used for interval zero, or the
+ * rounded max ESIT payload + number of packets * largest overhead.
+ */
+ bw_used = DIV_ROUND_UP(bw_table->interval0_esit_payload, block_size) +
+ bw_table->interval_bw[0].num_packets *
+ xhci_get_largest_overhead(&bw_table->interval_bw[0]);
+
+ for (i = 1; i < XHCI_MAX_INTERVAL; i++) {
+ unsigned int bw_added;
+ unsigned int largest_mps;
+ unsigned int interval_overhead;
+
+ /*
+ * How many packets could we transmit in this interval?
+ * If packets didn't fit in the previous interval, we will need
+ * to transmit that many packets twice within this interval.
+ */
+ packets_remaining = 2 * packets_remaining +
+ bw_table->interval_bw[i].num_packets;
+
+ /* Find the largest max packet size of this or the previous
+ * interval.
+ */
+ if (list_empty(&bw_table->interval_bw[i].endpoints))
+ largest_mps = 0;
+ else {
+ struct xhci_virt_ep *virt_ep;
+ struct list_head *ep_entry;
+
+ ep_entry = bw_table->interval_bw[i].endpoints.next;
+ virt_ep = list_entry(ep_entry,
+ struct xhci_virt_ep, bw_endpoint_list);
+ /* Convert to blocks, rounding up */
+ largest_mps = DIV_ROUND_UP(
+ virt_ep->bw_info.max_packet_size,
+ block_size);
+ }
+ if (largest_mps > packet_size)
+ packet_size = largest_mps;
+
+ /* Use the larger overhead of this or the previous interval. */
+ interval_overhead = xhci_get_largest_overhead(
+ &bw_table->interval_bw[i]);
+ if (interval_overhead > overhead)
+ overhead = interval_overhead;
+
+ /* How many packets can we evenly distribute across
+ * (1 << (i + 1)) possible scheduling opportunities?
+ */
+ packets_transmitted = packets_remaining >> (i + 1);
+
+ /* Add in the bandwidth used for those scheduled packets */
+ bw_added = packets_transmitted * (overhead + packet_size);
+
+ /* How many packets do we have remaining to transmit? */
+ packets_remaining = packets_remaining % (1 << (i + 1));
+
+ /* What largest max packet size should those packets have? */
+ /* If we've transmitted all packets, don't carry over the
+ * largest packet size.
+ */
+ if (packets_remaining == 0) {
+ packet_size = 0;
+ overhead = 0;
+ } else if (packets_transmitted > 0) {
+ /* Otherwise if we do have remaining packets, and we've
+ * scheduled some packets in this interval, take the
+ * largest max packet size from endpoints with this
+ * interval.
+ */
+ packet_size = largest_mps;
+ overhead = interval_overhead;
+ }
+ /* Otherwise carry over packet_size and overhead from the last
+ * time we had a remainder.
+ */
+ bw_used += bw_added;
+ if (bw_used > max_bandwidth) {
+ xhci_warn(xhci, "Not enough bandwidth. "
+ "Proposed: %u, Max: %u\n",
+ bw_used, max_bandwidth);
+ return -ENOMEM;
+ }
+ }
+ /*
+ * Ok, we know we have some packets left over after even-handedly
+ * scheduling interval 15. We don't know which microframes they will
+ * fit into, so we over-schedule and say they will be scheduled every
+ * microframe.
+ */
+ if (packets_remaining > 0)
+ bw_used += overhead + packet_size;
+
+ if (!virt_dev->tt_info && virt_dev->udev->speed == USB_SPEED_HIGH) {
+ unsigned int port_index = virt_dev->real_port - 1;
+
+ /* OK, we're manipulating a HS device attached to a
+ * root port bandwidth domain. Include the number of active TTs
+ * in the bandwidth used.
+ */
+ bw_used += TT_HS_OVERHEAD *
+ xhci->rh_bw[port_index].num_active_tts;
+ }
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Final bandwidth: %u, Limit: %u, Reserved: %u, "
+ "Available: %u " "percent",
+ bw_used, max_bandwidth, bw_reserved,
+ (max_bandwidth - bw_used - bw_reserved) * 100 /
+ max_bandwidth);
+
+ bw_used += bw_reserved;
+ if (bw_used > max_bandwidth) {
+ xhci_warn(xhci, "Not enough bandwidth. Proposed: %u, Max: %u\n",
+ bw_used, max_bandwidth);
+ return -ENOMEM;
+ }
+
+ bw_table->bw_used = bw_used;
+ return 0;
+}
+
+static bool xhci_is_async_ep(unsigned int ep_type)
+{
+ return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP &&
+ ep_type != ISOC_IN_EP &&
+ ep_type != INT_IN_EP);
+}
+
+static bool xhci_is_sync_in_ep(unsigned int ep_type)
+{
+ return (ep_type == ISOC_IN_EP || ep_type == INT_IN_EP);
+}
+
+static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw)
+{
+ unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK);
+
+ if (ep_bw->ep_interval == 0)
+ return SS_OVERHEAD_BURST +
+ (ep_bw->mult * ep_bw->num_packets *
+ (SS_OVERHEAD + mps));
+ return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets *
+ (SS_OVERHEAD + mps + SS_OVERHEAD_BURST),
+ 1 << ep_bw->ep_interval);
+
+}
+
+void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info)
+{
+ struct xhci_interval_bw *interval_bw;
+ int normalized_interval;
+
+ if (xhci_is_async_ep(ep_bw->type))
+ return;
+
+ if (udev->speed == USB_SPEED_SUPER) {
+ if (xhci_is_sync_in_ep(ep_bw->type))
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_in -=
+ xhci_get_ss_bw_consumed(ep_bw);
+ else
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_out -=
+ xhci_get_ss_bw_consumed(ep_bw);
+ return;
+ }
+
+ /* SuperSpeed endpoints never get added to intervals in the table, so
+ * this check is only valid for HS/FS/LS devices.
+ */
+ if (list_empty(&virt_ep->bw_endpoint_list))
+ return;
+ /* For LS/FS devices, we need to translate the interval expressed in
+ * microframes to frames.
+ */
+ if (udev->speed == USB_SPEED_HIGH)
+ normalized_interval = ep_bw->ep_interval;
+ else
+ normalized_interval = ep_bw->ep_interval - 3;
+
+ if (normalized_interval == 0)
+ bw_table->interval0_esit_payload -= ep_bw->max_esit_payload;
+ interval_bw = &bw_table->interval_bw[normalized_interval];
+ interval_bw->num_packets -= ep_bw->num_packets;
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_FULL:
+ interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_HIGH:
+ interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_SUPER:
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ /* Should never happen because only LS/FS/HS endpoints will get
+ * added to the endpoint list.
+ */
+ return;
+ }
+ if (tt_info)
+ tt_info->active_eps -= 1;
+ list_del_init(&virt_ep->bw_endpoint_list);
+}
+
+static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info)
+{
+ struct xhci_interval_bw *interval_bw;
+ struct xhci_virt_ep *smaller_ep;
+ int normalized_interval;
+
+ if (xhci_is_async_ep(ep_bw->type))
+ return;
+
+ if (udev->speed == USB_SPEED_SUPER) {
+ if (xhci_is_sync_in_ep(ep_bw->type))
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_in +=
+ xhci_get_ss_bw_consumed(ep_bw);
+ else
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_out +=
+ xhci_get_ss_bw_consumed(ep_bw);
+ return;
+ }
+
+ /* For LS/FS devices, we need to translate the interval expressed in
+ * microframes to frames.
+ */
+ if (udev->speed == USB_SPEED_HIGH)
+ normalized_interval = ep_bw->ep_interval;
+ else
+ normalized_interval = ep_bw->ep_interval - 3;
+
+ if (normalized_interval == 0)
+ bw_table->interval0_esit_payload += ep_bw->max_esit_payload;
+ interval_bw = &bw_table->interval_bw[normalized_interval];
+ interval_bw->num_packets += ep_bw->num_packets;
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ interval_bw->overhead[LS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_FULL:
+ interval_bw->overhead[FS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_HIGH:
+ interval_bw->overhead[HS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_SUPER:
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ /* Should never happen because only LS/FS/HS endpoints will get
+ * added to the endpoint list.
+ */
+ return;
+ }
+
+ if (tt_info)
+ tt_info->active_eps += 1;
+ /* Insert the endpoint into the list, largest max packet size first. */
+ list_for_each_entry(smaller_ep, &interval_bw->endpoints,
+ bw_endpoint_list) {
+ if (ep_bw->max_packet_size >=
+ smaller_ep->bw_info.max_packet_size) {
+ /* Add the new ep before the smaller endpoint */
+ list_add_tail(&virt_ep->bw_endpoint_list,
+ &smaller_ep->bw_endpoint_list);
+ return;
+ }
+ }
+ /* Add the new endpoint at the end of the list. */
+ list_add_tail(&virt_ep->bw_endpoint_list,
+ &interval_bw->endpoints);
+}
+
+void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ struct xhci_root_port_bw_info *rh_bw_info;
+ if (!virt_dev->tt_info)
+ return;
+
+ rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1];
+ if (old_active_eps == 0 &&
+ virt_dev->tt_info->active_eps != 0) {
+ rh_bw_info->num_active_tts += 1;
+ rh_bw_info->bw_table.bw_used += TT_HS_OVERHEAD;
+ } else if (old_active_eps != 0 &&
+ virt_dev->tt_info->active_eps == 0) {
+ rh_bw_info->num_active_tts -= 1;
+ rh_bw_info->bw_table.bw_used -= TT_HS_OVERHEAD;
+ }
+}
+
+static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct xhci_container_ctx *in_ctx)
+{
+ struct xhci_bw_info ep_bw_info[31];
+ int i;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ int old_active_eps = 0;
+
+ if (virt_dev->tt_info)
+ old_active_eps = virt_dev->tt_info->active_eps;
+
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 31; i++) {
+ if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
+ continue;
+
+ /* Make a copy of the BW info in case we need to revert this */
+ memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info,
+ sizeof(ep_bw_info[i]));
+ /* Drop the endpoint from the interval table if the endpoint is
+ * being dropped or changed.
+ */
+ if (EP_IS_DROPPED(ctrl_ctx, i))
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ /* Overwrite the information stored in the endpoints' bw_info */
+ xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev);
+ for (i = 0; i < 31; i++) {
+ /* Add any changed or added endpoints to the interval table */
+ if (EP_IS_ADDED(ctrl_ctx, i))
+ xhci_add_ep_to_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+
+ if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) {
+ /* Ok, this fits in the bandwidth we have.
+ * Update the number of active TTs.
+ */
+ xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
+ return 0;
+ }
+
+ /* We don't have enough bandwidth for this, revert the stored info. */
+ for (i = 0; i < 31; i++) {
+ if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
+ continue;
+
+ /* Drop the new copies of any added or changed endpoints from
+ * the interval table.
+ */
+ if (EP_IS_ADDED(ctrl_ctx, i)) {
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ /* Revert the endpoint back to its old information */
+ memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i],
+ sizeof(ep_bw_info[i]));
+ /* Add any changed or dropped endpoints back into the table */
+ if (EP_IS_DROPPED(ctrl_ctx, i))
+ xhci_add_ep_to_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ return -ENOMEM;
+}
+
+
+/* Issue a configure endpoint command or evaluate context command
+ * and wait for it to finish.
+ */
+static int xhci_configure_endpoint(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct xhci_command *command,
+ bool ctx_change, bool must_succeed)
+{
+ int ret;
+ unsigned long flags;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_virt_device *virt_dev;
+
+ if (!command)
+ return -EINVAL;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ virt_dev = xhci->devs[udev->slot_id];
+
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) &&
+ xhci_reserve_host_resources(xhci, ctrl_ctx)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "Not enough host resources, "
+ "active endpoint contexts = %u\n",
+ xhci->num_active_eps);
+ return -ENOMEM;
+ }
+ if ((xhci->quirks & XHCI_SW_BW_CHECKING) &&
+ xhci_reserve_bandwidth(xhci, virt_dev, command->in_ctx)) {
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
+ xhci_free_host_resources(xhci, ctrl_ctx);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "Not enough bandwidth\n");
+ return -ENOMEM;
+ }
+
+ if (!ctx_change)
+ ret = xhci_queue_configure_endpoint(xhci, command,
+ command->in_ctx->dma,
+ udev->slot_id, must_succeed);
+ else
+ ret = xhci_queue_evaluate_context(xhci, command,
+ command->in_ctx->dma,
+ udev->slot_id, must_succeed);
+ if (ret < 0) {
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
+ xhci_free_host_resources(xhci, ctrl_ctx);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "FIXME allocate a new ring segment");
+ return -ENOMEM;
+ }
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Wait for the configure endpoint command to complete */
+ wait_for_completion(command->completion);
+
+ if (!ctx_change)
+ ret = xhci_configure_endpoint_result(xhci, udev,
+ &command->status);
+ else
+ ret = xhci_evaluate_context_result(xhci, udev,
+ &command->status);
+
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* If the command failed, remove the reserved resources.
+ * Otherwise, clean up the estimate to include dropped eps.
+ */
+ if (ret)
+ xhci_free_host_resources(xhci, ctrl_ctx);
+ else
+ xhci_finish_resource_reservation(xhci, ctrl_ctx);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ }
+ return ret;
+}
+
+static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci,
+ struct xhci_virt_device *vdev, int i)
+{
+ struct xhci_virt_ep *ep = &vdev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n",
+ xhci_get_endpoint_address(i));
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+}
+
+/* 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;
+ struct xhci_hcd *xhci;
+ struct xhci_virt_device *virt_dev;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ struct xhci_command *command;
+
+ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return -ENODEV;
+
+ xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+ virt_dev = xhci->devs[udev->slot_id];
+
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = virt_dev->in_ctx;
+
+ /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto command_cleanup;
+ }
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG);
+ ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
+
+ /* Don't issue the command if there's no endpoints to update. */
+ if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) &&
+ ctrl_ctx->drop_flags == 0) {
+ ret = 0;
+ goto command_cleanup;
+ }
+ /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ for (i = 31; i >= 1; i--) {
+ __le32 le32 = cpu_to_le32(BIT(i));
+
+ if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32))
+ || (ctrl_ctx->add_flags & le32) || i == 1) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i));
+ break;
+ }
+ }
+ xhci_dbg(xhci, "New Input Control Context:\n");
+ xhci_dbg_ctx(xhci, virt_dev->in_ctx,
+ LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
+
+ ret = xhci_configure_endpoint(xhci, udev, command,
+ false, false);
+ if (ret)
+ /* Callee should call reset_bandwidth() */
+ goto command_cleanup;
+
+ xhci_dbg(xhci, "Output context after successful config ep cmd:\n");
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx,
+ LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
+
+ /* Free any rings that were dropped, but not changed. */
+ for (i = 1; i < 31; ++i) {
+ if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
+ !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
+ }
+ }
+ xhci_zero_in_ctx(xhci, virt_dev);
+ /*
+ * Install any rings for completely new endpoints or changed endpoints,
+ * and free or cache any old rings from changed endpoints.
+ */
+ for (i = 1; i < 31; ++i) {
+ if (!virt_dev->eps[i].new_ring)
+ continue;
+ /* Only cache or free the old ring if it exists.
+ * It may not if this is the first add of an endpoint.
+ */
+ if (virt_dev->eps[i].ring) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ }
+ xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
+ virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
+ virt_dev->eps[i].new_ring = NULL;
+ }
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
+
+ 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, true, __func__);
+ if (ret <= 0)
+ return;
+ xhci = hcd_to_xhci(hcd);
+
+ 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->eps[i].new_ring) {
+ xhci_ring_free(xhci, virt_dev->eps[i].new_ring);
+ virt_dev->eps[i].new_ring = NULL;
+ }
+ }
+ xhci_zero_in_ctx(xhci, virt_dev);
+}
+
+static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_container_ctx *out_ctx,
+ struct xhci_input_control_ctx *ctrl_ctx,
+ u32 add_flags, u32 drop_flags)
+{
+ ctrl_ctx->add_flags = cpu_to_le32(add_flags);
+ ctrl_ctx->drop_flags = cpu_to_le32(drop_flags);
+ xhci_slot_copy(xhci, in_ctx, out_ctx);
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+
+ xhci_dbg(xhci, "Input Context:\n");
+ xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags));
+}
+
+static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_dequeue_state *deq_state)
+{
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_container_ctx *in_ctx;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 added_ctxs;
+ dma_addr_t addr;
+
+ in_ctx = xhci->devs[slot_id]->in_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return;
+ }
+
+ xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx,
+ xhci->devs[slot_id]->out_ctx, ep_index);
+ ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
+ addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg,
+ deq_state->new_deq_ptr);
+ if (addr == 0) {
+ xhci_warn(xhci, "WARN Cannot submit config ep after "
+ "reset ep command\n");
+ xhci_warn(xhci, "WARN deq seg = %p, deq ptr = %p\n",
+ deq_state->new_deq_seg,
+ deq_state->new_deq_ptr);
+ return;
+ }
+ ep_ctx->deq = cpu_to_le64(addr | deq_state->new_cycle_state);
+
+ added_ctxs = xhci_get_endpoint_flag_from_index(ep_index);
+ xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx,
+ xhci->devs[slot_id]->out_ctx, ctrl_ctx,
+ added_ctxs, added_ctxs);
+}
+
+void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
+ unsigned int ep_index, struct xhci_td *td)
+{
+ struct xhci_dequeue_state deq_state;
+ struct xhci_virt_ep *ep;
+ struct usb_device *udev = td->urb->dev;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
+ "Cleaning up stalled endpoint ring");
+ ep = &xhci->devs[udev->slot_id]->eps[ep_index];
+ /* We need to move the HW's dequeue pointer past this TD,
+ * or it will attempt to resend it on the next doorbell ring.
+ */
+ xhci_find_new_dequeue_state(xhci, udev->slot_id,
+ ep_index, ep->stopped_stream, td, &deq_state);
+
+ if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
+ return;
+
+ /* HW with the reset endpoint quirk will use the saved dequeue state to
+ * issue a configure endpoint command later.
+ */
+ if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
+ "Queueing new dequeue state");
+ xhci_queue_new_dequeue_state(xhci, udev->slot_id,
+ ep_index, ep->stopped_stream, &deq_state);
+ } else {
+ /* Better hope no one uses the input context between now and the
+ * reset endpoint completion!
+ * XXX: No idea how this hardware will react when stream rings
+ * are enabled.
+ */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Setting up input context for "
+ "configure endpoint command");
+ xhci_setup_input_ctx_for_quirk(xhci, udev->slot_id,
+ ep_index, &deq_state);
+ }
+}
+
+/* Called when clearing halted device. The core should have sent the control
+ * message to clear the device halt condition. The host side of the halt should
+ * already be cleared with a reset endpoint command issued when the STALL tx
+ * event was received.
+ *
+ * Context: in_interrupt
+ */
+
+void xhci_endpoint_reset(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct xhci_hcd *xhci;
+
+ xhci = hcd_to_xhci(hcd);
+
+ /*
+ * We might need to implement the config ep cmd in xhci 4.8.1 note:
+ * The Reset Endpoint Command may only be issued to endpoints in the
+ * Halted state. If software wishes reset the Data Toggle or Sequence
+ * Number of an endpoint that isn't in the Halted state, then software
+ * may issue a Configure Endpoint Command with the Drop and Add bits set
+ * for the target endpoint. that is in the Stopped state.
+ */
+
+ /* For now just print debug to follow the situation */
+ xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n",
+ ep->desc.bEndpointAddress);
+}
+
+static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
+ struct usb_device *udev, struct usb_host_endpoint *ep,
+ unsigned int slot_id)
+{
+ int ret;
+ unsigned int ep_index;
+ unsigned int ep_state;
+
+ if (!ep)
+ return -EINVAL;
+ ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
+ if (ret <= 0)
+ return -EINVAL;
+ if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) {
+ xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
+ " descriptor for ep 0x%x does not support streams\n",
+ ep->desc.bEndpointAddress);
+ return -EINVAL;
+ }
+
+ ep_index = xhci_get_endpoint_index(&ep->desc);
+ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
+ if (ep_state & EP_HAS_STREAMS ||
+ ep_state & EP_GETTING_STREAMS) {
+ xhci_warn(xhci, "WARN: SuperSpeed bulk endpoint 0x%x "
+ "already has streams set up.\n",
+ ep->desc.bEndpointAddress);
+ xhci_warn(xhci, "Send email to xHCI maintainer and ask for "
+ "dynamic stream context array reallocation.\n");
+ return -EINVAL;
+ }
+ if (!list_empty(&xhci->devs[slot_id]->eps[ep_index].ring->td_list)) {
+ xhci_warn(xhci, "Cannot setup streams for SuperSpeed bulk "
+ "endpoint 0x%x; URBs are pending.\n",
+ ep->desc.bEndpointAddress);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void xhci_calculate_streams_entries(struct xhci_hcd *xhci,
+ unsigned int *num_streams, unsigned int *num_stream_ctxs)
+{
+ unsigned int max_streams;
+
+ /* The stream context array size must be a power of two */
+ *num_stream_ctxs = roundup_pow_of_two(*num_streams);
+ /*
+ * Find out how many primary stream array entries the host controller
+ * supports. Later we may use secondary stream arrays (similar to 2nd
+ * level page entries), but that's an optional feature for xHCI host
+ * controllers. xHCs must support at least 4 stream IDs.
+ */
+ max_streams = HCC_MAX_PSA(xhci->hcc_params);
+ if (*num_stream_ctxs > max_streams) {
+ xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n",
+ max_streams);
+ *num_stream_ctxs = max_streams;
+ *num_streams = max_streams;
+ }
+}
+
+/* Returns an error code if one of the endpoint already has streams.
+ * This does not change any data structures, it only checks and gathers
+ * information.
+ */
+static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ unsigned int *num_streams, u32 *changed_ep_bitmask)
+{
+ unsigned int max_streams;
+ unsigned int endpoint_flag;
+ int i;
+ int ret;
+
+ for (i = 0; i < num_eps; i++) {
+ ret = xhci_check_streams_endpoint(xhci, udev,
+ eps[i], udev->slot_id);
+ if (ret < 0)
+ return ret;
+
+ max_streams = usb_ss_max_streams(&eps[i]->ss_ep_comp);
+ if (max_streams < (*num_streams - 1)) {
+ xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n",
+ eps[i]->desc.bEndpointAddress,
+ max_streams);
+ *num_streams = max_streams+1;
+ }
+
+ endpoint_flag = xhci_get_endpoint_flag(&eps[i]->desc);
+ if (*changed_ep_bitmask & endpoint_flag)
+ return -EINVAL;
+ *changed_ep_bitmask |= endpoint_flag;
+ }
+ return 0;
+}
+
+static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps)
+{
+ u32 changed_ep_bitmask = 0;
+ unsigned int slot_id;
+ unsigned int ep_index;
+ unsigned int ep_state;
+ int i;
+
+ slot_id = udev->slot_id;
+ if (!xhci->devs[slot_id])
+ return 0;
+
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
+ /* Are streams already being freed for the endpoint? */
+ if (ep_state & EP_GETTING_NO_STREAMS) {
+ xhci_warn(xhci, "WARN Can't disable streams for "
+ "endpoint 0x%x, "
+ "streams are being disabled already\n",
+ eps[i]->desc.bEndpointAddress);
+ return 0;
+ }
+ /* Are there actually any streams to free? */
+ if (!(ep_state & EP_HAS_STREAMS) &&
+ !(ep_state & EP_GETTING_STREAMS)) {
+ xhci_warn(xhci, "WARN Can't disable streams for "
+ "endpoint 0x%x, "
+ "streams are already disabled!\n",
+ eps[i]->desc.bEndpointAddress);
+ xhci_warn(xhci, "WARN xhci_free_streams() called "
+ "with non-streams endpoint\n");
+ return 0;
+ }
+ changed_ep_bitmask |= xhci_get_endpoint_flag(&eps[i]->desc);
+ }
+ return changed_ep_bitmask;
+}
+
+/*
+ * The USB device drivers use this function (though the HCD interface in USB
+ * core) to prepare a set of bulk endpoints to use streams. Streams are used to
+ * coordinate mass storage command queueing across multiple endpoints (basically
+ * a stream ID == a task ID).
+ *
+ * Setting up streams involves allocating the same size stream context array
+ * for each endpoint and issuing a configure endpoint command for all endpoints.
+ *
+ * Don't allow the call to succeed if one endpoint only supports one stream
+ * (which means it doesn't support streams at all).
+ *
+ * Drivers may get less stream IDs than they asked for, if the host controller
+ * hardware or endpoints claim they can't support the number of requested
+ * stream IDs.
+ */
+int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ unsigned int num_streams, gfp_t mem_flags)
+{
+ int i, ret;
+ struct xhci_hcd *xhci;
+ struct xhci_virt_device *vdev;
+ struct xhci_command *config_cmd;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ unsigned int ep_index;
+ unsigned int num_stream_ctxs;
+ unsigned long flags;
+ u32 changed_ep_bitmask = 0;
+
+ if (!eps)
+ return -EINVAL;
+
+ /* Add one to the number of streams requested to account for
+ * stream 0 that is reserved for xHCI usage.
+ */
+ num_streams += 1;
+ xhci = hcd_to_xhci(hcd);
+ xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n",
+ num_streams);
+
+ /* MaxPSASize value 0 (2 streams) means streams are not supported */
+ if ((xhci->quirks & XHCI_BROKEN_STREAMS) ||
+ HCC_MAX_PSA(xhci->hcc_params) < 4) {
+ xhci_dbg(xhci, "xHCI controller does not support streams.\n");
+ return -ENOSYS;
+ }
+
+ config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
+ if (!config_cmd) {
+ xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
+ return -ENOMEM;
+ }
+ ctrl_ctx = xhci_get_input_control_ctx(config_cmd->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ xhci_free_command(xhci, config_cmd);
+ return -ENOMEM;
+ }
+
+ /* Check to make sure all endpoints are not already configured for
+ * streams. While we're at it, find the maximum number of streams that
+ * all the endpoints will support and check for duplicate endpoints.
+ */
+ spin_lock_irqsave(&xhci->lock, flags);
+ ret = xhci_calculate_streams_and_bitmask(xhci, udev, eps,
+ num_eps, &num_streams, &changed_ep_bitmask);
+ if (ret < 0) {
+ xhci_free_command(xhci, config_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return ret;
+ }
+ if (num_streams <= 1) {
+ xhci_warn(xhci, "WARN: endpoints can't handle "
+ "more than one stream.\n");
+ xhci_free_command(xhci, config_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -EINVAL;
+ }
+ vdev = xhci->devs[udev->slot_id];
+ /* Mark each endpoint as being in transition, so
+ * xhci_urb_enqueue() will reject all URBs.
+ */
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ vdev->eps[ep_index].ep_state |= EP_GETTING_STREAMS;
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Setup internal data structures and allocate HW data structures for
+ * streams (but don't install the HW structures in the input context
+ * until we're sure all memory allocation succeeded).
+ */
+ xhci_calculate_streams_entries(xhci, &num_streams, &num_stream_ctxs);
+ xhci_dbg(xhci, "Need %u stream ctx entries for %u stream IDs.\n",
+ num_stream_ctxs, num_streams);
+
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci,
+ num_stream_ctxs,
+ num_streams, mem_flags);
+ if (!vdev->eps[ep_index].stream_info)
+ goto cleanup;
+ /* Set maxPstreams in endpoint context and update deq ptr to
+ * point to stream context array. FIXME
+ */
+ }
+
+ /* Set up the input context for a configure endpoint command. */
+ for (i = 0; i < num_eps; i++) {
+ struct xhci_ep_ctx *ep_ctx;
+
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ ep_ctx = xhci_get_ep_ctx(xhci, config_cmd->in_ctx, ep_index);
+
+ xhci_endpoint_copy(xhci, config_cmd->in_ctx,
+ vdev->out_ctx, ep_index);
+ xhci_setup_streams_ep_input_ctx(xhci, ep_ctx,
+ vdev->eps[ep_index].stream_info);
+ }
+ /* Tell the HW to drop its old copy of the endpoint context info
+ * and add the updated copy from the input context.
+ */
+ xhci_setup_input_ctx_for_config_ep(xhci, config_cmd->in_ctx,
+ vdev->out_ctx, ctrl_ctx,
+ changed_ep_bitmask, changed_ep_bitmask);
+
+ /* Issue and wait for the configure endpoint command */
+ ret = xhci_configure_endpoint(xhci, udev, config_cmd,
+ false, false);
+
+ /* xHC rejected the configure endpoint command for some reason, so we
+ * leave the old ring intact and free our internal streams data
+ * structure.
+ */
+ if (ret < 0)
+ goto cleanup;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS;
+ xhci_dbg(xhci, "Slot %u ep ctx %u now has streams.\n",
+ udev->slot_id, ep_index);
+ vdev->eps[ep_index].ep_state |= EP_HAS_STREAMS;
+ }
+ xhci_free_command(xhci, config_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Subtract 1 for stream 0, which drivers can't use */
+ return num_streams - 1;
+
+cleanup:
+ /* If it didn't work, free the streams! */
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
+ vdev->eps[ep_index].stream_info = NULL;
+ /* FIXME Unset maxPstreams in endpoint context and
+ * update deq ptr to point to normal string ring.
+ */
+ vdev->eps[ep_index].ep_state &= ~EP_GETTING_STREAMS;
+ vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS;
+ xhci_endpoint_zero(xhci, vdev, eps[i]);
+ }
+ xhci_free_command(xhci, config_cmd);
+ return -ENOMEM;
+}
+
+/* Transition the endpoint from using streams to being a "normal" endpoint
+ * without streams.
+ *
+ * Modify the endpoint context state, submit a configure endpoint command,
+ * and free all endpoint rings for streams if that completes successfully.
+ */
+int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ gfp_t mem_flags)
+{
+ int i, ret;
+ struct xhci_hcd *xhci;
+ struct xhci_virt_device *vdev;
+ struct xhci_command *command;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ unsigned int ep_index;
+ unsigned long flags;
+ u32 changed_ep_bitmask;
+
+ xhci = hcd_to_xhci(hcd);
+ vdev = xhci->devs[udev->slot_id];
+
+ /* Set up a configure endpoint command to remove the streams rings */
+ spin_lock_irqsave(&xhci->lock, flags);
+ changed_ep_bitmask = xhci_calculate_no_streams_bitmask(xhci,
+ udev, eps, num_eps);
+ if (changed_ep_bitmask == 0) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -EINVAL;
+ }
+
+ /* Use the xhci_command structure from the first endpoint. We may have
+ * allocated too many, but the driver may call xhci_free_streams() for
+ * each endpoint it grouped into one call to xhci_alloc_streams().
+ */
+ ep_index = xhci_get_endpoint_index(&eps[0]->desc);
+ command = vdev->eps[ep_index].stream_info->free_streams_command;
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_eps; i++) {
+ struct xhci_ep_ctx *ep_ctx;
+
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index);
+ xhci->devs[udev->slot_id]->eps[ep_index].ep_state |=
+ EP_GETTING_NO_STREAMS;
+
+ xhci_endpoint_copy(xhci, command->in_ctx,
+ vdev->out_ctx, ep_index);
+ xhci_setup_no_streams_ep_input_ctx(ep_ctx,
+ &vdev->eps[ep_index]);
+ }
+ xhci_setup_input_ctx_for_config_ep(xhci, command->in_ctx,
+ vdev->out_ctx, ctrl_ctx,
+ changed_ep_bitmask, changed_ep_bitmask);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Issue and wait for the configure endpoint command,
+ * which must succeed.
+ */
+ ret = xhci_configure_endpoint(xhci, udev, command,
+ false, true);
+
+ /* xHC rejected the configure endpoint command for some reason, so we
+ * leave the streams rings intact.
+ */
+ if (ret < 0)
+ return ret;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
+ vdev->eps[ep_index].stream_info = NULL;
+ /* FIXME Unset maxPstreams in endpoint context and
+ * update deq ptr to point to normal string ring.
+ */
+ vdev->eps[ep_index].ep_state &= ~EP_GETTING_NO_STREAMS;
+ vdev->eps[ep_index].ep_state &= ~EP_HAS_STREAMS;
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ return 0;
+}
+
+/*
+ * Deletes endpoint resources for endpoints that were active before a Reset
+ * Device command, or a Disable Slot command. The Reset Device command leaves
+ * the control endpoint intact, whereas the Disable Slot command deletes it.
+ *
+ * Must be called with xhci->lock held.
+ */
+void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev, bool drop_control_ep)
+{
+ int i;
+ unsigned int num_dropped_eps = 0;
+ unsigned int drop_flags = 0;
+
+ for (i = (drop_control_ep ? 0 : 1); i < 31; i++) {
+ if (virt_dev->eps[i].ring) {
+ drop_flags |= 1 << i;
+ num_dropped_eps++;
+ }
+ }
+ xhci->num_active_eps -= num_dropped_eps;
+ if (num_dropped_eps)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Dropped %u ep ctxs, flags = 0x%x, "
+ "%u now active.",
+ num_dropped_eps, drop_flags,
+ xhci->num_active_eps);
+}
+
+/*
+ * This submits a Reset Device Command, which will set the device state to 0,
+ * set the device address to 0, and disable all the endpoints except the default
+ * control endpoint. The USB core should come back and call
+ * xhci_address_device(), and then re-set up the configuration. If this is
+ * called because of a usb_reset_and_verify_device(), then the old alternate
+ * settings will be re-installed through the normal bandwidth allocation
+ * functions.
+ *
+ * Wait for the Reset Device command to finish. Remove all structures
+ * associated with the endpoints that were disabled. Clear the input device
+ * structure? Cache the rings? Reset the control endpoint 0 max packet size?
+ *
+ * If the virt_dev to be reset does not exist or does not match the udev,
+ * it means the device is lost, possibly due to the xHC restore error and
+ * re-initialization during S3/S4. In this case, call xhci_alloc_dev() to
+ * re-allocate the device.
+ */
+int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ int ret, i;
+ unsigned long flags;
+ struct xhci_hcd *xhci;
+ unsigned int slot_id;
+ struct xhci_virt_device *virt_dev;
+ struct xhci_command *reset_device_cmd;
+ int last_freed_endpoint;
+ struct xhci_slot_ctx *slot_ctx;
+ int old_active_eps = 0;
+
+ ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+ slot_id = udev->slot_id;
+ virt_dev = xhci->devs[slot_id];
+ if (!virt_dev) {
+ xhci_dbg(xhci, "The device to be reset with slot ID %u does "
+ "not exist. Re-allocate the device\n", slot_id);
+ ret = xhci_alloc_dev(hcd, udev);
+ if (ret == 1)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (virt_dev->udev != udev) {
+ /* If the virt_dev and the udev does not match, this virt_dev
+ * may belong to another udev.
+ * Re-allocate the device.
+ */
+ xhci_dbg(xhci, "The device to be reset with slot ID %u does "
+ "not match the udev. Re-allocate the device\n",
+ slot_id);
+ ret = xhci_alloc_dev(hcd, udev);
+ if (ret == 1)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ /* If device is not setup, there is no point in resetting it */
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+ if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) ==
+ SLOT_STATE_DISABLED)
+ return 0;
+
+ xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id);
+ /* Allocate the command structure that holds the struct completion.
+ * Assume we're in process context, since the normal device reset
+ * process has to wait for the device anyway. Storage devices are
+ * reset as part of error handling, so use GFP_NOIO instead of
+ * GFP_KERNEL.
+ */
+ reset_device_cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
+ if (!reset_device_cmd) {
+ xhci_dbg(xhci, "Couldn't allocate command structure.\n");
+ return -ENOMEM;
+ }
+
+ /* Attempt to submit the Reset Device command to the command ring */
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ ret = xhci_queue_reset_device(xhci, reset_device_cmd, slot_id);
+ if (ret) {
+ xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ goto command_cleanup;
+ }
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* Wait for the Reset Device command to finish */
+ wait_for_completion(reset_device_cmd->completion);
+
+ /* The Reset Device command can't fail, according to the 0.95/0.96 spec,
+ * unless we tried to reset a slot ID that wasn't enabled,
+ * or the device wasn't in the addressed or configured state.
+ */
+ ret = reset_device_cmd->status;
+ switch (ret) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout waiting for reset device command\n");
+ ret = -ETIME;
+ goto command_cleanup;
+ case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
+ case COMP_CTX_STATE: /* 0.96 completion code for same thing */
+ xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n",
+ slot_id,
+ xhci_get_slot_state(xhci, virt_dev->out_ctx));
+ xhci_dbg(xhci, "Not freeing device rings.\n");
+ /* Don't treat this as an error. May change my mind later. */
+ ret = 0;
+ goto command_cleanup;
+ case COMP_SUCCESS:
+ xhci_dbg(xhci, "Successful reset device command.\n");
+ break;
+ default:
+ if (xhci_is_vendor_info_code(xhci, ret))
+ break;
+ xhci_warn(xhci, "Unknown completion code %u for "
+ "reset device command.\n", ret);
+ ret = -EINVAL;
+ goto command_cleanup;
+ }
+
+ /* Free up host controller endpoint resources */
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* Don't delete the default control endpoint resources */
+ xhci_free_device_endpoint_resources(xhci, virt_dev, false);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ }
+
+ /* Everything but endpoint 0 is disabled, so free or cache the rings. */
+ last_freed_endpoint = 1;
+ for (i = 1; i < 31; ++i) {
+ struct xhci_virt_ep *ep = &virt_dev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n",
+ xhci_get_endpoint_address(i));
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+
+ if (ep->ring) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ last_freed_endpoint = i;
+ }
+ if (!list_empty(&virt_dev->eps[i].bw_endpoint_list))
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info);
+ }
+ /* If necessary, update the number of active TTs on this root port */
+ xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
+
+ xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
+ ret = 0;
+
+command_cleanup:
+ xhci_free_command(xhci, reset_device_cmd);
+ return ret;
+}
+
+/*
+ * 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);
+ struct xhci_virt_device *virt_dev;
+ unsigned long flags;
+ u32 state;
+ int i, ret;
+ struct xhci_command *command;
+
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return;
+
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+ /*
+ * We called pm_runtime_get_noresume when the device was attached.
+ * Decrement the counter here to allow controller to runtime suspend
+ * if no devices remain.
+ */
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ pm_runtime_put_noidle(hcd->self.controller);
+#endif
+
+ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
+ /* If the host is halted due to driver unload, we still need to free the
+ * device.
+ */
+ if (ret <= 0 && ret != -ENODEV) {
+ kfree(command);
+ return;
+ }
+
+ virt_dev = xhci->devs[udev->slot_id];
+
+ /* Stop any wayward timer functions (which may grab the lock) */
+ for (i = 0; i < 31; ++i) {
+ virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING;
+ del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
+ }
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* Don't disable the slot if the host controller is dead. */
+ state = readl(&xhci->op_regs->status);
+ if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
+ xhci_free_virt_device(xhci, udev->slot_id);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ kfree(command);
+ return;
+ }
+
+ if (xhci_queue_slot_control(xhci, command, 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?
+ */
+}
+
+/*
+ * Checks if we have enough host controller resources for the default control
+ * endpoint.
+ *
+ * Must be called with xhci->lock held.
+ */
+static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci)
+{
+ if (xhci->num_active_eps + 1 > xhci->limit_active_eps) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Not enough ep ctxs: "
+ "%u active, need to add 1, limit is %u.",
+ xhci->num_active_eps, xhci->limit_active_eps);
+ return -ENOMEM;
+ }
+ xhci->num_active_eps += 1;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "Adding 1 ep ctx, %u now active.",
+ xhci->num_active_eps);
+ return 0;
+}
+
+
+/*
+ * 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 ret, slot_id;
+ struct xhci_command *command;
+
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return 0;
+
+ /* xhci->slot_id and xhci->addr_dev are not thread-safe */
+ mutex_lock(&xhci->mutex);
+ spin_lock_irqsave(&xhci->lock, flags);
+ command->completion = &xhci->addr_dev;
+ ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
+ if (ret) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ mutex_unlock(&xhci->mutex);
+ xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+ kfree(command);
+ return 0;
+ }
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(command->completion);
+ slot_id = xhci->slot_id;
+ mutex_unlock(&xhci->mutex);
+
+ if (!slot_id || command->status != COMP_SUCCESS) {
+ xhci_err(xhci, "Error while assigning device slot ID\n");
+ xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
+ HCS_MAX_SLOTS(
+ readl(&xhci->cap_regs->hcs_params1)));
+ kfree(command);
+ return 0;
+ }
+
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ ret = xhci_reserve_host_control_ep_resources(xhci);
+ if (ret) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "Not enough host resources, "
+ "active endpoint contexts = %u\n",
+ xhci->num_active_eps);
+ goto disable_slot;
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ }
+ /* Use GFP_NOIO, since this function can be called from
+ * xhci_discover_or_reset_device(), which may be called as part of
+ * mass storage driver error handling.
+ */
+ if (!xhci_alloc_virt_device(xhci, slot_id, udev, GFP_NOIO)) {
+ xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
+ goto disable_slot;
+ }
+ udev->slot_id = slot_id;
+
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+ /*
+ * If resetting upon resume, we can't put the controller into runtime
+ * suspend if there is a device attached.
+ */
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ pm_runtime_get_noresume(hcd->self.controller);
+#endif
+
+
+ kfree(command);
+ /* Is this a LS or FS device under a HS hub? */
+ /* Hub or peripherial? */
+ return 1;
+
+disable_slot:
+ /* Disable slot, if we can do it without mem alloc */
+ spin_lock_irqsave(&xhci->lock, flags);
+ command->completion = NULL;
+ command->status = 0;
+ if (!xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
+ udev->slot_id))
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+}
+
+/*
+ * Issue an Address Device command and optionally send a corresponding
+ * SetAddress request to the device.
+ * We should be protected by the usb_address0_mutex in hub_wq's hub_port_init,
+ * so we should only issue and wait on one address command at the same time.
+ */
+static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
+ enum xhci_setup_dev setup)
+{
+ const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address";
+ unsigned long flags;
+ struct xhci_virt_device *virt_dev;
+ int ret = 0;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_slot_ctx *slot_ctx;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ u64 temp_64;
+ struct xhci_command *command = NULL;
+
+ mutex_lock(&xhci->mutex);
+
+ if (!udev->slot_id) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Bad Slot ID %d", udev->slot_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ virt_dev = xhci->devs[udev->slot_id];
+
+ if (WARN_ON(!virt_dev)) {
+ /*
+ * In plug/unplug torture test with an NEC controller,
+ * a zero-dereference was observed once due to virt_dev = 0.
+ * Print useful debug rather than crash if it is observed again!
+ */
+ xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n",
+ udev->slot_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (setup == SETUP_CONTEXT_ONLY) {
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+ if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) ==
+ SLOT_STATE_DEFAULT) {
+ xhci_dbg(xhci, "Slot already in default state\n");
+ goto out;
+ }
+ }
+
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ command->in_ctx = virt_dev->in_ctx;
+ command->completion = &xhci->addr_dev;
+
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ /*
+ * If this is the first Set Address since device plug-in or
+ * virt_device realloaction after a resume with an xHCI power loss,
+ * then set up the slot context.
+ */
+ if (!slot_ctx->dev_info)
+ xhci_setup_addressable_virt_dev(xhci, udev);
+ /* Otherwise, update the control endpoint ring enqueue pointer. */
+ else
+ xhci_copy_ep0_dequeue_into_input_ctx(xhci, udev);
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
+ ctrl_ctx->drop_flags = 0;
+
+ xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ ret = xhci_queue_address_device(xhci, command, virt_dev->in_ctx->dma,
+ udev->slot_id, setup);
+ if (ret) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "FIXME: allocate a command ring segment");
+ goto out;
+ }
+ 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? */
+ wait_for_completion(command->completion);
+
+ /* 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.
+ */
+ switch (command->status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for setup device command\n");
+ ret = -ETIME;
+ break;
+ case COMP_CTX_STATE:
+ case COMP_EBADSLT:
+ xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n",
+ act, udev->slot_id);
+ ret = -EINVAL;
+ break;
+ case COMP_TX_ERR:
+ dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
+ ret = -EPROTO;
+ break;
+ case COMP_DEV_ERR:
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for setup %s command\n", act);
+ ret = -ENODEV;
+ break;
+ case COMP_SUCCESS:
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Successful setup %s command", act);
+ break;
+ default:
+ xhci_err(xhci,
+ "ERROR: unexpected setup %s command completion code 0x%x.\n",
+ act, command->status);
+ xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ goto out;
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Op regs DCBAA ptr = %#016llx", temp_64);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Slot ID %d dcbaa entry @%p = %#016llx",
+ udev->slot_id,
+ &xhci->dcbaa->dev_context_ptrs[udev->slot_id],
+ (unsigned long long)
+ le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id]));
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Output Context DMA address = %#08llx",
+ (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, 2);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+ xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2);
+ /*
+ * USB core uses address 1 for the roothubs, so we add one to the
+ * address given back to us by the HC.
+ */
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+ /* Zero the input context control for later use */
+ ctrl_ctx->add_flags = 0;
+ ctrl_ctx->drop_flags = 0;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_address,
+ "Internal device address = %d",
+ le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
+out:
+ mutex_unlock(&xhci->mutex);
+ kfree(command);
+ return ret;
+}
+
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS);
+}
+
+int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY);
+}
+
+/*
+ * Transfer the port index into real index in the HW port status
+ * registers. Caculate offset between the port's PORTSC register
+ * and port status base. Divide the number of per port register
+ * to get the real index. The raw port number bases 1.
+ */
+int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ __le32 __iomem *base_addr = &xhci->op_regs->port_status_base;
+ __le32 __iomem *addr;
+ int raw_port;
+
+ if (hcd->speed != HCD_USB3)
+ addr = xhci->usb2_ports[port1 - 1];
+ else
+ addr = xhci->usb3_ports[port1 - 1];
+
+ raw_port = (addr - base_addr)/NUM_PORT_REGS + 1;
+ return raw_port;
+}
+
+/*
+ * Issue an Evaluate Context command to change the Maximum Exit Latency in the
+ * slot context. If that succeeds, store the new MEL in the xhci_virt_device.
+ */
+static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci,
+ struct usb_device *udev, u16 max_exit_latency)
+{
+ struct xhci_virt_device *virt_dev;
+ struct xhci_command *command;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ virt_dev = xhci->devs[udev->slot_id];
+
+ /*
+ * virt_dev might not exists yet if xHC resumed from hibernate (S4) and
+ * xHC was re-initialized. Exit latency will be set later after
+ * hub_port_finish_reset() is done and xhci->devs[] are re-allocated
+ */
+
+ if (!virt_dev || max_exit_latency == virt_dev->current_mel) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+ }
+
+ /* Attempt to issue an Evaluate Context command to change the MEL. */
+ command = xhci->lpm_command;
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx);
+ slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT));
+ slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency);
+ slot_ctx->dev_state = 0;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Set up evaluate context for LPM MEL change.");
+ xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, command->in_ctx, 0);
+
+ /* Issue and wait for the evaluate context command. */
+ ret = xhci_configure_endpoint(xhci, udev, command,
+ true, true);
+ xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0);
+
+ if (!ret) {
+ spin_lock_irqsave(&xhci->lock, flags);
+ virt_dev->current_mel = max_exit_latency;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ }
+ return ret;
+}
+
+#ifdef CONFIG_PM
+
+/* BESL to HIRD Encoding array for USB2 LPM */
+static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000,
+ 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
+
+/* Calculate HIRD/BESL for USB2 PORTPMSC*/
+static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
+ struct usb_device *udev)
+{
+ int u2del, besl, besl_host;
+ int besl_device = 0;
+ u32 field;
+
+ u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+ field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
+
+ if (field & USB_BESL_SUPPORT) {
+ for (besl_host = 0; besl_host < 16; besl_host++) {
+ if (xhci_besl_encoding[besl_host] >= u2del)
+ break;
+ }
+ /* Use baseline BESL value as default */
+ if (field & USB_BESL_BASELINE_VALID)
+ besl_device = USB_GET_BESL_BASELINE(field);
+ else if (field & USB_BESL_DEEP_VALID)
+ besl_device = USB_GET_BESL_DEEP(field);
+ } else {
+ if (u2del <= 50)
+ besl_host = 0;
+ else
+ besl_host = (u2del - 51) / 75 + 1;
+ }
+
+ besl = besl_host + besl_device;
+ if (besl > 15)
+ besl = 15;
+
+ return besl;
+}
+
+/* Calculate BESLD, L1 timeout and HIRDM for USB2 PORTHLPMC */
+static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
+{
+ u32 field;
+ int l1;
+ int besld = 0;
+ int hirdm = 0;
+
+ field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
+
+ /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */
+ l1 = udev->l1_params.timeout / 256;
+
+ /* device has preferred BESLD */
+ if (field & USB_BESL_DEEP_VALID) {
+ besld = USB_GET_BESL_DEEP(field);
+ hirdm = 1;
+ }
+
+ return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm);
+}
+
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ __le32 __iomem **port_array;
+ __le32 __iomem *pm_addr, *hlpm_addr;
+ u32 pm_val, hlpm_val, field;
+ unsigned int port_num;
+ unsigned long flags;
+ int hird, exit_latency;
+ int ret;
+
+ if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
+ !udev->lpm_capable)
+ return -EPERM;
+
+ if (!udev->parent || udev->parent->parent ||
+ udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ return -EPERM;
+
+ if (udev->usb2_hw_lpm_capable != 1)
+ return -EPERM;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ port_array = xhci->usb2_ports;
+ port_num = udev->portnum - 1;
+ pm_addr = port_array[port_num] + PORTPMSC;
+ pm_val = readl(pm_addr);
+ hlpm_addr = port_array[port_num] + PORTHLPMC;
+ field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
+
+ xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
+ enable ? "enable" : "disable", port_num + 1);
+
+ if (enable) {
+ /* Host supports BESL timeout instead of HIRD */
+ if (udev->usb2_hw_lpm_besl_capable) {
+ /* if device doesn't have a preferred BESL value use a
+ * default one which works with mixed HIRD and BESL
+ * systems. See XHCI_DEFAULT_BESL definition in xhci.h
+ */
+ if ((field & USB_BESL_SUPPORT) &&
+ (field & USB_BESL_BASELINE_VALID))
+ hird = USB_GET_BESL_BASELINE(field);
+ else
+ hird = udev->l1_params.besl;
+
+ exit_latency = xhci_besl_encoding[hird];
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ /* USB 3.0 code dedicate one xhci->lpm_command->in_ctx
+ * input context for link powermanagement evaluate
+ * context commands. It is protected by hcd->bandwidth
+ * mutex and is shared by all devices. We need to set
+ * the max ext latency in USB 2 BESL LPM as well, so
+ * use the same mutex and xhci_change_max_exit_latency()
+ */
+ mutex_lock(hcd->bandwidth_mutex);
+ ret = xhci_change_max_exit_latency(xhci, udev,
+ exit_latency);
+ mutex_unlock(hcd->bandwidth_mutex);
+
+ if (ret < 0)
+ return ret;
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
+ writel(hlpm_val, hlpm_addr);
+ /* flush write */
+ readl(hlpm_addr);
+ } else {
+ hird = xhci_calculate_hird_besl(xhci, udev);
+ }
+
+ pm_val &= ~PORT_HIRD_MASK;
+ pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
+ writel(pm_val, pm_addr);
+ pm_val = readl(pm_addr);
+ pm_val |= PORT_HLE;
+ writel(pm_val, pm_addr);
+ /* flush write */
+ readl(pm_addr);
+ } else {
+ pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
+ writel(pm_val, pm_addr);
+ /* flush write */
+ readl(pm_addr);
+ if (udev->usb2_hw_lpm_besl_capable) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ mutex_lock(hcd->bandwidth_mutex);
+ xhci_change_max_exit_latency(xhci, udev, 0);
+ mutex_unlock(hcd->bandwidth_mutex);
+ return 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+}
+
+/* check if a usb2 port supports a given extened capability protocol
+ * only USB2 ports extended protocol capability values are cached.
+ * Return 1 if capability is supported
+ */
+static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
+ unsigned capability)
+{
+ u32 port_offset, port_count;
+ int i;
+
+ for (i = 0; i < xhci->num_ext_caps; i++) {
+ if (xhci->ext_caps[i] & capability) {
+ /* port offsets starts at 1 */
+ port_offset = XHCI_EXT_PORT_OFF(xhci->ext_caps[i]) - 1;
+ port_count = XHCI_EXT_PORT_COUNT(xhci->ext_caps[i]);
+ if (port >= port_offset &&
+ port < port_offset + port_count)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int portnum = udev->portnum - 1;
+
+ if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
+ !udev->lpm_capable)
+ return 0;
+
+ /* we only support lpm for non-hub device connected to root hub yet */
+ if (!udev->parent || udev->parent->parent ||
+ udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ return 0;
+
+ if (xhci->hw_lpm_support == 1 &&
+ xhci_check_usb2_port_capability(
+ xhci, portnum, XHCI_HLC)) {
+ udev->usb2_hw_lpm_capable = 1;
+ udev->l1_params.timeout = XHCI_L1_TIMEOUT;
+ udev->l1_params.besl = XHCI_DEFAULT_BESL;
+ if (xhci_check_usb2_port_capability(xhci, portnum,
+ XHCI_BLC))
+ udev->usb2_hw_lpm_besl_capable = 1;
+ }
+
+ return 0;
+}
+
+/*---------------------- USB 3.0 Link PM functions ------------------------*/
+
+/* Service interval in nanoseconds = 2^(bInterval - 1) * 125us * 1000ns / 1us */
+static unsigned long long xhci_service_interval_to_ns(
+ struct usb_endpoint_descriptor *desc)
+{
+ return (1ULL << (desc->bInterval - 1)) * 125 * 1000;
+}
+
+static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
+ enum usb3_link_state state)
+{
+ unsigned long long sel;
+ unsigned long long pel;
+ unsigned int max_sel_pel;
+ char *state_name;
+
+ switch (state) {
+ case USB3_LPM_U1:
+ /* Convert SEL and PEL stored in nanoseconds to microseconds */
+ sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
+ pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
+ max_sel_pel = USB3_LPM_MAX_U1_SEL_PEL;
+ state_name = "U1";
+ break;
+ case USB3_LPM_U2:
+ sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
+ pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
+ max_sel_pel = USB3_LPM_MAX_U2_SEL_PEL;
+ state_name = "U2";
+ break;
+ default:
+ dev_warn(&udev->dev, "%s: Can't get timeout for non-U1 or U2 state.\n",
+ __func__);
+ return USB3_LPM_DISABLED;
+ }
+
+ if (sel <= max_sel_pel && pel <= max_sel_pel)
+ return USB3_LPM_DEVICE_INITIATED;
+
+ if (sel > max_sel_pel)
+ dev_dbg(&udev->dev, "Device-initiated %s disabled "
+ "due to long SEL %llu ms\n",
+ state_name, sel);
+ else
+ dev_dbg(&udev->dev, "Device-initiated %s disabled "
+ "due to long PEL %llu ms\n",
+ state_name, pel);
+ return USB3_LPM_DISABLED;
+}
+
+/* The U1 timeout should be the maximum of the following values:
+ * - For control endpoints, U1 system exit latency (SEL) * 3
+ * - For bulk endpoints, U1 SEL * 5
+ * - For interrupt endpoints:
+ * - Notification EPs, U1 SEL * 3
+ * - Periodic EPs, max(105% of bInterval, U1 SEL * 2)
+ * - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2)
+ */
+static unsigned long long xhci_calculate_intel_u1_timeout(
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+ int ep_type;
+ int intr_type;
+
+ ep_type = usb_endpoint_type(desc);
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ timeout_ns = udev->u1_params.sel * 3;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ timeout_ns = udev->u1_params.sel * 5;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ intr_type = usb_endpoint_interrupt_type(desc);
+ if (intr_type == USB_ENDPOINT_INTR_NOTIFICATION) {
+ timeout_ns = udev->u1_params.sel * 3;
+ break;
+ }
+ /* Otherwise the calculation is the same as isoc eps */
+ case USB_ENDPOINT_XFER_ISOC:
+ timeout_ns = xhci_service_interval_to_ns(desc);
+ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns * 105, 100);
+ if (timeout_ns < udev->u1_params.sel * 2)
+ timeout_ns = udev->u1_params.sel * 2;
+ break;
+ default:
+ return 0;
+ }
+
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U1 timeout value. */
+static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
+ else
+ timeout_ns = udev->u1_params.sel;
+
+ /* The U1 timeout is encoded in 1us intervals.
+ * Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
+ */
+ if (timeout_ns == USB3_LPM_DISABLED)
+ timeout_ns = 1;
+ else
+ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000);
+
+ /* If the necessary timeout value is bigger than what we can set in the
+ * USB 3.0 hub, we have to disable hub-initiated U1.
+ */
+ if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT)
+ return timeout_ns;
+ dev_dbg(&udev->dev, "Hub-initiated U1 disabled "
+ "due to long timeout %llu ms\n", timeout_ns);
+ return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
+}
+
+/* The U2 timeout should be the maximum of:
+ * - 10 ms (to avoid the bandwidth impact on the scheduler)
+ * - largest bInterval of any active periodic endpoint (to avoid going
+ * into lower power link states between intervals).
+ * - the U2 Exit Latency of the device
+ */
+static unsigned long long xhci_calculate_intel_u2_timeout(
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+ unsigned long long u2_del_ns;
+
+ timeout_ns = 10 * 1000 * 1000;
+
+ if ((usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) &&
+ (xhci_service_interval_to_ns(desc) > timeout_ns))
+ timeout_ns = xhci_service_interval_to_ns(desc);
+
+ u2_del_ns = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat) * 1000ULL;
+ if (u2_del_ns > timeout_ns)
+ timeout_ns = u2_del_ns;
+
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U2 timeout value. */
+static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
+ else
+ timeout_ns = udev->u2_params.sel;
+
+ /* The U2 timeout is encoded in 256us intervals */
+ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
+ /* If the necessary timeout value is bigger than what we can set in the
+ * USB 3.0 hub, we have to disable hub-initiated U2.
+ */
+ if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT)
+ return timeout_ns;
+ dev_dbg(&udev->dev, "Hub-initiated U2 disabled "
+ "due to long timeout %llu ms\n", timeout_ns);
+ return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2);
+}
+
+static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc,
+ enum usb3_link_state state,
+ u16 *timeout)
+{
+ if (state == USB3_LPM_U1)
+ return xhci_calculate_u1_timeout(xhci, udev, desc);
+ else if (state == USB3_LPM_U2)
+ return xhci_calculate_u2_timeout(xhci, udev, desc);
+
+ return USB3_LPM_DISABLED;
+}
+
+static int xhci_update_timeout_for_endpoint(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc,
+ enum usb3_link_state state,
+ u16 *timeout)
+{
+ u16 alt_timeout;
+
+ alt_timeout = xhci_call_host_update_timeout_for_endpoint(xhci, udev,
+ desc, state, timeout);
+
+ /* If we found we can't enable hub-initiated LPM, or
+ * the U1 or U2 exit latency was too high to allow
+ * device-initiated LPM as well, just stop searching.
+ */
+ if (alt_timeout == USB3_LPM_DISABLED ||
+ alt_timeout == USB3_LPM_DEVICE_INITIATED) {
+ *timeout = alt_timeout;
+ return -E2BIG;
+ }
+ if (alt_timeout > *timeout)
+ *timeout = alt_timeout;
+ return 0;
+}
+
+static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_host_interface *alt,
+ enum usb3_link_state state,
+ u16 *timeout)
+{
+ int j;
+
+ for (j = 0; j < alt->desc.bNumEndpoints; j++) {
+ if (xhci_update_timeout_for_endpoint(xhci, udev,
+ &alt->endpoint[j].desc, state, timeout))
+ return -E2BIG;
+ continue;
+ }
+ return 0;
+}
+
+static int xhci_check_intel_tier_policy(struct usb_device *udev,
+ enum usb3_link_state state)
+{
+ struct usb_device *parent;
+ unsigned int num_hubs;
+
+ if (state == USB3_LPM_U2)
+ return 0;
+
+ /* Don't enable U1 if the device is on a 2nd tier hub or lower. */
+ for (parent = udev->parent, num_hubs = 0; parent->parent;
+ parent = parent->parent)
+ num_hubs++;
+
+ if (num_hubs < 2)
+ return 0;
+
+ dev_dbg(&udev->dev, "Disabling U1 link state for device"
+ " below second-tier hub.\n");
+ dev_dbg(&udev->dev, "Plug device into first-tier hub "
+ "to decrease power consumption.\n");
+ return -E2BIG;
+}
+
+static int xhci_check_tier_policy(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ enum usb3_link_state state)
+{
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ return xhci_check_intel_tier_policy(udev, state);
+ else
+ return 0;
+}
+
+/* Returns the U1 or U2 timeout that should be enabled.
+ * If the tier check or timeout setting functions return with a non-zero exit
+ * code, that means the timeout value has been finalized and we shouldn't look
+ * at any more endpoints.
+ */
+static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct usb_host_config *config;
+ char *state_name;
+ int i;
+ u16 timeout = USB3_LPM_DISABLED;
+
+ if (state == USB3_LPM_U1)
+ state_name = "U1";
+ else if (state == USB3_LPM_U2)
+ state_name = "U2";
+ else {
+ dev_warn(&udev->dev, "Can't enable unknown link state %i\n",
+ state);
+ return timeout;
+ }
+
+ if (xhci_check_tier_policy(xhci, udev, state) < 0)
+ return timeout;
+
+ /* Gather some information about the currently installed configuration
+ * and alternate interface settings.
+ */
+ if (xhci_update_timeout_for_endpoint(xhci, udev, &udev->ep0.desc,
+ state, &timeout))
+ return timeout;
+
+ config = udev->actconfig;
+ if (!config)
+ return timeout;
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ struct usb_driver *driver;
+ struct usb_interface *intf = config->interface[i];
+
+ if (!intf)
+ continue;
+
+ /* Check if any currently bound drivers want hub-initiated LPM
+ * disabled.
+ */
+ if (intf->dev.driver) {
+ driver = to_usb_driver(intf->dev.driver);
+ if (driver && driver->disable_hub_initiated_lpm) {
+ dev_dbg(&udev->dev, "Hub-initiated %s disabled "
+ "at request of driver %s\n",
+ state_name, driver->name);
+ return xhci_get_timeout_no_hub_lpm(udev, state);
+ }
+ }
+
+ /* Not sure how this could happen... */
+ if (!intf->cur_altsetting)
+ continue;
+
+ if (xhci_update_timeout_for_interface(xhci, udev,
+ intf->cur_altsetting,
+ state, &timeout))
+ return timeout;
+ }
+ return timeout;
+}
+
+static int calculate_max_exit_latency(struct usb_device *udev,
+ enum usb3_link_state state_changed,
+ u16 hub_encoded_timeout)
+{
+ unsigned long long u1_mel_us = 0;
+ unsigned long long u2_mel_us = 0;
+ unsigned long long mel_us = 0;
+ bool disabling_u1;
+ bool disabling_u2;
+ bool enabling_u1;
+ bool enabling_u2;
+
+ disabling_u1 = (state_changed == USB3_LPM_U1 &&
+ hub_encoded_timeout == USB3_LPM_DISABLED);
+ disabling_u2 = (state_changed == USB3_LPM_U2 &&
+ hub_encoded_timeout == USB3_LPM_DISABLED);
+
+ enabling_u1 = (state_changed == USB3_LPM_U1 &&
+ hub_encoded_timeout != USB3_LPM_DISABLED);
+ enabling_u2 = (state_changed == USB3_LPM_U2 &&
+ hub_encoded_timeout != USB3_LPM_DISABLED);
+
+ /* If U1 was already enabled and we're not disabling it,
+ * or we're going to enable U1, account for the U1 max exit latency.
+ */
+ if ((udev->u1_params.timeout != USB3_LPM_DISABLED && !disabling_u1) ||
+ enabling_u1)
+ u1_mel_us = DIV_ROUND_UP(udev->u1_params.mel, 1000);
+ if ((udev->u2_params.timeout != USB3_LPM_DISABLED && !disabling_u2) ||
+ enabling_u2)
+ u2_mel_us = DIV_ROUND_UP(udev->u2_params.mel, 1000);
+
+ if (u1_mel_us > u2_mel_us)
+ mel_us = u1_mel_us;
+ else
+ mel_us = u2_mel_us;
+ /* xHCI host controller max exit latency field is only 16 bits wide. */
+ if (mel_us > MAX_EXIT) {
+ dev_warn(&udev->dev, "Link PM max exit latency of %lluus "
+ "is too big.\n", mel_us);
+ return -E2BIG;
+ }
+ return mel_us;
+}
+
+/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
+int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state)
+{
+ struct xhci_hcd *xhci;
+ u16 hub_encoded_timeout;
+ int mel;
+ int ret;
+
+ xhci = hcd_to_xhci(hcd);
+ /* The LPM timeout values are pretty host-controller specific, so don't
+ * enable hub-initiated timeouts unless the vendor has provided
+ * information about their timeout algorithm.
+ */
+ if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
+ !xhci->devs[udev->slot_id])
+ return USB3_LPM_DISABLED;
+
+ hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state);
+ mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout);
+ if (mel < 0) {
+ /* Max Exit Latency is too big, disable LPM. */
+ hub_encoded_timeout = USB3_LPM_DISABLED;
+ mel = 0;
+ }
+
+ ret = xhci_change_max_exit_latency(xhci, udev, mel);
+ if (ret)
+ return ret;
+ return hub_encoded_timeout;
+}
+
+int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state)
+{
+ struct xhci_hcd *xhci;
+ u16 mel;
+ int ret;
+
+ xhci = hcd_to_xhci(hcd);
+ if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
+ !xhci->devs[udev->slot_id])
+ return 0;
+
+ mel = calculate_max_exit_latency(udev, state, USB3_LPM_DISABLED);
+ ret = xhci_change_max_exit_latency(xhci, udev, mel);
+ if (ret)
+ return ret;
+ return 0;
+}
+#else /* CONFIG_PM */
+
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable)
+{
+ return 0;
+}
+
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ return 0;
+}
+
+int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state)
+{
+ return USB3_LPM_DISABLED;
+}
+
+int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/* Once a hub descriptor is fetched for a device, we need to update the xHC's
+ * internal data structures for the device.
+ */
+int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_virt_device *vdev;
+ struct xhci_command *config_cmd;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ unsigned long flags;
+ unsigned think_time;
+ int ret;
+
+ /* Ignore root hubs */
+ if (!hdev->parent)
+ return 0;
+
+ vdev = xhci->devs[hdev->slot_id];
+ if (!vdev) {
+ xhci_warn(xhci, "Cannot update hub desc for unknown device.\n");
+ return -EINVAL;
+ }
+ config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
+ if (!config_cmd) {
+ xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
+ return -ENOMEM;
+ }
+ ctrl_ctx = xhci_get_input_control_ctx(config_cmd->in_ctx);
+ if (!ctrl_ctx) {
+ xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
+ __func__);
+ xhci_free_command(xhci, config_cmd);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (hdev->speed == USB_SPEED_HIGH &&
+ xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) {
+ xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n");
+ xhci_free_command(xhci, config_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ENOMEM;
+ }
+
+ xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx);
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx);
+ slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
+ if (tt->multi)
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ if (xhci->hci_version > 0x95) {
+ xhci_dbg(xhci, "xHCI version %x needs hub "
+ "TT think time and number of ports\n",
+ (unsigned int) xhci->hci_version);
+ slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(hdev->maxchild));
+ /* Set TT think time - convert from ns to FS bit times.
+ * 0 = 8 FS bit times, 1 = 16 FS bit times,
+ * 2 = 24 FS bit times, 3 = 32 FS bit times.
+ *
+ * xHCI 1.0: this field shall be 0 if the device is not a
+ * High-spped hub.
+ */
+ think_time = tt->think_time;
+ if (think_time != 0)
+ think_time = (think_time / 666) - 1;
+ if (xhci->hci_version < 0x100 || hdev->speed == USB_SPEED_HIGH)
+ slot_ctx->tt_info |=
+ cpu_to_le32(TT_THINK_TIME(think_time));
+ } else {
+ xhci_dbg(xhci, "xHCI version %x doesn't need hub "
+ "TT think time or number of ports\n",
+ (unsigned int) xhci->hci_version);
+ }
+ slot_ctx->dev_state = 0;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ xhci_dbg(xhci, "Set up %s for hub device.\n",
+ (xhci->hci_version > 0x95) ?
+ "configure endpoint" : "evaluate context");
+ xhci_dbg(xhci, "Slot %u Input Context:\n", hdev->slot_id);
+ xhci_dbg_ctx(xhci, config_cmd->in_ctx, 0);
+
+ /* Issue and wait for the configure endpoint or
+ * evaluate context command.
+ */
+ if (xhci->hci_version > 0x95)
+ ret = xhci_configure_endpoint(xhci, hdev, config_cmd,
+ false, false);
+ else
+ ret = xhci_configure_endpoint(xhci, hdev, config_cmd,
+ true, false);
+
+ xhci_dbg(xhci, "Slot %u Output Context:\n", hdev->slot_id);
+ xhci_dbg_ctx(xhci, vdev->out_ctx, 0);
+
+ xhci_free_command(xhci, config_cmd);
+ return ret;
+}
+
+int xhci_get_frame(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ /* EHCI mods by the periodic size. Why? */
+ return readl(&xhci->run_regs->microframe_index) >> 3;
+}
+
+int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
+{
+ struct xhci_hcd *xhci;
+ struct device *dev = hcd->self.controller;
+ int retval;
+
+ /* Accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
+
+ /* support to build packet from discontinuous buffers */
+ hcd->self.no_sg_constraint = 1;
+
+ /* XHCI controllers don't stop the ep queue on short packets :| */
+ hcd->self.no_stop_on_short = 1;
+
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
+ if (!xhci)
+ return -ENOMEM;
+ *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+ xhci->main_hcd = hcd;
+ /* Mark the first roothub as being USB 2.0.
+ * The xHCI driver will register the USB 3.0 roothub.
+ */
+ hcd->speed = HCD_USB2;
+ hcd->self.root_hub->speed = USB_SPEED_HIGH;
+ /*
+ * USB 2.0 roothub under xHCI has an integrated TT,
+ * (rate matching hub) as opposed to having an OHCI/UHCI
+ * companion controller.
+ */
+ hcd->has_tt = 1;
+ } else {
+ /* xHCI private pointer was set in xhci_pci_probe for the second
+ * registered roothub.
+ */
+ return 0;
+ }
+
+ mutex_init(&xhci->mutex);
+ xhci->cap_regs = hcd->regs;
+ xhci->op_regs = hcd->regs +
+ HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
+ xhci->run_regs = hcd->regs +
+ (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
+ /* Cache read-only capability registers */
+ xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
+ xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
+ xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
+ xhci->hcc_params = readl(&xhci->cap_regs->hc_capbase);
+ xhci->hci_version = HC_VERSION(xhci->hcc_params);
+ xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
+ xhci_print_registers(xhci);
+
+ xhci->quirks = quirks;
+
+ get_quirks(dev, xhci);
+
+ /* In xhci controllers which follow xhci 1.0 spec gives a spurious
+ * success event after a short transfer. This quirk will ignore such
+ * spurious event.
+ */
+ if (xhci->hci_version > 0x96)
+ xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
+
+ /* Make sure the HC is halted. */
+ retval = xhci_halt(xhci);
+ if (retval)
+ goto error;
+
+ xhci_dbg(xhci, "Resetting HCD\n");
+ /* Reset the internal HC memory state and registers. */
+ retval = xhci_reset(xhci);
+ if (retval)
+ goto error;
+ xhci_dbg(xhci, "Reset complete\n");
+
+ /* Set dma_mask and coherent_dma_mask to 64-bits,
+ * if xHC supports 64-bit addressing */
+ if (HCC_64BIT_ADDR(xhci->hcc_params) &&
+ !dma_set_mask(dev, DMA_BIT_MASK(64))) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+ }
+
+ xhci_dbg(xhci, "Calling HCD init\n");
+ /* Initialize HCD and host controller data structures. */
+ retval = xhci_init(hcd);
+ if (retval)
+ goto error;
+ xhci_dbg(xhci, "Called HCD init\n");
+
+ xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%08x\n",
+ xhci->hcc_params, xhci->hci_version, xhci->quirks);
+
+ return 0;
+error:
+ kfree(xhci);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(xhci_gen_setup);
+
+static const struct hc_driver xhci_hc_driver = {
+ .description = "xhci-hcd",
+ .product_desc = "xHCI Host Controller",
+ .hcd_priv_size = sizeof(struct xhci_hcd *),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = xhci_irq,
+ .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = NULL, /* set in xhci_init_driver() */
+ .start = xhci_run,
+ .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,
+ .alloc_streams = xhci_alloc_streams,
+ .free_streams = xhci_free_streams,
+ .add_endpoint = xhci_add_endpoint,
+ .drop_endpoint = xhci_drop_endpoint,
+ .endpoint_reset = xhci_endpoint_reset,
+ .check_bandwidth = xhci_check_bandwidth,
+ .reset_bandwidth = xhci_reset_bandwidth,
+ .address_device = xhci_address_device,
+ .enable_device = xhci_enable_device,
+ .update_hub_device = xhci_update_hub_device,
+ .reset_device = xhci_discover_or_reset_device,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = xhci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_control = xhci_hub_control,
+ .hub_status_data = xhci_hub_status_data,
+ .bus_suspend = xhci_bus_suspend,
+ .bus_resume = xhci_bus_resume,
+
+ /*
+ * call back when device connected and addressed
+ */
+ .update_device = xhci_update_device,
+ .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
+ .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
+ .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
+ .find_raw_port_number = xhci_find_raw_port_number,
+};
+
+void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *))
+{
+ BUG_ON(!setup_fn);
+ *drv = xhci_hc_driver;
+ drv->reset = setup_fn;
+}
+EXPORT_SYMBOL_GPL(xhci_init_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static int __init xhci_hcd_init(void)
+{
+ /*
+ * 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_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);
+ return 0;
+}
+
+/*
+ * If an init function is provided, an exit function must also be provided
+ * to allow module unload.
+ */
+static void __exit xhci_hcd_fini(void) { }
+
+module_init(xhci_hcd_init);
+module_exit(xhci_hcd_fini);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
new file mode 100644
index 000000000..6977f8491
--- /dev/null
+++ b/drivers/usb/host/xhci.h
@@ -0,0 +1,1878 @@
+
+/*
+ * 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 <linux/kernel.h>
+#include <linux/usb/hcd.h>
+
+/* Code sharing between pci-quirks and xhci hcd */
+#include "xhci-ext-caps.h"
+#include "pci-quirks.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
+ */
+
+/**
+ * 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 {
+ __le32 hc_capbase;
+ __le32 hcs_params1;
+ __le32 hcs_params2;
+ __le32 hcs_params3;
+ __le32 hcc_params;
+ __le32 db_off;
+ __le32 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)
+/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
+/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
+/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
+#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
+
+/* 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(p) (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
+
+#define PORTSC 0
+#define PORTPMSC 1
+#define PORTLI 2
+#define PORTHLPMC 3
+
+/**
+ * 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 {
+ __le32 command;
+ __le32 status;
+ __le32 page_size;
+ __le32 reserved1;
+ __le32 reserved2;
+ __le32 dev_notification;
+ __le64 cmd_ring;
+ /* rsvd: offset 0x20-2F */
+ __le32 reserved3[4];
+ __le64 dcbaa_ptr;
+ __le32 config_reg;
+ /* rsvd: offset 0x3C-3FF */
+ __le32 reserved4[241];
+ /* port 1 registers, which serve as a base address for other ports */
+ __le32 port_status_base;
+ __le32 port_power_base;
+ __le32 port_link_base;
+ __le32 reserved5;
+ /* registers for ports 2-255 */
+ __le32 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)
+/* host controller save/restore state. */
+#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). */
+
+/* IMAN - Interrupt Management Register */
+#define IMAN_IE (1 << 1)
+#define IMAN_IP (1 << 0)
+
+/* 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_RSVD_BITS (0x3f)
+
+/* 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.
+ */
+#define PORT_PLS_MASK (0xf << 5)
+#define XDEV_U0 (0x0 << 5)
+#define XDEV_U2 (0x2 << 5)
+#define XDEV_U3 (0x3 << 5)
+#define XDEV_RESUME (0xf << 5)
+/* 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)
+/* Cold Attach Status - xHC can set this bit to report device attached during
+ * Sx state. Warm port reset should be perfomed to clear this bit and move port
+ * to connected state.
+ */
+#define PORT_CAS (1 << 24)
+/* 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 non-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)
+
+/* We mark duplicate entries with -1 */
+#define DUPLICATE_ENTRY ((u8)(-1))
+
+/* 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)
+#define PORT_U1_TIMEOUT_MASK 0xff
+/* Inactivity timer value for transitions into U2 */
+#define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8)
+#define PORT_U2_TIMEOUT_MASK (0xff << 8)
+/* Bits 24:31 for port testing */
+
+/* USB2 Protocol PORTSPMSC */
+#define PORT_L1S_MASK 7
+#define PORT_L1S_SUCCESS 1
+#define PORT_RWE (1 << 3)
+#define PORT_HIRD(p) (((p) & 0xf) << 4)
+#define PORT_HIRD_MASK (0xf << 4)
+#define PORT_L1DS_MASK (0xff << 8)
+#define PORT_L1DS(p) (((p) & 0xff) << 8)
+#define PORT_HLE (1 << 16)
+
+
+/* USB2 Protocol PORTHLPMC */
+#define PORT_HIRDM(p)((p) & 3)
+#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)
+#define PORT_BESLD(p)(((p) & 0xf) << 10)
+
+/* use 512 microseconds as USB2 LPM L1 default timeout. */
+#define XHCI_L1_TIMEOUT 512
+
+/* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume latency.
+ * Safe to use with mixed HIRD and BESL systems (host and device) and is used
+ * by other operating systems.
+ *
+ * XHCI 1.0 errata 8/14/12 Table 13 notes:
+ * "Software should choose xHC BESL/BESLD field values that do not violate a
+ * device's resume latency requirements,
+ * e.g. not program values > '4' if BLC = '1' and a HIRD device is attached,
+ * or not program values < '4' if BLC = '0' and a BESL device is attached.
+ */
+#define XHCI_DEFAULT_BESL 4
+
+/**
+ * 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 {
+ __le32 irq_pending;
+ __le32 irq_control;
+ __le32 erst_size;
+ __le32 rsvd;
+ __le64 erst_base;
+ __le64 erst_dequeue;
+};
+
+/* 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 {
+ __le32 microframe_index;
+ __le32 rsvd[7];
+ struct xhci_intr_reg ir_set[128];
+};
+
+/**
+ * struct doorbell_array
+ *
+ * Bits 0 - 7: Endpoint target
+ * Bits 8 - 15: RsvdZ
+ * Bits 16 - 31: Stream ID
+ *
+ * Section 5.6
+ */
+struct xhci_doorbell_array {
+ __le32 doorbell[256];
+};
+
+#define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16))
+#define DB_VALUE_HOST 0x00000000
+
+/**
+ * struct xhci_protocol_caps
+ * @revision: major revision, minor revision, capability ID,
+ * and next capability pointer.
+ * @name_string: Four ASCII characters to say which spec this xHC
+ * follows, typically "USB ".
+ * @port_info: Port offset, count, and protocol-defined information.
+ */
+struct xhci_protocol_caps {
+ u32 revision;
+ u32 name_string;
+ u32 port_info;
+};
+
+#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff)
+#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff)
+#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff)
+
+/**
+ * struct xhci_container_ctx
+ * @type: Type of context. Used to calculated offsets to contained contexts.
+ * @size: Size of the context data
+ * @bytes: The raw context data given to HW
+ * @dma: dma address of the bytes
+ *
+ * Represents either a Device or Input context. Holds a pointer to the raw
+ * memory used for the context (bytes) and dma address of it (dma).
+ */
+struct xhci_container_ctx {
+ unsigned type;
+#define XHCI_CTX_TYPE_DEVICE 0x1
+#define XHCI_CTX_TYPE_INPUT 0x2
+
+ int size;
+
+ u8 *bytes;
+ dma_addr_t dma;
+};
+
+/**
+ * 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 {
+ __le32 dev_info;
+ __le32 dev_info2;
+ __le32 tt_info;
+ __le32 dev_state;
+ /* offset 0x10 to 0x1f reserved for HC internal use */
+ __le32 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)
+#define DEVINFO_TO_ROOT_HUB_PORT(p) (((p) >> 16) & 0xff)
+/* Maximum number of ports under a hub device */
+#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24)
+
+/* 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)
+#define TT_THINK_TIME(p) (((p) & 0x3) << 16)
+
+/* 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)
+
+#define SLOT_STATE_DISABLED 0
+#define SLOT_STATE_ENABLED SLOT_STATE_DISABLED
+#define SLOT_STATE_DEFAULT 1
+#define SLOT_STATE_ADDRESSED 2
+#define SLOT_STATE_CONFIGURED 3
+
+/**
+ * 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 {
+ __le32 ep_info;
+ __le32 ep_info2;
+ __le64 deq;
+ __le32 tx_info;
+ /* offset 0x14 - 0x1f reserved for HC internal use */
+ __le32 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)
+#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3)
+/* 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)
+#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
+#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff)
+#define EP_MAXPSTREAMS_MASK (0x1f << 10)
+#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
+/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
+#define EP_HAS_LSA (1 << 15)
+
+/* 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 CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7)
+#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 CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff)
+#define MAX_PACKET(p) (((p)&0xffff) << 16)
+#define MAX_PACKET_MASK (0xffff << 16)
+#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff)
+
+/* Get max packet size from ep desc. Bit 10..0 specify the max packet size.
+ * USB2.0 spec 9.6.6.
+ */
+#define GET_MAX_PACKET(p) ((p) & 0x7ff)
+
+/* tx_info bitmasks */
+#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff)
+#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16)
+#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
+
+/* deq bitmasks */
+#define EP_CTX_CYCLE_MASK (1 << 0)
+#define SCTX_DEQ_MASK (~0xfL)
+
+
+/**
+ * struct xhci_input_control_context
+ * Input control 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_input_control_ctx {
+ __le32 drop_flags;
+ __le32 add_flags;
+ __le32 rsvd2[6];
+};
+
+#define EP_IS_ADDED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))
+#define EP_IS_DROPPED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1)))
+
+/* Represents everything that is needed to issue a command on the command ring.
+ * It's useful to pre-allocate these for commands that cannot fail due to
+ * out-of-memory errors, like freeing streams.
+ */
+struct xhci_command {
+ /* Input context for changing device state */
+ struct xhci_container_ctx *in_ctx;
+ u32 status;
+ /* If completion is null, no one is waiting on this command
+ * and the structure can be freed after the command completes.
+ */
+ struct completion *completion;
+ union xhci_trb *command_trb;
+ struct list_head cmd_list;
+};
+
+/* drop context bitmasks */
+#define DROP_EP(x) (0x1 << x)
+/* add context bitmasks */
+#define ADD_EP(x) (0x1 << x)
+
+struct xhci_stream_ctx {
+ /* 64-bit stream ring address, cycle state, and stream type */
+ __le64 stream_ring;
+ /* offset 0x14 - 0x1f reserved for HC internal use */
+ __le32 reserved[2];
+};
+
+/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */
+#define SCT_FOR_CTX(p) (((p) & 0x7) << 1)
+/* Secondary stream array type, dequeue pointer is to a transfer ring */
+#define SCT_SEC_TR 0
+/* Primary stream array type, dequeue pointer is to a transfer ring */
+#define SCT_PRI_TR 1
+/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */
+#define SCT_SSA_8 2
+#define SCT_SSA_16 3
+#define SCT_SSA_32 4
+#define SCT_SSA_64 5
+#define SCT_SSA_128 6
+#define SCT_SSA_256 7
+
+/* Assume no secondary streams for now */
+struct xhci_stream_info {
+ struct xhci_ring **stream_rings;
+ /* Number of streams, including stream 0 (which drivers can't use) */
+ unsigned int num_streams;
+ /* The stream context array may be bigger than
+ * the number of streams the driver asked for
+ */
+ struct xhci_stream_ctx *stream_ctx_array;
+ unsigned int num_stream_ctxs;
+ dma_addr_t ctx_array_dma;
+ /* For mapping physical TRB addresses to segments in stream rings */
+ struct radix_tree_root trb_address_map;
+ struct xhci_command *free_streams_command;
+};
+
+#define SMALL_STREAM_ARRAY_SIZE 256
+#define MEDIUM_STREAM_ARRAY_SIZE 1024
+
+/* Some Intel xHCI host controllers need software to keep track of the bus
+ * bandwidth. Keep track of endpoint info here. Each root port is allocated
+ * the full bus bandwidth. We must also treat TTs (including each port under a
+ * multi-TT hub) as a separate bandwidth domain. The direct memory interface
+ * (DMI) also limits the total bandwidth (across all domains) that can be used.
+ */
+struct xhci_bw_info {
+ /* ep_interval is zero-based */
+ unsigned int ep_interval;
+ /* mult and num_packets are one-based */
+ unsigned int mult;
+ unsigned int num_packets;
+ unsigned int max_packet_size;
+ unsigned int max_esit_payload;
+ unsigned int type;
+};
+
+/* "Block" sizes in bytes the hardware uses for different device speeds.
+ * The logic in this part of the hardware limits the number of bits the hardware
+ * can use, so must represent bandwidth in a less precise manner to mimic what
+ * the scheduler hardware computes.
+ */
+#define FS_BLOCK 1
+#define HS_BLOCK 4
+#define SS_BLOCK 16
+#define DMI_BLOCK 32
+
+/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated
+ * with each byte transferred. SuperSpeed devices have an initial overhead to
+ * set up bursts. These are in blocks, see above. LS overhead has already been
+ * translated into FS blocks.
+ */
+#define DMI_OVERHEAD 8
+#define DMI_OVERHEAD_BURST 4
+#define SS_OVERHEAD 8
+#define SS_OVERHEAD_BURST 32
+#define HS_OVERHEAD 26
+#define FS_OVERHEAD 20
+#define LS_OVERHEAD 128
+/* The TTs need to claim roughly twice as much bandwidth (94 bytes per
+ * microframe ~= 24Mbps) of the HS bus as the devices can actually use because
+ * of overhead associated with split transfers crossing microframe boundaries.
+ * 31 blocks is pure protocol overhead.
+ */
+#define TT_HS_OVERHEAD (31 + 94)
+#define TT_DMI_OVERHEAD (25 + 12)
+
+/* Bandwidth limits in blocks */
+#define FS_BW_LIMIT 1285
+#define TT_BW_LIMIT 1320
+#define HS_BW_LIMIT 1607
+#define SS_BW_LIMIT_IN 3906
+#define DMI_BW_LIMIT_IN 3906
+#define SS_BW_LIMIT_OUT 3906
+#define DMI_BW_LIMIT_OUT 3906
+
+/* Percentage of bus bandwidth reserved for non-periodic transfers */
+#define FS_BW_RESERVED 10
+#define HS_BW_RESERVED 20
+#define SS_BW_RESERVED 10
+
+struct xhci_virt_ep {
+ struct xhci_ring *ring;
+ /* Related to endpoints that are configured to use stream IDs only */
+ struct xhci_stream_info *stream_info;
+ /* 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_ring;
+ unsigned int ep_state;
+#define SET_DEQ_PENDING (1 << 0)
+#define EP_HALTED (1 << 1) /* For stall handling */
+#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */
+/* Transitioning the endpoint to using streams, don't enqueue URBs */
+#define EP_GETTING_STREAMS (1 << 3)
+#define EP_HAS_STREAMS (1 << 4)
+/* Transitioning the endpoint to not using streams, don't enqueue URBs */
+#define EP_GETTING_NO_STREAMS (1 << 5)
+ /* ---- Related to URB cancellation ---- */
+ struct list_head cancelled_td_list;
+ struct xhci_td *stopped_td;
+ unsigned int stopped_stream;
+ /* Watchdog timer for stop endpoint command to cancel URBs */
+ struct timer_list stop_cmd_timer;
+ int stop_cmds_pending;
+ struct xhci_hcd *xhci;
+ /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
+ * command. We'll need to update the ring's dequeue segment and dequeue
+ * pointer after the command completes.
+ */
+ struct xhci_segment *queued_deq_seg;
+ union xhci_trb *queued_deq_ptr;
+ /*
+ * Sometimes the xHC can not process isochronous endpoint ring quickly
+ * enough, and it will miss some isoc tds on the ring and generate
+ * a Missed Service Error Event.
+ * Set skip flag when receive a Missed Service Error Event and
+ * process the missed tds on the endpoint ring.
+ */
+ bool skip;
+ /* Bandwidth checking storage */
+ struct xhci_bw_info bw_info;
+ struct list_head bw_endpoint_list;
+};
+
+enum xhci_overhead_type {
+ LS_OVERHEAD_TYPE = 0,
+ FS_OVERHEAD_TYPE,
+ HS_OVERHEAD_TYPE,
+};
+
+struct xhci_interval_bw {
+ unsigned int num_packets;
+ /* Sorted by max packet size.
+ * Head of the list is the greatest max packet size.
+ */
+ struct list_head endpoints;
+ /* How many endpoints of each speed are present. */
+ unsigned int overhead[3];
+};
+
+#define XHCI_MAX_INTERVAL 16
+
+struct xhci_interval_bw_table {
+ unsigned int interval0_esit_payload;
+ struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL];
+ /* Includes reserved bandwidth for async endpoints */
+ unsigned int bw_used;
+ unsigned int ss_bw_in;
+ unsigned int ss_bw_out;
+};
+
+
+struct xhci_virt_device {
+ struct usb_device *udev;
+ /*
+ * 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_container_ctx *out_ctx;
+ /* Used for addressing devices and configuration changes */
+ struct xhci_container_ctx *in_ctx;
+ /* Rings saved to ensure old alt settings can be re-instated */
+ struct xhci_ring **ring_cache;
+ int num_rings_cached;
+#define XHCI_MAX_RINGS_CACHED 31
+ struct xhci_virt_ep eps[31];
+ struct completion cmd_completion;
+ u8 fake_port;
+ u8 real_port;
+ struct xhci_interval_bw_table *bw_table;
+ struct xhci_tt_bw_info *tt_info;
+ /* The current max exit latency for the enabled USB3 link states. */
+ u16 current_mel;
+};
+
+/*
+ * For each roothub, keep track of the bandwidth information for each periodic
+ * interval.
+ *
+ * If a high speed hub is attached to the roothub, each TT associated with that
+ * hub is a separate bandwidth domain. The interval information for the
+ * endpoints on the devices under that TT will appear in the TT structure.
+ */
+struct xhci_root_port_bw_info {
+ struct list_head tts;
+ unsigned int num_active_tts;
+ struct xhci_interval_bw_table bw_table;
+};
+
+struct xhci_tt_bw_info {
+ struct list_head tt_list;
+ int slot_id;
+ int ttport;
+ struct xhci_interval_bw_table bw_table;
+ int active_eps;
+};
+
+
+/**
+ * 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 */
+ __le64 dev_context_ptrs[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_transfer_event {
+ /* 64-bit buffer address, or immediate data */
+ __le64 buffer;
+ __le32 transfer_len;
+ /* This field is interpreted differently based on the type of TRB */
+ __le32 flags;
+};
+
+/* Transfer event TRB length bit mask */
+/* bits 0:23 */
+#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
+
+/** 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
+/* Incompatible Device Error */
+#define COMP_DEV_ERR 22
+/* 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 transferred length in the event is invalid */
+#define COMP_STOP_INVAL 27
+/* Control Abort Error - Debug Capability - control pipe aborted */
+#define COMP_DBG_ABORT 28
+/* Max Exit Latency Too Large Error */
+#define COMP_MEL_ERR 29
+/* TRB type 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 */
+#define COMP_2ND_BW_ERR 35
+/* Split Transaction Error */
+#define COMP_SPLIT_ERR 36
+
+struct xhci_link_trb {
+ /* 64-bit segment pointer*/
+ __le64 segment_ptr;
+ __le32 intr_target;
+ __le32 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 */
+ __le64 cmd_trb;
+ __le32 status;
+ __le32 flags;
+};
+
+/* flags bitmasks */
+
+/* Address device - disable SetAddress */
+#define TRB_BSR (1<<9)
+enum xhci_setup_dev {
+ SETUP_CONTEXT_ONLY,
+ SETUP_CONTEXT_ADDRESS,
+};
+
+/* 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)
+
+#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
+#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
+#define LAST_EP_INDEX 30
+
+/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */
+#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
+#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
+#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
+
+
+/* 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)
+/* 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)
+#define TRB_TBC(p) (((p) & 0x3) << 7)
+#define TRB_TLBPC(p) (((p) & 0xf) << 16)
+
+/* 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)
+
+/* Block Event Interrupt */
+#define TRB_BEI (1<<9)
+
+/* Control transfer TRB specific fields */
+#define TRB_DIR_IN (1<<16)
+#define TRB_TX_TYPE(p) ((p) << 16)
+#define TRB_DATA_OUT 2
+#define TRB_DATA_IN 3
+
+/* Isochronous TRB specific fields */
+#define TRB_SIA (1<<31)
+
+struct xhci_generic_trb {
+ __le32 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)
+#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 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 Endpoint Command */
+#define TRB_RESET_EP 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 */
+
+/* Nec vendor-specific command completion event. */
+#define TRB_NEC_CMD_COMP 48
+/* Get NEC firmware revision. */
+#define TRB_NEC_GET_FW 49
+
+#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
+/* Above, but for __le32 types -- can avoid work by swapping constants: */
+#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_LINK)))
+#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)))
+
+#define NEC_FW_MINOR(p) (((p) >> 0) & 0xff)
+#define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff)
+
+/*
+ * 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 256
+/* Allow two commands + a link TRB, along with any reserved command TRBs */
+#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
+#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT*16)
+#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE))
+/* 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;
+ /* actual_length of the URB has already been set */
+ bool urb_length_set;
+};
+
+/* xHCI command default timeout value */
+#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ)
+
+/* command descriptor */
+struct xhci_cd {
+ struct xhci_command *command;
+ union xhci_trb *cmd_trb;
+};
+
+struct xhci_dequeue_state {
+ struct xhci_segment *new_deq_seg;
+ union xhci_trb *new_deq_ptr;
+ int new_cycle_state;
+};
+
+enum xhci_ring_type {
+ TYPE_CTRL = 0,
+ TYPE_ISOC,
+ TYPE_BULK,
+ TYPE_INTR,
+ TYPE_STREAM,
+ TYPE_COMMAND,
+ TYPE_EVENT,
+};
+
+struct xhci_ring {
+ struct xhci_segment *first_seg;
+ struct xhci_segment *last_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;
+ /*
+ * 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;
+ unsigned int stream_id;
+ unsigned int num_segs;
+ unsigned int num_trbs_free;
+ unsigned int num_trbs_free_temp;
+ enum xhci_ring_type type;
+ bool last_td_was_short;
+ struct radix_tree_root *trb_address_map;
+};
+
+struct xhci_erst_entry {
+ /* 64-bit event ring segment address */
+ __le64 seg_addr;
+ __le32 seg_size;
+ /* Set to zero */
+ __le32 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;
+};
+
+struct xhci_scratchpad {
+ u64 *sp_array;
+ dma_addr_t sp_dma;
+ void **sp_buffers;
+ dma_addr_t *sp_dma_buffers;
+};
+
+struct urb_priv {
+ int length;
+ int td_cnt;
+ struct xhci_td *td[0];
+};
+
+/*
+ * 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
+/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */
+#define XHCI_STOP_EP_CMD_TIMEOUT 5
+/* XXX: Make these module parameters */
+
+struct s3_save {
+ u32 command;
+ u32 dev_nt;
+ u64 dcbaa_ptr;
+ u32 config_reg;
+ u32 irq_pending;
+ u32 irq_control;
+ u32 erst_size;
+ u64 erst_base;
+ u64 erst_dequeue;
+};
+
+/* Use for lpm */
+struct dev_info {
+ u32 dev_id;
+ struct list_head list;
+};
+
+struct xhci_bus_state {
+ unsigned long bus_suspended;
+ unsigned long next_statechange;
+
+ /* Port suspend arrays are indexed by the portnum of the fake roothub */
+ /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
+ u32 port_c_suspend;
+ u32 suspended_ports;
+ u32 port_remote_wakeup;
+ unsigned long resume_done[USB_MAXCHILDREN];
+ /* which ports have started to resume */
+ unsigned long resuming_ports;
+ /* Which ports are waiting on RExit to U0 transition. */
+ unsigned long rexit_ports;
+ struct completion rexit_done[USB_MAXCHILDREN];
+};
+
+
+/*
+ * It can take up to 20 ms to transition from RExit to U0 on the
+ * Intel Lynx Point LP xHCI host.
+ */
+#define XHCI_MAX_REXIT_TIMEOUT (20 * 1000)
+
+static inline unsigned int hcd_index(struct usb_hcd *hcd)
+{
+ if (hcd->speed == HCD_USB3)
+ return 0;
+ else
+ return 1;
+}
+
+/* There is one xhci_hcd structure per controller */
+struct xhci_hcd {
+ struct usb_hcd *main_hcd;
+ struct usb_hcd *shared_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;
+ /* msi-x vectors */
+ int msix_count;
+ struct msix_entry *msix_entries;
+ /* optional clock */
+ struct clk *clk;
+ /* data structures */
+ struct xhci_device_context_array *dcbaa;
+ struct xhci_ring *cmd_ring;
+ unsigned int cmd_ring_state;
+#define CMD_RING_STATE_RUNNING (1 << 0)
+#define CMD_RING_STATE_ABORTED (1 << 1)
+#define CMD_RING_STATE_STOPPED (1 << 2)
+ struct list_head cmd_list;
+ unsigned int cmd_ring_reserved_trbs;
+ struct timer_list cmd_timer;
+ struct xhci_command *current_cmd;
+ struct xhci_ring *event_ring;
+ struct xhci_erst erst;
+ /* Scratchpad */
+ struct xhci_scratchpad *scratchpad;
+ /* Store LPM test failed devices' information */
+ struct list_head lpm_failed_devs;
+
+ /* slot enabling and address device helpers */
+ /* these are not thread safe so use mutex */
+ struct mutex mutex;
+ struct completion addr_dev;
+ int slot_id;
+ /* For USB 3.0 LPM enable/disable. */
+ struct xhci_command *lpm_command;
+ /* Internal mirror of the HW's dcbaa */
+ struct xhci_virt_device *devs[MAX_HC_SLOTS];
+ /* For keeping track of bandwidth domains per roothub. */
+ struct xhci_root_port_bw_info *rh_bw;
+
+ /* DMA pools */
+ struct dma_pool *device_pool;
+ struct dma_pool *segment_pool;
+ struct dma_pool *small_streams_pool;
+ struct dma_pool *medium_streams_pool;
+
+ /* Host controller watchdog timer structures */
+ unsigned int xhc_state;
+
+ u32 command;
+ struct s3_save s3;
+/* Host controller is dying - not responding to commands. "I'm not dead yet!"
+ *
+ * xHC interrupts have been disabled and a watchdog timer will (or has already)
+ * halt the xHCI host, and complete all URBs with an -ESHUTDOWN code. Any code
+ * that sees this status (other than the timer that set it) should stop touching
+ * hardware immediately. Interrupt handlers should return immediately when
+ * they see this status (any time they drop and re-acquire xhci->lock).
+ * xhci_urb_dequeue() should call usb_hcd_check_unlink_urb() and return without
+ * putting the TD on the canceled list, etc.
+ *
+ * There are no reports of xHCI host controllers that display this issue.
+ */
+#define XHCI_STATE_DYING (1 << 0)
+#define XHCI_STATE_HALTED (1 << 1)
+ /* Statistics */
+ int error_bitmask;
+ unsigned int quirks;
+#define XHCI_LINK_TRB_QUIRK (1 << 0)
+#define XHCI_RESET_EP_QUIRK (1 << 1)
+#define XHCI_NEC_HOST (1 << 2)
+#define XHCI_AMD_PLL_FIX (1 << 3)
+#define XHCI_SPURIOUS_SUCCESS (1 << 4)
+/*
+ * Certain Intel host controllers have a limit to the number of endpoint
+ * contexts they can handle. Ideally, they would signal that they can't handle
+ * anymore endpoint contexts by returning a Resource Error for the Configure
+ * Endpoint command, but they don't. Instead they expect software to keep track
+ * of the number of active endpoints for them, across configure endpoint
+ * commands, reset device commands, disable slot commands, and address device
+ * commands.
+ */
+#define XHCI_EP_LIMIT_QUIRK (1 << 5)
+#define XHCI_BROKEN_MSI (1 << 6)
+#define XHCI_RESET_ON_RESUME (1 << 7)
+#define XHCI_SW_BW_CHECKING (1 << 8)
+#define XHCI_AMD_0x96_HOST (1 << 9)
+#define XHCI_TRUST_TX_LENGTH (1 << 10)
+#define XHCI_LPM_SUPPORT (1 << 11)
+#define XHCI_INTEL_HOST (1 << 12)
+#define XHCI_SPURIOUS_REBOOT (1 << 13)
+#define XHCI_COMP_MODE_QUIRK (1 << 14)
+#define XHCI_AVOID_BEI (1 << 15)
+#define XHCI_PLAT (1 << 16)
+#define XHCI_SLOW_SUSPEND (1 << 17)
+#define XHCI_SPURIOUS_WAKEUP (1 << 18)
+/* For controllers with a broken beyond repair streams implementation */
+#define XHCI_BROKEN_STREAMS (1 << 19)
+#define XHCI_PME_STUCK_QUIRK (1 << 20)
+ unsigned int num_active_eps;
+ unsigned int limit_active_eps;
+ /* There are two roothubs to keep track of bus suspend info for */
+ struct xhci_bus_state bus_state[2];
+ /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
+ u8 *port_array;
+ /* Array of pointers to USB 3.0 PORTSC registers */
+ __le32 __iomem **usb3_ports;
+ unsigned int num_usb3_ports;
+ /* Array of pointers to USB 2.0 PORTSC registers */
+ __le32 __iomem **usb2_ports;
+ unsigned int num_usb2_ports;
+ /* support xHCI 0.96 spec USB2 software LPM */
+ unsigned sw_lpm_support:1;
+ /* support xHCI 1.0 spec USB2 hardware LPM */
+ unsigned hw_lpm_support:1;
+ /* cached usb2 extened protocol capabilites */
+ u32 *ext_caps;
+ unsigned int num_ext_caps;
+ /* Compliance Mode Recovery Data */
+ struct timer_list comp_mode_recovery_timer;
+ u32 port_status_u0;
+/* Compliance Mode Timer Triggered every 2 seconds */
+#define COMP_MODE_RCVRY_MSECS 2000
+};
+
+/* 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 xhci->main_hcd;
+}
+
+#define xhci_dbg(xhci, fmt, args...) \
+ dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#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)
+#define xhci_warn_ratelimited(xhci, fmt, args...) \
+ dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#define xhci_info(xhci, fmt, args...) \
+ dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+
+/*
+ * 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.
+ */
+static inline u64 xhci_read_64(const struct xhci_hcd *xhci,
+ __le64 __iomem *regs)
+{
+ __u32 __iomem *ptr = (__u32 __iomem *) regs;
+ u64 val_lo = readl(ptr);
+ u64 val_hi = readl(ptr + 1);
+ return val_lo + (val_hi << 32);
+}
+static inline void xhci_write_64(struct xhci_hcd *xhci,
+ const u64 val, __le64 __iomem *regs)
+{
+ __u32 __iomem *ptr = (__u32 __iomem *) regs;
+ u32 val_lo = lower_32_bits(val);
+ u32 val_hi = upper_32_bits(val);
+
+ writel(val_lo, ptr);
+ writel(val_hi, ptr + 1);
+}
+
+static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
+{
+ return xhci->quirks & XHCI_LINK_TRB_QUIRK;
+}
+
+/* xHCI debugging */
+void xhci_print_ir_set(struct xhci_hcd *xhci, 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_container_ctx *ctx, unsigned int last_ep);
+char *xhci_get_slot_state(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx);
+void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_virt_ep *ep);
+void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
+ const char *fmt, ...);
+
+/* xHCI memory management */
+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);
+void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
+ struct usb_device *udev);
+unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc);
+unsigned int xhci_get_endpoint_address(unsigned int ep_index);
+unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc);
+unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index);
+unsigned int xhci_last_valid_endpoint(u32 added_ctxs);
+void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep);
+void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info);
+void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps);
+void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info);
+void xhci_update_bw_info(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_input_control_ctx *ctrl_ctx,
+ struct xhci_virt_device *virt_dev);
+void xhci_endpoint_copy(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_container_ctx *out_ctx,
+ unsigned int ep_index);
+void xhci_slot_copy(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_container_ctx *out_ctx);
+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);
+int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs, gfp_t flags);
+void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ unsigned int ep_index);
+struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
+ unsigned int num_stream_ctxs,
+ unsigned int num_streams, gfp_t flags);
+void xhci_free_stream_info(struct xhci_hcd *xhci,
+ struct xhci_stream_info *stream_info);
+void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
+ struct xhci_ep_ctx *ep_ctx,
+ struct xhci_stream_info *stream_info);
+void xhci_setup_no_streams_ep_input_ctx(struct xhci_ep_ctx *ep_ctx,
+ struct xhci_virt_ep *ep);
+void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev, bool drop_control_ep);
+struct xhci_ring *xhci_dma_to_transfer_ring(
+ struct xhci_virt_ep *ep,
+ u64 address);
+struct xhci_ring *xhci_stream_id_to_ring(
+ struct xhci_virt_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id);
+struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
+ bool allocate_in_ctx, bool allocate_completion,
+ gfp_t mem_flags);
+void xhci_urb_free_priv(struct urb_priv *urb_priv);
+void xhci_free_command(struct xhci_hcd *xhci,
+ struct xhci_command *command);
+
+/* xHCI host controller glue */
+typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
+int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+void xhci_quiesce(struct xhci_hcd *xhci);
+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_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
+void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *));
+
+#ifdef CONFIG_PM
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
+int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
+#else
+#define xhci_suspend NULL
+#define xhci_resume NULL
+#endif
+
+int xhci_get_frame(struct usb_hcd *hcd);
+irqreturn_t xhci_irq(struct usb_hcd *hcd);
+irqreturn_t xhci_msi_irq(int irq, void *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_alloc_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
+int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ unsigned int num_streams, gfp_t mem_flags);
+int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ gfp_t mem_flags);
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable);
+int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
+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);
+void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev);
+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);
+struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
+ struct xhci_segment *start_seg, union xhci_trb *start_trb,
+ union xhci_trb *end_trb, dma_addr_t suspect_dma, bool debug);
+int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
+void xhci_ring_cmd_db(struct xhci_hcd *xhci);
+int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 trb_type, u32 slot_id);
+int xhci_queue_address_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, enum xhci_setup_dev);
+int xhci_queue_vendor_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 field1, u32 field2, u32 field3, u32 field4);
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index, int suspend);
+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_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
+ int slot_id, unsigned int ep_index);
+int xhci_queue_isoc_tx_prepare(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,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
+ bool command_must_succeed);
+int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
+int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index);
+int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 slot_id);
+void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id, struct xhci_td *cur_td,
+ struct xhci_dequeue_state *state);
+void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id,
+ struct xhci_dequeue_state *deq_state);
+void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
+ unsigned int ep_index, struct xhci_td *td);
+void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_dequeue_state *deq_state);
+void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+void xhci_handle_command_timeout(unsigned long data);
+
+void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
+ unsigned int ep_index, unsigned int stream_id);
+void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
+
+/* xHCI roothub code */
+void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 link_state);
+int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state);
+int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
+ struct usb_device *udev, enum usb3_link_state state);
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 port_bit);
+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);
+int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
+
+#ifdef CONFIG_PM
+int xhci_bus_suspend(struct usb_hcd *hcd);
+int xhci_bus_resume(struct usb_hcd *hcd);
+#else
+#define xhci_bus_suspend NULL
+#define xhci_bus_resume NULL
+#endif /* CONFIG_PM */
+
+u32 xhci_port_state_to_neutral(u32 state);
+int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 port);
+void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
+
+/* xHCI contexts */
+struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx);
+struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
+struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index);
+
+#endif /* __LINUX_XHCI_HCD_H */