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

Commit dcf83e7c authored by Hemant Kumar's avatar Hemant Kumar
Browse files

usb: xhci-msm-hsic: Do not disable power event irq



Commit 877e12 (usb: xhci-msm-hsic: Handle power event irq
properly) only enabled power event irq during controller
suspend. HSIC controller generates power event irq for
IN_L2_IRQ as a result of HSIC bus suspend. This interrupt
remains un-acknowledged to controller if host initiated
resume causes bus resume and controller suspend is aborted.
HSIC controller becomes unresponsive when bus is suspended
next time and power event irq is generated for IN_L2_IRQ.
Hence always acknowledge IN_L2_IRQ to controller by leaving
power event irq enabled all the time and block on IN_L2_IRQ
before finishing HSIC bus suspend. Also register for
OUT_L2_IRQ and acknowledge it as per the hardware programming
guide recommendation.

CRs-Fixed: 609856
Change-Id: Ifaa3113da1b3f256c9d9b3db130918afc15d266f
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 219364c5
Loading
Loading
Loading
Loading
+56 −25
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#define MSM_HSIC_BASE			(hcd->regs)

#define MSM_HSIC_PORTSC			(MSM_HSIC_BASE + 0x0420)
#define MSM_HSIC_PORTLI			(MSM_HSIC_BASE + 0x0428)
#define MSM_HSIC_GCTL			(MSM_HSIC_BASE + 0xc110)
#define MSM_HSIC_GUSB2PHYCFG		(MSM_HSIC_BASE + 0xc200)
#define MSM_HSIC_GUSB2PHYACC		(MSM_HSIC_BASE + 0xc280)
@@ -68,9 +69,11 @@

/* PWR_EVENT_IRQ_STAT reg */
#define LPM_IN_L2_IRQ_STAT	BIT(4)
#define LPM_OUT_L2_IRQ_STAT	BIT(5)

/* PWR_EVENT_IRQ_MASK reg */
#define LPM_IN_L2_IRQ_MASK	BIT(4)
#define LPM_OUT_L2_IRQ_MASK	BIT(5)

