Commit c9c394ab authored by Artur Petrosyan's avatar Artur Petrosyan Committed by Greg Kroah-Hartman
Browse files

usb: dwc2: Update enter and exit partial power down functions



These are wrapper functions which are calling device or host
enter/exit partial power down functions.

This change is done because we need to separate device and
host partial power down functions as the programming flow
has a lot of difference between host and device. With this
update during partial power down exit driver relies on
backup value of "GOTGCTL_CURMODE_HOST" to determine the
mode of core before entering to PPD.

Acked-by: default avatarMinas Harutyunyan <Minas.Harutyunyan@synopsys.com>
Signed-off-by: default avatarArtur Petrosyan <Arthur.Petrosyan@synopsys.com>
Link: https://lore.kernel.org/r/20210408094446.6491BA022E@mailhost.synopsys.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9ce9e5ad
Loading
Loading
Loading
Loading
+19 −94
Original line number Diff line number Diff line
@@ -131,54 +131,26 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
 * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down.
 *
 * @hsotg: Programming view of the DWC_otg controller
 * @rem_wakeup: indicates whether resume is initiated by Reset.
 * @restore: Controller registers need to be restored
 */
int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup,
				 bool restore)
{
	u32 pcgcctl;
	int ret = 0;

	if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
		return -ENOTSUPP;

	pcgcctl = dwc2_readl(hsotg, PCGCTL);
	pcgcctl &= ~PCGCTL_STOPPCLK;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);

	pcgcctl = dwc2_readl(hsotg, PCGCTL);
	pcgcctl &= ~PCGCTL_PWRCLMP;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);

	pcgcctl = dwc2_readl(hsotg, PCGCTL);
	pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);
	struct dwc2_gregs_backup *gr;

	udelay(100);
	if (restore) {
		ret = dwc2_restore_global_registers(hsotg);
		if (ret) {
			dev_err(hsotg->dev, "%s: failed to restore registers\n",
				__func__);
			return ret;
		}
		if (dwc2_is_host_mode(hsotg)) {
			ret = dwc2_restore_host_registers(hsotg);
			if (ret) {
				dev_err(hsotg->dev, "%s: failed to restore host registers\n",
					__func__);
				return ret;
			}
		} else {
			ret = dwc2_restore_device_registers(hsotg, 0);
			if (ret) {
				dev_err(hsotg->dev, "%s: failed to restore device registers\n",
					__func__);
				return ret;
			}
		}
	}
	gr = &hsotg->gr_backup;

	return ret;
	/*
	 * Restore host or device regisers with the same mode core enterted
	 * to partial power down by checking "GOTGCTL_CURMODE_HOST" backup
	 * value of the "gotgctl" register.
	 */
	if (gr->gotgctl & GOTGCTL_CURMODE_HOST)
		return dwc2_host_exit_partial_power_down(hsotg, rem_wakeup,
							 restore);
	else
		return dwc2_gadget_exit_partial_power_down(hsotg, restore);
}

/**
@@ -188,57 +160,10 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
 */
int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
{
	u32 pcgcctl;
	int ret = 0;

	if (!hsotg->params.power_down)
		return -ENOTSUPP;

	/* Backup all registers */
	ret = dwc2_backup_global_registers(hsotg);
	if (ret) {
		dev_err(hsotg->dev, "%s: failed to backup global registers\n",
			__func__);
		return ret;
	}

	if (dwc2_is_host_mode(hsotg)) {
		ret = dwc2_backup_host_registers(hsotg);
		if (ret) {
			dev_err(hsotg->dev, "%s: failed to backup host registers\n",
				__func__);
			return ret;
		}
	} else {
		ret = dwc2_backup_device_registers(hsotg);
		if (ret) {
			dev_err(hsotg->dev, "%s: failed to backup device registers\n",
				__func__);
			return ret;
		}
	}

	/*
	 * Clear any pending interrupts since dwc2 will not be able to
	 * clear them after entering partial_power_down.
	 */
	dwc2_writel(hsotg, 0xffffffff, GINTSTS);

	/* Put the controller in low power state */
	pcgcctl = dwc2_readl(hsotg, PCGCTL);

	pcgcctl |= PCGCTL_PWRCLMP;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);
	ndelay(20);

	pcgcctl |= PCGCTL_RSTPDWNMODULE;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);
	ndelay(20);

	pcgcctl |= PCGCTL_STOPPCLK;
	dwc2_writel(hsotg, pcgcctl, PCGCTL);

	return ret;
	if (dwc2_is_host_mode(hsotg))
		return dwc2_host_enter_partial_power_down(hsotg);
	else
		return dwc2_gadget_enter_partial_power_down(hsotg);
}

