Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
dwc_otg: Rewrite driver to include FIQ_FSM
This extensive rewrite changes the operational capabilities of the FIQ
to perform the entirety of a split transaction in FIQ context.

The advantages are numerous:
- Total CPU interrupt processing time is reduced by ~90%
- Interrupt latency no longer affects split transactions, except
  in extreme cases
- NAK holdoff is now adjustable

All driver options relating to the FIQ have been changed.
- dwc_otg.fiq_enable
- dwc_otg.fiq_fsm_enable
- dwc_otg.fiq_fsm_mask
- dwc_otg.nak_holdoff

See the code for how to set the module options.

Known bugs:
- Isochronous OUT transfers are subject to occasional data corruption.
- Unplugging the root port or booting with the root port disconnected
  will result in the interrupt handling breaking and USB becoming
  unresponsive
- Setting dwc_otg.fiq_enable=0 causes interrupt handling to break.
  • Loading branch information
P33M authored and popcornmix committed Feb 21, 2014
1 parent da615ca commit e18eaac
Show file tree
Hide file tree
Showing 15 changed files with 2,872 additions and 957 deletions.
19 changes: 0 additions & 19 deletions arch/arm/mach-bcm2708/bcm2708.c
Expand Up @@ -328,20 +328,6 @@ static struct resource bcm2708_usb_resources[] = {
},
};

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);

Expand Down Expand Up @@ -789,11 +775,6 @@ 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);
Expand Down
3 changes: 2 additions & 1 deletion drivers/usb/host/dwc_otg/Makefile
Expand Up @@ -36,7 +36,8 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o
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
dwc_otg-objs += dwc_otg_fiq_fsm.o
dwc_otg-objs += dwc_otg_fiq_stub.o
ifneq ($(CFI),)
dwc_otg-objs += dwc_otg_cfi.o
endif
Expand Down
1 change: 0 additions & 1 deletion drivers/usb/host/dwc_otg/dwc_otg_cil.c
Expand Up @@ -2873,7 +2873,6 @@ void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
hcchar.b.chen = 1;
hcchar.b.chdis = 0;
DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);

hc->xfer_started = 1;
hc->requests++;

Expand Down
47 changes: 27 additions & 20 deletions drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
Expand Up @@ -45,7 +45,6 @@
#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)
Expand Down Expand Up @@ -1319,7 +1318,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
/**
* This function returns the Core Interrupt register.
*/
static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk)
static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd)
{
gahbcfg_data_t gahbcfg = {.d32 = 0 };
gintsts_data_t gintsts;
Expand All @@ -1345,16 +1344,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
}
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);
if(fiq_enable) {
local_fiq_disable();
gintmsk.d32 |= gintmsk_common.d32;
gintsts_saved.d32 &= ~gintmsk_common.d32;
reenable_gintmsk->d32 = gintmsk.d32;
local_irq_restore(flags);
/* Pull in the interrupts that the FIQ has masked */
gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
/* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
reenable_gintmsk->d32 |= gintmsk.d32;
reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
reenable_gintmsk->d32 &= gintmsk_common.d32;
local_fiq_enable();
}

gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
Expand All @@ -1366,13 +1364,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
gintsts.d32, gintmsk.d32);
}
#endif
if (!fiq_fix_enable){
if (!fiq_enable){
if (gahbcfg.b.glblintrmsk)
return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
else
return 0;
}
else {
} else {
/* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface.
* Can't trust the global interrupt mask bit in this case.
*/
return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
}

Expand Down Expand Up @@ -1406,7 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev)
{
int retval = 0;
gintsts_data_t gintsts;
gintmsk_data_t reenable_gintmsk;
gintmsk_data_t gintmsk_reenable = { .d32 = 0 };
gpwrdn_data_t gpwrdn = {.d32 = 0 };
dwc_otg_device_t *otg_dev = dev;
dwc_otg_core_if_t *core_if = otg_dev->core_if;
Expand All @@ -1428,7 +1428,10 @@ int32_t dwc_otg_handle_common_intr(void *dev)
}

if (core_if->hibernation_suspend <= 0) {
gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk);
/* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end
* of this handler - god only knows why it's done like this
*/
gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd);

if (gintsts.b.modemismatch) {
retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
Expand Down Expand Up @@ -1525,11 +1528,16 @@ int32_t dwc_otg_handle_common_intr(void *dev)
gintsts.b.portintr = 1;
DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
retval |= 1;
reenable_gintmsk.b.portintr = 1;
gintmsk_reenable.b.portintr = 1;

}

DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32);
/* Did we actually handle anything? if so, unmask the interrupt */
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval);
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32);
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32);
if (retval) {
DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32);
}

} else {
DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
Expand Down Expand Up @@ -1583,6 +1591,5 @@ int32_t dwc_otg_handle_common_intr(void *dev)
}
if (core_if->lock)
DWC_SPINUNLOCK(core_if->lock);

return retval;
}
47 changes: 27 additions & 20 deletions drivers/usb/host/dwc_otg/dwc_otg_driver.c
Expand Up @@ -56,6 +56,7 @@
#include "dwc_otg_core_if.h"
#include "dwc_otg_pcd_if.h"
#include "dwc_otg_hcd_if.h"
#include "dwc_otg_fiq_fsm.h"

#define DWC_DRIVER_VERSION "3.00a 10-AUG-2012"
#define DWC_DRIVER_DESC "HS OTG USB Controller driver"
Expand All @@ -64,7 +65,6 @@ bool microframe_schedule=true;

static const char dwc_driver_name[] = "dwc_otg";

extern void* dummy_send;

extern int pcd_init(
#ifdef LM_INTERFACE
Expand Down Expand Up @@ -240,13 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = {
.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 switch the fiq fix on or off
bool fiq_enable = 1;
// 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;
bool fiq_fsm_enable = false;
//Bulk split-transaction NAK holdoff in microframes
uint16_t nak_holdoff = 8;

unsigned short fiq_fsm_mask = 0x01;

/**
* This function shows the Driver Version.
Expand Down Expand Up @@ -800,7 +801,7 @@ static int dwc_otg_driver_probe(
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 (fiq_enable)
{
if (!request_mem_region(_dev->resource[1].start,
_dev->resource[1].end - _dev->resource[1].start + 1,
Expand All @@ -813,7 +814,6 @@ static int dwc_otg_driver_probe(
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
Expand Down Expand Up @@ -1071,9 +1071,9 @@ static int __init dwc_otg_driver_init(void)
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;
if(fiq_fsm_enable && !fiq_enable) {
printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n");
fiq_enable = 1;
}

printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name,
Expand All @@ -1095,9 +1095,9 @@ static int __init dwc_otg_driver_init(void)
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");
printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");

error = driver_create_file(drv, &driver_attr_version);
#ifdef DEBUG
Expand Down Expand Up @@ -1378,12 +1378,19 @@ 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");
module_param(fiq_split_enable, bool, 0444);
MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions");
module_param(fiq_enable, bool, 0444);
MODULE_PARM_DESC(fiq_enable, "Enable the FIQ");
module_param(nak_holdoff, ushort, 0644);
MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8");
module_param(fiq_fsm_enable, bool, 0444);
MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask");
module_param(fiq_fsm_mask, ushort, 0444);
MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n"
"Bit 0 : Non-periodic split transactions\n"
"Bit 1 : Periodic split transactions\n"
"Bit 2 : High-speed multi-transfer isochronous\n"
"All other bits should be set 0.");


/** @page "Module Parameters"
*
Expand Down

0 comments on commit e18eaac

Please sign in to comment.