[PATCH 5/8] soc/tegra: pmc: support T210 USB 2.0 Sleepwalk

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This commit implements Tegra210 PMC USB 2.0 (UTMI and HSIC) Sleepwalk
programming sequence. With Sleepwalk enabled, XUSB host controller
can be put into ELPG (Engine Level PowerGate) state when controller
is idle to save power. The Sleepwalk logic is in charge of wake event
detection and maintain resume signal accordingly till XUSB host
controller is bring out of ELPG.

Signed-off-by: JC Kuo <jckuo@xxxxxxxxxx>
---
 drivers/soc/tegra/pmc.c | 462 ++++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/pmc.h |  13 ++
 2 files changed, 475 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 8878720dd779..8a143cfc83b3 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -142,6 +142,142 @@
 #define  TEGRA_SMC_PMC_READ	0xaa
 #define  TEGRA_SMC_PMC_WRITE	0xbb
 
+/* USB2 SLEEPWALK registers */
+#define UTMIP(_port, _offset1, _offset2) \
+		(((_port) <= 2) ? (_offset1) : (_offset2))
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG(x)	UTMIP(x, 0x1fc, 0x4d0)
+#define   UTMIP_MASTER_ENABLE(x)		UTMIP(x, BIT(8 * (x)), BIT(0))
+#define   UTMIP_FSLS_USE_PMC(x)			UTMIP(x, BIT(8 * (x) + 1), \
+							BIT(1))
+#define   UTMIP_PCTRL_USE_PMC(x)		UTMIP(x, BIT(8 * (x) + 2), \
+							BIT(2))
+#define   UTMIP_TCTRL_USE_PMC(x)		UTMIP(x, BIT(8 * (x) + 3), \
+							BIT(3))
+#define   UTMIP_WAKE_VAL(_port, _value)		(((_value) & 0xf) << \
+					(UTMIP(_port, 8 * (_port) + 4, 4)))
+#define   UTMIP_WAKE_VAL_NONE(_port)		UTMIP_WAKE_VAL(_port, 12)
+#define   UTMIP_WAKE_VAL_ANY(_port)		UTMIP_WAKE_VAL(_port, 15)
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1	(0x4d0)
+#define   UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x)	BIT((x) + 8)
+#define   UTMIP_RPD_CTRL_USE_PMC_PX(x)		BIT((x) + 16)
+
+#define PMC_UTMIP_MASTER_CONFIG		(0x274)
+#define   UTMIP_PWR(x)				UTMIP(x, BIT(x), BIT(4))
+#define   UHSIC_PWR(x)				BIT(3)
+
+#define PMC_USB_DEBOUNCE_DEL		(0xec)
+#define   DEBOUNCE_VAL(x)			(((x) & 0xffff) << 0)
+#define   UTMIP_LINE_DEB_CNT(x)			(((x) & 0xf) << 16)
+#define   UHSIC_LINE_DEB_CNT(x)			(((x) & 0xf) << 20)
+
+#define PMC_UTMIP_UHSIC_FAKE(x)		UTMIP(x, 0x218, 0x294)
+#define   UTMIP_FAKE_USBOP_VAL(x)		UTMIP(x, BIT(4 * (x)), BIT(8))
+#define   UTMIP_FAKE_USBON_VAL(x)		UTMIP(x, BIT(4 * (x) + 1), \
+							BIT(9))
+#define   UTMIP_FAKE_USBOP_EN(x)		UTMIP(x, BIT(4 * (x) + 2), \
+							BIT(10))
+#define   UTMIP_FAKE_USBON_EN(x)		UTMIP(x, BIT(4 * (x) + 3), \
+							BIT(11))
+
+#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x)	UTMIP(x, 0x200, 0x288)
+#define   UTMIP_LINEVAL_WALK_EN(x)		UTMIP(x, BIT(8 * (x) + 7), \
+							BIT(15))
+
+#define PMC_USB_AO			(0xf0)
+#define   USBOP_VAL_PD(x)			UTMIP(x, BIT(4 * (x)), BIT(20))
+#define   USBON_VAL_PD(x)			UTMIP(x, BIT(4 * (x) + 1), \
+							BIT(21))
+#define   STROBE_VAL_PD(x)			BIT(12)
+#define   DATA0_VAL_PD(x)			BIT(13)
+#define   DATA1_VAL_PD				BIT(24)
+
+#define PMC_UTMIP_UHSIC_SAVED_STATE(x)	UTMIP(x, 0x1f0, 0x280)
+#define   SPEED(_port, _value)			(((_value) & 0x3) << \
+						(UTMIP(_port, 8 * (_port), 8)))
+#define   UTMI_HS(_port)			SPEED(_port, 0)
+#define   UTMI_FS(_port)			SPEED(_port, 1)
+#define   UTMI_LS(_port)			SPEED(_port, 2)
+#define   UTMI_RST(_port)			SPEED(_port, 3)
+
+#define PMC_UTMIP_UHSIC_TRIGGERS		(0x1ec)
+#define   UTMIP_CLR_WALK_PTR(x)			UTMIP(x, BIT(x), BIT(16))
+#define   UTMIP_CAP_CFG(x)			UTMIP(x, BIT((x) + 4), BIT(17))
+#define   UTMIP_CLR_WAKE_ALARM(x)		UTMIP(x, BIT((x) + 12), \
+							BIT(19))
+#define   UHSIC_CLR_WALK_PTR			BIT(3)
+#define   UHSIC_CLR_WAKE_ALARM			BIT(15)
+
+#define PMC_UTMIP_SLEEPWALK_PX(x)	UTMIP(x, 0x204 + (4 * (x)), \
+							0x4e0)
+/* phase A */
+#define   UTMIP_USBOP_RPD_A			BIT(0)
+#define   UTMIP_USBON_RPD_A			BIT(1)
+#define   UTMIP_AP_A				BIT(4)
+#define   UTMIP_AN_A				BIT(5)
+#define   UTMIP_HIGHZ_A				BIT(6)
+/* phase B */
+#define   UTMIP_USBOP_RPD_B			BIT(8)
+#define   UTMIP_USBON_RPD_B			BIT(9)
+#define   UTMIP_AP_B				BIT(12)
+#define   UTMIP_AN_B				BIT(13)
+#define   UTMIP_HIGHZ_B				BIT(14)
+/* phase C */
+#define   UTMIP_USBOP_RPD_C			BIT(16)
+#define   UTMIP_USBON_RPD_C			BIT(17)
+#define   UTMIP_AP_C				BIT(20)
+#define   UTMIP_AN_C				BIT(21)
+#define   UTMIP_HIGHZ_C				BIT(22)
+/* phase D */
+#define   UTMIP_USBOP_RPD_D			BIT(24)
+#define   UTMIP_USBON_RPD_D			BIT(25)
+#define   UTMIP_AP_D				BIT(28)
+#define   UTMIP_AN_D				BIT(29)
+#define   UTMIP_HIGHZ_D				BIT(30)
+
+#define PMC_UTMIP_UHSIC_LINE_WAKEUP	(0x26c)
+#define   UTMIP_LINE_WAKEUP_EN(x)		UTMIP(x, BIT(x), BIT(4))
+#define   UHSIC_LINE_WAKEUP_EN			BIT(3)
+
+#define PMC_UTMIP_TERM_PAD_CFG		(0x1f8)
+#define   PCTRL_VAL(x)				(((x) & 0x3f) << 1)
+#define   TCTRL_VAL(x)				(((x) & 0x3f) << 7)
+
+#define PMC_UTMIP_PAD_CFGX(x)		(0x4c0 + (4 * (x)))
+#define   RPD_CTRL_PX(x)			(((x) & 0x1f) << 22)
+
+#define PMC_UHSIC_SLEEP_CFG	PMC_UTMIP_UHSIC_SLEEP_CFG(0)
+#define   UHSIC_MASTER_ENABLE			BIT(24)
+#define   UHSIC_WAKE_VAL(_value)		(((_value) & 0xf) << 28)
+#define   UHSIC_WAKE_VAL_SD10			UHSIC_WAKE_VAL(2)
+#define   UHSIC_WAKE_VAL_NONE			UHSIC_WAKE_VAL(12)
+
+#define PMC_UHSIC_FAKE			PMC_UTMIP_UHSIC_FAKE(0)
+#define   UHSIC_FAKE_STROBE_VAL			BIT(12)
+#define   UHSIC_FAKE_DATA_VAL			BIT(13)
+#define   UHSIC_FAKE_STROBE_EN			BIT(14)
+#define   UHSIC_FAKE_DATA_EN			BIT(15)
+
+#define PMC_UHSIC_SAVED_STATE		PMC_UTMIP_UHSIC_SAVED_STATE(0)
+#define   UHSIC_MODE(_value)			(((_value) & 0x1) << 24)
+#define   UHSIC_HS				UHSIC_MODE(0)
+#define   UHSIC_RST				UHSIC_MODE(1)
+
+#define PMC_UHSIC_SLEEPWALK_CFG		PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0)
+#define   UHSIC_WAKE_WALK_EN			BIT(30)
+#define   UHSIC_LINEVAL_WALK_EN			BIT(31)
+
+#define PMC_UHSIC_SLEEPWALK_P0		(0x210)
+#define   UHSIC_DATA0_RPD_A			BIT(1)
+#define   UHSIC_DATA0_RPU_B			BIT(11)
+#define   UHSIC_DATA0_RPU_C			BIT(19)
+#define   UHSIC_DATA0_RPU_D			BIT(27)
+#define   UHSIC_STROBE_RPU_A			BIT(2)
+#define   UHSIC_STROBE_RPD_B			BIT(8)
+#define   UHSIC_STROBE_RPD_C			BIT(16)
+#define   UHSIC_STROBE_RPD_D			BIT(24)
+
 struct tegra_powergate {
 	struct generic_pm_domain genpd;
 	struct tegra_pmc *pmc;
@@ -689,6 +825,332 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain)
 	return err;
 }
 
