[PATCH v7 03/14] phy: tegra: xusb: Move usb3 port init for Tegra210

2021-01-19 Thread JC Kuo
The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy
.power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane
is assigned for XUSB super-speed.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   mutex_lock()/mutex_unlock() fix
   update copyright string
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 316 +-
 drivers/phy/tegra/xusb.c  |   4 +-
 drivers/phy/tegra/xusb.h  |   4 +-
 3 files changed, 180 insertions(+), 144 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..4dc9286ec1b8 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (C) 2015 Google, Inc.
  */
 
@@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
 }
 
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+   { 0, "pcie", 6 },
+   { 1, "pcie", 5 },
+   { 2, "pcie", 0 },
+   { 2, "pcie", 3 },
+   { 3, "pcie", 4 },
+   { 3, "sata", 0 },
+   { 0, NULL,   0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+   const struct tegra_xusb_lane_map *map;
+
+   for (map = tegra210_usb3_map; map->type; map++) {
+   if (map->index == lane->index &&
+   strcmp(map->type, lane->pad->soc->name) == 0) {
+   dev_dbg(lane->pad->padctl->dev, "lane = %s map to port 
= usb3-%d\n",
+   lane->pad->soc->lanes[lane->index].name, 
map->port);
+   return map->port;
+   }
+   }
+
+   return -EINVAL;
+}
+
 /* must be called under padctl->lock */
 static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
@@ -470,19 +496,14 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
 
-   mutex_lock(>lock);
-
if (WARN_ON(pcie->enable == 0))
-   goto unlock;
+   return;
 
if (--pcie->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 /* must be called under padctl->lock */
@@ -712,19 +733,14 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
 
-   mutex_lock(>lock);
-
if (WARN_ON(sata->enable == 0))
-   goto unlock;
+   return;
 
if (--sata->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
@@ -1599,6 +1615,128 @@ static const struct tegra_xusb_lane_soc 
tegra210_pcie_lanes[] = {
TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
 };
 
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+   int port;
+
+   if (!lane || !lane->pad || !lane->pad->padctl)
+   return NULL;
+
+   port = tegra210_usb3_lane_map(lane);
+   if (port < 0)
+   return NULL;
+
+   return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+   struct device *dev = >dev;
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+   unsigned int index;
+   u32 value;
+
+   if (!usb3) {
+   dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+   return -ENODEV;
+   }
+
+   index = usb3->base.index;
+
+   value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+   if (!usb3->internal)
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+   

[PATCH v7 05/14] phy: tegra: xusb: Add Tegra210 lane_iddq operation

2021-01-19 Thread JC Kuo
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 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   add 'misc_ctl2' data member to UPHY lane for carrying MISC_PAD_PX_CTL2 offset
   tegra210_uphy_lane_iddq_[enable/disable]() to access 'misc_ctl2' data member

 drivers/phy/tegra/xusb-tegra210.c | 82 ---
 drivers/phy/tegra/xusb.c  |  6 +++
 drivers/phy/tegra/xusb.h  |  6 +++
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index faacb866cd1f..b038d032fea1 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
@@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad 
= {
.ops = _hsic_ops,
 };
 
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc)
\
+   {   \
+   .name = _name,  \
+   .offset = _offset,  \
+   .shift = _shift,\
+   .mask = _mask,  \
+   .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions),  \
+   .funcs = tegra210_##_type##_functions,  \
+   .regs.misc_ctl2 = _misc,\
+   }
+
 static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1648,13 +1710,13 @@ sta

[PATCH v7 06/14] phy: tegra: xusb: Add sleepwalk and suspend/resume

2021-01-19 Thread JC Kuo
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.

Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
and can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB UPHY individually.

  - tegra_xusb_padctl_enable_phy_sleepwalk()
  - tegra_xusb_padctl_disable_phy_sleepwalk()

Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB PHY individually.

  - tegra_xusb_padctl_enable_phy_wake()
  - tegra_xusb_padctl_disable_phy_wake()
  - tegra_xusb_padctl_remote_wake_detected()

This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
  - tegra_xusb_padctl_suspend_noirq()
  - tegra_xusb_padctl_resume_noirq()

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   add 'Acked-by: Thierry Reding '
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   commit message improvement, no change in code

 drivers/phy/tegra/xusb.c   | 82 ++
 drivers/phy/tegra/xusb.h   |  8 
 include/linux/phy/tegra/xusb.h | 10 -
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index a34d304677bb..0aadac678191 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1273,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct 
platform_device *pdev)
return err;
 }
 
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+   return padctl->soc->ops->suspend_noirq(padctl);
+
+   return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+   return padctl->soc->ops->resume_noirq(padctl);
+
+   return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
 static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+   .pm = _xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1343,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy,
+  enum usb_device_speed speed)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_sleepwalk)
+   return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_sleepwalk)
+   return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct 
phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_wake)
+   return lane->pad->ops->enable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_wake)
+   return lane->pad->ops->disable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(ph

[PATCH v7 07/14] soc/tegra: pmc: Provide USB sleepwalk register map

2021-01-19 Thread JC Kuo
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   commit message improvement
   remove an unnecessary type cast when invokes devm_regmap_init()
v3:
   commit message improvement
   drop regmap_reg() usage
   rename 'reg' with 'offset'
   rename 'val' with 'value'
   drop '__force' when invokes devm_regmap_init()
   print error code of devm_regmap_init()
   move devm_regmap_init() a litter bit earlier
   explicitly set '.has_usb_sleepwalk=false'

 drivers/soc/tegra/pmc.c | 94 +
 1 file changed, 94 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index df9a5ca8c99c..a619a23f9592 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -43,6 +43,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -102,6 +103,9 @@
 
 #define PMC_PWR_DET_VALUE  0xe4
 
+#define PMC_USB_DEBOUNCE_DEL   0xec
+#define PMC_USB_AO 0xf0
+
 #define PMC_SCRATCH41  0x140
 
 #define PMC_WAKE2_MASK 0x160
@@ -133,6 +137,13 @@
 #define IO_DPD2_STATUS 0x1c4
 #define SEL_DPD_TIM0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS   0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG  0x1fc
+#define PMC_UTMIP_UHSIC_FAKE   0x218
+
 #define PMC_SCRATCH54  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT  8
 #define  PMC_SCRATCH54_ADDR_SHIFT  0
@@ -145,8 +156,18 @@
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL0x270
+#define PMC_UTMIP_MASTER_CONFIG0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS  0x27c
+#define PMC_UTMIP_MASTER2_CONFIG   0x29c
+
 #define GPU_RG_CNTRL   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0 0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3 0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
bool has_blink_output;
+   bool has_usb_sleepwalk;
 };
 
 /**
@@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
*pmc,
 err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+   regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+   regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+   regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+   regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
PMC_UTMIP_UHSIC_LINE_WAKEUP),
+   regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+   regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+   regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+   regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+   .yes_ranges = pmc_usb_sleepwalk_ranges,
+   .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned 
int *value)
+{
+   struct tegra_pmc *pmc = context;
+
+   *value = tegra_pmc_readl(pmc, offset);
+   return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, 
unsigned int value)
+{
+   struct tegra_pmc *pmc = context;
+
+   tegra_pmc_writel(pmc, value, offset);
+   return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+   .name = "usb_sleepwalk",
+   .reg_bits = 32,
+   .val_bits = 32,
+   .reg_stride = 4,
+   .fast_io = true,
+   .rd_table = _usb_sleepwalk_table,
+   .wr_table = _usb_sleepwalk_table,
+   .reg_read = tegra_pmc_regmap_readl,
+   .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+   struct regmap *regmap;
+   int err;
+
+   if (pmc->soc->has_usb_sleepwalk) {
+   regmap = devm_regmap_init(pmc->dev, NULL, pmc, 
_sleepwalk_regmap_config);
+   if (IS_ERR(regmap)) {
+   err = PTR_ERR(regmap);
+   dev_err(pmc->dev, "failed to allocate register map 
(%d)\n", err);
+   return err;
+ 

[PATCH v7 11/14] phy: tegra: xusb: Tegra210 host mode VBUS control

2021-01-19 Thread JC Kuo
To support XUSB host controller ELPG, this commit moves VBUS control
.phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit().
When XUSB host controller enters ELPG, host driver invokes
.phy_power_off(), VBUS should remain ON so that USB devices will not
disconnect. VBUS can be turned OFF when host driver invokes
.phy_exit() which indicates disabling a USB port.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Add wake/sleepwalk for Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 52 ---
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 8af73ba78ad7..9d39f812fb43 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1819,8 +1819,25 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   unsigned int index = lane->index;
+   struct tegra_xusb_usb2_port *port;
+   int err;
u32 value;
 
+   port = tegra_xusb_find_usb2_port(padctl, index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_enable(port->supply);
+   if (err)
+   return err;
+   }
+
+   mutex_lock(>lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
   XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -1828,11 +1845,30 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
 
+   mutex_unlock(>lock);
+
return 0;
 }
 
 static int tegra210_usb2_phy_exit(struct phy *phy)
 {
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb2_port *port;
+   int err;
+
+   port = tegra_xusb_find_usb2_port(padctl, lane->index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", 
lane->index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_disable(port->supply);
+   if (err)
+   return err;
+   }
+
return 0;
 }
 
@@ -1953,6 +1989,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
priv = to_tegra210_xusb_padctl(padctl);
 
+   mutex_lock(>lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -2046,14 +2084,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
  XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-   if (port->supply && port->mode == USB_DR_MODE_HOST) {
-   err = regulator_enable(port->supply);
-   if (err)
-   return err;
-   }
-
-   mutex_lock(>lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(>lock);
@@ -2062,7 +2092,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
err = clk_prepare_enable(pad->clk);
if (err)
-   goto disable_regulator;
+   goto out;
 
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -2094,8 +2124,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
return 0;
 
-disable_regulator:
-   regulator_disable(port->supply);
+out:
mutex_unlock(>lock);
return err;
 }
@@ -2154,7 +2183,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
 
 out:
-   regulator_disable(port->supply);
mutex_unlock(>lock);
return 0;
 }
-- 
2.25.1



[PATCH v7 12/14] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2021-01-19 Thread JC Kuo
This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
sleepwalk operations.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   add 'Acked-by: Thierry Reding '
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'

 drivers/phy/tegra/xusb-tegra186.c | 558 +-
 1 file changed, 557 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c 
b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..2208c26f8af9 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -113,6 +113,117 @@
 #define  ID_OVERRIDE_FLOATING  ID_OVERRIDE(8)
 #define  ID_OVERRIDE_GROUNDED  ID_OVERRIDE(0)
 
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL   (0x4)
+#define   UHSIC_LINE_DEB_CNT(x)(((x) & 0xf) << 4)
+#define   UTMIP_LINE_DEB_CNT(x)((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x)  (0x40 + (x) * 4)
+#define   CLR_WALK_PTR (1 << 0)
+#define   CAP_CFG  (1 << 1)
+#define   CLR_WAKE_ALARM   (1 << 3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x)  (0x60 + (x) * 4)
+#define   HSIC_CLR_WALK_PTR(1 << 0)
+#define   HSIC_CLR_WAKE_ALARM  (1 << 3)
+#define   HSIC_CAP_CFG (1 << 4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x)   (0x70 + (x) * 4)
+#define   SPEED(x) ((x) & 0x3)
+#define UTMI_HSSPEED(0)
+#define UTMI_FSSPEED(1)
+#define UTMI_LSSPEED(2)
+#define UTMI_RST   SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x)   (0x90 + (x) * 4)
+#define   MODE(x)  ((x) & 0x1)
+#define   MODE_HS  MODE(0)
+#define   MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define   FAKE_USBOP_VAL   (1 << 0)
+#define   FAKE_USBON_VAL   (1 << 1)
+#define   FAKE_USBOP_EN(1 << 2)
+#define   FAKE_USBON_EN(1 << 3)
+#define   FAKE_STROBE_VAL  (1 << 0)
+#define   FAKE_DATA_VAL(1 << 1)
+#define   FAKE_STROBE_EN   (1 << 2)
+#define   FAKE_DATA_EN (1 << 3)
+#define   WAKE_WALK_EN (1 << 14)
+#define   MASTER_ENABLE(1 << 15)
+#define   LINEVAL_WALK_EN  (1 << 16)
+#define   WAKE_VAL(x)  (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE  WAKE_VAL(12)
+#define WAKE_VAL_ANY   WAKE_VAL(15)
+#define WAKE_VAL_DS10  WAKE_VAL(2)
+#define   LINE_WAKEUP_EN   (1 << 21)
+#define   MASTER_CFG_SEL   (1 << 22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define   USBOP_RPD_A  (1 << 0)
+#define   USBON_RPD_A  (1 << 1)
+#define   AP_A (1 << 4)
+#define   AN_A (1 << 5)
+#define   HIGHZ_A  (1 << 6)
+/* phase B */
+#define   USBOP_RPD_B  (1 << 8)
+#define   USBON_RPD_B  (1 << 9)
+#define   AP_B (1 << 12)
+#define   AN_B (1 << 13)
+#define   HIGHZ_B  (1 << 14)
+/* phase C */
+#define   USBOP_RPD_C  (1 << 16)
+#define   USBON_RPD_C  (1 << 17)
+#define   AP_C (1 << 20)
+#define   AN_C (1 << 21)
+#define   HIGHZ_C  (1 << 22)
+/* phase D */
+#define   USB

[PATCH v7 13/14] usb: host: xhci-tegra: Unlink power domain devices

2021-01-19 Thread JC Kuo
This commit unlinks xhci-tegra platform device with SS/host power
domain devices. Reasons for this change is - at ELPG entry, PHY
sleepwalk and wake configuration need to be done before powering
down SS/host partitions, and PHY need be powered off after powering
down SS/host partitions. Sequence looks like roughly below:

  tegra_xusb_enter_elpg() -> xhci_suspend()
  -> enable PHY sleepwalk and wake if needed
  -> power down SS/host partitions
  -> power down PHY

If SS/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.

This commit introduces:
  1. tegra_xusb_unpowergate_partitions() to power up SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_get_sync() to request power
 driver to power up partitions; If power domain devices are not
 available, tegra_powergate_sequence_power_up() will be used to
 power up partitions.

  2. tegra_xusb_powergate_partitions() to power down SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_put_sync() to request power
 driver to power down partitions; If power domain devices are not
 available, tegra_powergate_power_off() will be used to power down
 partitions.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   add 'Acked-by: Thierry Reding '
v6:
   no change
v5:
   no change
v4:
   commit message improvement
   update copyright string
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 206 ++
 1 file changed, 112 insertions(+), 94 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 50bb91b6a4b8..5b39a739f8f0 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2,7 +2,7 @@
 /*
  * NVIDIA Tegra xHCI host controller driver
  *
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
  * Copyright (C) 2014 Google, Inc.
  */
 
@@ -249,8 +249,7 @@ struct tegra_xusb {
 
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
-   struct device_link *genpd_dl_host;
-   struct device_link *genpd_dl_ss;
+   bool use_genpd;
 
struct phy **phys;
unsigned int num_phys;
@@ -821,36 +820,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
-   regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-   tegra_xusb_clk_disable(tegra);
-
return 0;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-   int err;
-
-   err = tegra_xusb_clk_enable(tegra);
-   if (err) {
-   dev_err(dev, "failed to enable clocks: %d\n", err);
-   return err;
-   }
-
-   err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
-   if (err) {
-   dev_err(dev, "failed to enable regulators: %d\n", err);
-   goto disable_clk;
-   }
-
return 0;
-
-disable_clk:
-   tegra_xusb_clk_disable(tegra);
-   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1026,10 +1001,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb 
*tegra)
 static void tegra_xusb_powerdomain_remove(struct device *dev,
  struct tegra_xusb *tegra)
 {
-   if (tegra->genpd_dl_ss)
-   device_link_del(tegra->genpd_dl_ss);
-   if (tegra->genpd_dl_host)
-   device_link_del(tegra->genpd_dl_host);
+   if (!tegra->use_genpd)
+   return;
+
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1055,20 +1029,84 @@ static int tegra_xusb_powerdomain_init(struct device 
*dev,
return err;
}
 
-   tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
-  DL_FLAG_PM_RUNTIME |
-  DL_FLAG_STATELESS);
-   if (!tegra->genpd_dl_host) {
-   dev_err(dev, "adding host device link failed!\n");
-   return -ENODEV;
+   tegra->use_genpd = true;
+
+   return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+   struct device *dev = tegra->dev;
+   int rc;
+
+   if (tegra->use_genpd) {
+   rc = pm_runtime_get_sync(tegra->

[PATCH v7 01/14] clk: tegra: Add PLLE HW power sequencer control

2021-01-19 Thread JC Kuo
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.

XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.

PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.

This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
   PLLE hardware sequencer at proper time.

2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
   check whether PLLE hardware sequencer has been enabled or not.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   update copyright strings
v3:
   rename 'val' with 'value

 drivers/clk/tegra/clk-tegra210.c | 53 +++-
 include/linux/clk/tegra.h|  4 ++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 68cbb98af567..b9099012dc7b 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2014 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020 NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLRE_BASE_DEFAULT_MASK0x1c00
 #define PLLRE_MISC0_WRITE_MASK 0x67ff
 
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET   (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE(1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL  (1 << 4)
+#define PLLE_AUX_SS_SWCTL  (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE(1 << 24)
+
 /* PLLX */
 #define PLLX_USE_DYN_RAMP  1
 #define PLLX_BASE_LOCK (1 << 27)
@@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLU_MISC0_WRITE_MASK  0xbfff
 #define PLLU_MISC1_WRITE_MASK  0x0007
 
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+   u32 value;
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   if (value & PLLE_AUX_SEQ_ENABLE)
+   return true;
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+   u32 value;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   return 0;
+
+   /* skip if PLLE is not enabled yet */
+   value = readl_relaxed(clk_base + PLLE_MISC0);
+   if (!(value & PLLE_MISC_LOCK))
+   return -EIO;
+
+   value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+   writel_relaxed(value, clk_base + PLLE_MISC0);
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+   value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   value |= PLLE_AUX_SEQ_ENABLE;
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
 void tegra210_xusb_pll_hw_control_enable(void)
 {
u32 val;
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index eb016fc9cc0b..f7ff722a03dd 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #ifndef __LINUX_CLK_TEGRA_H_
@@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void)
 }
 #endif
 
+extern int tegra210_plle_hw_sequence_start(void);
+extern bool tegra210_plle_hw_sequence_is_enabled(void);
 extern void tegra210_xusb_pll_hw_control_enable(void);
 extern void tegra210_xusb_pll_hw_sequence_start(void);
 extern void tegra210_sata_pll_hw_control_enable(void);
-- 
2.25.1



[PATCH v7 14/14] xhci: tegra: Enable ELPG for runtime/system PM

2021-01-19 Thread JC Kuo
This commit implements the complete programming sequence for ELPG
entry and exit.

 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).

 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.

At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.

Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.

The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   add 'Acked-by: Thierry Reding '
v6:
   fix compiling warning: extra tokens at end of #ifdef directive
v5:
   avoid using xhci_get_rhub()
   protect ELPG routines with (CONFIG_PM || CONFIG_PM_SLEEP)
v4:
   reshuffle the code to avoid these pre-declarations
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 407 ++
 1 file changed, 370 insertions(+), 37 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 5b39a739f8f0..ce97ff054c68 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -15,9 +15,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -224,6 +226,7 @@ struct tegra_xusb {
 
int xhci_irq;
int mbox_irq;
+   int padctl_irq;
 
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -269,6 +272,7 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
 
+   bool suspended;
struct tegra_xusb_context context;
 };
 
@@ -665,6 +669,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
mutex_lock(>lock);
 
+   if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+   goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(, value);
 
@@ -678,6 +685,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
tegra_xusb_mbox_handle(tegra, );
 
+out:
mutex_unlock(>lock);
return IRQ_HANDLED;
 }
@@ -818,16 +826,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
}
 }
 
-static int tegra_xusb_runtime_suspend(struct device *dev)
-{
-   return 0;
-}
-
-static int tegra_xusb_runtime_resume(struct device *dev)
-{
-   return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int tegra_xusb_init_context(struct tegra_xusb *tegra)
 {
@@ -1128,6 +1126,24 @@ static int __tegra_xusb_enable_firmware_messages(struct 
tegra_xusb *tegra)
return err;
 }
 
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+   struct tegra_xusb *tegra = data;
+
+   mutex_lock(>lock);
+
+   if (tegra->suspended) {
+   mutex_unlock(>lock);
+   return IRQ_HANDLED;
+   }
+
+   mutex_unlock(>lock);
+
+   pm_runtime_resume(tegra->dev);
+
+   return IRQ_HANDLED;
+}
+
 static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
 {
int err;
@@ -1251,6 +1267,52 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
 }
 
