diff mbox series

[v2,04/12] phy: tegra: xusb: t210: add lane_iddq operations

Message ID 20200831044043.1561074-5-jckuo@nvidia.com
State New
Headers show
Series Tegra XHCI controller ELPG support | expand

Commit Message

JC Kuo Aug. 31, 2020, 4:40 a.m. UTC
As per Tegra210 TRM, before changing lane assignments, driver should
keep lanes in IDDQ and sleep state; after changing lane assignments,
driver should bring lanes out of IDDQ.
This commit implements the required operations.

Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
 drivers/phy/tegra/xusb-tegra210.c | 94 +++++++++++++++++++++++++++++++
 drivers/phy/tegra/xusb.c          |  6 ++
 drivers/phy/tegra/xusb.h          |  4 +-
 3 files changed, 103 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 3a2d9797fb9f..fe1ab440424d 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -198,6 +198,18 @@ 
 #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18)
 #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13)
 
+#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(x) (0x464 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ BIT(0)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD BIT(1)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK (0x3 << 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL (0x3 << 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD BIT(24)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ BIT(8)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD BIT(9)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK (0x3 << 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL (0x3 << 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD BIT(25)
+
 #define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860
 
 #define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864
@@ -209,6 +221,7 @@ 
 #define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c
 
 #define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960
+#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964
 
 #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40)
 #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16
@@ -1636,6 +1649,63 @@  static const struct tegra_xusb_pad_soc tegra210_hsic_pad = {
 	.ops = &tegra210_hsic_ops,
 };
 
+static void
+tegra210_uphy_lane_iddq_enable(struct tegra_xusb_padctl *padctl, unsigned lane)
+{
+	u32 val, offset;
+
+	if (lane <= 6)
+		offset = XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(lane);
+	else if (lane == 7)
+		offset = XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2;
+	else {
+		WARN(true, "invalid lane %u\n", lane);
+		return;
+	}
+
+	val = padctl_readl(padctl, offset);
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+	padctl_writel(padctl, val, offset);
+}
+
+static void
+tegra210_uphy_lane_iddq_disable(struct tegra_xusb_padctl *padctl, unsigned lane)
+{
+	u32 val, offset;
+
+	if (lane <= 6)
+		offset = XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(lane);
+	else if (lane == 7)
+		offset = XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2;
+	else {
+		WARN(true, "invalid lane %d\n", lane);
+		return;
+	}
+
+	val = padctl_readl(padctl, offset);
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+	val &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+	val |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+	padctl_writel(padctl, val, offset);
+}
+
+
 static const char *tegra210_pcie_functions[] = {
 	"pcie-x1",
 	"usb3-ss",
@@ -1808,9 +1878,21 @@  static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane)
 	kfree(pcie);
 }
 
+static void tegra210_pcie_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+	tegra210_uphy_lane_iddq_enable(lane->pad->padctl, lane->index);
+}
+
+static void tegra210_pcie_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+	tegra210_uphy_lane_iddq_disable(lane->pad->padctl, lane->index);
+}
+
 static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = {
 	.probe = tegra210_pcie_lane_probe,
 	.remove = tegra210_pcie_lane_remove,
+	.iddq_enable = tegra210_pcie_lane_iddq_enable,
+	.iddq_disable = tegra210_pcie_lane_iddq_disable,
 };
 
 static int tegra210_pcie_phy_init(struct phy *phy)
@@ -1971,9 +2053,21 @@  static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane)
 	kfree(sata);
 }
 
+static void tegra210_sata_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+	tegra210_uphy_lane_iddq_enable(lane->pad->padctl, lane->index + 7);
+}
+
+static void tegra210_sata_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+	tegra210_uphy_lane_iddq_disable(lane->pad->padctl, lane->index + 7);
+}
+
 static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = {
 	.probe = tegra210_sata_lane_probe,
 	.remove = tegra210_sata_lane_remove,
+	.iddq_enable = tegra210_sata_lane_iddq_enable,
+	.iddq_disable = tegra210_sata_lane_iddq_disable,
 };
 
 static int tegra210_sata_phy_init(struct phy *phy)
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index b48b590aa0c1..74abd67e3a31 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -321,11 +321,17 @@  static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
 	if (soc->num_funcs < 2)
 		return;
 
+	if (lane->pad->ops->iddq_enable && lane->pad->ops->iddq_disable)
+		lane->pad->ops->iddq_enable(lane);
+
 	/* choose function */
 	value = padctl_readl(padctl, soc->offset);
 	value &= ~(soc->mask << soc->shift);
 	value |= lane->function << soc->shift;
 	padctl_writel(padctl, value, soc->offset);
+
+	if (lane->pad->ops->iddq_enable && lane->pad->ops->iddq_disable)
+		lane->pad->ops->iddq_disable(lane);
 }
 
 static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 0c828694cf2d..485b692a3b15 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -22,6 +22,7 @@  struct phy;
 struct phy_provider;
 struct platform_device;
 struct regulator;
+struct tegra_xusb_padctl;
 
 /*
  * lanes
@@ -126,6 +127,8 @@  struct tegra_xusb_lane_ops {
 					 struct device_node *np,
 					 unsigned int index);
 	void (*remove)(struct tegra_xusb_lane *lane);
+	void (*iddq_enable)(struct tegra_xusb_lane *lane);
+	void (*iddq_disable)(struct tegra_xusb_lane *lane);
 };
 
 bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function);
@@ -134,7 +137,6 @@  bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function);
  * pads
  */
 struct tegra_xusb_pad_soc;
-struct tegra_xusb_padctl;
 
 struct tegra_xusb_pad_ops {
 	struct tegra_xusb_pad *(*probe)(struct tegra_xusb_padctl *padctl,