+/* T210 USB2 SLEEPWALK APIs */
+int tegra_pmc_utmi_phy_enable_sleepwalk(int port, enum usb_device_speed speed,
+					struct tegra_utmi_pad_config *config)
+{
+	u32 reg;
+
+	pr_debug("PMC %s : port %d, speed %d\n", __func__, port, speed);
+
+	/* ensure sleepwalk logic is disabled */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~UTMIP_MASTER_ENABLE(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	/* ensure sleepwalk logics are in low power mode */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_MASTER_CONFIG);
+	reg |= UTMIP_PWR(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_MASTER_CONFIG);
+
+	/* set debounce time */
+	reg = tegra_pmc_readl(pmc, PMC_USB_DEBOUNCE_DEL);
+	reg &= ~UTMIP_LINE_DEB_CNT(~0);
+	reg |= UTMIP_LINE_DEB_CNT(0x1);
+	tegra_pmc_writel(pmc, reg, PMC_USB_DEBOUNCE_DEL);
+
+	/* ensure fake events of sleepwalk logic are desiabled */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_FAKE(port));
+	reg &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
+			UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_FAKE(port));
+
+	/* ensure wake events of sleepwalk logic are not latched */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg &= ~UTMIP_LINE_WAKEUP_EN(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	/* disable wake event triggers of sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~UTMIP_WAKE_VAL(port, ~0);
+	reg |= UTMIP_WAKE_VAL_NONE(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	/* power down the line state detectors of the pad */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	/* save state per speed */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+	reg &= ~SPEED(port, ~0);
+	if (speed == USB_SPEED_HIGH)
+		reg |= UTMI_HS(port);
+	else if (speed == USB_SPEED_FULL)
+		reg |= UTMI_FS(port);
+	else if (speed == USB_SPEED_LOW)
+		reg |= UTMI_LS(port);
+	else
+		reg |= UTMI_RST(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+
+	/* enable the trigger of the sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
+	reg |= UTMIP_LINEVAL_WALK_EN(port);
+	tegra_pmc_writel(pmc, reg, 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
+	 */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_TRIGGERS);
+	reg |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) |
+		UTMIP_CAP_CFG(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_TRIGGERS);
+
+	/* program electrical parameters read from XUSB PADCTL */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_TERM_PAD_CFG);
+	reg &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
+	reg |= (TCTRL_VAL(config->tctrl) | PCTRL_VAL(config->pctrl));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_TERM_PAD_CFG);
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_PAD_CFGX(port));
+	reg &= ~RPD_CTRL_PX(~0);
+	reg |= RPD_CTRL_PX(config->rpd_ctrl);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_PAD_CFGX(port));
+
+	/* setup the pull-ups and pull-downs of the signals during the four
+	 * stages of sleepwalk.
+	 * if device is connected, program sleepwalk logic to maintain a J and
+	 * keep driving K upon seeing remote wake.
+	 */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_SLEEPWALK_PX(port));
+	reg = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C |
+		UTMIP_USBOP_RPD_D);
+	reg |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C |
+		UTMIP_USBON_RPD_D);
+	if (speed == USB_SPEED_UNKNOWN) {
+		reg |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C |
+			UTMIP_HIGHZ_D);
+	} else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) {
+		/* J state: D+/D- = high/low, K state: D+/D- = low/high */
+		reg |= UTMIP_HIGHZ_A;
+		reg |= UTMIP_AP_A;
+		reg |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D);
+	} else if (speed == USB_SPEED_LOW) {
+		/* J state: D+/D- = low/high, K state: D+/D- = high/low */
+		reg |= UTMIP_HIGHZ_A;
+		reg |= UTMIP_AN_A;
+		reg |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D);
+	}
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_SLEEPWALK_PX(port));
+
+	/* power up the line state detectors of the pad */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	usleep_range(50, 100);
+
+	/* switch the electric control of the USB2.0 pad to PMC */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
+			UTMIP_TCTRL_USE_PMC(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+	reg |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) |
+			UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+	/* set the wake signaling trigger events */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~UTMIP_WAKE_VAL(port, ~0);
+	reg |= UTMIP_WAKE_VAL_ANY(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	/* enable the wake detection */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg |= UTMIP_MASTER_ENABLE(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg |= UTMIP_LINE_WAKEUP_EN(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_pmc_utmi_phy_enable_sleepwalk);
+
+int tegra_pmc_utmi_phy_disable_sleepwalk(int port)
+{
+	u32 reg;
+
+	pr_debug("PMC %s : port %d\n", __func__, port);
+
+	/* disable the wake detection */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~UTMIP_MASTER_ENABLE(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg &= ~UTMIP_LINE_WAKEUP_EN(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	/* switch the electric control of the USB2.0 pad to XUSB or USB2 */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
+			UTMIP_TCTRL_USE_PMC(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+	reg &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) |
+			UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+	/* disable wake event triggers of sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+	reg &= ~UTMIP_WAKE_VAL(port, ~0);
+	reg |= UTMIP_WAKE_VAL_NONE(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+	/* power down the line state detectors of the port */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	/* clear alarm of the sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_TRIGGERS);
+	reg |= UTMIP_CLR_WAKE_ALARM(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_TRIGGERS);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_pmc_utmi_phy_disable_sleepwalk);
+
+int tegra_pmc_hsic_phy_enable_sleepwalk(int port)
+{
+	u32 reg;
+
+	pr_debug("PMC %s : port %dn", __func__, port);
+
+	/* ensure sleepwalk logic is disabled */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg &= ~UHSIC_MASTER_ENABLE;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	/* ensure sleepwalk logics are in low power mode */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_MASTER_CONFIG);
+	reg |= UHSIC_PWR(port);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_MASTER_CONFIG);
+
+	/* set debounce time */
+	reg = tegra_pmc_readl(pmc, PMC_USB_DEBOUNCE_DEL);
+	reg &= ~UHSIC_LINE_DEB_CNT(~0);
+	reg |= UHSIC_LINE_DEB_CNT(0x1);
+	tegra_pmc_writel(pmc, reg, PMC_USB_DEBOUNCE_DEL);
+
+	/* ensure fake events of sleepwalk logic are desiabled */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_FAKE);
+	reg &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL |
+			UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN);
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_FAKE);
+
+	/* ensure wake events of sleepwalk logic are not latched */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg &= ~UHSIC_LINE_WAKEUP_EN;
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	/* disable wake event triggers of sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg &= ~UHSIC_WAKE_VAL(~0);
+	reg |= UHSIC_WAKE_VAL_NONE;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	/* power down the line state detectors of the port */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	/* save state, HSIC always comes up as HS */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SAVED_STATE);
+	reg &= ~UHSIC_MODE(~0);
+	reg |= UHSIC_HS;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SAVED_STATE);
+
+	/* enable the trigger of the sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEPWALK_CFG);
+	reg |= (UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN);
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEPWALK_CFG);
+
+	/* reset the walk pointer and clear the alarm of the sleepwalk logic,
+	 * as well as capture the configuration of the USB2.0 port
+	 */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_TRIGGERS);
+	reg |= (UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM);
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_TRIGGERS);
+
+	/* setup the pull-ups and pull-downs of the signals during the four
+	 * stages of sleepwalk.
+	 * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake
+	 */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEPWALK_P0);
+	reg = (UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C |
+		UHSIC_DATA0_RPU_D);
+	reg |= (UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C |
+		UHSIC_STROBE_RPD_D);
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEPWALK_P0);
+
+	/* power up the line state detectors of the port */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg &= ~(STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	usleep_range(50, 100);
+
+	/* set the wake signaling trigger events */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg &= ~UHSIC_WAKE_VAL(~0);
+	reg |= UHSIC_WAKE_VAL_SD10;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	/* enable the wake detection */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg |= UHSIC_MASTER_ENABLE;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg |= UHSIC_LINE_WAKEUP_EN;
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_pmc_hsic_phy_enable_sleepwalk);
+
+int tegra_pmc_hsic_phy_disable_sleepwalk(int port)
+{
+	u32 reg;
+
+	pr_debug("PMC %s : port %dn", __func__, port);
+
+	/* disable the wake detection */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg &= ~UHSIC_MASTER_ENABLE;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+	reg &= ~UHSIC_LINE_WAKEUP_EN;
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+	/* disable wake event triggers of sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UHSIC_SLEEP_CFG);
+	reg &= ~UHSIC_WAKE_VAL(~0);
+	reg |= UHSIC_WAKE_VAL_NONE;
+	tegra_pmc_writel(pmc, reg, PMC_UHSIC_SLEEP_CFG);
+
+	/* power down the line state detectors of the port */
+	reg = tegra_pmc_readl(pmc, PMC_USB_AO);
+	reg |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
+	tegra_pmc_writel(pmc, reg, PMC_USB_AO);
+
+	/* clear alarm of the sleepwalk logic */
+	reg = tegra_pmc_readl(pmc, PMC_UTMIP_UHSIC_TRIGGERS);
+	reg |= UHSIC_CLR_WAKE_ALARM;
+	tegra_pmc_writel(pmc, reg, PMC_UTMIP_UHSIC_TRIGGERS);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_pmc_hsic_phy_disable_sleepwalk);
+
 /**
  * tegra_powergate_power_on() - power on partition
  * @id: partition ID
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
index 57e58faf660b..987109e79da9 100644
--- a/include/soc/tegra/pmc.h
+++ b/include/soc/tegra/pmc.h
@@ -11,6 +11,7 @@
 #define __SOC_TEGRA_PMC_H__
 
 #include <linux/reboot.h>
+#include <linux/usb/ch9.h>
 
 #include <soc/tegra/pm.h>
 
@@ -171,6 +172,18 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
 void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
 void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
 
+/* T210 USB2 SLEEPWALK APIs */
+struct tegra_utmi_pad_config {
+	u32 tctrl;
+	u32 pctrl;
+	u32 rpd_ctrl;
+};
+int tegra_pmc_utmi_phy_enable_sleepwalk(int port, enum usb_device_speed speed,
+					struct tegra_utmi_pad_config *config);
+int tegra_pmc_utmi_phy_disable_sleepwalk(int port);
+int tegra_pmc_hsic_phy_enable_sleepwalk(int port);
+int tegra_pmc_hsic_phy_disable_sleepwalk(int port);
+
 #else
 static inline int tegra_powergate_power_on(unsigned int id)
 {
-- 
2.17.1




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux