diff options
author | Florian Fainelli <florian@openwrt.org> | 2014-02-28 20:30:08 +0000 |
---|---|---|
committer | Florian Fainelli <florian@openwrt.org> | 2014-02-28 20:30:08 +0000 |
commit | bb39b8d99aae1f7eb13a97bd874838da91080de6 (patch) | |
tree | 3046f53937c0bc5dc13e2b2ab7b688a1932199bf /target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch | |
parent | c6c0d09f85c211560a1405441925681cfa25e8b1 (diff) | |
download | upstream-bb39b8d99aae1f7eb13a97bd874838da91080de6.tar.gz upstream-bb39b8d99aae1f7eb13a97bd874838da91080de6.tar.bz2 upstream-bb39b8d99aae1f7eb13a97bd874838da91080de6.zip |
brcm2708: update against latest rpi-3.10.y branch
Update our copies of the brcm2708 patches to the latest rpi-3.10-y
rebased against linux-3.10.y stable (3.10.32). This should hopefully
make it easier for us in the future to leverage the raspberry/rpi-*
branches.
Signed-off-by: Florian Fainelli <florian@openwrt.org>
SVN-Revision: 39770
Diffstat (limited to 'target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch')
-rw-r--r-- | target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch | 1398 |
1 files changed, 1398 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch b/target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch new file mode 100644 index 0000000000..aa379c8dce --- /dev/null +++ b/target/linux/brcm2708/patches-3.10/0072-USB-fix-using-a-FIQ-to-implement-split-transactions.patch @@ -0,0 +1,1398 @@ +From d0f04f1be88d3ad42f3c875f9ab45a1cdb2129bd Mon Sep 17 00:00:00 2001 +From: Gordon Hollingworth <gordon@holliweb.co.uk> +Date: Thu, 4 Apr 2013 11:05:21 +0100 +Subject: [PATCH 072/174] 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 +--- + .../usb/host/dwc_common_port/dwc_common_linux.c | 7 +- + drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 37 +- + drivers/usb/host/dwc_otg/dwc_otg_driver.c | 6 +- + drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 125 ++++- + drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 20 +- + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 538 ++++++++++++++++++--- + drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 18 +- + drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 31 +- + drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 26 +- + drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 2 +- + 10 files changed, 696 insertions(+), 114 deletions(-) + +--- 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,12 @@ 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_irq_restore(flags); + } + + #if 0 +@@ -1301,7 +1306,7 @@ EXPORT_SYMBOL(DWC_EXCEPTION); + EXPORT_SYMBOL(__DWC_DEBUG); + #endif + +-EXPORT_SYMBOL(__DWC_DMA_ALLOC); ++EXPORT_SYMBOL(__DWC_DMA_ALLOC); + EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC); + EXPORT_SYMBOL(__DWC_DMA_FREE); + EXPORT_SYMBOL(__DWC_ALLOC); +--- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c +@@ -47,8 +47,6 @@ + #include "dwc_otg_hcd.h" + #include "dwc_otg_mphi_fix.h" + +-extern bool fiq_fix_enable; +- + #ifdef DEBUG + inline const char *op_state_str(dwc_otg_core_if_t * core_if) + { +@@ -1321,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; +@@ -1338,19 +1336,33 @@ 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 +@@ -1394,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; +@@ -1415,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); +@@ -1512,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_driver.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c +@@ -242,7 +242,8 @@ static struct dwc_otg_driver_module_para + + //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; + +@@ -1090,6 +1091,7 @@ static int __init dwc_otg_driver_init(vo + } + 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 +@@ -1374,6 +1376,8 @@ 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" + * +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +@@ -45,6 +45,7 @@ + + #include "dwc_otg_hcd.h" + #include "dwc_otg_regs.h" ++#include "dwc_otg_mphi_fix.h" + + extern bool microframe_schedule, nak_holdoff_enable; + +@@ -581,6 +582,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); + } + } + +@@ -716,6 +719,8 @@ static void completion_tasklet_func(void + + 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); +@@ -979,6 +984,10 @@ 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)); ++ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc)); ++ + out: + return retval; + } +@@ -1124,7 +1133,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; +@@ -1271,6 +1285,62 @@ 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); ++ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd); ++ 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); ++ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1; ++ ++ 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 +@@ -1304,11 +1374,22 @@ 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 && 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--; +@@ -1329,8 +1410,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; + } + + /* +@@ -1369,10 +1448,19 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s + qh->nak_frame = 0xffff; + } + } ++ ++ if (qh->do_split && dwc_otg_hcd_allocate_port(hcd, qh)) ++ { ++ g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1); ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ continue; ++ } ++ + if (microframe_schedule) { + 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--; +@@ -1396,16 +1484,17 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s + + g_np_sent++; + +- if (ret_val == DWC_OTG_TRANSACTION_NONE) { +- ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; +- } else { +- ret_val = DWC_OTG_TRANSACTION_ALL; +- } +- + 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 */ +@@ -1522,6 +1611,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 +@@ -1651,6 +1749,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); +--- 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; +@@ -370,6 +370,8 @@ 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); +@@ -574,6 +576,10 @@ struct dwc_otg_hcd { + /** Frame List */ + uint32_t *frame_list; + ++ /** Hub - Port assignment */ ++ int hub_port[16]; ++ int hub_port_alloc[256]; ++ + /** Frame List DMA address */ + dma_addr_t frame_list_dma; + +@@ -604,12 +610,16 @@ 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 */ + /** @{ */ + extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); +-extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd, int32_t); ++extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd); + extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * + dwc_otg_hcd); + extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +@@ -38,6 +38,7 @@ + + #include <linux/jiffies.h> + #include <mach/hardware.h> ++#include <asm/fiq.h> + + + extern bool microframe_schedule; +@@ -52,21 +53,295 @@ extern bool microframe_schedule; + + void * dummy_send; + mphi_regs_t c_mphi_regs; ++volatile void *dwc_regs_base; + int fiq_done, int_done; +-int g_next_sched_frame, g_np_count, g_np_sent, g_work_expected; +-static int mphi_int_count = 0 ; + +-extern bool fiq_fix_enable, nak_holdoff_enable; ++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 _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 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 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 port_id(hcsplt_data_t hcsplt) ++{ ++ return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8); ++} ++ ++int 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(); ++ } ++ } ++ } ++ ++ // 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)) dwc_otg_hcd_handle_fiq(void) + { +- gintsts_data_t gintsts; +- hfnum_data_t hfnum; + + /* entry takes care to store registers we will be treading on here */ + asm __volatile__ ( +@@ -74,43 +349,112 @@ void __attribute__ ((naked)) dwc_otg_hcd + /* stash FIQ and normal regs */ + "stmdb sp!, {r0-r12, lr};" + /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ +- "sub fp, ip, #256 ;" ++ "sub fp, ip, #512 ;" + ); + +- fiq_done++; +- gintsts.d32 = FIQ_READ_IO_ADDRESS(USB_BASE + 0x14) & FIQ_READ_IO_ADDRESS(USB_BASE + 0x18); +- hfnum.d32 = FIQ_READ_IO_ADDRESS(USB_BASE + 0x408); +- +- if(gintsts.d32) ++ // 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 + { +- if(gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) ++ 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 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 +- */ +- gintsts.d32 = 0; +- gintsts.b.sofintr = 1; +- FIQ_WRITE_IO_ADDRESS((USB_BASE + 0x14), 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; ++ } + +- g_work_expected = 0; ++ { ++ gintsts_data_t gintsts = { .b.hcintr = 1 }; ++ ++ // Always clear the channel interrupt ++ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); ++ } ++ } ++ } ++ else ++ { ++ last_sof = -1; ++ } + } +- else ++ ++ // 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) + { +- g_work_expected = 1; + /* To enable the MPHI interrupt (INT 32) + */ +- FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); ++ FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); + FIQ_WRITE( c_mphi_regs.outddb, (1 << 29)); + + mphi_int_count++; +- /* Clear the USB global interrupt so we don't just sit in the FIQ */ +- FIQ_MODIFY_IO_ADDRESS((USB_BASE + 0x8),1,0); +- + } + } ++ while(0); ++ + mb(); + + /* exit back to normal mode restoring everything */ +@@ -133,6 +477,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_ + + 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 +@@ -140,6 +485,9 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_ + + #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) { + goto exit_handler_routine; +@@ -147,11 +495,18 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_ + 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) { + 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) +@@ -171,11 +526,12 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_ + 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, g_work_expected); ++ 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, g_work_expected); ++ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); + } ++ + if (gintsts.b.rxstsqlvl) { + retval |= + dwc_otg_hcd_handle_rx_status_q_level_intr +@@ -190,7 +546,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); +@@ -227,26 +586,35 @@ exit_handler_routine: + + if (fiq_fix_enable) + { +- /* Clear the MPHI interrupt */ +- DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16)); +- if (mphi_int_count >= 60) ++ 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) + { +- 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; ++ /* 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++; + } +- 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); + } +- +- /* Re-Enable FIQ interrupt from USB peripheral */ +- DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1); + } + + DWC_SPINUNLOCK(dwc_otg_hcd->lock); +@@ -294,13 +662,12 @@ static inline void track_missed_sofs(uin + * (micro)frame. Periodic transactions may be queued to the controller for the + * next (micro)frame. + */ +-int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd, int32_t work_expected) ++int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) + { + hfnum_data_t hfnum; + 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; + +@@ -326,6 +693,7 @@ 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. +@@ -351,15 +719,10 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_ + dwc_otg_hcd_queue_transactions(hcd, tr_type); + did_something = 1; + } +- if(work_expected && !did_something) +- DWC_DEBUGPL(DBG_USER, "Nothing to do !! frame = %x, g_next_sched_frame = %x\n", (int) hfnum.b.frnum, g_next_sched_frame); +- if(!work_expected && did_something) +- DWC_DEBUGPL(DBG_USER, "Unexpected work done !! frame = %x, g_next_sched_frame = %x\n", (int) hfnum.b.frnum, g_next_sched_frame); +- + + /* Clear interrupt */ +- gintsts.b.sofintr = 1; +- DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ //gintsts.b.sofintr = 1; ++ //DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); + + return 1; + } +@@ -643,6 +1006,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); +@@ -683,7 +1055,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; + } +@@ -727,7 +1102,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, +@@ -930,6 +1304,9 @@ static void release_channel(dwc_otg_hcd_ + int free_qtd; + dwc_irqflags_t flags; + dwc_spinlock_t *channel_lock = hcd->channel_lock; ++#ifdef FIQ_DEBUG ++ int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0; ++#endif + + DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", + __func__, hc->hc_num, halt_status, hc->xfer_len); +@@ -1008,9 +1385,24 @@ 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(); ++ } ++ ++ hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr); ++ hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1; ++ ++ 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) { +@@ -1633,8 +2025,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. +@@ -2012,10 +2406,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- +@@ -2047,8 +2441,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 +@@ -2161,13 +2558,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)) { +@@ -2184,7 +2583,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; +@@ -2197,12 +2596,23 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc + 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; +@@ -2220,7 +2630,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); +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c +@@ -392,7 +392,11 @@ static struct dwc_otg_hcd_function_ops h + static struct fiq_handler fh = { + .name = "usb_fiq", + }; +-static uint8_t fiqStack[1024]; ++struct fiq_stack_s { ++ int magic1; ++ uint8_t stack[2048]; ++ int magic2; ++} fiq_stack; + + extern mphi_regs_t c_mphi_regs; + /** +@@ -434,9 +438,11 @@ int hcd_init(dwc_bus_dev_t *_dev) + memset(®s,0,sizeof(regs)); + regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq; + regs.ARM_r9 = (long)0; +- regs.ARM_sp = (long)fiqStack + sizeof(fiqStack) - 4; ++ 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. +@@ -459,6 +465,8 @@ int hcd_init(dwc_bus_dev_t *_dev) + + 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; +@@ -466,6 +474,8 @@ int hcd_init(dwc_bus_dev_t *_dev) + 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 +@@ -839,6 +849,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; + +@@ -191,6 +192,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) { +@@ -737,6 +739,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--; +@@ -766,21 +771,21 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h + { + 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) ++ dwc_otg_qh_t *qh_tmp; ++ dwc_list_link_t *qh_list; ++ DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) + { +- /* +- * 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; ++ 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); +--- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h ++++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h +@@ -1,10 +1,7 @@ + #ifndef __DWC_OTG_MPHI_FIX_H__ + #define __DWC_OTG_MPHI_FIX_H__ +- +-#define FIQ_WRITE_IO_ADDRESS(_addr_,_data_) *(volatile uint32_t *) IO_ADDRESS(_addr_) = _data_ +-#define FIQ_READ_IO_ADDRESS(_addr_) *(volatile uint32_t *) IO_ADDRESS(_addr_) +-#define FIQ_MODIFY_IO_ADDRESS(_addr_,_clear_,_set_) FIQ_WRITE_IO_ADDRESS(_addr_ , (FIQ_READ_IO_ADDRESS(_addr_)&~_clear_)|_set_) +-#define FIQ_WRITE(_addr_,_data_) *(volatile uint32_t *) _addr_ = _data_ ++#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_)) ++#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_)) + + typedef struct { + volatile void* base; +@@ -12,13 +9,13 @@ typedef struct { + volatile void* outdda; + volatile void* outddb; + volatile void* intstat; +-} mphi_regs_t; ++} 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__) +@@ -30,7 +27,22 @@ void dwc_debug_otg_int(gotgint_data_t go + #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_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) { |