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

Commit 31119d1d authored by Rohith Kollalsi's avatar Rohith Kollalsi Committed by Udipto Goswami
Browse files

usb: dwc3: Use pwr_evt_irq to wakeup if dp/dm directly connected to GIC



Currently support to enable/disable dplus hsphy irq and dminus hsphy
irq in case of resume/suspend is present only for pdc interrupts.
This support should be present even for non pdc interrupts if the
platform uses snps usb hs phy. Absence of this support leads to
dp/dm hsphy irq not being fired and usb stuck in lpm in host mode
eventhough there is a disconnect or remote wakeup issued from
hs/fs device connected to it. Hence add support to enable/disable
dp hsphy irq and dm hsphy irq for platforms that use snps hs phy.

On platforms which use snps phy and mpm interrupts, dp/dm lines are
directly connected to gic when cpu is active, mpm is involved only
when cpu is not active. Now in cpu active case, in host mode if usb
goes to lpm with hs/fs/ls device connected and unplug/remote wakeup
is issued, dp falling edge is monitored in software to detect that.
But GIC is not capable of detecting a falling edge. Hence this
change adds support to program power event irq to wakeup the system
in this case. Note that GIC takes care of dp rising edge wakeup
events like usb connect over otg when usb is in lpm and in host
mode. Only in case where dp falling edge is monitored i.e in case
of hs/fs/ls device connect and usb enteres lpm, then power event
irq is used to detect wakeup event as GIC cannot detect falling
edge.

Change-Id: I085b86c7df52daeedfb35cb71657c5163c2d5aaa
Signed-off-by: default avatarRohith Kollalsi <rkollals@codeaurora.org>
parent 062e152d
Loading
Loading
Loading
Loading
+76 −24
Original line number Diff line number Diff line
@@ -318,11 +318,15 @@ struct dwc3_msm {
	bool			suspend;
	bool			use_pdc_interrupts;
	enum dwc3_id_state	id_state;
	bool			use_pwr_event_for_wakeup;
	unsigned long		use_pwr_event_for_wakeup;
#define PWR_EVENT_SS_WAKEUP		BIT(0)
#define PWR_EVENT_HS_WAKEUP		BIT(1)

	unsigned long		lpm_flags;
#define MDWC3_SS_PHY_SUSPEND		BIT(0)
#define MDWC3_ASYNC_IRQ_WAKE_CAPABILITY	BIT(1)
#define MDWC3_POWER_COLLAPSE		BIT(2)
#define MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP BIT(3)

	struct notifier_block	usbdev_nb;
	bool			hc_died;
@@ -2373,7 +2377,7 @@ static void configure_usb_wakeup_interrupt(struct dwc3_msm *mdwc,
	}
}

static void enable_usb_pdc_interrupt(struct dwc3_msm *mdwc, bool enable)
static void configure_usb_wakeup_interrupts(struct dwc3_msm *mdwc, bool enable)
{
	if (!enable)
		goto disable_usb_irq;
@@ -2429,7 +2433,7 @@ static void configure_nonpdc_usb_interrupt(struct dwc3_msm *mdwc,
	}
}

static void dwc3_msm_set_ss_pwr_events(struct dwc3_msm *mdwc, bool on)
static void dwc3_msm_set_pwr_events(struct dwc3_msm *mdwc, bool on)
{
	u32 irq_mask, irq_stat;

@@ -2440,12 +2444,28 @@ static void dwc3_msm_set_ss_pwr_events(struct dwc3_msm *mdwc, bool on)

	irq_mask = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_MASK_REG);

	if (on)
	if (on) {
	       /*
		* In case of platforms which use mpm interrupts, in case where
		* suspend happens with a hs/fs/ls device connected in host mode
		* DP/DM falling edge will be monitored, but gic doesn't have
		* capability to detect falling edge. So program power event irq
		* to notify exit from lpm in such case.
		*/
		if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_HS_WAKEUP)
			irq_mask |= PWR_EVNT_LPM_OUT_L2_MASK;
		if ((mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP)
				&& !(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND))
			irq_mask |= (PWR_EVNT_POWERDOWN_OUT_P3_MASK |
				PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK);
	else
	} else {
		if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_HS_WAKEUP)
			irq_mask &= ~PWR_EVNT_LPM_OUT_L2_MASK;
		if ((mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP)
				&& !(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND))
			irq_mask &= ~(PWR_EVNT_POWERDOWN_OUT_P3_MASK |
				PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK);
	}

	dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_MASK_REG, irq_mask);
}
@@ -2576,7 +2596,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool enable_wakeup)
		((mdwc->hs_phy->flags & (PHY_HSFS_MODE | PHY_LS_MODE)) &&
			!dwc3_msm_is_superspeed(mdwc)));
	can_suspend_ssphy = dwc->maximum_speed >= USB_SPEED_SUPER &&
			(!mdwc->use_pwr_event_for_wakeup || no_active_ss);
			(!(mdwc->use_pwr_event_for_wakeup &
				PWR_EVENT_SS_WAKEUP) || no_active_ss ||
							!enable_wakeup);
	/* Suspend SS PHY */
	if (can_suspend_ssphy) {
		if (mdwc->in_host_mode) {
@@ -2590,10 +2612,21 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool enable_wakeup)
			mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE;
		usb_phy_set_suspend(mdwc->ss_phy, 1);
		mdwc->lpm_flags |= MDWC3_SS_PHY_SUSPEND;
	} else if (mdwc->use_pwr_event_for_wakeup) {
		dwc3_msm_set_ss_pwr_events(mdwc, true);
	} else if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP) {
		mdwc->lpm_flags |= MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP;
	}

	/*
	 * When operating in HS host mode, check if pwr event IRQ is
	 * required for wakeup.
	 */
	if (mdwc->in_host_mode && (mdwc->use_pwr_event_for_wakeup
						& PWR_EVENT_HS_WAKEUP))
		mdwc->lpm_flags |= MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP;

	if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP)
		dwc3_msm_set_pwr_events(mdwc, true);

	/* make sure above writes are completed before turning off clocks */
	wmb();

