phy: tegra: xusb: Add set_mode support for USB 2 phy on Tegra210

Add support for set_mode on USB 2 phy. This allow XUSB host/device mode
drivers to configure the hardware to corresponding modes.

Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Nagarjuna Kristam 2020-02-10 13:41:32 +05:30 committed by Thierry Reding
parent 5a40fc4b93
commit de792a6da7
1 changed files with 104 additions and 27 deletions

View File

@ -236,6 +236,7 @@
#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_FLOATING 8
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
struct tegra210_xusb_fuse_calibration {
u32 hs_curr_level[4];
@ -935,6 +936,103 @@ static int tegra210_usb2_phy_exit(struct phy *phy)
return tegra210_xusb_padctl_disable(lane->pad->padctl);
}
static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
bool status)
{
u32 value;
dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
if (status) {
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
} else {
value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
}
padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
return 0;
}
static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
bool status)
{
u32 value;
dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear");
value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
if (status) {
if (value & XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON) {
value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
usleep_range(1000, 2000);
value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
}
value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
} else {
value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
}
padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
return 0;
}
static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl,
lane->index);
int err = 0;
mutex_lock(&padctl->lock);
dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode);
if (mode == PHY_MODE_USB_OTG) {
if (submode == USB_ROLE_HOST) {
tegra210_xusb_padctl_id_override(padctl, true);
err = regulator_enable(port->supply);
} else if (submode == USB_ROLE_DEVICE) {
tegra210_xusb_padctl_vbus_override(padctl, true);
} else if (submode == USB_ROLE_NONE) {
/*
* When port is peripheral only or role transitions to
* USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not
* be enabled.
*/
if (regulator_is_enabled(port->supply))
regulator_disable(port->supply);
tegra210_xusb_padctl_id_override(padctl, false);
tegra210_xusb_padctl_vbus_override(padctl, false);
}
}
mutex_unlock(&padctl->lock);
return err;
}
static int tegra210_usb2_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
@ -1048,9 +1146,11 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
err = regulator_enable(port->supply);
if (err)
return err;
if (port->supply && port->mode == USB_DR_MODE_HOST) {
err = regulator_enable(port->supply);
if (err)
return err;
}
mutex_lock(&padctl->lock);
@ -1164,6 +1264,7 @@ static const struct phy_ops tegra210_usb2_phy_ops = {
.exit = tegra210_usb2_phy_exit,
.power_on = tegra210_usb2_phy_power_on,
.power_off = tegra210_usb2_phy_power_off,
.set_mode = tegra210_usb2_phy_set_mode,
.owner = THIS_MODULE,
};
@ -2023,30 +2124,6 @@ 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 status)
{
u32 value;
dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
if (status) {
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
} else {
value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
}
padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
return 0;
}
static int tegra210_utmi_port_reset(struct phy *phy)
{
struct tegra_xusb_padctl *padctl;