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

Commit 1bfdbc66 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: dwc3-msm: Cleanup suspend and resume functions"

parents c63856e7 27c20985
Loading
Loading
Loading
Loading
+0 −2
Original line number Original line Diff line number Diff line
@@ -33,8 +33,6 @@ Optional properties :
		TX fifo allocation in bytes
		TX fifo allocation in bytes
- qcom,utmi-clk-rate: Indicates refclk frequency (in Hz) to the core. If not
- qcom,utmi-clk-rate: Indicates refclk frequency (in Hz) to the core. If not
  specified, default of 19.2MHz is assumed.
  specified, default of 19.2MHz is assumed.
- qcom,no-suspend-resume: If present, the device will not perform any activity
		during suspend/resume
- qcom,usb-dbm : phandle for the DBM device
- qcom,usb-dbm : phandle for the DBM device
- qcom,power-collapse-on-cable-disconnect: If present, USB core will perform
- qcom,power-collapse-on-cable-disconnect: If present, USB core will perform
	power collapse when cable is disconencted.
	power collapse when cable is disconencted.
+52 −133
Original line number Original line Diff line number Diff line
@@ -62,10 +62,6 @@ static int override_phy_init;
module_param(override_phy_init, int, S_IRUGO|S_IWUSR);
module_param(override_phy_init, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(override_phy_init, "Override HSPHY Init Seq");
MODULE_PARM_DESC(override_phy_init, "Override HSPHY Init Seq");


static bool usb_lpm_override;
module_param(usb_lpm_override, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usb_lpm_override, "Override no_suspend_resume with USB");

/* Max current to be drawn for HVDCP charger */
/* Max current to be drawn for HVDCP charger */
static int hvdcp_max_current = DWC3_HVDCP_CHG_MAX;
static int hvdcp_max_current = DWC3_HVDCP_CHG_MAX;
module_param(hvdcp_max_current, int, S_IRUGO|S_IWUSR);
module_param(hvdcp_max_current, int, S_IRUGO|S_IWUSR);
@@ -85,7 +81,6 @@ MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
 *
 *
 */
 */
#define QSCRATCH_REG_OFFSET	(0x000F8800)
#define QSCRATCH_REG_OFFSET	(0x000F8800)
#define QSCRATCH_CTRL_REG      (QSCRATCH_REG_OFFSET + 0x04)
#define QSCRATCH_GENERAL_CFG	(QSCRATCH_REG_OFFSET + 0x08)
#define QSCRATCH_GENERAL_CFG	(QSCRATCH_REG_OFFSET + 0x08)
#define CGCTL_REG		(QSCRATCH_REG_OFFSET + 0x28)
#define CGCTL_REG		(QSCRATCH_REG_OFFSET + 0x28)
#define PWR_EVNT_IRQ_STAT_REG    (QSCRATCH_REG_OFFSET + 0x58)
#define PWR_EVNT_IRQ_STAT_REG    (QSCRATCH_REG_OFFSET + 0x58)
@@ -205,13 +200,8 @@ struct dwc3_msm {
	enum dwc3_id_state	id_state;
	enum dwc3_id_state	id_state;
	unsigned long		lpm_flags;
	unsigned long		lpm_flags;
#define MDWC3_SS_PHY_SUSPEND		BIT(0)
#define MDWC3_SS_PHY_SUSPEND		BIT(0)
#define MDWC3_TCXO_SHUTDOWN		BIT(1)
#define MDWC3_ASYNC_IRQ_WAKE_CAPABILITY	BIT(1)
#define MDWC3_ASYNC_IRQ_WAKE_CAPABILITY	BIT(2)
#define MDWC3_POWER_COLLAPSE		BIT(2)
#define MDWC3_POWER_COLLAPSE		BIT(3)
#define MDWC3_CORECLK_OFF		BIT(4)

	u32 qscratch_ctl_val;
	bool suspend_resume_no_support;


	bool power_collapse; /* power collapse on cable disconnect */
	bool power_collapse; /* power collapse on cable disconnect */
	bool power_collapse_por; /* perform POR sequence after power collapse */
	bool power_collapse_por; /* perform POR sequence after power collapse */
@@ -1062,12 +1052,6 @@ static void dwc3_msm_qscratch_reg_init(struct dwc3_msm *mdwc)
	dwc3_msm_write_reg(mdwc->base, CGCTL_REG,
	dwc3_msm_write_reg(mdwc->base, CGCTL_REG,
		dwc3_msm_read_reg(mdwc->base, CGCTL_REG) | 0x18);
		dwc3_msm_read_reg(mdwc->base, CGCTL_REG) | 0x18);


	/*
	 * This is required to restore the POR value after userspace
	 * is done with charger detection.
	 */
	mdwc->qscratch_ctl_val =
		dwc3_msm_read_reg(mdwc->base, QSCRATCH_CTRL_REG);
}
}


