diff options
Diffstat (limited to 'drivers/usb/dwc2/core.c')
| -rw-r--r-- | drivers/usb/dwc2/core.c | 138 | 
1 files changed, 41 insertions, 97 deletions
| diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index fec17a2d2447..6f70ab9577b4 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -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);  }  /** @@ -374,6 +299,12 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,  			__func__);  	} else {  		dev_dbg(hsotg->dev, "restore done  generated here\n"); + +		/* +		 * To avoid restore done interrupt storm after restore is +		 * generated clear GINTSTS_RESTOREDONE bit. +		 */ +		dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTSTS);  	}  } @@ -460,9 +391,6 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)   */  int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)  { -	if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION) -		return -ENOTSUPP; -  	if (is_host)  		return dwc2_host_enter_hibernation(hsotg);  	else @@ -545,6 +473,22 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)  		dwc2_writel(hsotg, greset, GRSTCTL);  	} +	/* +	 * Switching from device mode to host mode by disconnecting +	 * device cable core enters and exits form hibernation. +	 * However, the fifo map remains not cleared. It results +	 * to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ +	 * gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) +	 * if in host mode we disconnect the micro a to b host +	 * cable. Because core reset occurs. +	 * To avoid the WARNING, fifo_map should be cleared +	 * in dwc2_core_reset() function by taking into account configs. +	 * fifo_map must be cleared only if driver is configured in +	 * "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" +	 * mode. +	 */ +	dwc2_clear_fifo_map(hsotg); +  	/* Wait for AHB master IDLE state */  	if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) {  		dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n", | 
