Hi JC, On 25-01-2019 14:27, JC Kuo wrote: > Hi Nagarjuna, > 1. tegra210_utmi_bias_pad_power_off() should > set XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD in order to really power down bias > pad. Will update the setting accordingly > 2. The XUSB_PADCTL_USB2_BIAS_PAD_CTL0 register programming > in tegra210_usb2_phy_power_on() can be removed since you have added it > to tegra210_utmi_bias_pad_power_on(). Will check and remove code from phy_power_on which is used in utmi pad and bias pad power on API's > 3. In tegra210_usb2_phy_power_off(), there is > + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); > + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( > + port->usb3_port_fake); > + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( > + port->usb3_port_fake); > + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); > I think it should be > + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); > + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( > + port->usb3_port_fake); > + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(port->usb3_port_fake, > + XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED ); > + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); > Yes, suggest change is right setting for disabled, will do the same. > 4. XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VAL should be renamed > with XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING . Another possible ID pin > state is "grounded" should be defined > XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED with value 0x0. > Will do > Thanks, > JC > > > On Thu, Jan 3, 2019 at 7:43 PM Nagarjuna Kristam <nkristam@xxxxxxxxxx> > wrote: > >> Add support for XUSB device mode controller on Tegra210. >> Update PADCTL driver to set port cap based on DT config. >> Add code to handle property "nvidia,usb3-port-fake" >> Provide API's to control vbus override and utmi pad power control >> >> Signed-off-by: Nagarjuna Kristam <nkristam@xxxxxxxxxx> >> --- >> drivers/phy/tegra/xusb-tegra210.c | 297 >> +++++++++++++++++++++++++++++++++++++- >> drivers/phy/tegra/xusb.c | 75 +++++++++- >> drivers/phy/tegra/xusb.h | 16 +- >> include/linux/phy/tegra/xusb.h | 7 +- >> 4 files changed, 386 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/phy/tegra/xusb-tegra210.c >> b/drivers/phy/tegra/xusb-tegra210.c >> index 05bee32..0289e10 100644 >> --- a/drivers/phy/tegra/xusb-tegra210.c >> +++ b/drivers/phy/tegra/xusb-tegra210.c >> @@ -1,5 +1,5 @@ >> /* >> - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. >> + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. >> * Copyright (C) 2015 Google, Inc. >> * >> * This program is free software; you can redistribute it and/or modify it >> @@ -47,7 +47,10 @@ >> #define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 0x1 >> >> #define XUSB_PADCTL_USB2_PORT_CAP 0x008 >> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(x) (0x0 << ((x) * 4)) >> #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(x) (0x1 << ((x) * 4)) >> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(x) (0x2 << ((x) * 4)) >> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(x) (0x3 << ((x) * 4)) >> #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(x) (0x3 << ((x) * 4)) >> >> #define XUSB_PADCTL_SS_PORT_MAP 0x014 >> @@ -55,6 +58,7 @@ >> #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 5) >> #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 5)) >> #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_PROGRAM1 0x024 >> #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) >> @@ -69,9 +73,14 @@ >> #define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) >> #define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x))) >> >> +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(x) (0x080 + (x) * 0x40) >> +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP (1 << 18) >> +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN (1 << 22) >> + >> #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40) >> #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7 >> #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3 >> +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL 0x1 >> #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6) >> >> #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40) >> @@ -230,6 +239,12 @@ >> #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(x) (0xa74 + (x) * 0x40) >> #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL 0xfcf01368 >> >> +#define XUSB_PADCTL_USB2_VBUS_ID 0xc60 >> +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON (1 << 14) >> +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18 >> +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf >> +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VAL 8 >> + >> struct tegra210_xusb_fuse_calibration { >> u32 hs_curr_level[4]; >> u32 hs_term_range_adj; >> @@ -905,6 +920,161 @@ static const struct tegra_xusb_lane_ops >> tegra210_usb2_lane_ops = { >> .remove = tegra210_usb2_lane_remove, >> }; >> >> +/* must be called under padctl->lock */ >> +static void tegra210_utmi_bias_pad_power_on(struct tegra_xusb_padctl >> *padctl, >> + struct tegra_xusb_usb2_pad *pad) >> +{ >> + u32 reg; >> + >> + if (pad->enable++ > 0) >> + return; >> + >> + dev_dbg(padctl->dev, "power on BIAS PAD & USB2 tracking\n"); >> + >> + if (clk_prepare_enable(pad->clk)) >> + dev_warn(padctl->dev, "failed to enable BIAS PAD & USB2 >> tracking\n"); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> + >> + reg &= ~((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)); >> + reg |= (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, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); >> + reg &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; >> + >> + reg &= ~((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)); >> + reg |= (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) >> + reg |= >> (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL << >> + >> XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT); >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); >> + >> + udelay(1); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> + reg &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> +} >> + >> +/* must be called under padctl->lock */ >> +static void tegra210_utmi_bias_pad_power_off(struct tegra_xusb_padctl >> *padctl, >> + struct tegra_xusb_usb2_pad *pad) >> +{ >> + u32 reg; >> + >> + if (WARN_ON(pad->enable == 0)) >> + return; >> + >> + if (--pad->enable > 0) >> + return; >> + >> + dev_dbg(padctl->dev, "power off USB2 tracking\n"); >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> + reg |= XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); >> + >> + clk_disable_unprepare(pad->clk); >> +} >> + >> +void tegra210_utmi_pad_power_on(struct phy *phy) >> +{ >> + struct tegra_xusb_lane *lane; >> + struct tegra_xusb_usb2_lane *usb2; >> + struct tegra_xusb_usb2_pad *pad; >> + struct tegra_xusb_padctl *padctl; >> + unsigned int index; >> + struct device *dev; >> + u32 reg; >> + >> + if (!phy) >> + return; >> + >> + lane = phy_get_drvdata(phy); >> + usb2 = to_usb2_lane(lane); >> + pad = to_usb2_pad(lane->pad); >> + padctl = lane->pad->padctl; >> + index = lane->index; >> + dev = padctl->dev; >> + >> + if (usb2->powered_on) >> + return; >> + >> + mutex_lock(&padctl->lock); >> + >> + dev_info(dev, "power on UTMI pads %d\n", index); >> + >> + tegra210_utmi_bias_pad_power_on(padctl, pad); >> + >> + udelay(2); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); >> + reg &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); >> + reg &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); >> + >> + usb2->powered_on = true; >> + >> + mutex_unlock(&padctl->lock); >> +} >> + >> +void tegra210_utmi_pad_power_down(struct phy *phy) >> +{ >> + struct tegra_xusb_lane *lane; >> + struct tegra_xusb_usb2_lane *usb2; >> + struct tegra_xusb_usb2_pad *pad; >> + struct tegra_xusb_padctl *padctl; >> + unsigned int index; >> + struct device *dev; >> + u32 reg; >> + >> + if (!phy) >> + return; >> + >> + lane = phy_get_drvdata(phy); >> + usb2 = to_usb2_lane(lane); >> + pad = to_usb2_pad(lane->pad); >> + padctl = lane->pad->padctl; >> + index = lane->index; >> + dev = padctl->dev; >> + >> + if (!usb2->powered_on) >> + return; >> + >> + mutex_lock(&padctl->lock); >> + >> + dev_info(dev, "power down UTMI pad %d\n", index); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); >> + reg |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); >> + reg |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); >> + >> + udelay(2); >> + >> + tegra210_utmi_bias_pad_power_off(padctl, pad); >> + usb2->powered_on = false; >> + >> + mutex_unlock(&padctl->lock); >> +} >> + >> static int tegra210_usb2_phy_init(struct phy *phy) >> { >> struct tegra_xusb_lane *lane = phy_get_drvdata(phy); >> @@ -948,6 +1118,34 @@ static int tegra210_usb2_phy_power_on(struct phy >> *phy) >> >> priv = to_tegra210_xusb_padctl(padctl); >> >> + if (port->usb3_port_fake != -1) { >> + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); >> + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( >> + port->usb3_port_fake); >> + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP( >> + port->usb3_port_fake, index); >> + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); >> + >> + usleep_range(100, 200); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value &= >> ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); >> + >> + usleep_range(100, 200); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN( >> + port->usb3_port_fake); >> + 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) >> | >> @@ -965,7 +1163,14 @@ static int tegra210_usb2_phy_power_on(struct phy >> *phy) >> >> value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); >> value &= ~XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(index); >> - value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index); >> + if (port->port_cap == USB_PORT_DISABLED) >> + value |= >> XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(index); >> + else if (port->port_cap == USB_DEVICE_CAP) >> + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(index); >> + else if (port->port_cap == USB_HOST_CAP) >> + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index); >> + else if (port->port_cap == USB_OTG_CAP) >> + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(index); >> padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP); >> >> value = padctl_readl(padctl, >> XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); >> @@ -997,7 +1202,12 @@ static int tegra210_usb2_phy_power_on(struct phy >> *phy) >> >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); >> value &= ~(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK >> << >> >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT); >> - value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; >> + if (port->port_cap == USB_HOST_CAP) >> + value |= >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; >> + else >> + value |= >> + >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL << >> + >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT; >> padctl_writel(padctl, value, >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); >> >> @@ -1070,6 +1280,34 @@ static int tegra210_usb2_phy_power_off(struct phy >> *phy) >> >> mutex_lock(&padctl->lock); >> >> + if (port->usb3_port_fake != -1) { >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value |= >> XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); >> + >> + usleep_range(100, 200); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); >> + >> + usleep_range(250, 350); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); >> + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); >> + >> + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); >> + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( >> + port->usb3_port_fake); >> + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( >> + port->usb3_port_fake); >> + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); >> + } >> + >> if (WARN_ON(pad->enable == 0)) >> goto out; >> >> @@ -1953,6 +2191,55 @@ static const struct tegra_xusb_port_ops >> tegra210_usb3_port_ops = { >> .map = tegra210_usb3_port_map, >> }; >> >> +static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl >> *padctl, >> + bool set) >> +{ >> + u32 reg; >> + >> + dev_dbg(padctl->dev, "%s vbus override\n", set ? "set" : "clear"); >> + >> + reg = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); >> + if (set) { >> + reg |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; >> + reg &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << >> + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); >> + reg |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VAL << >> + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; >> + } else >> + reg &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; >> + padctl_writel(padctl, reg, XUSB_PADCTL_USB2_VBUS_ID); >> + >> + return 0; >> +} >> + >> +static int tegra210_utmi_port_reset_quirk(struct phy *phy) >> +{ >> + struct tegra_xusb_padctl *padctl; >> + struct tegra_xusb_lane *lane; >> + struct device *dev; >> + u32 reg; >> + >> + if (!phy) >> + return -ENODEV; >> + >> + lane = phy_get_drvdata(phy); >> + padctl = lane->pad->padctl; >> + dev = padctl->dev; >> + >> + reg = padctl_readl(padctl, >> + >> XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(0)); >> + dev_dbg(dev, "BATTERY_CHRG_OTGPADX_CTL0(0): 0x%x\n", reg); >> + >> + if ((reg & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP) || >> + (reg & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN)) { >> + dev_dbg(dev, "Toggle vbus\n"); >> + tegra210_xusb_padctl_vbus_override(padctl, false); >> + tegra210_xusb_padctl_vbus_override(padctl, true); >> + return 1; >> + } >> + return 0; >> +} >> + >> static int >> tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration >> *fuse) >> { >> @@ -2015,6 +2302,10 @@ static const struct tegra_xusb_padctl_ops >> tegra210_xusb_padctl_ops = { >> .remove = tegra210_xusb_padctl_remove, >> .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, >> .hsic_set_idle = tegra210_hsic_set_idle, >> + .vbus_override = tegra210_xusb_padctl_vbus_override, >> + .utmi_pad_power_on = tegra210_utmi_pad_power_on, >> + .utmi_pad_power_down = tegra210_utmi_pad_power_down, >> + .utmi_port_reset_quirk = tegra210_utmi_port_reset_quirk, >> }; >> >> const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = { >> diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c >> index 5b3b886..9a25b5d 100644 >> --- a/drivers/phy/tegra/xusb.c >> +++ b/drivers/phy/tegra/xusb.c >> @@ -1,5 +1,5 @@ >> /* >> - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. >> + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. >> * >> * This program is free software; you can redistribute it and/or modify it >> * under the terms and conditions of the GNU General Public License, >> @@ -546,9 +546,26 @@ static int tegra_xusb_usb2_port_parse_dt(struct >> tegra_xusb_usb2_port *usb2) >> { >> struct tegra_xusb_port *port = &usb2->base; >> struct device_node *np = port->dev.of_node; >> + const char *prop_string; >> + u32 value; >> >> usb2->internal = of_property_read_bool(np, "nvidia,internal"); >> >> + usb2->port_cap = USB_PORT_DISABLED; /* default */ >> + if (!of_property_read_string(np, "mode", &prop_string)) { >> + if (!strcmp("host", prop_string)) >> + usb2->port_cap = USB_HOST_CAP; >> + else if (!strcmp("device", prop_string)) >> + usb2->port_cap = USB_DEVICE_CAP; >> + else if (!strcmp("otg", prop_string)) >> + usb2->port_cap = USB_OTG_CAP; >> + } >> + >> + if (!of_property_read_u32(np, "nvidia,usb3-port-fake", &value)) >> + usb2->usb3_port_fake = value; >> + else >> + usb2->usb3_port_fake = -1; /* default */ >> + >> usb2->supply = devm_regulator_get(&port->dev, "vbus"); >> return PTR_ERR_OR_ZERO(usb2->supply); >> } >> @@ -976,7 +993,7 @@ int tegra_xusb_padctl_usb3_save_context(struct >> tegra_xusb_padctl *padctl, >> if (padctl->soc->ops->usb3_save_context) >> return padctl->soc->ops->usb3_save_context(padctl, port); >> >> - return -ENOSYS; >> + return -EINVAL; >> } >> EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context); >> >> @@ -986,7 +1003,7 @@ int tegra_xusb_padctl_hsic_set_idle(struct >> tegra_xusb_padctl *padctl, >> if (padctl->soc->ops->hsic_set_idle) >> return padctl->soc->ops->hsic_set_idle(padctl, port, idle); >> >> - return -ENOSYS; >> + return -EINVAL; >> } >> EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); >> >> @@ -997,10 +1014,60 @@ int tegra_xusb_padctl_usb3_set_lfps_detect(struct >> tegra_xusb_padctl *padctl, >> return padctl->soc->ops->usb3_set_lfps_detect(padctl, port, >> enable); >> >> - return -ENOSYS; >> + return -EINVAL; >> } >> EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect); >> >> +int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl) >> +{ >> + if (padctl->soc->ops->vbus_override) >> + return padctl->soc->ops->vbus_override(padctl, true); >> + >> + return -EINVAL; >> +} >> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_set_vbus_override); >> + >> +int tegra_xusb_padctl_clear_vbus_override(struct tegra_xusb_padctl >> *padctl) >> +{ >> + if (padctl->soc->ops->vbus_override) >> + return padctl->soc->ops->vbus_override(padctl, false); >> + >> + return -EINVAL; >> +} >> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_clear_vbus_override); >> + >> +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; >> + >> + if (padctl->soc->ops->utmi_pad_power_on) >> + padctl->soc->ops->utmi_pad_power_on(phy); >> +} >> +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on); >> + >> +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; >> + >> + if (padctl->soc->ops->utmi_pad_power_down) >> + padctl->soc->ops->utmi_pad_power_down(phy); >> +} >> +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down); >> + >> +int tegra_phy_xusb_utmi_port_reset_quirk(struct phy *phy) >> +{ >> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); >> + struct tegra_xusb_padctl *padctl = lane->pad->padctl; >> + >> + if (padctl->soc->ops->utmi_port_reset_quirk) >> + return padctl->soc->ops->utmi_port_reset_quirk(phy); >> + >> + return -EINVAL; >> +} >> +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset_quirk); >> + >> MODULE_AUTHOR("Thierry Reding <treding@xxxxxxxxxx>"); >> MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); >> MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h >> index b49dbc3..74acc08 100644 >> --- a/drivers/phy/tegra/xusb.h >> +++ b/drivers/phy/tegra/xusb.h >> @@ -1,5 +1,5 @@ >> /* >> - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. >> + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. >> * Copyright (c) 2015, Google Inc. >> * >> * This program is free software; you can redistribute it and/or modify it >> @@ -58,6 +58,7 @@ 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 * >> @@ -267,11 +268,20 @@ struct tegra_xusb_port * >> tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type, >> unsigned int index); >> >> +enum tegra_xusb_usb_port_cap { >> + USB_PORT_DISABLED = 0, >> + USB_HOST_CAP, >> + USB_DEVICE_CAP, >> + USB_OTG_CAP, >> +}; >> + >> struct tegra_xusb_usb2_port { >> struct tegra_xusb_port base; >> >> struct regulator *supply; >> bool internal; >> + enum tegra_xusb_usb_port_cap port_cap; >> + int usb3_port_fake; >> }; >> >> static inline struct tegra_xusb_usb2_port * >> @@ -353,6 +363,10 @@ struct tegra_xusb_padctl_ops { >> unsigned int index, bool idle); >> int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl, >> unsigned int index, bool enable); >> + int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set); >> + void (*utmi_pad_power_on)(struct phy *phy); >> + void (*utmi_pad_power_down)(struct phy *phy); >> + int (*utmi_port_reset_quirk)(struct phy *phy); >> }; >> >> struct tegra_xusb_padctl_soc { >> diff --git a/include/linux/phy/tegra/xusb.h >> b/include/linux/phy/tegra/xusb.h >> index 8e1a57a..143af44 100644 >> --- a/include/linux/phy/tegra/xusb.h >> +++ b/include/linux/phy/tegra/xusb.h >> @@ -1,5 +1,5 @@ >> /* >> - * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. >> + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. >> * >> * This program is free software; you can redistribute it and/or modify it >> * under the terms and conditions of the GNU General Public License, >> @@ -26,5 +26,10 @@ int tegra_xusb_padctl_hsic_set_idle(struct >> tegra_xusb_padctl *padctl, >> unsigned int port, bool idle); >> int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl >> *padctl, >> unsigned int port, bool enable); >> +int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl); >> +int tegra_xusb_padctl_clear_vbus_override(struct tegra_xusb_padctl >> *padctl); >> >> +void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy); >> +void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy); >> +int tegra_phy_xusb_utmi_port_reset_quirk(struct phy *phy); >> #endif /* PHY_TEGRA_XUSB_H */ >> -- >> 2.7.4 >> >> >