+#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   struct tegra_xusb_padctl *padctl = tegra->padctl;
+   unsigned int i;
+   int port;
+
+   for (i = 0; i < tegra->num_usb_phys; i++) {
+   if (is_usb2_otg_phy(tegra, i)) {
+   port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+   if ((port >= 0) && (index == (unsigned int)port))
+   return true;
+   }
+   }
+
+   return false;
+}
+
+static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, 
unsigned int index)
+{
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0)
+   return true;
+
+   if (strcmp(tegra->soc->phy_types[ph

[PATCH v7 10/14] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2021-01-19 Thread JC Kuo
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk
routines. Sleepwalk logic is in PMC (always-on) hardware block.
PMC driver provides managed access to the sleepwalk registers
via regmap framework.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   add 'Acked-by: Thierry Reding '
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
   remove a blank line 
   rename 'pmc_dev' with 'pdev'
   remove 'struct device_node *np' 
   rename label 'no_pmc' with 'out'
   defer .probe() if PMC driver is yet to load 

v3:
   rename 'pmc_reg" with 'regmap' and move to the top of 'struct 
tegra210_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'
   rename tegra_pmc_*() with tegra210_pmc_*()
   remove VBUS ON/OFF control change

 drivers/phy/tegra/xusb-tegra210.c | 930 ++
 1 file changed, 930 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index b038d032fea1..8af73ba78ad7 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -16,6 +16,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 
@@ -52,6 +54,20 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define   USB2_PORT_WAKE_INTERRUPT_ENABLE(x)  BIT((x))
+#define   USB2_PORT_WAKEUP_EVENT(x)   BIT((x) + 7)
+#define   SS_PORT_WAKE_INTERRUPT_ENABLE(x)BIT((x) + 14)
+#define   SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define   USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define   USB2_HSIC_PORT_WAKEUP_EVENT(x)  BIT((x) + 30)
+#define   ALL_WAKE_EVENTS ( \
+   USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+   USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+   SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+   SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+   USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define   RPD_CTRL(x)  (((x) & 0x1f) << 26)
+#define   RPD_CTRL_VALUE(x)(((x) >> 26) & 0x1f)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define   TCTRL_VALUE(x)(((x) & 0x3f) >> 0)
+#define   PCTRL_VALUE(x)(((x) >> 6) & 0x3f)
 
 #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
 #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -251,16 +271,161 @@
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
+/* 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_PW

[PATCH v7 09/14] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2021-01-19 Thread JC Kuo
This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
PHY driver. It is a phandle and specifier referring to the Tegra210
pmc@7000e400 node.

Signed-off-by: JC Kuo 
Acked-by: Rob Herring 
---
v7:
   no change
v6:
   no change
v5:
   replace "pmc@7000e400 node" -> with "PMC node"
v4:
   new change to document "nvidia,pmc" prop

 .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 38c5fa21f435..b62397d2bb0c 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -54,6 +54,7 @@ For Tegra210:
 - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
 - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
 - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
+- nvidia,pmc: phandle and specifier referring to the Tegra210 PMC node.
 
 For Tegra186:
 - avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY
-- 
2.25.1



[PATCH v7 08/14] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2021-01-19 Thread JC Kuo
PMC driver provides USB sleepwalk registers access to XUSB PADCTL
driver. This commit adds a "nvidia,pmc" property which points to
PMC node to XUSB PADCTL device node.

Signed-off-by: JC Kuo 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 4fbf8c15b0a1..83f6d11c578b 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1043,6 +1043,7 @@ padctl: padctl@7009f000 {
interrupts = ;
resets = <_car 142>;
reset-names = "padctl";
+   nvidia,pmc =  <_pmc>;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v7 04/14] phy: tegra: xusb: Rearrange UPHY init on Tegra210

2021-01-19 Thread JC Kuo
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:

1. PLLE hardware power sequencer has to be enabled only after both
   PEX UPHY PLL and SATA UPHY PLL are initialized.
   tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()

2. At cold boot and SC7 exit, the following bits must be cleared after
   PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
   a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
   b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
   c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN

   tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
   charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
   will be cleared by tegra210_aux_mux_lp0_clamp_disable().

3. Once UPHY PLL hardware power sequencer is enabled, do not assert
   reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   merge v5 [04/15] phy: tegra: xusb: tegra210: Do not reset UPHY PLL   
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   make separate changes
   use "unsigned int" instead "int" type for PHY index
   add blank line for better readability

 drivers/phy/tegra/xusb-tegra210.c | 197 +++---
 drivers/phy/tegra/xusb.h  |   4 +-
 2 files changed, 103 insertions(+), 98 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 4dc9286ec1b8..faacb866cd1f 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
 
-   if (pcie->enable > 0) {
-   pcie->enable++;
+   if (pcie->enable)
return 0;
-   }
 
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
 
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 
tegra210_xusb_pll_hw_sequence_start();
 
-   pcie->enable++;
+skip_pll_init:
+   pcie->enable = true;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
return 0;
 
@@ -495,29 +504,44 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+   u32 value;
+   unsigned int i;
 
-   if (WARN_ON(pcie->enable == 0))
+   if (WARN_ON(!pcie->enable))
return;
 
-   if (--pcie->enable > 0)
-   return;
+   pcie->enable = false;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
-   reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
 }
 
 /* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool 
usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+   struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
+   bool usb;
 
-   if (sata->enable > 0) {
-   sata->enable++;
+   if (sata->enable)
+   return 0;
+
+   if (IS_ERR(lane))
return 0;
-   }
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
+   usb = tegra_xusb_lane_check(lane, "usb3-ss");
 
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -718,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct 
tegra_xusb_padctl *padctl, bool usb)
 
tegra2

[PATCH v7 00/14] Tegra XHCI controller ELPG support

2021-01-19 Thread JC Kuo
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.

JC Kuo (14):
  clk: tegra: Add PLLE HW power sequencer control
  clk: tegra: Don't enable PLLE HW sequencer at init
  phy: tegra: xusb: Move usb3 port init for Tegra210
  phy: tegra: xusb: Rearrange UPHY init on Tegra210
  phy: tegra: xusb: Add Tegra210 lane_iddq operation
  phy: tegra: xusb: Add sleepwalk and suspend/resume
  soc/tegra: pmc: Provide USB sleepwalk register map
  arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
  dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop
  phy: tegra: xusb: Add wake/sleepwalk for Tegra210
  phy: tegra: xusb: Tegra210 host mode VBUS control
  phy: tegra: xusb: Add wake/sleepwalk for Tegra186
  usb: host: xhci-tegra: Unlink power domain devices
  xhci: tegra: Enable ELPG for runtime/system PM

 .../phy/nvidia,tegra124-xusb-padctl.txt   |1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi  |1 +
 drivers/clk/tegra/clk-pll.c   |   12 -
 drivers/clk/tegra/clk-tegra210.c  |   53 +-
 drivers/phy/tegra/xusb-tegra186.c |  558 -
 drivers/phy/tegra/xusb-tegra210.c | 1889 +
 drivers/phy/tegra/xusb.c  |   92 +-
 drivers/phy/tegra/xusb.h  |   22 +-
 drivers/soc/tegra/pmc.c   |   94 +
 drivers/usb/host/xhci-tegra.c |  613 --
 include/linux/clk/tegra.h |4 +-
 include/linux/phy/tegra/xusb.h|   10 +-
 12 files changed, 2784 insertions(+), 565 deletions(-)

v5 "phy: tegra: xusb: tegra210: Do not reset UPHY PLL" is moved
into v6 "phy: tegra: xusb: Rearrange UPHY init on Tegra210"
-- 
2.25.1



[PATCH v7 02/14] clk: tegra: Don't enable PLLE HW sequencer at init

2021-01-19 Thread JC Kuo
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v7:
   no change
v6:
   no change
v5:
   no change
v4:
   no change 
v3:
   no change

 drivers/clk/tegra/clk-pll.c | 12 
 1 file changed, 12 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c5cc0a2dac6f..0193cebe8c5a 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw)
pll_writel(val, PLLE_SS_CTRL, pll);
udelay(1);
 
-   val = pll_readl_misc(pll);
-   val &= ~PLLE_MISC_IDDQ_SW_CTRL;
-   pll_writel_misc(val, pll);
-
-   val = pll_readl(pll->params->aux_reg, pll);
-   val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
-   val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
-   pll_writel(val, pll->params->aux_reg, pll);
-   udelay(1);
-   val |= PLLE_AUX_SEQ_ENABLE;
-   pll_writel(val, pll->params->aux_reg, pll);
-
 out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
-- 
2.25.1



Re: [PATCH v6 04/15] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2021-01-19 Thread JC Kuo


On 1/19/21 9:52 PM, Thierry Reding wrote:
> On Tue, Jan 19, 2021 at 04:55:35PM +0800, JC Kuo wrote:
>> Once UPHY PLL hardware power sequencer is enabled, do not assert
>> reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
>> This commit removes reset_control_assert(pcie->rst) and
>> reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v6:
>>no change
>> v5:
>>no change
>> v4:
>>no change
>> v3:
>>new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"
>>
>>  drivers/phy/tegra/xusb-tegra210.c | 2 --
>>  1 file changed, 2 deletions(-)
>>
>> diff --git a/drivers/phy/tegra/xusb-tegra210.c 
>> b/drivers/phy/tegra/xusb-tegra210.c
>> index 4dc9286ec1b8..9bfecdfecf35 100644
>> --- a/drivers/phy/tegra/xusb-tegra210.c
>> +++ b/drivers/phy/tegra/xusb-tegra210.c
>> @@ -502,7 +502,6 @@ static void tegra210_pex_uphy_disable(struct 
>> tegra_xusb_padctl *padctl)
>>  if (--pcie->enable > 0)
>>  return;
>>  
>> -reset_control_assert(pcie->rst);
>>  clk_disable_unprepare(pcie->pll);
>>  }
>>  
>> @@ -739,7 +738,6 @@ static void tegra210_sata_uphy_disable(struct 
>> tegra_xusb_padctl *padctl)
>>  if (--sata->enable > 0)
>>  return;
>>  
>> -reset_control_assert(sata->rst);
>>  clk_disable_unprepare(sata->pll);
>>  }
> 
> Isn't this going to break things between here and patch 5 where the
> hardware sequencer is enabled? If so, it might be better to move this
> into patch 5 so that things stay functional and bisectible.
Hi Thierry,
Yes, I will move it into patch 5.

Thanks,
JC

> 
> Thierry
> 


[PATCH v6 11/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2021-01-19 Thread JC Kuo
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk
routines. Sleepwalk logic is in PMC (always-on) hardware block.
PMC driver provides managed access to the sleepwalk registers
via regmap framework.

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
   remove a blank line 
   rename 'pmc_dev' with 'pdev'
   remove 'struct device_node *np' 
   rename label 'no_pmc' with 'out'
   defer .probe() if PMC driver is yet to load 

v3:
   rename 'pmc_reg" with 'regmap' and move to the top of 'struct 
tegra210_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'
   rename tegra_pmc_*() with tegra210_pmc_*()
   remove VBUS ON/OFF control change

 drivers/phy/tegra/xusb-tegra210.c | 930 ++
 1 file changed, 930 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index b038d032fea1..8af73ba78ad7 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -16,6 +16,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 
@@ -52,6 +54,20 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define   USB2_PORT_WAKE_INTERRUPT_ENABLE(x)  BIT((x))
+#define   USB2_PORT_WAKEUP_EVENT(x)   BIT((x) + 7)
+#define   SS_PORT_WAKE_INTERRUPT_ENABLE(x)BIT((x) + 14)
+#define   SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define   USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define   USB2_HSIC_PORT_WAKEUP_EVENT(x)  BIT((x) + 30)
+#define   ALL_WAKE_EVENTS ( \
+   USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+   USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+   SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+   SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+   USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define   RPD_CTRL(x)  (((x) & 0x1f) << 26)
+#define   RPD_CTRL_VALUE(x)(((x) >> 26) & 0x1f)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define   TCTRL_VALUE(x)(((x) & 0x3f) >> 0)
+#define   PCTRL_VALUE(x)(((x) >> 6) & 0x3f)
 
 #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
 #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -251,16 +271,161 @@
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
+/* 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 

[PATCH v6 08/15] soc/tegra: pmc: Provide USB sleepwalk register map

2021-01-19 Thread JC Kuo
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   commit message improvement
   remove an unnecessary type cast when invokes devm_regmap_init()
v3:
   commit message improvement
   drop regmap_reg() usage
   rename 'reg' with 'offset'
   rename 'val' with 'value'
   drop '__force' when invokes devm_regmap_init()
   print error code of devm_regmap_init()
   move devm_regmap_init() a litter bit earlier
   explicitly set '.has_usb_sleepwalk=false'

 drivers/soc/tegra/pmc.c | 94 +
 1 file changed, 94 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index df9a5ca8c99c..a619a23f9592 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -43,6 +43,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -102,6 +103,9 @@
 
 #define PMC_PWR_DET_VALUE  0xe4
 
+#define PMC_USB_DEBOUNCE_DEL   0xec
+#define PMC_USB_AO 0xf0
+
 #define PMC_SCRATCH41  0x140
 
 #define PMC_WAKE2_MASK 0x160
@@ -133,6 +137,13 @@
 #define IO_DPD2_STATUS 0x1c4
 #define SEL_DPD_TIM0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS   0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG  0x1fc
+#define PMC_UTMIP_UHSIC_FAKE   0x218
+
 #define PMC_SCRATCH54  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT  8
 #define  PMC_SCRATCH54_ADDR_SHIFT  0
@@ -145,8 +156,18 @@
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL0x270
+#define PMC_UTMIP_MASTER_CONFIG0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS  0x27c
+#define PMC_UTMIP_MASTER2_CONFIG   0x29c
+
 #define GPU_RG_CNTRL   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0 0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3 0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
bool has_blink_output;
+   bool has_usb_sleepwalk;
 };
 
 /**
@@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
*pmc,
 err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+   regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+   regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+   regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+   regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
PMC_UTMIP_UHSIC_LINE_WAKEUP),
+   regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+   regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+   regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+   regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+   .yes_ranges = pmc_usb_sleepwalk_ranges,
+   .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned 
int *value)
+{
+   struct tegra_pmc *pmc = context;
+
+   *value = tegra_pmc_readl(pmc, offset);
+   return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, 
unsigned int value)
+{
+   struct tegra_pmc *pmc = context;
+
+   tegra_pmc_writel(pmc, value, offset);
+   return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+   .name = "usb_sleepwalk",
+   .reg_bits = 32,
+   .val_bits = 32,
+   .reg_stride = 4,
+   .fast_io = true,
+   .rd_table = _usb_sleepwalk_table,
+   .wr_table = _usb_sleepwalk_table,
+   .reg_read = tegra_pmc_regmap_readl,
+   .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+   struct regmap *regmap;
+   int err;
+
+   if (pmc->soc->has_usb_sleepwalk) {
+   regmap = devm_regmap_init(pmc->dev, NULL, pmc, 
_sleepwalk_regmap_config);
+   if (IS_ERR(regmap)) {
+   err = PTR_ERR(regmap);
+   dev_err(pmc->dev, "failed to allocate register map 
(%d)\n", err);
+   return err;
+   }
+ 

[PATCH v6 09/15] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2021-01-19 Thread JC Kuo
PMC driver provides USB sleepwalk registers access to XUSB PADCTL
driver. This commit adds a "nvidia,pmc" property which points to
PMC node to XUSB PADCTL device node.

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 4fbf8c15b0a1..83f6d11c578b 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1043,6 +1043,7 @@ padctl: padctl@7009f000 {
interrupts = ;
resets = <_car 142>;
reset-names = "padctl";
+   nvidia,pmc =  <_pmc>;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v6 10/15] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2021-01-19 Thread JC Kuo
This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
PHY driver. It is a phandle and specifier referring to the Tegra210
pmc@7000e400 node.

Signed-off-by: JC Kuo 
Acked-by: Rob Herring 
---
v6:
   no change
v5:
   replace "pmc@7000e400 node" -> with "PMC node"
v4:
   new change to document "nvidia,pmc" prop

 .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 38c5fa21f435..b62397d2bb0c 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -54,6 +54,7 @@ For Tegra210:
 - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
 - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
 - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
+- nvidia,pmc: phandle and specifier referring to the Tegra210 PMC node.
 
 For Tegra186:
 - avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY
-- 
2.25.1



[PATCH v6 03/15] phy: tegra: xusb: Move usb3 port init for Tegra210

2021-01-19 Thread JC Kuo
The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy
.power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane
is assigned for XUSB super-speed.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   mutex_lock()/mutex_unlock() fix
   update copyright string
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 316 +-
 drivers/phy/tegra/xusb.c  |   4 +-
 drivers/phy/tegra/xusb.h  |   4 +-
 3 files changed, 180 insertions(+), 144 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..4dc9286ec1b8 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (C) 2015 Google, Inc.
  */
 
@@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
 }
 
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+   { 0, "pcie", 6 },
+   { 1, "pcie", 5 },
+   { 2, "pcie", 0 },
+   { 2, "pcie", 3 },
+   { 3, "pcie", 4 },
+   { 3, "sata", 0 },
+   { 0, NULL,   0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+   const struct tegra_xusb_lane_map *map;
+
+   for (map = tegra210_usb3_map; map->type; map++) {
+   if (map->index == lane->index &&
+   strcmp(map->type, lane->pad->soc->name) == 0) {
+   dev_dbg(lane->pad->padctl->dev, "lane = %s map to port 
= usb3-%d\n",
+   lane->pad->soc->lanes[lane->index].name, 
map->port);
+   return map->port;
+   }
+   }
+
+   return -EINVAL;
+}
+
 /* must be called under padctl->lock */
 static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
@@ -470,19 +496,14 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
 
-   mutex_lock(>lock);
-
if (WARN_ON(pcie->enable == 0))
-   goto unlock;
+   return;
 
if (--pcie->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 /* must be called under padctl->lock */
@@ -712,19 +733,14 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
 
-   mutex_lock(>lock);
-
if (WARN_ON(sata->enable == 0))
-   goto unlock;
+   return;
 
if (--sata->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
@@ -1599,6 +1615,128 @@ static const struct tegra_xusb_lane_soc 
tegra210_pcie_lanes[] = {
TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
 };
 
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+   int port;
+
+   if (!lane || !lane->pad || !lane->pad->padctl)
+   return NULL;
+
+   port = tegra210_usb3_lane_map(lane);
+   if (port < 0)
+   return NULL;
+
+   return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+   struct device *dev = >dev;
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+   unsigned int index;
+   u32 value;
+
+   if (!usb3) {
+   dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+   return -ENODEV;
+   }
+
+   index = usb3->base.index;
+
+   value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+   if (!usb3->internal)
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+   else
+   valu

[PATCH v6 05/15] phy: tegra: xusb: Rearrange UPHY init on Tegra210

2021-01-19 Thread JC Kuo
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:

1. PLLE hardware power sequencer has to be enabled only after both
   PEX UPHY PLL and SATA UPHY PLL are initialized.
   tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()

2. At cold boot and SC7 exit, the following bits must be cleared after
   PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
   a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
   b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
   c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN

   tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
   charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
   will be cleared by tegra210_aux_mux_lp0_clamp_disable().

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   make separate changes
   use "unsigned int" instead "int" type for PHY index
   add blank line for better readability

 drivers/phy/tegra/xusb-tegra210.c | 195 --
 drivers/phy/tegra/xusb.h  |   4 +-
 2 files changed, 103 insertions(+), 96 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 9bfecdfecf35..faacb866cd1f 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
 
-   if (pcie->enable > 0) {
-   pcie->enable++;
+   if (pcie->enable)
return 0;
-   }
 
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
 
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 
tegra210_xusb_pll_hw_sequence_start();
 
-   pcie->enable++;
+skip_pll_init:
+   pcie->enable = true;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
return 0;
 
@@ -495,28 +504,44 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+   u32 value;
+   unsigned int i;
 
-   if (WARN_ON(pcie->enable == 0))
+   if (WARN_ON(!pcie->enable))
return;
 
-   if (--pcie->enable > 0)
-   return;
+   pcie->enable = false;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
clk_disable_unprepare(pcie->pll);
 }
 
 /* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool 
usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+   struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
+   bool usb;
 
-   if (sata->enable > 0) {
-   sata->enable++;
+   if (sata->enable)
return 0;
-   }
+
+   if (IS_ERR(lane))
+   return 0;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
+   usb = tegra_xusb_lane_check(lane, "usb3-ss");
 
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -717,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct 
tegra_xusb_padctl *padctl, bool usb)
 
tegra210_sata_pll_hw_sequence_start();
 
-   sata->enable++;
+skip_pll_init:
+   sata->enable = true;
+
+   for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MU

[PATCH v6 01/15] clk: tegra: Add PLLE HW power sequencer control

2021-01-19 Thread JC Kuo
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.

XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.

PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.

This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
   PLLE hardware sequencer at proper time.

2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
   check whether PLLE hardware sequencer has been enabled or not.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5: no change
v4:
   update copyright strings
v3:
   rename 'val' with 'value

 drivers/clk/tegra/clk-tegra210.c | 53 +++-
 include/linux/clk/tegra.h|  4 ++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 68cbb98af567..b9099012dc7b 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2014 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020 NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLRE_BASE_DEFAULT_MASK0x1c00
 #define PLLRE_MISC0_WRITE_MASK 0x67ff
 
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET   (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE(1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL  (1 << 4)
+#define PLLE_AUX_SS_SWCTL  (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE(1 << 24)
+
 /* PLLX */
 #define PLLX_USE_DYN_RAMP  1
 #define PLLX_BASE_LOCK (1 << 27)
@@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLU_MISC0_WRITE_MASK  0xbfff
 #define PLLU_MISC1_WRITE_MASK  0x0007
 
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+   u32 value;
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   if (value & PLLE_AUX_SEQ_ENABLE)
+   return true;
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+   u32 value;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   return 0;
+
+   /* skip if PLLE is not enabled yet */
+   value = readl_relaxed(clk_base + PLLE_MISC0);
+   if (!(value & PLLE_MISC_LOCK))
+   return -EIO;
+
+   value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+   writel_relaxed(value, clk_base + PLLE_MISC0);
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+   value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   value |= PLLE_AUX_SEQ_ENABLE;
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
 void tegra210_xusb_pll_hw_control_enable(void)
 {
u32 val;
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index eb016fc9cc0b..f7ff722a03dd 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #ifndef __LINUX_CLK_TEGRA_H_
@@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void)
 }
 #endif
 
+extern int tegra210_plle_hw_sequence_start(void);
+extern bool tegra210_plle_hw_sequence_is_enabled(void);
 extern void tegra210_xusb_pll_hw_control_enable(void);
 extern void tegra210_xusb_pll_hw_sequence_start(void);
 extern void tegra210_sata_pll_hw_control_enable(void);
-- 
2.25.1



[PATCH v6 02/15] clk: tegra: Don't enable PLLE HW sequencer at init

2021-01-19 Thread JC Kuo
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   no change 
v3:
   no change

 drivers/clk/tegra/clk-pll.c | 12 
 1 file changed, 12 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c5cc0a2dac6f..0193cebe8c5a 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw)
pll_writel(val, PLLE_SS_CTRL, pll);
udelay(1);
 
-   val = pll_readl_misc(pll);
-   val &= ~PLLE_MISC_IDDQ_SW_CTRL;
-   pll_writel_misc(val, pll);
-
-   val = pll_readl(pll->params->aux_reg, pll);
-   val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
-   val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
-   pll_writel(val, pll->params->aux_reg, pll);
-   udelay(1);
-   val |= PLLE_AUX_SEQ_ENABLE;
-   pll_writel(val, pll->params->aux_reg, pll);
-
 out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
-- 
2.25.1



[PATCH v6 06/15] phy: tegra: xusb: Add Tegra210 lane_iddq operation

2021-01-19 Thread JC Kuo
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 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   add 'misc_ctl2' data member to UPHY lane for carrying MISC_PAD_PX_CTL2 offset
   tegra210_uphy_lane_iddq_[enable/disable]() to access 'misc_ctl2' data member

 drivers/phy/tegra/xusb-tegra210.c | 82 ---
 drivers/phy/tegra/xusb.c  |  6 +++
 drivers/phy/tegra/xusb.h  |  6 +++
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index faacb866cd1f..b038d032fea1 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
@@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad 
= {
.ops = _hsic_ops,
 };
 
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc)
\
+   {   \
+   .name = _name,  \
+   .offset = _offset,  \
+   .shift = _shift,\
+   .mask = _mask,  \
+   .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions),  \
+   .funcs = tegra210_##_type##_functions,  \
+   .regs.misc_ctl2 = _misc,\
+   }
+
 static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1648,13 +1710,13 @@ sta

[PATCH v6 07/15] phy: tegra: xusb: Add sleepwalk and suspend/resume

2021-01-19 Thread JC Kuo
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.

Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
and can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB UPHY individually.

  - tegra_xusb_padctl_enable_phy_sleepwalk()
  - tegra_xusb_padctl_disable_phy_sleepwalk()

Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB PHY individually.

  - tegra_xusb_padctl_enable_phy_wake()
  - tegra_xusb_padctl_disable_phy_wake()
  - tegra_xusb_padctl_remote_wake_detected()

This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
  - tegra_xusb_padctl_suspend_noirq()
  - tegra_xusb_padctl_resume_noirq()

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   commit message improvement, no change in code

 drivers/phy/tegra/xusb.c   | 82 ++
 drivers/phy/tegra/xusb.h   |  8 
 include/linux/phy/tegra/xusb.h | 10 -
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index a34d304677bb..0aadac678191 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1273,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct 
platform_device *pdev)
return err;
 }
 
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+   return padctl->soc->ops->suspend_noirq(padctl);
+
+   return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+   return padctl->soc->ops->resume_noirq(padctl);
+
+   return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
 static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+   .pm = _xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1343,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy,
+  enum usb_device_speed speed)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_sleepwalk)
+   return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_sleepwalk)
+   return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct 
phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_wake)
+   return lane->pad->ops->enable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_wake)
+   return lane->pad->ops->disable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->remote_wake_detected)
+   return lane-&

[PATCH v6 13/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2021-01-19 Thread JC Kuo
This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
sleepwalk operations.

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'

 drivers/phy/tegra/xusb-tegra186.c | 558 +-
 1 file changed, 557 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c 
b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..2208c26f8af9 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -113,6 +113,117 @@
 #define  ID_OVERRIDE_FLOATING  ID_OVERRIDE(8)
 #define  ID_OVERRIDE_GROUNDED  ID_OVERRIDE(0)
 
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL   (0x4)
+#define   UHSIC_LINE_DEB_CNT(x)(((x) & 0xf) << 4)
+#define   UTMIP_LINE_DEB_CNT(x)((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x)  (0x40 + (x) * 4)
+#define   CLR_WALK_PTR (1 << 0)
+#define   CAP_CFG  (1 << 1)
+#define   CLR_WAKE_ALARM   (1 << 3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x)  (0x60 + (x) * 4)
+#define   HSIC_CLR_WALK_PTR(1 << 0)
+#define   HSIC_CLR_WAKE_ALARM  (1 << 3)
+#define   HSIC_CAP_CFG (1 << 4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x)   (0x70 + (x) * 4)
+#define   SPEED(x) ((x) & 0x3)
+#define UTMI_HSSPEED(0)
+#define UTMI_FSSPEED(1)
+#define UTMI_LSSPEED(2)
+#define UTMI_RST   SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x)   (0x90 + (x) * 4)
+#define   MODE(x)  ((x) & 0x1)
+#define   MODE_HS  MODE(0)
+#define   MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define   FAKE_USBOP_VAL   (1 << 0)
+#define   FAKE_USBON_VAL   (1 << 1)
+#define   FAKE_USBOP_EN(1 << 2)
+#define   FAKE_USBON_EN(1 << 3)
+#define   FAKE_STROBE_VAL  (1 << 0)
+#define   FAKE_DATA_VAL(1 << 1)
+#define   FAKE_STROBE_EN   (1 << 2)
+#define   FAKE_DATA_EN (1 << 3)
+#define   WAKE_WALK_EN (1 << 14)
+#define   MASTER_ENABLE(1 << 15)
+#define   LINEVAL_WALK_EN  (1 << 16)
+#define   WAKE_VAL(x)  (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE  WAKE_VAL(12)
+#define WAKE_VAL_ANY   WAKE_VAL(15)
+#define WAKE_VAL_DS10  WAKE_VAL(2)
+#define   LINE_WAKEUP_EN   (1 << 21)
+#define   MASTER_CFG_SEL   (1 << 22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define   USBOP_RPD_A  (1 << 0)
+#define   USBON_RPD_A  (1 << 1)
+#define   AP_A (1 << 4)
+#define   AN_A (1 << 5)
+#define   HIGHZ_A  (1 << 6)
+/* phase B */
+#define   USBOP_RPD_B  (1 << 8)
+#define   USBON_RPD_B  (1 << 9)
+#define   AP_B (1 << 12)
+#define   AN_B (1 << 13)
+#define   HIGHZ_B  (1 << 14)
+/* phase C */
+#define   USBOP_RPD_C  (1 << 16)
+#define   USBON_RPD_C  (1 << 17)
+#define   AP_C (1 << 20)
+#define   AN_C (1 << 21)
+#define   HIGHZ_C  (1 << 22)
+/* phase D */
+#define   USBOP_RPD_D  (1 << 24)
+#define   USBON_RPD_D

[PATCH v6 14/15] usb: host: xhci-tegra: Unlink power domain devices

2021-01-19 Thread JC Kuo
This commit unlinks xhci-tegra platform device with SS/host power
domain devices. Reasons for this change is - at ELPG entry, PHY
sleepwalk and wake configuration need to be done before powering
down SS/host partitions, and PHY need be powered off after powering
down SS/host partitions. Sequence looks like roughly below:

  tegra_xusb_enter_elpg() -> xhci_suspend()
  -> enable PHY sleepwalk and wake if needed
  -> power down SS/host partitions
  -> power down PHY

If SS/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.

This commit introduces:
  1. tegra_xusb_unpowergate_partitions() to power up SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_get_sync() to request power
 driver to power up partitions; If power domain devices are not
 available, tegra_powergate_sequence_power_up() will be used to
 power up partitions.

  2. tegra_xusb_powergate_partitions() to power down SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_put_sync() to request power
 driver to power down partitions; If power domain devices are not
 available, tegra_powergate_power_off() will be used to power down
 partitions.

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   commit message improvement
   update copyright string
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 206 ++
 1 file changed, 112 insertions(+), 94 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 50bb91b6a4b8..5b39a739f8f0 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2,7 +2,7 @@
 /*
  * NVIDIA Tegra xHCI host controller driver
  *
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
  * Copyright (C) 2014 Google, Inc.
  */
 
@@ -249,8 +249,7 @@ struct tegra_xusb {
 
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
-   struct device_link *genpd_dl_host;
-   struct device_link *genpd_dl_ss;
+   bool use_genpd;
 
struct phy **phys;
unsigned int num_phys;
@@ -821,36 +820,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
-   regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-   tegra_xusb_clk_disable(tegra);
-
return 0;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-   int err;
-
-   err = tegra_xusb_clk_enable(tegra);
-   if (err) {
-   dev_err(dev, "failed to enable clocks: %d\n", err);
-   return err;
-   }
-
-   err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
-   if (err) {
-   dev_err(dev, "failed to enable regulators: %d\n", err);
-   goto disable_clk;
-   }
-
return 0;
-
-disable_clk:
-   tegra_xusb_clk_disable(tegra);
-   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1026,10 +1001,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb 
*tegra)
 static void tegra_xusb_powerdomain_remove(struct device *dev,
  struct tegra_xusb *tegra)
 {
-   if (tegra->genpd_dl_ss)
-   device_link_del(tegra->genpd_dl_ss);
-   if (tegra->genpd_dl_host)
-   device_link_del(tegra->genpd_dl_host);
+   if (!tegra->use_genpd)
+   return;
+
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1055,20 +1029,84 @@ static int tegra_xusb_powerdomain_init(struct device 
*dev,
return err;
}
 
-   tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
-  DL_FLAG_PM_RUNTIME |
-  DL_FLAG_STATELESS);
-   if (!tegra->genpd_dl_host) {
-   dev_err(dev, "adding host device link failed!\n");
-   return -ENODEV;
+   tegra->use_genpd = true;
+
+   return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+   struct device *dev = tegra->dev;
+   int rc;
+
+   if (tegra->use_genpd) {
+   rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
+   if (rc < 0) {
+  

[PATCH v6 15/15] xhci: tegra: Enable ELPG for runtime/system PM

2021-01-19 Thread JC Kuo
This commit implements the complete programming sequence for ELPG
entry and exit.

 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).

 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.

At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.

Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.

The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.

Signed-off-by: JC Kuo 
---
v6:
   fix compiling warning: extra tokens at end of #ifdef directive
v5:
   avoid using xhci_get_rhub()
   protect ELPG routines with (CONFIG_PM || CONFIG_PM_SLEEP)
v4:
   reshuffle the code to avoid these pre-declarations
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 407 ++
 1 file changed, 370 insertions(+), 37 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 5b39a739f8f0..ce97ff054c68 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -15,9 +15,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -224,6 +226,7 @@ struct tegra_xusb {
 
int xhci_irq;
int mbox_irq;
+   int padctl_irq;
 
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -269,6 +272,7 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
 
+   bool suspended;
struct tegra_xusb_context context;
 };
 
@@ -665,6 +669,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
mutex_lock(>lock);
 
+   if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+   goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(, value);
 
@@ -678,6 +685,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
tegra_xusb_mbox_handle(tegra, );
 
+out:
mutex_unlock(>lock);
return IRQ_HANDLED;
 }
@@ -818,16 +826,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
}
 }
 
