]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
phy: tegra: xusb: Add Tegra186 support
authorJC Kuo <jckuo@nvidia.com>
Thu, 21 Feb 2019 15:46:34 +0000 (16:46 +0100)
committerKishon Vijay Abraham I <kishon@ti.com>
Wed, 17 Apr 2019 08:42:47 +0000 (14:12 +0530)
Add support for the XUSB pad controller found on Tegra186 SoCs. It is
mostly similar to the same IP found on earlier chips, but the number of
pads exposed differs, as do the programming sequences.

Note that the DVDD_PEX, DVDD_PEX_PLL, HVDD_PEX and HVDD_PEX_PLL power
supplies of the XUSB pad controller require strict power sequencing and
are therefore controlled by the PMIC on Tegra186.

Signed-off-by: JC Kuo <jckuo@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
[dan.carpenter@oracle.com: Fix testing the wrong variable in probe()]
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
[yuehaibing@huawei.com: Make two functions static to fix sparse warning]
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
MAINTAINERS
drivers/phy/tegra/Makefile
drivers/phy/tegra/xusb-tegra186.c [new file with mode: 0644]
drivers/phy/tegra/xusb.c
drivers/phy/tegra/xusb.h

index e17ebf70b5480ecc232ce1f62aedf95a03b5f403..1d308fd7afaa58512a3b2826b513f6c8ace8d8b3 100644 (file)
@@ -15304,6 +15304,11 @@ M:     Laxman Dewangan <ldewangan@nvidia.com>
 S:     Supported
 F:     drivers/spi/spi-tegra*
 
+TEGRA XUSB PADCTL DRIVER
+M:     JC Kuo <jckuo@nvidia.com>
+S:     Supported
+F:     drivers/phy/tegra/xusb*
+
 TEHUTI ETHERNET DRIVER
 M:     Andy Gospodarek <andy@greyhouse.net>
 L:     netdev@vger.kernel.org