static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
@@ -1216,16 +1200,11 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)


static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
{
{
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	unsigned long timeout;
	unsigned long timeout;
	u32 reg = 0;
	u32 reg = 0;
	bool device_mode;

	device_mode = dwc->is_drd &&
			mdwc->otg_state == OTG_STATE_B_PERIPHERAL;


	if ((mdwc->in_host_mode || device_mode) &&
	if ((mdwc->in_host_mode || mdwc->vbus_active)
			dwc3_msm_is_superspeed(mdwc)) {
			&& dwc3_msm_is_superspeed(mdwc)) {
		if (!atomic_read(&mdwc->in_p3)) {
		if (!atomic_read(&mdwc->in_p3)) {
			dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
			dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
			return -EBUSY;
			return -EBUSY;
@@ -1295,29 +1274,16 @@ static void dwc3_msm_bus_vote_w(struct work_struct *w)
static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
{
{
	int ret, i;
	int ret, i;
	bool dcp;
	bool host_ss_active;
	bool can_suspend_ssphy;
	bool can_suspend_ssphy;
	bool device_bus_suspend = false;
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);


	dev_dbg(mdwc->dev, "%s: entering lpm. usb_lpm_override:%d\n",
					 __func__, usb_lpm_override);
	dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm));
	dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm));


	if (!usb_lpm_override && mdwc->suspend_resume_no_support) {
		dev_dbg(mdwc->dev, "%s no support for suspend\n", __func__);
		return -EPERM;
	}

	if (atomic_read(&dwc->in_lpm)) {
	if (atomic_read(&dwc->in_lpm)) {
		dev_dbg(mdwc->dev, "%s: Already suspended\n", __func__);
		dev_dbg(mdwc->dev, "%s: Already suspended\n", __func__);
		return 0;
		return 0;
	}
	}


	if (dwc->is_drd && mdwc->otg_state == OTG_STATE_B_SUSPEND)
		device_bus_suspend = true;

	if (!mdwc->in_host_mode) {
	if (!mdwc->in_host_mode) {
		/* pending device events unprocessed */
		/* pending device events unprocessed */
		for (i = 0; i < dwc->num_event_buffers; i++) {
		for (i = 0; i < dwc->num_event_buffers; i++) {
@@ -1350,33 +1316,12 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
		return -EBUSY;
		return -EBUSY;
	}
	}


	host_ss_active = dwc3_msm_is_host_superspeed(mdwc);

	if (mdwc->chg_state != USB_CHG_STATE_DETECTED) {
		/* charger detection wasn't complete; re-init flags */
		mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
		mdwc->chg_type = DWC3_INVALID_CHARGER;
	}

	dcp = ((mdwc->chg_type == DWC3_DCP_CHARGER) ||
	      (mdwc->chg_type == DWC3_PROPRIETARY_CHARGER) ||
	      (mdwc->chg_type == DWC3_FLOATED_CHARGER));
	if (dcp) {
		mdwc->hs_phy->flags |= PHY_CHARGER_CONNECTED;
		mdwc->ss_phy->flags |= PHY_CHARGER_CONNECTED;
	} else {
		mdwc->hs_phy->flags &= ~PHY_CHARGER_CONNECTED;
		mdwc->ss_phy->flags &= ~PHY_CHARGER_CONNECTED;
	}

	can_suspend_ssphy = !(mdwc->in_host_mode && host_ss_active);

	/*
	/*
	 * Check if device is not in CONFIGURED state
	 * Check if device is not in CONFIGURED state
	 * then check cotroller state of L2 and break
	 * then check controller state of L2 and break
	 * LPM sequeunce.
	 * LPM sequence. Check this for device bus suspend case.
	 */
	 */
	if (device_bus_suspend &&
	if ((dwc->is_drd && mdwc->otg_state == OTG_STATE_B_SUSPEND) &&
		(dwc->gadget.state != USB_STATE_CONFIGURED)) {
		(dwc->gadget.state != USB_STATE_CONFIGURED)) {
		pr_err("%s(): Trying to go in LPM with state:%d\n",
		pr_err("%s(): Trying to go in LPM with state:%d\n",
					__func__, dwc->gadget.state);
					__func__, dwc->gadget.state);
@@ -1388,14 +1333,14 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
	if (ret)
	if (ret)
		return ret;
		return ret;


	/* Initialize variables here */
	can_suspend_ssphy = !(mdwc->in_host_mode &&
				dwc3_msm_is_host_superspeed(mdwc));

	/* Disable core irq */
	/* Disable core irq */
	if (dwc->irq)
	if (dwc->irq)
		disable_irq(dwc->irq);
		disable_irq(dwc->irq);


	if (!dcp && !mdwc->in_host_mode)
		dwc3_msm_write_reg(mdwc->base, QSCRATCH_CTRL_REG,
			mdwc->qscratch_ctl_val);

	/* Enable wakeup from LPM */
	/* Enable wakeup from LPM */
	if (mdwc->pwr_event_irq) {
	if (mdwc->pwr_event_irq) {
		disable_irq(mdwc->pwr_event_irq);
		disable_irq(mdwc->pwr_event_irq);
@@ -1403,40 +1348,38 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
		enable_irq_wake(mdwc->pwr_event_irq);
		enable_irq_wake(mdwc->pwr_event_irq);
	}
	}


	/* Suspend HS PHY */
	usb_phy_set_suspend(mdwc->hs_phy, 1);
	usb_phy_set_suspend(mdwc->hs_phy, 1);


	/* Suspend SS PHY */
	if (can_suspend_ssphy) {
	if (can_suspend_ssphy) {
		usb_phy_set_suspend(mdwc->ss_phy, 1);
		usb_phy_set_suspend(mdwc->ss_phy, 1);
		usleep_range(1000, 1200);
		mdwc->lpm_flags |= MDWC3_SS_PHY_SUSPEND;
		mdwc->lpm_flags |= MDWC3_SS_PHY_SUSPEND;
	}
	}


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


	/* Perform controller power collapse */
	/* Disable clocks */
	if (!mdwc->in_host_mode && !device_bus_suspend &&
				mdwc->power_collapse) {
		mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
		dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
		dwc3_msm_config_gdsc(mdwc, 0);
		clk_disable_unprepare(mdwc->sleep_clk);
	}

	clk_disable_unprepare(mdwc->iface_clk);
	clk_disable_unprepare(mdwc->iface_clk);
	if (mdwc->bus_aggr_clk)
	if (mdwc->bus_aggr_clk)
		clk_disable_unprepare(mdwc->bus_aggr_clk);
		clk_disable_unprepare(mdwc->bus_aggr_clk);
	clk_disable_unprepare(mdwc->utmi_clk);
	clk_disable_unprepare(mdwc->utmi_clk);


	if (can_suspend_ssphy) {
	clk_set_rate(mdwc->core_clk, 19200000);
	clk_set_rate(mdwc->core_clk, 19200000);
	clk_disable_unprepare(mdwc->core_clk);
	clk_disable_unprepare(mdwc->core_clk);
		mdwc->lpm_flags |= MDWC3_CORECLK_OFF;
	/* USB PHY no more requires TCXO */
	/* USB PHY no more requires TCXO */
	clk_disable_unprepare(mdwc->xo_clk);
	clk_disable_unprepare(mdwc->xo_clk);
		mdwc->lpm_flags |= MDWC3_TCXO_SHUTDOWN;

	/* Perform controller power collapse */
	if (!mdwc->in_host_mode && !mdwc->vbus_active && mdwc->power_collapse) {
		mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
		dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
		dwc3_msm_config_gdsc(mdwc, 0);
		clk_disable_unprepare(mdwc->sleep_clk);
	}
	}


	/* Remove bus voting */
	if (mdwc->bus_perf_client) {
	if (mdwc->bus_perf_client) {
		mdwc->bus_vote = 0;
		mdwc->bus_vote = 0;
		schedule_work(&mdwc->bus_vote_w);
		schedule_work(&mdwc->bus_vote_w);
@@ -1455,19 +1398,18 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
		pm_relax(mdwc->dev);
		pm_relax(mdwc->dev);
	}
	}


	if (mdwc->hs_phy_irq) {
	/*
	/*
	 * with DCP or during cable disconnect, we dont require wakeup
	 * with DCP or during cable disconnect, we dont require wakeup
	 * using HS_PHY_IRQ. Hence enable wakeup only in case of host
	 * using HS_PHY_IRQ. Hence enable wakeup only in case of host
	 * bus suspend and device bus suspend.
	 * bus suspend and device bus suspend.
	 */
	 */
		if (mdwc->in_host_mode || device_bus_suspend) {
	if (mdwc->hs_phy_irq && (mdwc->vbus_active || mdwc->in_host_mode)) {
		enable_irq_wake(mdwc->hs_phy_irq);
		enable_irq_wake(mdwc->hs_phy_irq);
		mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
		mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
	}
	}
	}


	atomic_set(&dwc->in_lpm, 1);
	atomic_set(&dwc->in_lpm, 1);

	if (mdwc->pwr_event_irq)
	if (mdwc->pwr_event_irq)
		enable_irq(mdwc->pwr_event_irq);
		enable_irq(mdwc->pwr_event_irq);


@@ -1478,7 +1420,6 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
static int dwc3_msm_resume(struct dwc3_msm *mdwc)
static int dwc3_msm_resume(struct dwc3_msm *mdwc)
{
{
	int ret;
	int ret;
	bool dcp;
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);


	dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__);
	dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__);
@@ -1490,30 +1431,17 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)


	pm_stay_awake(mdwc->dev);
	pm_stay_awake(mdwc->dev);


	/* Enable bus voting */
	if (mdwc->bus_perf_client) {
	if (mdwc->bus_perf_client) {
		mdwc->bus_vote = 1;
		mdwc->bus_vote = 1;
		schedule_work(&mdwc->bus_vote_w);
		schedule_work(&mdwc->bus_vote_w);
	}
	}


	dcp = ((mdwc->chg_type == DWC3_DCP_CHARGER) ||
	      (mdwc->chg_type == DWC3_PROPRIETARY_CHARGER) ||
	      (mdwc->chg_type == DWC3_FLOATED_CHARGER));
	if (dcp) {
		mdwc->hs_phy->flags |= PHY_CHARGER_CONNECTED;
		mdwc->ss_phy->flags |= PHY_CHARGER_CONNECTED;
	} else {
		mdwc->hs_phy->flags &= ~PHY_CHARGER_CONNECTED;
		mdwc->ss_phy->flags &= ~PHY_CHARGER_CONNECTED;
	}

	if (mdwc->lpm_flags & MDWC3_TCXO_SHUTDOWN) {
	/* Vote for TCXO while waking up USB HSPHY */
	/* Vote for TCXO while waking up USB HSPHY */
	ret = clk_prepare_enable(mdwc->xo_clk);
	ret = clk_prepare_enable(mdwc->xo_clk);
	if (ret)
	if (ret)
		dev_err(mdwc->dev, "%s failed to vote TCXO buffer%d\n",
		dev_err(mdwc->dev, "%s failed to vote TCXO buffer%d\n",
						__func__, ret);
						__func__, ret);
		mdwc->lpm_flags &= ~MDWC3_TCXO_SHUTDOWN;
	}


	/* Restore controller power collapse */
	/* Restore controller power collapse */
	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
@@ -1524,31 +1452,25 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
		/* HW requires a short delay for reset to take place properly */
		/* HW requires a short delay for reset to take place properly */
		usleep_range(1000, 1200);
		usleep_range(1000, 1200);
		clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
		clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
		clk_prepare_enable(mdwc->sleep_clk);
	}
	}



	/* Resume SS PHY */
	if (mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
	if (mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
		usb_phy_set_suspend(mdwc->ss_phy, 0);
		usb_phy_set_suspend(mdwc->ss_phy, 0);
		mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND;
		mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND;
	}
	}
	usleep_range(1000, 1200);


	if (mdwc->bus_aggr_clk)
	/* Resume HS PHY */
		clk_prepare_enable(mdwc->bus_aggr_clk);
	usb_phy_set_suspend(mdwc->hs_phy, 0);


	clk_prepare_enable(mdwc->iface_clk);
	/* Enable clocks */
	if (mdwc->lpm_flags & MDWC3_CORECLK_OFF) {
	clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
	clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
	clk_prepare_enable(mdwc->core_clk);
	clk_prepare_enable(mdwc->core_clk);
		mdwc->lpm_flags &= ~MDWC3_CORECLK_OFF;
	}

	clk_prepare_enable(mdwc->utmi_clk);
	clk_prepare_enable(mdwc->utmi_clk);

	if (mdwc->bus_aggr_clk)
	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE)
		clk_prepare_enable(mdwc->bus_aggr_clk);
		clk_prepare_enable(mdwc->sleep_clk);
	clk_prepare_enable(mdwc->iface_clk);

	usb_phy_set_suspend(mdwc->hs_phy, 0);


	/* Recover from controller power collapse */
	/* Recover from controller power collapse */
	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
@@ -2229,9 +2151,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	mdwc->charging_disabled = of_property_read_bool(node,
	mdwc->charging_disabled = of_property_read_bool(node,
				"qcom,charging-disabled");
				"qcom,charging-disabled");


	mdwc->suspend_resume_no_support = of_property_read_bool(node,
				"qcom,no-suspend-resume");

	mdwc->power_collapse_por = of_property_read_bool(node,
	mdwc->power_collapse_por = of_property_read_bool(node,
		"qcom,por-after-power-collapse");
		"qcom,por-after-power-collapse");