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

Commit 76c3eb34 authored by Ajay Agarwal's avatar Ajay Agarwal Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarAjay Agarwal <ajaya@codeaurora.org>
Signed-off-by: default avatarHarsh Agarwal <harshq@codeaurora.org>
parent 3081acb9
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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))
+164 −12
Original line number Diff line number Diff line
@@ -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,14 +2633,26 @@ 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);
	}

	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);

@@ -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,6 +3421,7 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
	 * Handle other power events that could not have been handled during
	 * Low Power Mode
	 */
	if (!mdwc->dual_port)
		dwc3_pwr_event_handler(mdwc);

	if (pm_qos_request_active(&mdwc->pm_qos_req_dma))
@@ -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);