diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 500e8f20ee27..8109a4884cb6 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -28,6 +28,8 @@ #define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f #define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7 #define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf +#define FUSE_SKU_CALIB_HS_SQUELCH_SHIFT 29 +#define FUSE_SKU_CALIB_HS_SQUELCH_MASK 0x7 #define FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT 0 #define FUSE_USB_CALIB_EXT_RPD_CTRL_MASK 0x1f @@ -95,6 +97,7 @@ #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 29) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 27) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 26) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_TERM_SEL (1 << 25) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0 #define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f @@ -413,6 +416,8 @@ #define UHSIC_STROBE_RPD_C BIT(16) #define UHSIC_STROBE_RPD_D BIT(24) +#define TEGRA_UTMI_PAD_MAX 4 + struct init_data { u8 cfg_addr; u16 cfg_wdata; @@ -428,6 +433,7 @@ static struct init_data tegra210b01_usb3_pll_g1_init_data[] = { struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; u32 hs_term_range_adj; + u32 hs_squelch; u32 rpd_ctrl; }; @@ -442,6 +448,8 @@ struct tegra210_xusb_padctl { struct tegra_xusb_padctl base; struct regmap *regmap; + DECLARE_BITMAP(utmi_pad_enabled, TEGRA_UTMI_PAD_MAX); + struct tegra210_xusb_fuse_calibration fuse; struct tegra210_xusb_padctl_context context; }; @@ -1939,6 +1947,151 @@ static int tegra210_usb2_phy_exit(struct phy *phy) return 0; } +static void tegra210_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_usb2_pad *pad) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + u32 value; + int err; + + if (!bitmap_empty(priv->utmi_pad_enabled, TEGRA_UTMI_PAD_MAX)) + return; + + err = clk_prepare_enable(pad->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 &= ~(XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT); + value |= ((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL & + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK) << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT); + value &= ~(XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT); + value |= ((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL & + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK) << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); + value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; + value &= ~(XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT); + value |= ((priv->fuse.hs_squelch & + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK) << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT); + value &= ~(XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT); + value |= ((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL & + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK) << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); + + udelay(1); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); +} + +static void tegra210_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_usb2_pad *pad) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + u32 value; + + if (!bitmap_empty(priv->utmi_pad_enabled, TEGRA_UTMI_PAD_MAX)) + return; + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); +} + +static void tegra210_utmi_pad_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct tegra_xusb_usb2_port *port; + struct device *dev = padctl->dev; + unsigned int index = lane->index; + u32 value; + + if (!phy) + return; + + mutex_lock(&padctl->lock); + if (test_bit(index, priv->utmi_pad_enabled)) { + mutex_unlock(&padctl->lock); + return; + } + + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(dev, "no port found for USB2 lane %u\n", index); + mutex_unlock(&padctl->lock); + return; + } + + dev_dbg(dev, "power on UTMI pad %u\n", index); + + tegra210_utmi_bias_pad_power_on(padctl, pad); + + udelay(2); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + value &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + + set_bit(index, priv->utmi_pad_enabled); + mutex_unlock(&padctl->lock); +} + +static void tegra210_utmi_pad_power_down(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + unsigned int index = lane->index; + u32 value; + + if (!phy) + return; + + mutex_lock(&padctl->lock); + if (!test_bit(index, priv->utmi_pad_enabled)) { + mutex_unlock(&padctl->lock); + return; + } + + dev_dbg(padctl->dev, "power down UTMI pad %u\n", index); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + + udelay(2); + + clear_bit(index, priv->utmi_pad_enabled); + + tegra210_utmi_bias_pad_power_off(padctl, pad); + + mutex_unlock(&padctl->lock); +} + static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, bool status) { @@ -2040,13 +2193,11 @@ static int tegra210_usb2_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_usb2_pad *pad = to_usb2_pad(lane->pad); struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra210_xusb_padctl *priv; struct tegra_xusb_usb2_port *port; unsigned int index = lane->index; u32 value; - int err; port = tegra_xusb_find_usb2_port(padctl, index); if (!port) { @@ -2086,21 +2237,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); } - value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK << - XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) | - (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK << - XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT)); - value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL << - XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT); - - if (tegra_sku_info.revision < TEGRA_REVISION_A02) - value |= - (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL << - XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT); - - padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); value &= ~XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(index); if (port->mode == USB_DR_MODE_UNKNOWN) @@ -2114,28 +2250,40 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP); value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); - value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK << - XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) | - XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD | - XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 | - XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI); - value |= (priv->fuse.hs_curr_level[index] + - usb2->hs_curr_level_offset) << - XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT; + value &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI; + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_TERM_SEL; + value &= ~(XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT); + if (usb2->hs_curr_level_offset) { + int hs_current_level; + + hs_current_level = (int) priv->fuse.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_current_level & + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK) << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT; + } else { + value |= (priv->fuse.hs_curr_level[index] & + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK) << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT; + } + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); - value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK << - XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) | - (XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_MASK << - XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT) | - XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR | - XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD | - XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD); - value |= (priv->fuse.hs_term_range_adj << - XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) | - (priv->fuse.rpd_ctrl << - XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT); + value &= ~(XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT); + value |= (priv->fuse.hs_term_range_adj & + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK) << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT; + value &= ~RPD_CTRL(~0); + value |= RPD_CTRL(priv->fuse.rpd_ctrl); padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); value = padctl_readl(padctl, @@ -2151,55 +2299,14 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); - if (pad->enable > 0) { - pad->enable++; - mutex_unlock(&padctl->lock); - return 0; - } - - err = clk_prepare_enable(pad->clk); - if (err) - goto out; - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); - value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << - XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) | - (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK << - XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT)); - value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL << - XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) | - (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL << - XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT); - padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; - padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - - udelay(1); - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); - value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; - padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); - - udelay(50); - - clk_disable_unprepare(pad->clk); - - pad->enable++; mutex_unlock(&padctl->lock); return 0; - -out: - mutex_unlock(&padctl->lock); - return err; } static int tegra210_usb2_phy_power_off(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra_xusb_usb2_port *port; u32 value; @@ -2239,17 +2346,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); } - if (WARN_ON(pad->enable == 0)) - goto out; - - if (--pad->enable > 0) - goto out; - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; - padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); - -out: mutex_unlock(&padctl->lock); return 0; } @@ -3239,6 +3335,8 @@ static int tegra210_utmi_port_reset(struct phy *phy) static int tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse) { + struct tegra210_xusb_padctl *priv = + container_of(fuse, struct tegra210_xusb_padctl, fuse); unsigned int i; u32 value; int err; @@ -3247,12 +3345,18 @@ tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse) if (err < 0) return err; + dev_info(priv->base.dev, "TEGRA_FUSE_SKU_CALIB_0 = 0x%x\n", value); + for (i = 0; i < ARRAY_SIZE(fuse->hs_curr_level); i++) { fuse->hs_curr_level[i] = (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) & FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK; } + fuse->hs_squelch = + (value >> FUSE_SKU_CALIB_HS_SQUELCH_SHIFT) + & FUSE_SKU_CALIB_HS_SQUELCH_MASK; + fuse->hs_term_range_adj = (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) & FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK; @@ -3261,6 +3365,8 @@ tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse) if (err < 0) return err; + dev_info(priv->base.dev, "TEGRA_FUSE_USB_CALIB_EXT_0 = 0x%x\n", value); + fuse->rpd_ctrl = (value >> FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT) & FUSE_USB_CALIB_EXT_RPD_CTRL_MASK; @@ -3392,6 +3498,8 @@ static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { .hsic_set_idle = tegra210_hsic_set_idle, .vbus_override = tegra210_xusb_padctl_vbus_override, .utmi_port_reset = tegra210_utmi_port_reset, + .utmi_pad_power_on = tegra210_utmi_pad_power_on, + .utmi_pad_power_down = tegra210_utmi_pad_power_down, }; static const char * const tegra210_xusb_padctl_supply_names[] = {