From 56822de577325e209a9ecfcf3e62d8bb70c2f2cc Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 8 Apr 2013 21:12:48 +0100 Subject: [PATCH 032/196] Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh --- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 7 ++++++- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 22 +++++++++++++++++++++- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 5 +++++ drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 21 +++++++++++++++++++-- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 19 +++++++++++++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c index 3ac720b..d353a9a 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c @@ -243,6 +243,9 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = { //Global variable to switch the fiq fix on or off (declared in bcm2708.c) extern bool fiq_fix_enable; +//Global variable to switch the nak holdoff on or off +bool nak_holdoff_enable = true; + /** * This function shows the Driver Version. @@ -1086,6 +1089,7 @@ static int __init dwc_otg_driver_init(void) 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"); error = driver_create_file(drv, &driver_attr_version); #ifdef DEBUG @@ -1366,9 +1370,10 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); module_param(microframe_schedule, bool, 0444); MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); - module_param(fiq_fix_enable, bool, 0444); MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix"); +module_param(nak_holdoff_enable, bool, 0444); +MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff"); /** @page "Module Parameters" * diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c index 0ce7e46..2b7945a 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -527,6 +527,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, { dwc_otg_qh_t *qh; dwc_otg_qtd_t *urb_qtd; + BUG_ON(!hcd); + BUG_ON(!dwc_otg_urb); #ifdef DEBUG /* integrity checks (Broadcom) */ @@ -543,14 +545,17 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, return -DWC_E_INVALID; } urb_qtd = dwc_otg_urb->qtd; + BUG_ON(!urb_qtd); if (urb_qtd->qh == NULL) { DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); return -DWC_E_INVALID; } #else urb_qtd = dwc_otg_urb->qtd; + BUG_ON(!urb_qtd); #endif qh = urb_qtd->qh; + BUG_ON(!qh); if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { if (urb_qtd->in_process) { dump_channel_info(hcd, qh); @@ -1309,6 +1314,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) num_channels - hcd->periodic_channels) && !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); + + /* + * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission + * we hold off on bulk retransmissions to reduce NAK interrupt overhead for + * cheeky devices that just hold off using NAKs + */ + if (dwc_full_frame_num(qh->nak_frame) == dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) { + // Make fiq interrupt run on next frame (i.e. 8 uframes) + 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) { @@ -1321,7 +1342,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) last_sel_trans_num_nonper_scheduled++; #endif /* DEBUG_HOST_CHANNELS */ } - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); assign_and_init_hc(hcd, qh); diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h index 6d82127..45e44ea 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h @@ -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; diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c index 21e8f09..3e762e2 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c @@ -56,7 +56,12 @@ 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; +extern bool fiq_fix_enable, nak_holdoff_enable; + +hcchar_data_t nak_hcchar; +hctsiz_data_t nak_hctsiz; +hcsplt_data_t nak_hcsplt; +int nak_count; void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void) { @@ -230,7 +235,7 @@ exit_handler_routine: DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31)); mphi_int_count = 0; } - int_done++; + int_done++; if((jiffies / HZ) > last_time) { /* Once a second output the fiq and irq numbers, useful for debug */ @@ -1419,6 +1424,18 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, "NAK Received--\n", hc->hc_num); /* + * When we get bulk NAKs then remember this so we holdoff on this qh until + * the beginning of the next frame + */ + switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { + case UE_BULK: + //case UE_INTERRUPT: + //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. */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c index ac10323..e6b2a7b 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c @@ -181,6 +181,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) if (microframe_schedule) qh->speed = dev_speed; + qh->nak_frame = 0xffff; if (((dev_speed == USB_SPEED_LOW) || (dev_speed == USB_SPEED_FULL)) && @@ -764,6 +765,24 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int sched_next_periodic_split) { if (dwc_qh_is_non_per(qh)) { + + dwc_otg_qh_t *qh_tmp; + dwc_list_link_t *qh_list; + DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) + { + qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry); + if(qh_tmp == qh) + { + /* + * FIQ is being disabled because this one nevers gets a np_count increment + * This is still not absolutely correct, but it should fix itself with + * just an unnecessary extra interrupt + */ + g_np_sent = g_np_count; + } + } + + dwc_otg_hcd_qh_remove(hcd, qh); if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { /* Add back to inactive non-periodic schedule. */ -- 1.9.1