Skip to content

Commit

Permalink
Enable fiq fix by default. Add NAK holdoff scheme. Enabled by default…
Browse files Browse the repository at this point in the history
…, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh
  • Loading branch information
popcornmix committed Oct 8, 2012
1 parent 9b2353b commit a8afdd5
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 5 deletions.
9 changes: 7 additions & 2 deletions drivers/usb/host/dwc_otg/dwc_otg_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,10 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = {
};

//Global variable to switch the fiq fix on or off
bool fiq_fix_enable = false;
bool fiq_fix_enable = true;

//Global variable to switch the nak holdoff on or off
bool nak_holdoff_enable = true;


/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
*
Expand Down
22 changes: 21 additions & 1 deletion drivers/usb/host/dwc_otg/dwc_otg_hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) */

Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
21 changes: 19 additions & 2 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -230,7 +235,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
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 */
Expand Down Expand Up @@ -1418,6 +1423,18 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
"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.
Expand Down
19 changes: 19 additions & 0 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) &&
Expand Down Expand Up @@ -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. */
Expand Down

0 comments on commit a8afdd5

Please sign in to comment.