index 898589238fd94c7cae4a92bca9c6f20b97f9bf63..a93cd9a499b2bd4fd3e8e4f12e99290fc6c00022 100644 (file)
@@ -4,3 +4,4 @@ phy-tegra-xusb-y += xusb.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
new file mode 100644 (file)
index 0000000..6f3afaf
--- /dev/null
@@ -0,0 +1,899 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2019, NVIDIA CORPORATION.  All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "xusb.h"
+
+/* FUSE USB_CALIB registers */
+#define HS_CURR_LEVEL_PADX_SHIFT(x)    ((x) ? (11 + (x - 1) * 6) : 0)
+#define HS_CURR_LEVEL_PAD_MASK         0x3f
+#define HS_TERM_RANGE_ADJ_SHIFT                7
+#define HS_TERM_RANGE_ADJ_MASK         0xf
+#define HS_SQUELCH_SHIFT               29
+#define HS_SQUELCH_MASK                        0x7
+
+#define RPD_CTRL_SHIFT                 0
+#define RPD_CTRL_MASK                  0x1f
+
+/* XUSB PADCTL registers */
+#define XUSB_PADCTL_USB2_PAD_MUX       0x4
+#define  USB2_PORT_SHIFT(x)            ((x) * 2)
+#define  USB2_PORT_MASK                        0x3
+#define   PORT_XUSB                    1
+#define  HSIC_PORT_SHIFT(x)            ((x) + 20)
+#define  HSIC_PORT_MASK                        0x1
+#define   PORT_HSIC                    0
+
+#define XUSB_PADCTL_USB2_PORT_CAP      0x8
+#define XUSB_PADCTL_SS_PORT_CAP                0xc
+#define  PORTX_CAP_SHIFT(x)            ((x) * 4)
+#define  PORT_CAP_MASK                 0x3
+#define   PORT_CAP_DISABLED            0x0
+#define   PORT_CAP_HOST                        0x1
+#define   PORT_CAP_DEVICE              0x2
+#define   PORT_CAP_OTG                 0x3
+
+#define XUSB_PADCTL_ELPG_PROGRAM               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) | SS_PORT_WAKEUP_EVENT(0) |           \
+       SS_PORT_WAKEUP_EVENT(1) | SS_PORT_WAKEUP_EVENT(2) |             \
+       USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
+#define XUSB_PADCTL_ELPG_PROGRAM_1             0x24
+#define  SSPX_ELPG_CLAMP_EN(x)                 BIT(0 + (x) * 3)
+#define  SSPX_ELPG_CLAMP_EN_EARLY(x)           BIT(1 + (x) * 3)
+#define  SSPX_ELPG_VCORE_DOWN(x)               BIT(2 + (x) * 3)
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x)      (0x88 + (x) * 0x40)
+#define  HS_CURR_LEVEL(x)                      ((x) & 0x3f)
+#define  TERM_SEL                              BIT(25)
+#define  USB2_OTG_PD                           BIT(26)
+#define  USB2_OTG_PD2                          BIT(27)
+#define  USB2_OTG_PD2_OVRD_EN                  BIT(28)
+#define  USB2_OTG_PD_ZI                                BIT(29)
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x)      (0x8c + (x) * 0x40)
+#define  USB2_OTG_PD_DR                                BIT(2)
+#define  TERM_RANGE_ADJ(x)                     (((x) & 0xf) << 3)
+#define  RPD_CTRL(x)                           (((x) & 0x1f) << 26)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0         0x284
+#define  BIAS_PAD_PD                           BIT(11)
+#define  HS_SQUELCH_LEVEL(x)                   (((x) & 0x7) << 0)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1         0x288
+#define  USB2_TRK_START_TIMER(x)               (((x) & 0x7f) << 12)
+#define  USB2_TRK_DONE_RESET_TIMER(x)          (((x) & 0x7f) << 19)
+#define  USB2_PD_TRK                           BIT(26)
+
+#define XUSB_PADCTL_HSIC_PADX_CTL0(x)          (0x300 + (x) * 0x20)
+#define  HSIC_PD_TX_DATA0                      BIT(1)
+#define  HSIC_PD_TX_STROBE                     BIT(3)
+#define  HSIC_PD_RX_DATA0                      BIT(4)
+#define  HSIC_PD_RX_STROBE                     BIT(6)
+#define  HSIC_PD_ZI_DATA0                      BIT(7)
+#define  HSIC_PD_ZI_STROBE                     BIT(9)
+#define  HSIC_RPD_DATA0                                BIT(13)
+#define  HSIC_RPD_STROBE                       BIT(15)
+#define  HSIC_RPU_DATA0                                BIT(16)
+#define  HSIC_RPU_STROBE                       BIT(18)
+
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL0          0x340
+#define  HSIC_TRK_START_TIMER(x)               (((x) & 0x7f) << 5)
+#define  HSIC_TRK_DONE_RESET_TIMER(x)          (((x) & 0x7f) << 12)
+#define  HSIC_PD_TRK                           BIT(19)
+
+#define USB2_VBUS_ID                           0x360
+#define  VBUS_OVERRIDE                         BIT(14)
+#define  ID_OVERRIDE(x)                                (((x) & 0xf) << 18)
+#define  ID_OVERRIDE_FLOATING                  ID_OVERRIDE(8)
+#define  ID_OVERRIDE_GROUNDED                  ID_OVERRIDE(0)
+
+#define TEGRA186_LANE(_name, _offset, _shift, _mask, _type)            \
+       {                                                               \
+               .name = _name,                                          \
+               .offset = _offset,                                      \
+               .shift = _shift,                                        \
+               .mask = _mask,                                          \
+               .num_funcs = ARRAY_SIZE(tegra186_##_type##_functions),  \
+               .funcs = tegra186_##_type##_functions,                  \
+       }
+
+struct tegra_xusb_fuse_calibration {
+       u32 *hs_curr_level;
+       u32 hs_squelch;
+       u32 hs_term_range_adj;
+       u32 rpd_ctrl;
+};
+
+struct tegra186_xusb_padctl {
+       struct tegra_xusb_padctl base;
+
+       struct tegra_xusb_fuse_calibration calib;
+
+       /* UTMI bias and tracking */
+       struct clk *usb2_trk_clk;
+       unsigned int bias_pad_enable;
+};
+
+static inline struct tegra186_xusb_padctl *
+to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl)
+{
+       return container_of(padctl, struct tegra186_xusb_padctl, base);
+}
+
+/* USB 2.0 UTMI PHY support */
+static struct tegra_xusb_lane *
+tegra186_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+                        unsigned int index)
+{
+       struct tegra_xusb_usb2_lane *usb2;
+       int err;
+
+       usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+       if (!usb2)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&usb2->base.list);
+       usb2->base.soc = &pad->soc->lanes[index];
+       usb2->base.index = index;
+       usb2->base.pad = pad;
+       usb2->base.np = np;
+
+       err = tegra_xusb_lane_parse_dt(&usb2->base, np);
+       if (err < 0) {
+               kfree(usb2);
+               return ERR_PTR(err);
+       }
+
+       return &usb2->base;
+}
+
+static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane)
+{
+       struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+
+       kfree(usb2);
+}
+
+static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = {
+       .probe = tegra186_usb2_lane_probe,
+       .remove = tegra186_usb2_lane_remove,
+};
+
+static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
+{
+       struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+       struct device *dev = padctl->dev;
+       u32 value;
+       int err;
+
+       mutex_lock(&padctl->lock);
+
+       if (priv->bias_pad_enable++ > 0) {
+               mutex_unlock(&padctl->lock);
+               return;
+       }
+
+       err = clk_prepare_enable(priv->usb2_trk_clk);
+       if (err < 0)
+               dev_warn(dev, "failed to enable USB2 trk clock: %d\n", err);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+       value &= ~USB2_TRK_START_TIMER(~0);
+       value |= USB2_TRK_START_TIMER(0x1e);
+       value &= ~USB2_TRK_DONE_RESET_TIMER(~0);
+       value |= USB2_TRK_DONE_RESET_TIMER(0xa);
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+       value &= ~BIAS_PAD_PD;
+       value &= ~HS_SQUELCH_LEVEL(~0);
+       value |= HS_SQUELCH_LEVEL(priv->calib.hs_squelch);
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+       udelay(1);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+       value &= ~USB2_PD_TRK;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       mutex_unlock(&padctl->lock);
+}
+
+static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl)
+{
+       struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+       u32 value;
+
+       mutex_lock(&padctl->lock);
+
+       if (WARN_ON(priv->bias_pad_enable == 0)) {
+               mutex_unlock(&padctl->lock);
+               return;
+       }
+
+       if (--priv->bias_pad_enable > 0) {
+               mutex_unlock(&padctl->lock);
+               return;
+       }
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+       value |= USB2_PD_TRK;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       clk_disable_unprepare(priv->usb2_trk_clk);
+
+       mutex_unlock(&padctl->lock);
+}
+
+static void tegra_phy_xusb_utmi_pad_power_on(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;
+       struct device *dev = padctl->dev;
+       unsigned int index = lane->index;
+       u32 value;
+
+       if (!phy)
+               return;
+
+       port = tegra_xusb_find_usb2_port(padctl, index);
+       if (!port) {
+               dev_err(dev, "no port found for USB2 lane %u\n", index);
+               return;
+       }
+
+       tegra186_utmi_bias_pad_power_on(padctl);
+
+       udelay(2);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+       value &= ~USB2_OTG_PD;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+       value &= ~USB2_OTG_PD_DR;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+}
+
+static void tegra_phy_xusb_utmi_pad_power_down(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;
+       u32 value;
+
+       if (!phy)
+               return;
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+       value |= USB2_OTG_PD;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+       value |= USB2_OTG_PD_DR;
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+       udelay(2);
+
+       tegra186_utmi_bias_pad_power_off(padctl);
+}
+
+static int tegra186_utmi_phy_power_on(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+       struct tegra_xusb_usb2_port *port;
+       unsigned int index = lane->index;
+       struct device *dev = padctl->dev;
+       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;
+       }
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+       value &= ~(USB2_PORT_MASK << USB2_PORT_SHIFT(index));
+       value |= (PORT_XUSB << USB2_PORT_SHIFT(index));
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+       value &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(index));
+
+       if (port->mode == USB_DR_MODE_UNKNOWN)
+               value |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(index));
+       else if (port->mode == USB_DR_MODE_PERIPHERAL)
+               value |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(index));
+       else if (port->mode == USB_DR_MODE_HOST)
+               value |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(index));
+       else if (port->mode == USB_DR_MODE_OTG)
+               value |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(index));
+
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+       value &= ~USB2_OTG_PD_ZI;
+       value |= TERM_SEL;
+       value &= ~HS_CURR_LEVEL(~0);
+
+       if (usb2->hs_curr_level_offset) {
+               int hs_current_level;
+
+               hs_current_level = (int)priv->calib.hs_curr_level[index] +
+                                               usb2->hs_curr_level_offset;
+
+               if (hs_current_level < 0)
+                       hs_current_level = 0;
+               if (hs_current_level > 0x3f)
+                       hs_current_level = 0x3f;
+
+               value |= HS_CURR_LEVEL(hs_current_level);
+       } else {
+               value |= HS_CURR_LEVEL(priv->calib.hs_curr_level[index]);
+       }
+
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+       value &= ~TERM_RANGE_ADJ(~0);
+       value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
+       value &= ~RPD_CTRL(~0);
+       value |= RPD_CTRL(priv->calib.rpd_ctrl);
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+       /* TODO: pad power saving */
+       tegra_phy_xusb_utmi_pad_power_on(phy);
+       return 0;
+}
+
+static int tegra186_utmi_phy_power_off(struct phy *phy)
+{
+       /* TODO: pad power saving */
+       tegra_phy_xusb_utmi_pad_power_down(phy);
+
+       return 0;
+}
+
+static int tegra186_utmi_phy_init(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;
+       unsigned int index = lane->index;
+       struct device *dev = padctl->dev;
+       int err;
+
+       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) {
+                       dev_err(dev, "failed to enable port %u VBUS: %d\n",
+                               index, err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int tegra186_utmi_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;
+       unsigned int index = lane->index;
+       struct device *dev = padctl->dev;
+       int err;
+
+       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_disable(port->supply);
+               if (err) {
+                       dev_err(dev, "failed to disable port %u VBUS: %d\n",
+                               index, err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static const struct phy_ops utmi_phy_ops = {
+       .init = tegra186_utmi_phy_init,
+       .exit = tegra186_utmi_phy_exit,
+       .power_on = tegra186_utmi_phy_power_on,
+       .power_off = tegra186_utmi_phy_power_off,
+       .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra186_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
+                       const struct tegra_xusb_pad_soc *soc,
+                       struct device_node *np)
+{
+       struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+       struct tegra_xusb_usb2_pad *usb2;
+       struct tegra_xusb_pad *pad;
+       int err;
+
+       usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+       if (!usb2)
+               return ERR_PTR(-ENOMEM);
+
+       pad = &usb2->base;
+       pad->ops = &tegra186_usb2_lane_ops;
+       pad->soc = soc;
+
+       err = tegra_xusb_pad_init(pad, padctl, np);
+       if (err < 0) {
+               kfree(usb2);
+               goto out;
+       }
+
+       priv->usb2_trk_clk = devm_clk_get(&pad->dev, "trk");
+       if (IS_ERR(priv->usb2_trk_clk)) {
+               err = PTR_ERR(priv->usb2_trk_clk);
+               dev_dbg(&pad->dev, "failed to get usb2 trk clock: %d\n", err);
+               goto unregister;
+       }
+
+       err = tegra_xusb_pad_register(pad, &utmi_phy_ops);
+       if (err < 0)
+               goto unregister;
+
+       dev_set_drvdata(&pad->dev, pad);
+
+       return pad;
+
+unregister:
+       device_unregister(&pad->dev);
+out:
+       return ERR_PTR(err);
+}
+
+static void tegra186_usb2_pad_remove(struct tegra_xusb_pad *pad)
+{
+       struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
+
+       kfree(usb2);
+}
+
+static const struct tegra_xusb_pad_ops tegra186_usb2_pad_ops = {
+       .probe = tegra186_usb2_pad_probe,
+       .remove = tegra186_usb2_pad_remove,
+};
+
+static const char * const tegra186_usb2_functions[] = {
+       "xusb",
+};
+
+static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
+       TEGRA186_LANE("usb2-0", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-1", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-2", 0,  0, 0, usb2),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
+       .name = "usb2",
+       .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
+       .lanes = tegra186_usb2_lanes,
+       .ops = &tegra186_usb2_pad_ops,
+};
+
+static int tegra186_usb2_port_enable(struct tegra_xusb_port *port)
+{
+       return 0;
+}
+
+static void tegra186_usb2_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra186_usb2_port_map(struct tegra_xusb_port *port)
+{
+       return tegra_xusb_find_lane(port->padctl, "usb2", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = {
+       .enable = tegra186_usb2_port_enable,
+       .disable = tegra186_usb2_port_disable,
+       .map = tegra186_usb2_port_map,
+};
+
+/* SuperSpeed PHY support */
+static struct tegra_xusb_lane *
+tegra186_usb3_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+                        unsigned int index)
+{
+       struct tegra_xusb_usb3_lane *usb3;
+       int err;
+
+       usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
+       if (!usb3)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&usb3->base.list);
+       usb3->base.soc = &pad->soc->lanes[index];
+       usb3->base.index = index;
+       usb3->base.pad = pad;
+       usb3->base.np = np;
+
+       err = tegra_xusb_lane_parse_dt(&usb3->base, np);
+       if (err < 0) {
+               kfree(usb3);
+               return ERR_PTR(err);
+       }
+
+       return &usb3->base;
+}
+
+static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane)
+{
+       struct tegra_xusb_usb3_lane *usb3 = to_usb3_lane(lane);
+
+       kfree(usb3);
+}
+
+static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = {
+       .probe = tegra186_usb3_lane_probe,
+       .remove = tegra186_usb3_lane_remove,
+};
+static int tegra186_usb3_port_enable(struct tegra_xusb_port *port)
+{
+       return 0;
+}
+
+static void tegra186_usb3_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra186_usb3_port_map(struct tegra_xusb_port *port)
+{
+       return tegra_xusb_find_lane(port->padctl, "usb3", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
+       .enable = tegra186_usb3_port_enable,
+       .disable = tegra186_usb3_port_disable,
+       .map = tegra186_usb3_port_map,
+};
+
+static int tegra186_usb3_phy_power_on(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       struct tegra_xusb_usb3_port *port;
+       struct tegra_xusb_usb2_port *usb2;
+       unsigned int index = lane->index;
+       struct device *dev = padctl->dev;
+       u32 value;
+
+       port = tegra_xusb_find_usb3_port(padctl, index);
+       if (!port) {
+               dev_err(dev, "no port found for USB3 lane %u\n", index);
+               return -ENODEV;
+       }
+
+       usb2 = tegra_xusb_find_usb2_port(padctl, port->port);
+       if (!usb2) {
+               dev_err(dev, "no companion port found for USB3 lane %u\n",
+                       index);
+               return -ENODEV;
+       }
+
+       mutex_lock(&padctl->lock);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP);
+       value &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(index));
+
+       if (usb2->mode == USB_DR_MODE_UNKNOWN)
+               value |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(index));
+       else if (usb2->mode == USB_DR_MODE_PERIPHERAL)
+               value |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(index));
+       else if (usb2->mode == USB_DR_MODE_HOST)
+               value |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(index));
+       else if (usb2->mode == USB_DR_MODE_OTG)
+               value |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(index));
+
+       padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value &= ~SSPX_ELPG_VCORE_DOWN(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       usleep_range(100, 200);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       usleep_range(100, 200);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value &= ~SSPX_ELPG_CLAMP_EN(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       mutex_unlock(&padctl->lock);
+
+       return 0;
+}
+
+static int tegra186_usb3_phy_power_off(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       struct tegra_xusb_usb3_port *port;
+       unsigned int index = lane->index;
+       struct device *dev = padctl->dev;
+       u32 value;
+
+       port = tegra_xusb_find_usb3_port(padctl, index);
+       if (!port) {
+               dev_err(dev, "no port found for USB3 lane %u\n", index);
+               return -ENODEV;
+       }
+
+       mutex_lock(&padctl->lock);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value |= SSPX_ELPG_CLAMP_EN_EARLY(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       usleep_range(100, 200);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value |= SSPX_ELPG_CLAMP_EN(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       usleep_range(250, 350);
+
+       value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+       value |= SSPX_ELPG_VCORE_DOWN(index);
+       padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+       mutex_unlock(&padctl->lock);
+
+       return 0;
+}
+
+static int tegra186_usb3_phy_init(struct phy *phy)
+{
+       return 0;
+}
+
+static int tegra186_usb3_phy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops usb3_phy_ops = {
+       .init = tegra186_usb3_phy_init,
+       .exit = tegra186_usb3_phy_exit,
+       .power_on = tegra186_usb3_phy_power_on,
+       .power_off = tegra186_usb3_phy_power_off,
+       .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra186_usb3_pad_probe(struct tegra_xusb_padctl *padctl,
+                       const struct tegra_xusb_pad_soc *soc,
+                       struct device_node *np)
+{
+       struct tegra_xusb_usb3_pad *usb3;
+       struct tegra_xusb_pad *pad;
+       int err;
+
+       usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
+       if (!usb3)
+               return ERR_PTR(-ENOMEM);
+
+       pad = &usb3->base;
+       pad->ops = &tegra186_usb3_lane_ops;
+       pad->soc = soc;
+
+       err = tegra_xusb_pad_init(pad, padctl, np);
+       if (err < 0) {
+               kfree(usb3);
+               goto out;
+       }
+
+       err = tegra_xusb_pad_register(pad, &usb3_phy_ops);
+       if (err < 0)
+               goto unregister;
+
+       dev_set_drvdata(&pad->dev, pad);
+
+       return pad;
+
+unregister:
+       device_unregister(&pad->dev);
+out:
+       return ERR_PTR(err);
+}
+
+static void tegra186_usb3_pad_remove(struct tegra_xusb_pad *pad)
+{
+       struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
+
+       kfree(usb2);
+}
+
+static const struct tegra_xusb_pad_ops tegra186_usb3_pad_ops = {
+       .probe = tegra186_usb3_pad_probe,
+       .remove = tegra186_usb3_pad_remove,
+};
+
+static const char * const tegra186_usb3_functions[] = {
+       "xusb",
+};
+
+static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
+       TEGRA186_LANE("usb3-0", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-1", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-2", 0,  0, 0, usb3),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
+       .name = "usb3",
+       .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
+       .lanes = tegra186_usb3_lanes,
+       .ops = &tegra186_usb3_pad_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
+       &tegra186_usb2_pad,
+       &tegra186_usb3_pad,
+#if 0 /* TODO implement */
+       &tegra186_hsic_pad,
+#endif
+};
+
+static int
+tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
+{
+       struct device *dev = padctl->base.dev;
+       unsigned int i, count;
+       u32 value, *level;
+       int err;
+
+       count = padctl->base.soc->ports.usb2.count;
+
+       level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
+       if (!level)
+               return -ENOMEM;
+
+       err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+       if (err) {
+               dev_err(dev, "failed to read calibration fuse: %d\n", err);
+               return err;
+       }
+
+       dev_dbg(dev, "FUSE_USB_CALIB_0 %#x\n", value);
+
+       for (i = 0; i < count; i++)
+               level[i] = (value >> HS_CURR_LEVEL_PADX_SHIFT(i)) &
+                               HS_CURR_LEVEL_PAD_MASK;
+
+       padctl->calib.hs_curr_level = level;
+
+       padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
+                                       HS_SQUELCH_MASK;
+       padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) &
+                                               HS_TERM_RANGE_ADJ_MASK;
+
+       err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
+       if (err) {
+               dev_err(dev, "failed to read calibration fuse: %d\n", err);
+               return err;
+       }
+
+       dev_dbg(dev, "FUSE_USB_CALIB_EXT_0 %#x\n", value);
+
+       padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
+
+       return 0;
+}
+
+static struct tegra_xusb_padctl *
+tegra186_xusb_padctl_probe(struct device *dev,
+                          const struct tegra_xusb_padctl_soc *soc)
+{
+       struct tegra186_xusb_padctl *priv;
+       int err;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+
+       priv->base.dev = dev;
+       priv->base.soc = soc;
+
+       err = tegra186_xusb_read_fuse_calibration(priv);
+       if (err < 0)
+               return ERR_PTR(err);
+
+       return &priv->base;
+}
+
+static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
+{
+}
+
+static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
+       .probe = tegra186_xusb_padctl_probe,
+       .remove = tegra186_xusb_padctl_remove,
+};
+
+static const char * const tegra186_xusb_padctl_supply_names[] = {
+       "avdd-pll-erefeut",
+       "avdd-usb",
+       "vclamp-usb",
+       "vddio-hsic",
+};
+
+const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
+       .num_pads = ARRAY_SIZE(tegra186_pads),
+       .pads = tegra186_pads,
+       .ports = {
+               .usb2 = {
+                       .ops = &tegra186_usb2_port_ops,
+                       .count = 3,
+               },
+#if 0 /* TODO implement */
+               .hsic = {
+                       .ops = &tegra186_hsic_port_ops,
+                       .count = 1,
+               },
+#endif
+               .usb3 = {
+                       .ops = &tegra186_usb3_port_ops,
+                       .count = 3,
+               },
+       },
+       .ops = &tegra186_xusb_padctl_ops,
+       .supply_names = tegra186_xusb_padctl_supply_names,
+       .num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
+};
+EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
+
+MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");
+MODULE_LICENSE("GPL v2");
index e510629f4f1cd4cadb4008280099346f74ddd311..0417213ed68b03e457e7201621e6a30ee5baedb6 100644 (file)
@@ -67,6 +67,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
                .compatible = "nvidia,tegra210-xusb-padctl",
                .data = &tegra210_xusb_padctl_soc,
        },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_186_SOC)
