From: Dinh Nguyen <dinguyen@xxxxxxxxxx> Make the dwc2_handle_common_intr() function handle all interrupts for the DWC2 and s3c_hsotg driver. Signed-off-by: Dinh Nguyen <dinguyen@xxxxxxxxxx> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> Cc: Paul Zimmerman <paulz@xxxxxxxxxxxx> Cc: Felipe Balbi <balbi@xxxxxx> Cc: Ben Dooks <ben-linux@xxxxxxxxx> Cc: Matt Porter <mporter@xxxxxxxxxx> Cc: Kukjin Kim <kgene.kim@xxxxxxxxxxx> Cc: Stephen Warren <swarren@xxxxxxxxxxxxx> Cc: Matthijs Kooijman <matthijs@xxxxxxxx> Cc: Jingoo Han <jg1.han@xxxxxxxxxxx> Cc: Sachin Kamat <sachin.kamat@xxxxxxxxxx> Cc: Robert Baldyga <r.baldyga@xxxxxxxxxxx> --- drivers/usb/dwc2/Makefile | 13 +-- drivers/usb/dwc2/core.h | 16 ++++ drivers/usb/dwc2/core_intr.c | 108 ++++++++++++++++++++- drivers/usb/dwc2/s3c-hsotg.c | 212 ++---------------------------------------- 4 files changed, 133 insertions(+), 216 deletions(-) diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index c4b2132..2cbbabf 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -1,23 +1,12 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG -ifeq ($(CONFIG_USB_DWC2_HOST),y) obj-$(CONFIG_USB_DWC2) += dwc2.o dwc2-y += core.o core_intr.o dwc2-y += hcd.o hcd_intr.o dwc2-y += hcd_queue.o hcd_ddma.o endif -obj-$(CONFIG_USB_S3C_HSOTG) += s3c_hsotg.o -s3c_hsotg-y += s3c-hsotg.o - - -# NOTE: This driver at present only implements the Host mode -# of the controller. The existing s3c-hsotg driver supports -# Peripheral mode, but only for the Samsung S3C platforms. -# There are plans to merge the s3c-hsotg driver with this -# driver in the near future to create a dual-role driver. Once -# that is done, Host mode will become an optional feature that -# is selected with a config option. +obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_DWC2) += dwc2_pci.o diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 1dae260..dd5c496 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -227,6 +227,11 @@ do { \ } \ } while (0) +/* IRQ flags which will trigger a retry around the IRQ loop */ +#define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \ + GINTSTS_PTXFEMP | \ + GINTSTS_RXFLVL) + /* Device States */ enum dwc2_lx_state { DWC2_L0, /* On state */ @@ -945,5 +950,16 @@ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); /* Gadget externs */ extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); extern void s3c_hsotg_remove(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_epint(struct dwc2_hsotg *dwc2, unsigned int idx, + int dir_in); +extern void kill_all_requests(struct dwc2_hsotg *dwc2, + struct s3c_hsotg_ep *ep, int result, bool force); +extern void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_disable_gsint(struct dwc2_hsotg *dwc2, u32 ints); +extern void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *dwc2, bool periodic); +extern void s3c_hsotg_handle_rx(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_dump(struct dwc2_hsotg *dwc2); #endif /* __DWC2_CORE_H__ */ diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 7a26186..c25e36f 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; + call_gadget(hsotg, resume); } else { if (hsotg->lx_state != DWC2_L1) { u32 pcgcctl = readl(hsotg->regs + PCGCTL); @@ -394,6 +395,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + call_gadget(hsotg, suspend); } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); @@ -438,7 +440,7 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) gintsts, gintmsk); if (gahbcfg & GAHBCFG_GLBL_INTR_EN) - return gintsts & gintmsk & gintmsk_common; + return gintsts & gintmsk; else return 0; } @@ -461,6 +463,7 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) struct dwc2_hsotg *hsotg = dev; u32 gintsts; irqreturn_t retval = IRQ_NONE; + int retry_count = 8; if (!dwc2_is_controller_alive(hsotg)) { dev_warn(hsotg->dev, "Controller is dead\n"); @@ -468,11 +471,16 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) } spin_lock(&hsotg->lock); - +irq_retry: gintsts = dwc2_read_common_intr(hsotg); if (gintsts & ~GINTSTS_PRTINT) retval = IRQ_HANDLED; + if (gintsts & GINTSTS_ENUMDONE) { + writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); + s3c_hsotg_irq_enumdone(hsotg); + } + if (gintsts & GINTSTS_MODEMIS) dwc2_handle_mode_mismatch_intr(hsotg); if (gintsts & GINTSTS_OTGINT) @@ -488,6 +496,95 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) if (gintsts & GINTSTS_USBSUSP) dwc2_handle_usb_suspend_intr(hsotg); + if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { + u32 daint = readl(hsotg->regs + DAINT); + u32 daint_out = daint >> DAINT_OUTEP_SHIFT; + u32 daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + int ep; + + dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); + for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { + if (daint_out & 1) + s3c_hsotg_epint(hsotg, ep, 0); + } + + for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) { + if (daint_in & 1) + s3c_hsotg_epint(hsotg, ep, 1); + } + } + + if (gintsts & GINTSTS_USBRST) { + u32 usb_status = readl(hsotg->regs + GOTGCTL); + + dev_dbg(hsotg->dev, "%s: USBRST\n", __func__); + dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", + readl(hsotg->regs + GNPTXSTS)); + + writel(GINTSTS_USBRST, hsotg->regs + GINTSTS); + + if (usb_status & GOTGCTL_BSESVLD) { + if (time_after(jiffies, hsotg->s3c_hsotg->last_rst + + msecs_to_jiffies(200))) { + kill_all_requests(hsotg, &hsotg->eps[0], + -ECONNRESET, true); + s3c_hsotg_core_init(hsotg); + hsotg->s3c_hsotg->last_rst = jiffies; + } + } + } + + if (gintsts & GINTSTS_NPTXFEMP) { + dev_dbg(hsotg->dev, "NPTXFEMP\n"); + + /* + * Disable the interrupt to stop it happening again + * unless one of these endpoint routines decides that + * it needs re-enabling + */ + s3c_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP); + s3c_hsotg_irq_fifoempty(hsotg, false); + } + + if (gintsts & GINTSTS_PTXFEMP) { + dev_dbg(hsotg->dev, "PTXFEMP\n"); + + /* See note in GINTSTS_NPTxFEmp */ + s3c_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP); + s3c_hsotg_irq_fifoempty(hsotg, true); + } + + if (gintsts & GINTSTS_RXFLVL) { + /* + * note, since GINTSTS_RXFLVL doubles as FIFO-not-empty, + * we need to retry s3c_hsotg_handle_rx if this is still + * set. + */ + s3c_hsotg_handle_rx(hsotg); + } + + if (gintsts & GINTSTS_ERLYSUSP) { + dev_dbg(hsotg->dev, "GINTSTS_ERLYSUSP\n"); + writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS); + } + + /* + * these next two seem to crop-up occasionally causing the core + * to shutdown the USB transfer, so try clearing them and logging + * the occurrence. + */ + if (gintsts & GINTSTS_GOUTNAKEFF) { + dev_dbg(hsotg->dev, "GOUTNAKEFF triggered\n"); + writel(DCTL_CGOUTNAK, hsotg->regs + DCTL); + s3c_hsotg_dump(hsotg); + } + + if (gintsts & GINTSTS_GINNAKEFF) { + dev_dbg(hsotg->dev, "GINNAKEFF triggered\n"); + writel(DCTL_CGNPINNAK, hsotg->regs + DCTL); + s3c_hsotg_dump(hsotg); + } + if (gintsts & GINTSTS_PRTINT) { /* * The port interrupt occurs while in device mode with HPRT0 @@ -503,6 +600,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) } } + /* + * if we've had fifo events, we should try and go around the + * loop again to see if there's any point in returning yet. + */ + if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) + goto irq_retry; + spin_unlock(&hsotg->lock); out: return retval; diff --git a/drivers/usb/dwc2/s3c-hsotg.c b/drivers/usb/dwc2/s3c-hsotg.c index 59a97ee..6c0aba8 100644 --- a/drivers/usb/dwc2/s3c-hsotg.c +++ b/drivers/usb/dwc2/s3c-hsotg.c @@ -65,9 +65,6 @@ static inline void __bic32(void __iomem *ptr, u32 val) writel(readl(ptr) & ~val, ptr); } -/* forward decleration of functions */ -static void s3c_hsotg_dump(struct dwc2_hsotg *dwc2); - /** * using_dma - return the DMA status of the driver. * @hsotg: The driver state. @@ -115,7 +112,7 @@ static void s3c_hsotg_en_gsint(struct dwc2_hsotg *dwc2, u32 ints) * @hsotg: The device state * @ints: A bitmask of the interrupts to enable */ -static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *dwc2, u32 ints) +void s3c_hsotg_disable_gsint(struct dwc2_hsotg *dwc2, u32 ints) { u32 gsintmsk = readl(dwc2->regs + GINTMSK); u32 new_gsintmsk; @@ -1015,7 +1012,6 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *dwc2, } static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *dwc2); -static void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); /** * s3c_hsotg_process_control - process a control request @@ -1477,7 +1473,7 @@ static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *dwc2) * as the actual data should be sent to the memory directly and we turn * on the completion interrupts to get notifications of transfer completion. */ -static void s3c_hsotg_handle_rx(struct dwc2_hsotg *dwc2) +void s3c_hsotg_handle_rx(struct dwc2_hsotg *dwc2) { u32 grxstsr = readl(dwc2->regs + GRXSTSP); u32 epnum, status, size; @@ -1771,7 +1767,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *dwc2, * * Process and clear any interrupt pending for an individual endpoint */ -static void s3c_hsotg_epint(struct dwc2_hsotg *dwc2, unsigned int idx, +void s3c_hsotg_epint(struct dwc2_hsotg *dwc2, unsigned int idx, int dir_in) { struct s3c_hsotg_ep *hs_ep = &dwc2->eps[idx]; @@ -1896,7 +1892,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *dwc2, unsigned int idx, * Handle updating the device settings after the enumeration phase has * been completed. */ -static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *dwc2) +void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *dwc2) { u32 dsts = readl(dwc2->regs + DSTS); int ep0_mps = 0, ep_mps; @@ -1973,7 +1969,7 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *dwc2) * Go through the requests on the given endpoint and mark them * completed with the given result code. */ -static void kill_all_requests(struct dwc2_hsotg *dwc2, +void kill_all_requests(struct dwc2_hsotg *dwc2, struct s3c_hsotg_ep *ep, int result, bool force) { @@ -2004,7 +2000,7 @@ static void kill_all_requests(struct dwc2_hsotg *dwc2, * transactions and signal the gadget driver that this * has happened. */ -static void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) +void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) { unsigned ep; @@ -2019,7 +2015,7 @@ static void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) * @hsotg: The device state: * @periodic: True if this is a periodic FIFO interrupt */ -static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *dwc2, bool periodic) +void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *dwc2, bool periodic) { struct s3c_hsotg_ep *ep; int epno, ret; @@ -2101,7 +2097,7 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *dwc2) * * Issue a soft reset to the core, and await the core finishing it. */ -static void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) +void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) { s3c_hsotg_corereset(dwc2); @@ -2229,194 +2225,6 @@ static void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) } /** - * s3c_hsotg_irq - handle device interrupt - * @irq: The IRQ number triggered - * @pw: The pw value when registered the handler. - */ -static irqreturn_t s3c_hsotg_irq(int irq, void *pw) -{ - struct dwc2_hsotg *dwc2 = pw; - int retry_count = 8; - u32 gintsts; - u32 gintmsk; - - spin_lock(&dwc2->lock); -irq_retry: - gintsts = readl(dwc2->regs + GINTSTS); - gintmsk = readl(dwc2->regs + GINTMSK); - - dev_dbg(dwc2->dev, "%s: %08x %08x (%08x) retry %d\n", - __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count); - - gintsts &= gintmsk; - - if (gintsts & GINTSTS_OTGINT) { - u32 otgint = readl(dwc2->regs + GOTGINT); - - dev_info(dwc2->dev, "OTGInt: %08x\n", otgint); - - writel(otgint, dwc2->regs + GOTGINT); - } - - if (gintsts & GINTSTS_SESSREQINT) { - dev_dbg(dwc2->dev, "%s: SessReqInt\n", __func__); - writel(GINTSTS_SESSREQINT, dwc2->regs + GINTSTS); - } - - if (gintsts & GINTSTS_ENUMDONE) { - writel(GINTSTS_ENUMDONE, dwc2->regs + GINTSTS); - - s3c_hsotg_irq_enumdone(dwc2); - } - - if (gintsts & GINTSTS_CONIDSTSCHNG) { - dev_dbg(dwc2->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n", - readl(dwc2->regs + DSTS), - readl(dwc2->regs + GOTGCTL)); - - writel(GINTSTS_CONIDSTSCHNG, dwc2->regs + GINTSTS); - } - - if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { - u32 daint = readl(dwc2->regs + DAINT); - u32 daintmsk = readl(dwc2->regs + DAINTMSK); - u32 daint_out, daint_in; - int ep; - - daint &= daintmsk; - daint_out = daint >> DAINT_OUTEP_SHIFT; - daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); - - dev_dbg(dwc2->dev, "%s: daint=%08x\n", __func__, daint); - - for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { - if (daint_out & 1) - s3c_hsotg_epint(dwc2, ep, 0); - } - - for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) { - if (daint_in & 1) - s3c_hsotg_epint(dwc2, ep, 1); - } - } - - if (gintsts & GINTSTS_USBRST) { - - u32 usb_status = readl(dwc2->regs + GOTGCTL); - - dev_info(dwc2->dev, "%s: USBRst\n", __func__); - dev_dbg(dwc2->dev, "GNPTXSTS=%08x\n", - readl(dwc2->regs + GNPTXSTS)); - - writel(GINTSTS_USBRST, dwc2->regs + GINTSTS); - - if (usb_status & GOTGCTL_BSESVLD) { - if (time_after(jiffies, dwc2->s3c_hsotg->last_rst + - msecs_to_jiffies(200))) { - - kill_all_requests(dwc2, &dwc2->eps[0], - -ECONNRESET, true); - - s3c_hsotg_core_init(dwc2); - dwc2->s3c_hsotg->last_rst = jiffies; - } - } - } - - /* check both FIFOs */ - - if (gintsts & GINTSTS_NPTXFEMP) { - dev_dbg(dwc2->dev, "NPTxFEmp\n"); - - /* - * Disable the interrupt to stop it happening again - * unless one of these endpoint routines decides that - * it needs re-enabling - */ - - s3c_hsotg_disable_gsint(dwc2, GINTSTS_NPTXFEMP); - s3c_hsotg_irq_fifoempty(dwc2, false); - } - - if (gintsts & GINTSTS_PTXFEMP) { - dev_dbg(dwc2->dev, "PTxFEmp\n"); - - /* See note in GINTSTS_NPTxFEmp */ - - s3c_hsotg_disable_gsint(dwc2, GINTSTS_PTXFEMP); - s3c_hsotg_irq_fifoempty(dwc2, true); - } - - if (gintsts & GINTSTS_RXFLVL) { - /* - * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, - * we need to retry s3c_hsotg_handle_rx if this is still - * set. - */ - - s3c_hsotg_handle_rx(dwc2); - } - - if (gintsts & GINTSTS_MODEMIS) { - dev_warn(dwc2->dev, "warning, mode mismatch triggered\n"); - writel(GINTSTS_MODEMIS, dwc2->regs + GINTSTS); - } - - if (gintsts & GINTSTS_USBSUSP) { - dev_info(dwc2->dev, "GINTSTS_USBSusp\n"); - writel(GINTSTS_USBSUSP, dwc2->regs + GINTSTS); - - call_gadget(dwc2, suspend); - } - - if (gintsts & GINTSTS_WKUPINT) { - dev_info(dwc2->dev, "GINTSTS_WkUpIn\n"); - writel(GINTSTS_WKUPINT, dwc2->regs + GINTSTS); - - call_gadget(dwc2, resume); - } - - if (gintsts & GINTSTS_ERLYSUSP) { - dev_dbg(dwc2->dev, "GINTSTS_ErlySusp\n"); - writel(GINTSTS_ERLYSUSP, dwc2->regs + GINTSTS); - } - - /* - * these next two seem to crop-up occasionally causing the core - * to shutdown the USB transfer, so try clearing them and logging - * the occurrence. - */ - - if (gintsts & GINTSTS_GOUTNAKEFF) { - dev_info(dwc2->dev, "GOUTNakEff triggered\n"); - - writel(DCTL_CGOUTNAK, dwc2->regs + DCTL); - - s3c_hsotg_dump(dwc2); - } - - if (gintsts & GINTSTS_GINNAKEFF) { - dev_info(dwc2->dev, "GINNakEff triggered\n"); - - writel(DCTL_CGNPINNAK, dwc2->regs + DCTL); - - s3c_hsotg_dump(dwc2); - } - - /* - * if we've had fifo events, we should try and go around the - * loop again to see if there's any point in returning yet. - */ - - if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) - goto irq_retry; - - spin_unlock(&dwc2->lock); - - return IRQ_HANDLED; -} - -/** * s3c_hsotg_ep_enable - enable the given endpoint * @ep: The USB endpint to configure * @desc: The USB endpoint descriptor to configure with. @@ -3037,7 +2845,7 @@ static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *dwc2) * s3c_hsotg_dump - dump state of the udc * @param: The device state */ -static void s3c_hsotg_dump(struct dwc2_hsotg *dwc2) +void s3c_hsotg_dump(struct dwc2_hsotg *dwc2) { #ifdef DEBUG struct device *dev = dwc2->dev; @@ -3404,7 +3212,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *dwc2, int irq) spin_lock_init(&dwc2->lock); - ret = devm_request_irq(dwc2->dev, irq, s3c_hsotg_irq, 0, + ret = devm_request_irq(dwc2->dev, irq, dwc2_handle_common_intr, 0, dev_name(dev), dwc2); if (ret < 0) { dev_err(dev, "cannot claim IRQ\n"); -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html