From 41452d2332fdb6daf11feaedf08b7f9b966e2a38 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 11 Sep 2015 17:17:30 +0000 Subject: brcm2708: remove linux 3.18 support Signed-off-by: Felix Fietkau git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46870 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- ...ch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch | 3244 -------------------- 1 file changed, 3244 deletions(-) delete mode 100644 target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch (limited to 'target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch') diff --git a/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch b/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch deleted file mode 100644 index 8d3464fefe..0000000000 --- a/target/linux/brcm2708/patches-3.18/0025-Add-FIQ-patch-to-dwc_otg-driver.-Enable-with-dwc_otg.patch +++ /dev/null @@ -1,3244 +0,0 @@ -From a29a51d9320d44124fe13457c45663d3051a9452 Mon Sep 17 00:00:00 2001 -From: popcornmix -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 -+#include -+ - #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 -+#include -+#include -+ - - 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 - #include - #include -+#include - #include - #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) { -- cgit v1.2.3