#define PHY_LPM_WAIT_TIMEOUT_MS	5000
#define ULPI_IO_TIMEOUT_USECS	(10 * 1000)
@@ -545,14 +548,21 @@ static irqreturn_t mxhci_hsic_pwr_event_irq(int irq, void *data)

	stat = readl_relaxed(MSM_HSIC_PWR_EVENT_IRQ_STAT);
	if (stat & LPM_IN_L2_IRQ_STAT) {
		xhci_dbg_log_event(&dbg_hsic, NULL, "LPM_IN_L2_IRQ", 0);
		xhci_dbg_log_event(&dbg_hsic, NULL, "LPM_IN_L2_IRQ", stat);
		writel_relaxed(stat, MSM_HSIC_PWR_EVENT_IRQ_STAT);

		/* Ensure irq is acked before turning off clks for lpm */
		mb();
		complete(&mxhci->phy_in_lpm);
	} else if (stat & LPM_OUT_L2_IRQ_STAT) {
		xhci_dbg_log_event(&dbg_hsic, NULL, "LPM_OUT_L2_IRQ", stat);
		writel_relaxed(stat, MSM_HSIC_PWR_EVENT_IRQ_STAT);

		/* ensure to ack the OUT_L2_IRQ */
		mb();
	} else {
		xhci_dbg_log_event(&dbg_hsic, NULL, "spurious pwr evt irq", 0);
		xhci_dbg_log_event(&dbg_hsic, NULL, "spurious pwr evt irq",
				stat);
		dev_info(mxhci->dev,
			"%s: spurious interrupt.pwr_event_irq stat = %x\n",
			__func__, stat);
@@ -566,9 +576,11 @@ static int mxhci_hsic_bus_suspend(struct usb_hcd *hcd)
	struct mxhci_hsic_hcd *mxhci = hcd_to_hsic(hcd->primary_hcd);
	int ret;

	if (!usb_hcd_is_primary_hcd(hcd))
		return 0;

	/* don't miss connect bus state from peripheral for USB 2.0 root hub */
	if (usb_hcd_is_primary_hcd(hcd) &&
			!(readl_relaxed(MSM_HSIC_PORTSC) & PORT_PE)) {
	if (!(readl_relaxed(MSM_HSIC_PORTSC) & PORT_PE)) {
		xhci_dbg_log_event(&dbg_hsic, NULL,
				"port is not enabled; skip suspend", 0);
		dev_dbg(mxhci->dev, "%s: port is not enabled; skip suspend\n",
@@ -576,10 +588,44 @@ static int mxhci_hsic_bus_suspend(struct usb_hcd *hcd)
		return -EAGAIN;
	}

	init_completion(&mxhci->phy_in_lpm);

	ret = xhci_bus_suspend(hcd);
	if (ret)
		return ret;

	xhci_dbg_log_event(&dbg_hsic, NULL, "Suspend RH", ret);
	/* make sure HSIC phy is in LPM */
	ret = wait_for_completion_timeout(&mxhci->phy_in_lpm,
			msecs_to_jiffies(PHY_LPM_WAIT_TIMEOUT_MS));
	if (!ret) {
		dev_dbg(mxhci->dev, "IN_L2_IRQ timeout\n");
		xhci_dbg_log_event(&dbg_hsic, NULL, "IN_L2_IRQ timeout",
			readl_relaxed(MSM_HSIC_PWR_EVENT_IRQ_STAT));
		xhci_dbg_log_event(&dbg_hsic, NULL, "PORTSC",
				readl_relaxed(MSM_HSIC_PORTSC));
		xhci_dbg_log_event(&dbg_hsic, NULL, "PORTLI",
				readl_relaxed(MSM_HSIC_PORTLI));
		panic("fail to get IN_L2 power event irq");
	}

	xhci_dbg_log_event(&dbg_hsic, NULL, "Suspend RH",
			readl_relaxed(MSM_HSIC_PORTSC));
	xhci_dbg_log_event(&dbg_hsic, NULL, "IN_L2_IRQ_STAT",
			readl_relaxed(MSM_HSIC_PWR_EVENT_IRQ_STAT));
	return 0;
}

static int mxhci_hsic_bus_resume(struct usb_hcd *hcd)
{
	int ret;

	if (!usb_hcd_is_primary_hcd(hcd))
		return 0;

	ret = xhci_bus_resume(hcd);

	xhci_dbg_log_event(&dbg_hsic, NULL, "Resume RH",
			readl_relaxed(MSM_HSIC_PORTSC));
	return ret;
}

@@ -603,22 +649,8 @@ static int mxhci_hsic_suspend(struct mxhci_hsic_hcd *mxhci)
		return -EBUSY;
	}

	init_completion(&mxhci->phy_in_lpm);
	enable_irq(mxhci->pwr_event_irq);

	/* make sure HSIC phy is in LPM */
	ret = wait_for_completion_timeout(
			&mxhci->phy_in_lpm,
			msecs_to_jiffies(PHY_LPM_WAIT_TIMEOUT_MS));
	if (!ret) {
		dev_err(mxhci->dev, "HSIC phy failed to enter lpm\n");
		xhci_dbg_log_event(&dbg_hsic, NULL, "Phy suspend failure", 0);
		enable_irq(hcd->irq);
		disable_irq(mxhci->pwr_event_irq);
		return -EBUSY;
	}

	disable_irq(mxhci->pwr_event_irq);
	xhci_dbg_log_event(&dbg_hsic, NULL, "Read PWR_EVENT_IRQ_STAT",
			readl_relaxed(MSM_HSIC_PWR_EVENT_IRQ_STAT));

	/* Don't poll the roothubs after bus suspend. */
	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
@@ -778,7 +810,7 @@ static struct hc_driver mxhci_hsic_hc_driver = {
	.hub_control =		xhci_hub_control,
	.hub_status_data =	xhci_hub_status_data,
	.bus_suspend =		mxhci_hsic_bus_suspend,
	.bus_resume =		xhci_bus_resume,
	.bus_resume =		mxhci_hsic_bus_resume,

	/* dbg log support */
	.log_urb =		xhci_hsic_log_urb,
@@ -1034,7 +1066,8 @@ static int mxhci_hsic_probe(struct platform_device *pdev)
	}

	/* enable pwr event irq for LPM_IN_L2_IRQ */
	writel_relaxed(LPM_IN_L2_IRQ_MASK, MSM_HSIC_PWR_EVNT_IRQ_MASK);
	writel_relaxed(LPM_IN_L2_IRQ_MASK | LPM_OUT_L2_IRQ_MASK,
			MSM_HSIC_PWR_EVNT_IRQ_MASK);

	mxhci->wakeup_irq = platform_get_irq_byname(pdev, "wakeup_irq");
	if (mxhci->wakeup_irq < 0) {
@@ -1091,8 +1124,6 @@ static int mxhci_hsic_probe(struct platform_device *pdev)
		goto remove_usb3_hcd;
	}

	/* enable irq only when entering lpm */
	irq_set_status_flags(mxhci->pwr_event_irq, IRQ_NOAUTOEN);
	ret = devm_request_irq(&pdev->dev, mxhci->pwr_event_irq,
				mxhci_hsic_pwr_event_irq,
				0, "mxhci_hsic_pwr_evt", mxhci);