@@ -2654,11 +2687,14 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool enable_wakeup)
	/*
	 * with DCP or during cable disconnect, we dont require wakeup
	 * using HS_PHY_IRQ or SS_PHY_IRQ. Hence enable wakeup only in
	 * case of host bus suspend and device bus suspend.
	 * case of host bus suspend and device bus suspend. Also in
	 * case of platforms with mpm interrupts and snps phy, enable
	 * dpse hsphy irq and dmse hsphy irq as done for pdc interrupts.
	 */
	if (!(mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) && enable_wakeup) {
		if (mdwc->use_pdc_interrupts) {
			enable_usb_pdc_interrupt(mdwc, true);
		if (mdwc->use_pdc_interrupts ||
				!mdwc->wakeup_irq[HS_PHY_IRQ].irq) {
			configure_usb_wakeup_interrupts(mdwc, true);
		} else {
			uirq = &mdwc->wakeup_irq[HS_PHY_IRQ];
			configure_nonpdc_usb_interrupt(mdwc, uirq, true);
@@ -2668,8 +2704,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool enable_wakeup)
		mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
	}

	if (mdwc->use_pwr_event_for_wakeup &&
			!(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND))
	if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP)
		enable_irq(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq);

	dev_info(mdwc->dev, "DWC3 in low power mode\n");
@@ -2771,10 +2806,10 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
	 * Disable any wakeup events that were enabled if pwr_event_irq
	 * is used as wakeup interrupt.
	 */
	if (mdwc->use_pwr_event_for_wakeup &&
			!(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND)) {
	if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP) {
		disable_irq_nosync(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq);
		dwc3_msm_set_ss_pwr_events(mdwc, false);
		dwc3_msm_set_pwr_events(mdwc, false);
		mdwc->lpm_flags &= ~MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP;
	}

	/* Resume SS PHY */
@@ -2824,8 +2859,9 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)

	/* Disable wakeup capable for HS_PHY IRQ & SS_PHY_IRQ if enabled */
	if (mdwc->lpm_flags & MDWC3_ASYNC_IRQ_WAKE_CAPABILITY) {
		if (mdwc->use_pdc_interrupts) {
			enable_usb_pdc_interrupt(mdwc, false);
		if (mdwc->use_pdc_interrupts ||
			!mdwc->wakeup_irq[HS_PHY_IRQ].irq) {
			configure_usb_wakeup_interrupts(mdwc, false);
		} else {
			uirq = &mdwc->wakeup_irq[HS_PHY_IRQ];
			configure_nonpdc_usb_interrupt(mdwc, uirq, false);
@@ -3084,6 +3120,13 @@ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc)
		irq_clear |= PWR_EVNT_LPM_OUT_L1_MASK;
	}

	/* Handle exit from L2 events */
	if (irq_stat & PWR_EVNT_LPM_OUT_L2_MASK) {
		dev_dbg(mdwc->dev, "%s: handling PWR_EVNT_LPM_OUT_L2_MASK\n",
				__func__);
		irq_stat &= ~PWR_EVNT_LPM_OUT_L2_MASK;
		irq_clear |= PWR_EVNT_LPM_OUT_L2_MASK;
	}
	/* Unhandled events */
	if (irq_stat)
		dev_dbg(mdwc->dev, "%s: unexpected PWR_EVNT, irq_stat=%X\n",
@@ -3913,8 +3956,17 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	 * On platforms with SS PHY that do not support ss_phy_irq for wakeup
	 * events, use pwr_event_irq for wakeup events in superspeed mode.
	 */
	mdwc->use_pwr_event_for_wakeup = dwc->maximum_speed >= USB_SPEED_SUPER
					&& !mdwc->wakeup_irq[SS_PHY_IRQ].irq;
	if (dwc->maximum_speed >= USB_SPEED_SUPER
			&& !mdwc->wakeup_irq[SS_PHY_IRQ].irq)
		mdwc->use_pwr_event_for_wakeup |= PWR_EVENT_SS_WAKEUP;

	/*
	 * On platforms with mpm interrupts and snps phy, when operating in
	 * HS host mode use power event irq for wakeup events as GIC is not
	 * capable to detect falling edge of dp/dm hsphy irq.
	 */
	if (!mdwc->use_pdc_interrupts && !mdwc->wakeup_irq[HS_PHY_IRQ].irq)
		mdwc->use_pwr_event_for_wakeup |= PWR_EVENT_HS_WAKEUP;

	/*
	 * Clocks and regulators will not be turned on until the first time