static int tegra210_pmc_utmi_enable_phy_sleepwalk()

in tegra/xusb-tegra210.c [1335:1506]


static int tegra210_pmc_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
						  enum usb_device_speed speed)
{
	struct tegra_xusb_padctl *padctl = lane->pad->padctl;
	struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
	unsigned int port = lane->index;
	u32 value, tctrl, pctrl, rpd_ctrl;

	if (!priv->regmap)
		return -EOPNOTSUPP;

	if (speed > USB_SPEED_HIGH)
		return -EINVAL;

	value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
	tctrl = TCTRL_VALUE(value);
	pctrl = PCTRL_VALUE(value);

	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
	rpd_ctrl = RPD_CTRL_VALUE(value);

	/* ensure sleepwalk logic is disabled */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
	value &= ~UTMIP_MASTER_ENABLE(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));

	/* ensure sleepwalk logics are in low power mode */
	value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
	value |= UTMIP_PWR(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG);

	/* set debounce time */
	value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
	value &= ~UTMIP_LINE_DEB_CNT(~0);
	value |= UTMIP_LINE_DEB_CNT(0x1);
	padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL);

	/* ensure fake events of sleepwalk logic are desiabled */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port));
	value &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
		   UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_FAKE(port));

	/* ensure wake events of sleepwalk logic are not latched */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
	value &= ~UTMIP_LINE_WAKEUP_EN(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);

	/* disable wake event triggers of sleepwalk logic */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
	value &= ~UTMIP_WAKE_VAL(port, ~0);
	value |= UTMIP_WAKE_VAL_NONE(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));

	/* power down the line state detectors of the pad */
	value = padctl_pmc_readl(priv, PMC_USB_AO);
	value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
	padctl_pmc_writel(priv, value, PMC_USB_AO);

	/* save state per speed */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port));
	value &= ~SPEED(port, ~0);

	switch (speed) {
	case USB_SPEED_HIGH:
		value |= UTMI_HS(port);
		break;

	case USB_SPEED_FULL:
		value |= UTMI_FS(port);
		break;

	case USB_SPEED_LOW:
		value |= UTMI_LS(port);
		break;

	default:
		value |= UTMI_RST(port);
		break;
	}

	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SAVED_STATE(port));

	/* enable the trigger of the sleepwalk logic */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
	value |= UTMIP_LINEVAL_WALK_EN(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));

	/*
	 * Reset the walk pointer and clear the alarm of the sleepwalk logic,
	 * as well as capture the configuration of the USB2.0 pad.
	 */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
	value |= UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | UTMIP_CAP_CFG(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);

	/* program electrical parameters read from XUSB PADCTL */
	value = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG);
	value &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
	value |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl));
	padctl_pmc_writel(priv, value, PMC_UTMIP_TERM_PAD_CFG);

	value = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port));
	value &= ~RPD_CTRL_PX(~0);
	value |= RPD_CTRL_PX(rpd_ctrl);
	padctl_pmc_writel(priv, value, PMC_UTMIP_PAD_CFGX(port));

	/*
	 * Set up the pull-ups and pull-downs of the signals during the four
	 * stages of sleepwalk. If a device is connected, program sleepwalk
	 * logic to maintain a J and keep driving K upon seeing remote wake.
	 */
	value = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port));
	value = UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | UTMIP_USBOP_RPD_D;
	value |= UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | UTMIP_USBON_RPD_D;

	switch (speed) {
	case USB_SPEED_HIGH:
	case USB_SPEED_FULL:
		/* J state: D+/D- = high/low, K state: D+/D- = low/high */
		value |= UTMIP_HIGHZ_A;
		value |= UTMIP_AP_A;
		value |= UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D;
		break;

	case USB_SPEED_LOW:
		/* J state: D+/D- = low/high, K state: D+/D- = high/low */
		value |= UTMIP_HIGHZ_A;
		value |= UTMIP_AN_A;
		value |= UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D;
		break;

	default:
		value |= UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | UTMIP_HIGHZ_D;
		break;
	}

	padctl_pmc_writel(priv, value, PMC_UTMIP_SLEEPWALK_PX(port));

	/* power up the line state detectors of the pad */
	value = padctl_pmc_readl(priv, PMC_USB_AO);
	value &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
	padctl_pmc_writel(priv, value, PMC_USB_AO);

	usleep_range(50, 100);

	/* switch the electric control of the USB2.0 pad to PMC */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
	value |= UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));

	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
	value |= UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);

	/* set the wake signaling trigger events */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
	value &= ~UTMIP_WAKE_VAL(port, ~0);
	value |= UTMIP_WAKE_VAL_ANY(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));

	/* enable the wake detection */
	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
	value |= UTMIP_MASTER_ENABLE(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));

	value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
	value |= UTMIP_LINE_WAKEUP_EN(port);
	padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);

	return 0;
}