Commit 8acc379b authored by Svyatoslav Ryhel's avatar Svyatoslav Ryhel Committed by Greg Kroah-Hartman
Browse files

usb: phy: tegra: add HSIC support



Add support for HSIC USB mode, which can be set for second USB controller
and PHY on Tegra SoC along with already supported UTMI or ULPI.

Signed-off-by: default avatarSvyatoslav Ryhel <clamor95@gmail.com>
Link: https://patch.msgid.link/20260122151125.7367-3-clamor95@gmail.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 53cc2d90
Loading
Loading
Loading
Loading
+238 −11
Original line number Diff line number Diff line
@@ -29,17 +29,26 @@
#include <linux/usb/tegra_usb_phy.h>
#include <linux/usb/ulpi.h>

#define USB_TXFILLTUNING			0x154
#define USB_FIFO_TXFILL_THRES(x)		(((x) & 0x1f) << 16)
#define USB_FIFO_TXFILL_MASK			0x1f0000

#define ULPI_VIEWPORT				0x170

/* PORTSC PTS/PHCD bits, Tegra20 only */
#define TEGRA_USB_PORTSC1			0x184
#define   TEGRA_USB_PORTSC1_PTS(x)		(((x) & 0x3) << 30)
#define   TEGRA_USB_PORTSC1_PHCD		BIT(23)
#define   TEGRA_USB_PORTSC1_WKOC		BIT(22)
#define   TEGRA_USB_PORTSC1_WKDS		BIT(21)
#define   TEGRA_USB_PORTSC1_WKCN		BIT(20)

/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
#define TEGRA30_USB_PORTSC1			0x174
#define TEGRA_USB_HOSTPC1_DEVLC			0x1b4
#define   TEGRA_USB_HOSTPC1_DEVLC_PTS(x)	(((x) & 0x7) << 29)
#define   TEGRA_USB_HOSTPC1_DEVLC_PHCD		BIT(22)
#define   TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC	BIT(2)

/* Bits of PORTSC1, which will get cleared by writing 1 into them */
#define TEGRA_PORTSC1_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_OCC)
@@ -51,11 +60,12 @@
#define   USB_SUSP_CLR				BIT(5)
#define   USB_PHY_CLK_VALID			BIT(7)
#define   UTMIP_RESET				BIT(11)
#define   UHSIC_RESET				BIT(11)
#define   UTMIP_PHY_ENABLE			BIT(12)
#define   ULPI_PHY_ENABLE			BIT(13)
#define   USB_SUSP_SET				BIT(14)
#define   UHSIC_RESET				BIT(14)
#define   USB_WAKEUP_DEBOUNCE_COUNT(x)		(((x) & 0x7) << 16)
#define   UHSIC_PHY_ENABLE			BIT(19)

#define USB_PHY_VBUS_SENSORS			0x404
#define   B_SESS_VLD_WAKEUP_EN			BIT(14)
@@ -156,6 +166,58 @@
#define UTMIP_BIAS_CFG1				0x83c
#define   UTMIP_BIAS_PDTRK_COUNT(x)		(((x) & 0x1f) << 3)

/*
 * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800
 * just where UTMIP registers should have been. This is the case only with Tegra20
 * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers shifter by 0x400
 * to 0xc00, but register layout is preserved.
 */
#define UHSIC_PLL_CFG1				0x804
#define   UHSIC_XTAL_FREQ_COUNT(x)		(((x) & 0xfff) << 0)
#define   UHSIC_PLLU_ENABLE_DLY_COUNT(x)	(((x) & 0x1f) << 14)

#define UHSIC_HSRX_CFG0				0x808
#define   UHSIC_ELASTIC_UNDERRUN_LIMIT(x)	(((x) & 0x1f) << 2)
#define   UHSIC_ELASTIC_OVERRUN_LIMIT(x)	(((x) & 0x1f) << 8)
#define   UHSIC_IDLE_WAIT(x)			(((x) & 0x1f) << 13)

#define UHSIC_HSRX_CFG1				0x80c
#define   UHSIC_HS_SYNC_START_DLY(x)		(((x) & 0x1f) << 1)

#define UHSIC_TX_CFG0				0x810
#define   UHSIC_HS_READY_WAIT_FOR_VALID		BIT(9)

#define UHSIC_MISC_CFG0				0x814
#define   UHSIC_SUSPEND_EXIT_ON_EDGE		BIT(7)
#define   UHSIC_DETECT_SHORT_CONNECT		BIT(8)
#define   UHSIC_FORCE_XCVR_MODE			BIT(15)
#define   UHSIC_DISABLE_BUSRESET		BIT(20)

#define UHSIC_MISC_CFG1				0x818
#define   UHSIC_PLLU_STABLE_COUNT(x)		(((x) & 0xfff) << 2)

#define UHSIC_PADS_CFG0				0x81c
#define   UHSIC_TX_RTUNEN			0xf000
#define   UHSIC_TX_RTUNE(x)			(((x) & 0xf) << 12)

#define UHSIC_PADS_CFG1				0x820
#define   UHSIC_PD_BG				BIT(2)
#define   UHSIC_PD_TX				BIT(3)
#define   UHSIC_PD_TRK				BIT(4)
#define   UHSIC_PD_RX				BIT(5)
#define   UHSIC_PD_ZI				BIT(6)
#define   UHSIC_RX_SEL				BIT(7)
#define   UHSIC_RPD_DATA			BIT(9)
#define   UHSIC_RPD_STROBE			BIT(10)
#define   UHSIC_RPU_DATA			BIT(11)
#define   UHSIC_RPU_STROBE			BIT(12)

#define UHSIC_CMD_CFG0				0x824
#define   UHSIC_PRETEND_CONNECT_DETECT		BIT(5)

#define UHSIC_STAT_CFG0				0x828
#define   UHSIC_CONNECT_DETECT			BIT(0)

/* For Tegra30 and above only, the address is different in Tegra20 */
#define USB_USBMODE				0x1f8
#define   USB_USBMODE_MASK			(3 << 0)
@@ -174,7 +236,8 @@ struct tegra_xtal_freq {
	u8 enable_delay;
	u8 stable_count;
	u8 active_delay;
	u8 xtal_freq_count;
	u8 utmi_xtal_freq_count;
	u16 hsic_xtal_freq_count;
	u16 debounce;
};

@@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
		.enable_delay = 0x02,
		.stable_count = 0x2F,
		.active_delay = 0x04,
		.xtal_freq_count = 0x76,
		.utmi_xtal_freq_count = 0x76,
		.hsic_xtal_freq_count = 0x1CA,
		.debounce = 0x7530,
	},
	{
@@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
		.enable_delay = 0x02,
		.stable_count = 0x33,
		.active_delay = 0x05,
		.xtal_freq_count = 0x7F,
		.utmi_xtal_freq_count = 0x7F,
		.hsic_xtal_freq_count = 0x1F0,
		.debounce = 0x7EF4,
	},
	{
@@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
		.enable_delay = 0x03,
		.stable_count = 0x4B,
		.active_delay = 0x06,
		.xtal_freq_count = 0xBB,
		.utmi_xtal_freq_count = 0xBB,
		.hsic_xtal_freq_count = 0x2DD,
		.debounce = 0xBB80,
	},
	{
@@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
		.enable_delay = 0x04,
		.stable_count = 0x66,
		.active_delay = 0x09,
		.xtal_freq_count = 0xFE,
		.utmi_xtal_freq_count = 0xFE,
		.hsic_xtal_freq_count = 0x3E0,
		.debounce = 0xFDE8,
	},
};
@@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
		val = readl_relaxed(base + UTMIP_PLL_CFG1);
		val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
			UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
		val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
		val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) |
			UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
		writel_relaxed(val, base + UTMIP_PLL_CFG1);
	}
@@ -803,6 +870,153 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
	return 0;
}

static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg)
{
	void __iomem *base = phy->regs;
	u32 shift = phy->soc_config->uhsic_registers_shift;

	return readl_relaxed(base + shift + reg);
}

static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value)
{
	void __iomem *base = phy->regs;
	u32 shift = phy->soc_config->uhsic_registers_shift;

	writel_relaxed(value, base + shift + reg);
}