-static int tegra_xusb_runtime_suspend(struct device *dev)
-{
-   return 0;
-}
-
-static int tegra_xusb_runtime_resume(struct device *dev)
-{
-   return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int tegra_xusb_init_context(struct tegra_xusb *tegra)
 {
@@ -1128,6 +1126,24 @@ static int __tegra_xusb_enable_firmware_messages(struct 
tegra_xusb *tegra)
return err;
 }
 
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+   struct tegra_xusb *tegra = data;
+
+   mutex_lock(>lock);
+
+   if (tegra->suspended) {
+   mutex_unlock(>lock);
+   return IRQ_HANDLED;
+   }
+
+   mutex_unlock(>lock);
+
+   pm_runtime_resume(tegra->dev);
+
+   return IRQ_HANDLED;
+}
+
 static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
 {
int err;
@@ -1251,6 +1267,52 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
 }
 
+#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   struct tegra_xusb_padctl *padctl = tegra->padctl;
+   unsigned int i;
+   int port;
+
+   for (i = 0; i < tegra->num_usb_phys; i++) {
+   if (is_usb2_otg_phy(tegra, i)) {
+   port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+   if ((port >= 0) && (index == (unsigned int)port))
+   return true;
+   }
+   }
+
+   return false;
+}
+
+static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, 
unsigned int index)
+{
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0)
+   return true;
+
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "usb2") == 0) {
+   if (is_usb2_otg_phy(tegra, inde

[PATCH v6 00/15] Tegra XHCI controller ELPG support

2021-01-19 Thread JC Kuo
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.

JC Kuo (15):
  clk: tegra: Add PLLE HW power sequencer control
  clk: tegra: Don't enable PLLE HW sequencer at init
  phy: tegra: xusb: Move usb3 port init for Tegra210
  phy: tegra: xusb: tegra210: Do not reset UPHY PLL
  phy: tegra: xusb: Rearrange UPHY init on Tegra210
  phy: tegra: xusb: Add Tegra210 lane_iddq operation
  phy: tegra: xusb: Add sleepwalk and suspend/resume
  soc/tegra: pmc: Provide USB sleepwalk register map
  arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
  dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop
  phy: tegra: xusb: Add wake/sleepwalk for Tegra210
  phy: tegra: xusb: Tegra210 host mode VBUS control
  phy: tegra: xusb: Add wake/sleepwalk for Tegra186
  usb: host: xhci-tegra: Unlink power domain devices
  xhci: tegra: Enable ELPG for runtime/system PM

 .../phy/nvidia,tegra124-xusb-padctl.txt   |1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi  |1 +
 drivers/clk/tegra/clk-pll.c   |   12 -
 drivers/clk/tegra/clk-tegra210.c  |   53 +-
 drivers/phy/tegra/xusb-tegra186.c |  558 -
 drivers/phy/tegra/xusb-tegra210.c | 1889 +
 drivers/phy/tegra/xusb.c  |   92 +-
 drivers/phy/tegra/xusb.h  |   22 +-
 drivers/soc/tegra/pmc.c   |   94 +
 drivers/usb/host/xhci-tegra.c |  613 --
 include/linux/clk/tegra.h |4 +-
 include/linux/phy/tegra/xusb.h|   10 +-
 12 files changed, 2784 insertions(+), 565 deletions(-)

v5 "arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq" has been
merged separately.
-- 
2.25.1



[PATCH v6 12/15] phy: tegra: xusb: Tegra210 host mode VBUS control

2021-01-19 Thread JC Kuo
To support XUSB host controller ELPG, this commit moves VBUS control
.phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit().
When XUSB host controller enters ELPG, host driver invokes
.phy_power_off(), VBUS should remain ON so that USB devices will not
disconnect. VBUS can be turned OFF when host driver invokes
.phy_exit() which indicates disabling a USB port.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Add wake/sleepwalk for Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 52 ---
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 8af73ba78ad7..9d39f812fb43 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1819,8 +1819,25 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   unsigned int index = lane->index;
+   struct tegra_xusb_usb2_port *port;
+   int err;
u32 value;
 
+   port = tegra_xusb_find_usb2_port(padctl, index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_enable(port->supply);
+   if (err)
+   return err;
+   }
+
+   mutex_lock(>lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
   XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -1828,11 +1845,30 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
 
+   mutex_unlock(>lock);
+
return 0;
 }
 
 static int tegra210_usb2_phy_exit(struct phy *phy)
 {
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb2_port *port;
+   int err;
+
+   port = tegra_xusb_find_usb2_port(padctl, lane->index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", 
lane->index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_disable(port->supply);
+   if (err)
+   return err;
+   }
+
return 0;
 }
 
@@ -1953,6 +1989,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
priv = to_tegra210_xusb_padctl(padctl);
 
+   mutex_lock(>lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -2046,14 +2084,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
  XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-   if (port->supply && port->mode == USB_DR_MODE_HOST) {
-   err = regulator_enable(port->supply);
-   if (err)
-   return err;
-   }
-
-   mutex_lock(>lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(>lock);
@@ -2062,7 +2092,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
err = clk_prepare_enable(pad->clk);
if (err)
-   goto disable_regulator;
+   goto out;
 
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -2094,8 +2124,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
return 0;
 
-disable_regulator:
-   regulator_disable(port->supply);
+out:
mutex_unlock(>lock);
return err;
 }
@@ -2154,7 +2183,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
 
 out:
-   regulator_disable(port->supply);
mutex_unlock(>lock);
return 0;
 }
-- 
2.25.1



[PATCH v6 04/15] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2021-01-19 Thread JC Kuo
Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
This commit removes reset_control_assert(pcie->rst) and
reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.

Signed-off-by: JC Kuo 
---
v6:
   no change
v5:
   no change
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 4dc9286ec1b8..9bfecdfecf35 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -502,7 +502,6 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--pcie->enable > 0)
return;
 
-   reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
 }
 
@@ -739,7 +738,6 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--sata->enable > 0)
return;
 
-   reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
 }
 
-- 
2.25.1



[PATCH] arm64: tegra: Enable Jetson-Xavier J512 USB host

2021-01-18 Thread JC Kuo
This commit enables USB host mode at J512 type-C port of Jetson-Xavier.

