Merge "usb: dwc3: Use high priority worker as bottom half handler"

This commit is contained in:
Linux Build Service Account 2017-04-04 03:08:04 -07:00 committed by Gerrit - the friendly Code Review server
commit 123020a779
6 changed files with 96 additions and 7 deletions

View file

@ -670,6 +670,16 @@ int dwc3_core_init(struct dwc3 *dwc)
}
}
/*
* Workaround for STAR 9000961433 which affects only version
* 3.00a of the DWC_usb3 core. This prevents the controller
* interrupt from being masked while handling events. IMOD
* allows us to work around this issue. Enable it for the
* affected version.
*/
if (!dwc->imod_interval && (dwc->revision == DWC3_REVISION_300A))
dwc->imod_interval = 1;
ret = dwc3_core_reset(dwc);
if (ret)
goto err0;
@ -1000,6 +1010,15 @@ err0:
#define DWC3_ALIGN_MASK (16 - 1)
/* check whether the core supports IMOD */
bool dwc3_has_imod(struct dwc3 *dwc)
{
return ((dwc3_is_usb3(dwc) &&
dwc->revision >= DWC3_REVISION_300A) ||
(dwc3_is_usb31(dwc) &&
dwc->revision >= DWC3_USB31_REVISION_120A));
}
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1040,8 +1059,8 @@ static int dwc3_probe(struct platform_device *pdev)
/* will be enabled in dwc3_msm_resume() */
irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_interrupt,
IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",
dwc);
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
@ -1219,6 +1238,14 @@ static int dwc3_probe(struct platform_device *pdev)
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);
if (!dwc->dwc_wq) {
pr_err("%s: Unable to create workqueue dwc_wq\n", __func__);
return -ENOMEM;
}
INIT_WORK(&dwc->bh_work, dwc3_bh_work);
pm_runtime_no_callbacks(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@ -1284,6 +1311,7 @@ err0:
* memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
destroy_workqueue(dwc->dwc_wq);
return ret;
}
@ -1313,6 +1341,8 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
destroy_workqueue(dwc->dwc_wq);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);

View file

@ -66,6 +66,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB (1 << 31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@ -149,6 +150,8 @@
#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
@ -433,6 +436,11 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
#define DWC3_DEV_IMOD_COUNT_SHIFT 16
#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16)
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
/* Structures */
struct dwc3_trb;
@ -837,6 +845,8 @@ struct dwc3_scratchpad_array {
* @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
* @wait_linkstate: waitqueue for waiting LINK to move into required state
* @vbus_draw: current to be drawn from USB
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@ -920,6 +930,7 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
/*
@ -928,6 +939,7 @@ struct dwc3 {
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@ -1008,6 +1020,11 @@ struct dwc3 {
bool b_suspend;
unsigned vbus_draw;
u16 imod_interval;
struct workqueue_struct *dwc_wq;
struct work_struct bh_work;
/* IRQ timing statistics */
int irq;
unsigned long irq_cnt;
@ -1175,6 +1192,20 @@ struct dwc3_gadget_ep_cmd_params {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
/* check whether we are on the DWC_usb3 core */
static inline bool dwc3_is_usb3(struct dwc3 *dwc)
{
return !(dwc->revision & DWC3_REVISION_IS_DWC31);
}
/* check whether we are on the DWC_usb31 core */
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
{
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
}
bool dwc3_has_imod(struct dwc3 *dwc);
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);

View file

@ -2053,6 +2053,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
if (dwc->irq)
disable_irq(dwc->irq);
if (work_busy(&dwc->bh_work))
dbg_event(0xFF, "pend evt", 0);
/* disable power event irq, hs and ss phy irq is used as wake up src */
disable_irq(mdwc->pwr_event_irq);

View file

@ -2011,6 +2011,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
int ret = 0;
u32 reg;
/*
* Use IMOD if enabled via dwc->imod_interval. Otherwise, if
* the core supports IMOD, disable it.
*/
if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
} else if (dwc3_has_imod(dwc)) {
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
}
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@ -3362,8 +3373,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
*/
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
left -= 4;
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
}
dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] += (evt->count / 4);
@ -3377,9 +3386,22 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
if (dwc->imod_interval)
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf),
DWC3_GEVNTCOUNT_EHB);
return ret;
}
void dwc3_bh_work(struct work_struct *w)
{
struct dwc3 *dwc = container_of(w, struct dwc3, bh_work);
pm_runtime_get_sync(dwc->dev);
dwc3_thread_interrupt(dwc->irq, dwc);
pm_runtime_put(dwc->dev);
}
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
@ -3434,6 +3456,8 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
reg |= DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), count);
return IRQ_WAKE_THREAD;
}
@ -3469,7 +3493,7 @@ irqreturn_t dwc3_interrupt(int irq, void *_dwc)
dwc->irq_dbg_index = (dwc->irq_dbg_index + 1) % MAX_INTR_STATS;
if (ret == IRQ_WAKE_THREAD)
dwc3_thread_interrupt(dwc->irq, dwc);
queue_work(dwc->dwc_wq, &dwc->bh_work);
return IRQ_HANDLED;
}

View file

@ -105,6 +105,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
irqreturn_t dwc3_interrupt(int irq, void *_dwc);
void dwc3_bh_work(struct work_struct *w);
static inline dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
struct dwc3_trb *trb)

View file

@ -245,7 +245,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}
ret = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_usb_phy;
@ -254,7 +254,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto dealloc_usb2_hcd;