Commit de792a6d authored by Nagarjuna Kristam's avatar Nagarjuna Kristam Committed by Thierry Reding
Browse files

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: default avatarNagarjuna Kristam <nkristam@nvidia.com>
Acked-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 5a40fc4b
Loading
Loading
Loading
Loading
+104 −27
Original line number Diff line number Diff line
@@ -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));

	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;