Signed-off-by: JC Kuo 
---
 .../arm64/boot/dts/nvidia/tegra194-p2888.dtsi |  8 +++
 .../boot/dts/nvidia/tegra194-p2972-.dts   | 24 +--
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
index d71b7a1140fe..7e7b0eb90c80 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
@@ -93,6 +93,10 @@ padctl@352 {
vclamp-usb-supply = <_1v8ao>;
 
ports {
+   usb2-0 {
+   vbus-supply = <_5v0_sys>;
+   };
+
usb2-1 {
vbus-supply = <_5v0_sys>;
};
@@ -105,6 +109,10 @@ usb3-0 {
vbus-supply = <_5v0_sys>;
};
 
+   usb3-2 {
+   vbus-supply = <_5v0_sys>;
+   };
+
usb3-3 {
vbus-supply = <_5v0_sys>;
};
diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p2972-.dts 
b/arch/arm64/boot/dts/nvidia/tegra194-p2972-.dts
index 54d057beec59..8697927b1fe7 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194-p2972-.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra194-p2972-.dts
@@ -57,6 +57,10 @@ padctl@352 {
pads {
usb2 {
lanes {
+   usb2-0 {
+   status = "okay";
+   };
+
usb2-1 {
status = "okay";
};
@@ -73,6 +77,10 @@ usb3-0 {
status = "okay";
};
 
+   usb3-2 {
+   status = "okay";
+   };
+
usb3-3 {
status = "okay";
};
@@ -81,6 +89,11 @@ usb3-3 {
};
 
ports {
+   usb2-0 {
+   mode = "host";
+   status = "okay";
+   };
+
usb2-1 {
mode = "host";
status = "okay";
@@ -96,6 +109,11 @@ usb3-0 {
status = "okay";
};
 
+   usb3-2 {
+   nvidia,usb2-companion = <0>;
+   status = "okay";
+   };
+
usb3-3 {
nvidia,usb2-companion = <3>;
maximum-speed = "super-speed";
@@ -107,11 +125,13 @@ usb3-3 {
usb@361 {
status = "okay";
 
-   phys =  
<&{/bus@0/padctl@352/pads/usb2/lanes/usb2-1}>,
+   phys =  
<&{/bus@0/padctl@352/pads/usb2/lanes/usb2-0}>,
+   
<&{/bus@0/padctl@352/pads/usb2/lanes/usb2-1}>,

<&{/bus@0/padctl@352/pads/usb2/lanes/usb2-3}>,

<&{/bus@0/padctl@352/pads/usb3/lanes/usb3-0}>,
+   
<&{/bus@0/padctl@352/pads/usb3/lanes/usb3-2}>,

<&{/bus@0/padctl@352/pads/usb3/lanes/usb3-3}>;
-   phy-names = "usb2-1", "usb2-3", "usb3-0", "usb3-3";
+   phy-names = "usb2-0", "usb2-1", "usb2-3", "usb3-0", 
"usb3-2", "usb3-3";
};
 
pwm@c34 {
-- 
2.25.1



[PATCH v5 03/16] phy: tegra: xusb: Move usb3 port init for Tegra210

2020-11-19 Thread JC Kuo
The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy
.power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane
is assigned for XUSB super-speed.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   mutex_lock()/mutex_unlock() fix
   update copyright string
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 316 +-
 drivers/phy/tegra/xusb.c  |   4 +-
 drivers/phy/tegra/xusb.h  |   4 +-
 3 files changed, 180 insertions(+), 144 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..4dc9286ec1b8 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (C) 2015 Google, Inc.
  */
 
@@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
 }
 
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+   { 0, "pcie", 6 },
+   { 1, "pcie", 5 },
+   { 2, "pcie", 0 },
+   { 2, "pcie", 3 },
+   { 3, "pcie", 4 },
+   { 3, "sata", 0 },
+   { 0, NULL,   0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+   const struct tegra_xusb_lane_map *map;
+
+   for (map = tegra210_usb3_map; map->type; map++) {
+   if (map->index == lane->index &&
+   strcmp(map->type, lane->pad->soc->name) == 0) {
+   dev_dbg(lane->pad->padctl->dev, "lane = %s map to port 
= usb3-%d\n",
+   lane->pad->soc->lanes[lane->index].name, 
map->port);
+   return map->port;
+   }
+   }
+
+   return -EINVAL;
+}
+
 /* must be called under padctl->lock */
 static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
@@ -470,19 +496,14 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
 
-   mutex_lock(>lock);
-
if (WARN_ON(pcie->enable == 0))
-   goto unlock;
+   return;
 
if (--pcie->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 /* must be called under padctl->lock */
@@ -712,19 +733,14 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
 
-   mutex_lock(>lock);
-
if (WARN_ON(sata->enable == 0))
-   goto unlock;
+   return;
 
if (--sata->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
@@ -1599,6 +1615,128 @@ static const struct tegra_xusb_lane_soc 
tegra210_pcie_lanes[] = {
TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
 };
 
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+   int port;
+
+   if (!lane || !lane->pad || !lane->pad->padctl)
+   return NULL;
+
+   port = tegra210_usb3_lane_map(lane);
+   if (port < 0)
+   return NULL;
+
+   return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+   struct device *dev = >dev;
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+   unsigned int index;
+   u32 value;
+
+   if (!usb3) {
+   dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+   return -ENODEV;
+   }
+
+   index = usb3->base.index;
+
+   value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+   if (!usb3->internal)
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+   else
+   value |= XUSB_PADCTL_S

[PATCH v5 16/16] xhci: tegra: Enable ELPG for runtime/system PM

2020-11-19 Thread JC Kuo
This commit implements the complete programming sequence for ELPG
entry and exit.

 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).

 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.

At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.

Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.

The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.

Signed-off-by: JC Kuo 
---
v5:
   avoid using xhci_get_rhub()
   protect ELPG routines with (CONFIG_PM || CONFIG_PM_SLEEP)
v4:
   reshuffle the code to avoid these pre-declarations
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 407 ++
 1 file changed, 370 insertions(+), 37 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 375aaf4d22dc..c75987c607ff 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -15,9 +15,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -224,6 +226,7 @@ struct tegra_xusb {
 
int xhci_irq;
int mbox_irq;
+   int padctl_irq;
 
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -269,6 +272,7 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
 
+   bool suspended;
struct tegra_xusb_context context;
 };
 
@@ -658,6 +662,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
mutex_lock(>lock);
 
+   if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+   goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(, value);
 
@@ -671,6 +678,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
tegra_xusb_mbox_handle(tegra, );
 
+out:
mutex_unlock(>lock);
return IRQ_HANDLED;
 }
@@ -811,16 +819,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
}
 }
 
-static int tegra_xusb_runtime_suspend(struct device *dev)
-{
-   return 0;
-}
-
-static int tegra_xusb_runtime_resume(struct device *dev)
-{
-   return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int tegra_xusb_init_context(struct tegra_xusb *tegra)
 {
@@ -1121,6 +1119,24 @@ static int __tegra_xusb_enable_firmware_messages(struct 
tegra_xusb *tegra)
return err;
 }
 
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+   struct tegra_xusb *tegra = data;
+
+   mutex_lock(>lock);
+
+   if (tegra->suspended) {
+   mutex_unlock(>lock);
+   return IRQ_HANDLED;
+   }
+
+   mutex_unlock(>lock);
+
+   pm_runtime_resume(tegra->dev);
+
+   return IRQ_HANDLED;
+}
+
 static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
 {
int err;
@@ -1244,6 +1260,52 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
 }
 
+#ifdef IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   struct tegra_xusb_padctl *padctl = tegra->padctl;
+   unsigned int i;
+   int port;
+
+   for (i = 0; i < tegra->num_usb_phys; i++) {
+   if (is_usb2_otg_phy(tegra, i)) {
+   port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+   if ((port >= 0) && (index == (unsigned int)port))
+   return true;
+   }
+   }
+
+   return false;
+}
+
+static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, 
unsigned int index)
+{
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0)
+   return true;
+
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "usb2") == 0) {
+   if (is_usb2_otg_phy(tegra, index))
+   return ((index == tegra->otg

[PATCH v5 14/16] arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq

2020-11-19 Thread JC Kuo
This commit adds "interrupts" property to Tegra210/Tegra186/Tegra194
XUSB PADCTL node. XUSB PADCTL interrupt will be raised when USB wake
event happens. This is required for supporting XUSB host controller
ELPG.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 3 files changed, 3 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index 98544d16d01b..53ab8e5487e0 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -685,6 +685,7 @@ padctl: padctl@352 {
reg = <0x0 0x0352 0x0 0x1000>,
  <0x0 0x0354 0x0 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA186_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index 6946fb210e48..c84d1f040111 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -801,6 +801,7 @@ xusb_padctl: padctl@352 {
reg = <0x0352 0x1000>,
  <0x0354 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA194_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 88e778655e99..b4537671a6ca 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1043,6 +1043,7 @@ padctl: padctl@7009f000 {
resets = <_car 142>;
reset-names = "padctl";
nvidia,pmc =  <_pmc>;
+   interrupts = ;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v5 13/16] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2020-11-19 Thread JC Kuo
This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
sleepwalk operations.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'

 drivers/phy/tegra/xusb-tegra186.c | 558 +-
 1 file changed, 557 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c 
b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..2208c26f8af9 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -113,6 +113,117 @@
 #define  ID_OVERRIDE_FLOATING  ID_OVERRIDE(8)
 #define  ID_OVERRIDE_GROUNDED  ID_OVERRIDE(0)
 
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL   (0x4)
+#define   UHSIC_LINE_DEB_CNT(x)(((x) & 0xf) << 4)
+#define   UTMIP_LINE_DEB_CNT(x)((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x)  (0x40 + (x) * 4)
+#define   CLR_WALK_PTR (1 << 0)
+#define   CAP_CFG  (1 << 1)
+#define   CLR_WAKE_ALARM   (1 << 3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x)  (0x60 + (x) * 4)
+#define   HSIC_CLR_WALK_PTR(1 << 0)
+#define   HSIC_CLR_WAKE_ALARM  (1 << 3)
+#define   HSIC_CAP_CFG (1 << 4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x)   (0x70 + (x) * 4)
+#define   SPEED(x) ((x) & 0x3)
+#define UTMI_HSSPEED(0)
+#define UTMI_FSSPEED(1)
+#define UTMI_LSSPEED(2)
+#define UTMI_RST   SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x)   (0x90 + (x) * 4)
+#define   MODE(x)  ((x) & 0x1)
+#define   MODE_HS  MODE(0)
+#define   MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define   FAKE_USBOP_VAL   (1 << 0)
+#define   FAKE_USBON_VAL   (1 << 1)
+#define   FAKE_USBOP_EN(1 << 2)
+#define   FAKE_USBON_EN(1 << 3)
+#define   FAKE_STROBE_VAL  (1 << 0)
+#define   FAKE_DATA_VAL(1 << 1)
+#define   FAKE_STROBE_EN   (1 << 2)
+#define   FAKE_DATA_EN (1 << 3)
+#define   WAKE_WALK_EN (1 << 14)
+#define   MASTER_ENABLE(1 << 15)
+#define   LINEVAL_WALK_EN  (1 << 16)
+#define   WAKE_VAL(x)  (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE  WAKE_VAL(12)
+#define WAKE_VAL_ANY   WAKE_VAL(15)
+#define WAKE_VAL_DS10  WAKE_VAL(2)
+#define   LINE_WAKEUP_EN   (1 << 21)
+#define   MASTER_CFG_SEL   (1 << 22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define   USBOP_RPD_A  (1 << 0)
+#define   USBON_RPD_A  (1 << 1)
+#define   AP_A (1 << 4)
+#define   AN_A (1 << 5)
+#define   HIGHZ_A  (1 << 6)
+/* phase B */
+#define   USBOP_RPD_B  (1 << 8)
+#define   USBON_RPD_B  (1 << 9)
+#define   AP_B (1 << 12)
+#define   AN_B (1 << 13)
+#define   HIGHZ_B  (1 << 14)
+/* phase C */
+#define   USBOP_RPD_C  (1 << 16)
+#define   USBON_RPD_C  (1 << 17)
+#define   AP_C (1 << 20)
+#define   AN_C (1 << 21)
+#define   HIGHZ_C  (1 << 22)
+/* phase D */
+#define   USBOP_RPD_D  (1 << 24)
+#define   USBON_RPD_D  (1 <&

[PATCH v5 15/16] usb: host: xhci-tegra: Unlink power domain devices

2020-11-19 Thread JC Kuo
This commit unlinks xhci-tegra platform device with SS/host power
domain devices. Reasons for this change is - at ELPG entry, PHY
sleepwalk and wake configuration need to be done before powering
down SS/host partitions, and PHY need be powered off after powering
down SS/host partitions. Sequence looks like roughly below:

  tegra_xusb_enter_elpg() -> xhci_suspend()
  -> enable PHY sleepwalk and wake if needed
  -> power down SS/host partitions
  -> power down PHY

If SS/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.

This commit introduces:
  1. tegra_xusb_unpowergate_partitions() to power up SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_get_sync() to request power
 driver to power up partitions; If power domain devices are not
 available, tegra_powergate_sequence_power_up() will be used to
 power up partitions.

  2. tegra_xusb_powergate_partitions() to power down SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_put_sync() to request power
 driver to power down partitions; If power domain devices are not
 available, tegra_powergate_power_off() will be used to power down
 partitions.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   commit message improvement
   update copyright string
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 206 ++
 1 file changed, 112 insertions(+), 94 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 934be1686352..375aaf4d22dc 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2,7 +2,7 @@
 /*
  * NVIDIA Tegra xHCI host controller driver
  *
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
  * Copyright (C) 2014 Google, Inc.
  */
 
@@ -249,8 +249,7 @@ struct tegra_xusb {
 
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
-   struct device_link *genpd_dl_host;
-   struct device_link *genpd_dl_ss;
+   bool use_genpd;
 
struct phy **phys;
unsigned int num_phys;
@@ -814,36 +813,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
-   regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-   tegra_xusb_clk_disable(tegra);
-
return 0;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-   int err;
-
-   err = tegra_xusb_clk_enable(tegra);
-   if (err) {
-   dev_err(dev, "failed to enable clocks: %d\n", err);
-   return err;
-   }
-
-   err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
-   if (err) {
-   dev_err(dev, "failed to enable regulators: %d\n", err);
-   goto disable_clk;
-   }
-
return 0;
-
-disable_clk:
-   tegra_xusb_clk_disable(tegra);
-   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1019,10 +994,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb 
*tegra)
 static void tegra_xusb_powerdomain_remove(struct device *dev,
  struct tegra_xusb *tegra)
 {
-   if (tegra->genpd_dl_ss)
-   device_link_del(tegra->genpd_dl_ss);
-   if (tegra->genpd_dl_host)
-   device_link_del(tegra->genpd_dl_host);
+   if (!tegra->use_genpd)
+   return;
+
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1022,84 @@ static int tegra_xusb_powerdomain_init(struct device 
*dev,
return err;
}
 
-   tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
-  DL_FLAG_PM_RUNTIME |
-  DL_FLAG_STATELESS);
-   if (!tegra->genpd_dl_host) {
-   dev_err(dev, "adding host device link failed!\n");
-   return -ENODEV;
+   tegra->use_genpd = true;
+
+   return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+   struct device *dev = tegra->dev;
+   int rc;
+
+   if (tegra->use_genpd) {
+   rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
+   if (rc < 0) {
+  

[PATCH v5 12/16] phy: tegra: xusb: Tegra210 host mode VBUS control

2020-11-19 Thread JC Kuo
To support XUSB host controller ELPG, this commit moves VBUS control
.phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit().
When XUSB host controller enters ELPG, host driver invokes
.phy_power_off(), VBUS should remain ON so that USB devices will not
disconnect. VBUS can be turned OFF when host driver invokes
.phy_exit() which indicates disabling a USB port.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Add wake/sleepwalk for Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 52 ---
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 8af73ba78ad7..9d39f812fb43 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1819,8 +1819,25 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   unsigned int index = lane->index;
+   struct tegra_xusb_usb2_port *port;
+   int err;
u32 value;
 
+   port = tegra_xusb_find_usb2_port(padctl, index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_enable(port->supply);
+   if (err)
+   return err;
+   }
+
+   mutex_lock(>lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
   XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -1828,11 +1845,30 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
 
+   mutex_unlock(>lock);
+
return 0;
 }
 
 static int tegra210_usb2_phy_exit(struct phy *phy)
 {
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb2_port *port;
+   int err;
+
+   port = tegra_xusb_find_usb2_port(padctl, lane->index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", 
lane->index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_disable(port->supply);
+   if (err)
+   return err;
+   }
+
return 0;
 }
 
@@ -1953,6 +1989,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
priv = to_tegra210_xusb_padctl(padctl);
 
+   mutex_lock(>lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -2046,14 +2084,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
  XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-   if (port->supply && port->mode == USB_DR_MODE_HOST) {
-   err = regulator_enable(port->supply);
-   if (err)
-   return err;
-   }
-
-   mutex_lock(>lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(>lock);
@@ -2062,7 +2092,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
err = clk_prepare_enable(pad->clk);
if (err)
-   goto disable_regulator;
+   goto out;
 
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -2094,8 +2124,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
return 0;
 
-disable_regulator:
-   regulator_disable(port->supply);
+out:
mutex_unlock(>lock);
return err;
 }
@@ -2154,7 +2183,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
 
 out:
-   regulator_disable(port->supply);
mutex_unlock(>lock);
return 0;
 }
-- 
2.25.1



[PATCH v5 11/16] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2020-11-19 Thread JC Kuo
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk
routines. Sleepwalk logic is in PMC (always-on) hardware block.
PMC driver provides managed access to the sleepwalk registers
via regmap framework.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
   remove a blank line 
   rename 'pmc_dev' with 'pdev'
   remove 'struct device_node *np' 
   rename label 'no_pmc' with 'out'
   defer .probe() if PMC driver is yet to load 

v3:
   rename 'pmc_reg" with 'regmap' and move to the top of 'struct 
tegra210_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'
   rename tegra_pmc_*() with tegra210_pmc_*()
   remove VBUS ON/OFF control change

 drivers/phy/tegra/xusb-tegra210.c | 930 ++
 1 file changed, 930 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index b038d032fea1..8af73ba78ad7 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -16,6 +16,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 
@@ -52,6 +54,20 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define   USB2_PORT_WAKE_INTERRUPT_ENABLE(x)  BIT((x))
+#define   USB2_PORT_WAKEUP_EVENT(x)   BIT((x) + 7)
+#define   SS_PORT_WAKE_INTERRUPT_ENABLE(x)BIT((x) + 14)
+#define   SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define   USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define   USB2_HSIC_PORT_WAKEUP_EVENT(x)  BIT((x) + 30)
+#define   ALL_WAKE_EVENTS ( \
+   USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+   USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+   SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+   SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+   USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define   RPD_CTRL(x)  (((x) & 0x1f) << 26)
+#define   RPD_CTRL_VALUE(x)(((x) >> 26) & 0x1f)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define   TCTRL_VALUE(x)(((x) & 0x3f) >> 0)
+#define   PCTRL_VALUE(x)(((x) >> 6) & 0x3f)
 
 #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
 #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -251,16 +271,161 @@
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
+/* 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_

[PATCH v5 10/16] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2020-11-19 Thread JC Kuo
This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
PHY driver. It is a phandle and specifier referring to the Tegra210
pmc@7000e400 node.

Signed-off-by: JC Kuo 
---
v5:
   replace "pmc@7000e400 node" -> with "PMC node"
v4:
   new change to document "nvidia,pmc" prop

 .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 38c5fa21f435..b62397d2bb0c 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -54,6 +54,7 @@ For Tegra210:
 - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
 - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
 - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
+- nvidia,pmc: phandle and specifier referring to the Tegra210 PMC node.
 
 For Tegra186:
 - avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY
-- 
2.25.1



[PATCH v5 09/16] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2020-11-19 Thread JC Kuo
PMC driver provides USB sleepwalk registers access to XUSB PADCTL
driver. This commit adds a "nvidia,pmc" property which points to
PMC node to XUSB PADCTL device node.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index d41eb12a2c92..88e778655e99 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1042,6 +1042,7 @@ padctl: padctl@7009f000 {
reg = <0x0 0x7009f000 0x0 0x1000>;
resets = <_car 142>;
reset-names = "padctl";
+   nvidia,pmc =  <_pmc>;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v5 08/16] soc/tegra: pmc: Provide USB sleepwalk register map

2020-11-19 Thread JC Kuo
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   commit message improvement
   remove an unnecessary type cast when invokes devm_regmap_init()
v3:
   commit message improvement
   drop regmap_reg() usage
   rename 'reg' with 'offset'
   rename 'val' with 'value'
   drop '__force' when invokes devm_regmap_init()
   print error code of devm_regmap_init()
   move devm_regmap_init() a litter bit earlier
   explicitly set '.has_usb_sleepwalk=false'

 drivers/soc/tegra/pmc.c | 94 +
 1 file changed, 94 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index df9a5ca8c99c..a619a23f9592 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -43,6 +43,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -102,6 +103,9 @@
 
 #define PMC_PWR_DET_VALUE  0xe4
 
+#define PMC_USB_DEBOUNCE_DEL   0xec
+#define PMC_USB_AO 0xf0
+
 #define PMC_SCRATCH41  0x140
 
 #define PMC_WAKE2_MASK 0x160
@@ -133,6 +137,13 @@
 #define IO_DPD2_STATUS 0x1c4
 #define SEL_DPD_TIM0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS   0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG  0x1fc
+#define PMC_UTMIP_UHSIC_FAKE   0x218
+
 #define PMC_SCRATCH54  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT  8
 #define  PMC_SCRATCH54_ADDR_SHIFT  0
@@ -145,8 +156,18 @@
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL0x270
+#define PMC_UTMIP_MASTER_CONFIG0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS  0x27c
+#define PMC_UTMIP_MASTER2_CONFIG   0x29c
+
 #define GPU_RG_CNTRL   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0 0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3 0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
bool has_blink_output;
+   bool has_usb_sleepwalk;
 };
 
 /**
@@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
*pmc,
 err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+   regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+   regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+   regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+   regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
PMC_UTMIP_UHSIC_LINE_WAKEUP),
+   regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+   regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+   regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+   regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+   .yes_ranges = pmc_usb_sleepwalk_ranges,
+   .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned 
int *value)
+{
+   struct tegra_pmc *pmc = context;
+
+   *value = tegra_pmc_readl(pmc, offset);
+   return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, 
unsigned int value)
+{
+   struct tegra_pmc *pmc = context;
+
+   tegra_pmc_writel(pmc, value, offset);
+   return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+   .name = "usb_sleepwalk",
+   .reg_bits = 32,
+   .val_bits = 32,
+   .reg_stride = 4,
+   .fast_io = true,
+   .rd_table = _usb_sleepwalk_table,
+   .wr_table = _usb_sleepwalk_table,
+   .reg_read = tegra_pmc_regmap_readl,
+   .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+   struct regmap *regmap;
+   int err;
+
+   if (pmc->soc->has_usb_sleepwalk) {
+   regmap = devm_regmap_init(pmc->dev, NULL, pmc, 
_sleepwalk_regmap_config);
+   if (IS_ERR(regmap)) {
+   err = PTR_ERR(regmap);
+   dev_err(pmc->dev, "failed to allocate register map 
(%d)\n", err);
+   return err;
+   }
+   }
+
+   retur

[PATCH v5 07/16] phy: tegra: xusb: Add sleepwalk and suspend/resume

2020-11-19 Thread JC Kuo
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.

Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
and can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB UPHY individually.

  - tegra_xusb_padctl_enable_phy_sleepwalk()
  - tegra_xusb_padctl_disable_phy_sleepwalk()

Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB PHY individually.

  - tegra_xusb_padctl_enable_phy_wake()
  - tegra_xusb_padctl_disable_phy_wake()
  - tegra_xusb_padctl_remote_wake_detected()

This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
  - tegra_xusb_padctl_suspend_noirq()
  - tegra_xusb_padctl_resume_noirq()

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   commit message improvement, no change in code

 drivers/phy/tegra/xusb.c   | 82 ++
 drivers/phy/tegra/xusb.h   |  8 
 include/linux/phy/tegra/xusb.h | 10 -
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index e81ac7a088e9..0fdee9ab64da 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1275,10 +1275,36 @@ static int tegra_xusb_padctl_remove(struct 
platform_device *pdev)
return err;
 }
 
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+   return padctl->soc->ops->suspend_noirq(padctl);
+
+   return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+   return padctl->soc->ops->resume_noirq(padctl);
+
+   return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
 static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+   .pm = _xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1345,6 +1371,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy,
+  enum usb_device_speed speed)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_sleepwalk)
+   return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_sleepwalk)
+   return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct 
phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_wake)
+   return lane->pad->ops->enable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_wake)
+   return lane->pad->ops->disable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->remote_wake_detected)
+   return lane-&

[PATCH v5 05/16] phy: tegra: xusb: Rearrange UPHY init on Tegra210

2020-11-19 Thread JC Kuo
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:

1. PLLE hardware power sequencer has to be enabled only after both
   PEX UPHY PLL and SATA UPHY PLL are initialized.
   tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()

2. At cold boot and SC7 exit, the following bits must be cleared after
   PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
   a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
   b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
   c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN

   tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
   charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
   will be cleared by tegra210_aux_mux_lp0_clamp_disable().

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   no change
v3:
   make separate changes
   use "unsigned int" instead "int" type for PHY index
   add blank line for better readability

 drivers/phy/tegra/xusb-tegra210.c | 195 --
 drivers/phy/tegra/xusb.h  |   4 +-
 2 files changed, 103 insertions(+), 96 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 9bfecdfecf35..faacb866cd1f 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
 
-   if (pcie->enable > 0) {
-   pcie->enable++;
+   if (pcie->enable)
return 0;
-   }
 
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
 
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 
tegra210_xusb_pll_hw_sequence_start();
 
-   pcie->enable++;
+skip_pll_init:
+   pcie->enable = true;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
return 0;
 
@@ -495,28 +504,44 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+   u32 value;
+   unsigned int i;
 
-   if (WARN_ON(pcie->enable == 0))
+   if (WARN_ON(!pcie->enable))
return;
 
-   if (--pcie->enable > 0)
-   return;
+   pcie->enable = false;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
clk_disable_unprepare(pcie->pll);
 }
 
 /* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool 
usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+   struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
+   bool usb;
 
-   if (sata->enable > 0) {
-   sata->enable++;
+   if (sata->enable)
return 0;
-   }
+
+   if (IS_ERR(lane))
+   return 0;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
+   usb = tegra_xusb_lane_check(lane, "usb3-ss");
 
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -717,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct 
tegra_xusb_padctl *padctl, bool usb)
 
tegra210_sata_pll_hw_sequence_start();
 
-   sata->enable++;
+skip_pll_init:
+   sata->enable = true;
+
+   for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   va

[PATCH v5 06/16] phy: tegra: xusb: Add Tegra210 lane_iddq operation

2020-11-19 Thread JC Kuo
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 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   no change
v3:
   add 'misc_ctl2' data member to UPHY lane for carrying MISC_PAD_PX_CTL2 offset
   tegra210_uphy_lane_iddq_[enable/disable]() to access 'misc_ctl2' data member

 drivers/phy/tegra/xusb-tegra210.c | 82 ---
 drivers/phy/tegra/xusb.c  |  6 +++
 drivers/phy/tegra/xusb.h  |  6 +++
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index faacb866cd1f..b038d032fea1 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
@@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad 
= {
.ops = _hsic_ops,
 };
 
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc)
\
+   {   \
+   .name = _name,  \
+   .offset = _offset,  \
+   .shift = _shift,\
+   .mask = _mask,  \
+   .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions),  \
+   .funcs = tegra210_##_type##_functions,  \
+   .regs.misc_ctl2 = _misc,\
+   }
+
 static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1648,13 +1710,13 @@ static const char *tegra210_pcie_functi

[PATCH v5 04/16] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2020-11-19 Thread JC Kuo
Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
This commit removes reset_control_assert(pcie->rst) and
reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.

Signed-off-by: JC Kuo 
---
v5:
   no change
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 4dc9286ec1b8..9bfecdfecf35 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -502,7 +502,6 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--pcie->enable > 0)
return;
 
-   reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
 }
 
@@ -739,7 +738,6 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--sata->enable > 0)
return;
 
-   reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
 }
 
-- 
2.25.1



[PATCH v5 02/16] clk: tegra: Don't enable PLLE HW sequencer at init

2020-11-19 Thread JC Kuo
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5:
   no change
v4:
   no change 
v3:
   no change

 drivers/clk/tegra/clk-pll.c | 12 
 1 file changed, 12 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c5cc0a2dac6f..0193cebe8c5a 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw)
pll_writel(val, PLLE_SS_CTRL, pll);
udelay(1);
 
-   val = pll_readl_misc(pll);
-   val &= ~PLLE_MISC_IDDQ_SW_CTRL;
-   pll_writel_misc(val, pll);
-
-   val = pll_readl(pll->params->aux_reg, pll);
-   val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
-   val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
-   pll_writel(val, pll->params->aux_reg, pll);
-   udelay(1);
-   val |= PLLE_AUX_SEQ_ENABLE;
-   pll_writel(val, pll->params->aux_reg, pll);
-
 out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
-- 
2.25.1



[PATCH v5 01/16] clk: tegra: Add PLLE HW power sequencer control

2020-11-19 Thread JC Kuo
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.

XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.

PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.

This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
   PLLE hardware sequencer at proper time.

2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
   check whether PLLE hardware sequencer has been enabled or not.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v5: no change
v4:
   update copyright strings
v3:
   rename 'val' with 'value
 drivers/clk/tegra/clk-tegra210.c | 53 +++-
 include/linux/clk/tegra.h|  4 ++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 68cbb98af567..b9099012dc7b 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2014 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020 NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLRE_BASE_DEFAULT_MASK0x1c00
 #define PLLRE_MISC0_WRITE_MASK 0x67ff
 
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET   (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE(1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL  (1 << 4)
+#define PLLE_AUX_SS_SWCTL  (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE(1 << 24)
+
 /* PLLX */
 #define PLLX_USE_DYN_RAMP  1
 #define PLLX_BASE_LOCK (1 << 27)
@@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLU_MISC0_WRITE_MASK  0xbfff
 #define PLLU_MISC1_WRITE_MASK  0x0007
 
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+   u32 value;
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   if (value & PLLE_AUX_SEQ_ENABLE)
+   return true;
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+   u32 value;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   return 0;
+
+   /* skip if PLLE is not enabled yet */
+   value = readl_relaxed(clk_base + PLLE_MISC0);
+   if (!(value & PLLE_MISC_LOCK))
+   return -EIO;
+
+   value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+   writel_relaxed(value, clk_base + PLLE_MISC0);
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+   value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   value |= PLLE_AUX_SEQ_ENABLE;
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
 void tegra210_xusb_pll_hw_control_enable(void)
 {
u32 val;
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index 3f01d43f0598..7be477377ad3 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #ifndef __LINUX_CLK_TEGRA_H_
@@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void)
 }
 #endif
 
+extern int tegra210_plle_hw_sequence_start(void);
+extern bool tegra210_plle_hw_sequence_is_enabled(void);
 extern void tegra210_xusb_pll_hw_control_enable(void);
 extern void tegra210_xusb_pll_hw_sequence_start(void);
 extern void tegra210_sata_pll_hw_control_enable(void);
-- 
2.25.1



[PATCH v5 00/16] Tegra XHCI controller ELPG support

2020-11-19 Thread JC Kuo
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.

JC Kuo (16):
  clk: tegra: Add PLLE HW power sequencer control
  clk: tegra: Don't enable PLLE HW sequencer at init
  phy: tegra: xusb: Move usb3 port init for Tegra210
  phy: tegra: xusb: tegra210: Do not reset UPHY PLL
  phy: tegra: xusb: Rearrange UPHY init on Tegra210
  phy: tegra: xusb: Add Tegra210 lane_iddq operation
  phy: tegra: xusb: Add sleepwalk and suspend/resume
  soc/tegra: pmc: Provide USB sleepwalk register map
  arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
  dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop
  phy: tegra: xusb: Add wake/sleepwalk for Tegra210
  phy: tegra: xusb: Tegra210 host mode VBUS control
  phy: tegra: xusb: Add wake/sleepwalk for Tegra186
  arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq
  usb: host: xhci-tegra: Unlink power domain devices
  xhci: tegra: Enable ELPG for runtime/system PM

 .../phy/nvidia,tegra124-xusb-padctl.txt   |1 +
 arch/arm64/boot/dts/nvidia/tegra186.dtsi  |1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi  |1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi  |2 +
 drivers/clk/tegra/clk-pll.c   |   12 -
 drivers/clk/tegra/clk-tegra210.c  |   53 +-
 drivers/phy/tegra/xusb-tegra186.c |  558 -
 drivers/phy/tegra/xusb-tegra210.c | 1889 +
 drivers/phy/tegra/xusb.c  |   92 +-
 drivers/phy/tegra/xusb.h  |   22 +-
 drivers/soc/tegra/pmc.c   |   94 +
 drivers/usb/host/xhci-tegra.c |  613 --
 include/linux/clk/tegra.h |4 +-
 include/linux/phy/tegra/xusb.h|   10 +-
 14 files changed, 2787 insertions(+), 565 deletions(-)

-- 
2.25.1



[PATCH v3] arm64: tegra: jetson-tx1: Fix USB_VBUS_EN0 regulator

2020-11-18 Thread JC Kuo
USB Host mode is broken at OTG port of Jetson-TX1 platform because
USB_VBUS_EN0 regulator (regulator@11) is being overwritten by vdd-cam-1v2
regulator. This commit rearrange USB_VBUS_EN0 to be regulator@14.

Fixes: 257c8047be44 ("arm64: tegra: jetson-tx1: Add camera supplies")
Cc: sta...@vger.kernel.org
Signed-off-by: JC Kuo 
Reviewed-by: Jon Hunter 
---
v3:
add 'Cc: sta...@vger.kernel.org' tag
v2:
add 'Fixes:' tag
add Reviewed-by: Jon Hunter 

 .../arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 20 +--
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index e18e1a9a3011..a9caaf7c0d67 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1663,16 +1663,6 @@ vdd_usb_vbus: regulator@9 {
vin-supply = <_5v0_sys>;
};
 
-   vdd_usb_vbus_otg: regulator@11 {
-   compatible = "regulator-fixed";
-   regulator-name = "USB_VBUS_EN0";
-   regulator-min-microvolt = <500>;
-   regulator-max-microvolt = <500>;
-   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
-   enable-active-high;
-   vin-supply = <_5v0_sys>;
-   };
-
vdd_hdmi: regulator@10 {
compatible = "regulator-fixed";
regulator-name = "VDD_HDMI_5V0";
@@ -1712,4 +1702,14 @@ vdd_cam_1v8: regulator@13 {
enable-active-high;
vin-supply = <_3v3_sys>;
};
+
+   vdd_usb_vbus_otg: regulator@14 {
+   compatible = "regulator-fixed";
+   regulator-name = "USB_VBUS_EN0";
+   regulator-min-microvolt = <500>;
+   regulator-max-microvolt = <500>;
+   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
+   enable-active-high;
+   vin-supply = <_5v0_sys>;
+   };
 };
-- 
2.25.1



Re: [PATCH v4 10/16] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2020-11-18 Thread JC Kuo
On 11/14/20 12:20 AM, Thierry Reding wrote:
> On Mon, Oct 19, 2020 at 04:40:46PM -0500, Rob Herring wrote:
>> On Fri, Oct 16, 2020 at 09:07:20PM +0800, JC Kuo wrote:
>>> This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
>>> PHY driver. It is a phandle and specifier referring to the Tegra210
>>> pmc@7000e400 node.
>>>
>>> Signed-off-by: JC Kuo 
>>> ---
>>> v4:
>>>new change to document "nvidia,pmc" prop
>>>
>>>  .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
>>>  1 file changed, 1 insertion(+)
>>>
>>> diff --git 
>>> a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
>>> b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>>> index 38c5fa21f435..ea559baeb546 100644
>>> --- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>>> +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>>> @@ -54,6 +54,7 @@ For Tegra210:
>>>  - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 
>>> 1.05 V.
>>>  - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
>>>  - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
>>> +- nvidia,pmc: phandle and specifier referring to the Tegra210 pmc@7000e400 
>>> node.
>>
>> 'Tegra210 pmc@7000e400' is kind of specific. Going to update this for 
>> every address and chip?
>>
>> If there's only one PMC, you can just find the compatible PMC node. Then 
>> you don't need a DT update.
> 
> I did advise against doing the lookup by compatible string because I
> think it has a couple of downsides. On one hand it's going to create an
> additional maintenance burden on the XUSB pad controller driver because
> the PMC is usually not compatible between SoC versions, so for every new
> SoC generation we'll have to add the PMC compatible string to the XUSB
> pad controller driver.
> 
> On the other hand, this is new functionality and we can easily gate that
> on the existence of this phandle, so it doesn't impact DT backwards
> compatibility. That also has the advantage of explicitly listing the
> dependency between the PMC and the XUSB pad controller in DT, so if we
> ever do get around to come up with an even better mechanism to resolve
> these dependencies than deferred probe, we do have the data already
> available.
> 
> Thierry
> 
Hi Thierry,
Thanks for the ideas. I will stay with the adding "nvidia,pmc" phandle property
implementation. As for the DT document change, I will modify it with below.

nvidia,pmc: phandle and specifier referring to the Tegra210 PMC node.

Thanks,
JC


[PATCH v2] arm64: tegra: jetson-tx1: Fix USB_VBUS_EN0 regulator

2020-11-18 Thread JC Kuo
USB_VBUS_EN0 regulator (regulator@11) is being overwritten by vdd-cam-1v2
regulator. This commit rearrange USB_VBUS_EN0 to be regulator@14.

Fixes: 257c8047be44 ("arm64: tegra: jetson-tx1: Add camera supplies")
Signed-off-by: JC Kuo 
Reviewed-by: Jon Hunter 
---
v2:
add 'Fixes:' tag
add Reviewed-by: Jon Hunter 

 .../arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 20 +--
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index e18e1a9a3011..a9caaf7c0d67 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1663,16 +1663,6 @@ vdd_usb_vbus: regulator@9 {
vin-supply = <_5v0_sys>;
};
 
-   vdd_usb_vbus_otg: regulator@11 {
-   compatible = "regulator-fixed";
-   regulator-name = "USB_VBUS_EN0";
-   regulator-min-microvolt = <500>;
-   regulator-max-microvolt = <500>;
-   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
-   enable-active-high;
-   vin-supply = <_5v0_sys>;
-   };
-
vdd_hdmi: regulator@10 {
compatible = "regulator-fixed";
regulator-name = "VDD_HDMI_5V0";
@@ -1712,4 +1702,14 @@ vdd_cam_1v8: regulator@13 {
enable-active-high;
vin-supply = <_3v3_sys>;
};
+
+   vdd_usb_vbus_otg: regulator@14 {
+   compatible = "regulator-fixed";
+   regulator-name = "USB_VBUS_EN0";
+   regulator-min-microvolt = <500>;
+   regulator-max-microvolt = <500>;
+   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
+   enable-active-high;
+   vin-supply = <_5v0_sys>;
+   };
 };
-- 
2.25.1



Re: [PATCH v1] arm64: tegra: jetson-tx1: Fix USB_VBUS_EN0 regulator

2020-11-18 Thread JC Kuo
On 11/18/20 7:24 PM, Jon Hunter wrote:
> 
> On 18/11/2020 03:46, JC Kuo wrote:
>> USB_VBUS_EN0 regulator (regulator@11) is being overwritten by vdd-cam-1v2
>> regulator. This commit rearrange USB_VBUS_EN0 to be regulator@14.
>>
>> Signed-off-by: JC Kuo 
>> ---
>>  .../arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 20 +--
>>  1 file changed, 10 insertions(+), 10 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi 
>> b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
>> index e18e1a9a3011..a9caaf7c0d67 100644
>> --- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
>> +++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
>> @@ -1663,16 +1663,6 @@ vdd_usb_vbus: regulator@9 {
>>  vin-supply = <_5v0_sys>;
>>  };
>>  
>> -vdd_usb_vbus_otg: regulator@11 {
>> -compatible = "regulator-fixed";
>> -regulator-name = "USB_VBUS_EN0";
>> -regulator-min-microvolt = <500>;
>> -regulator-max-microvolt = <500>;
>> -gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
>> -enable-active-high;
>> -vin-supply = <_5v0_sys>;
>> -};
>> -
>>  vdd_hdmi: regulator@10 {
>>  compatible = "regulator-fixed";
>>  regulator-name = "VDD_HDMI_5V0";
>> @@ -1712,4 +1702,14 @@ vdd_cam_1v8: regulator@13 {
>>  enable-active-high;
>>  vin-supply = <_3v3_sys>;
>>  };
>> +
>> +vdd_usb_vbus_otg: regulator@14 {
>> +compatible = "regulator-fixed";
>> +regulator-name = "USB_VBUS_EN0";
>> +regulator-min-microvolt = <500>;
>> +regulator-max-microvolt = <500>;
>> +gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
>> +enable-active-high;
>> +vin-supply = <_5v0_sys>;
>> +};
>>  };
>>
> 
> Thanks for catching this! We should add the 'Fixes:' tag.
Thanks for review. I will submit v2 and add 'Fixes:' tag.
> 
> By the way, I assume that VBUS is currently broken for the OTG port.
> Without this change is that USB port completely broken? I am wondering
> if we need to CC sta...@vger.kernel.org on this.
You are correct. Host mode at the OTG port is currently broken because VBUS is
not supplied to the OTG port. I will CC sta...@vger.kernel.org when send v2.
> 
> Reviewed-by: Jon Hunter 
> 
> Cheers
> Jon
> 


[PATCH v1] arm64: tegra: jetson-tx1: Fix USB_VBUS_EN0 regulator

2020-11-17 Thread JC Kuo
USB_VBUS_EN0 regulator (regulator@11) is being overwritten by vdd-cam-1v2
regulator. This commit rearrange USB_VBUS_EN0 to be regulator@14.

Signed-off-by: JC Kuo 
---
 .../arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 20 +--
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index e18e1a9a3011..a9caaf7c0d67 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1663,16 +1663,6 @@ vdd_usb_vbus: regulator@9 {
vin-supply = <_5v0_sys>;
};
 
-   vdd_usb_vbus_otg: regulator@11 {
-   compatible = "regulator-fixed";
-   regulator-name = "USB_VBUS_EN0";
-   regulator-min-microvolt = <500>;
-   regulator-max-microvolt = <500>;
-   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
-   enable-active-high;
-   vin-supply = <_5v0_sys>;
-   };
-
vdd_hdmi: regulator@10 {
compatible = "regulator-fixed";
regulator-name = "VDD_HDMI_5V0";
@@ -1712,4 +1702,14 @@ vdd_cam_1v8: regulator@13 {
enable-active-high;
vin-supply = <_3v3_sys>;
};
+
+   vdd_usb_vbus_otg: regulator@14 {
+   compatible = "regulator-fixed";
+   regulator-name = "USB_VBUS_EN0";
+   regulator-min-microvolt = <500>;
+   regulator-max-microvolt = <500>;
+   gpio = < TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
+   enable-active-high;
+   vin-supply = <_5v0_sys>;
+   };
 };
-- 
2.25.1



[PATCH v1] phy: tegra: xusb: Fix usb_phy device driver field

2020-11-17 Thread JC Kuo
In commit "phy: tegra: xusb: Add usb-phy support", an OTG capable PHY
device, such as phy-usb2.0 device of Jetson-TX1 platform, will be
bound to the tegra-xusb-padctl driver by the following line in
tegra_xusb_setup_usb_role_switch().

port->usb_phy.dev->driver = port->padctl->dev->driver;

With this, dev_pm_ops set of tegra-xusb-padctl driver will be invoked
for the OTG capable PHY incorrectly as below logs show.

This commit fixes the issue by assigning an empty driver to it.

[  153.451108] tegra-xusb-padctl phy-usb2.0: > 
tegra_xusb_padctl_suspend_noirq(dev=80917000)
[  153.460353] tegra-xusb-padctl phy-usb2.0:   driver: 8000114453e0 
(tegra_xusb_padctl_driver)
[  153.469245] tegra-xusb-padctl phy-usb2.0:   padctl: 829f6480
[  153.475772] tegra-xusb-padctl phy-usb2.0: soc: ef7bdd7f 
(0xef7bdd7f)
[  153.484061] Unable to handle kernel paging request at virtual address 
007bdd80004f
[  153.492132] Mem abort info:
[  153.495083]   ESR = 0x9604
[  153.498308]   EC = 0x25: DABT (current EL), IL = 32 bits
[  153.503771]   SET = 0, FnV = 0
[  153.506979]   EA = 0, S1PTW = 0
[  153.510260] Data abort info:
[  153.513200]   ISV = 0, ISS = 0x0004
[  153.517181]   CM = 0, WnR = 0
[  153.520302] [007bdd80004f] address between user and kernel address ranges
[  153.527600] Internal error: Oops: 9604 [#1] PREEMPT SMP
[  153.533231] Modules linked in: nouveau panel_simple tegra_video(C) tegra_drm 
drm_ttm_helper videobuf2_dma_contig ttm videobuf2_memops cec videobuf2_v4l2 
videobuf2_common drm_kms_helper v4l2_fwnode videodev drm mc snd_hda_codec_hdmi 
cdc_ether usbnet snd_hda_tegra r8152 crct10dif_ce snd_hda_codec snd_hda_core 
tegra_xudc host1x lp855x_bl at24 ip_tables x_tables ipv6
[  153.566417] CPU: 0 PID: 300 Comm: systemd-sleep Tainted: G C
5.10.0-rc3-next-20201113-00019-g5c064d5372b0-dirty #624
[  153.578283] Hardware name: NVIDIA Jetson TX1 Developer Kit (DT)
[  153.584281] pstate: 4005 (nZcv daif -PAN -UAO -TCO BTYPE=--)
[  153.590381] pc : tegra_xusb_padctl_suspend_noirq+0x88/0x100
[  153.596016] lr : tegra_xusb_padctl_suspend_noirq+0x80/0x100
[  153.601632] sp : 8000120dbb60
[  153.604999] x29: 8000120dbb60 x28: 80a1df00
[  153.610430] x27: 0002 x26: 8000106f8540
[  153.615858] x25: 8000113ac4a4 x24: 80001148c198
[  153.621277] x23: 800010c4538c x22: 0002
[  153.626692] x21: 800010ccde80 x20: 829f6480
[  153.632107] x19: 80917000 x18: 0030
[  153.637521] x17:  x16: 
[  153.642933] x15: 80a1e380 x14: 74636461702d6273
[  153.648346] x13: 8000113ad058 x12: 0f39
[  153.653759] x11: 0513 x10: 800011405058
[  153.659176] x9 : f000 x8 : 8000113ad058
[  153.664590] x7 : 800011405058 x6 : 
[  153.670002] x5 :  x4 : fe908bc0
[  153.675414] x3 : fe910228 x2 : 162ef67e0581e700
[  153.680826] x1 : 162ef67e0581e700 x0 : ef7bdd7f
[  153.686241] Call trace:
[  153.688769]  tegra_xusb_padctl_suspend_noirq+0x88/0x100
[  153.694077]  __device_suspend_noirq+0x68/0x1cc
[  153.698594]  dpm_noirq_suspend_devices+0x10c/0x1d0
[  153.703456]  dpm_suspend_noirq+0x28/0xa0
[  153.707461]  suspend_devices_and_enter+0x234/0x4bc
[  153.712314]  pm_suspend+0x1e4/0x270
[  153.715868]  state_store+0x8c/0x110
[  153.719440]  kobj_attr_store+0x1c/0x30
[  153.723259]  sysfs_kf_write+0x4c/0x7c
[  153.726981]  kernfs_fop_write+0x124/0x240
[  153.731065]  vfs_write+0xe4/0x204
[  153.734449]  ksys_write+0x6c/0x100
[  153.737925]  __arm64_sys_write+0x20/0x30
[  153.741931]  el0_svc_common.constprop.0+0x78/0x1a0
[  153.746789]  do_el0_svc+0x24/0x90
[  153.750181]  el0_sync_handler+0x254/0x260
[  153.754251]  el0_sync+0x174/0x180
[  153.757663] Code: aa0303e2 94000f64 f9405680 b4e0 (f9402803)
[  153.763826] ---[ end trace 81543a3394cb409d ]---

Fixes: e8f7d2f409a1 ("phy: tegra: xusb: Add usb-phy support")

Signed-off-by: JC Kuo 
---
 drivers/phy/tegra/xusb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index ad88d74c1884..181a1be5f491 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -688,7 +688,7 @@ static int tegra_xusb_setup_usb_role_switch(struct 
tegra_xusb_port *port)
 * reference to retrieve usb-phy details.
 */
port->usb_phy.dev = >pad->lanes[port->index]->dev;
-   port->usb_phy.dev->driver = port->padctl->dev->driver;
+   port->usb_phy.dev->driver = port->dev.driver;
port->usb_phy.otg->usb_phy = >usb_phy;
port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral;
port->usb_phy.otg->set_host = tegra_xusb_set_host;
-- 
2.25.1



Re: [PATCH v4 00/16] Tegra XHCI controller ELPG support

2020-11-16 Thread JC Kuo


On 11/14/20 12:44 AM, Thierry Reding wrote:
> On Fri, Oct 16, 2020 at 09:07:10PM +0800, JC Kuo wrote:
>> Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
>> state for power saving when all of the connected USB devices are in
>> suspended state. This patch series includes clk, phy and pmc changes
>> that are required for properly place controller in ELPG and bring
>> controller out of ELPG.
>>
>> JC Kuo (16):
>>   clk: tegra: Add PLLE HW power sequencer control
>>   clk: tegra: Don't enable PLLE HW sequencer at init
>>   phy: tegra: xusb: Move usb3 port init for Tegra210
>>   phy: tegra: xusb: tegra210: Do not reset UPHY PLL
>>   phy: tegra: xusb: Rearrange UPHY init on Tegra210
>>   phy: tegra: xusb: Add Tegra210 lane_iddq operation
>>   phy: tegra: xusb: Add sleepwalk and suspend/resume
>>   soc/tegra: pmc: Provide USB sleepwalk register map
>>   arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
>>   dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop
>>   phy: tegra: xusb: Add wake/sleepwalk for Tegra210
>>   phy: tegra: xusb: Tegra210 host mode VBUS control
>>   phy: tegra: xusb: Add wake/sleepwalk for Tegra186
>>   arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq
>>   usb: host: xhci-tegra: Unlink power domain devices
>>   xhci: tegra: Enable ELPG for runtime/system PM
>>
>>  .../phy/nvidia,tegra124-xusb-padctl.txt   |1 +
>>  arch/arm64/boot/dts/nvidia/tegra186.dtsi  |1 +
>>  arch/arm64/boot/dts/nvidia/tegra194.dtsi  |1 +
>>  arch/arm64/boot/dts/nvidia/tegra210.dtsi  |2 +
>>  drivers/clk/tegra/clk-pll.c   |   12 -
>>  drivers/clk/tegra/clk-tegra210.c  |   53 +-
>>  drivers/phy/tegra/xusb-tegra186.c |  558 -
>>  drivers/phy/tegra/xusb-tegra210.c | 1889 +
>>  drivers/phy/tegra/xusb.c  |   92 +-
>>  drivers/phy/tegra/xusb.h  |   22 +-
>>  drivers/soc/tegra/pmc.c   |   94 +
>>  drivers/usb/host/xhci-tegra.c |  610 --
>>  include/linux/clk/tegra.h |4 +-
>>  include/linux/phy/tegra/xusb.h|   10 +-
>>  14 files changed, 2785 insertions(+), 564 deletions(-)
> 
> I've been testing this, but I keep seeing the following oops on suspend
> on a Jetson TX1:
> 
> [  153.451108] tegra-xusb-padctl phy-usb2.0: > 
> tegra_xusb_padctl_suspend_noirq(dev=80917000)
> [  153.460353] tegra-xusb-padctl phy-usb2.0:   driver: 8000114453e0 
> (tegra_xusb_padctl_driver)
> [  153.469245] tegra-xusb-padctl phy-usb2.0:   padctl: 829f6480
> [  153.475772] tegra-xusb-padctl phy-usb2.0: soc: ef7bdd7f 
> (0xef7bdd7f)
> [  153.484061] Unable to handle kernel paging request at virtual address 
> 007bdd80004f
> [  153.492132] Mem abort info:
> [  153.495083]   ESR = 0x9604
> [  153.498308]   EC = 0x25: DABT (current EL), IL = 32 bits
> [  153.503771]   SET = 0, FnV = 0
> [  153.506979]   EA = 0, S1PTW = 0
> [  153.510260] Data abort info:
> [  153.513200]   ISV = 0, ISS = 0x0004
> [  153.517181]   CM = 0, WnR = 0
> [  153.520302] [007bdd80004f] address between user and kernel address 
> ranges
> [  153.527600] Internal error: Oops: 9604 [#1] PREEMPT SMP
> [  153.533231] Modules linked in: nouveau panel_simple tegra_video(C) 
> tegra_drm drm_ttm_helper videobuf2_dma_contig ttm videobuf2_memops cec 
> videobuf2_v4l2 videobuf2_common drm_kms_helper v4l2_fwnode videodev drm mc 
> snd_hda_codec_hdmi cdc_ether usbnet snd_hda_tegra r8152 crct10dif_ce 
> snd_hda_codec snd_hda_core tegra_xudc host1x lp855x_bl at24 ip_tables 
> x_tables ipv6
> [  153.566417] CPU: 0 PID: 300 Comm: systemd-sleep Tainted: G C   
>  5.10.0-rc3-next-20201113-00019-g5c064d5372b0-dirty #624
> [  153.578283] Hardware name: NVIDIA Jetson TX1 Developer Kit (DT)
> [  153.584281] pstate: 4005 (nZcv daif -PAN -UAO -TCO BTYPE=--)
> [  153.590381] pc : tegra_xusb_padctl_suspend_noirq+0x88/0x100
> [  153.596016] lr : tegra_xusb_padctl_suspend_noirq+0x80/0x100
> [  153.601632] sp : 8000120dbb60
> [  153.604999] x29: 8000120dbb60 x28: 80a1df00
> [  153.610430] x27: 0002 x26: 8000106f8540
> [  153.615858] x25: 8000113ac4a4 x24: 80001148c198
> [  153.621277] x23: 800010c4538c x22: 0002
> [  153.626692] x21: 800010ccde80 x20: 829f6480
> [  153.632107] x19: 80917000 x18: 0030
> [  153.637521] x17:  x16: 
> [  153.642933] x15: 80a1e380 x14: 74636461702d6273

Re: [PATCH] phy: tegra: Don't warn on probe deferral

2020-11-13 Thread JC Kuo
On 11/11/20 6:37 PM, Jon Hunter wrote:
> Deferred probe is an expected return value for devm_regulator_bulk_get().
> Given that the driver deals with it properly, there's no need to output a
> warning that may potentially confuse users.
> 
> Signed-off-by: Jon Hunter 
> ---
>  drivers/phy/tegra/xusb.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
> index ad88d74c1884..2eafb813825b 100644
> --- a/drivers/phy/tegra/xusb.c
> +++ b/drivers/phy/tegra/xusb.c
> @@ -1200,7 +1200,7 @@ static int tegra_xusb_padctl_probe(struct 
> platform_device *pdev)
>   err = devm_regulator_bulk_get(>dev, padctl->soc->num_supplies,
> padctl->supplies);
>   if (err < 0) {
> - dev_err(>dev, "failed to get regulators: %d\n", err);
> + dev_err_probe(>dev, err, "failed to get regulators\n");
>   goto remove;
>   }
>  
> 
Acked-by: JC Kuo 


Re: [PATCH v4 10/16] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2020-10-28 Thread JC Kuo
On 10/20/20 5:40 AM, Rob Herring wrote:
> On Fri, Oct 16, 2020 at 09:07:20PM +0800, JC Kuo wrote:
>> This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
>> PHY driver. It is a phandle and specifier referring to the Tegra210
>> pmc@7000e400 node.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v4:
>>new change to document "nvidia,pmc" prop
>>
>>  .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git 
>> a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
>> b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>> index 38c5fa21f435..ea559baeb546 100644
>> --- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>> +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>> @@ -54,6 +54,7 @@ For Tegra210:
>>  - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 
>> V.
>>  - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
>>  - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
>> +- nvidia,pmc: phandle and specifier referring to the Tegra210 pmc@7000e400 
>> node.
> 
> 'Tegra210 pmc@7000e400' is kind of specific. Going to update this for 
> every address and chip?
> 
> If there's only one PMC, you can just find the compatible PMC node. Then 
> you don't need a DT update.
> 
> Rob
> 
Hi Rob,
Thanks for your review and suggestion. Yes, there is only one PMC node. You mean
I can retrieve the PMC node with the following code and if do do DT update is
not required, right?

np = of_find_compatible_node(NULL, NULL, "nvidia,tegra210-pmc");

JC


[PATCH v4 12/16] phy: tegra: xusb: Tegra210 host mode VBUS control

2020-10-16 Thread JC Kuo
To support XUSB host controller ELPG, this commit moves VBUS control
.phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit().
When XUSB host controller enters ELPG, host driver invokes
.phy_power_off(), VBUS should remain ON so that USB devices will not
disconnect. VBUS can be turned OFF when host driver invokes
.phy_exit() which indicates disabling a USB port.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Add wake/sleepwalk for Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 52 ---
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 8af73ba78ad7..9d39f812fb43 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1819,8 +1819,25 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   unsigned int index = lane->index;
+   struct tegra_xusb_usb2_port *port;
+   int err;
u32 value;
 
+   port = tegra_xusb_find_usb2_port(padctl, index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_enable(port->supply);
+   if (err)
+   return err;
+   }
+
+   mutex_lock(>lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
   XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -1828,11 +1845,30 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
 
+   mutex_unlock(>lock);
+
return 0;
 }
 
 static int tegra210_usb2_phy_exit(struct phy *phy)
 {
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb2_port *port;
+   int err;
+
+   port = tegra_xusb_find_usb2_port(padctl, lane->index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", 
lane->index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_disable(port->supply);
+   if (err)
+   return err;
+   }
+
return 0;
 }
 
@@ -1953,6 +1989,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
priv = to_tegra210_xusb_padctl(padctl);
 
+   mutex_lock(>lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -2046,14 +2084,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
  XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-   if (port->supply && port->mode == USB_DR_MODE_HOST) {
-   err = regulator_enable(port->supply);
-   if (err)
-   return err;
-   }
-
-   mutex_lock(>lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(>lock);
@@ -2062,7 +2092,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
err = clk_prepare_enable(pad->clk);
if (err)
-   goto disable_regulator;
+   goto out;
 
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -2094,8 +2124,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
return 0;
 
-disable_regulator:
-   regulator_disable(port->supply);
+out:
mutex_unlock(>lock);
return err;
 }
@@ -2154,7 +2183,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
 
 out:
-   regulator_disable(port->supply);
mutex_unlock(>lock);
return 0;
 }
-- 
2.25.1



[PATCH v4 11/16] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2020-10-16 Thread JC Kuo
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk
routines. Sleepwalk logic is in PMC (always-on) hardware block.
PMC driver provides managed access to the sleepwalk registers
via regmap framework.

Signed-off-by: JC Kuo 
---
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
   remove a blank line 
   rename 'pmc_dev' with 'pdev'
   remove 'struct device_node *np' 
   rename label 'no_pmc' with 'out'
   defer .probe() if PMC driver is yet to load 

v3:
   rename 'pmc_reg" with 'regmap' and move to the top of 'struct 
tegra210_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'
   rename tegra_pmc_*() with tegra210_pmc_*()
   remove VBUS ON/OFF control change

 drivers/phy/tegra/xusb-tegra210.c | 930 ++
 1 file changed, 930 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index b038d032fea1..8af73ba78ad7 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -16,6 +16,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 
@@ -52,6 +54,20 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define   USB2_PORT_WAKE_INTERRUPT_ENABLE(x)  BIT((x))
+#define   USB2_PORT_WAKEUP_EVENT(x)   BIT((x) + 7)
+#define   SS_PORT_WAKE_INTERRUPT_ENABLE(x)BIT((x) + 14)
+#define   SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define   USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define   USB2_HSIC_PORT_WAKEUP_EVENT(x)  BIT((x) + 30)
+#define   ALL_WAKE_EVENTS ( \
+   USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+   USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+   SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+   SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+   USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define   RPD_CTRL(x)  (((x) & 0x1f) << 26)
+#define   RPD_CTRL_VALUE(x)(((x) >> 26) & 0x1f)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define   TCTRL_VALUE(x)(((x) & 0x3f) >> 0)
+#define   PCTRL_VALUE(x)(((x) >> 6) & 0x3f)
 
 #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
 #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -251,16 +271,161 @@
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
+/* 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)

[PATCH v4 16/16] xhci: tegra: Enable ELPG for runtime/system PM

2020-10-16 Thread JC Kuo
This commit implements the complete programming sequence for ELPG
entry and exit.

 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).

 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.

At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.

Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.

The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.

Signed-off-by: JC Kuo 
---
v4:
   reshuffle the code to avoid these pre-declarations
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 404 +++---
 1 file changed, 368 insertions(+), 36 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 375aaf4d22dc..4e854d1ba74d 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -15,9 +15,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -224,6 +226,7 @@ struct tegra_xusb {
 
int xhci_irq;
int mbox_irq;
+   int padctl_irq;
 
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -269,6 +272,7 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
 
+   bool suspended;
struct tegra_xusb_context context;
 };
 
@@ -658,6 +662,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
mutex_lock(>lock);
 
+   if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+   goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(, value);
 
@@ -671,6 +678,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
tegra_xusb_mbox_handle(tegra, );
 
+out:
mutex_unlock(>lock);
return IRQ_HANDLED;
 }
@@ -811,16 +819,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
}
 }
 
-static int tegra_xusb_runtime_suspend(struct device *dev)
-{
-   return 0;
-}
-
-static int tegra_xusb_runtime_resume(struct device *dev)
-{
-   return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int tegra_xusb_init_context(struct tegra_xusb *tegra)
 {
@@ -1121,6 +1119,24 @@ static int __tegra_xusb_enable_firmware_messages(struct 
tegra_xusb *tegra)
return err;
 }
 
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+   struct tegra_xusb *tegra = data;
+
+   mutex_lock(>lock);
+
+   if (tegra->suspended) {
+   mutex_unlock(>lock);
+   return IRQ_HANDLED;
+   }
+
+   mutex_unlock(>lock);
+
+   pm_runtime_resume(tegra->dev);
+
+   return IRQ_HANDLED;
+}
+
 static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
 {
int err;
@@ -1244,6 +1260,50 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
 }
 
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   struct tegra_xusb_padctl *padctl = tegra->padctl;
+   unsigned int i;
+   int port;
+
+   for (i = 0; i < tegra->num_usb_phys; i++) {
+   if (is_usb2_otg_phy(tegra, i)) {
+   port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+   if ((port >= 0) && (index == (unsigned int)port))
+   return true;
+   }
+   }
+
+   return false;
+}
+
+static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, 
unsigned int index)
+{
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0)
+   return true;
+
+   if (strcmp(tegra->soc->phy_types[phy_type].name, "usb2") == 0) {
+   if (is_usb2_otg_phy(tegra, index))
+   return ((index == tegra->otg_usb2_port) && 
tegra->host_mode);
+   else
+   return true;
+   }
+
+   if (strcmp(

[PATCH v4 13/16] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2020-10-16 Thread JC Kuo
This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
sleepwalk operations.

Signed-off-by: JC Kuo 
---
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'

 drivers/phy/tegra/xusb-tegra186.c | 558 +-
 1 file changed, 557 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c 
b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..2208c26f8af9 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -113,6 +113,117 @@
 #define  ID_OVERRIDE_FLOATING  ID_OVERRIDE(8)
 #define  ID_OVERRIDE_GROUNDED  ID_OVERRIDE(0)
 
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL   (0x4)
+#define   UHSIC_LINE_DEB_CNT(x)(((x) & 0xf) << 4)
+#define   UTMIP_LINE_DEB_CNT(x)((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x)  (0x40 + (x) * 4)
+#define   CLR_WALK_PTR (1 << 0)
+#define   CAP_CFG  (1 << 1)
+#define   CLR_WAKE_ALARM   (1 << 3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x)  (0x60 + (x) * 4)
+#define   HSIC_CLR_WALK_PTR(1 << 0)
+#define   HSIC_CLR_WAKE_ALARM  (1 << 3)
+#define   HSIC_CAP_CFG (1 << 4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x)   (0x70 + (x) * 4)
+#define   SPEED(x) ((x) & 0x3)
+#define UTMI_HSSPEED(0)
+#define UTMI_FSSPEED(1)
+#define UTMI_LSSPEED(2)
+#define UTMI_RST   SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x)   (0x90 + (x) * 4)
+#define   MODE(x)  ((x) & 0x1)
+#define   MODE_HS  MODE(0)
+#define   MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define   FAKE_USBOP_VAL   (1 << 0)
+#define   FAKE_USBON_VAL   (1 << 1)
+#define   FAKE_USBOP_EN(1 << 2)
+#define   FAKE_USBON_EN(1 << 3)
+#define   FAKE_STROBE_VAL  (1 << 0)
+#define   FAKE_DATA_VAL(1 << 1)
+#define   FAKE_STROBE_EN   (1 << 2)
+#define   FAKE_DATA_EN (1 << 3)
+#define   WAKE_WALK_EN (1 << 14)
+#define   MASTER_ENABLE(1 << 15)
+#define   LINEVAL_WALK_EN  (1 << 16)
+#define   WAKE_VAL(x)  (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE  WAKE_VAL(12)
+#define WAKE_VAL_ANY   WAKE_VAL(15)
+#define WAKE_VAL_DS10  WAKE_VAL(2)
+#define   LINE_WAKEUP_EN   (1 << 21)
+#define   MASTER_CFG_SEL   (1 << 22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define   USBOP_RPD_A  (1 << 0)
+#define   USBON_RPD_A  (1 << 1)
+#define   AP_A (1 << 4)
+#define   AN_A (1 << 5)
+#define   HIGHZ_A  (1 << 6)
+/* phase B */
+#define   USBOP_RPD_B  (1 << 8)
+#define   USBON_RPD_B  (1 << 9)
+#define   AP_B (1 << 12)
+#define   AN_B (1 << 13)
+#define   HIGHZ_B  (1 << 14)
+/* phase C */
+#define   USBOP_RPD_C  (1 << 16)
+#define   USBON_RPD_C  (1 << 17)
+#define   AP_C (1 << 20)
+#define   AN_C (1 << 21)
+#define   HIGHZ_C  (1 << 22)
+/* phase D */
+#define   USBOP_RPD_D  (1 << 24)
+#define   USBON_RPD_D  (1 << 25)
+#d

[PATCH v4 14/16] arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq

2020-10-16 Thread JC Kuo
This commit adds "interrupts" property to Tegra210/Tegra186/Tegra194
XUSB PADCTL node. XUSB PADCTL interrupt will be raised when USB wake
event happens. This is required for supporting XUSB host controller
ELPG.

Signed-off-by: JC Kuo 
---
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 3 files changed, 3 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index 0c46ab7bbbf3..c5a82bccc6ff 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -685,6 +685,7 @@ padctl: padctl@352 {
reg = <0x0 0x0352 0x0 0x1000>,
  <0x0 0x0354 0x0 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA186_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index e9c90f0f44ff..73bad615020a 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -801,6 +801,7 @@ xusb_padctl: padctl@352 {
reg = <0x0352 0x1000>,
  <0x0354 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA194_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 120b92d9b80b..26660aec730e 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1043,6 +1043,7 @@ padctl: padctl@7009f000 {
resets = <_car 142>;
reset-names = "padctl";
nvidia,pmc =  <_pmc>;
+   interrupts = ;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v4 15/16] usb: host: xhci-tegra: Unlink power domain devices

2020-10-16 Thread JC Kuo
This commit unlinks xhci-tegra platform device with SS/host power
domain devices. Reasons for this change is - at ELPG entry, PHY
sleepwalk and wake configuration need to be done before powering
down SS/host partitions, and PHY need be powered off after powering
down SS/host partitions. Sequence looks like roughly below:

  tegra_xusb_enter_elpg() -> xhci_suspend()
  -> enable PHY sleepwalk and wake if needed
  -> power down SS/host partitions
  -> power down PHY

If SS/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.

This commit introduces:
  1. tegra_xusb_unpowergate_partitions() to power up SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_get_sync() to request power
 driver to power up partitions; If power domain devices are not
 available, tegra_powergate_sequence_power_up() will be used to
 power up partitions.

  2. tegra_xusb_powergate_partitions() to power down SS and host
 partitions together. If SS/host power domain devices are
 available, it invokes pm_runtime_put_sync() to request power
 driver to power down partitions; If power domain devices are not
 available, tegra_powergate_power_off() will be used to power down
 partitions.

Signed-off-by: JC Kuo 
---
v4:
   commit message improvement
   update copyright string
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()

 drivers/usb/host/xhci-tegra.c | 206 ++
 1 file changed, 112 insertions(+), 94 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 934be1686352..375aaf4d22dc 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2,7 +2,7 @@
 /*
  * NVIDIA Tegra xHCI host controller driver
  *
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
  * Copyright (C) 2014 Google, Inc.
  */
 
@@ -249,8 +249,7 @@ struct tegra_xusb {
 
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
-   struct device_link *genpd_dl_host;
-   struct device_link *genpd_dl_ss;
+   bool use_genpd;
 
struct phy **phys;
unsigned int num_phys;
@@ -814,36 +813,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
-   regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-   tegra_xusb_clk_disable(tegra);
-
return 0;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-   int err;
-
-   err = tegra_xusb_clk_enable(tegra);
-   if (err) {
-   dev_err(dev, "failed to enable clocks: %d\n", err);
-   return err;
-   }
-
-   err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
-   if (err) {
-   dev_err(dev, "failed to enable regulators: %d\n", err);
-   goto disable_clk;
-   }
-
return 0;
-
-disable_clk:
-   tegra_xusb_clk_disable(tegra);
-   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1019,10 +994,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb 
*tegra)
 static void tegra_xusb_powerdomain_remove(struct device *dev,
  struct tegra_xusb *tegra)
 {
-   if (tegra->genpd_dl_ss)
-   device_link_del(tegra->genpd_dl_ss);
-   if (tegra->genpd_dl_host)
-   device_link_del(tegra->genpd_dl_host);
+   if (!tegra->use_genpd)
+   return;
+
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1022,84 @@ static int tegra_xusb_powerdomain_init(struct device 
*dev,
return err;
}
 
-   tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
-  DL_FLAG_PM_RUNTIME |
-  DL_FLAG_STATELESS);
-   if (!tegra->genpd_dl_host) {
-   dev_err(dev, "adding host device link failed!\n");
-   return -ENODEV;
+   tegra->use_genpd = true;
+
+   return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+   struct device *dev = tegra->dev;
+   int rc;
+
+   if (tegra->use_genpd) {
+   rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
+   if (rc < 0) {
+   dev_err(dev, "

[PATCH v4 03/16] phy: tegra: xusb: Move usb3 port init for Tegra210

2020-10-16 Thread JC Kuo
The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy
.power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane
is assigned for XUSB super-speed.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4:
   mutex_lock()/mutex_unlock() fix
   update copyright string
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 316 +-
 drivers/phy/tegra/xusb.c  |   4 +-
 drivers/phy/tegra/xusb.h  |   4 +-
 3 files changed, 180 insertions(+), 144 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..4dc9286ec1b8 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (C) 2015 Google, Inc.
  */
 
@@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
 }
 
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+   { 0, "pcie", 6 },
+   { 1, "pcie", 5 },
+   { 2, "pcie", 0 },
+   { 2, "pcie", 3 },
+   { 3, "pcie", 4 },
+   { 3, "sata", 0 },
+   { 0, NULL,   0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+   const struct tegra_xusb_lane_map *map;
+
+   for (map = tegra210_usb3_map; map->type; map++) {
+   if (map->index == lane->index &&
+   strcmp(map->type, lane->pad->soc->name) == 0) {
+   dev_dbg(lane->pad->padctl->dev, "lane = %s map to port 
= usb3-%d\n",
+   lane->pad->soc->lanes[lane->index].name, 
map->port);
+   return map->port;
+   }
+   }
+
+   return -EINVAL;
+}
+
 /* must be called under padctl->lock */
 static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
@@ -470,19 +496,14 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
 
-   mutex_lock(>lock);
-
if (WARN_ON(pcie->enable == 0))
-   goto unlock;
+   return;
 
if (--pcie->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 /* must be called under padctl->lock */
@@ -712,19 +733,14 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
 
-   mutex_lock(>lock);
-
if (WARN_ON(sata->enable == 0))
-   goto unlock;
+   return;
 
if (--sata->enable > 0)
-   goto unlock;
+   return;
 
reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
@@ -1599,6 +1615,128 @@ static const struct tegra_xusb_lane_soc 
tegra210_pcie_lanes[] = {
TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
 };
 
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+   int port;
+
+   if (!lane || !lane->pad || !lane->pad->padctl)
+   return NULL;
+
+   port = tegra210_usb3_lane_map(lane);
+   if (port < 0)
+   return NULL;
+
+   return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+   struct device *dev = >dev;
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+   unsigned int index;
+   u32 value;
+
+   if (!usb3) {
+   dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+   return -ENODEV;
+   }
+
+   index = usb3->base.index;
+
+   value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+   if (!usb3->internal)
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+   else
+   value |= XUSB_PADCTL_SS_PORT_MAP_PORT

[PATCH v4 09/16] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2020-10-16 Thread JC Kuo
PMC driver provides USB sleepwalk registers access to XUSB PADCTL
driver. This commit adds a "nvidia,pmc" property which points to
PMC node to XUSB PADCTL device node.

Signed-off-by: JC Kuo 
---
v4:
   no change
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index d47c88950d38..120b92d9b80b 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1042,6 +1042,7 @@ padctl: padctl@7009f000 {
reg = <0x0 0x7009f000 0x0 0x1000>;
resets = <_car 142>;
reset-names = "padctl";
+   nvidia,pmc =  <_pmc>;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v4 10/16] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop

2020-10-16 Thread JC Kuo
This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb
PHY driver. It is a phandle and specifier referring to the Tegra210
pmc@7000e400 node.

Signed-off-by: JC Kuo 
---
v4:
   new change to document "nvidia,pmc" prop

 .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt  | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt 
b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 38c5fa21f435..ea559baeb546 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -54,6 +54,7 @@ For Tegra210:
 - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
 - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
 - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
+- nvidia,pmc: phandle and specifier referring to the Tegra210 pmc@7000e400 
node.
 
 For Tegra186:
 - avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY
-- 
2.25.1



[PATCH v4 05/16] phy: tegra: xusb: Rearrange UPHY init on Tegra210

2020-10-16 Thread JC Kuo
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:

1. PLLE hardware power sequencer has to be enabled only after both
   PEX UPHY PLL and SATA UPHY PLL are initialized.
   tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()

2. At cold boot and SC7 exit, the following bits must be cleared after
   PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
   a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
   b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
   c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN

   tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
   charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
   will be cleared by tegra210_aux_mux_lp0_clamp_disable().

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4: 

   
   no change
v3:
   make separate changes
   use "unsigned int" instead "int" type for PHY index
   add blank line for better readability

 drivers/phy/tegra/xusb-tegra210.c | 195 --
 drivers/phy/tegra/xusb.h  |   4 +-
 2 files changed, 103 insertions(+), 96 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 9bfecdfecf35..faacb866cd1f 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
 
-   if (pcie->enable > 0) {
-   pcie->enable++;
+   if (pcie->enable)
return 0;
-   }
 
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
 
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 
tegra210_xusb_pll_hw_sequence_start();
 
-   pcie->enable++;
+skip_pll_init:
+   pcie->enable = true;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
return 0;
 
@@ -495,28 +504,44 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+   u32 value;
+   unsigned int i;
 
-   if (WARN_ON(pcie->enable == 0))
+   if (WARN_ON(!pcie->enable))
return;
 
-   if (--pcie->enable > 0)
-   return;
+   pcie->enable = false;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
clk_disable_unprepare(pcie->pll);
 }
 
 /* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool 
usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+   struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
+   bool usb;
 
-   if (sata->enable > 0) {
-   sata->enable++;
+   if (sata->enable)
return 0;
-   }
+
+   if (IS_ERR(lane))
+   return 0;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
+   usb = tegra_xusb_lane_check(lane, "usb3-ss");
 
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -717,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct 
tegra_xusb_padctl *padctl, bool usb)
 
tegra210_sata_pll_hw_sequence_start();
 
-   sata->enable++;
+skip_pll_init:
+   sata->enable 

[PATCH v4 01/16] clk: tegra: Add PLLE HW power sequencer control

2020-10-16 Thread JC Kuo
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.

XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.

PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.

This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
   PLLE hardware sequencer at proper time.

2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
   check whether PLLE hardware sequencer has been enabled or not.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4:
   update copyright strings
v3:
   rename 'val' with 'value

 drivers/clk/tegra/clk-tegra210.c | 53 +++-
 include/linux/clk/tegra.h|  4 ++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 68cbb98af567..b9099012dc7b 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2014 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020 NVIDIA CORPORATION.  All rights reserved.
  */
 
 #include 
@@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLRE_BASE_DEFAULT_MASK0x1c00
 #define PLLRE_MISC0_WRITE_MASK 0x67ff
 
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET   (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE(1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL  (1 << 4)
+#define PLLE_AUX_SS_SWCTL  (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE(1 << 24)
+
 /* PLLX */
 #define PLLX_USE_DYN_RAMP  1
 #define PLLX_BASE_LOCK (1 << 27)
@@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLU_MISC0_WRITE_MASK  0xbfff
 #define PLLU_MISC1_WRITE_MASK  0x0007
 
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+   u32 value;
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   if (value & PLLE_AUX_SEQ_ENABLE)
+   return true;
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+   u32 value;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   return 0;
+
+   /* skip if PLLE is not enabled yet */
+   value = readl_relaxed(clk_base + PLLE_MISC0);
+   if (!(value & PLLE_MISC_LOCK))
+   return -EIO;
+
+   value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+   writel_relaxed(value, clk_base + PLLE_MISC0);
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+   value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   value |= PLLE_AUX_SEQ_ENABLE;
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
 void tegra210_xusb_pll_hw_control_enable(void)
 {
u32 val;
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index 3f01d43f0598..7be477377ad3 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2020, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #ifndef __LINUX_CLK_TEGRA_H_
@@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void)
 }
 #endif
 
+extern int tegra210_plle_hw_sequence_start(void);
+extern bool tegra210_plle_hw_sequence_is_enabled(void);
 extern void tegra210_xusb_pll_hw_control_enable(void);
 extern void tegra210_xusb_pll_hw_sequence_start(void);
 extern void tegra210_sata_pll_hw_control_enable(void);
-- 
2.25.1



[PATCH v4 07/16] phy: tegra: xusb: Add sleepwalk and suspend/resume

2020-10-16 Thread JC Kuo
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.

Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
and can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB UPHY individually.

  - tegra_xusb_padctl_enable_phy_sleepwalk()
  - tegra_xusb_padctl_disable_phy_sleepwalk()

Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB PHY individually.

  - tegra_xusb_padctl_enable_phy_wake()
  - tegra_xusb_padctl_disable_phy_wake()
  - tegra_xusb_padctl_remote_wake_detected()

This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
  - tegra_xusb_padctl_suspend_noirq()
  - tegra_xusb_padctl_resume_noirq()

Signed-off-by: JC Kuo 
---
v4:
   move sleepwalk/wake stubs from 'struct tegra_xusb_padctl_ops' to
   'struct tegra_xusb_lane_ops'
v3:
   commit message improvement, no change in code

 drivers/phy/tegra/xusb.c   | 82 ++
 drivers/phy/tegra/xusb.h   |  8 
 include/linux/phy/tegra/xusb.h | 10 -
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 90578fb98aab..f0dc9c43959f 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1274,10 +1274,36 @@ static int tegra_xusb_padctl_remove(struct 
platform_device *pdev)
return err;
 }
 
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+   return padctl->soc->ops->suspend_noirq(padctl);
+
+   return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+   return padctl->soc->ops->resume_noirq(padctl);
+
+   return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
 static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+   .pm = _xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1344,6 +1370,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy,
+  enum usb_device_speed speed)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_sleepwalk)
+   return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_sleepwalk)
+   return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct 
phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->enable_phy_wake)
+   return lane->pad->ops->enable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->disable_phy_wake)
+   return lane->pad->ops->disable_phy_wake(lane);
+
+   return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+   if (lane->pad->ops->remote_wake_detected)
+   return lane->pad->ops->remote_

[PATCH v4 08/16] soc/tegra: pmc: Provide USB sleepwalk register map

2020-10-16 Thread JC Kuo
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4:
   commit message improvement
   remove an unnecessary type cast when invokes devm_regmap_init()
v3:
   commit message improvement
   drop regmap_reg() usage
   rename 'reg' with 'offset'
   rename 'val' with 'value'
   drop '__force' when invokes devm_regmap_init()
   print error code of devm_regmap_init()
   move devm_regmap_init() a litter bit earlier
   explicitly set '.has_usb_sleepwalk=false'

 drivers/soc/tegra/pmc.c | 94 +
 1 file changed, 94 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index df9a5ca8c99c..a619a23f9592 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -43,6 +43,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -102,6 +103,9 @@
 
 #define PMC_PWR_DET_VALUE  0xe4
 
+#define PMC_USB_DEBOUNCE_DEL   0xec
+#define PMC_USB_AO 0xf0
+
 #define PMC_SCRATCH41  0x140
 
 #define PMC_WAKE2_MASK 0x160
@@ -133,6 +137,13 @@
 #define IO_DPD2_STATUS 0x1c4
 #define SEL_DPD_TIM0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS   0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG  0x1fc
+#define PMC_UTMIP_UHSIC_FAKE   0x218
+
 #define PMC_SCRATCH54  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT  8
 #define  PMC_SCRATCH54_ADDR_SHIFT  0
@@ -145,8 +156,18 @@
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL0x270
+#define PMC_UTMIP_MASTER_CONFIG0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS  0x27c
+#define PMC_UTMIP_MASTER2_CONFIG   0x29c
+
 #define GPU_RG_CNTRL   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0 0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3 0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
bool has_blink_output;
+   bool has_usb_sleepwalk;
 };
 
 /**
@@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
*pmc,
 err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+   regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+   regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+   regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+   regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
PMC_UTMIP_UHSIC_LINE_WAKEUP),
+   regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+   regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+   regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+   regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+   .yes_ranges = pmc_usb_sleepwalk_ranges,
+   .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned 
int *value)
+{
+   struct tegra_pmc *pmc = context;
+
+   *value = tegra_pmc_readl(pmc, offset);
+   return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, 
unsigned int value)
+{
+   struct tegra_pmc *pmc = context;
+
+   tegra_pmc_writel(pmc, value, offset);
+   return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+   .name = "usb_sleepwalk",
+   .reg_bits = 32,
+   .val_bits = 32,
+   .reg_stride = 4,
+   .fast_io = true,
+   .rd_table = _usb_sleepwalk_table,
+   .wr_table = _usb_sleepwalk_table,
+   .reg_read = tegra_pmc_regmap_readl,
+   .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+   struct regmap *regmap;
+   int err;
+
+   if (pmc->soc->has_usb_sleepwalk) {
+   regmap = devm_regmap_init(pmc->dev, NULL, pmc, 
_sleepwalk_regmap_config);
+   if (IS_ERR(regmap)) {
+   err = PTR_ERR(regmap);
+   dev_err(pmc->dev, "failed to allocate register map 
(%d)\n", err);
+   return err;
+   }
+   }
+
+   return 0;
+}
+
 static int 

[PATCH v4 06/16] phy: tegra: xusb: Add Tegra210 lane_iddq operation

2020-10-16 Thread JC Kuo
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 
Acked-by: Thierry Reding 
---
v4:
   no change
v3:
   add 'misc_ctl2' data member to UPHY lane for carrying MISC_PAD_PX_CTL2 offset
   tegra210_uphy_lane_iddq_[enable/disable]() to access 'misc_ctl2' data member

 drivers/phy/tegra/xusb-tegra210.c | 82 ---
 drivers/phy/tegra/xusb.c  |  6 +++
 drivers/phy/tegra/xusb.h  |  6 +++
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index faacb866cd1f..b038d032fea1 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
@@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad 
= {
.ops = _hsic_ops,
 };
 
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc)
\
+   {   \
+   .name = _name,  \
+   .offset = _offset,  \
+   .shift = _shift,\
+   .mask = _mask,  \
+   .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions),  \
+   .funcs = tegra210_##_type##_functions,  \
+   .regs.misc_ctl2 = _misc,\
+   }
+
 static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1648,13 +1710,13 @@ static const char *tegra210_pcie_functions[] = {
 };
 
 

[PATCH v4 02/16] clk: tegra: Don't enable PLLE HW sequencer at init

2020-10-16 Thread JC Kuo
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.

Signed-off-by: JC Kuo 
Acked-by: Thierry Reding 
---
v4:
   no change 
v3:
   no change

 drivers/clk/tegra/clk-pll.c | 12 
 1 file changed, 12 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c5cc0a2dac6f..0193cebe8c5a 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw)
pll_writel(val, PLLE_SS_CTRL, pll);
udelay(1);
 
-   val = pll_readl_misc(pll);
-   val &= ~PLLE_MISC_IDDQ_SW_CTRL;
-   pll_writel_misc(val, pll);
-
-   val = pll_readl(pll->params->aux_reg, pll);
-   val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
-   val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
-   pll_writel(val, pll->params->aux_reg, pll);
-   udelay(1);
-   val |= PLLE_AUX_SEQ_ENABLE;
-   pll_writel(val, pll->params->aux_reg, pll);
-
 out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
-- 
2.25.1



[PATCH v4 04/16] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2020-10-16 Thread JC Kuo
Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
This commit removes reset_control_assert(pcie->rst) and
reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.

Signed-off-by: JC Kuo 
---
v4:
   no change
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 4dc9286ec1b8..9bfecdfecf35 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -502,7 +502,6 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--pcie->enable > 0)
return;
 
-   reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
 }
 
@@ -739,7 +738,6 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--sata->enable > 0)
return;
 
-   reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
 }
 
-- 
2.25.1



[PATCH v4 00/16] Tegra XHCI controller ELPG support

2020-10-16 Thread JC Kuo
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.

JC Kuo (16):
  clk: tegra: Add PLLE HW power sequencer control
  clk: tegra: Don't enable PLLE HW sequencer at init
  phy: tegra: xusb: Move usb3 port init for Tegra210
  phy: tegra: xusb: tegra210: Do not reset UPHY PLL
  phy: tegra: xusb: Rearrange UPHY init on Tegra210
  phy: tegra: xusb: Add Tegra210 lane_iddq operation
  phy: tegra: xusb: Add sleepwalk and suspend/resume
  soc/tegra: pmc: Provide USB sleepwalk register map
  arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
  dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop
  phy: tegra: xusb: Add wake/sleepwalk for Tegra210
  phy: tegra: xusb: Tegra210 host mode VBUS control
  phy: tegra: xusb: Add wake/sleepwalk for Tegra186
  arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq
  usb: host: xhci-tegra: Unlink power domain devices
  xhci: tegra: Enable ELPG for runtime/system PM

 .../phy/nvidia,tegra124-xusb-padctl.txt   |1 +
 arch/arm64/boot/dts/nvidia/tegra186.dtsi  |1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi  |1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi  |2 +
 drivers/clk/tegra/clk-pll.c   |   12 -
 drivers/clk/tegra/clk-tegra210.c  |   53 +-
 drivers/phy/tegra/xusb-tegra186.c |  558 -
 drivers/phy/tegra/xusb-tegra210.c | 1889 +
 drivers/phy/tegra/xusb.c  |   92 +-
 drivers/phy/tegra/xusb.h  |   22 +-
 drivers/soc/tegra/pmc.c   |   94 +
 drivers/usb/host/xhci-tegra.c |  610 --
 include/linux/clk/tegra.h |4 +-
 include/linux/phy/tegra/xusb.h|   10 +-
 14 files changed, 2785 insertions(+), 564 deletions(-)

-- 
2.25.1



Re: [PATCH v3 15/15] xhci: tegra: Enable ELPG for runtime/system PM

2020-10-15 Thread JC Kuo
I will amend accordingly and submit new patch.

Thanks for review.
JC

On 9/28/20 10:06 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:41PM +0800, JC Kuo wrote:
>> This commit implements the complete programming sequence for ELPG
>> entry and exit.
>>
>>  1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
>> and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
>> sleepwalk and wake detection circuits to maintain USB lines level
>> and respond to wake events (wake-on-connect, wake-on-disconnect,
>> device-initiated-wake).
>>
>>  2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
>> and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
>> wake detection circuits.
>>
>> At runtime suspend, XUSB host controller can enter ELPG to reduce
>> power consumption. When XUSB PADCTL wake detection circuit detects
>> a wake event, an interrupt will be raised. xhci-tegra driver then
>> will invoke pm_runtime_resume() for xhci-tegra.
>>
>> Runtime resume could also be triggered by protocol drivers, this is
>> the host-initiated-wake event. At runtime resume, xhci-tegra driver
>> brings XUSB host controller out of ELPG to handle the wake events.
>>
>> The same ELPG enter/exit procedure will be performed for system
>> suspend/resume path so USB devices can remain connected across SC7.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v3:
>>use 'unsigned int' for PHY index
>>remove unnecessary 'else'
>>drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()
>>
>>  drivers/usb/host/xhci-tegra.c | 389 +++---
>>  1 file changed, 360 insertions(+), 29 deletions(-)
>>
>> diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
>> index aabff8ee0bb3..ba3f40e78171 100644
>> --- a/drivers/usb/host/xhci-tegra.c
>> +++ b/drivers/usb/host/xhci-tegra.c
>> @@ -15,9 +15,11 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>  #include 
>>  #include 
>>  #include 
>> @@ -224,6 +226,7 @@ struct tegra_xusb {
>>  
>>  int xhci_irq;
>>  int mbox_irq;
>> +int padctl_irq;
>>  
>>  void __iomem *ipfs_base;
>>  void __iomem *fpci_base;
>> @@ -269,10 +272,13 @@ struct tegra_xusb {
>>  dma_addr_t phys;
>>  } fw;
>>  
>> +bool suspended;
>>  struct tegra_xusb_context context;
>>  };
>>  
>>  static struct hc_driver __read_mostly tegra_xhci_hc_driver;
>> +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime);
>> +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime);
> 
> Can we reshuffle the code to avoid these predeclarations? Looks like
> they're only used in tegra_xusb_runtime_{suspend,resume}(), so perhaps
> move the implementations right before those?
> 
> Thierry
> 


Re: [PATCH v3 14/15] usb: host: xhci-tegra: Unlink power domain devices

2020-10-15 Thread JC Kuo
I will modify the commit message accordingly.

Thanks for review.
JC

On 9/28/20 9:53 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:40PM +0800, JC Kuo wrote:
>> This commit unlinks xhci-tegra platform device with ss/host power
>> domain devices. Reasons for this change is - at elpg entry, PHY
> 
> s/elpg/ELPG/
> 
>> sleepwalk and wake configuration need to be done before powering
>> down ss/host partitions, and PHY need be powered off after powering
> 
> s/ss/SS/ here (because it's an abbreviation) and in a few cases below.
> 
> Otherwise this seems fine, though it'd be good if Jon could take a look
> since he's more familiar with the power domain setup here.
> 
> Thierry
> 


Re: [PATCH v3 12/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2020-10-15 Thread JC Kuo
I will amend accordingly and submit new patch.

Thanks for review.
JC

On 9/28/20 9:50 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:38PM +0800, JC Kuo wrote:
>> This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
>> sleepwalk operations.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v3:
>>move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
>>change return data of .phy_remote_wake_detected() to 'bool'
>>change input parameter of .phy_remote_wake_detected() to 'struct phy*'
>>remove unnecessary 'else'
>>rename 'val' with 'value'
>>
>>  drivers/phy/tegra/xusb-tegra186.c | 626 ++
>>  1 file changed, 626 insertions(+)
>>
>> diff --git a/drivers/phy/tegra/xusb-tegra186.c 
>> b/drivers/phy/tegra/xusb-tegra186.c
>> index 5d64f69b39a9..104e2a8496b4 100644
>> --- a/drivers/phy/tegra/xusb-tegra186.c
>> +++ b/drivers/phy/tegra/xusb-tegra186.c
>> @@ -113,6 +113,117 @@
>>  #define  ID_OVERRIDE_FLOATING   ID_OVERRIDE(8)
>>  #define  ID_OVERRIDE_GROUNDED   ID_OVERRIDE(0)
>>  
>> +/* XUSB AO registers */
>> +#define XUSB_AO_USB_DEBOUNCE_DEL(0x4)
>> +#define   UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4)
>> +#define   UTMIP_LINE_DEB_CNT(x) ((x) & 0xf)
>> +
>> +#define XUSB_AO_UTMIP_TRIGGERS(x)   (0x40 + (x) * 4)
>> +#define   CLR_WALK_PTR  (1 << 0)
>> +#define   CAP_CFG   (1 << 1)
>> +#define   CLR_WAKE_ALARM(1 << 3)
>> +
>> +#define XUSB_AO_UHSIC_TRIGGERS(x)   (0x60 + (x) * 4)
>> +#define   HSIC_CLR_WALK_PTR (1 << 0)
>> +#define   HSIC_CLR_WAKE_ALARM   (1 << 3)
>> +#define   HSIC_CAP_CFG  (1 << 4)
>> +
>> +#define XUSB_AO_UTMIP_SAVED_STATE(x)(0x70 + (x) * 4)
>> +#define   SPEED(x)  ((x) & 0x3)
>> +#define UTMI_HS SPEED(0)
>> +#define UTMI_FS SPEED(1)
>> +#define UTMI_LS SPEED(2)
>> +#define UTMI_RSTSPEED(3)
>> +
>> +#define XUSB_AO_UHSIC_SAVED_STATE(x)(0x90 + (x) * 4)
>> +#define   MODE(x)   ((x) & 0x1)
>> +#define   MODE_HS   MODE(0)
>> +#define   MODE_RST  MODE(1)
>> +
>> +#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x)  (0xd0 + (x) * 4)
>> +#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x)  (0xf0 + (x) * 4)
>> +#define   FAKE_USBOP_VAL(1 << 0)
>> +#define   FAKE_USBON_VAL(1 << 1)
>> +#define   FAKE_USBOP_EN (1 << 2)
>> +#define   FAKE_USBON_EN (1 << 3)
>> +#define   FAKE_STROBE_VAL   (1 << 0)
>> +#define   FAKE_DATA_VAL (1 << 1)
>> +#define   FAKE_STROBE_EN(1 << 2)
>> +#define   FAKE_DATA_EN  (1 << 3)
>> +#define   WAKE_WALK_EN  (1 << 14)
>> +#define   MASTER_ENABLE (1 << 15)
>> +#define   LINEVAL_WALK_EN   (1 << 16)
>> +#define   WAKE_VAL(x)   (((x) & 0xf) << 17)
>> +#define WAKE_VAL_NONE   WAKE_VAL(12)
>> +#define WAKE_VAL_ANYWAKE_VAL(15)
>> +#define WAKE_VAL_DS10   WAKE_VAL(2)
>> +#define   LINE_WAKEUP_EN(1 << 21)
>> +#define   MASTER_CFG_SEL(1 << 22)
>> +
>> +#define XUSB_AO_UTMIP_SLEEPWALK(x)  (0x100 + (x) * 4)
>> +/* phase A */
>> +#define   USBOP_RPD_A   (1 << 0)
>> +#define   USBON_RPD_A   (1 << 1)
>> +#define   AP_A  (1 << 4)
>> +#define   AN_A  (1 << 5)
>> +#define   HIGHZ_A   (1 << 6)
>> +/* phase B */
>> +#define   USBOP_RPD_B   (1 << 8)
>> +#define   USBON_RPD_B   (1 << 9)
>> +#define   AP_B  (1 << 12)
>> 

Re: [PATCH v3 10/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2020-10-14 Thread JC Kuo
On 9/28/20 9:40 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:36PM +0800, JC Kuo wrote:
> [...]
>> diff --git a/drivers/phy/tegra/xusb-tegra210.c 
>> b/drivers/phy/tegra/xusb-tegra210.c
> [...]
> 
> Could we add function pointers to struct tegra_xusb_lane_ops for all of
> these? That would allow us to assign them once during probe and then we
> don't have to bother with these is_*() functions and multiplexing but
> instead just call ->enable_phy_wake() and ->disable_phy_wake() directly.
Yes, I will implement in this way. Thanks for the suggestion.

> 
>> +
>> +
> 
> There's an extra blank line here.
> 
I will remove it. Thanks.

>>  static struct tegra_xusb_pad *
>>  tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl,
>>  const struct tegra_xusb_pad_soc *soc,
>> @@ -2293,6 +3225,8 @@ tegra210_xusb_padctl_probe(struct device *dev,
>> const struct tegra_xusb_padctl_soc *soc)
>>  {
>>  struct tegra210_xusb_padctl *padctl;
>> +struct device_node *node, *np = dev->of_node;
> 
> We only need dev->of_node once, so I don't think we need to store it in
> a local variable. Just make this:
> 
>   struct device_node *np;
> 
>> +struct platform_device *pmc_dev;
> 
> I'd call this pdev, which is the canonical name for variables pointing
> to a platform device.
> 
>>  int err;
>>  
>>  padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
>> @@ -2306,6 +3240,23 @@ tegra210_xusb_padctl_probe(struct device *dev,
>>  if (err < 0)
>>  return ERR_PTR(err);
>>  
>> +node = of_parse_phandle(np, "nvidia,pmc", 0);
>> +if (!node) {
> 
> And make this:
> 
>   np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
>   if (!np) {
> 
>> +dev_info(dev, "nvidia,pmc property is missing\n");
> 
> It might be better for this to be a warning, to make it easier to catch.
> 
>> +goto no_pmc;
>> +}
>> +
>> +pmc_dev = of_find_device_by_node(node);
>> +if (!pmc_dev) {
>> +dev_info(dev, "pmc device is not available\n");
> 
> Same here. Also s/pmc/PMC/ in the message
> 
>> +goto no_pmc;
> 
> Maybe call the label "out", "done" or something similar. "no_pmc" makes
> it sound like it's meant for error cases, which makes it confusing when
> you fallthrough for the success case as well.
> 
I will amend accordingly. Thanks.

> Actually, in this case it might be easier to just return here instead of
> using a goto.
> 
>> +}
>> +
>> +padctl->regmap = dev_get_regmap(_dev->dev, "usb_sleepwalk");
>> +if (!padctl->regmap)
>> +dev_info(dev, "pmc regmap is not available.\n");
> 
> Do we perhaps want to defer probe here?
The return value of dev_get_regmap() doesn't tell if PMC driver is loaded.

I will add the following to for defer probe.
if (!device_is_bound(>dev))
return -EPROBE_DEFER;
> 
> Thierry
> 

Thanks for review.
JC


Re: [PATCH v3 09/15] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2020-10-13 Thread JC Kuo
I will add a dt-bindings commit for this change.

Thanks for review.
JC

On 9/28/20 9:18 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:35PM +0800, JC Kuo wrote:
>> PMC driver provides USB sleepwalk registers access to XUSB PADCTL
>> driver. This commit adds a "nvidia,pmc" property which points to
>> PMC node to XUSB PADCTL device node.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v3:
>>no change
>>
>>  arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
>> b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> index 829f786af133..67c90a0ea32e 100644
>> --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
>> @@ -1040,6 +1040,7 @@ padctl: padctl@7009f000 {
>>  reg = <0x0 0x7009f000 0x0 0x1000>;
>>  resets = <_car 142>;
>>  reset-names = "padctl";
>> +nvidia,pmc =  <_pmc>;
> 
> I hadn't noticed before but it looks like the DT bindings haven't been
> updated with this new property.
> 
> Thierry
> 


Re: [PATCH v3 08/15] soc/tegra: pmc: Provide usb sleepwalk register map

2020-10-13 Thread JC Kuo
I will amend commit accordingly and submit a new patch.

Thanks for review.
JC

On 9/28/20 9:17 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:34PM +0800, JC Kuo wrote:
>> This commit implements a register map which grants USB (UTMI and HSIC)
>> sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
>> is in PMC hardware block but USB PHY drivers have the best knowledge
>> of proper programming sequence. This approach prevents using custom
>> pmc APIs.
> 
> I don't think this final sentence is useful. The commit message should
> explain what you're doing, but there's no need to enumerate any other
> inferior solution you didn't choose to implement.
> 
> If you do want to keep it: s/pmc/PMC/.
> 
> While at it, perhaps replace "usb" by "USB" in the subject as well.
> 
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v3:
>>commit message improvement
>>drop regmap_reg() usage
>>rename 'reg' with 'offset'
>>rename 'val' with 'value'
>>drop '__force' when invokes devm_regmap_init()
>>print error code of devm_regmap_init()
>>move devm_regmap_init() a litter bit earlier
>>explicitly set '.has_usb_sleepwalk=false'
>>
>>  drivers/soc/tegra/pmc.c | 95 +
>>  1 file changed, 95 insertions(+)
>>
>> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
>> index d332e5d9abac..ff24891ce9ca 100644
>> --- a/drivers/soc/tegra/pmc.c
>> +++ b/drivers/soc/tegra/pmc.c
>> @@ -43,6 +43,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>  
>>  #include 
>>  #include 
>> @@ -102,6 +103,9 @@
>>  
>>  #define PMC_PWR_DET_VALUE   0xe4
>>  
>> +#define PMC_USB_DEBOUNCE_DEL0xec
>> +#define PMC_USB_AO  0xf0
>> +
>>  #define PMC_SCRATCH41   0x140
>>  
>>  #define PMC_WAKE2_MASK  0x160
>> @@ -133,6 +137,13 @@
>>  #define IO_DPD2_STATUS  0x1c4
>>  #define SEL_DPD_TIM 0x1c8
>>  
>> +#define PMC_UTMIP_UHSIC_TRIGGERS0x1ec
>> +#define PMC_UTMIP_UHSIC_SAVED_STATE 0x1f0
>> +
>> +#define PMC_UTMIP_TERM_PAD_CFG  0x1f8
>> +#define PMC_UTMIP_UHSIC_SLEEP_CFG   0x1fc
>> +#define PMC_UTMIP_UHSIC_FAKE0x218
>> +
>>  #define PMC_SCRATCH54   0x258
>>  #define  PMC_SCRATCH54_DATA_SHIFT   8
>>  #define  PMC_SCRATCH54_ADDR_SHIFT   0
>> @@ -145,8 +156,18 @@
>>  #define  PMC_SCRATCH55_CHECKSUM_SHIFT   16
>>  #define  PMC_SCRATCH55_I2CSLV1_SHIFT0
>>  
>> +#define  PMC_UTMIP_UHSIC_LINE_WAKEUP0x26c
>> +
>> +#define PMC_UTMIP_BIAS_MASTER_CNTRL 0x270
>> +#define PMC_UTMIP_MASTER_CONFIG 0x274
>> +#define PMC_UTMIP_UHSIC2_TRIGGERS   0x27c
>> +#define PMC_UTMIP_MASTER2_CONFIG0x29c
>> +
>>  #define GPU_RG_CNTRL0x2d4
>>  
>> +#define PMC_UTMIP_PAD_CFG0  0x4c0
>> +#define PMC_UTMIP_UHSIC_SLEEP_CFG1  0x4d0
>> +#define PMC_UTMIP_SLEEPWALK_P3  0x4e0
>>  /* Tegra186 and later */
>>  #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
>>  #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
>> @@ -334,6 +355,7 @@ struct tegra_pmc_soc {
>>  const struct pmc_clk_init_data *pmc_clks_data;
>>  unsigned int num_pmc_clks;
>>  bool has_blink_output;
>> +bool has_usb_sleepwalk;
>>  };
>>  
>>  static const char * const tegra186_reset_sources[] = {
>> @@ -2495,6 +2517,68 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
>> *pmc,
>>   err);
>>  }
>>  
>> +static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
>> +regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
>> +regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
>> +regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
>> +regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
>> PMC_UTMIP_UHSIC_LINE_WAKEUP),
>> +regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
>> +regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
>> +regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
>> +regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
>> +};
>> +
>> +static const struct regmap_access_table pmc_usb_sleepwalk_table = {
>> +.yes_ranges = pmc_usb_sleepwalk

Re: [PATCH v3 04/15] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2020-10-13 Thread JC Kuo
Asserting reset to a PLL when it's managed by hardware power sequencer would
break sequencer's state machine. Putting PLL in reset doesn't save some extra 
power.

Thanks for review.
JC

On 9/28/20 9:06 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:30PM +0800, JC Kuo wrote:
>> Once UPHY PLL hardware power sequencer is enabled, do not assert
>> reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
>> This commit removes reset_control_assert(pcie->rst) and
>> reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.
>>
>> Signed-off-by: JC Kuo 
>> ---
>> v3:
>>new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"
>>
>>  drivers/phy/tegra/xusb-tegra210.c | 2 --
>>  1 file changed, 2 deletions(-)
>>
>> diff --git a/drivers/phy/tegra/xusb-tegra210.c 
>> b/drivers/phy/tegra/xusb-tegra210.c
>> index f06e7bc7a51b..ef4bbcbed60b 100644
>> --- a/drivers/phy/tegra/xusb-tegra210.c
>> +++ b/drivers/phy/tegra/xusb-tegra210.c
>> @@ -504,7 +504,6 @@ static void tegra210_pex_uphy_disable(struct 
>> tegra_xusb_padctl *padctl)
>>  if (--pcie->enable > 0)
>>  goto unlock;
>>  
>> -reset_control_assert(pcie->rst);
>>  clk_disable_unprepare(pcie->pll);
>>  
>>  unlock:
>> @@ -746,7 +745,6 @@ static void tegra210_sata_uphy_disable(struct 
>> tegra_xusb_padctl *padctl)
>>  if (--sata->enable > 0)
>>  goto unlock;
>>  
>> -reset_control_assert(sata->rst);
>>  clk_disable_unprepare(sata->pll);
>>  
>>  unlock:
> 
> Does this mean that we can no longer reset these PLLs anymore? Is that
> safe? Would we ever need to reset them for recovery or similar? For
> power saving, is disabling the clock enough, or could we save some extra
> power by putting the PLLs into reset?
> 
> Thierry
> 


Re: [PATCH v3 00/15] Tegra XHCI controller ELPG support

2020-10-13 Thread JC Kuo
Yes, it's safe to apply "clk: tegra: Don't enable PLLE HW sequencer at init"
before the others have applied. Disabling PLLE hardware power sequencer will not
cause any functionality problem to XUSB/PCIE/SATA. The only thing changed is
PLLE won't be powered off by hardware when all clients are in low power state,
i.e., software has to explicitly power off PLLE.

Thanks for review.
JC

On 9/28/20 8:54 PM, Thierry Reding wrote:
> On Wed, Sep 09, 2020 at 04:10:26PM +0800, JC Kuo wrote:
>> Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
>> state for power saving when all of the connected USB devices are in
>> suspended state. This patch series includes clk, phy and pmc changes
>> that are required for properly place controller in ELPG and bring
>> controller out of ELPG.
>>
>> JC Kuo (15):
>>   clk: tegra: Add PLLE HW power sequencer control
>>   clk: tegra: Don't enable PLLE HW sequencer at init
> 
> Is it safe to apply this second patch before the others have applied?
> Since we now need to explicitly enable the HW sequencer, it won't be
> enabled before the corresponding patch does that. So applying patch 2
> before the others sounds like it would break existing users of the HW
> sequencer.
> 
> Thierry
> 


[PATCH v3 04/15] phy: tegra: xusb: tegra210: Do not reset UPHY PLL

2020-09-09 Thread JC Kuo
Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken.
This commit removes reset_control_assert(pcie->rst) and
reset_control_assert(sata->rst) from PEX/SATA UPHY disable procedure.

Signed-off-by: JC Kuo 
---
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index f06e7bc7a51b..ef4bbcbed60b 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -504,7 +504,6 @@ static void tegra210_pex_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--pcie->enable > 0)
goto unlock;
 
-   reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
 
 unlock:
@@ -746,7 +745,6 @@ static void tegra210_sata_uphy_disable(struct 
tegra_xusb_padctl *padctl)
if (--sata->enable > 0)
goto unlock;
 
-   reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
 
 unlock:
-- 
2.25.1



[PATCH v3 03/15] phy: tegra: xusb: Move usb3 port init for Tegra210

2020-09-09 Thread JC Kuo
The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy
.power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane
is assigned for XUSB super-speed.

Signed-off-by: JC Kuo 
---
v3:
   new, was a part of "phy: tegra: xusb: Rearrange UPHY init on Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 298 +-
 drivers/phy/tegra/xusb.c  |   2 +-
 drivers/phy/tegra/xusb.h  |   2 +
 3 files changed, 174 insertions(+), 128 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 66bd4613835b..f06e7bc7a51b 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
 }
 
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+   { 0, "pcie", 6 },
+   { 1, "pcie", 5 },
+   { 2, "pcie", 0 },
+   { 2, "pcie", 3 },
+   { 3, "pcie", 4 },
+   { 3, "sata", 0 },
+   { 0, NULL,   0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+   const struct tegra_xusb_lane_map *map;
+
+   for (map = tegra210_usb3_map; map->type; map++) {
+   if (map->index == lane->index &&
+   strcmp(map->type, lane->pad->soc->name) == 0) {
+   dev_dbg(lane->pad->padctl->dev, "lane = %s map to port 
= usb3-%d\n",
+   lane->pad->soc->lanes[lane->index].name, 
map->port);
+   return map->port;
+   }
+   }
+
+   return -EINVAL;
+}
+
 /* must be called under padctl->lock */
 static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
@@ -1599,6 +1625,128 @@ static const struct tegra_xusb_lane_soc 
tegra210_pcie_lanes[] = {
TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
 };
 
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+   int port;
+
+   if (!lane || !lane->pad || !lane->pad->padctl)
+   return NULL;
+
+   port = tegra210_usb3_lane_map(lane);
+   if (port < 0)
+   return NULL;
+
+   return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+   struct device *dev = >dev;
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+   unsigned int index;
+   u32 value;
+
+   if (!usb3) {
+   dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+   return -ENODEV;
+   }
+
+   index = usb3->base.index;
+
+   value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+   if (!usb3->internal)
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+   else
+   value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+
+   value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+   value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
+   padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+   value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+   value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
+  XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
+   value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
+XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
+   padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+
+   value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+   value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
+  XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
+   value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
+XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
+   padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+
+   padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
+
+   value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+   value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
+  XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT

[PATCH v3 05/15] phy: tegra: xusb: Rearrange UPHY init on Tegra210

2020-09-09 Thread JC Kuo
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:

1. PLLE hardware power sequencer has to be enabled only after both
   PEX UPHY PLL and SATA UPHY PLL are initialized.
   tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()

2. At cold boot and SC7 exit, the following bits must be cleared after
   PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
   a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
   b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
   c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN

   tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
   charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
   will be cleared by tegra210_aux_mux_lp0_clamp_disable().

Signed-off-by: JC Kuo 
---
v3:
   make separate changes
   use "unsigned int" instead "int" type for PHY index
   add blank line for better readability
   
 drivers/phy/tegra/xusb-tegra210.c | 203 +++---
 drivers/phy/tegra/xusb.h  |   4 +-
 2 files changed, 102 insertions(+), 105 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index ef4bbcbed60b..b3f065ef92e6 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
 
-   if (pcie->enable > 0) {
-   pcie->enable++;
+   if (pcie->enable)
return 0;
-   }
 
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
 
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 
tegra210_xusb_pll_hw_sequence_start();
 
-   pcie->enable++;
+skip_pll_init:
+   pcie->enable = true;
+
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
return 0;
 
@@ -495,33 +504,44 @@ static int tegra210_pex_uphy_enable(struct 
tegra_xusb_padctl *padctl)
 static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+   u32 value;
+   unsigned int i;
 
-   mutex_lock(>lock);
+   if (WARN_ON(!pcie->enable))
+   return;
 
-   if (WARN_ON(pcie->enable == 0))
-   goto unlock;
+   pcie->enable = false;
 
-   if (--pcie->enable > 0)
-   goto unlock;
+   for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+   value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+   value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+   padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+   }
 
clk_disable_unprepare(pcie->pll);
-
-unlock:
-   mutex_unlock(>lock);
 }
 
 /* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool 
usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
 {
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+   struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+   unsigned int i;
int err;
+   bool usb;
 
-   if (sata->enable > 0) {
-   sata->enable++;
+   if (sata->enable)
+   return 0;
+
+   if (IS_ERR(lane))
return 0;
-   }
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   goto skip_pll_init;
+
+   usb = tegra_xusb_lane_check(lane, "usb3-ss");
 
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -722,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct 
tegra_xusb_padctl *padctl, bool usb)
 
tegra210_sata_pll_hw_sequence_start();
 
-   sata->enable++;
+skip_pll_init:
+   sata->enable = true;
+
+   for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+   value = padctl_

[PATCH v3 06/15] phy: tegra: xusb: Add Tegra210 lane_iddq operation

2020-09-09 Thread JC Kuo
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 
---
v3:
   add 'misc_ctl2' data member to UPHY lane for carrying MISC_PAD_PX_CTL2 offset
   tegra210_uphy_lane_iddq_[enable/disable]() to access 'misc_ctl2' data member
   
 drivers/phy/tegra/xusb-tegra210.c | 82 ---
 drivers/phy/tegra/xusb.c  |  6 +++
 drivers/phy/tegra/xusb.h  |  6 +++
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index b3f065ef92e6..ff5dbc4818b0 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
@@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad 
= {
.ops = _hsic_ops,
 };
 
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   u32 value;
+
+   value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+   value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+   value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+   padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc)
\
+   {   \
+   .name = _name,  \
+   .offset = _offset,  \
+   .shift = _shift,\
+   .mask = _mask,  \
+   .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions),  \
+   .funcs = tegra210_##_type##_functions,  \
+   .regs.misc_ctl2 = _misc,\
+   }
+
 static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1648,13 +1710,13 @@ static const char *tegra210_pcie_functions[] = {
 };
 
 static const struct tegra_xusb_lane_so

[PATCH v3 10/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra210

2020-09-09 Thread JC Kuo
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk
routines. Sleepwalk logic is in PMC (always-on) hardware block.
PMC driver provides managed access to the sleepwalk registers
via regmap framework.

Signed-off-by: JC Kuo 
---
v3:
   rename 'pmc_reg" with 'regmap' and move to the top of 'struct 
tegra210_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'
   rename tegra_pmc_*() with tegra210_pmc_*()
   remove VBUS ON/OFF control change

 drivers/phy/tegra/xusb-tegra210.c | 1023 +
 1 file changed, 1023 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index ff5dbc4818b0..36949039e529 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -16,6 +16,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 
@@ -52,6 +54,20 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define   USB2_PORT_WAKE_INTERRUPT_ENABLE(x)  BIT((x))
+#define   USB2_PORT_WAKEUP_EVENT(x)   BIT((x) + 7)
+#define   SS_PORT_WAKE_INTERRUPT_ENABLE(x)BIT((x) + 14)
+#define   SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define   USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define   USB2_HSIC_PORT_WAKEUP_EVENT(x)  BIT((x) + 30)
+#define   ALL_WAKE_EVENTS ( \
+   USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+   USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+   SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+   SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+   USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
 #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define   RPD_CTRL(x)  (((x) & 0x1f) << 26)
+#define   RPD_CTRL_VALUE(x)(((x) >> 26) & 0x1f)
 
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define   TCTRL_VALUE(x)(((x) & 0x3f) >> 0)
+#define   PCTRL_VALUE(x)(((x) >> 6) & 0x3f)
 
 #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
 #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -251,16 +271,161 @@
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
+/* 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) & 0x) << 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,

[PATCH v3 08/15] soc/tegra: pmc: Provide usb sleepwalk register map

2020-09-09 Thread JC Kuo
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence. This approach prevents using custom
pmc APIs.

Signed-off-by: JC Kuo 
---
v3:
   commit message improvement
   drop regmap_reg() usage
   rename 'reg' with 'offset'
   rename 'val' with 'value'
   drop '__force' when invokes devm_regmap_init()
   print error code of devm_regmap_init()
   move devm_regmap_init() a litter bit earlier
   explicitly set '.has_usb_sleepwalk=false'
   
 drivers/soc/tegra/pmc.c | 95 +
 1 file changed, 95 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index d332e5d9abac..ff24891ce9ca 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -43,6 +43,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -102,6 +103,9 @@
 
 #define PMC_PWR_DET_VALUE  0xe4
 
+#define PMC_USB_DEBOUNCE_DEL   0xec
+#define PMC_USB_AO 0xf0
+
 #define PMC_SCRATCH41  0x140
 
 #define PMC_WAKE2_MASK 0x160
@@ -133,6 +137,13 @@
 #define IO_DPD2_STATUS 0x1c4
 #define SEL_DPD_TIM0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS   0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG 0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG  0x1fc
+#define PMC_UTMIP_UHSIC_FAKE   0x218
+
 #define PMC_SCRATCH54  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT  8
 #define  PMC_SCRATCH54_ADDR_SHIFT  0
@@ -145,8 +156,18 @@
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL0x270
+#define PMC_UTMIP_MASTER_CONFIG0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS  0x27c
+#define PMC_UTMIP_MASTER2_CONFIG   0x29c
+
 #define GPU_RG_CNTRL   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0 0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3 0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
bool has_blink_output;
+   bool has_usb_sleepwalk;
 };
 
 static const char * const tegra186_reset_sources[] = {
@@ -2495,6 +2517,68 @@ static void tegra_pmc_clock_register(struct tegra_pmc 
*pmc,
 err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+   regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+   regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+   regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+   regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, 
PMC_UTMIP_UHSIC_LINE_WAKEUP),
+   regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+   regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+   regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+   regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+   .yes_ranges = pmc_usb_sleepwalk_ranges,
+   .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned 
int *value)
+{
+   struct tegra_pmc *pmc = context;
+
+   *value = tegra_pmc_readl(pmc, offset);
+   return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, 
unsigned int value)
+{
+   struct tegra_pmc *pmc = context;
+
+   tegra_pmc_writel(pmc, value, offset);
+   return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+   .name = "usb_sleepwalk",
+   .reg_bits = 32,
+   .val_bits = 32,
+   .reg_stride = 4,
+   .fast_io = true,
+   .rd_table = _usb_sleepwalk_table,
+   .wr_table = _usb_sleepwalk_table,
+   .reg_read = tegra_pmc_regmap_readl,
+   .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+   struct regmap *regmap;
+   int err;
+
+   if (pmc->soc->has_usb_sleepwalk) {
+   regmap = devm_regmap_init(pmc->dev, NULL, (void *) pmc,
+ _sleepwalk_regmap_config);
+   if (IS_ERR(regmap)) {
+   err = PTR_ERR(regmap);
+   dev_err(pmc->dev, "failed to allocate register map 
(%d)\n", err);
+   return err;
+   }
+   }
+
+   retur

[PATCH v3 12/15] phy: tegra: xusb: Add wake/sleepwalk for Tegra186

2020-09-09 Thread JC Kuo
This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and
sleepwalk operations.

Signed-off-by: JC Kuo 
---
v3:
   move 'ao_regs' to the top of 'struct tegra186_xusb_padctl'
   change return data of .phy_remote_wake_detected() to 'bool'
   change input parameter of .phy_remote_wake_detected() to 'struct phy*'
   remove unnecessary 'else'
   rename 'val' with 'value'

 drivers/phy/tegra/xusb-tegra186.c | 626 ++
 1 file changed, 626 insertions(+)

diff --git a/drivers/phy/tegra/xusb-tegra186.c 
b/drivers/phy/tegra/xusb-tegra186.c
index 5d64f69b39a9..104e2a8496b4 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -113,6 +113,117 @@
 #define  ID_OVERRIDE_FLOATING  ID_OVERRIDE(8)
 #define  ID_OVERRIDE_GROUNDED  ID_OVERRIDE(0)
 
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL   (0x4)
+#define   UHSIC_LINE_DEB_CNT(x)(((x) & 0xf) << 4)
+#define   UTMIP_LINE_DEB_CNT(x)((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x)  (0x40 + (x) * 4)
+#define   CLR_WALK_PTR (1 << 0)
+#define   CAP_CFG  (1 << 1)
+#define   CLR_WAKE_ALARM   (1 << 3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x)  (0x60 + (x) * 4)
+#define   HSIC_CLR_WALK_PTR(1 << 0)
+#define   HSIC_CLR_WAKE_ALARM  (1 << 3)
+#define   HSIC_CAP_CFG (1 << 4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x)   (0x70 + (x) * 4)
+#define   SPEED(x) ((x) & 0x3)
+#define UTMI_HSSPEED(0)
+#define UTMI_FSSPEED(1)
+#define UTMI_LSSPEED(2)
+#define UTMI_RST   SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x)   (0x90 + (x) * 4)
+#define   MODE(x)  ((x) & 0x1)
+#define   MODE_HS  MODE(0)
+#define   MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define   FAKE_USBOP_VAL   (1 << 0)
+#define   FAKE_USBON_VAL   (1 << 1)
+#define   FAKE_USBOP_EN(1 << 2)
+#define   FAKE_USBON_EN(1 << 3)
+#define   FAKE_STROBE_VAL  (1 << 0)
+#define   FAKE_DATA_VAL(1 << 1)
+#define   FAKE_STROBE_EN   (1 << 2)
+#define   FAKE_DATA_EN (1 << 3)
+#define   WAKE_WALK_EN (1 << 14)
+#define   MASTER_ENABLE(1 << 15)
+#define   LINEVAL_WALK_EN  (1 << 16)
+#define   WAKE_VAL(x)  (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE  WAKE_VAL(12)
+#define WAKE_VAL_ANY   WAKE_VAL(15)
+#define WAKE_VAL_DS10  WAKE_VAL(2)
+#define   LINE_WAKEUP_EN   (1 << 21)
+#define   MASTER_CFG_SEL   (1 << 22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define   USBOP_RPD_A  (1 << 0)
+#define   USBON_RPD_A  (1 << 1)
+#define   AP_A (1 << 4)
+#define   AN_A (1 << 5)
+#define   HIGHZ_A  (1 << 6)
+/* phase B */
+#define   USBOP_RPD_B  (1 << 8)
+#define   USBON_RPD_B  (1 << 9)
+#define   AP_B (1 << 12)
+#define   AN_B (1 << 13)
+#define   HIGHZ_B  (1 << 14)
+/* phase C */
+#define   USBOP_RPD_C  (1 << 16)
+#define   USBON_RPD_C  (1 << 17)
+#define   AP_C (1 << 20)
+#define   AN_C (1 << 21)
+#define   HIGHZ_C  (1 << 22)
+/* phase D */
+#define   USBOP_RPD_D  (1 << 24)
+#define   USBON_RPD_D  (1 << 25)
+#define   AP_D (1 << 28)
+#define   AN_D (1 << 29)
+#define   HIGHZ_D  (1 << 30)
+
+#define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4)
+/* phase A */
+#define   RPD_STROBE_A (1 <&

[PATCH v3 14/15] usb: host: xhci-tegra: Unlink power domain devices

2020-09-09 Thread JC Kuo
This commit unlinks xhci-tegra platform device with ss/host power
domain devices. Reasons for this change is - at elpg entry, PHY
sleepwalk and wake configuration need to be done before powering
down ss/host partitions, and PHY need be powered off after powering
down ss/host partitions. Sequence looks like roughly below:

  tegra_xusb_enter_elpg() -> xhci_suspend()
  -> enable PHY sleepwalk and wake if needed
  -> power down ss/host partitions
  -> power down PHY

If ss/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.

This commit introduces:
  1. tegra_xusb_unpowergate_partitions() to power up ss and host
 partitions together. If ss/host power domain devices are
 available, it invokes pm_runtime_get_sync() to request power
 driver to power up partitions; If power domain devices are not
 available, tegra_powergate_sequence_power_up() will be used to
 power up partitions.

  2. tegra_xusb_powergate_partitions() to power down ss and host
 partitions together. If ss/host power domain devices are
 available, it invokes pm_runtime_put_sync() to request power
 driver to power down partitions; If power domain devices are not
 available, tegra_powergate_power_off() will be used to power down
 partitions.

Signed-off-by: JC Kuo 
---
v3:
   'use_genpd' base on the results of tegra_xusb_powerdomain_init()

 drivers/usb/host/xhci-tegra.c | 199 ++
 1 file changed, 108 insertions(+), 91 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 934be1686352..aabff8ee0bb3 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -249,8 +249,7 @@ struct tegra_xusb {
 
struct device *genpd_dev_host;
struct device *genpd_dev_ss;
-   struct device_link *genpd_dl_host;
-   struct device_link *genpd_dl_ss;
+   bool use_genpd;
 
struct phy **phys;
unsigned int num_phys;
@@ -814,36 +813,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-
-   regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-   tegra_xusb_clk_disable(tegra);
-
return 0;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   struct tegra_xusb *tegra = dev_get_drvdata(dev);
-   int err;
-
-   err = tegra_xusb_clk_enable(tegra);
-   if (err) {
-   dev_err(dev, "failed to enable clocks: %d\n", err);
-   return err;
-   }
-
-   err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
-   if (err) {
-   dev_err(dev, "failed to enable regulators: %d\n", err);
-   goto disable_clk;
-   }
-
return 0;
-
-disable_clk:
-   tegra_xusb_clk_disable(tegra);
-   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1019,10 +994,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb 
*tegra)
 static void tegra_xusb_powerdomain_remove(struct device *dev,
  struct tegra_xusb *tegra)
 {
-   if (tegra->genpd_dl_ss)
-   device_link_del(tegra->genpd_dl_ss);
-   if (tegra->genpd_dl_host)
-   device_link_del(tegra->genpd_dl_host);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1019,84 @@ static int tegra_xusb_powerdomain_init(struct device 
*dev,
return err;
}
 
-   tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
-  DL_FLAG_PM_RUNTIME |
-  DL_FLAG_STATELESS);
-   if (!tegra->genpd_dl_host) {
-   dev_err(dev, "adding host device link failed!\n");
-   return -ENODEV;
+   tegra->use_genpd = true;
+
+   return 0;
+}
+
+static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
+{
+   struct device *dev = tegra->dev;
+   int rc;
+
+   if (tegra->use_genpd) {
+   rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
+   if (rc < 0) {
+   dev_err(dev, "failed to enable XUSB SS partition\n");
+   return rc;
+   }
+
+   rc = pm_runtime_get_sync(tegra->genpd_dev_host);
+   if (rc < 0) {
+   dev_err(dev, "failed to enable XUSB Host partition\n");
+   pm_runtime_put_sync(tegra->genpd_dev_ss);
+   

[PATCH v3 11/15] phy: tegra: xusb: Tegra210 host mode VBUS control

2020-09-09 Thread JC Kuo
To support XUSB host controller ELPG, this commit moves VBUS control
.phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit().
When XUSB host controller enters ELPG, host driver invokes
.phy_power_off(), VBUS should remain ON so that USB devices will not
disconnect. VBUS can be turned OFF when host driver invokes
.phy_exit() which indicates disabling a USB port.

Signed-off-by: JC Kuo 
---
v3:
   new, was a part of "phy: tegra: xusb: Add wake/sleepwalk for Tegra210"

 drivers/phy/tegra/xusb-tegra210.c | 52 ---
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 36949039e529..0cc6b6fd8474 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1834,8 +1834,25 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   unsigned int index = lane->index;
+   struct tegra_xusb_usb2_port *port;
+   int err;
u32 value;
 
+   port = tegra_xusb_find_usb2_port(padctl, index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_enable(port->supply);
+   if (err)
+   return err;
+   }
+
+   mutex_lock(>lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
   XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -1843,11 +1860,30 @@ static int tegra210_usb2_phy_init(struct phy *phy)
 XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
 
+   mutex_unlock(>lock);
+
return 0;
 }
 
 static int tegra210_usb2_phy_exit(struct phy *phy)
 {
+   struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+   struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+   struct tegra_xusb_usb2_port *port;
+   int err;
+
+   port = tegra_xusb_find_usb2_port(padctl, lane->index);
+   if (!port) {
+   dev_err(>dev, "no port found for USB2 lane %u\n", 
lane->index);
+   return -ENODEV;
+   }
+
+   if (port->supply && port->mode == USB_DR_MODE_HOST) {
+   err = regulator_disable(port->supply);
+   if (err)
+   return err;
+   }
+
return 0;
 }
 
@@ -1968,6 +2004,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
priv = to_tegra210_xusb_padctl(padctl);
 
+   mutex_lock(>lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -2061,14 +2099,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
  XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-   if (port->supply && port->mode == USB_DR_MODE_HOST) {
-   err = regulator_enable(port->supply);
-   if (err)
-   return err;
-   }
-
-   mutex_lock(>lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(>lock);
@@ -2077,7 +2107,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
err = clk_prepare_enable(pad->clk);
if (err)
-   goto disable_regulator;
+   goto out;
 
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -2109,8 +2139,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
return 0;
 
-disable_regulator:
-   regulator_disable(port->supply);
+out:
mutex_unlock(>lock);
return err;
 }
@@ -2169,7 +2198,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
 
 out:
-   regulator_disable(port->supply);
mutex_unlock(>lock);
return 0;
 }
-- 
2.25.1



[PATCH v3 15/15] xhci: tegra: Enable ELPG for runtime/system PM

2020-09-09 Thread JC Kuo
This commit implements the complete programming sequence for ELPG
entry and exit.

 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).

 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.

At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.

Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.

The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.

Signed-off-by: JC Kuo 
---
v3:
   use 'unsigned int' for PHY index
   remove unnecessary 'else'
   drop IRQF_TRIGGER_HIGH when invokes devm_request_threaded_irq()
   
 drivers/usb/host/xhci-tegra.c | 389 +++---
 1 file changed, 360 insertions(+), 29 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index aabff8ee0bb3..ba3f40e78171 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -15,9 +15,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -224,6 +226,7 @@ struct tegra_xusb {
 
int xhci_irq;
int mbox_irq;
+   int padctl_irq;
 
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -269,10 +272,13 @@ struct tegra_xusb {
dma_addr_t phys;
} fw;
 
+   bool suspended;
struct tegra_xusb_context context;
 };
 
 static struct hc_driver __read_mostly tegra_xhci_hc_driver;
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime);
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime);
 
 static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset)
 {
@@ -658,6 +664,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
mutex_lock(>lock);
 
+   if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
+   goto out;
+
value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(, value);
 
@@ -671,6 +680,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
*data)
 
tegra_xusb_mbox_handle(tegra, );
 
+out:
mutex_unlock(>lock);
return IRQ_HANDLED;
 }
@@ -813,12 +823,27 @@ static void tegra_xusb_phy_disable(struct tegra_xusb 
*tegra)
 
 static int tegra_xusb_runtime_suspend(struct device *dev)
 {
-   return 0;
+   struct tegra_xusb *tegra = dev_get_drvdata(dev);
+   int ret;
+
+   synchronize_irq(tegra->mbox_irq);
+   mutex_lock(>lock);
+   ret = tegra_xusb_enter_elpg(tegra, true);
+   mutex_unlock(>lock);
+
+   return ret;
 }
 
 static int tegra_xusb_runtime_resume(struct device *dev)
 {
-   return 0;
+   struct tegra_xusb *tegra = dev_get_drvdata(dev);
+   int err;
+
+   mutex_lock(>lock);
+   err = tegra_xusb_exit_elpg(tegra, true);
+   mutex_unlock(>lock);
+
+   return err;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -1118,6 +1143,24 @@ static int __tegra_xusb_enable_firmware_messages(struct 
tegra_xusb *tegra)
return err;
 }
 
+static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data)
+{
+   struct tegra_xusb *tegra = data;
+
+   mutex_lock(>lock);
+
+   if (tegra->suspended) {
+   mutex_unlock(>lock);
+   return IRQ_HANDLED;
+   }
+
+   mutex_unlock(>lock);
+
+   pm_runtime_resume(tegra->dev);
+
+   return IRQ_HANDLED;
+}
+
 static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
 {
int err;
@@ -1241,6 +1284,50 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
 }
 
+static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   return (tegra->usbphy[index] != NULL);
+}
+
+static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index)
+{
+   struct tegra_xusb_padctl *padctl = tegra->padctl;
+   unsigned int i;
+   int port;
+
+   for (i = 0; i < tegra->num_usb_phys; i++) {
+   if (is_usb2_otg_phy(tegra, i)) {
+   port = tegra_xusb_padctl_get_usb3_companion(padctl, i);
+   if ((port >= 0) && (index == (unsigned int)port))
+   return true

[PATCH v3 13/15] arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq

2020-09-09 Thread JC Kuo
This commit adds "interrupts" property to Tegra210/Tegra186/Tegra194
XUSB PADCTL node. XUSB PADCTL interrupt will be raised when USB wake
event happens. This is required for supporting XUSB host controller
ELPG.

Signed-off-by: JC Kuo 
---
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 3 files changed, 3 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index 34d249d85da7..454a857e5e56 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -466,6 +466,7 @@ padctl: padctl@352 {
reg = <0x0 0x0352 0x0 0x1000>,
  <0x0 0x0354 0x0 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA186_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index 48160f48003a..2b5216a34c06 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -561,6 +561,7 @@ xusb_padctl: padctl@352 {
reg = <0x0352 0x1000>,
  <0x0354 0x1000>;
reg-names = "padctl", "ao";
+   interrupts = ;
 
resets = < TEGRA194_RESET_XUSB_PADCTL>;
reset-names = "padctl";
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 67c90a0ea32e..2ba526779ee5 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1041,6 +1041,7 @@ padctl: padctl@7009f000 {
resets = <_car 142>;
reset-names = "padctl";
nvidia,pmc =  <_pmc>;
+   interrupts = ;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v3 09/15] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop

2020-09-09 Thread JC Kuo
PMC driver provides USB sleepwalk registers access to XUSB PADCTL
driver. This commit adds a "nvidia,pmc" property which points to
PMC node to XUSB PADCTL device node.

Signed-off-by: JC Kuo 
---
v3:
   no change

 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi 
b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 829f786af133..67c90a0ea32e 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1040,6 +1040,7 @@ padctl: padctl@7009f000 {
reg = <0x0 0x7009f000 0x0 0x1000>;
resets = <_car 142>;
reset-names = "padctl";
+   nvidia,pmc =  <_pmc>;
 
status = "disabled";
 
-- 
2.25.1



[PATCH v3 00/15] Tegra XHCI controller ELPG support

2020-09-09 Thread JC Kuo
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.

JC Kuo (15):
  clk: tegra: Add PLLE HW power sequencer control
  clk: tegra: Don't enable PLLE HW sequencer at init
  phy: tegra: xusb: Move usb3 port init for Tegra210
  phy: tegra: xusb: tegra210: Do not reset UPHY PLL
  phy: tegra: xusb: Rearrange UPHY init on Tegra210
  phy: tegra: xusb: Add Tegra210 lane_iddq operation
  phy: tegra: xusb: Add sleepwalk and suspend/resume
  soc/tegra: pmc: Provide usb sleepwalk register map
  arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
  phy: tegra: xusb: Add wake/sleepwalk for Tegra210
  phy: tegra: xusb: Tegra210 host mode VBUS control
  phy: tegra: xusb: Add wake/sleepwalk for Tegra186
  arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq
  usb: host: xhci-tegra: Unlink power domain devices
  xhci: tegra: Enable ELPG for runtime/system PM

 arch/arm64/boot/dts/nvidia/tegra186.dtsi |1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi |1 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi |2 +
 drivers/clk/tegra/clk-pll.c  |   12 -
 drivers/clk/tegra/clk-tegra210.c |   51 +
 drivers/phy/tegra/xusb-tegra186.c|  626 +++
 drivers/phy/tegra/xusb-tegra210.c| 1968 +-
 drivers/phy/tegra/xusb.c |   81 +-
 drivers/phy/tegra/xusb.h |   21 +-
 drivers/soc/tegra/pmc.c  |   95 ++
 drivers/usb/host/xhci-tegra.c|  572 +--
 include/linux/clk/tegra.h|2 +
 include/linux/phy/tegra/xusb.h   |8 +
 13 files changed, 2907 insertions(+), 533 deletions(-)

-- 
2.25.1



[PATCH v3 02/15] clk: tegra: Don't enable PLLE HW sequencer at init

2020-09-09 Thread JC Kuo
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.

Signed-off-by: JC Kuo 
---
v3:
   no change

 drivers/clk/tegra/clk-pll.c | 12 
 1 file changed, 12 deletions(-)

diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c5cc0a2dac6f..0193cebe8c5a 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw)
pll_writel(val, PLLE_SS_CTRL, pll);
udelay(1);
 
-   val = pll_readl_misc(pll);
-   val &= ~PLLE_MISC_IDDQ_SW_CTRL;
-   pll_writel_misc(val, pll);
-
-   val = pll_readl(pll->params->aux_reg, pll);
-   val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
-   val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
-   pll_writel(val, pll->params->aux_reg, pll);
-   udelay(1);
-   val |= PLLE_AUX_SEQ_ENABLE;
-   pll_writel(val, pll->params->aux_reg, pll);
-
 out:
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
-- 
2.25.1



[PATCH v3 07/15] phy: tegra: xusb: Add sleepwalk and suspend/resume

2020-09-09 Thread JC Kuo
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.

Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
and can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB UPHY individually.

  - tegra_xusb_padctl_enable_phy_sleepwalk()
  - tegra_xusb_padctl_disable_phy_sleepwalk()

Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB PHY individually.

  - tegra_xusb_padctl_enable_phy_wake()
  - tegra_xusb_padctl_disable_phy_wake()
  - tegra_xusb_padctl_remote_wake_detected()

This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
  - tegra_xusb_padctl_suspend_noirq()
  - tegra_xusb_padctl_resume_noirq()

Signed-off-by: JC Kuo 
---
v3:
   commit message improvement, no change in code

 drivers/phy/tegra/xusb.c   | 73 ++
 drivers/phy/tegra/xusb.h   |  9 +
 include/linux/phy/tegra/xusb.h |  8 
 3 files changed, 90 insertions(+)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index b92478a350df..bc700e6a5519 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1274,10 +1274,36 @@ static int tegra_xusb_padctl_remove(struct 
platform_device *pdev)
return err;
 }
 
+static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+   return padctl->soc->ops->suspend_noirq(padctl);
+
+   return 0;
+}
+
+static int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+   struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+   if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+   return padctl->soc->ops->resume_noirq(padctl);
+
+   return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
 static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+   .pm = _xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1344,6 +1370,53 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy,
+  enum usb_device_speed speed)
+{
+   if (padctl->soc->ops->enable_phy_sleepwalk)
+   return padctl->soc->ops->enable_phy_sleepwalk(padctl, phy,
+ speed);
+
+   return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   if (padctl->soc->ops->disable_phy_sleepwalk)
+   return padctl->soc->ops->disable_phy_sleepwalk(padctl, phy);
+
+   return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct 
phy *phy)
+{
+   if (padctl->soc->ops->enable_phy_wake)
+   return padctl->soc->ops->enable_phy_wake(padctl, phy);
+
+   return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   if (padctl->soc->ops->disable_phy_wake)
+   return padctl->soc->ops->disable_phy_wake(padctl, phy);
+
+   return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, 
struct phy *phy)
+{
+   if (padctl->soc->ops->remote_wake_detected)
+   return padctl->soc->ops->remote_wake_detected(phy);
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected);
+
 int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
   unsigned int port, bool enable)
 {
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 946

[PATCH v3 01/15] clk: tegra: Add PLLE HW power sequencer control

2020-09-09 Thread JC Kuo
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.

XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.

PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.

This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
   PLLE hardware sequencer at proper time.

2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
   check whether PLLE hardware sequencer has been enabled or not.

Signed-off-by: JC Kuo 
---
v3:
   rename 'val' with 'value

 drivers/clk/tegra/clk-tegra210.c | 51 
 include/linux/clk/tegra.h|  2 ++
 2 files changed, 53 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 68cbb98af567..a91bf9b9be7a 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLRE_BASE_DEFAULT_MASK0x1c00
 #define PLLRE_MISC0_WRITE_MASK 0x67ff
 
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET   (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE(1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL  (1 << 4)
+#define PLLE_AUX_SS_SWCTL  (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE(1 << 24)
+
 /* PLLX */
 #define PLLX_USE_DYN_RAMP  1
 #define PLLX_BASE_LOCK (1 << 27)
@@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = {
 #define PLLU_MISC0_WRITE_MASK  0xbfff
 #define PLLU_MISC1_WRITE_MASK  0x0007
 
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+   u32 value;
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   if (value & PLLE_AUX_SEQ_ENABLE)
+   return true;
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+   u32 value;
+
+   if (tegra210_plle_hw_sequence_is_enabled())
+   return 0;
+
+   /* skip if PLLE is not enabled yet */
+   value = readl_relaxed(clk_base + PLLE_MISC0);
+   if (!(value & PLLE_MISC_LOCK))
+   return -EIO;
+
+   value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+   writel_relaxed(value, clk_base + PLLE_MISC0);
+
+   value = readl_relaxed(clk_base + PLLE_AUX);
+   value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+   value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   value |= PLLE_AUX_SEQ_ENABLE;
+   writel_relaxed(value, clk_base + PLLE_AUX);
+
+   fence_udelay(1, clk_base);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
 void tegra210_xusb_pll_hw_control_enable(void)
 {
u32 val;
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index 3f01d43f0598..7a6b29ca3040 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void)
 }
 #endif
 
+extern int tegra210_plle_hw_sequence_start(void);
+extern bool tegra210_plle_hw_sequence_is_enabled(void);
 extern void tegra210_xusb_pll_hw_control_enable(void);
 extern void tegra210_xusb_pll_hw_sequence_start(void);
 extern void tegra210_sata_pll_hw_control_enable(void);
-- 
2.25.1



  1   2   >