Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB dwc_otg: fix system lockup when interrupts are threaded #4

Merged
merged 3 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,7 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
gintmsk_common.b.lpmtranrcvd = 1;
#endif
gintmsk_common.b.restoredone = 1;
unsigned long flags;
if(dwc_otg_is_device_mode(core_if))
{
/** @todo: The port interrupt occurs while in device
Expand All @@ -1345,17 +1346,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
gintmsk_common.b.portintr = 1;
}
if(fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
/* Pull in the interrupts that the FIQ has masked */
gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
gintmsk.d32 |= gintmsk_common.d32;
/* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
reenable_gintmsk->d32 = gintmsk.d32;
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
Expand Down
14 changes: 14 additions & 0 deletions drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,18 @@ extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels);

extern void dwc_otg_fiq_nop(struct fiq_state *state);

#define fiq_fsm_spin_lock_irqsave(lock, flags) \
do { \
local_fiq_disable(); \
local_irq_save(flags); \
fiq_fsm_spin_lock(lock); \
} while (0)

#define fiq_fsm_spin_unlock_irqrestore(lock, flags) \
do { \
fiq_fsm_spin_unlock(lock); \
local_irq_restore(flags); \
local_fiq_enable(); \
} while (0)

#endif /* DWC_OTG_FIQ_FSM_H_ */
55 changes: 23 additions & 32 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ static int32_t dwc_otg_hcd_start_cb(void *p)
static int32_t dwc_otg_hcd_disconnect_cb(void *p)
{
gintsts_data_t intr;
unsigned long flags;
dwc_otg_hcd_t *dwc_otg_hcd = p;

DWC_SPINLOCK(dwc_otg_hcd->lock);
Expand All @@ -309,8 +310,7 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
dwc_otg_hcd->flags.b.port_connect_status_change = 1;
dwc_otg_hcd->flags.b.port_connect_status = 0;
if(fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&dwc_otg_hcd->fiq_state->lock, flags);
}
/*
* Shutdown any transfers in process by clearing the Tx FIFO Empty
Expand Down Expand Up @@ -389,8 +389,7 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p)
}

if(fiq_enable) {
fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&dwc_otg_hcd->fiq_state->lock, flags);
}

if (dwc_otg_hcd->fops->disconnect) {
Expand Down Expand Up @@ -556,6 +555,7 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
{
dwc_otg_qh_t *qh;
dwc_otg_qtd_t *urb_qtd;
unsigned long flags;
BUG_ON(!hcd);
BUG_ON(!dwc_otg_urb);

Expand Down Expand Up @@ -614,15 +614,13 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
int running = 0;
enum fiq_fsm_state state;

local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
qh->channel->halt_pending = 1;
if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);

if (dwc_qh_is_non_per(qh)) {
do {
Expand Down Expand Up @@ -1463,12 +1461,10 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)

dwc_otg_hc_init(hcd->core_if, hc);

local_irq_save(flags);

if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
}
if (fiq_enable)
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
else
local_irq_save(flags);

/* Enable the top level host channel interrupt. */
intr_enable = (1 << hc->hc_num);
Expand All @@ -1478,12 +1474,10 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
gintmsk.b.hcintr = 1;
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);

if (fiq_enable) {
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
}

local_irq_restore(flags);
if (fiq_enable)
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
else
local_irq_restore(flags);
hc->qh = qh;
}

Expand Down Expand Up @@ -1708,6 +1702,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
int xfer_len, nrpackets;
hcdma_data_t hcdma;
hfnum_data_t hfnum;
unsigned long flags;

if (st->fsm != FIQ_PASSTHROUGH)
return 0;
Expand Down Expand Up @@ -1783,8 +1778,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
Expand All @@ -1804,8 +1798,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
}
mb();
st->hcchar_copy.b.chen = 0;
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
return 0;
}

Expand All @@ -1831,6 +1824,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
/* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */
int hub_addr, port_addr, frame, uframe;
struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num];
unsigned long flags;

