summaryrefslogtreecommitdiff
path: root/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch')
-rwxr-xr-xtarget/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch3298
1 files changed, 3298 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch b/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch
new file mode 100755
index 0000000..822f8da
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch
@@ -0,0 +1,3298 @@
+From a29a51d9320d44124fe13457c45663d3051a9452 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Wed, 3 Jul 2013 00:46:42 +0100
+Subject: [PATCH 025/114] Add FIQ patch to dwc_otg driver. Enable with
+ dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks
+ to Gordon and Costas
+
+Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005.
+
+Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh
+
+Make sure we wait for the reset to finish
+
+dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel
+ memory corruption, escalating to OOPS under high USB load.
+
+dwc_otg: Fix unsafe access of QTD during URB enqueue
+
+In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the
+transaction could complete almost immediately after the qtd was assigned
+to a host channel during URB enqueue, which meant the qtd pointer was no
+longer valid having been completed and removed. Usually, this resulted in
+an OOPS during URB submission. By predetermining whether transactions
+need to be queued or not, this unsafe pointer access is avoided.
+
+This bug was only evident on the Pi model A where a device was attached
+that had no periodic endpoints (e.g. USB pendrive or some wlan devices).
+
+dwc_otg: Fix incorrect URB allocation error handling
+
+If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS
+because for some reason a member of the *unallocated* struct was set to
+zero. Error handling changed to fail correctly.
+
+dwc_otg: fix potential use-after-free case in interrupt handler
+
+If a transaction had previously aborted, certain interrupts are
+enabled to track error counts and reset where necessary. On IN
+endpoints the host generates an ACK interrupt near-simultaneously
+with completion of transfer. In the case where this transfer had
+previously had an error, this results in a use-after-free on
+the QTD memory space with a 1-byte length being overwritten to
+0x00.
+
+dwc_otg: add handling of SPLIT transaction data toggle errors
+
+Previously a data toggle error on packets from a USB1.1 device behind
+a TT would result in the Pi locking up as the driver never handled
+the associated interrupt. Patch adds basic retry mechanism and
+interrupt acknowledgement to cater for either a chance toggle error or
+for devices that have a broken initial toggle state (FT8U232/FT232BM).
+
+dwc_otg: implement tasklet for returning URBs to usbcore hcd layer
+
+The dwc_otg driver interrupt handler for transfer completion will spend
+a very long time with interrupts disabled when a URB is completed -
+this is because usb_hcd_giveback_urb is called from within the handler
+which for a USB device driver with complicated processing (e.g. webcam)
+will take an exorbitant amount of time to complete. This results in
+missed completion interrupts for other USB packets which lead to them
+being dropped due to microframe overruns.
+
+This patch splits returning the URB to the usb hcd layer into a
+high-priority tasklet. This will have most benefit for isochronous IN
+transfers but will also have incidental benefit where multiple periodic
+devices are active at once.
+
+dwc_otg: fix NAK holdoff and allow on split transactions only
+
+This corrects a bug where if a single active non-periodic endpoint
+had at least one transaction in its qh, on frnum == MAX_FRNUM the qh
+would get skipped and never get queued again. This would result in
+a silent device until error detection (automatic or otherwise) would
+either reset the device or flush and requeue the URBs.
+
+Additionally the NAK holdoff was enabled for all transactions - this
+would potentially stall a HS endpoint for 1ms if a previous error state
+enabled this interrupt and the next response was a NAK. Fix so that
+only split transactions get held off.
+
+dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler
+
+usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it
+asynchronously in the tasklet was not safe (regression in
+c4564d4a1a0a9b10d4419e48239f5d99e88d2667).
+
+This change unlinks it from the endpoint prior to queueing it for handling in
+the tasklet, and also adds a check to ensure the urb is OK to be unlinked
+before doing so.
+
+NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb
+when a USB device was unplugged/replugged during data transfer. This effect
+was reproduced using automated USB port power control, hundreds of replug
+events were performed during active transfers to confirm that the problem was
+eliminated.
+
+USB fix using a FIQ to implement split transactions
+
+This commit adds a FIQ implementaion that schedules
+the split transactions using a FIQ so we don't get
+held off by the interrupt latency of Linux
+
+dwc_otg: fix device attributes and avoid kernel warnings on boot
+
+dcw_otg: avoid logging function that can cause panics
+
+See: https://github.com/raspberrypi/firmware/issues/21
+Thanks to cleverca22 for fix
+
+dwc_otg: mask correct interrupts after transaction error recovery
+
+The dwc_otg driver will unmask certain interrupts on a transaction
+that previously halted in the error state in order to reset the
+QTD error count. The various fine-grained interrupt handlers do not
+consider that other interrupts besides themselves were unmasked.
+
+By disabling the two other interrupts only ever enabled in DMA mode
+for this purpose, we can avoid unnecessary function calls in the
+IRQ handler. This will also prevent an unneccesary FIQ interrupt
+from being generated if the FIQ is enabled.
+
+dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ
+
+In the case of a transaction to a device that had previously aborted
+due to an error, several interrupts are enabled to reset the error
+count when a device responds. This has the side-effect of making the
+FIQ thrash because the hardware will generate multiple instances of
+a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK
+on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the
+associated interrupts.
+
+Additionally, on non-split transactions make sure that only unmasked
+interrupts are cleared. This caused a hard-to-trigger but serious
+race condition when you had the combination of an endpoint awaiting
+error recovery and a transaction completed on an endpoint - due to
+the sequencing and timing of interrupts generated by the dwc_otg core,
+it was possible to confuse the IRQ handler.
+
+Fix function tracing
+
+dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue
+
+dwc_otg: prevent OOPSes during device disconnects
+
+The dwc_otg_urb_enqueue function is thread-unsafe. In particular the
+access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and
+friends does not occur within a critical section and so if a device
+was unplugged during activity there was a high chance that the
+usbcore hub_thread would try to disable the endpoint with partially-
+formed entries in the URB queue. This would result in BUG() or null
+pointer dereferences.
+
+Fix so that access of urb->hcpriv, enqueuing to the hardware and
+adding to usbcore endpoint URB lists is contained within a single
+critical section.
+
+dwc_otg: prevent BUG() in TT allocation if hub address is > 16
+
+A fixed-size array is used to track TT allocation. This was
+previously set to 16 which caused a crash because
+dwc_otg_hcd_allocate_port would read past the end of the array.
+
+This was hit if a hub was plugged in which enumerated as addr > 16,
+due to previous device resets or unplugs.
+
+Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows
+to a large size if 128 hub addresses are supported. This field is
+for debug only for tracking which frame an allocate happened in.
+
+dwc_otg: make channel halts with unknown state less damaging
+
+If the IRQ received a channel halt interrupt through the FIQ
+with no other bits set, the IRQ would not release the host
+channel and never complete the URB.
+
+Add catchall handling to treat as a transaction error and retry.
+
+dwc_otg: fiq_split: use TTs with more granularity
+
+This fixes certain issues with split transaction scheduling.
+
+- Isochronous multi-packet OUT transactions now hog the TT until
+ they are completed - this prevents hubs aborting transactions
+ if they get a periodic start-split out-of-order
+- Don't perform TT allocation on non-periodic endpoints - this
+ allows simultaneous use of the TT's bulk/control and periodic
+ transaction buffers
+
+This commit will mainly affect USB audio playback.
+
+dwc_otg: fix potential sleep while atomic during urb enqueue
+
+Fixes a regression introduced with eb1b482a. Kmalloc called from
+dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have
+the GPF_ATOMIC flag set. Force this flag when inside the larger
+critical section.
+
+dwc_otg: make fiq_split_enable imply fiq_fix_enable
+
+Failing to set up the FIQ correctly would result in
+"IRQ 32: nobody cared" errors in dmesg.
+
+dwc_otg: prevent crashes on host port disconnects
+
+Fix several issues resulting in crashes or inconsistent state
+if a Model A root port was disconnected.
+
+- Clean up queue heads properly in kill_urbs_in_qh_list by
+ removing the empty QHs from the schedule lists
+- Set the halt status properly to prevent IRQ handlers from
+ using freed memory
+- Add fiq_split related cleanup for saved registers
+- Make microframe scheduling reclaim host channels if
+ active during a disconnect
+- Abort URBs with -ESHUTDOWN status response, informing
+ device drivers so they respond in a more correct fashion
+ and don't try to resubmit URBs
+- Prevent IRQ handlers from attempting to handle channel
+ interrupts if the associated URB was dequeued (and the
+ driver state was cleared)
+
+dwc_otg: prevent leaking URBs during enqueue
+
+A dwc_otg_urb would get leaked if the HCD enqueue function
+failed for any reason. Free the URB at the appropriate points.
+
+dwc_otg: Enable NAK holdoff for control split transactions
+
+Certain low-speed devices take a very long time to complete a
+data or status stage of a control transaction, producing NAK
+responses until they complete internal processing - the USB2.0
+spec limit is up to 500mS. This causes the same type of interrupt
+storm as seen with USB-serial dongles prior to c8edb238.
+
+In certain circumstances, usually while booting, this interrupt
+storm could cause SD card timeouts.
+
+dwc_otg: Fix for occasional lockup on boot when doing a USB reset
+
+dwc_otg: Don't issue traffic to LS devices in FS mode
+
+Issuing low-speed packets when the root port is in full-speed mode
+causes the root port to stop responding. Explicitly fail when
+enqueuing URBs to a LS endpoint on a FS bus.
+
+Fix ARM architecture issue with local_irq_restore()
+
+If local_fiq_enable() is called before a local_irq_restore(flags) where
+the flags variable has the F bit set, the FIQ will be erroneously disabled.
+
+Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR.
+
+Also fix some of the hacks previously implemented for previous dwc_otg
+incarnations.
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/include/asm/irqflags.h | 16 +-
+ arch/arm/kernel/fiqasm.S | 4 +
+ arch/arm/mach-bcm2708/armctrl.c | 19 +-
+ arch/arm/mach-bcm2708/bcm2708.c | 29 +-
+ arch/arm/mach-bcm2708/include/mach/irqs.h | 153 ++---
+ .../usb/host/dwc_common_port/dwc_common_linux.c | 11 +
+ drivers/usb/host/dwc_common_port/dwc_list.h | 14 +-
+ drivers/usb/host/dwc_common_port/dwc_os.h | 2 +
+ drivers/usb/host/dwc_otg/Makefile | 1 +
+ drivers/usb/host/dwc_otg/dwc_otg_attr.c | 14 +-
+ drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +-
+ drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 1 +
+ drivers/usb/host/dwc_otg/dwc_otg_driver.c | 52 +-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 303 +++++++--
+ drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 37 +-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 3 +-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 5 +
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 705 ++++++++++++++++++++-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 159 +++--
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 53 +-
+ drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 ++++
+ drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 ++
+ drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 3 +
+ drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 2 +-
+ 25 files changed, 1544 insertions(+), 251 deletions(-)
+ create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
+ create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 6283d7d..ba9c18e 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -395,6 +395,7 @@ config ARCH_BCM2708
+ select ARM_ERRATA_411920
+ select MACH_BCM2708
+ select VC4
++ select FIQ
+ help
+ This enables support for Broadcom BCM2708 boards.
+
+diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h
+index 3b763d6..5770408 100644
+--- a/arch/arm/include/asm/irqflags.h
++++ b/arch/arm/include/asm/irqflags.h
+@@ -145,12 +145,22 @@ static inline unsigned long arch_local_save_flags(void)
+ }
+
+ /*
+- * restore saved IRQ & FIQ state
++ * restore saved IRQ state
+ */
+ static inline void arch_local_irq_restore(unsigned long flags)
+ {
+- asm volatile(
+- " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore"
++ unsigned long temp = 0;
++ flags &= ~(1 << 6);
++ asm volatile (
++ " mrs %0, cpsr"
++ : "=r" (temp)
++ :
++ : "memory", "cc");
++ /* Preserve FIQ bit */
++ temp &= (1 << 6);
++ flags = flags | temp;
++ asm volatile (
++ " msr cpsr_c, %0 @ local_irq_restore"
+ :
+ : "r" (flags)
+ : "memory", "cc");
+diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S
+index 8dd26e1..eef4847 100644
+--- a/arch/arm/kernel/fiqasm.S
++++ b/arch/arm/kernel/fiqasm.S
+@@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs)
+ mov r0, r0 @ avoid hazard prior to ARMv4
+ ret lr
+ ENDPROC(__get_fiq_regs)
++
++ENTRY(__FIQ_Branch)
++ mov pc, r8
++ENDPROC(__FIQ_Branch)
+diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c
+index ef1c8d5..96fa9b9 100644
+--- a/arch/arm/mach-bcm2708/armctrl.c
++++ b/arch/arm/mach-bcm2708/armctrl.c
+@@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_data *d)
+ 0
+ };
+
+- unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
+- writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3]));
++ if (d->irq >= FIQ_START) {
++ writel(0, __io_address(ARM_IRQ_FAST));
++ } else {
++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
++ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3]));
++ }
+ }
+
+ static void armctrl_unmask_irq(struct irq_data *d)
+@@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct irq_data *d)
+ 0
+ };
+
+- unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
+- writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3]));
++ if (d->irq >= FIQ_START) {
++ unsigned int data =
++ (unsigned int)irq_get_chip_data(d->irq) - FIQ_START;
++ writel(0x80 | data, __io_address(ARM_IRQ_FAST));
++ } else {
++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
++ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3]));
++ }
+ }
+
+ #if defined(CONFIG_PM)
+@@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start,
+ }
+
+ armctrl_pm_register(base, irq_start, resume_sources);
++ init_FIQ(FIQ_START);
+ return 0;
+ }
+diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
+index 7dd89a7f..dc59a6b 100644
+--- a/arch/arm/mach-bcm2708/bcm2708.c
++++ b/arch/arm/mach-bcm2708/bcm2708.c
+@@ -321,12 +321,32 @@ static struct resource bcm2708_usb_resources[] = {
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+- .start = IRQ_USB,
+- .end = IRQ_USB,
++ .start = MPHI_BASE,
++ .end = MPHI_BASE + SZ_4K - 1,
++ .flags = IORESOURCE_MEM,
++ },
++ [2] = {
++ .start = IRQ_HOSTPORT,
++ .end = IRQ_HOSTPORT,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+
++bool fiq_fix_enable = true;
++
++static struct resource bcm2708_usb_resources_no_fiq_fix[] = {
++ [0] = {
++ .start = USB_BASE,
++ .end = USB_BASE + SZ_128K - 1,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_USB,
++ .end = IRQ_USB,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
+ static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
+
+ static struct platform_device bcm2708_usb_device = {
+@@ -681,6 +701,11 @@ void __init bcm2708_init(void)
+ #endif
+ bcm_register_device(&bcm2708_systemtimer_device);
+ bcm_register_device(&bcm2708_fb_device);
++ if (!fiq_fix_enable)
++ {
++ bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix;
++ bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix);
++ }
+ bcm_register_device(&bcm2708_usb_device);
+ bcm_register_device(&bcm2708_uart1_device);
+ bcm_register_device(&bcm2708_powerman_device);
+diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h
+index 3a88a1a..45152ed 100644
+--- a/arch/arm/mach-bcm2708/include/mach/irqs.h
++++ b/arch/arm/mach-bcm2708/include/mach/irqs.h
+@@ -106,87 +106,90 @@
+ #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1)
+ #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2)
+
++#define FIQ_START HARD_IRQS
++
+ /*
+ * FIQ interrupts definitions are the same as the INT definitions.
+ */
+-#define FIQ_TIMER0 INT_TIMER0
+-#define FIQ_TIMER1 INT_TIMER1
+-#define FIQ_TIMER2 INT_TIMER2
+-#define FIQ_TIMER3 INT_TIMER3
+-#define FIQ_CODEC0 INT_CODEC0
+-#define FIQ_CODEC1 INT_CODEC1
+-#define FIQ_CODEC2 INT_CODEC2
+-#define FIQ_JPEG INT_JPEG
+-#define FIQ_ISP INT_ISP
+-#define FIQ_USB INT_USB
+-#define FIQ_3D INT_3D
+-#define FIQ_TRANSPOSER INT_TRANSPOSER
+-#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0
+-#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1
+-#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2
+-#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3
+-#define FIQ_DMA0 INT_DMA0
+-#define FIQ_DMA1 INT_DMA1
+-#define FIQ_DMA2 INT_DMA2
+-#define FIQ_DMA3 INT_DMA3
+-#define FIQ_DMA4 INT_DMA4
+-#define FIQ_DMA5 INT_DMA5
+-#define FIQ_DMA6 INT_DMA6
+-#define FIQ_DMA7 INT_DMA7
+-#define FIQ_DMA8 INT_DMA8
+-#define FIQ_DMA9 INT_DMA9
+-#define FIQ_DMA10 INT_DMA10
+-#define FIQ_DMA11 INT_DMA11
+-#define FIQ_DMA12 INT_DMA12
+-#define FIQ_AUX INT_AUX
+-#define FIQ_ARM INT_ARM
+-#define FIQ_VPUDMA INT_VPUDMA
+-#define FIQ_HOSTPORT INT_HOSTPORT
+-#define FIQ_VIDEOSCALER INT_VIDEOSCALER
+-#define FIQ_CCP2TX INT_CCP2TX
+-#define FIQ_SDC INT_SDC
+-#define FIQ_DSI0 INT_DSI0
+-#define FIQ_AVE INT_AVE
+-#define FIQ_CAM0 INT_CAM0
+-#define FIQ_CAM1 INT_CAM1
+-#define FIQ_HDMI0 INT_HDMI0
+-#define FIQ_HDMI1 INT_HDMI1
+-#define FIQ_PIXELVALVE1 INT_PIXELVALVE1
+-#define FIQ_I2CSPISLV INT_I2CSPISLV
+-#define FIQ_DSI1 INT_DSI1
+-#define FIQ_PWA0 INT_PWA0
+-#define FIQ_PWA1 INT_PWA1
+-#define FIQ_CPR INT_CPR
+-#define FIQ_SMI INT_SMI
+-#define FIQ_GPIO0 INT_GPIO0
+-#define FIQ_GPIO1 INT_GPIO1
+-#define FIQ_GPIO2 INT_GPIO2
+-#define FIQ_GPIO3 INT_GPIO3
+-#define FIQ_I2C INT_I2C
+-#define FIQ_SPI INT_SPI
+-#define FIQ_I2SPCM INT_I2SPCM
+-#define FIQ_SDIO INT_SDIO
+-#define FIQ_UART INT_UART
+-#define FIQ_SLIMBUS INT_SLIMBUS
+-#define FIQ_VEC INT_VEC
+-#define FIQ_CPG INT_CPG
+-#define FIQ_RNG INT_RNG
+-#define FIQ_ARASANSDIO INT_ARASANSDIO
+-#define FIQ_AVSPMON INT_AVSPMON
++#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0)
++#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1)
++#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2)
++#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3)
++#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0)
++#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1)
++#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2)
++#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG)
++#define FIQ_ISP (FIQ_START+INTERRUPT_ISP)
++#define FIQ_USB (FIQ_START+INTERRUPT_USB)
++#define FIQ_3D (FIQ_START+INTERRUPT_3D)
++#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER)
++#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0)
++#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1)
++#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2)
++#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3)
++#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0)
++#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1)
++#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2)
++#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3)
++#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4)
++#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5)
++#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6)
++#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7)
++#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8)
++#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9)
++#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10)
++#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11)
++#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12)
++#define FIQ_AUX (FIQ_START+INTERRUPT_AUX)
++#define FIQ_ARM (FIQ_START+INTERRUPT_ARM)
++#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA)
++#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT)
++#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER)
++#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX)
++#define FIQ_SDC (FIQ_START+INTERRUPT_SDC)
++#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0)
++#define FIQ_AVE (FIQ_START+INTERRUPT_AVE)
++#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0)
++#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1)
++#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0)
++#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1)
++#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1)
++#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV)
++#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1)
++#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0)
++#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1)
++#define FIQ_CPR (FIQ_START+INTERRUPT_CPR)
++#define FIQ_SMI (FIQ_START+INTERRUPT_SMI)
++#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0)
++#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1)
++#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2)
++#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3)
++#define FIQ_I2C (FIQ_START+INTERRUPT_I2C)
++#define FIQ_SPI (FIQ_START+INTERRUPT_SPI)
++#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM)
++#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO)
++#define FIQ_UART (FIQ_START+INTERRUPT_UART)
++#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS)
++#define FIQ_VEC (FIQ_START+INTERRUPT_VEC)
++#define FIQ_CPG (FIQ_START+INTERRUPT_CPG)
++#define FIQ_RNG (FIQ_START+INTERRUPT_RNG)
++#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO)
++#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON)
+
+-#define FIQ_ARM_TIMER INT_ARM_TIMER
+-#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX
+-#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0
+-#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1
+-#define FIQ_VPU0_HALTED INT_VPU0_HALTED
+-#define FIQ_VPU1_HALTED INT_VPU1_HALTED
+-#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0
+-#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1
+-#define FIQ_PENDING1 INT_PENDING1
+-#define FIQ_PENDING2 INT_PENDING2
++#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER)
++#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX)
++#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0)
++#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1)
++#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED)
++#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED)
++#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0)
++#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1)
++#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1)
++#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2)
+
+ #define HARD_IRQS (64 + 21)
+-#define GPIO_IRQ_START (HARD_IRQS)
++#define FIQ_IRQS (64 + 21)
++#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS)
+ #define GPIO_IRQS (32*5)
+ #define SPARE_ALLOC_IRQS 64
+ #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS)
+diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
+index 1668f10..5c50a8b 100644
+--- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c
++++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
+@@ -580,7 +580,13 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
+
+ void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
+ {
++ unsigned long flags;
++
++ local_irq_save(flags);
++ local_fiq_disable();
+ writel((readl(reg) & ~clear_mask) | set_mask, reg);
++ local_fiq_enable();
++ local_irq_restore(flags);
+ }
+
+ #if 0
+@@ -995,6 +1001,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+ tasklet_schedule(&task->t);
+ }
+
++void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
++{
++ tasklet_hi_schedule(&task->t);
++}
++
+
+ /* workqueues
+ - run in process context (can sleep)
+diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h
+index 89cc325..4ce560d 100644
+--- a/drivers/usb/host/dwc_common_port/dwc_list.h
++++ b/drivers/usb/host/dwc_common_port/dwc_list.h
+@@ -384,17 +384,17 @@ struct { \
+ #define DWC_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+ #define DWC_TAILQ_EMPTY(head) \
+- (TAILQ_FIRST(head) == TAILQ_END(head))
++ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
+
+ #define DWC_TAILQ_FOREACH(var, head, field) \
+- for((var) = TAILQ_FIRST(head); \
+- (var) != TAILQ_END(head); \
+- (var) = TAILQ_NEXT(var, field))
++ for ((var) = DWC_TAILQ_FIRST(head); \
++ (var) != DWC_TAILQ_END(head); \
++ (var) = DWC_TAILQ_NEXT(var, field))
+
+ #define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+- for((var) = TAILQ_LAST(head, headname); \
+- (var) != TAILQ_END(head); \
+- (var) = TAILQ_PREV(var, headname, field))
++ for ((var) = DWC_TAILQ_LAST(head, headname); \
++ (var) != DWC_TAILQ_END(head); \
++ (var) = DWC_TAILQ_PREV(var, headname, field))
+
+ /*
+ * Tail queue functions.
+diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h
+index 8117731..a2bbe23 100644
+--- a/drivers/usb/host/dwc_common_port/dwc_os.h
++++ b/drivers/usb/host/dwc_common_port/dwc_os.h
+@@ -982,6 +982,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task);
+ extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
+ #define dwc_task_schedule DWC_TASK_SCHEDULE
+
++extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
++#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
+
+ /** @name Timer
+ *
+diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile
+index 236c47c..a56f193 100644
+--- a/drivers/usb/host/dwc_otg/Makefile
++++ b/drivers/usb/host/dwc_otg/Makefile
+@@ -36,6 +36,7 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o
+ dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o
+ dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o
+ dwc_otg-objs += dwc_otg_adp.o
++dwc_otg-objs += dwc_otg_mphi_fix.o
+ ifneq ($(CFI),)
+ dwc_otg-objs += dwc_otg_cfi.o
+ endif
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_attr.c b/drivers/usb/host/dwc_otg/dwc_otg_attr.c
+index fab2961..9da0c92 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_attr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c
+@@ -909,7 +909,7 @@ static ssize_t regdump_show(struct device *_dev,
+ return sprintf(buf, "Register Dump\n");
+ }
+
+-DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0);
++DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0);
+
+ /**
+ * Dump global registers and either host or device registers (depending on the
+@@ -920,12 +920,12 @@ static ssize_t spramdump_show(struct device *_dev,
+ {
+ dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+- dwc_otg_dump_spram(otg_dev->core_if);
++ //dwc_otg_dump_spram(otg_dev->core_if);
+
+ return sprintf(buf, "SPRAM Dump\n");
+ }
+
+-DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0);
++DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0);
+
+ /**
+ * Dump the current hcd state.
+@@ -940,7 +940,7 @@ static ssize_t hcddump_show(struct device *_dev,
+ return sprintf(buf, "HCD Dump\n");
+ }
+
+-DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0);
++DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0);
+
+ /**
+ * Dump the average frame remaining at SOF. This can be used to
+@@ -958,7 +958,7 @@ static ssize_t hcd_frrem_show(struct device *_dev,
+ return sprintf(buf, "HCD Dump Frame Remaining\n");
+ }
+
+-DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0);
++DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0);
+
+ /**
+ * Displays the time required to read the GNPTXFSIZ register many times (the
+@@ -986,7 +986,7 @@ static ssize_t rd_reg_test_show(struct device *_dev,
+ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+ }
+
+-DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0);
++DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0);
+
+ /**
+ * Displays the time required to write the GNPTXFSIZ register many times (the
+@@ -1014,7 +1014,7 @@ static ssize_t wr_reg_test_show(struct device *_dev,
+ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+ }
+
+-DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0);
++DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0);
+
+ #ifdef CONFIG_USB_DWC_OTG_LPM
+
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
+index 59fc862..2f8b3bd 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
+@@ -45,6 +45,7 @@
+ #include "dwc_otg_driver.h"
+ #include "dwc_otg_pcd.h"
+ #include "dwc_otg_hcd.h"
++#include "dwc_otg_mphi_fix.h"
+
+ #ifdef DEBUG
+ inline const char *op_state_str(dwc_otg_core_if_t * core_if)
+@@ -1318,7 +1319,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
+ /**
+ * This function returns the Core Interrupt register.
+ */
+-static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk)
+ {
+ gahbcfg_data_t gahbcfg = {.d32 = 0 };
+ gintsts_data_t gintsts;
+@@ -1335,26 +1336,45 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
+ gintmsk_common.b.lpmtranrcvd = 1;
+ #endif
+ gintmsk_common.b.restoredone = 1;
+- /** @todo: The port interrupt occurs while in device
+- * mode. Added code to CIL to clear the interrupt for now!
+- */
+- gintmsk_common.b.portintr = 1;
+-
++ if(dwc_otg_is_device_mode(core_if))
++ {
++ /** @todo: The port interrupt occurs while in device
++ * mode. Added code to CIL to clear the interrupt for now!
++ */
++ gintmsk_common.b.portintr = 1;
++ }
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
++ {
++ unsigned long flags;
++
++ // Re-enable the saved interrupts
++ local_irq_save(flags);
++ local_fiq_disable();
++ gintmsk.d32 |= gintmsk_common.d32;
++ gintsts_saved.d32 &= ~gintmsk_common.d32;
++ reenable_gintmsk->d32 = gintmsk.d32;
++ local_irq_restore(flags);
++ }
++
+ gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
+
+ #ifdef DEBUG
+ /* if any common interrupts set */
+ if (gintsts.d32 & gintmsk_common.d32) {
+- DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
++ DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n",
+ gintsts.d32, gintmsk.d32);
+ }
+ #endif
+- if (gahbcfg.b.glblintrmsk)
++ if (!fiq_fix_enable){
++ if (gahbcfg.b.glblintrmsk)
++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
++ else
++ return 0;
++ }
++ else {
+ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
+- else
+- return 0;
++ }
+
+ }
+
+@@ -1386,6 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev)
+ {
+ int retval = 0;
+ gintsts_data_t gintsts;
++ gintmsk_data_t reenable_gintmsk;
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ dwc_otg_device_t *otg_dev = dev;
+ dwc_otg_core_if_t *core_if = otg_dev->core_if;
+@@ -1407,7 +1428,7 @@ int32_t dwc_otg_handle_common_intr(void *dev)
+ }
+
+ if (core_if->hibernation_suspend <= 0) {
+- gintsts.d32 = dwc_otg_read_common_intr(core_if);
++ gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk);
+
+ if (gintsts.b.modemismatch) {
+ retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+@@ -1504,8 +1525,12 @@ int32_t dwc_otg_handle_common_intr(void *dev)
+ gintsts.b.portintr = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
+ retval |= 1;
++ reenable_gintmsk.b.portintr = 1;
+
+ }
++
++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32);
++
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
+
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
+index 8900318..ccc24e0 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
++++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
+@@ -49,6 +49,7 @@ static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new)
+ return old;
+ }
+
++#define DBG_USER (0x1)
+ /** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
+ #define DBG_CIL (0x2)
+ /** When debug level has the DBG_CILV bit set, display CIL Verbose debug
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
+index ac2c846..f06c3d22 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
+@@ -64,6 +64,8 @@ bool microframe_schedule=true;
+
+ static const char dwc_driver_name[] = "dwc_otg";
+
++extern void* dummy_send;
++
+ extern int pcd_init(
+ #ifdef LM_INTERFACE
+ struct lm_device *_dev
+@@ -238,6 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = {
+ .adp_enable = -1,
+ };
+
++//Global variable to switch the fiq fix on or off (declared in bcm2708.c)
++extern bool fiq_fix_enable;
++// Global variable to enable the split transaction fix
++bool fiq_split_enable = true;
++//Global variable to switch the nak holdoff on or off
++bool nak_holdoff_enable = true;
++
++
+ /**
+ * This function shows the Driver Version.
+ */
+@@ -779,17 +789,33 @@ static int dwc_otg_driver_probe(
+ _dev->resource->start,
+ _dev->resource->end - _dev->resource->start + 1);
+ #if 1
+- if (!request_mem_region(_dev->resource->start,
+- _dev->resource->end - _dev->resource->start + 1,
++ if (!request_mem_region(_dev->resource[0].start,
++ _dev->resource[0].end - _dev->resource[0].start + 1,
+ "dwc_otg")) {
+ dev_dbg(&_dev->dev, "error reserving mapped memory\n");
+ retval = -EFAULT;
+ goto fail;
+ }
+
+- dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start,
+- _dev->resource->end -
+- _dev->resource->start+1);
++ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start,
++ _dev->resource[0].end -
++ _dev->resource[0].start+1);
++ if (fiq_fix_enable)
++ {
++ if (!request_mem_region(_dev->resource[1].start,
++ _dev->resource[1].end - _dev->resource[1].start + 1,
++ "dwc_otg")) {
++ dev_dbg(&_dev->dev, "error reserving mapped memory\n");
++ retval = -EFAULT;
++ goto fail;
++ }
++
++ dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start,
++ _dev->resource[1].end -
++ _dev->resource[1].start + 1);
++ dummy_send = (void *) kmalloc(16, GFP_ATOMIC);
++ }
++
+ #else
+ {
+ struct map_desc desc = {
+@@ -1044,6 +1070,12 @@ static int __init dwc_otg_driver_init(void)
+ int retval = 0;
+ int error;
+ struct device_driver *drv;
++
++ if(fiq_split_enable && !fiq_fix_enable) {
++ printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n");
++ fiq_fix_enable = 1;
++ }
++
+ printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name,
+ DWC_DRIVER_VERSION,
+ #ifdef LM_INTERFACE
+@@ -1063,6 +1095,9 @@ static int __init dwc_otg_driver_init(void)
+ printk(KERN_ERR "%s retval=%d\n", __func__, retval);
+ return retval;
+ }
++ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled");
++ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled");
++ printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled");
+
+ error = driver_create_file(drv, &driver_attr_version);
+ #ifdef DEBUG
+@@ -1343,6 +1378,13 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0");
+ module_param(microframe_schedule, bool, 0444);
+ MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
+
++module_param(fiq_fix_enable, bool, 0444);
++MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix");
++module_param(nak_holdoff_enable, bool, 0444);
++MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff");
++module_param(fiq_split_enable, bool, 0444);
++MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions");
++
+ /** @page "Module Parameters"
+ *
+ * The following parameters may be specified when starting the module.
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+index 1e89549..986d361 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+@@ -40,10 +40,14 @@
+ * header file.
+ */
+
++#include <linux/usb.h>
++#include <linux/usb/hcd.h>
++
+ #include "dwc_otg_hcd.h"
+ #include "dwc_otg_regs.h"
++#include "dwc_otg_mphi_fix.h"
+
+-extern bool microframe_schedule;
++extern bool microframe_schedule, nak_holdoff_enable;
+
+ //#define DEBUG_HOST_CHANNELS
+ #ifdef DEBUG_HOST_CHANNELS
+@@ -53,6 +57,13 @@ static int last_sel_trans_num_avail_hc_at_start = 0;
+ static int last_sel_trans_num_avail_hc_at_end = 0;
+ #endif /* DEBUG_HOST_CHANNELS */
+
++extern int g_next_sched_frame, g_np_count, g_np_sent;
++
++extern haint_data_t haint_saved;
++extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
++extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
++extern gintsts_data_t ginsts_saved;
++
+ dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
+ {
+ return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
+@@ -162,31 +173,43 @@ static void del_timers(dwc_otg_hcd_t * hcd)
+
+ /**
+ * Processes all the URBs in a single list of QHs. Completes them with
+- * -ETIMEDOUT and frees the QTD.
++ * -ESHUTDOWN and frees the QTD.
+ */
+ static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+ {
+- dwc_list_link_t *qh_item;
++ dwc_list_link_t *qh_item, *qh_tmp;
+ dwc_otg_qh_t *qh;
+ dwc_otg_qtd_t *qtd, *qtd_tmp;
+
+- DWC_LIST_FOREACH(qh_item, qh_list) {
++ DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
+ qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
+ &qh->qtd_list, qtd_list_entry) {
+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+ if (qtd->urb != NULL) {
+ hcd->fops->complete(hcd, qtd->urb->priv,
+- qtd->urb, -DWC_E_TIMEOUT);
++ qtd->urb, -DWC_E_SHUTDOWN);
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+ }
+
+ }
++ if(qh->channel) {
++ /* Using hcchar.chen == 1 is not a reliable test.
++ * It is possible that the channel has already halted
++ * but not yet been through the IRQ handler.
++ */
++ dwc_otg_hc_halt(hcd->core_if, qh->channel,
++ DWC_OTG_HC_XFER_URB_DEQUEUE);
++ if(microframe_schedule)
++ hcd->available_host_channels++;
++ qh->channel = NULL;
++ }
++ dwc_otg_hcd_qh_remove(hcd, qh);
+ }
+ }
+
+ /**
+- * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
++ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+@@ -272,7 +295,8 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
+ */
+ dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+ dwc_otg_hcd->flags.b.port_connect_status = 0;
+-
++ if(fiq_fix_enable)
++ local_fiq_disable();
+ /*
+ * Shutdown any transfers in process by clearing the Tx FIFO Empty
+ * interrupt mask and status bits and disabling subsequent host
+@@ -368,8 +392,22 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
+ channel->qh = NULL;
+ }
+ }
++ if(fiq_split_enable) {
++ for(i=0; i < 128; i++) {
++ dwc_otg_hcd->hub_port[i] = 0;
++ }
++ haint_saved.d32 = 0;
++ for(i=0; i < MAX_EPS_CHANNELS; i++) {
++ hcint_saved[i].d32 = 0;
++ hcintmsk_saved[i].d32 = 0;
++ }
++ }
++
+ }
+
++ if(fiq_fix_enable)
++ local_fiq_enable();
++
+ if (dwc_otg_hcd->fops->disconnect) {
+ dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
+ }
+@@ -407,6 +445,7 @@ static int dwc_otg_hcd_sleep_cb(void *p)
+ }
+ #endif
+
++
+ /**
+ * HCD Callback function for Remote Wakeup.
+ *
+@@ -457,10 +496,12 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
+ dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
+ int atomic_alloc)
+ {
+- dwc_irqflags_t flags;
+ int retval = 0;
++ uint8_t needs_scheduling = 0;
++ dwc_otg_transaction_type_e tr_type;
+ dwc_otg_qtd_t *qtd;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
++ hprt0_data_t hprt0 = { .d32 = 0 };
+
+ #ifdef DEBUG /* integrity checks (Broadcom) */
+ if (NULL == hcd->core_if) {
+@@ -475,6 +516,16 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
+ return -DWC_E_NO_DEVICE;
+ }
+
++ /* Some core configurations cannot support LS traffic on a FS root port */
++ if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) &&
++ (hcd->core_if->hwcfg2.b.fs_phy_type == 1) &&
++ (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) {
++ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) {
++ return -DWC_E_NO_DEVICE;
++ }
++ }
++
+ qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc);
+ if (qtd == NULL) {
+ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
+@@ -490,32 +541,27 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
+ return -DWC_E_NO_MEMORY;
+ }
+ #endif
+- retval =
+- dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
++ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
++ if(!intr_mask.b.sofintr) needs_scheduling = 1;
++ if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP))
++ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
++ needs_scheduling = 0;
++
++ retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
+ // creates a new queue in ep_handle if it doesn't exist already
+ if (retval < 0) {
+ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
+ "Error status %d\n", retval);
+ dwc_otg_hcd_qtd_free(qtd);
+- } else {
+- qtd->qh = *ep_handle;
++ return retval;
+ }
+- intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
+- if (!intr_mask.b.sofintr && retval == 0) {
+- dwc_otg_transaction_type_e tr_type;
+- if ((qtd->qh->ep_type == UE_BULK)
+- && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) {
+- /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
+- return 0;
+- }
+- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
++
++ if(needs_scheduling) {
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ }
+-
+ return retval;
+ }
+
+@@ -524,6 +570,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
+ {
+ dwc_otg_qh_t *qh;
+ dwc_otg_qtd_t *urb_qtd;
++ BUG_ON(!hcd);
++ BUG_ON(!dwc_otg_urb);
+
+ #ifdef DEBUG /* integrity checks (Broadcom) */
+
+@@ -540,14 +588,17 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
+ return -DWC_E_INVALID;
+ }
+ urb_qtd = dwc_otg_urb->qtd;
++ BUG_ON(!urb_qtd);
+ if (urb_qtd->qh == NULL) {
+ DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n");
+ return -DWC_E_INVALID;
+ }
+ #else
+ urb_qtd = dwc_otg_urb->qtd;
++ BUG_ON(!urb_qtd);
+ #endif
+ qh = urb_qtd->qh;
++ BUG_ON(!qh);
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ if (urb_qtd->in_process) {
+ dump_channel_info(hcd, qh);
+@@ -571,6 +622,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
+ */
+ dwc_otg_hc_halt(hcd->core_if, qh->channel,
+ DWC_OTG_HC_XFER_URB_DEQUEUE);
++
++ dwc_otg_hcd_release_port(hcd, qh);
+ }
+ }
+
+@@ -687,6 +740,33 @@ static void reset_tasklet_func(void *data)
+ dwc_otg_hcd->flags.b.port_reset_change = 1;
+ }
+
++static void completion_tasklet_func(void *ptr)
++{
++ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
++ struct urb *urb;
++ urb_tq_entry_t *item;
++ dwc_irqflags_t flags;
++
++ /* This could just be spin_lock_irq */
++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
++ while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
++ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
++ urb = item->urb;
++ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
++ urb_tq_entries);
++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
++ DWC_FREE(item);
++
++ usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
++
++ fiq_print(FIQDBG_PORTHUB, "COMPLETE");
++
++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
++ }
++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
++ return;
++}
++
+ static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+ {
+ dwc_list_link_t *item;
+@@ -819,12 +899,14 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
+ } else if (dwc_otg_hcd->status_buf != NULL) {
+ DWC_FREE(dwc_otg_hcd->status_buf);
+ }
++ DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock);
+ DWC_SPINLOCK_FREE(dwc_otg_hcd->lock);
+ /* Set core_if's lock pointer to NULL */
+ dwc_otg_hcd->core_if->lock = NULL;
+
+ DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
+ DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
++ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
+
+ #ifdef DWC_DEV_SRPCAP
+ if (dwc_otg_hcd->core_if->power_down == 2 &&
+@@ -874,7 +956,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
+ DWC_LIST_INIT(&hcd->periodic_sched_ready);
+ DWC_LIST_INIT(&hcd->periodic_sched_assigned);
+ DWC_LIST_INIT(&hcd->periodic_sched_queued);
+-
++ DWC_TAILQ_INIT(&hcd->completed_urb_list);
+ /*
+ * Create a host channel descriptor for each host channel implemented
+ * in the controller. Initialize the channel descriptor array.
+@@ -912,6 +994,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
+
+ /* Initialize reset tasklet. */
+ hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
++
++ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
++ completion_tasklet_func, hcd);
+ #ifdef DWC_DEV_SRPCAP
+ if (hcd->core_if->power_down == 2) {
+ /* Initialize Power on timer for Host power up in case hibernation */
+@@ -944,6 +1029,12 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
+ hcd->frame_list = NULL;
+ hcd->frame_list_dma = 0;
+ hcd->periodic_qh_count = 0;
++
++ DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port));
++#ifdef FIQ_DEBUG
++ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc));
++#endif
++
+ out:
+ return retval;
+ }
+@@ -1089,7 +1180,12 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ uint32_t hub_addr, port_addr;
+ hc->do_split = 1;
+ hc->xact_pos = qtd->isoc_split_pos;
+- hc->complete_split = qtd->complete_split;
++ /* We don't need to do complete splits anymore */
++ if(fiq_split_enable)
++ hc->complete_split = qtd->complete_split = 0;
++ else
++ hc->complete_split = qtd->complete_split;
++
+ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr);
+ hc->hub_addr = (uint8_t) hub_addr;
+ hc->port_addr = (uint8_t) port_addr;
+@@ -1236,6 +1332,65 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ hc->qh = qh;
+ }
+
++/*
++** Check the transaction to see if the port / hub has already been assigned for
++** a split transaction
++**
++** Return 0 - Port is already in use
++*/
++int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
++{
++ uint32_t hub_addr, port_addr;
++
++ if(!fiq_split_enable)
++ return 0;
++
++ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
++
++ if(hcd->hub_port[hub_addr] & (1 << port_addr))
++ {
++ fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count);
++
++ qh->skip_count++;
++
++ if(qh->skip_count > 40000)
++ {
++ printk_once(KERN_ERR "Error: Having to skip port allocation");
++ local_fiq_disable();
++ BUG();
++ return 0;
++ }
++ return 1;
++ }
++ else
++ {
++ qh->skip_count = 0;
++ hcd->hub_port[hub_addr] |= 1 << port_addr;
++ fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
++#ifdef FIQ_DEBUG
++ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd);
++#endif
++ return 0;
++ }
++}
++void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
++{
++ uint32_t hub_addr, port_addr;
++
++ if(!fiq_split_enable)
++ return;
++
++ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
++
++ hcd->hub_port[hub_addr] &= ~(1 << port_addr);
++#ifdef FIQ_DEBUG
++ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1;
++#endif
++ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
++
++}
++
++
+ /**
+ * This function selects transactions from the HCD transfer schedule and
+ * assigns them to available host channels. It is called from HCD interrupt
+@@ -1249,9 +1404,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+ {
+ dwc_list_link_t *qh_ptr;
+ dwc_otg_qh_t *qh;
++ dwc_otg_qtd_t *qtd;
+ int num_channels;
+ dwc_irqflags_t flags;
+- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
++ dwc_spinlock_t *channel_lock = hcd->channel_lock;
+ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
+
+ #ifdef DEBUG_SOF
+@@ -1269,11 +1425,29 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+
+ while (qh_ptr != &hcd->periodic_sched_ready &&
+ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
++
++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
++
++ if(qh->do_split) {
++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
++ if(!(qh->ep_type == UE_ISOCHRONOUS &&
++ (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
++ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) {
++ if(dwc_otg_hcd_allocate_port(hcd, qh))
++ {
++ qh_ptr = DWC_LIST_NEXT(qh_ptr);
++ g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1);
++ continue;
++ }
++ }
++ }
++
+ if (microframe_schedule) {
+ // Make sure we leave one channel for non periodic transactions.
+ DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
+ if (hcd->available_host_channels <= 1) {
+ DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
++ if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh);
+ break;
+ }
+ hcd->available_host_channels--;
+@@ -1294,8 +1468,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+ &qh->qh_list_entry);
+ DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
+-
+- ret_val = DWC_OTG_TRANSACTION_PERIODIC;
+ }
+
+ /*
+@@ -1310,6 +1482,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+ num_channels - hcd->periodic_channels) &&
+ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+
++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
++
++ /*
++ * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission
++ * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed
++ * cheeky devices that just hold off using NAKs
++ */
++ if (nak_holdoff_enable && qh->do_split) {
++ if (qh->nak_frame != 0xffff &&
++ dwc_full_frame_num(qh->nak_frame) ==
++ dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) {
++ /*
++ * Revisit: Need to avoid trampling on periodic scheduling.
++ * Currently we are safe because g_np_count != g_np_sent whenever we hit this,
++ * but if this behaviour is changed then periodic endpoints will get a slower
++ * polling rate.
++ */
++ g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM;
++ qh_ptr = DWC_LIST_NEXT(qh_ptr);
++ continue;
++ } else {
++ qh->nak_frame = 0xffff;
++ }
++ }
++
+ if (microframe_schedule) {
+ DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
+ if (hcd->available_host_channels < 1) {
+@@ -1322,7 +1519,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+ last_sel_trans_num_nonper_scheduled++;
+ #endif /* DEBUG_HOST_CHANNELS */
+ }
+- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
+ assign_and_init_hc(hcd, qh);
+
+@@ -1336,21 +1532,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+ &qh->qh_list_entry);
+ DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
+
+- if (ret_val == DWC_OTG_TRANSACTION_NONE) {
+- ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
+- } else {
+- ret_val = DWC_OTG_TRANSACTION_ALL;
+- }
++ g_np_sent++;
+
+ if (!microframe_schedule)
+ hcd->non_periodic_channels++;
+ }
+
++ if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned))
++ ret_val |= DWC_OTG_TRANSACTION_PERIODIC;
++
++ if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active))
++ ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC;
++
++
+ #ifdef DEBUG_HOST_CHANNELS
+ last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels;
+ #endif /* DEBUG_HOST_CHANNELS */
+-
+- DWC_SPINLOCK_FREE(channel_lock);
+ return ret_val;
+ }
+
+@@ -1464,6 +1661,15 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd)
+
+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
++ // Do not send a split start transaction any later than frame .6
++ // Note, we have to schedule a periodic in .5 to make it go in .6
++ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
++ {
++ qh_ptr = qh_ptr->next;
++ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
++ continue;
++ }
++
+ /*
+ * Set a flag if we're queuing high-bandwidth in slave mode.
+ * The flag prevents any halts to get into the request queue in
+@@ -1593,6 +1799,15 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd)
+
+ qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
+ qh_list_entry);
++
++ // Do not send a split start transaction any later than frame .5
++ // non periodic transactions will start immediately in this uframe
++ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
++ {
++ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
++ break;
++ }
++
+ status =
+ queue_transaction(hcd, qh->channel,
+ tx_status.b.nptxfspcavail);
+@@ -3118,17 +3333,13 @@ dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
+ else
+ dwc_otg_urb = DWC_ALLOC(size);
+
+- if (NULL != dwc_otg_urb)
+- dwc_otg_urb->packet_count = iso_desc_count;
++ if (dwc_otg_urb)
++ dwc_otg_urb->packet_count = iso_desc_count;
+ else {
+- dwc_otg_urb->packet_count = 0;
+- if (size != 0) {
+- DWC_ERROR("**** DWC OTG HCD URB alloc - "
+- "%salloc of %db failed\n",
+- atomic_alloc?"atomic ":"", size);
+- }
+- }
+-
++ DWC_ERROR("**** DWC OTG HCD URB alloc - "
++ "%salloc of %db failed\n",
++ atomic_alloc?"atomic ":"", size);
++ }
+ return dwc_otg_urb;
+ }
+
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
+index bb4f67a..0007fa1 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
+@@ -168,10 +168,10 @@ typedef enum dwc_otg_control_phase {
+
+ /** Transaction types. */
+ typedef enum dwc_otg_transaction_type {
+- DWC_OTG_TRANSACTION_NONE,
+- DWC_OTG_TRANSACTION_PERIODIC,
+- DWC_OTG_TRANSACTION_NON_PERIODIC,
+- DWC_OTG_TRANSACTION_ALL
++ DWC_OTG_TRANSACTION_NONE = 0,
++ DWC_OTG_TRANSACTION_PERIODIC = 1,
++ DWC_OTG_TRANSACTION_NON_PERIODIC = 2,
++ DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC
+ } dwc_otg_transaction_type_e;
+
+ struct dwc_otg_qh;
+@@ -321,6 +321,11 @@ typedef struct dwc_otg_qh {
+ */
+ uint16_t sched_frame;
+
++ /*
++ ** Frame a NAK was received on this queue head, used to minimise NAK retransmission
++ */
++ uint16_t nak_frame;
++
+ /** (micro)frame at which last start split was initialized. */
+ uint16_t start_split_frame;
+
+@@ -365,10 +370,19 @@ typedef struct dwc_otg_qh {
+
+ uint16_t speed;
+ uint16_t frame_usecs[8];
++
++ uint32_t skip_count;
+ } dwc_otg_qh_t;
+
+ DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
+
++typedef struct urb_tq_entry {
++ struct urb *urb;
++ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
++} urb_tq_entry_t;
++
++DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
++
+ /**
+ * This structure holds the state of the HCD, including the non-periodic and
+ * periodic schedules.
+@@ -546,9 +560,12 @@ struct dwc_otg_hcd {
+ /* Tasket to do a reset */
+ dwc_tasklet_t *reset_tasklet;
+
++ dwc_tasklet_t *completion_tasklet;
++ struct urb_list completed_urb_list;
++
+ /* */
+ dwc_spinlock_t *lock;
+-
++ dwc_spinlock_t *channel_lock;
+ /**
+ * Private data that could be used by OS wrapper.
+ */
+@@ -559,6 +576,12 @@ struct dwc_otg_hcd {
+ /** Frame List */
+ uint32_t *frame_list;
+
++ /** Hub - Port assignment */
++ int hub_port[128];
++#ifdef FIQ_DEBUG
++ int hub_port_alloc[2048];
++#endif
++
+ /** Frame List DMA address */
+ dma_addr_t frame_list_dma;
+
+@@ -589,6 +612,10 @@ extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t
+ extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
+ dwc_otg_transaction_type_e tr_type);
+
++int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh);
++void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh);
++
++
+ /** @} */
+
+ /** @name Interrupt Handler Functions */
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
+index 274967b..ee920c4 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
+@@ -276,7 +276,7 @@ void dump_frame_list(dwc_otg_hcd_t * hcd)
+ static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ {
+ dwc_irqflags_t flags;
+- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
++ dwc_spinlock_t *channel_lock = hcd->channel_lock;
+
+ dwc_hc_t *hc = qh->channel;
+ if (dwc_qh_is_non_per(qh)) {
+@@ -306,7 +306,6 @@ static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ dwc_memset(qh->desc_list, 0x00,
+ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
+ }
+- DWC_SPINLOCK_FREE(channel_lock);
+ }
+
+ /**
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
+index 4823167..fb57db0 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
+@@ -113,6 +113,11 @@ extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd);
+ */
+ extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+
++/** This function is used to handle the fast interrupt
++ *
++ */
++extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void);
++
+ /**
+ * Returns private data set by
+ * dwc_otg_hcd_set_priv_data function.
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+index b41e164..64d33a5 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+@@ -34,6 +34,12 @@
+
+ #include "dwc_otg_hcd.h"
+ #include "dwc_otg_regs.h"
++#include "dwc_otg_mphi_fix.h"
++
++#include <linux/jiffies.h>
++#include <mach/hardware.h>
++#include <asm/fiq.h>
++
+
+ extern bool microframe_schedule;
+
+@@ -41,38 +47,487 @@ extern bool microframe_schedule;
+ * This file contains the implementation of the HCD Interrupt handlers.
+ */
+
++/*
++ * Some globals to communicate between the FIQ and INTERRUPT
++ */
++
++void * dummy_send;
++mphi_regs_t c_mphi_regs;
++volatile void *dwc_regs_base;
++int fiq_done, int_done;
++
++gintsts_data_t gintsts_saved = {.d32 = 0};
++hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
++hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
++int split_out_xfersize[MAX_EPS_CHANNELS];
++haint_data_t haint_saved;
++
++int g_next_sched_frame, g_np_count, g_np_sent;
++static int mphi_int_count = 0 ;
++
++hcchar_data_t nak_hcchar;
++hctsiz_data_t nak_hctsiz;
++hcsplt_data_t nak_hcsplt;
++int nak_count;
++
++int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
++int split_start_frame[MAX_EPS_CHANNELS];
++int queued_port[MAX_EPS_CHANNELS];
++
++#ifdef FIQ_DEBUG
++char buffer[1000*16];
++int wptr;
++void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...)
++{
++ FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB;
++ va_list args;
++ char text[17];
++ hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
++ unsigned long flags;
++
++ local_irq_save(flags);
++ local_fiq_disable();
++ if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR)
++ {
++ snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937);
++ va_start(args, fmt);
++ vsnprintf(text+8, 9, fmt, args);
++ va_end(args);
++
++ memcpy(buffer + wptr, text, 16);
++ wptr = (wptr + 16) % sizeof(buffer);
++ }
++ local_irq_restore(flags);
++}
++#endif
++
++void notrace fiq_queue_request(int channel, int odd_frame)
++{
++ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
++ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) };
++
++ if(hcsplt.b.spltena == 0)
++ {
++ fiq_print(FIQDBG_ERR, "SPLTENA ");
++ BUG();
++ }
++
++ if(hcchar.b.epdir == 1)
++ {
++ fiq_print(FIQDBG_SCHED, "IN Ch %d", channel);
++ }
++ else
++ {
++ hctsiz.b.xfersize = 0;
++ fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel);
++ }
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32);
++
++ hcsplt.b.compsplt = 1;
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32);
++
++ // Send the Split complete
++ hcchar.b.chen = 1;
++ hcchar.b.oddfrm = odd_frame ? 1 : 0;
++
++ // Post this for transmit on the next frame for periodic or this frame for non-periodic
++ fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN");
++
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32);
++}
++
++static int last_sof = -1;
++
++/*
++** Function to handle the start of frame interrupt, choose whether we need to do anything and
++** therefore trigger the main interrupt
++**
++** returns int != 0 - interrupt has been handled
++*/
++int diff;
++
++int notrace fiq_sof_handle(hfnum_data_t hfnum)
++{
++ int handled = 0;
++ int i;
++
++ // Just check that once we're running we don't miss a SOF
++ /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff)))
++ {
++ fiq_print(FIQDBG_ERR, "LASTSOF ");
++ fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7);
++ fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7);
++ BUG();
++ }*/
++
++ // Only start remembering the last sof when the interrupt has been
++ // enabled (we don't check the mask to come in here...)
++ if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3))
++ last_sof = hfnum.b.frnum;
++
++ for(i = 0; i < MAX_EPS_CHANNELS; i++)
++ {
++ if(complete_sched[i] != -1)
++ {
++ if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0))
++ {
++ fiq_queue_request(i, hfnum.b.frnum & 1);
++ complete_sched[i] = -1;
++ }
++ }
++
++ if(complete_sched[i] != -1)
++ {
++ // This is because we've seen a split complete occur with no start...
++ // most likely because missed the complete 0x3fff frames ago!
++
++ diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ;
++ if(diff > 32 && diff < 0x3f00)
++ {
++ fiq_print(FIQDBG_ERR, "SPLTMISS");
++ BUG();
++ }
++ }
++ }
++
++ if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
++ {
++ /*
++ * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet
++ * g_next_sched_frame is the next frame we have periodic packets for
++ *
++ * if neither of these are required for this frame then just clear the interrupt
++ */
++ handled = 1;
++
++ }
++
++ return handled;
++}
++
++int notrace port_id(hcsplt_data_t hcsplt)
++{
++ return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8);
++}
++
++int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum)
++{
++ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
++ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
++ hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) };
++ hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) };
++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)};
++
++ hcint_saved[channel].d32 |= hcint.d32;
++ hcintmsk_saved[channel].d32 = hcintmsk.d32;
++
++ if(hcsplt.b.spltena)
++ {
++ fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt));
++ if(hcint.b.chhltd)
++ {
++ fiq_print(FIQDBG_SCHED, "CH HLT %d", channel);
++ fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]);
++ }
++ if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr)
++ {
++ queued_port[channel] = 0;
++ fiq_print(FIQDBG_ERR, "CHAN ERR");
++ }
++ if(hcint.b.xfercomp)
++ {
++ // Clear the port allocation and transmit anything also on this port
++ queued_port[channel] = 0;
++ fiq_print(FIQDBG_SCHED, "XFERCOMP");
++ }
++ if(hcint.b.nak)
++ {
++ queued_port[channel] = 0;
++ fiq_print(FIQDBG_SCHED, "NAK");
++ }
++ if(hcint.b.ack && !hcsplt.b.compsplt)
++ {
++ int i;
++
++ // Do not complete isochronous out transactions
++ if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0)
++ {
++ queued_port[channel] = 0;
++ fiq_print(FIQDBG_SCHED, "ISOC_OUT");
++ }
++ else
++ {
++ // Make sure we check the port / hub combination that we sent this split on.
++ // Do not queue a second request to the same port
++ for(i = 0; i < MAX_EPS_CHANNELS; i++)
++ {
++ if(port_id(hcsplt) == queued_port[i])
++ {
++ fiq_print(FIQDBG_ERR, "PORTERR ");
++ //BUG();
++ }
++ }
++
++ split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7;
++
++ // Note, the size of an OUT is in the start split phase, not
++ // the complete split
++ split_out_xfersize[channel] = hctsiz.b.xfersize;
++
++ hcint_saved[channel].b.chhltd = 0;
++ hcint_saved[channel].b.ack = 0;
++
++ queued_port[channel] = port_id(hcsplt);
++
++ if(hcchar.b.eptype & 1)
++ {
++ // Send the periodic complete in the same oddness frame as the ACK went...
++ fiq_queue_request(channel, !(hfnum.b.frnum & 1));
++ // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
++ }
++ else
++ {
++ // Schedule the split complete to occur later
++ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2);
++ fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
++ }
++ }
++ }
++ if(hcint.b.nyet)
++ {
++ fiq_print(FIQDBG_ERR, "NYETERR1");
++ //BUG();
++ // Can transmit a split complete up to uframe .0 of the next frame
++ if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8))
++ {
++ // Send it next frame
++ if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc
++ {
++ fiq_print(FIQDBG_SCHED, "NYT:SEND");
++ fiq_queue_request(channel, !(hfnum.b.frnum & 1));
++ }
++ else
++ {
++ // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP)
++ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
++ fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
++ }
++ hcint_saved[channel].b.chhltd = 0;
++ hcint_saved[channel].b.nyet = 0;
++ }
++ else
++ {
++ queued_port[channel] = 0;
++ fiq_print(FIQDBG_ERR, "NYETERR2");
++ //BUG();
++ }
++ }
++ }
++ else
++ {
++ /*
++ * If we have any of NAK, ACK, Datatlgerr active on a
++ * non-split channel, the sole reason is to reset error
++ * counts for a previously broken transaction. The FIQ
++ * will thrash on NAK IN and ACK OUT in particular so
++ * handle it "once" and allow the IRQ to do the rest.
++ */
++ hcint.d32 &= hcintmsk.d32;
++ if(hcint.b.nak)
++ {
++ hcintmsk.b.nak = 0;
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
++ }
++ if (hcint.b.ack)
++ {
++ hcintmsk.b.ack = 0;
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
++ }
++ }
++
++ // Clear the interrupt, this will also clear the HAINT bit
++ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32);
++ return hcint_saved[channel].d32 == 0;
++}
++
++gintsts_data_t gintsts;
++gintmsk_data_t gintmsk;
++// triggered: The set of interrupts that were triggered
++// handled: The set of interrupts that have been handled (no IRQ is
++// required)
++// keep: The set of interrupts we want to keep unmasked even though we
++// want to trigger an IRQ to handle it (SOF and HCINTR)
++gintsts_data_t triggered, handled, keep;
++hfnum_data_t hfnum;
++
++void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void)
++{
++
++ /* entry takes care to store registers we will be treading on here */
++ asm __volatile__ (
++ "mov ip, sp ;"
++ /* stash FIQ and normal regs */
++ "stmdb sp!, {r0-r12, lr};"
++ /* !! THIS SETS THE FRAME, adjust to > sizeof locals */
++ "sub fp, ip, #512 ;"
++ );
++
++ // Cannot put local variables at the beginning of the function
++ // because otherwise 'C' will play with the stack pointer. any locals
++ // need to be inside the following block
++ do
++ {
++ fiq_done++;
++ gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14);
++ gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18);
++ hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408);
++ triggered.d32 = gintsts.d32 & gintmsk.d32;
++ handled.d32 = 0;
++ keep.d32 = 0;
++ fiq_print(FIQDBG_INT, "FIQ ");
++ fiq_print(FIQDBG_INT, "%08x", gintsts.d32);
++ fiq_print(FIQDBG_INT, "%08x", gintmsk.d32);
++ if(gintsts.d32)
++ {
++ // If port enabled
++ if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5)
++ {
++ if(gintsts.b.sofintr)
++ {
++ if(fiq_sof_handle(hfnum))
++ {
++ handled.b.sofintr = 1; /* Handled in FIQ */
++ }
++ else
++ {
++ /* Keer interrupt unmasked */
++ keep.b.sofintr = 1;
++ }
++ {
++ // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing
++ // a start of frame interrupt
++ gintsts_data_t gintsts = { .b.sofintr = 1 };
++ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
++ }
++ }
++
++ if(fiq_split_enable && gintsts.b.hcintr)
++ {
++ int i;
++ haint_data_t haint;
++ haintmsk_data_t haintmsk;
++
++ haint.d32 = FIQ_READ(dwc_regs_base + 0x414);
++ haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418);
++ haint.d32 &= haintmsk.d32;
++ haint_saved.d32 |= haint.d32;
++
++ fiq_print(FIQDBG_INT, "hcintr");
++ fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414));
++
++ // Go through each channel that has an enabled interrupt
++ for(i = 0; i < 16; i++)
++ if((haint.d32 >> i) & 1)
++ if(fiq_hcintr_handle(i, hfnum))
++ haint_saved.d32 &= ~(1 << i); /* this was handled */
++
++ /* If we've handled all host channel interrupts then don't trigger the interrupt */
++ if(haint_saved.d32 == 0)
++ {
++ handled.b.hcintr = 1;
++ }
++ else
++ {
++ /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */
++ keep.b.hcintr = 1;
++ }
++
++ {
++ gintsts_data_t gintsts = { .b.hcintr = 1 };
++
++ // Always clear the channel interrupt
++ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
++ }
++ }
++ }
++ else
++ {
++ last_sof = -1;
++ }
++ }
++
++ // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep
++ gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32));
++ // Save those that were triggered but not handled
++ gintsts_saved.d32 |= triggered.d32 & ~handled.d32;
++ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
++
++ // Clear and save any unhandled interrupts and trigger the interrupt
++ if(gintsts_saved.d32)
++ {
++ /* To enable the MPHI interrupt (INT 32)
++ */
++ FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send);
++ FIQ_WRITE( c_mphi_regs.outddb, (1 << 29));
++
++ mphi_int_count++;
++ }
++ }
++ while(0);
++
++ mb();
++
++ /* exit back to normal mode restoring everything */
++ asm __volatile__ (
++ /* return FIQ regs back to pristine state
++ * and get normal regs back
++ */
++ "ldmia sp!, {r0-r12, lr};"
++
++ /* return */
++ "subs pc, lr, #4;"
++ );
++}
++
+ /** This function handles interrupts for the HCD. */
+ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ {
+ int retval = 0;
++ static int last_time;
+
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ gintsts_data_t gintsts;
++ gintmsk_data_t gintmsk;
++ hfnum_data_t hfnum;
++
+ #ifdef DEBUG
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+
+- //GRAYG: debugging
+- if (NULL == global_regs) {
+- DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p "
+- "core_if=%p\n",
+- dwc_otg_hcd, global_regs);
+- return retval;
+- }
+ #endif
+
++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
++ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
++
+ /* Exit from ISR if core is hibernated */
+ if (core_if->hibernation_suspend == 1) {
+- return retval;
++ goto exit_handler_routine;
+ }
+ DWC_SPINLOCK(dwc_otg_hcd->lock);
+ /* Check if HOST Mode */
+ if (dwc_otg_is_host_mode(core_if)) {
+- gintsts.d32 = dwc_otg_read_core_intr(core_if);
++ local_fiq_disable();
++ gintmsk.d32 |= gintsts_saved.d32;
++ gintsts.d32 |= gintsts_saved.d32;
++ gintsts_saved.d32 = 0;
++ local_fiq_enable();
+ if (!gintsts.d32) {
+- DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+- return 0;
++ goto exit_handler_routine;
+ }
++ gintsts.d32 &= gintmsk.d32;
++
+ #ifdef DEBUG
++ // We should be OK doing this because the common interrupts should already have been serviced
+ /* Don't print debug message in the interrupt handler on SOF */
+ #ifndef DEBUG_SOF
+ if (gintsts.d32 != DWC_SOF_INTR_MASK)
+@@ -88,10 +543,16 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n",
+ gintsts.d32, core_if);
+ #endif
+-
+- if (gintsts.b.sofintr) {
++ hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum);
++ if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
++ {
++ /* Note, we should never get here if the FIQ is doing it's job properly*/
+ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
+ }
++ else if (gintsts.b.sofintr) {
++ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
++ }
++
+ if (gintsts.b.rxstsqlvl) {
+ retval |=
+ dwc_otg_hcd_handle_rx_status_q_level_intr
+@@ -106,7 +567,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ /** @todo Implement i2cintr handler. */
+ }
+ if (gintsts.b.portintr) {
++
++ gintmsk_data_t gintmsk = { .b.portintr = 1};
+ retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+ }
+ if (gintsts.b.hcintr) {
+ retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
+@@ -138,11 +602,48 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ #endif
+
+ }
++
++exit_handler_routine:
++
++ if (fiq_fix_enable)
++ {
++ local_fiq_disable();
++ // Make sure that we don't clear the interrupt if we've still got pending work to do
++ if(gintsts_saved.d32 == 0)
++ {
++ /* Clear the MPHI interrupt */
++ DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16));
++ if (mphi_int_count >= 60)
++ {
++ DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16)));
++ while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17)))
++ ;
++ DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31));
++ mphi_int_count = 0;
++ }
++ int_done++;
++ }
++
++ // Unmask handled interrupts
++ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
++ //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1);
++
++ local_fiq_enable();
++
++ if((jiffies / HZ) > last_time)
++ {
++ /* Once a second output the fiq and irq numbers, useful for debug */
++ last_time = jiffies / HZ;
++ DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done);
++ }
++ }
++
+ DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+ return retval;
+ }
+
+ #ifdef DWC_TRACK_MISSED_SOFS
++
+ #warning Compiling code to track missed SOFs
+ #define FRAME_NUM_ARRAY_SIZE 1000
+ /**
+@@ -188,7 +689,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
+ dwc_list_link_t *qh_entry;
+ dwc_otg_qh_t *qh;
+ dwc_otg_transaction_type_e tr_type;
+- gintsts_data_t gintsts = {.d32 = 0 };
++ int did_something = 0;
++ int32_t next_sched_frame = -1;
+
+ hfnum.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+@@ -212,17 +714,31 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
+ qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry);
+ qh_entry = qh_entry->next;
+ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) {
++
+ /*
+ * Move QH to the ready list to be executed next
+ * (micro)frame.
+ */
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+ &qh->qh_list_entry);
++
++ did_something = 1;
++ }
++ else
++ {
++ if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame))
++ {
++ next_sched_frame = qh->sched_frame;
++ }
+ }
+ }
++
++ g_next_sched_frame = next_sched_frame;
++
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
++ did_something = 1;
+ }
+
+ /* Clear interrupt */
+@@ -511,6 +1027,15 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+
+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
+
++ // Overwrite with saved interrupts from fiq handler
++ if(fiq_split_enable)
++ {
++ local_fiq_disable();
++ haint.d32 = haint_saved.d32;
++ haint_saved.d32 = 0;
++ local_fiq_enable();
++ }
++
+ for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
+ if (haint.b2.chint & (1 << i)) {
+ retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
+@@ -551,7 +1076,10 @@ static uint32_t get_actual_xfer_length(dwc_hc_t * hc,
+ *short_read = (hctsiz.b.xfersize != 0);
+ }
+ } else if (hc->qh->do_split) {
+- length = qtd->ssplit_out_xfer_count;
++ if(fiq_split_enable)
++ length = split_out_xfersize[hc->hc_num];
++ else
++ length = qtd->ssplit_out_xfer_count;
+ } else {
+ length = hc->xfer_len;
+ }
+@@ -595,7 +1123,6 @@ static int update_urb_state_xfer_comp(dwc_hc_t * hc,
+ DWC_OTG_HC_XFER_COMPLETE,
+ &short_read);
+
+-
+ /* non DWORD-aligned buffer case handling. */
+ if (hc->align_buff && xfer_length && hc->ep_is_in) {
+ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
+@@ -797,11 +1324,24 @@ static void release_channel(dwc_otg_hcd_t * hcd,
+ dwc_otg_transaction_type_e tr_type;
+ int free_qtd;
+ dwc_irqflags_t flags;
+- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
++ dwc_spinlock_t *channel_lock = hcd->channel_lock;
++#ifdef FIQ_DEBUG
++ int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0;
++#endif
++ int hog_port = 0;
+
+ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n",
+ __func__, hc->hc_num, halt_status, hc->xfer_len);
+
++ if(fiq_split_enable && hc->do_split) {
++ if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) {
++ if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID ||
++ hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) {
++ hog_port = 1;
++ }
++ }
++ }
++
+ switch (halt_status) {
+ case DWC_OTG_HC_XFER_URB_COMPLETE:
+ free_qtd = 1;
+@@ -876,15 +1416,32 @@ cleanup:
+
+ DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
+ hcd->available_host_channels++;
++ fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels);
+ DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
+ }
+
++ if(fiq_split_enable && hc->do_split)
++ {
++ if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr)))
++ {
++ fiq_print(FIQDBG_ERR, "PRTNOTAL");
++ //BUG();
++ }
++ if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC ||
++ hc->ep_type == DWC_OTG_EP_TYPE_INTR)) {
++ hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr);
++#ifdef FIQ_DEBUG
++ hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1;
++#endif
++ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp);
++ }
++ }
++
+ /* Try to queue more transfers now that there's a free channel. */
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+- DWC_SPINLOCK_FREE(channel_lock);
+ }
+
+ /**
+@@ -1295,6 +1852,17 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
+ "NAK Received--\n", hc->hc_num);
+
+ /*
++ * When we get bulk NAKs then remember this so we holdoff on this qh until
++ * the beginning of the next frame
++ */
++ switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
++ case UE_BULK:
++ case UE_CONTROL:
++ if (nak_holdoff_enable)
++ hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
++ }
++
++ /*
+ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+ * interrupt. Re-start the SSPLIT transfer.
+ */
+@@ -1316,7 +1884,11 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
+ * transfers in DMA mode for the sole purpose of
+ * resetting the error count after a transaction error
+ * occurs. The core will continue transferring data.
++ * Disable other interrupts unmasked for the same
++ * reason.
+ */
++ disable_hc_int(hc_regs, datatglerr);
++ disable_hc_int(hc_regs, ack);
+ qtd->error_count = 0;
+ goto handle_nak_done;
+ }
+@@ -1428,6 +2000,15 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd,
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
+ }
+ } else {
++ /*
++ * An unmasked ACK on a non-split DMA transaction is
++ * for the sole purpose of resetting error counts. Disable other
++ * interrupts unmasked for the same reason.
++ */
++ if(hcd->core_if->dma_enable) {
++ disable_hc_int(hc_regs, datatglerr);
++ disable_hc_int(hc_regs, nak);
++ }
+ qtd->error_count = 0;
+
+ if (hc->qh->ping_state) {
+@@ -1490,8 +2071,10 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ int frnum = dwc_otg_hcd_get_frame_number(hcd);
+
++ // With the FIQ running we only ever see the failed NYET
+ if (dwc_full_frame_num(frnum) !=
+- dwc_full_frame_num(hc->qh->sched_frame)) {
++ dwc_full_frame_num(hc->qh->sched_frame) ||
++ fiq_split_enable) {
+ /*
+ * No longer in the same full speed frame.
+ * Treat this as a transaction error.
+@@ -1778,13 +2361,28 @@ static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd,
+ dwc_otg_qtd_t * qtd)
+ {
+ DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+- "Data Toggle Error--\n", hc->hc_num);
++ "Data Toggle Error on %s transfer--\n",
++ hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
+
+- if (hc->ep_is_in) {
++ /* Data toggles on split transactions cause the hc to halt.
++ * restart transfer */
++ if(hc->qh->do_split)
++ {
++ qtd->error_count++;
++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
++ update_urb_state_xfer_intr(hc, hc_regs,
++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++ } else if (hc->ep_is_in) {
++ /* An unmasked data toggle error on a non-split DMA transaction is
++ * for the sole purpose of resetting error counts. Disable other
++ * interrupts unmasked for the same reason.
++ */
++ if(hcd->core_if->dma_enable) {
++ disable_hc_int(hc_regs, ack);
++ disable_hc_int(hc_regs, nak);
++ }
+ qtd->error_count = 0;
+- } else {
+- DWC_ERROR("Data Toggle Error on OUT transfer,"
+- "channel %d\n", hc->hc_num);
+ }
+
+ disable_hc_int(hc_regs, datatglerr);
+@@ -1862,10 +2460,10 @@ static inline int halt_status_ok(dwc_otg_hcd_t * hcd,
+ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+- dwc_otg_qtd_t * qtd)
++ dwc_otg_qtd_t * qtd,
++ hcint_data_t hcint,
++ hcintmsk_data_t hcintmsk)
+ {
+- hcint_data_t hcint;
+- hcintmsk_data_t hcintmsk;
+ int out_nak_enh = 0;
+
+ /* For core with OUT NAK enhancement, the flow for high-
+@@ -1897,8 +2495,11 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ }
+
+ /* Read the HCINTn register to determine the cause for the halt. */
+- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
++ if(!fiq_split_enable)
++ {
++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
++ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
++ }
+
+ if (hcint.b.xfercomp) {
+ /** @todo This is here because of a possible hardware bug. Spec
+@@ -1937,6 +2538,8 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.frmovrun) {
+ handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
++ } else if (hcint.b.datatglerr) {
++ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+ } else if (!out_nak_enh) {
+ if (hcint.b.nyet) {
+ /*
+@@ -1986,12 +2589,24 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ DWC_READ_REG32(&hcd->
+ core_if->core_global_regs->
+ gintsts));
++ /* Failthrough: use 3-strikes rule */
++ qtd->error_count++;
++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
++ update_urb_state_xfer_intr(hc, hc_regs,
++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+ }
+
+ }
+ } else {
+ DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
+ hcint.d32);
++ /* Failthrough: use 3-strikes rule */
++ qtd->error_count++;
++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
++ update_urb_state_xfer_intr(hc, hc_regs,
++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+ }
+ }
+
+@@ -2009,13 +2624,15 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+- dwc_otg_qtd_t * qtd)
++ dwc_otg_qtd_t * qtd,
++ hcint_data_t hcint,
++ hcintmsk_data_t hcintmsk)
+ {
+ DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+ "Channel Halted--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_enable) {
+- handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd);
++ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk);
+ } else {
+ #ifdef DEBUG
+ if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
+@@ -2032,7 +2649,7 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
+ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+ {
+ int retval = 0;
+- hcint_data_t hcint;
++ hcint_data_t hcint, hcint_orig;
+ hcintmsk_data_t hcintmsk;
+ dwc_hc_t *hc;
+ dwc_otg_hc_regs_t *hc_regs;
+@@ -2042,15 +2659,33 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+
+ hc = dwc_otg_hcd->hc_ptr_array[num];
+ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
++ if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
++ /* We are responding to a channel disable. Driver
++ * state is cleared - our qtd has gone away.
++ */
++ release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
++ return 1;
++ }
+ qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
+
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
++ hcint_orig = hcint;
+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+ DWC_DEBUGPL(DBG_HCDV,
+ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
+ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
+ hcint.d32 = hcint.d32 & hcintmsk.d32;
+
++ if(fiq_split_enable)
++ {
++ // replace with the saved interrupts from the fiq handler
++ local_fiq_disable();
++ hcint_orig.d32 = hcint_saved[num].d32;
++ hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32;
++ hcint_saved[num].d32 = 0;
++ local_fiq_enable();
++ }
++
+ if (!dwc_otg_hcd->core_if->dma_enable) {
+ if (hcint.b.chhltd && hcint.d32 != 0x2) {
+ hcint.b.chhltd = 0;
+@@ -2068,7 +2703,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+ hcint.b.nyet = 0;
+ }
+ if (hcint.b.chhltd) {
+- retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd);
++ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]);
+ }
+ if (hcint.b.ahberr) {
+ retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+@@ -2080,7 +2715,8 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+ retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.ack) {
+- retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
++ if(!hcint.b.chhltd)
++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.nyet) {
+ retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+@@ -2102,5 +2738,4 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+
+ return retval;
+ }
+-
+ #endif /* DWC_DEVICE_ONLY */
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
+index e4787f5..ee8eec9 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
+@@ -1,3 +1,4 @@
++
+ /* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $
+ * $Revision: #20 $
+@@ -50,6 +51,7 @@
+ #include <linux/dma-mapping.h>
+ #include <linux/version.h>
+ #include <asm/io.h>
++#include <asm/fiq.h>
+ #include <linux/usb.h>
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ #include <../drivers/usb/core/hcd.h>
+@@ -67,6 +69,8 @@
+ #include "dwc_otg_dbg.h"
+ #include "dwc_otg_driver.h"
+ #include "dwc_otg_hcd.h"
++#include "dwc_otg_mphi_fix.h"
++
+ /**
+ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
+ * qualified with its direction (possible 32 endpoints per device).
+@@ -76,6 +80,8 @@
+
+ static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
+
++extern bool fiq_fix_enable;
++
+ /** @name Linux HC Driver API Functions */
+ /** @{ */
+ /* manage i/o requests, device state */
+@@ -259,13 +265,15 @@ static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
+
+ /**
+ * Sets the final status of an URB and returns it to the device driver. Any
+- * required cleanup of the URB is performed.
++ * required cleanup of the URB is performed. The HCD lock should be held on
++ * entry.
+ */
+ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+ dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
+ {
+ struct urb *urb = (struct urb *)urb_handle;
+-
++ urb_tq_entry_t *new_entry;
++ int rc = 0;
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
+ __func__, urb, usb_pipedevice(urb->pipe),
+@@ -279,7 +287,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+ }
+ }
+ }
+-
++ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
+ urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
+ /* Convert status value. */
+ switch (status) {
+@@ -301,6 +309,9 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+ case -DWC_E_OVERFLOW:
+ status = -EOVERFLOW;
+ break;
++ case -DWC_E_SHUTDOWN:
++ status = -ESHUTDOWN;
++ break;
+ default:
+ if (status) {
+ DWC_PRINTF("Uknown urb status %d\n", status);
+@@ -342,18 +353,33 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+ }
+
+ DWC_FREE(dwc_otg_urb);
+-
++ if (!new_entry) {
++ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
++ urb->status = -EPROTO;
++ /* don't schedule the tasklet -
++ * directly return the packet here with error. */
+ #if USB_URB_EP_LINKING
+- usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+ #endif
+- DWC_SPINUNLOCK(hcd->lock);
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
+ #else
+- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
+ #endif
+- DWC_SPINLOCK(hcd->lock);
+-
++ } else {
++ new_entry->urb = urb;
++#if USB_URB_EP_LINKING
++ rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
++ if(0 == rc) {
++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
++ }
++#endif
++ if(0 == rc) {
++ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
++ urb_tq_entries);
++ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
++ }
++ }
+ return 0;
+ }
+
+@@ -366,6 +392,16 @@ static struct dwc_otg_hcd_function_ops hcd_fops = {
+ .get_b_hnp_enable = _get_b_hnp_enable,
+ };
+
++static struct fiq_handler fh = {
++ .name = "usb_fiq",
++};
++struct fiq_stack_s {
++ int magic1;
++ uint8_t stack[2048];
++ int magic2;
++} fiq_stack;
++
++extern mphi_regs_t c_mphi_regs;
+ /**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
+@@ -379,6 +415,7 @@ int hcd_init(dwc_bus_dev_t *_dev)
+ dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+ int retval = 0;
+ u64 dmamask;
++ struct pt_regs regs;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev);
+
+@@ -396,6 +433,20 @@ int hcd_init(dwc_bus_dev_t *_dev)
+ pci_set_consistent_dma_mask(_dev, dmamask);
+ #endif
+
++ if (fiq_fix_enable)
++ {
++ // Set up fiq
++ claim_fiq(&fh);
++ set_fiq_handler(__FIQ_Branch, 4);
++ memset(&regs,0,sizeof(regs));
++ regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq;
++ regs.ARM_r9 = (long)0;
++ regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4;
++ set_fiq_regs(&regs);
++ fiq_stack.magic1 = 0xdeadbeef;
++ fiq_stack.magic2 = 0xaa995566;
++ }
++
+ /*
+ * Allocate memory for the base HCD plus the DWC OTG HCD.
+ * Initialize the base HCD.
+@@ -415,6 +466,30 @@ int hcd_init(dwc_bus_dev_t *_dev)
+
+ hcd->regs = otg_dev->os_dep.base;
+
++ if (fiq_fix_enable)
++ {
++ volatile extern void *dwc_regs_base;
++
++ //Set the mphi periph to the required registers
++ c_mphi_regs.base = otg_dev->os_dep.mphi_base;
++ c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c;
++ c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28;
++ c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c;
++ c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50;
++
++ dwc_regs_base = otg_dev->os_dep.base;
++
++ //Enable mphi peripheral
++ writel((1<<31),c_mphi_regs.ctrl);
++#ifdef DEBUG
++ if (readl(c_mphi_regs.ctrl) & 0x80000000)
++ DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n");
++ else
++ DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n");
++#endif
++ // Enable FIQ interrupt from USB peripheral
++ enable_fiq(INTERRUPT_VC_USB);
++ }
+ /* Initialize the DWC OTG HCD. */
+ dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
+ if (!dwc_otg_hcd) {
+@@ -607,9 +682,7 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+ struct usb_host_endpoint *ep = urb->ep;
+ #endif
+-#if USB_URB_EP_LINKING
+ dwc_irqflags_t irqflags;
+-#endif
+ void **ref_ep_hcpriv = &ep->hcpriv;
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ dwc_otg_hcd_urb_t *dwc_otg_urb;
+@@ -661,9 +734,8 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
+ if(dwc_otg_urb == NULL)
+ return -ENOMEM;
+
+- urb->hcpriv = dwc_otg_urb;
+- if (!dwc_otg_urb && urb->number_of_packets)
+- return -ENOMEM;
++ if (!dwc_otg_urb && urb->number_of_packets)
++ return -ENOMEM;
+
+ dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe), ep_type,
+@@ -703,37 +775,42 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
+ iso_frame_desc[i].length);
+ }
+
++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
++ urb->hcpriv = dwc_otg_urb;
+ #if USB_URB_EP_LINKING
+- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
+ if (0 == retval)
+ #endif
+- {
+- retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
+- /*(dwc_otg_qh_t **)*/
+- ref_ep_hcpriv,
+- mem_flags == GFP_ATOMIC ? 1 : 0);
+- if (0 == retval) {
+- if (alloc_bandwidth) {
+- allocate_bus_bandwidth(hcd,
+- dwc_otg_hcd_get_ep_bandwidth(
+- dwc_otg_hcd, *ref_ep_hcpriv),
+- urb);
+- }
+- } else {
++ {
++ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
++ /*(dwc_otg_qh_t **)*/
++ ref_ep_hcpriv, 1);
++ if (0 == retval) {
++ if (alloc_bandwidth) {
++ allocate_bus_bandwidth(hcd,
++ dwc_otg_hcd_get_ep_bandwidth(
++ dwc_otg_hcd, *ref_ep_hcpriv),
++ urb);
++ }
++ } else {
++ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
+ #if USB_URB_EP_LINKING
+- dwc_irqflags_t irqflags;
+- DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
+- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
+- usb_hcd_unlink_urb_from_ep(hcd, urb);
+- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
++ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ #endif
+- if (retval == -DWC_E_NO_DEVICE) {
+- retval = -ENODEV;
+- }
+- }
+- }
++ DWC_FREE(dwc_otg_urb);
++ urb->hcpriv = NULL;
++ if (retval == -DWC_E_NO_DEVICE)
++ retval = -ENODEV;
++ }
++ }
++#if USB_URB_EP_LINKING
++ else
++ {
++ DWC_FREE(dwc_otg_urb);
++ urb->hcpriv = NULL;
++ }
++#endif
++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
+ return retval;
+ }
+
+@@ -777,6 +854,8 @@ static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ #endif
+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
++
++
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ usb_hcd_giveback_urb(hcd, urb);
+ #else
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
+index 0a1cbb7..5c22b6c 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
+@@ -41,6 +41,7 @@
+
+ #include "dwc_otg_hcd.h"
+ #include "dwc_otg_regs.h"
++#include "dwc_otg_mphi_fix.h"
+
+ extern bool microframe_schedule;
+
+@@ -182,6 +183,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
+ if (microframe_schedule)
+ qh->speed = dev_speed;
+
++ qh->nak_frame = 0xffff;
+
+ if (((dev_speed == USB_SPEED_LOW) ||
+ (dev_speed == USB_SPEED_FULL)) &&
+@@ -191,6 +193,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
+ dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+ hub_port);
+ qh->do_split = 1;
++ qh->skip_count = 0;
+ }
+
+ if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
+@@ -573,6 +576,9 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ return status;
+ }
+
++
++extern int g_next_sched_frame, g_np_count, g_np_sent;
++
+ /**
+ * Schedules an interrupt or isochronous transfer in the periodic schedule.
+ *
+@@ -631,8 +637,13 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
+ }
+ else {
+- /* Always start in the inactive schedule. */
+- DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
++ if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame))
++ {
++ g_next_sched_frame = qh->sched_frame;
++
++ }
++ /* Always start in the inactive schedule. */
++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
+ }
+
+ if (!microframe_schedule) {
+@@ -646,6 +657,7 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ return status;
+ }
+
++
+ /**
+ * This function adds a QH to either the non periodic or periodic schedule if
+ * it is not already in the schedule. If the QH is already in the schedule, no
+@@ -668,6 +680,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ /* Always start in the inactive schedule. */
+ DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
+ &qh->qh_list_entry);
++ g_np_count++;
+ } else {
+ status = schedule_periodic(hcd, qh);
+ if ( !hcd->periodic_qh_count ) {
+@@ -727,6 +740,9 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ hcd->non_periodic_qh_ptr->next;
+ }
+ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
++
++ // If we've removed the last non-periodic entry then there are none left!
++ g_np_count = g_np_sent;
+ } else {
+ deschedule_periodic(hcd, qh);
+ hcd->periodic_qh_count--;
+@@ -755,6 +771,24 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ int sched_next_periodic_split)
+ {
+ if (dwc_qh_is_non_per(qh)) {
++
++ dwc_otg_qh_t *qh_tmp;
++ dwc_list_link_t *qh_list;
++ DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive)
++ {
++ qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry);
++ if(qh_tmp == qh)
++ {
++ /*
++ * FIQ is being disabled because this one nevers gets a np_count increment
++ * This is still not absolutely correct, but it should fix itself with
++ * just an unnecessary extra interrupt
++ */
++ g_np_sent = g_np_count;
++ }
++ }
++
++
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+ /* Add back to inactive non-periodic schedule. */
+@@ -768,6 +802,7 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ if (sched_next_periodic_split) {
+
+ qh->sched_frame = frame_number;
++
+ if (dwc_frame_num_le(frame_number,
+ dwc_frame_num_inc
+ (qh->start_split_frame,
+@@ -816,6 +851,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+ &qh->qh_list_entry);
+ } else {
++ if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame))
++ {
++ g_next_sched_frame = qh->sched_frame;
++ }
++
+ DWC_LIST_MOVE_HEAD
+ (&hcd->periodic_sched_inactive,
+ &qh->qh_list_entry);
+@@ -880,6 +920,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
+ * QH to place the QTD into. If it does not find a QH, then it will create a
+ * new QH. If the QH to which the QTD is added is not currently scheduled, it
+ * is placed into the proper schedule based on its EP type.
++ * HCD lock must be held and interrupts must be disabled on entry
+ *
+ * @param[in] qtd The QTD to add
+ * @param[in] hcd The DWC HCD structure
+@@ -892,8 +933,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
+ dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
+ {
+ int retval = 0;
+- dwc_irqflags_t flags;
+-
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+ /*
+@@ -903,18 +942,16 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
+ if (*qh == NULL) {
+ *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
+ if (*qh == NULL) {
+- retval = -1;
++ retval = -DWC_E_NO_MEMORY;
+ goto done;
+ }
+ }
+- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ retval = dwc_otg_hcd_qh_add(hcd, *qh);
+ if (retval == 0) {
+ DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
+ qtd_list_entry);
++ qtd->qh = *qh;
+ }
+- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+-
+ done:
+
+ return retval;
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
+new file mode 100755
+index 0000000..50b94a8
+--- /dev/null
++++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
+@@ -0,0 +1,113 @@
++#include "dwc_otg_regs.h"
++#include "dwc_otg_dbg.h"
++
++void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name)
++{
++ DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n"
++ "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n"
++ "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n"
++ "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n"
++ "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n"
++ "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n"
++ "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n"
++ "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n"
++ "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
++ function_name,
++ gintsts.b.curmode,
++ gintsts.b.modemismatch,
++ gintsts.b.otgintr,
++ gintsts.b.sofintr,
++ gintsts.b.rxstsqlvl,
++ gintsts.b.nptxfempty,
++ gintsts.b.ginnakeff,
++ gintsts.b.goutnakeff,
++ gintsts.b.ulpickint,
++ gintsts.b.i2cintr,
++ gintsts.b.erlysuspend,
++ gintsts.b.usbsuspend,
++ gintsts.b.usbreset,
++ gintsts.b.enumdone,
++ gintsts.b.isooutdrop,
++ gintsts.b.eopframe,
++ gintsts.b.restoredone,
++ gintsts.b.epmismatch,
++ gintsts.b.inepint,
++ gintsts.b.outepintr,
++ gintsts.b.incomplisoin,
++ gintsts.b.incomplisoout,
++ gintsts.b.fetsusp,
++ gintsts.b.resetdet,
++ gintsts.b.portintr,
++ gintsts.b.hcintr,
++ gintsts.b.ptxfempty,
++ gintsts.b.lpmtranrcvd,
++ gintsts.b.conidstschng,
++ gintsts.b.disconnect,
++ gintsts.b.sessreqintr,
++ gintsts.b.wkupintr);
++ return;
++}
++
++void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name)
++{
++ DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n"
++ "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n"
++ "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n"
++ "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n"
++ "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n"
++ "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n"
++ "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n"
++ "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n"
++ "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
++ function_name,
++ gintmsk.b.modemismatch,
++ gintmsk.b.otgintr,
++ gintmsk.b.sofintr,
++ gintmsk.b.rxstsqlvl,
++ gintmsk.b.nptxfempty,
++ gintmsk.b.ginnakeff,
++ gintmsk.b.goutnakeff,
++ gintmsk.b.ulpickint,
++ gintmsk.b.i2cintr,
++ gintmsk.b.erlysuspend,
++ gintmsk.b.usbsuspend,
++ gintmsk.b.usbreset,
++ gintmsk.b.enumdone,
++ gintmsk.b.isooutdrop,
++ gintmsk.b.eopframe,
++ gintmsk.b.restoredone,
++ gintmsk.b.epmismatch,
++ gintmsk.b.inepintr,
++ gintmsk.b.outepintr,
++ gintmsk.b.incomplisoin,
++ gintmsk.b.incomplisoout,
++ gintmsk.b.fetsusp,
++ gintmsk.b.resetdet,
++ gintmsk.b.portintr,
++ gintmsk.b.hcintr,
++ gintmsk.b.ptxfempty,
++ gintmsk.b.lpmtranrcvd,
++ gintmsk.b.conidstschng,
++ gintmsk.b.disconnect,
++ gintmsk.b.sessreqintr,
++ gintmsk.b.wkupintr);
++ return;
++}
++
++void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name)
++{
++ DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n"
++ "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n"
++ "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n"
++ "mvic: %1i\n",
++ function_name,
++ gotgint.b.sesenddet,
++ gotgint.b.sesreqsucstschng,
++ gotgint.b.hstnegsucstschng,
++ gotgint.b.hstnegdet,
++ gotgint.b.adevtoutchng,
++ gotgint.b.debdone,
++ gotgint.b.mvic);
++
++ return;
++}
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
+new file mode 100755
+index 0000000..ca17379
+--- /dev/null
++++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
+@@ -0,0 +1,48 @@
++#ifndef __DWC_OTG_MPHI_FIX_H__
++#define __DWC_OTG_MPHI_FIX_H__
++#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_))
++#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_))
++
++typedef struct {
++ volatile void* base;
++ volatile void* ctrl;
++ volatile void* outdda;
++ volatile void* outddb;
++ volatile void* intstat;
++} mphi_regs_t;
++
++void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name);
++void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name);
++void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name);
++
++extern gintsts_data_t gintsts_saved;
++
++#ifdef DEBUG
++#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__)
++#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__)
++#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__)
++
++#else
++#define DWC_DBG_PRINT_CORE_INT(_arg_)
++#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_)
++#define DWC_DBG_PRINT_OTG_INT(_arg_)
++
++#endif
++
++typedef enum {
++ FIQDBG_SCHED = (1 << 0),
++ FIQDBG_INT = (1 << 1),
++ FIQDBG_ERR = (1 << 2),
++ FIQDBG_PORTHUB = (1 << 3),
++} FIQDBG_T;
++
++void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...);
++#ifdef FIQ_DEBUG
++#define fiq_print _fiq_print
++#else
++#define fiq_print(x, y, ...)
++#endif
++
++extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable;
++
++#endif
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
+index e46d9bb..6b2c7d0 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
++++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
+@@ -97,6 +97,9 @@ typedef struct os_dependent {
+ /** Register offset for Diagnostic API */
+ uint32_t reg_offset;
+
++ /** Base address for MPHI peripheral */
++ void *mphi_base;
++
+ #ifdef LM_INTERFACE
+ struct lm_device *lmdev;
+ #elif defined(PCI_INTERFACE)
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
+index 1b1f83c..c8590b5 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
+@@ -4276,7 +4276,7 @@ do { \
+ && (pcd->ep0state == EP0_OUT_DATA_PHASE))
+ status.d32 = core_if->dev_if->out_desc_addr->status.d32;
+ if (pcd->ep0state == EP0_OUT_STATUS_PHASE)
+- status.d32 = status.d32 = core_if->dev_if->
++ status.d32 = core_if->dev_if->
+ out_desc_addr->status.d32;
+
+ if (status.b.sr) {
+--
+1.8.3.2
+