mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
usb: dwc3-msm: Add support for 2nd set of wakeup IRQs
Certain targets may have a dual port capable host controller. Add support for the PHY IRQs for the second port. Since the SSPHY autosuspend will be disabled for such controllers, remove the support of pwr_evnt_irq as a mandatory IRQ and allow SSPHY to go into P3 as a part of suspend sequence. Handle the logic gracefully to maintain single port controllers. Change-Id: I5a53d6e23f64bec7ae940d23923df310309a28da Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org> Signed-off-by: Harsh Agarwal <harshq@codeaurora.org>
This commit is contained in:
parent
3081acb9e8
commit
76c3eb34d1
2 changed files with 165 additions and 13 deletions
|
|
@ -170,7 +170,7 @@
|
|||
#define GEN2_U3_EXIT_RSP_RX_CLK_MASK GEN2_U3_EXIT_RSP_RX_CLK(0xff)
|
||||
#define GEN1_U3_EXIT_RSP_RX_CLK(n) (n)
|
||||
#define GEN1_U3_EXIT_RSP_RX_CLK_MASK GEN1_U3_EXIT_RSP_RX_CLK(0xff)
|
||||
#define DWC31_LINK_GDBGLTSSM 0xd050
|
||||
#define DWC31_LINK_GDBGLTSSM(n) (0xd050 + ((n) * 0x80))
|
||||
|
||||
/* DWC 3.1 Tx De-emphasis Registers */
|
||||
#define DWC31_LCSR_TX_DEEMPH(n) (0xd060 + ((n) * 0x80))
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
#define CGCTL_REG (QSCRATCH_REG_OFFSET + 0x28)
|
||||
#define PWR_EVNT_IRQ_STAT_REG (QSCRATCH_REG_OFFSET + 0x58)
|
||||
#define PWR_EVNT_IRQ_MASK_REG (QSCRATCH_REG_OFFSET + 0x5C)
|
||||
#define PWR_EVNT_IRQ_STAT_REG1 (QSCRATCH_REG_OFFSET + 0x1DC)
|
||||
|
||||
#define PWR_EVNT_POWERDOWN_IN_P3_MASK BIT(2)
|
||||
#define PWR_EVNT_POWERDOWN_OUT_P3_MASK BIT(3)
|
||||
|
|
@ -302,6 +303,9 @@ enum msm_usb_irq {
|
|||
DP_HS_PHY_IRQ,
|
||||
DM_HS_PHY_IRQ,
|
||||
SS_PHY_IRQ,
|
||||
DP_HS_PHY_IRQ_1,
|
||||
DM_HS_PHY_IRQ_1,
|
||||
SS_PHY_IRQ_1,
|
||||
USB_MAX_IRQ
|
||||
};
|
||||
|
||||
|
|
@ -364,6 +368,19 @@ static const struct usb_irq_info usb_irq_info[USB_MAX_IRQ] = {
|
|||
IRQF_EARLY_RESUME,
|
||||
false,
|
||||
},
|
||||
{ "dp_hs_phy_irq1",
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
false,
|
||||
},
|
||||
{ "dm_hs_phy_irq1",
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
false,
|
||||
},
|
||||
{ "ss_phy_irq1",
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQ_TYPE_LEVEL_HIGH |
|
||||
IRQF_EARLY_RESUME,
|
||||
false,
|
||||
},
|
||||
};
|
||||
|
||||
struct usb_irq {
|
||||
|
|
@ -2564,7 +2581,7 @@ static void dwc3_dis_sleep_mode(struct dwc3_msm *mdwc)
|
|||
static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
|
||||
u32 val;
|
||||
u32 val = 0, val1 = 0;
|
||||
int ret;
|
||||
|
||||
/* Configure AHB2PHY for one wait state read/write */
|
||||
|
|
@ -2616,15 +2633,27 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
|
|||
__func__, ret);
|
||||
|
||||
/* Get initial P3 status and enable IN_P3 event */
|
||||
if (dwc3_is_usb31(dwc))
|
||||
if (dwc3_is_usb31(dwc)) {
|
||||
val = dwc3_msm_read_reg_field(mdwc->base,
|
||||
DWC31_LINK_GDBGLTSSM,
|
||||
DWC31_LINK_GDBGLTSSM(0),
|
||||
DWC3_GDBGLTSSM_LINKSTATE_MASK);
|
||||
else
|
||||
if (mdwc->dual_port)
|
||||
val1 = dwc3_msm_read_reg_field(mdwc->base,
|
||||
DWC31_LINK_GDBGLTSSM(1),
|
||||
DWC3_GDBGLTSSM_LINKSTATE_MASK);
|
||||
} else {
|
||||
val = dwc3_msm_read_reg_field(mdwc->base,
|
||||
DWC3_GDBGLTSSM, DWC3_GDBGLTSSM_LINKSTATE_MASK);
|
||||
atomic_set(&mdwc->in_p3, val == DWC3_LINK_STATE_U3);
|
||||
dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
|
||||
}
|
||||
|
||||
if (!mdwc->dual_port)
|
||||
val1 = DWC3_LINK_STATE_U3;
|
||||
|
||||
atomic_set(&mdwc->in_p3, (val == DWC3_LINK_STATE_U3) &&
|
||||
(val1 == DWC3_LINK_STATE_U3));
|
||||
|
||||
if (!mdwc->dual_port)
|
||||
dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
|
||||
PWR_EVNT_POWERDOWN_IN_P3_MASK, 1);
|
||||
|
||||
/* Set the core in host mode if it was in host mode during pm_suspend */
|
||||
|
|
@ -2636,13 +2665,58 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
|
|||
|
||||
}
|
||||
|
||||
static void dwc3_msm_dp_ssphy_autosuspend(struct dwc3_msm *mdwc)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 reg = 0, reg1 = 0;
|
||||
|
||||
/* pwr_evt_irq not configured for dual port; clear in_p3 explicitly */
|
||||
atomic_set(&mdwc->in_p3, 0);
|
||||
/* Clear previous P3 events */
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
|
||||
PWR_EVNT_POWERDOWN_IN_P3_MASK | PWR_EVNT_POWERDOWN_OUT_P3_MASK);
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
|
||||
PWR_EVNT_POWERDOWN_IN_P3_MASK | PWR_EVNT_POWERDOWN_OUT_P3_MASK);
|
||||
|
||||
/* Prepare SSPHYs for suspend */
|
||||
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(0));
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(0),
|
||||
reg | DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(1));
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(1),
|
||||
reg | DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
|
||||
/* Wait for PHYs to go into P3 */
|
||||
timeout = jiffies + msecs_to_jiffies(5);
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
reg = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG);
|
||||
reg1 = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1);
|
||||
if ((reg & PWR_EVNT_POWERDOWN_IN_P3_MASK) &&
|
||||
(reg1 & PWR_EVNT_POWERDOWN_IN_P3_MASK)) {
|
||||
atomic_set(&mdwc->in_p3, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear P3 event bit */
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
|
||||
PWR_EVNT_POWERDOWN_IN_P3_MASK);
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
|
||||
PWR_EVNT_POWERDOWN_IN_P3_MASK);
|
||||
}
|
||||
|
||||
static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc, bool ignore_p3_state)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 reg = 0;
|
||||
|
||||
/* Allow SSPHYs to go to P3 for dual port controllers */
|
||||
if (mdwc->dual_port)
|
||||
dwc3_msm_dp_ssphy_autosuspend(mdwc);
|
||||
|
||||
if (!ignore_p3_state && ((mdwc->in_host_mode || mdwc->in_device_mode)
|
||||
&& dwc3_msm_is_superspeed(mdwc) && !mdwc->in_restart)) {
|
||||
&& (dwc3_msm_is_superspeed(mdwc) || mdwc->dual_port) &&
|
||||
!mdwc->in_restart)) {
|
||||
if (!atomic_read(&mdwc->in_p3)) {
|
||||
dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
|
||||
return -EBUSY;
|
||||
|
|
@ -2672,6 +2746,33 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc, bool ignore_p3_state)
|
|||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
|
||||
PWR_EVNT_LPM_IN_L2_MASK);
|
||||
|
||||
/* Handling for dual port core */
|
||||
if (!mdwc->dual_port)
|
||||
return 0;
|
||||
|
||||
/* Clear previous L2 events */
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
|
||||
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
|
||||
|
||||
/* Prepare HSPHY for suspend */
|
||||
reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(1));
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(1),
|
||||
reg | DWC3_GUSB2PHYCFG_ENBLSLPM | DWC3_GUSB2PHYCFG_SUSPHY);
|
||||
|
||||
/* Wait for PHY to go into L2 */
|
||||
timeout = jiffies + msecs_to_jiffies(5);
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
reg = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1);
|
||||
if (reg & PWR_EVNT_LPM_IN_L2_MASK)
|
||||
break;
|
||||
}
|
||||
if (!(reg & PWR_EVNT_LPM_IN_L2_MASK))
|
||||
dev_err(mdwc->dev, "could not transition HS PHY1 to L2\n");
|
||||
|
||||
/* Clear L2 event bit */
|
||||
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG1,
|
||||
PWR_EVNT_LPM_IN_L2_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2848,12 +2949,40 @@ static void enable_usb_pdc_interrupt(struct dwc3_msm *mdwc, bool enable)
|
|||
IRQ_TYPE_EDGE_RISING, true);
|
||||
}
|
||||
|
||||
if (mdwc->dual_port) {
|
||||
if (mdwc->hs_phy1->flags & PHY_LS_MODE) {
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DM_HS_PHY_IRQ_1],
|
||||
IRQ_TYPE_EDGE_FALLING, enable);
|
||||
} else if (mdwc->hs_phy1->flags & PHY_HSFS_MODE) {
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DP_HS_PHY_IRQ_1],
|
||||
IRQ_TYPE_EDGE_FALLING, enable);
|
||||
} else {
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DP_HS_PHY_IRQ_1],
|
||||
IRQ_TYPE_EDGE_RISING, true);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DM_HS_PHY_IRQ_1],
|
||||
IRQ_TYPE_EDGE_RISING, true);
|
||||
}
|
||||
}
|
||||
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[SS_PHY_IRQ],
|
||||
IRQF_TRIGGER_HIGH | IRQ_TYPE_LEVEL_HIGH, enable);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[SS_PHY_IRQ_1],
|
||||
IRQF_TRIGGER_HIGH | IRQ_TYPE_LEVEL_HIGH, enable);
|
||||
return;
|
||||
|
||||
disable_usb_irq:
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DP_HS_PHY_IRQ_1], 0, enable);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DM_HS_PHY_IRQ_1], 0, enable);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[SS_PHY_IRQ_1], 0, enable);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
&mdwc->wakeup_irq[DP_HS_PHY_IRQ], 0, enable);
|
||||
configure_usb_wakeup_interrupt(mdwc,
|
||||
|
|
@ -3254,6 +3383,21 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
|
|||
dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) &
|
||||
~DWC3_GUSB2PHYCFG_SUSPHY);
|
||||
|
||||
if (mdwc->dual_port)
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(1),
|
||||
dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(1)) &
|
||||
~DWC3_GUSB2PHYCFG_SUSPHY);
|
||||
|
||||
if (dwc->dis_u3_susphy_quirk) {
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(0),
|
||||
dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(0)) &
|
||||
~DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
if (mdwc->dual_port)
|
||||
dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(1),
|
||||
dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(1)) &
|
||||
~DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
|
|
@ -3277,7 +3421,8 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
|
|||
* Handle other power events that could not have been handled during
|
||||
* Low Power Mode
|
||||
*/
|
||||
dwc3_pwr_event_handler(mdwc);
|
||||
if (!mdwc->dual_port)
|
||||
dwc3_pwr_event_handler(mdwc);
|
||||
|
||||
if (pm_qos_request_active(&mdwc->pm_qos_req_dma))
|
||||
schedule_delayed_work(&mdwc->perf_vote_work,
|
||||
|
|
@ -3471,7 +3616,7 @@ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc)
|
|||
/* Can't tell if entered or exit P3, so check LINKSTATE */
|
||||
if (dwc3_is_usb31(dwc))
|
||||
ls = dwc3_msm_read_reg_field(mdwc->base,
|
||||
DWC31_LINK_GDBGLTSSM,
|
||||
DWC31_LINK_GDBGLTSSM(0),
|
||||
DWC3_GDBGLTSSM_LINKSTATE_MASK);
|
||||
else
|
||||
ls = dwc3_msm_read_reg_field(mdwc->base,
|
||||
|
|
@ -4352,9 +4497,9 @@ static int dwc3_msm_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < USB_MAX_IRQ; i++) {
|
||||
mdwc->wakeup_irq[i].irq = platform_get_irq_byname(pdev,
|
||||
usb_irq_info[i].name);
|
||||
/* pwr_evnt_irq is only mandatory irq for single port core */
|
||||
if (mdwc->wakeup_irq[i].irq < 0) {
|
||||
/* pwr_evnt_irq is only mandatory irq */
|
||||
if (usb_irq_info[i].required) {
|
||||
if (!mdwc->dual_port && usb_irq_info[i].required) {
|
||||
dev_err(&pdev->dev, "get_irq for %s failed\n\n",
|
||||
usb_irq_info[i].name);
|
||||
ret = -EINVAL;
|
||||
|
|
@ -4429,7 +4574,8 @@ static int dwc3_msm_probe(struct platform_device *pdev)
|
|||
|
||||
/* Add power event if the dbm indicates coming out of L1 by interrupt */
|
||||
if (!mdwc->dbm_is_1p4) {
|
||||
if (!mdwc->wakeup_irq[PWR_EVNT_IRQ].irq) {
|
||||
/* Dual port core does not need pwr_evnt_irq */
|
||||
if (!mdwc->dual_port && !mdwc->wakeup_irq[PWR_EVNT_IRQ].irq) {
|
||||
dev_err(&pdev->dev,
|
||||
"need pwr_event_irq exiting L1\n");
|
||||
ret = -EINVAL;
|
||||
|
|
@ -4779,6 +4925,12 @@ static int dwc3_msm_remove(struct platform_device *pdev)
|
|||
disable_irq(mdwc->wakeup_irq[DM_HS_PHY_IRQ].irq);
|
||||
if (mdwc->wakeup_irq[SS_PHY_IRQ].irq)
|
||||
disable_irq(mdwc->wakeup_irq[SS_PHY_IRQ].irq);
|
||||
if (mdwc->wakeup_irq[DP_HS_PHY_IRQ_1].irq)
|
||||
disable_irq(mdwc->wakeup_irq[DP_HS_PHY_IRQ_1].irq);
|
||||
if (mdwc->wakeup_irq[DM_HS_PHY_IRQ_1].irq)
|
||||
disable_irq(mdwc->wakeup_irq[DM_HS_PHY_IRQ_1].irq);
|
||||
if (mdwc->wakeup_irq[SS_PHY_IRQ_1].irq)
|
||||
disable_irq(mdwc->wakeup_irq[SS_PHY_IRQ_1].irq);
|
||||
disable_irq(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq);
|
||||
|
||||
clk_disable_unprepare(mdwc->utmi_clk);
|
||||
|
|
|
|||
Loading…
Reference in a new issue