/*
* Non-periodic channel assignments stay in the non_periodic_active queue.
Expand Down Expand Up @@ -1951,8 +1945,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);

local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);

if (hc->ep_type & 0x1) {
hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
Expand Down Expand Up @@ -2061,8 +2054,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
}
mb();
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
return 0;
}

Expand Down Expand Up @@ -2543,6 +2535,7 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd)
void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
dwc_otg_transaction_type_e tr_type)
{
unsigned long flags;
#ifdef DEBUG_SOF
DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n");
#endif
Expand All @@ -2568,11 +2561,9 @@ void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
gintmsk.b.nptxfempty = 1;

if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
}
Expand Down
40 changes: 16 additions & 24 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
gintmsk_data_t gintmsk;
hfnum_data_t hfnum;
haintmsk_data_t haintmsk;
unsigned long flags;

#ifdef DEBUG
dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
Expand All @@ -100,8 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
/* Check if HOST Mode */
if (dwc_otg_is_host_mode(core_if)) {
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&dwc_otg_hcd->fiq_state->lock, flags);
/* Pull in from the FIQ's disabled mask */
gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
Expand All @@ -118,8 +118,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
gintsts.d32 &= gintmsk.d32;

if (fiq_enable) {
fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&dwc_otg_hcd->fiq_state->lock, flags);
}

if (!gintsts.d32) {
Expand Down Expand Up @@ -166,11 +165,9 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
gintmsk_data_t gintmsk = { .b.portintr = 1};
retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&dwc_otg_hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&dwc_otg_hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
}
Expand Down Expand Up @@ -210,8 +207,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
if (fiq_enable) {
gintmsk_data_t gintmsk_new;
haintmsk_data_t haintmsk_new;
local_fiq_disable();
fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&dwc_otg_hcd->fiq_state->lock, flags);
gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
if(fiq_fsm_enable)
haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
Expand All @@ -238,8 +234,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
/* Re-enable interrupts that the FIQ masked (first time round) */
FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&dwc_otg_hcd->fiq_state->lock, flags);

if ((jiffies / HZ) > last_time) {
//dwc_otg_qh_t *qh;
Expand Down Expand Up @@ -641,6 +636,7 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
{
int i;
int retval = 0;
unsigned long flags;
haint_data_t haint = { .d32 = 0 } ;

/* Clear appropriate bits in HCINTn to clear the interrupt bit in
Expand All @@ -653,12 +649,10 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
if(fiq_fsm_enable)
{
/* check the mask? */
local_fiq_disable();
fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&dwc_otg_hcd->fiq_state->lock, flags);
haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&dwc_otg_hcd->fiq_state->lock, flags);
}

for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
Expand Down Expand Up @@ -1065,6 +1059,8 @@ static void halt_channel(dwc_otg_hcd_t * hcd,
dwc_hc_t * hc,
dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
{
unsigned long flags;

if (hcd->core_if->dma_enable) {
release_channel(hcd, hc, qtd, halt_status);
return;
Expand All @@ -1087,11 +1083,9 @@ static void halt_channel(dwc_otg_hcd_t * hcd,
*/
gintmsk.b.nptxfempty = 1;
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
}
Expand All @@ -1112,11 +1106,9 @@ static void halt_channel(dwc_otg_hcd_t * hcd,
*/
gintmsk.b.ptxfempty = 1;
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
}
Expand Down
15 changes: 7 additions & 8 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
int status = 0;
unsigned long flags;
gintmsk_data_t intr_mask = {.d32 = 0 };

if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
Expand All @@ -697,11 +698,9 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
if ( !hcd->periodic_qh_count ) {
intr_mask.b.sofintr = 1;
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
}
Expand Down Expand Up @@ -745,6 +744,8 @@ static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
* @param qh QH to remove from schedule. */
void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
unsigned long flags;

gintmsk_data_t intr_mask = {.d32 = 0 };

if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
Expand All @@ -766,11 +767,9 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
if( !hcd->periodic_qh_count && !fiq_fsm_enable ) {
intr_mask.b.sofintr = 1;
if (fiq_enable) {
local_fiq_disable();
fiq_fsm_spin_lock(&hcd->fiq_state->lock);
fiq_fsm_spin_lock_irqsave(&hcd->fiq_state->lock, flags);
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
local_fiq_enable();
fiq_fsm_spin_unlock_irqrestore(&hcd->fiq_state->lock, flags);
} else {
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
}
Expand Down