static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
{
	struct tegra_utmip_config *config = phy->config;
	void __iomem *base = phy->regs;
	u32 val;

	val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
	val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
		 UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
	val |= UHSIC_RX_SEL;
	tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);

	udelay(2);

	val = readl_relaxed(base + USB_SUSP_CTRL);
	val |= UHSIC_RESET;
	writel_relaxed(val, base + USB_SUSP_CTRL);

	udelay(30);

	val = readl_relaxed(base + USB_SUSP_CTRL);
	val |= UHSIC_PHY_ENABLE;
	writel_relaxed(val, base + USB_SUSP_CTRL);

	val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0);
	val &= ~(UHSIC_IDLE_WAIT(~0) |
		 UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) |
		 UHSIC_ELASTIC_OVERRUN_LIMIT(~0));
	val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) |
		UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) |
		UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit);
	tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val);

	val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1);
	val &= ~UHSIC_HS_SYNC_START_DLY(~0);
	val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay);
	tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val);

	val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0);
	val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
	tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val);

	val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1);
	val &= ~UHSIC_PLLU_STABLE_COUNT(~0);
	val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
	tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val);

	val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1);
	val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) |
		UHSIC_PLLU_ENABLE_DLY_COUNT(~0));
	val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) |
		UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
	tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val);

	val = readl_relaxed(base + USB_SUSP_CTRL);
	val &= ~UHSIC_RESET;
	writel_relaxed(val, base + USB_SUSP_CTRL);

	udelay(2);

	if (phy->soc_config->requires_usbmode_setup) {
		val = readl_relaxed(base + USB_USBMODE);
		val &= ~USB_USBMODE_MASK;
		if (phy->mode == USB_DR_MODE_HOST)
			val |= USB_USBMODE_HOST;
		else
			val |= USB_USBMODE_DEVICE;
		writel_relaxed(val, base + USB_USBMODE);
	}

	if (phy->soc_config->has_hostpc)
		set_pts(phy, TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC);
	else
		set_pts(phy, 0);

	val = readl_relaxed(base + USB_TXFILLTUNING);
	if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
		val = USB_FIFO_TXFILL_THRES(0x10);
		writel_relaxed(val, base + USB_TXFILLTUNING);
	}

	if (phy->soc_config->has_hostpc) {
		val = readl_relaxed(base + TEGRA30_USB_PORTSC1);
		val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS |
			 TEGRA_USB_PORTSC1_WKCN);
		writel_relaxed(val, base + TEGRA30_USB_PORTSC1);
	} else {
		val = readl_relaxed(base + TEGRA_USB_PORTSC1);
		val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS |
			 TEGRA_USB_PORTSC1_WKCN);
		writel_relaxed(val, base + TEGRA_USB_PORTSC1);
	}

	val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0);
	val &= ~UHSIC_TX_RTUNEN;
	val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune);
	tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val);

	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
			       USB_PHY_CLK_VALID))
		dev_err(phy->u_phy.dev,
			"Timeout waiting for PHY to stabilize on enable (HSIC)\n");

	return 0;
}

static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
{
	void __iomem *base = phy->regs;
	u32 val;

	set_phcd(phy, true);

	val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
	val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
		UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
	tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);

	val = readl_relaxed(base + USB_SUSP_CTRL);
	val |= UHSIC_RESET;
	writel_relaxed(val, base + USB_SUSP_CTRL);

	udelay(30);

	val = readl_relaxed(base + USB_SUSP_CTRL);
	val &= ~UHSIC_PHY_ENABLE;
	writel_relaxed(val, base + USB_SUSP_CTRL);

	return 0;
}

static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
	int err = 0;
@@ -819,6 +1033,10 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
		err = ulpi_phy_power_on(phy);
		break;

	case USBPHY_INTERFACE_MODE_HSIC:
		err = uhsic_phy_power_on(phy);
		break;

	default:
		break;
	}
@@ -850,6 +1068,10 @@ static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
		err = ulpi_phy_power_off(phy);
		break;

	case USBPHY_INTERFACE_MODE_HSIC:
		err = uhsic_phy_power_off(phy);
		break;

	default:
		break;
	}
@@ -1247,6 +1469,8 @@ static const struct tegra_phy_soc_config tegra20_soc_config = {
	.requires_usbmode_setup = false,
	.requires_extra_tuning_parameters = false,
	.requires_pmc_ao_power_up = false,
	.uhsic_registers_shift = 0,
	.uhsic_tx_rtune = 0, /* 40 ohm */
};

static const struct tegra_phy_soc_config tegra30_soc_config = {
@@ -1255,6 +1479,8 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
	.requires_usbmode_setup = true,
	.requires_extra_tuning_parameters = true,
	.requires_pmc_ao_power_up = true,
	.uhsic_registers_shift = 0x400,
	.uhsic_tx_rtune = 8,  /* 50 ohm */
};

static const struct of_device_id tegra_usb_phy_id_table[] = {
@@ -1332,6 +1558,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
	tegra_phy->phy_type = of_usb_get_phy_mode(np);
	switch (tegra_phy->phy_type) {
	case USBPHY_INTERFACE_MODE_UTMI:
	case USBPHY_INTERFACE_MODE_HSIC:
		err = utmi_phy_probe(tegra_phy, pdev);
		if (err)
			return err;
+5 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ struct gpio_desc;
 * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level
 *      and hsdiscon_level should be set for adequate signal quality
 * requires_pmc_ao_power_up: true if USB AO is powered down by default
 * uhsic_registers_shift: for Tegra30+ where HSIC registers were shifted
 *      comparing to Tegra20 by 0x400, since Tegra20 has no UTMIP on PHY2
 * uhsic_tx_rtune: fine tuned 50 Ohm termination resistor for NMOS/PMOS driver
 */

struct tegra_phy_soc_config {
@@ -31,6 +34,8 @@ struct tegra_phy_soc_config {
	bool requires_usbmode_setup;
	bool requires_extra_tuning_parameters;
	bool requires_pmc_ao_power_up;
	u32 uhsic_registers_shift;
	u32 uhsic_tx_rtune;
};

struct tegra_utmip_config {