Merge "usb: dwc3: Use pwr_evt_irq to wakeup if dp/dm directly connected to GIC"
This commit is contained in:
commit
15adff880f
1 changed files with 79 additions and 24 deletions
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -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)
|
||||
irq_mask |= (PWR_EVNT_POWERDOWN_OUT_P3_MASK |
|
||||
PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK);
|
||||
else
|
||||
irq_mask &= ~(PWR_EVNT_POWERDOWN_OUT_P3_MASK |
|
||||
PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK);
|
||||
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 {
|
||||
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,11 +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);
|
||||
enable_irq(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq);
|
||||
} 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();
|
||||
|
||||
|
@ -2655,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);
|
||||
|
@ -2669,6 +2704,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool enable_wakeup)
|
|||
mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
|
||||
}
|
||||
|
||||
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");
|
||||
dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm));
|
||||
|
||||
|
@ -2768,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 */
|
||||
|
@ -2821,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);
|
||||
|
@ -3081,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",
|
||||
|
@ -3910,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
|
||||
|
|
Loading…
Reference in a new issue