Loading drivers/usb/dwc3/dwc3-msm.c +79 −24 Original line number Diff line number Diff line // 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> Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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); Loading @@ -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)); Loading Loading @@ -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 */ Loading Loading @@ -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); Loading Loading @@ -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", Loading Loading @@ -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 Loading
drivers/usb/dwc3/dwc3-msm.c +79 −24 Original line number Diff line number Diff line // 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> Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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); Loading @@ -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)); Loading Loading @@ -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 */ Loading Loading @@ -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); Loading Loading @@ -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", Loading Loading @@ -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