diff options
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 86 |
1 files changed, 60 insertions, 26 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 73b0b7fc77f1..742eb8268e9a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1496,6 +1496,7 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) } static irqreturn_t dwc3_interrupt(int irq, void *_dwc); +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) @@ -1566,8 +1567,8 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_ep0_out_start(dwc); irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, - "dwc3", dwc); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", irq, ret); @@ -2432,40 +2433,73 @@ static void dwc3_process_event_entry(struct dwc3 *dwc, } } +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + int i; + + spin_lock_irqsave(&dwc->lock, flags); + + for (i = 0; i < dwc->num_event_buffers; i++) { + struct dwc3_event_buffer *evt; + int left; + + evt = dwc->ev_buffs[i]; + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) + continue; + + while (left > 0) { + union dwc3_event event; + + event.raw = *(u32 *) (evt->buf + evt->lpos); + + dwc3_process_event_entry(dwc, &event); + + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4); + } + + evt->count = 0; + evt->flags &= ~DWC3_EVENT_PENDING; + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) { struct dwc3_event_buffer *evt; - int left; u32 count; + evt = dwc->ev_buffs[buf]; + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) return IRQ_NONE; - evt = dwc->ev_buffs[buf]; - left = count; - - while (left > 0) { - union dwc3_event event; - - event.raw = *(u32 *) (evt->buf + evt->lpos); - - dwc3_process_event_entry(dwc, &event); - /* - * XXX we wrap around correctly to the next entry as almost all - * entries are 4 bytes in size. There is one entry which has 12 - * bytes which is a regular entry followed by 8 bytes data. ATM - * I don't know how things are organized if were get next to the - * a boundary so I worry about that once we try to handle that. - */ - evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; - left -= 4; - - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); - } + evt->count = count; + evt->flags |= DWC3_EVENT_PENDING; - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; } static irqreturn_t dwc3_interrupt(int irq, void *_dwc) @@ -2480,7 +2514,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) irqreturn_t status; status = dwc3_process_event_buf(dwc, i); - if (status == IRQ_HANDLED) + if (status == IRQ_WAKE_THREAD) ret = status; } |