// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * core.c - DesignWare HS OTG Controller common routines * * Copyright (C) 2004-2013 Synopsys, Inc.
*/
/* * The Core code provides basic services for accessing and managing the * DWC_otg hardware. These services are used by both the Host Controller * Driver and the Peripheral Controller Driver.
*/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/dma-mapping.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/usb.h>
/** * dwc2_backup_global_registers() - Backup global controller registers. * When suspending usb bus, registers needs to be backuped * if controller power is disabled once suspended. * * @hsotg: Programming view of the DWC_otg controller
*/ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{ struct dwc2_gregs_backup *gr;
/** * dwc2_restore_global_registers() - Restore controller global registers. * When resuming usb bus, device registers needs to be restored * if controller power were disabled. * * @hsotg: Programming view of the DWC_otg controller
*/ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{ struct dwc2_gregs_backup *gr;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore global regs */
gr = &hsotg->gr_backup; if (!gr->valid) {
dev_err(hsotg->dev, "%s: no global registers to restore\n",
__func__); return -EINVAL;
}
gr->valid = false;
/** * 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, int rem_wakeup, bool restore)
{ struct dwc2_gregs_backup *gr;
gr = &hsotg->gr_backup;
/* * 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);
}
/** * dwc2_enter_partial_power_down() - Put controller in Partial Power Down. * * @hsotg: Programming view of the DWC_otg controller
*/ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
{ if (dwc2_is_host_mode(hsotg)) return dwc2_host_enter_partial_power_down(hsotg); else return dwc2_gadget_enter_partial_power_down(hsotg);
}
/** * dwc2_restore_essential_regs() - Restore essiential regs of core. * * @hsotg: Programming view of the DWC_otg controller * @rmode: Restore mode, enabled in case of remote-wakeup. * @is_host: Host or device mode.
*/ staticvoid dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode, int is_host)
{
u32 pcgcctl; struct dwc2_gregs_backup *gr; struct dwc2_dregs_backup *dr; struct dwc2_hregs_backup *hr;
gr = &hsotg->gr_backup;
dr = &hsotg->dr_backup;
hr = &hsotg->hr_backup;
/** * dwc2_hib_restore_common() - Common part of restore routine. * * @hsotg: Programming view of the DWC_otg controller * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup. * @is_host: Host or device mode.
*/ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, int is_host)
{
u32 gpwrdn;
/* Switch-on voltage to the core */
gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PWRDNSWTCH;
dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Set Restore Essential Regs bit in PCGCCTL register */
dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host);
/* * Wait For Restore_done Interrupt. This mechanism of polling the * interrupt is introduced to avoid any possible race conditions
*/ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
20000)) {
dev_dbg(hsotg->dev, "%s: Restore Done wasn't generated here\n",
__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);
}
}
/** * dwc2_wait_for_mode() - Waits for the controller mode. * @hsotg: Programming view of the DWC_otg controller. * @host_mode: If true, waits for host mode, otherwise device mode.
*/ staticvoid dwc2_wait_for_mode(struct dwc2_hsotg *hsotg, bool host_mode)
{
ktime_t start;
ktime_t end; unsignedint timeout = 110;
dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
host_mode ? "host" : "device");
end = ktime_get();
ms = ktime_to_ms(ktime_sub(end, start));
if (ms >= (s64)timeout) {
dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
__func__, host_mode ? "host" : "device"); break;
}
usleep_range(1000, 2000);
}
}
/** * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce * filter is enabled. * * @hsotg: Programming view of DWC_otg controller
*/ staticbool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
{
u32 gsnpsid;
u32 ghwcfg4;
if (!dwc2_hw_is_otg(hsotg)) returnfalse;
/* Check if core configuration includes the IDDIG filter. */
ghwcfg4 = dwc2_readl(hsotg, GHWCFG4); if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) returnfalse;
/* * Check if the IDDIG debounce filter is bypassed. Available * in core version >= 3.10a.
*/
gsnpsid = dwc2_readl(hsotg, GSNPSID); if (gsnpsid >= DWC2_CORE_REV_3_10a) {
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) returnfalse;
}
returntrue;
}
/* * dwc2_enter_hibernation() - Common function to enter hibernation. * * @hsotg: Programming view of the DWC_otg controller * @is_host: True if core is in host mode. * * Return: 0 if successful, negative error code otherwise
*/ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)
{ if (is_host) return dwc2_host_enter_hibernation(hsotg); else return dwc2_gadget_enter_hibernation(hsotg);
}
/* * dwc2_exit_hibernation() - Common function to exit from hibernation. * * @hsotg: Programming view of the DWC_otg controller * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup. * @reset: Enabled in case of restore with reset. * @is_host: True if core is in host mode. * * Return: 0 if successful, negative error code otherwise
*/ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset, int is_host)
{ if (is_host) return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset); else return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset);
}
/* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core.
*/ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset; bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
/* * If the current mode is host, either due to the force mode * bit being set (which persists after core reset) or the * connector id pin, a core soft reset will temporarily reset * the mode to device. A delay from the IDDIG debounce filter * will occur before going back to host mode. * * Determine whether we will go back into host mode after a * reset and account for this delay after the reset.
*/ if (dwc2_iddig_filter_enabled(hsotg)) {
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
u32 gusbcfg = dwc2_readl(hsotg, GUSBCFG);
/* * 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",
__func__); return -EBUSY;
}
if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
return 0;
}
/** * dwc2_force_mode() - Force the mode of the controller. * * Forcing the mode is needed for two cases: * * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the * controller to stay in a particular mode regardless of ID pin * changes. We do this once during probe. * * 2) During probe we want to read reset values of the hw * configuration registers that are only available in either host or * device mode. We may need to force the mode if the current mode does * not allow us to access the register in the mode that we want. * * In either case it only makes sense to force the mode if the * controller hardware is OTG capable. * * Checks are done in this function to determine whether doing a force * would be valid or not. * * If a force is done, it requires a IDDIG debounce filter delay if * the filter is configured and enabled. We poll the current mode of * the controller to account for this delay. * * @hsotg: Programming view of DWC_otg controller * @host: Host mode flag
*/ void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
u32 gusbcfg;
u32 set;
u32 clear;
dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
/* * Force mode has no effect if the hardware is not OTG.
*/ if (!dwc2_hw_is_otg(hsotg)) return;
/* * If dr_mode is either peripheral or host only, there is no * need to ever force the mode to the opposite mode.
*/ if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) return;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST)) return;
/** * dwc2_clear_force_mode() - Clears the force mode bits. * * After clearing the bits, wait up to 100 ms to account for any * potential IDDIG filter delay. We can't know if we expect this delay * or not because the value of the connector ID status is affected by * the force mode. We only need to call this once during probe if * dr_mode == OTG. * * @hsotg: Programming view of DWC_otg controller
*/ staticvoid dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
if (!dwc2_hw_is_otg(hsotg)) return;
dev_dbg(hsotg->dev, "Clearing force mode bits\n");
if (dwc2_iddig_filter_enabled(hsotg))
msleep(100);
}
/* * Sets or clears force mode based on the dr_mode parameter.
*/ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{ switch (hsotg->dr_mode) { case USB_DR_MODE_HOST: /* * NOTE: This is required for some rockchip soc based * platforms on their host-only dwc2.
*/ if (!dwc2_hw_is_otg(hsotg))
msleep(50);
break; case USB_DR_MODE_PERIPHERAL:
dwc2_force_mode(hsotg, false); break; case USB_DR_MODE_OTG:
dwc2_clear_force_mode(hsotg); break; default:
dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
__func__, hsotg->dr_mode); break;
}
}
/** * dwc2_dump_host_registers() - Prints the host registers * * @hsotg: Programming view of DWC_otg controller * * NOTE: This function will be removed once the peripheral controller code * is integrated and the driver is stable
*/ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
{ #ifdef DEBUG
u32 __iomem *addr; int i;
/** * dwc2_dump_global_registers() - Prints the core global registers * * @hsotg: Programming view of DWC_otg controller * * NOTE: This function will be removed once the peripheral controller code * is integrated and the driver is stable
*/ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
{ #ifdef DEBUG
u32 __iomem *addr;
/** * dwc2_hsotg_wait_bit_set - Waits for bit to be set. * @hsotg: Programming view of DWC_otg controller. * @offset: Register's offset where bit/bits must be set. * @mask: Mask of the bit/bits which must be set. * @timeout: Timeout to wait. * * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
*/ int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
u32 timeout)
{
u32 i;
for (i = 0; i < timeout; i++) { if (dwc2_readl(hsotg, offset) & mask) return 0;
udelay(1);
}
return -ETIMEDOUT;
}
/** * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear. * @hsotg: Programming view of DWC_otg controller. * @offset: Register's offset where bit/bits must be set. * @mask: Mask of the bit/bits which must be set. * @timeout: Timeout to wait. * * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
*/ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
u32 timeout)
{
u32 i;
for (i = 0; i < timeout; i++) { if (!(dwc2_readl(hsotg, offset) & mask)) return 0;
udelay(1);
}
return -ETIMEDOUT;
}
/* * Initializes the FSLSPClkSel field of the HCFG register depending on the * PHY type
*/ void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
{
u32 hcfg, val;
if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
hsotg->params.ulpi_fs_ls) ||
hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { /* Full speed PHY */
val = HCFG_FSLSPCLKSEL_48_MHZ;
} else { /* High speed PHY running at full speed or high speed */
val = HCFG_FSLSPCLKSEL_30_60_MHZ;
}
/* * Applicable only to HSOTG core v5.00a or higher. * Not applicable to HS/FS IOT devices.
*/ if ((gsnpsid & ~DWC2_CORE_REV_MASK) != DWC2_OTG_ID ||
gsnpsid < DWC2_CORE_REV_5_00a) return;
/* * core_init() is now called on every switch so only call the * following for the first time through
*/ if (select_phy) {
dev_dbg(hsotg->dev, "FS PHY selected\n");
if (hsotg->params.activate_stm_fs_transceiver) {
ggpio = dwc2_readl(hsotg, GGPIO); if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
dev_dbg(hsotg->dev, "Activating transceiver\n"); /* * STM32F4x9 uses the GGPIO register as general * core configuration register.
*/
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
dwc2_writel(hsotg, ggpio, GGPIO);
}
}
}
/* * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also * do this on HNP Dev/Host mode switches (done in dev_init and * host_init).
*/ if (dwc2_is_host_mode(hsotg))
dwc2_init_fs_ls_pclk_sel(hsotg);
if (hsotg->params.i2c_enable) {
dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
/* Program GUSBCFG.OtgUtmiFsSel to I2C */
usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* * HS PHY parameters. These parameters are preserved during soft reset * so only program the first time. Do a soft reset immediately after * setting phyif.
*/ switch (hsotg->params.phy_type) { case DWC2_PHY_TYPE_PARAM_ULPI: /* ULPI interface */
dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); if (hsotg->params.phy_ulpi_ddr)
usbcfg |= GUSBCFG_DDRSEL;
/* Set external VBUS indicator as needed. */ if (hsotg->params.oc_disable)
usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND |
GUSBCFG_INDICATORPASSTHROUGH); break; case DWC2_PHY_TYPE_PARAM_UTMI: /* UTMI+ interface */
dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); if (hsotg->params.phy_utmi_width == 16)
usbcfg |= GUSBCFG_PHYIF16; break; default:
dev_err(hsotg->dev, "FS PHY selected at HS!\n"); break;
}
if (usbcfg != usbcfg_old) {
dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* Reset after setting the PHY parameters */
retval = dwc2_core_reset(hsotg, false); if (retval) {
dev_err(hsotg->dev, "%s: Reset failed, aborting", __func__); return retval;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.