Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec053b1f authored by Jack Pham's avatar Jack Pham
Browse files

usb: dwc3: Stop processing more events after erratic error



In rare cases if the controller is unable to communicate with the
PHY it will trigger an ERRATIC ERROR event, upon which it is
recommended that the controller be reset. Currently the reset
is scheduled to be performed in a workqueue, which relinquishes
the dwc3_interrupt thread to continue to process the event buffer.
Meanwhile if the reset runs concurrently, it would disable the core
clock, which would result in unclocked I/O access if the gadget
interrupt handling continues to run. Fix this by stopping the
interrupt thread and prevent further interrupts from reading DWC3
registers until the reset sequence has completed.

Change-Id: Ib9a2173867d2ce3c9dbc5f5cbff0dda554168b35
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent ca82d9c0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1096,6 +1096,9 @@ void dwc3_gadget_restart(struct dwc3 *dwc);
void dwc3_post_host_reset_core_init(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);

void dwc3_gadget_enable_irq(struct dwc3 *dwc);
void dwc3_gadget_disable_irq(struct dwc3 *dwc);

extern void dwc3_set_notifier(
		void (*notify) (struct dwc3 *dwc3, unsigned event));
extern int dwc3_notify_event(struct dwc3 *dwc3, unsigned event);
+7 −21
Original line number Diff line number Diff line
@@ -1123,7 +1123,7 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
	case DWC3_CONTROLLER_ERROR_EVENT:
		dev_info(mdwc->dev, "DWC3_CONTROLLER_ERROR_EVENT received\n");
		dwc3_msm_dump_phy_info(mdwc);
		dwc3_msm_write_reg(mdwc->base, DWC3_DEVTEN, 0);
		dwc3_gadget_disable_irq(dwc);
		/*
		 * schedule work for doing block reset for recovery from erratic
		 * error event.
@@ -1211,30 +1211,16 @@ static void dwc3_block_reset_usb_work(struct work_struct *w)
{
	struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm,
						usb_block_reset_work);
	u32 reg;
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	unsigned long flags;

	dev_dbg(mdwc->dev, "%s\n", __func__);

	dwc3_msm_block_reset(&mdwc->ext_xceiv, true);

	reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
			DWC3_DEVTEN_CMDCMPLTEN |
			DWC3_DEVTEN_ERRTICERREN |
			DWC3_DEVTEN_WKUPEVTEN |
			DWC3_DEVTEN_CONNECTDONEEN |
			DWC3_DEVTEN_USBRSTEN |
			DWC3_DEVTEN_DISCONNEVTEN);
	/*
	 * Enable SUSPENDEVENT(BIT:6) for version 230A and above
	 * else enable USB Link change event (BIT:3) for older version
	 */
	if (dwc3_msm_read_reg(mdwc->base, DWC3_GSNPSID) < DWC3_REVISION_230A)
		reg |= DWC3_DEVTEN_ULSTCNGEN;
	else
		reg |= DWC3_DEVTEN_SUSPEND;

	dwc3_msm_write_reg(mdwc->base, DWC3_DEVTEN, reg);

	dwc3_gadget_enable_irq(dwc);
	spin_lock_irqsave(&dwc->lock, flags);
	dwc->err_evt_seen = 0;
	spin_unlock_irqrestore(&dwc->lock, flags);
}

static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
+26 −4
Original line number Diff line number Diff line
@@ -1978,7 +1978,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
	return ret;
}

static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
void dwc3_gadget_enable_irq(struct dwc3 *dwc)
{
	u32			reg;

@@ -2003,7 +2003,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}

static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{
	/* mask all interrupts */
	dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
@@ -3270,8 +3270,6 @@ static void dwc3_dump_reg_info(struct dwc3 *dwc)
	dbg_print_reg("OCTL", dwc3_readl(dwc->regs, DWC3_OCTL));
	dbg_print_reg("OEVT", dwc3_readl(dwc->regs, DWC3_OEVT));
	dbg_print_reg("OSTS", dwc3_readl(dwc->regs, DWC3_OSTS));

	dwc3_notify_event(dwc, DWC3_CONTROLLER_ERROR_EVENT);
}

static void dwc3_gadget_interrupt(struct dwc3 *dwc,
@@ -3421,6 +3419,21 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)

			dwc3_process_event_entry(dwc, &event);

			if (dwc->err_evt_seen) {
				/*
				 * if erratic error, skip remaining events
				 * while controller undergoes reset
				 */
				evt->lpos = (evt->lpos + left) %
						DWC3_EVENT_BUFFERS_SIZE;
				dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i),
						left);
				if (dwc3_notify_event(dwc,
						DWC3_CONTROLLER_ERROR_EVENT))
					dwc->err_evt_seen = 0;
				break;
			}

			/*
			 * FIXME we wrap around correctly to the next entry as
			 * almost all entries are 4 bytes in size. There is one
@@ -3439,6 +3452,9 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
		evt->count = 0;
		evt->flags &= ~DWC3_EVENT_PENDING;
		ret = IRQ_HANDLED;

		if (dwc->err_evt_seen)
			break;
	}

	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -3488,6 +3504,12 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)

	spin_lock(&dwc->lock);

	if (dwc->err_evt_seen) {
		/* controller reset is still pending */
		spin_unlock(&dwc->lock);
		return IRQ_HANDLED;
	}

	for (i = 0; i < dwc->num_event_buffers; i++) {
		irqreturn_t status;