+       {
+               .compatible = "nvidia,tegra186-xusb-padctl",
+               .data = &tegra186_xusb_padctl_soc,
+       },
 #endif
        { }
 };
index 5d5d22f6cb418b59f493efff43b9f83ef37f7e06..e0028b9fe70236f800e3a8692c4c361034cac402 100644 (file)
@@ -56,10 +56,21 @@ struct tegra_xusb_lane {
 int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
                             struct device_node *np);
 
+struct tegra_xusb_usb3_lane {
+       struct tegra_xusb_lane base;
+};
+
+static inline struct tegra_xusb_usb3_lane *
+to_usb3_lane(struct tegra_xusb_lane *lane)
+{
+       return container_of(lane, struct tegra_xusb_usb3_lane, base);
+}
+
 struct tegra_xusb_usb2_lane {
        struct tegra_xusb_lane base;
 
        u32 hs_curr_level_offset;
+       bool powered_on;
 };
 
 static inline struct tegra_xusb_usb2_lane *
@@ -170,6 +181,19 @@ int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
                            const struct phy_ops *ops);
 void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad);
 
+struct tegra_xusb_usb3_pad {
+       struct tegra_xusb_pad base;
+
+       unsigned int enable;
+       struct mutex lock;
+};
+
+static inline struct tegra_xusb_usb3_pad *
+to_usb3_pad(struct tegra_xusb_pad *pad)
+{
+       return container_of(pad, struct tegra_xusb_usb3_pad, base);
+}
+
 struct tegra_xusb_usb2_pad {
        struct tegra_xusb_pad base;
 
@@ -425,5 +449,8 @@ extern const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc;
 #if defined(CONFIG_ARCH_TEGRA_210_SOC)
 extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
 #endif
+#if defined(CONFIG_ARCH_TEGRA_186_SOC)
+extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc;
+#endif
 
 #endif /* __PHY_TEGRA_XUSB_H */