mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			3245 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			3245 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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
 | 
						|
 | 
						|
--- 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.
 | 
						|
 
 | 
						|
--- a/arch/arm/include/asm/irqflags.h
 | 
						|
+++ b/arch/arm/include/asm/irqflags.h
 | 
						|
@@ -145,12 +145,22 @@ static inline unsigned long arch_local_s
 | 
						|
 }
 | 
						|
 
 | 
						|
 /*
 | 
						|
- * 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");
 | 
						|
--- 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)
 | 
						|
--- 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_
 | 
						|
 		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 ir
 | 
						|
 		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 * b
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	armctrl_pm_register(base, irq_start, resume_sources);
 | 
						|
+	init_FIQ(FIQ_START);
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
--- a/arch/arm/mach-bcm2708/bcm2708.c
 | 
						|
+++ b/arch/arm/mach-bcm2708/bcm2708.c
 | 
						|
@@ -321,12 +321,32 @@ static struct resource bcm2708_usb_resou
 | 
						|
 	       .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);
 | 
						|
--- 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)
 | 
						|
--- 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 *
 | 
						|
 
 | 
						|
 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 *ta
 | 
						|
 	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)
 | 
						|
--- 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.
 | 
						|
--- 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
 | 
						|
 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
 | 
						|
  *
 | 
						|
--- 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_ci
 | 
						|
 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
 | 
						|
--- 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 devic
 | 
						|
 	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 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 devic
 | 
						|
 	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 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 d
 | 
						|
 		       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 d
 | 
						|
 		       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
 | 
						|
 
 | 
						|
--- 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(d
 | 
						|
 /**
 | 
						|
  * 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_comm
 | 
						|
 	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
 | 
						|
 {
 | 
						|
 	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
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	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
 | 
						|
 			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);
 | 
						|
 
 | 
						|
--- 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(c
 | 
						|
 	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
 | 
						|
--- 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_para
 | 
						|
 	.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(vo
 | 
						|
 	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(vo
 | 
						|
 		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
 | 
						|
 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.
 | 
						|
--- 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_a
 | 
						|
 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 * h
 | 
						|
 
 | 
						|
 /**
 | 
						|
  * 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
 | 
						|
 	 */
 | 
						|
 	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
 | 
						|
 				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_
 | 
						|
 			    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_
 | 
						|
 		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_
 | 
						|
 		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_
 | 
						|
 {
 | 
						|
 	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_
 | 
						|
 		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_
 | 
						|
 			 */
 | 
						|
 			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 *dat
 | 
						|
 	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
 | 
						|
 	} 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_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
 | 
						|
 
 | 
						|
 	/* 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
 | 
						|
 	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_h
 | 
						|
 		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_h
 | 
						|
 	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_s
 | 
						|
 {
 | 
						|
 	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_s
 | 
						|
 
 | 
						|
 	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_s
 | 
						|
 		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_s
 | 
						|
 		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_s
 | 
						|
 				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_s
 | 
						|
 				   &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(dw
 | 
						|
 
 | 
						|
 		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_channel
 | 
						|
 
 | 
						|
 		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
 | 
						|
 	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;
 | 
						|
 }
 | 
						|
 
 | 
						|
--- 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_ot
 | 
						|
 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 */
 | 
						|
--- 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
 | 
						|
 		dwc_memset(qh->desc_list, 0x00,
 | 
						|
 			   sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
 | 
						|
 	}
 | 
						|
-	DWC_SPINLOCK_FREE(channel_lock);
 | 
						|
 }
 | 
						|
 
 | 
						|
 /**
 | 
						|
--- 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_h
 | 
						|
  */
 | 
						|
 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.
 | 
						|
--- 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_
 | 
						|
 				    "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_
 | 
						|
 			/** @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_
 | 
						|
 #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_
 | 
						|
 	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_
 | 
						|
 		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_o
 | 
						|
 
 | 
						|
 	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(d
 | 
						|
 				*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(dw
 | 
						|
 					     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_
 | 
						|
 	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_ot
 | 
						|
 		    "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_ot
 | 
						|
 			 * 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_ot
 | 
						|
 			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_o
 | 
						|
 		    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_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
 | 
						|
 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(dw
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	/* 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(dw
 | 
						|
 		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(dw
 | 
						|
 				     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(dw
 | 
						|
 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
 | 
						|
 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
 | 
						|
 
 | 
						|
 	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
 | 
						|
 		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
 | 
						|
 		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
 | 
						|
 
 | 
						|
 	return retval;
 | 
						|
 }
 | 
						|
-
 | 
						|
 #endif /* DWC_DEVICE_ONLY */
 | 
						|
--- 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 us
 | 
						|
 
 | 
						|
 /**
 | 
						|
  * 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
 | 
						|
 			}
 | 
						|
 		}
 | 
						|
 	}
 | 
						|
-
 | 
						|
+	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
 | 
						|
 	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
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	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 h
 | 
						|
 	.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(®s,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(®s);
 | 
						|
+		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 us
 | 
						|
 #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 us
 | 
						|
 	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 us
 | 
						|
 						    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);
 | 
						|
-#endif
 | 
						|
-                        if (retval == -DWC_E_NO_DEVICE) {
 | 
						|
-                                retval = -ENODEV;
 | 
						|
-                        }
 | 
						|
-                }
 | 
						|
-        }
 | 
						|
+			usb_hcd_unlink_urb_from_ep(hcd, urb);
 | 
						|
+#endif
 | 
						|
+			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 us
 | 
						|
                 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
 | 
						|
--- 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_ot
 | 
						|
 	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_ot
 | 
						|
 			    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_h
 | 
						|
 	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
 | 
						|
 		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
 | 
						|
 	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 * h
 | 
						|
 		/* 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->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_h
 | 
						|
 			       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_h
 | 
						|
 			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_h
 | 
						|
 				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
 | 
						|
  * 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 *
 | 
						|
 			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 *
 | 
						|
 	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;
 | 
						|
--- /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;
 | 
						|
+}
 | 
						|
--- /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
 | 
						|
--- 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)
 | 
						|
--- 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) {
 |