/**
+2 −1
Original line number Diff line number Diff line
@@ -1303,7 +1303,8 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg)
 */
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait);
int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg);
int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore);
int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup,
				 bool restore);
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host);
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
		int reset, int is_host);
+10 −11
Original line number Diff line number Diff line
@@ -315,9 +315,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
		hsotg->lx_state);

	if (dwc2_is_device_mode(hsotg)) {
		if (hsotg->lx_state == DWC2_L2) {
			ret = dwc2_exit_partial_power_down(hsotg, true);
			if (ret && (ret != -ENOTSUPP))
		if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) {
			ret = dwc2_exit_partial_power_down(hsotg, 0,
							   true);
			if (ret)
				dev_err(hsotg->dev,
					"exit power_down failed\n");
		}
@@ -406,18 +407,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
	if (dwc2_is_device_mode(hsotg)) {
		dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
			dwc2_readl(hsotg, DSTS));
		if (hsotg->lx_state == DWC2_L2) {
		if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) {
			u32 dctl = dwc2_readl(hsotg, DCTL);

			/* Clear Remote Wakeup Signaling */
			dctl &= ~DCTL_RMTWKUPSIG;
			dwc2_writel(hsotg, dctl, DCTL);
			ret = dwc2_exit_partial_power_down(hsotg, true);
			if (ret && (ret != -ENOTSUPP))
				dev_err(hsotg->dev, "exit power_down failed\n");

			/* Change to L0 state */
			hsotg->lx_state = DWC2_L0;
			ret = dwc2_exit_partial_power_down(hsotg, 1,
							   true);
			if (ret)
				dev_err(hsotg->dev,
					"exit partial_power_down failed\n");
			call_gadget(hsotg, resume);
		} else {
			/* Change to L0 state */
+12 −8
Original line number Diff line number Diff line
@@ -3689,11 +3689,11 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
		dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS);

		/* This event must be used only if controller is suspended */
		if (hsotg->lx_state == DWC2_L2) {
			dwc2_exit_partial_power_down(hsotg, true);
		if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
			dwc2_exit_partial_power_down(hsotg, 0, true);

		hsotg->lx_state = DWC2_L0;
	}
	}

	if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
		u32 usb_status = dwc2_readl(hsotg, GOTGCTL);
@@ -4615,11 +4615,15 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
	spin_lock_irqsave(&hsotg->lock, flags);

	/*
	 * If controller is hibernated, it must exit from power_down
	 * before being initialized / de-initialized
	 * If controller is in partial power down state, it must exit from
	 * that state before being initialized / de-initialized
	 */
	if (hsotg->lx_state == DWC2_L2)
		dwc2_exit_partial_power_down(hsotg, false);
	if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd)
		/*
		 * No need to check the return value as
		 * registers are not being restored.
		 */
		dwc2_exit_partial_power_down(hsotg, 0, false);

	if (is_active) {
		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+1 −1
Original line number Diff line number Diff line
@@ -4418,7 +4418,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)


		/* Exit partial_power_down */
		ret = dwc2_exit_partial_power_down(hsotg, true);
		ret = dwc2_exit_partial_power_down(hsotg, 0, true);
		if (ret && (ret != -ENOTSUPP))
			dev_err(hsotg->dev, "exit partial_power_down failed\n");
	} else {
Loading