All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Tegra EHCI driver
@ 2011-02-09  5:22 Benoit Goby
       [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  0 siblings, 1 reply; 13+ messages in thread
From: Benoit Goby @ 2011-02-09  5:22 UTC (permalink / raw)
  To: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell
  Cc: Benoit Goby, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

This patch series adds support for EHCI compliant USB host
controllers found in Tegra SoCs. The EHCI driver depends on
the Tegra PHY configuration interface that is used by both
the ehci driver and the gadget driver. Tested on 2.6.38rc4.

Benoit Goby (2):
  [ARM] tegra: Add support for Tegra USB PHYs
  usb: host: Add EHCI driver for NVIDIA Tegra SoCs

Gary King (1):
  usb: host: ehci-hcd: Add controller_resets_phy quirk

Robert Morell (1):
  USB: ehci: tegra: Align DMA transfers to 32 bytes

 arch/arm/mach-tegra/Makefile               |    1 +
 arch/arm/mach-tegra/include/mach/usb_phy.h |   83 +++
 arch/arm/mach-tegra/usb_phy.c              |  794 ++++++++++++++++++++++++++++
 drivers/usb/Kconfig                        |    1 +
 drivers/usb/host/Kconfig                   |    8 +
 drivers/usb/host/ehci-hcd.c                |    8 +-
 drivers/usb/host/ehci-tegra.c              |  763 ++++++++++++++++++++++++++
 drivers/usb/host/ehci.h                    |    1 +
 include/linux/tegra_usb.h                  |   35 ++
 9 files changed, 1693 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/usb_phy.h
 create mode 100644 arch/arm/mach-tegra/usb_phy.c
 create mode 100644 drivers/usb/host/ehci-tegra.c
 create mode 100644 include/linux/tegra_usb.h

-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs
       [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
@ 2011-02-09  5:22   ` Benoit Goby
       [not found]     ` <1297228927-23497-2-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  5:22   ` [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk Benoit Goby
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Benoit Goby @ 2011-02-09  5:22 UTC (permalink / raw)
  To: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell
  Cc: Benoit Goby, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

Interface used by Tegra's gadget driver and ehci driver
to power on and configure the USB PHYs.

Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/mach-tegra/Makefile               |    1 +
 arch/arm/mach-tegra/include/mach/usb_phy.h |   83 +++
 arch/arm/mach-tegra/usb_phy.c              |  794 ++++++++++++++++++++++++++++
 3 files changed, 878 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/usb_phy.h
 create mode 100644 arch/arm/mach-tegra/usb_phy.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index cdbc68e..38b66a8 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
+obj-$(CONFIG_USB_SUPPORT)		+= usb_phy.o
 
 obj-${CONFIG_MACH_HARMONY}              += board-harmony.o
 obj-${CONFIG_MACH_HARMONY}              += board-harmony-pinmux.o
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
new file mode 100644
index 0000000..bf97667
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -0,0 +1,83 @@
+/*
+ * arch/arm/mach-tegra/include/mach/usb_phy.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_USB_PHY_H
+#define __MACH_USB_PHY_H
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+struct tegra_utmip_config {
+	u8 hssync_start_delay;
+	u8 elastic_limit;
+	u8 idle_wait_delay;
+	u8 term_range_adj;
+	u8 xcvr_setup;
+	u8 xcvr_lsfslew;
+	u8 xcvr_lsrslew;
+};
+
+struct tegra_ulpi_config {
+	int reset_gpio;
+	const char *clk;
+};
+
+enum tegra_usb_phy_port_speed {
+	TEGRA_USB_PHY_PORT_SPEED_FULL = 0,
+	TEGRA_USB_PHY_PORT_SPEED_LOW,
+	TEGRA_USB_PHY_PORT_SPEED_HIGH,
+};
+
+enum tegra_usb_phy_mode {
+	TEGRA_USB_PHY_MODE_DEVICE,
+	TEGRA_USB_PHY_MODE_HOST,
+};
+
+struct tegra_usb_phy {
+	int instance;
+	int freq_sel;
+	void __iomem *regs;
+	void __iomem *pad_regs;
+	struct clk *clk;
+	struct clk *pll_u;
+	struct clk *pad_clk;
+	enum tegra_usb_phy_mode mode;
+	void *config;
+};
+
+struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
+			void *config, enum tegra_usb_phy_mode phy_mode);
+
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
+
+int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
+				 enum tegra_usb_phy_port_speed port_speed);
+
+int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
+
+int tegra_usb_phy_close(struct tegra_usb_phy *phy);
+
+#endif /* __MACH_USB_PHY_H */
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
new file mode 100644
index 0000000..7242dda
--- /dev/null
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -0,0 +1,794 @@
+/*
+ * arch/arm/mach-tegra/usb_phy.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Erik Gilling <konkers-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
+ *	Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/resource.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <mach/usb_phy.h>
+#include <mach/iomap.h>
+
+#define USB_USBSTS		0x144
+#define   USB_USBSTS_PCI	(1 << 2)
+
+#define ULPI_VIEWPORT		0x170
+#define   ULPI_WAKEUP		(1 << 31)
+#define   ULPI_RUN		(1 << 30)
+#define   ULPI_RD_RW_WRITE	(1 << 29)
+#define   ULPI_RD_RW_READ	(0 << 29)
+#define   ULPI_PORT(x)		(((x) & 0x7) << 24)
+#define   ULPI_ADDR(x)		(((x) & 0xff) << 16)
+#define   ULPI_DATA_RD(x)	(((x) & 0xff) << 8)
+#define   ULPI_DATA_WR(x)	(((x) & 0xff) << 0)
+
+#define USB_PORTSC1		0x184
+#define   USB_PORTSC1_PTS(x)	(((x) & 0x3) << 30)
+#define   USB_PORTSC1_PSPD(x)	(((x) & 0x3) << 26)
+#define   USB_PORTSC1_PHCD	(1 << 23)
+#define   USB_PORTSC1_WKOC	(1 << 22)
+#define   USB_PORTSC1_WKDS	(1 << 21)
+#define   USB_PORTSC1_WKCN	(1 << 20)
+#define   USB_PORTSC1_PTC(x)	(((x) & 0xf) << 16)
+#define   USB_PORTSC1_PP	(1 << 12)
+#define   USB_PORTSC1_SUSP	(1 << 7)
+#define   USB_PORTSC1_PE	(1 << 2)
+#define   USB_PORTSC1_CCS	(1 << 0)
+
+#define USB_SUSP_CTRL		0x400
+#define   USB_WAKE_ON_CNNT_EN_DEV	(1 << 3)
+#define   USB_WAKE_ON_DISCON_EN_DEV	(1 << 4)
+#define   USB_SUSP_CLR		(1 << 5)
+#define   USB_PHY_CLK_VALID	(1 << 7)
+#define   UTMIP_RESET			(1 << 11)
+#define   UHSIC_RESET			(1 << 11)
+#define   UTMIP_PHY_ENABLE		(1 << 12)
+#define   ULPI_PHY_ENABLE	(1 << 13)
+#define   USB_SUSP_SET		(1 << 14)
+#define   USB_WAKEUP_DEBOUNCE_COUNT(x)	(((x) & 0x7) << 16)
+
+#define USB1_LEGACY_CTRL	0x410
+#define   USB1_NO_LEGACY_MODE			(1 << 0)
+#define   USB1_VBUS_SENSE_CTL_MASK		(3 << 1)
+#define   USB1_VBUS_SENSE_CTL_VBUS_WAKEUP	(0 << 1)
+#define   USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
+						(1 << 1)
+#define   USB1_VBUS_SENSE_CTL_AB_SESS_VLD	(2 << 1)
+#define   USB1_VBUS_SENSE_CTL_A_SESS_VLD	(3 << 1)
+
+#define ULPI_TIMING_CTRL_0	0x424
+#define   ULPI_OUTPUT_PINMUX_BYP	(1 << 10)
+#define   ULPI_CLKOUT_PINMUX_BYP	(1 << 11)
+
+#define ULPI_TIMING_CTRL_1	0x428
+#define   ULPI_DATA_TRIMMER_LOAD	(1 << 0)
+#define   ULPI_DATA_TRIMMER_SEL(x)	(((x) & 0x7) << 1)
+#define   ULPI_STPDIRNXT_TRIMMER_LOAD	(1 << 16)
+#define   ULPI_STPDIRNXT_TRIMMER_SEL(x)	(((x) & 0x7) << 17)
+#define   ULPI_DIR_TRIMMER_LOAD		(1 << 24)
+#define   ULPI_DIR_TRIMMER_SEL(x)	(((x) & 0x7) << 25)
+
+#define UTMIP_PLL_CFG1		0x804
+#define   UTMIP_XTAL_FREQ_COUNT(x)		(((x) & 0xfff) << 0)
+#define   UTMIP_PLLU_ENABLE_DLY_COUNT(x)	(((x) & 0x1f) << 27)
+
+#define UTMIP_XCVR_CFG0		0x808
+#define   UTMIP_XCVR_SETUP(x)			(((x) & 0xf) << 0)
+#define   UTMIP_XCVR_LSRSLEW(x)			(((x) & 0x3) << 8)
+#define   UTMIP_XCVR_LSFSLEW(x)			(((x) & 0x3) << 10)
+#define   UTMIP_FORCE_PD_POWERDOWN		(1 << 14)
+#define   UTMIP_FORCE_PD2_POWERDOWN		(1 << 16)
+#define   UTMIP_FORCE_PDZI_POWERDOWN		(1 << 18)
+#define   UTMIP_XCVR_HSSLEW_MSB(x)		(((x) & 0x7f) << 25)
+
+#define UTMIP_BIAS_CFG0		0x80c
+#define   UTMIP_OTGPD			(1 << 11)
+#define   UTMIP_BIASPD			(1 << 10)
+
+#define UTMIP_HSRX_CFG0		0x810
+#define   UTMIP_ELASTIC_LIMIT(x)	(((x) & 0x1f) << 10)
+#define   UTMIP_IDLE_WAIT(x)		(((x) & 0x1f) << 15)
+
+#define UTMIP_HSRX_CFG1		0x814
+#define   UTMIP_HS_SYNC_START_DLY(x)	(((x) & 0x1f) << 1)
+
+#define UTMIP_TX_CFG0		0x820
+#define   UTMIP_FS_PREABMLE_J		(1 << 19)
+#define   UTMIP_HS_DISCON_DISABLE	(1 << 8)
+
+#define UTMIP_MISC_CFG0		0x824
+#define   UTMIP_DPDM_OBSERVE		(1 << 26)
+#define   UTMIP_DPDM_OBSERVE_SEL(x)	(((x) & 0xf) << 27)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_J	UTMIP_DPDM_OBSERVE_SEL(0xf)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_K	UTMIP_DPDM_OBSERVE_SEL(0xe)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
+#define   UTMIP_SUSPEND_EXIT_ON_EDGE	(1 << 22)
+
+#define UTMIP_MISC_CFG1		0x828
+#define   UTMIP_PLL_ACTIVE_DLY_COUNT(x)	(((x) & 0x1f) << 18)
+#define   UTMIP_PLLU_STABLE_COUNT(x)	(((x) & 0xfff) << 6)
+
+#define UTMIP_DEBOUNCE_CFG0	0x82c
+#define   UTMIP_BIAS_DEBOUNCE_A(x)	(((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0	0x830
+#define   UTMIP_PD_CHRG			(1 << 0)
+
+#define UTMIP_SPARE_CFG0	0x834
+#define   FUSE_SETUP_SEL		(1 << 3)
+
+#define UTMIP_XCVR_CFG1		0x838
+#define   UTMIP_FORCE_PDDISC_POWERDOWN	(1 << 0)
+#define   UTMIP_FORCE_PDCHRP_POWERDOWN	(1 << 2)
+#define   UTMIP_FORCE_PDDR_POWERDOWN	(1 << 4)
+#define   UTMIP_XCVR_TERM_RANGE_ADJ(x)	(((x) & 0xf) << 18)
+
+#define UTMIP_BIAS_CFG1		0x83c
+#define   UTMIP_BIAS_PDTRK_COUNT(x)	(((x) & 0x1f) << 3)
+
+static DEFINE_SPINLOCK(utmip_pad_lock);
+static int utmip_pad_count;
+
+static const int udc_freq_table[] = {
+	12000000,
+	13000000,
+	19200000,
+	26000000,
+};
+
+static const u8 udc_delay_table[][4] = {
+	/* ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */
+	{0x02,         0x2F,       0x04,       0x76}, /* 12 MHz */
+	{0x02,         0x33,       0x05,       0x7F}, /* 13 MHz */
+	{0x03,         0x4B,       0x06,       0xBB}, /* 19.2 MHz */
+	{0x04,         0x66,       0x09,       0xFE}, /* 26 Mhz */
+};
+
+static const u16 udc_debounce_table[] = {
+	0x7530, /* 12 MHz */
+	0x7EF4, /* 13 MHz */
+	0xBB80, /* 19.2 MHz */
+	0xFDE8, /* 26 MHz */
+};
+
+static struct tegra_utmip_config utmip_default[] = {
+	[0] = {
+		.hssync_start_delay = 9,
+		.idle_wait_delay = 17,
+		.elastic_limit = 16,
+		.term_range_adj = 6,
+		.xcvr_setup = 9,
+		.xcvr_lsfslew = 1,
+		.xcvr_lsrslew = 1,
+	},
+	[2] = {
+		.hssync_start_delay = 9,
+		.idle_wait_delay = 17,
+		.elastic_limit = 16,
+		.term_range_adj = 6,
+		.xcvr_setup = 9,
+		.xcvr_lsfslew = 2,
+		.xcvr_lsrslew = 2,
+	},
+};
+
+static int utmip_pad_open(struct tegra_usb_phy *phy)
+{
+	phy->pad_clk = clk_get_sys("utmip-pad", NULL);
+	if (IS_ERR(phy->pad_clk)) {
+		pr_err("%s: can't get utmip pad clock\n", __func__);
+		return -1;
+	}
+
+	if (phy->instance == 0) {
+		phy->pad_regs = phy->regs;
+	} else {
+		phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
+		if (!phy->pad_regs) {
+			pr_err("%s: can't remap usb registers\n", __func__);
+			clk_put(phy->pad_clk);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static void utmip_pad_close(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 0)
+		iounmap(phy->pad_regs);
+	clk_put(phy->pad_clk);
+}
+
+static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+{
+	unsigned long val, flags;
+	void __iomem *base = phy->pad_regs;
+
+	clk_enable(phy->pad_clk);
+
+	spin_lock_irqsave(&utmip_pad_lock, flags);
+
+	if (utmip_pad_count++ == 0) {
+		val = readl(base + UTMIP_BIAS_CFG0);
+		val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+		writel(val, base + UTMIP_BIAS_CFG0);
+	}
+
+	spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+	clk_disable(phy->pad_clk);
+}
+
+static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+{
+	unsigned long val, flags;
+	void __iomem *base = phy->pad_regs;
+
+	if (!utmip_pad_count) {
+		pr_err("%s: utmip pad already powered off\n", __func__);
+		return -1;
+	}
+
+	clk_enable(phy->pad_clk);
+
+	spin_lock_irqsave(&utmip_pad_lock, flags);
+
+	if (--utmip_pad_count == 0) {
+		val = readl(base + UTMIP_BIAS_CFG0);
+		val |= UTMIP_OTGPD | UTMIP_BIASPD;
+		writel(val, base + UTMIP_BIAS_CFG0);
+	}
+
+	spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+	clk_disable(phy->pad_clk);
+
+	return 0;
+}
+
+static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+	unsigned long timeout = 2000;
+	do {
+		if ((readl(reg) & mask) == result)
+			return 0;
+		udelay(1);
+		timeout--;
+	} while (timeout);
+	return -1;
+}
+
+static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	if (phy->instance == 0) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+
+		udelay(10);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	if (phy->instance == 2) {
+		val = readl(base + USB_PORTSC1);
+		val |= USB_PORTSC1_PHCD;
+		writel(val, base + USB_PORTSC1);
+	}
+
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+		pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+}
+
+static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	if (phy->instance == 0) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= USB_SUSP_CLR;
+		writel(val, base + USB_SUSP_CTRL);
+
+		udelay(10);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_CLR;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	if (phy->instance == 2) {
+		val = readl(base + USB_PORTSC1);
+		val &= ~USB_PORTSC1_PHCD;
+		writel(val, base + USB_PORTSC1);
+	}
+
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+						     USB_PHY_CLK_VALID))
+		pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+}
+
+static void utmi_phy_power_on(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+	struct tegra_utmip_config *config = phy->config;
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	if (phy->instance == 0) {
+		val = readl(base + USB1_LEGACY_CTRL);
+		val |= USB1_NO_LEGACY_MODE;
+		writel(val, base + USB1_LEGACY_CTRL);
+	}
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val &= ~UTMIP_FS_PREABMLE_J;
+	writel(val, base + UTMIP_TX_CFG0);
+
+	val = readl(base + UTMIP_HSRX_CFG0);
+	val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
+	val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
+	val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
+	writel(val, base + UTMIP_HSRX_CFG0);
+
+	val = readl(base + UTMIP_HSRX_CFG1);
+	val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+	val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
+	writel(val, base + UTMIP_HSRX_CFG1);
+
+	val = readl(base + UTMIP_DEBOUNCE_CFG0);
+	val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+	val |= UTMIP_BIAS_DEBOUNCE_A(udc_debounce_table[phy->freq_sel]);
+	writel(val, base + UTMIP_DEBOUNCE_CFG0);
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+	writel(val, base + UTMIP_MISC_CFG0);
+
+	val = readl(base + UTMIP_MISC_CFG1);
+	val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0));
+	val |= UTMIP_PLL_ACTIVE_DLY_COUNT(udc_delay_table[phy->freq_sel][2]) |
+		UTMIP_PLLU_STABLE_COUNT(udc_delay_table[phy->freq_sel][1]);
+	writel(val, base + UTMIP_MISC_CFG1);
+
+	val = readl(base + UTMIP_PLL_CFG1);
+	val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
+	val |= UTMIP_XTAL_FREQ_COUNT(udc_delay_table[phy->freq_sel][3]) |
+		UTMIP_PLLU_ENABLE_DLY_COUNT(udc_delay_table[phy->freq_sel][0]);
+	writel(val, base + UTMIP_PLL_CFG1);
+
+	if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	utmip_pad_power_on(phy);
+
+	val = readl(base + UTMIP_XCVR_CFG0);
+	val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+		 UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) |
+		 UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) |
+		 UTMIP_XCVR_HSSLEW_MSB(~0));
+	val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
+	val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
+	val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
+	writel(val, base + UTMIP_XCVR_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG1);
+	val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+		 UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
+	val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
+	writel(val, base + UTMIP_XCVR_CFG1);
+
+	val = readl(base + UTMIP_BAT_CHRG_CFG0);
+	val &= ~UTMIP_PD_CHRG;
+	writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+	val = readl(base + UTMIP_BIAS_CFG1);
+	val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+	val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+	writel(val, base + UTMIP_BIAS_CFG1);
+
+	if (phy->instance == 0) {
+		val = readl(base + UTMIP_SPARE_CFG0);
+		if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
+			val &= ~FUSE_SETUP_SEL;
+		else
+			val |= FUSE_SETUP_SEL;
+		writel(val, base + UTMIP_SPARE_CFG0);
+	}
+
+	if (phy->instance == 2) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= UTMIP_PHY_ENABLE;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	val = readl(base + USB_SUSP_CTRL);
+	val &= ~UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	if (phy->instance == 0) {
+		val = readl(base + USB1_LEGACY_CTRL);
+		val &= ~USB1_VBUS_SENSE_CTL_MASK;
+		val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
+		writel(val, base + USB1_LEGACY_CTRL);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	utmi_phy_clk_enable(phy);
+
+	if (phy->instance == 2) {
+		val = readl(base + USB_PORTSC1);
+		val &= ~USB_PORTSC1_PTS(~0);
+		writel(val, base + USB_PORTSC1);
+	}
+}
+
+static void utmi_phy_power_off(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	utmi_phy_clk_disable(phy);
+
+	if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+		val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = readl(base + UTMIP_BAT_CHRG_CFG0);
+	val |= UTMIP_PD_CHRG;
+	writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG0);
+	val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+	       UTMIP_FORCE_PDZI_POWERDOWN;
+	writel(val, base + UTMIP_XCVR_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG1);
+	val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+	       UTMIP_FORCE_PDDR_POWERDOWN;
+	writel(val, base + UTMIP_XCVR_CFG1);
+
+	utmip_pad_power_off(phy);
+}
+
+static void utmi_phy_preresume(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val |= UTMIP_HS_DISCON_DISABLE;
+	writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_postresume(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val &= ~UTMIP_HS_DISCON_DISABLE;
+	writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
+				   enum tegra_usb_phy_port_speed port_speed)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+	if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+		val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
+	else
+		val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(1);
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val |= UTMIP_DPDM_OBSERVE;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(10);
+}
+
+static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_DPDM_OBSERVE;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(10);
+}
+
+static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0);
+	val |= ULPI_ADDR(addr) | ULPI_DATA_WR(data);
+	writel(val, base + ULPI_VIEWPORT);
+
+	if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_RUN, 0))
+		pr_err("%s: timeout accessing ulpi phy\n", __func__);
+}
+
+static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+	struct tegra_ulpi_config *config = phy->config;
+
+	gpio_direction_output(config->reset_gpio, 0);
+	msleep(5);
+	gpio_direction_output(config->reset_gpio, 1);
+
+	clk_enable(phy->clk);
+	msleep(1);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UHSIC_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = readl(base + ULPI_TIMING_CTRL_0);
+	val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+	writel(val, base + ULPI_TIMING_CTRL_0);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= ULPI_PHY_ENABLE;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = 0;
+	writel(val, base + ULPI_TIMING_CTRL_1);
+
+	val |= ULPI_DATA_TRIMMER_SEL(4);
+	val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+	val |= ULPI_DIR_TRIMMER_SEL(4);
+	writel(val, base + ULPI_TIMING_CTRL_1);
+	udelay(10);
+
+	val |= ULPI_DATA_TRIMMER_LOAD;
+	val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+	val |= ULPI_DIR_TRIMMER_LOAD;
+	writel(val, base + ULPI_TIMING_CTRL_1);
+
+	val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0);
+	writel(val, base + ULPI_VIEWPORT);
+
+	if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) {
+		pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__);
+		return;
+	}
+
+	/* Fix VbusInvalid due to floating VBUS */
+	ulpi_viewport_write(phy, 0x08, 0x40);
+	ulpi_viewport_write(phy, 0x0B, 0x80);
+
+	val = readl(base + USB_PORTSC1);
+	val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
+	writel(val, base + USB_PORTSC1);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= USB_SUSP_CLR;
+	writel(val, base + USB_SUSP_CTRL);
+	udelay(100);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val &= ~USB_SUSP_CLR;
+	writel(val, base + USB_SUSP_CTRL);
+}
+
+static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+	struct tegra_ulpi_config *config = phy->config;
+
+	/* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
+	 * Controller to immediately bring the ULPI PHY out of low power
+	 */
+	val = readl(base + USB_PORTSC1);
+	val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
+	writel(val, base + USB_PORTSC1);
+
+	gpio_direction_output(config->reset_gpio, 0);
+	clk_disable(phy->clk);
+}
+
+struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
+			void *config, enum tegra_usb_phy_mode phy_mode)
+{
+	struct tegra_usb_phy *phy;
+	struct tegra_ulpi_config *ulpi_config;
+	unsigned long parent_rate;
+	int freq_sel;
+	int err;
+
+	phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
+	if (!phy)
+		return ERR_PTR(-ENOMEM);
+
+	phy->instance = instance;
+	phy->regs = regs;
+	phy->config = config;
+	phy->mode = phy_mode;
+
+	if (!phy->config) {
+		if (instance == 1) {
+			pr_err("%s: ulpi phy configuration missing", __func__);
+			err = -EINVAL;
+			goto err0;
+		} else {
+			phy->config = &utmip_default[instance];
+		}
+	}
+
+	phy->pll_u = clk_get_sys(NULL, "pll_u");
+	if (IS_ERR(phy->pll_u)) {
+		pr_err("Can't get pll_u clock\n");
+		err = PTR_ERR(phy->pll_u);
+		goto err0;
+	}
+	clk_enable(phy->pll_u);
+
+	parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
+	for (freq_sel = 0; freq_sel < ARRAY_SIZE(udc_freq_table); freq_sel++) {
+		if (udc_freq_table[freq_sel] == parent_rate)
+			break;
+	}
+	if (freq_sel == ARRAY_SIZE(udc_freq_table)) {
+		pr_err("invalid pll_u parent rate %ld\n", parent_rate);
+		err = -EINVAL;
+		goto err1;
+	}
+	phy->freq_sel = freq_sel;
+
+	if (phy->instance == 1) {
+		ulpi_config = config;
+		phy->clk = clk_get_sys(NULL, ulpi_config->clk);
+		if (IS_ERR(phy->clk)) {
+			pr_err("%s: can't get ulpi clock\n", __func__);
+			err = -ENXIO;
+			goto err1;
+		}
+		tegra_gpio_enable(ulpi_config->reset_gpio);
+		gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
+		gpio_direction_output(ulpi_config->reset_gpio, 0);
+	} else {
+		err = utmip_pad_open(phy);
+		if (err < 0)
+			goto err1;
+	}
+
+	return phy;
+
+err1:
+	clk_disable(phy->pll_u);
+	clk_put(phy->pll_u);
+err0:
+	kfree(phy);
+	return ERR_PTR(err);
+}
+
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+{
+	if (phy->instance == 1)
+		ulpi_phy_power_on(phy);
+	else
+		utmi_phy_power_on(phy);
+
+	return 0;
+}
+
+int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+{
+	if (phy->instance == 1)
+		ulpi_phy_power_off(phy);
+	else
+		utmi_phy_power_off(phy);
+
+	return 0;
+}
+
+int tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 1)
+		utmi_phy_preresume(phy);
+	return 0;
+}
+
+int tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 1)
+		utmi_phy_postresume(phy);
+	return 0;
+}
+
+int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
+				 enum tegra_usb_phy_port_speed port_speed)
+{
+	if (phy->instance != 1)
+		utmi_phy_restore_start(phy, port_speed);
+	return 0;
+}
+
+int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 1)
+		utmi_phy_restore_end(phy);
+	return 0;
+}
+
+int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 1)
+		utmi_phy_clk_disable(phy);
+
+	return 0;
+}
+
+int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
+{
+	if (phy->instance != 1)
+		utmi_phy_clk_enable(phy);
+
+	return 0;
+}
+
+int tegra_usb_phy_close(struct tegra_usb_phy *phy)
+{
+	if (phy->instance == 1)
+		clk_put(phy->clk);
+	else
+		utmip_pad_close(phy);
+	clk_disable(phy->pll_u);
+	clk_put(phy->pll_u);
+	kfree(phy);
+	return 0;
+}
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk
       [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  5:22   ` [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs Benoit Goby
@ 2011-02-09  5:22   ` Benoit Goby
       [not found]     ` <1297228927-23497-3-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  5:22   ` [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs Benoit Goby
  2011-02-09  5:22   ` [PATCH 4/4] USB: ehci: tegra: Align DMA transfers to 32 bytes Benoit Goby
  3 siblings, 1 reply; 13+ messages in thread
From: Benoit Goby @ 2011-02-09  5:22 UTC (permalink / raw)
  To: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell
  Cc: Benoit Goby, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Gary King

From: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Tegra quirk: Resetting the controller has the side effect of resetting
the PHY. Only reset the controller when doing so won't also reset the
phy.

Signed-off-by: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/usb/host/ehci-hcd.c |    3 ++-
 drivers/usb/host/ehci.h     |    1 +
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 30515d3..7afa345 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -275,7 +275,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
 
 	command |= CMD_RESET;
 	dbg_cmd (ehci, "reset", command);
-	ehci_writel(ehci, command, &ehci->regs->command);
+	if (!ehci->controller_resets_phy)
+		ehci_writel(ehci, command, &ehci->regs->command);
 	ehci_to_hcd(ehci)->state = HC_STATE_HALT;
 	ehci->next_statechange = jiffies;
 	retval = handshake (ehci, &ehci->regs->command,
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index f86d3fa..8854491 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -134,6 +134,7 @@ struct ehci_hcd {			/* one per controller */
 	unsigned		amd_pll_fix:1;
 	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
+	unsigned		controller_resets_phy:1; /* Tegra quirk */
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs
       [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  5:22   ` [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs Benoit Goby
  2011-02-09  5:22   ` [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk Benoit Goby
@ 2011-02-09  5:22   ` Benoit Goby
       [not found]     ` <1297228927-23497-4-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  5:22   ` [PATCH 4/4] USB: ehci: tegra: Align DMA transfers to 32 bytes Benoit Goby
  3 siblings, 1 reply; 13+ messages in thread
From: Benoit Goby @ 2011-02-09  5:22 UTC (permalink / raw)
  To: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell
  Cc: Benoit Goby, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/usb/Kconfig           |    1 +
 drivers/usb/host/Kconfig      |    8 +
 drivers/usb/host/ehci-hcd.c   |    5 +
 drivers/usb/host/ehci-tegra.c |  673 +++++++++++++++++++++++++++++++++++++++++
 include/linux/tegra_usb.h     |   35 +++
 5 files changed, 722 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ehci-tegra.c
 create mode 100644 include/linux/tegra_usb.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index fceea5e..a2fa2a8 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -59,6 +59,7 @@ config USB_ARCH_HAS_EHCI
 	default y if PPC_MPC512x
 	default y if SOC_AU1200
 	default y if ARCH_IXP4XX
+	default y if ARCH_TEGRA
 	default y if ARCH_W90X900
 	default y if ARCH_AT91SAM9G45
 	default y if ARCH_MXC
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 24046c0..a769adc 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -459,6 +459,14 @@ config USB_HWA_HCD
 	  To compile this driver a module, choose M here: the module
 	  will be called "hwa-hc".
 
+config USB_TEGRA_HCD
+       boolean "NVIDIA Tegra HCD support"
+       depends on USB && ARCH_TEGRA && USB_EHCI_HCD
+       select USB_EHCI_ROOT_HUB_TT
+       help
+         This driver enables support for the internal USB Host Controller
+         found in NVIDIA Tegra SoCs. The Tegra controller is EHCI compliant.
+
 config USB_IMX21_HCD
        tristate "iMX21 HCD support"
        depends on USB && ARM && MACH_MX21
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7afa345..63e8b29 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1246,6 +1246,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ehci_msm_driver
 #endif
 
+#ifdef CONFIG_ARCH_TEGRA
+#include "ehci-tegra.c"
+#define PLATFORM_DRIVER		tegra_ehci_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
new file mode 100644
index 0000000..24783ca
--- /dev/null
+++ b/drivers/usb/host/ehci-tegra.c
@@ -0,0 +1,673 @@
+/*
+ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2009 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/tegra_usb.h>
+#include <linux/irq.h>
+#include <linux/usb/otg.h>
+#include <mach/usb_phy.h>
+
+#define TEGRA_USB_USBCMD_REG_OFFSET		0x140
+#define TEGRA_USB_USBCMD_RESET			(1 << 1)
+#define TEGRA_USB_USBMODE_REG_OFFSET		0x1a8
+#define TEGRA_USB_USBMODE_HOST			(3 << 0)
+#define TEGRA_USB_PORTSC1_PTC(x)		(((x) & 0xf) << 16)
+
+struct tegra_ehci_hcd {
+	struct ehci_hcd *ehci;
+	struct tegra_usb_phy *phy;
+	struct clk *clk;
+	struct clk *emc_clk;
+	struct otg_transceiver *transceiver;
+	int host_resumed;
+	int bus_suspended;
+	int port_resuming;
+	int power_down_on_bus_suspend;
+	enum tegra_usb_phy_port_speed port_speed;
+};
+
+static void tegra_ehci_power_up(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+	clk_enable(tegra->emc_clk);
+	clk_enable(tegra->clk);
+	tegra_usb_phy_power_on(tegra->phy);
+	tegra->host_resumed = 1;
+}
+
+static void tegra_ehci_power_down(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+	tegra->host_resumed = 0;
+	tegra_usb_phy_power_off(tegra->phy);
+	clk_disable(tegra->clk);
+	clk_disable(tegra->emc_clk);
+}
+
+static int tegra_ehci_hub_control(
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+)
+{
+	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+	u32 __iomem	*status_reg;
+	u32		temp;
+	unsigned long	flags;
+	int		retval = 0;
+
+	status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
+
+	spin_lock_irqsave(&ehci->lock, flags);
+
+	/*
+	 * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits
+	 * that are write on clear, by writing back the register read value, so
+	 * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits
+	 */
+	if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) {
+		temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
+		ehci_writel(ehci, temp & ~PORT_PE, status_reg);
+		goto done;
+	}
+
+	else if (typeReq == GetPortStatus) {
+		temp = ehci_readl(ehci, status_reg);
+		if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
+			/* resume completed */
+			tegra->port_resuming = 0;
+			tegra_usb_phy_postresume(tegra->phy);
+		}
+	}
+
+	else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
+		temp = ehci_readl(ehci, status_reg);
+		if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
+			retval = -EPIPE;
+			goto done;
+		}
+
+		/* After above check the port must be connected.
+		 * Set appropriate bit thus could put phy into low power
+		 * mode if we have hostpc feature
+		 */
+		temp &= ~PORT_WKCONN_E;
+		temp |= PORT_WKDISC_E | PORT_WKOC_E;
+		ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+		if (handshake(ehci, status_reg, PORT_SUSPEND,
+						PORT_SUSPEND, 5000))
+			pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+		set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
+		goto done;
+	}
+
+	/*
+	 * Tegra host controller will time the resume operation to clear the bit
+	 * when the port control state switches to HS or FS Idle. This behavior
+	 * is different from EHCI where the host controller driver is required
+	 * to set this bit to a zero after the resume duration is timed in the
+	 * driver.
+	 */
+	else if (typeReq == ClearPortFeature &&
+					wValue == USB_PORT_FEAT_SUSPEND) {
+		temp = ehci_readl(ehci, status_reg);
+		if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
+			retval = -EPIPE;
+			goto done;
+		}
+
+		if (!(temp & PORT_SUSPEND))
+			goto done;
+
+		tegra_usb_phy_preresume(tegra->phy);
+
+		ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
+
+		temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+		/* start resume signalling */
+		ehci_writel(ehci, temp | PORT_RESUME, status_reg);
+
+		spin_unlock_irqrestore(&ehci->lock, flags);
+		msleep(20);
+		spin_lock_irqsave(&ehci->lock, flags);
+
+		/* polling PORT_RESUME until the controller clear this bit */
+		if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
+			pr_err("%s: timeout waiting for RESUME\n", __func__);
+
+		/* polling PORT_SUSPEND until the controller clear this bit */
+		if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
+			pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
+		ehci->reset_done[wIndex-1] = 0;
+
+		tegra->port_resuming = 1;
+		goto done;
+	}
+
+	spin_unlock_irqrestore(&ehci->lock, flags);
+
+	/* Handle the hub control events here */
+	return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+done:
+	spin_unlock_irqrestore(&ehci->lock, flags);
+	return retval;
+}
+
+static int tegra_ehci_reset(struct usb_hcd *hcd)
+{
+	unsigned long temp;
+	int usec = 250*1000; /* see ehci_reset */
+
+	temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
+	temp |= TEGRA_USB_USBCMD_RESET;
+	writel(temp, hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
+
+	do {
+		temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
+		if (!(temp & TEGRA_USB_USBCMD_RESET))
+			break;
+		udelay(1);
+		usec--;
+	} while (usec);
+
+	if (!usec)
+		return -ETIMEDOUT;
+
+	/* Set to Host mode by setting bit 0-1 of USB device mode register */
+	temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
+	writel((temp | TEGRA_USB_USBMODE_HOST),
+			(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
+
+	return 0;
+}
+
+static void tegra_ehci_restart(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+	tegra_ehci_reset(hcd);
+
+	/* setup the frame list and Async q heads */
+	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+	ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+	/* setup the command register and set the controller in RUN mode */
+	ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+	ehci->command |= CMD_RUN;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+	down_write(&ehci_cf_port_reset_rwsem);
+	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+	/* flush posted writes */
+	ehci_readl(ehci, &ehci->regs->command);
+	up_write(&ehci_cf_port_reset_rwsem);
+}
+
+static int tegra_usb_suspend(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+	struct ehci_regs __iomem *hw = tegra->ehci->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tegra->ehci->lock, flags);
+
+	tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
+	ehci_halt(tegra->ehci);
+	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+	spin_unlock_irqrestore(&tegra->ehci->lock, flags);
+
+	tegra_ehci_power_down(hcd);
+	return 0;
+}
+
+static int tegra_usb_resume(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
+	struct ehci_regs __iomem *hw = ehci->regs;
+	unsigned long val;
+
+	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	tegra_ehci_power_up(hcd);
+
+	if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) {
+		/* Wait for the phy to detect new devices
+		 * before we restart the controller */
+		msleep(10);
+		goto restart;
+	}
+
+	tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
+
+	writel(TEGRA_USB_USBMODE_HOST, &hw->reserved[19]);
+
+	/* Enable Port Power */
+	val = readl(&hw->port_status[0]);
+	val |= PORT_POWER;
+	writel(val, &hw->port_status[0]);
+	udelay(10);
+
+	/* Check if the phy resume from LP0. When the phy resume from LP0
+	 * USB register will be reset. */
+	if (!readl(&hw->async_next)) {
+		/* Program the field PTC based on the saved speed mode */
+		val = readl(&hw->port_status[0]);
+		val &= ~(TEGRA_USB_PORTSC1_PTC(~0));
+		if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH)
+			val |= TEGRA_USB_PORTSC1_PTC(5);
+		else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)
+			val |= TEGRA_USB_PORTSC1_PTC(6);
+		else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+			val |= TEGRA_USB_PORTSC1_PTC(7);
+		writel(val, &hw->port_status[0]);
+		udelay(10);
+
+		/* Disable test mode by setting PTC field to NORMAL_OP */
+		val = readl(&hw->port_status[0]);
+		val &= ~(TEGRA_USB_PORTSC1_PTC(~0));
+		writel(val, &hw->port_status[0]);
+		udelay(10);
+	}
+
+	/* Poll until CCS is enabled */
+	if (handshake(ehci, &hw->port_status[0], PORT_CONNECT,
+						 PORT_CONNECT, 2000)) {
+		pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__);
+		goto restart;
+	}
+
+	/* Poll until PE is enabled */
+	if (handshake(ehci, &hw->port_status[0], PORT_PE,
+						 PORT_PE, 2000)) {
+		pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__);
+		goto restart;
+	}
+
+	/* Clear the PCI status, to avoid an interrupt taken upon resume */
+	val = readl(&hw->status);
+	val |= STS_PCD;
+	writel(val, &hw->status);
+
+	/* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */
+	val = readl(&hw->port_status[0]);
+	if ((val & PORT_POWER) && (val & PORT_PE)) {
+		val |= PORT_SUSPEND;
+		writel(val, &hw->port_status[0]);
+
+		/* Wait until port suspend completes */
+		if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND,
+							 PORT_SUSPEND, 1000)) {
+			pr_err("%s: timeout waiting for PORT_SUSPEND\n",
+								__func__);
+			goto restart;
+		}
+	}
+
+	tegra_ehci_phy_restore_end(tegra->phy);
+	return 0;
+
+restart:
+	if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH)
+		tegra_ehci_phy_restore_end(tegra->phy);
+
+	tegra_ehci_restart(hcd);
+	return 0;
+}
+
+static void tegra_ehci_shutdown(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+	/* ehci_shutdown touches the USB controller registers, make sure
+	 * controller has clocks to it */
+	if (!tegra->host_resumed)
+		tegra_ehci_power_up(hcd);
+
+	ehci_shutdown(hcd);
+}
+
+static int tegra_ehci_setup(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	/* EHCI registers start at offset 0x100 */
+	ehci->caps = hcd->regs + 0x100;
+	ehci->regs = hcd->regs + 0x100 +
+		HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache this readonly data; minimize chip reads */
+	ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	hcd->has_tt = 1;
+	ehci->sbrn = 0x20;
+
+	ehci_reset(ehci);
+
+	/*
+	 * Resetting the controller has the side effect of resetting the PHY.
+	 * So, never reset the controller after the calling
+	 * tegra_ehci_reinit API.
+	 */
+	ehci->controller_resets_phy = 1;
+
+	ehci_port_power(ehci, 1);
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int tegra_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+	int error_status = 0;
+
+	error_status = ehci_bus_suspend(hcd);
+	if (!error_status && tegra->power_down_on_bus_suspend) {
+		tegra_usb_suspend(hcd);
+		tegra->bus_suspended = 1;
+	}
+
+	return error_status;
+}
+
+static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
+{
+	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+	if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) {
+		tegra_usb_resume(hcd);
+		tegra->bus_suspended = 0;
+	}
+
+	tegra_usb_phy_preresume(tegra->phy);
+	tegra->port_resuming = 1;
+	return ehci_bus_resume(hcd);
+}
+#endif
+
+static const struct hc_driver tegra_ehci_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Tegra EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= tegra_ehci_setup,
+	.irq			= ehci_irq,
+
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= tegra_ehci_shutdown,
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.get_frame_number	= ehci_get_frame,
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= tegra_ehci_hub_control,
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+#ifdef CONFIG_PM
+	.bus_suspend		= tegra_ehci_bus_suspend,
+	.bus_resume		= tegra_ehci_bus_resume,
+#endif
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+};
+
+static int tegra_ehci_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct usb_hcd *hcd;
+	struct ehci_hcd *ehci;
+	struct tegra_ehci_hcd *tegra;
+	struct tegra_ehci_platform_data *pdata;
+	struct tegra_utmip_config *config;
+	int err = 0;
+	int irq;
+	int instance = pdev->id;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		return -EINVAL;
+	}
+
+	tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL);
+	if (!tegra)
+		return -ENOMEM;
+
+	hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev,
+					dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		err = -ENOMEM;
+		goto fail_hcd;
+	}
+
+	platform_set_drvdata(pdev, tegra);
+
+	tegra->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(tegra->clk)) {
+		dev_err(&pdev->dev, "Can't get ehci clock\n");
+		err = PTR_ERR(tegra->clk);
+		goto fail_clk;
+	}
+
+	err = clk_enable(tegra->clk);
+	if (err)
+		goto fail_clken;
+
+	tegra->emc_clk = clk_get(&pdev->dev, "emc");
+	if (IS_ERR(tegra->emc_clk)) {
+		dev_err(&pdev->dev, "Can't get emc clock\n");
+		err = PTR_ERR(tegra->emc_clk);
+		goto fail_emc_clk;
+	}
+
+	clk_enable(tegra->emc_clk);
+	clk_set_rate(tegra->emc_clk, 400000000);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get I/O memory\n");
+		err = -ENXIO;
+		goto fail_io;
+	}
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(res->start, resource_size(res));
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+		err = -ENOMEM;
+		goto fail_io;
+	}
+
+	config = pdata->phy_config;
+
+	tegra->phy = tegra_usb_phy_open(instance, hcd->regs, config,
+						TEGRA_USB_PHY_MODE_HOST);
+	if (IS_ERR(tegra->phy)) {
+		dev_err(&pdev->dev, "Failed to open USB phy\n");
+		err = -ENXIO;
+		goto fail_phy;
+	}
+
+	err = tegra_ehci_reset(hcd);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to reset controller\n");
+		goto fail;
+	}
+
+	tegra_usb_phy_power_on(tegra->phy);
+	tegra->host_resumed = 1;
+	tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend;
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "Failed to get IRQ\n");
+		err = -ENODEV;
+		goto fail;
+	}
+
+	set_irq_flags(irq, IRQF_VALID);
+
+	ehci = hcd_to_ehci(hcd);
+	tegra->ehci = ehci;
+
+#ifdef CONFIG_USB_OTG_UTILS
+	if (pdata->operating_mode == TEGRA_USB_OTG) {
+		tegra->transceiver = otg_get_transceiver();
+		if (tegra->transceiver)
+			otg_set_host(tegra->transceiver, &hcd->self);
+	}
+#endif
+
+	err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+	if (err != 0) {
+		dev_err(&pdev->dev, "Failed to add USB HCD\n");
+		goto fail;
+	}
+
+	return err;
+
+fail:
+#ifdef CONFIG_USB_OTG_UTILS
+	if (tegra->transceiver) {
+		otg_set_host(tegra->transceiver, NULL);
+		otg_put_transceiver(tegra->transceiver);
+	}
+#endif
+	tegra_usb_phy_close(tegra->phy);
+fail_phy:
+	iounmap(hcd->regs);
+fail_io:
+	clk_disable(tegra->emc_clk);
+	clk_put(tegra->emc_clk);
+fail_emc_clk:
+	clk_disable(tegra->clk);
+fail_clken:
+	clk_put(tegra->clk);
+fail_clk:
+	usb_put_hcd(hcd);
+fail_hcd:
+	kfree(tegra);
+	return err;
+}
+
+#ifdef CONFIG_PM
+static int tegra_ehci_resume(struct platform_device *pdev)
+{
+	struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+	if (tegra->bus_suspended)
+		return 0;
+
+	return tegra_usb_resume(hcd);
+}
+
+static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+	if (tegra->bus_suspended)
+		return 0;
+
+	if (time_before(jiffies, tegra->ehci->next_statechange))
+		msleep(10);
+
+	return tegra_usb_suspend(hcd);
+}
+#endif
+
+static int tegra_ehci_remove(struct platform_device *pdev)
+{
+	struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+	if (tegra == NULL || hcd == NULL)
+		return -EINVAL;
+
+#ifdef CONFIG_USB_OTG_UTILS
+	if (tegra->transceiver) {
+		otg_set_host(tegra->transceiver, NULL);
+		otg_put_transceiver(tegra->transceiver);
+	}
+#endif
+
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+
+	tegra_usb_phy_close(tegra->phy);
+	iounmap(hcd->regs);
+
+	clk_disable(tegra->clk);
+	clk_put(tegra->clk);
+
+	clk_disable(tegra->emc_clk);
+	clk_put(tegra->emc_clk);
+
+	kfree(tegra);
+	return 0;
+}
+
+static void tegra_ehci_hcd_shutdown(struct platform_device *pdev)
+{
+	struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver tegra_ehci_driver = {
+	.probe		= tegra_ehci_probe,
+	.remove		= tegra_ehci_remove,
+#ifdef CONFIG_PM
+	.suspend	= tegra_ehci_suspend,
+	.resume		= tegra_ehci_resume,
+#endif
+	.shutdown	= tegra_ehci_hcd_shutdown,
+	.driver		= {
+		.name	= "tegra-ehci",
+	}
+};
diff --git a/include/linux/tegra_usb.h b/include/linux/tegra_usb.h
new file mode 100644
index 0000000..2947ed2
--- /dev/null
+++ b/include/linux/tegra_usb.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _TEGRA_USB_H_
+#define _TEGRA_USB_H_
+
+enum tegra_usb_operating_modes {
+	TEGRA_USB_DEVICE,
+	TEGRA_USB_HOST,
+	TEGRA_USB_OTG,
+};
+
+struct tegra_ehci_platform_data {
+	enum tegra_usb_operating_modes operating_mode;
+	/* power down the phy on bus suspend */
+	int power_down_on_bus_suspend;
+	void *phy_config;
+};
+
+#endif /* _TEGRA_USB_H_ */
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/4] USB: ehci: tegra: Align DMA transfers to 32 bytes
       [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
                     ` (2 preceding siblings ...)
  2011-02-09  5:22   ` [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs Benoit Goby
@ 2011-02-09  5:22   ` Benoit Goby
  3 siblings, 0 replies; 13+ messages in thread
From: Benoit Goby @ 2011-02-09  5:22 UTC (permalink / raw)
  To: David Brownell, Greg Kroah-Hartman, linux-usb-u79uwXL29TY76Z2rM5mHXA
  Cc: Benoit Goby, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Robert Morell

From: Robert Morell <rmorell-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The Tegra2 USB controller doesn't properly deal with misaligned DMA
buffers, causing corruption.  This is especially prevalent with USB
network adapters, where skbuff alignment is often in the middle of a
4-byte dword.

To avoid this, allocate a temporary buffer for the DMA if the provided
buffer isn't sufficiently aligned.

Signed-off-by: Robert Morell <rmorell-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
---
 drivers/usb/host/ehci-tegra.c |   90 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 90 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 24783ca..6d2d247 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -32,6 +32,8 @@
 #define TEGRA_USB_USBMODE_HOST			(3 << 0)
 #define TEGRA_USB_PORTSC1_PTC(x)		(((x) & 0xf) << 16)
 
+#define TEGRA_USB_DMA_ALIGN 32
+
 struct tegra_ehci_hcd {
 	struct ehci_hcd *ehci;
 	struct tegra_usb_phy *phy;
@@ -423,6 +425,92 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
 }
 #endif
 
+struct temp_buffer {
+	void *kmalloc_ptr;
+	void *old_xfer_buffer;
+	u8 data[0];
+};
+
+static void free_temp_buffer(struct urb *urb)
+{
+	enum dma_data_direction dir;
+	struct temp_buffer *temp;
+
+	if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+		return;
+
+	dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	temp = container_of(urb->transfer_buffer, struct temp_buffer,
+			    data);
+
+	if (dir == DMA_FROM_DEVICE)
+		memcpy(temp->old_xfer_buffer, temp->data,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->old_xfer_buffer;
+	kfree(temp->kmalloc_ptr);
+
+	urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+}
+
+static int alloc_temp_buffer(struct urb *urb, gfp_t mem_flags)
+{
+	enum dma_data_direction dir;
+	struct temp_buffer *temp, *kmalloc_ptr;
+	size_t kmalloc_size;
+
+	if (urb->num_sgs || urb->sg ||
+	    urb->transfer_buffer_length == 0 ||
+	    !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1)))
+		return 0;
+
+	dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	/* Allocate a buffer with enough padding for alignment */
+	kmalloc_size = urb->transfer_buffer_length +
+		sizeof(struct temp_buffer) + TEGRA_USB_DMA_ALIGN - 1;
+
+	kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+	if (!kmalloc_ptr)
+		return -ENOMEM;
+
+	/* Position our struct temp_buffer such that data is aligned */
+	temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1;
+
+	temp->kmalloc_ptr = kmalloc_ptr;
+	temp->old_xfer_buffer = urb->transfer_buffer;
+	if (dir == DMA_TO_DEVICE)
+		memcpy(temp->data, urb->transfer_buffer,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->data;
+
+	urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
+
+	return 0;
+}
+
+static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+				      gfp_t mem_flags)
+{
+	int ret;
+
+	ret = alloc_temp_buffer(urb, mem_flags);
+	if (ret)
+		return ret;
+
+	ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+	if (ret)
+		free_temp_buffer(urb);
+
+	return ret;
+}
+
+static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+	usb_hcd_unmap_urb_for_dma(hcd, urb);
+	free_temp_buffer(urb);
+}
+
 static const struct hc_driver tegra_ehci_hc_driver = {
 	.description		= hcd_name,
 	.product_desc		= "Tegra EHCI Host Controller",
@@ -438,6 +526,8 @@ static const struct hc_driver tegra_ehci_hc_driver = {
 	.shutdown		= tegra_ehci_shutdown,
 	.urb_enqueue		= ehci_urb_enqueue,
 	.urb_dequeue		= ehci_urb_dequeue,
+	.map_urb_for_dma	= tegra_ehci_map_urb_for_dma,
+	.unmap_urb_for_dma	= tegra_ehci_unmap_urb_for_dma,
 	.endpoint_disable	= ehci_endpoint_disable,
 	.endpoint_reset		= ehci_endpoint_reset,
 	.get_frame_number	= ehci_get_frame,
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs
       [not found]     ` <1297228927-23497-2-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
@ 2011-02-09  7:05       ` rmorell-DDmLM1+adcrQT0dZR+AlfA
       [not found]         ` <20110209070540.GA17627-f3YH7lVHJt/FT5IIyIEb6QC/G2K4zDHf@public.gmane.org>
  2011-02-09  8:44       ` Matthieu CASTET
  1 sibling, 1 reply; 13+ messages in thread
From: rmorell-DDmLM1+adcrQT0dZR+AlfA @ 2011-02-09  7:05 UTC (permalink / raw)
  To: Benoit Goby
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

On Tue, Feb 08, 2011 at 09:22:04PM -0800, Benoit Goby wrote:
> Interface used by Tegra's gadget driver and ehci driver
> to power on and configure the USB PHYs.
> 
> Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
> ---

For some reason this patch (along with patch 3) appears to contain
spaces as indentation (no hard tabs), although patches 2 and 4 in this
series have the correct tabs.

[...]

> +#define USB_USBSTS             0x144
> +#define   USB_USBSTS_PCI       (1 << 2)

This doesn't appear to be used?

[...]

> +static DEFINE_SPINLOCK(utmip_pad_lock);
> +static int utmip_pad_count;
> +
> +static const int udc_freq_table[] = {
> +       12000000,
> +       13000000,
> +       19200000,
> +       26000000,
> +};
> +
> +static const u8 udc_delay_table[][4] = {
> +       /* ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */
> +       {0x02,         0x2F,       0x04,       0x76}, /* 12 MHz */
> +       {0x02,         0x33,       0x05,       0x7F}, /* 13 MHz */
> +       {0x03,         0x4B,       0x06,       0xBB}, /* 19.2 MHz */
> +       {0x04,         0x66,       0x09,       0xFE}, /* 26 Mhz */
> +};

Use an array of structs here?  Writing
"UTMIP_PLL_ACTIVE_DLY_COUNT(udc_delay_table[phy->freq_sel][2])" below is a
lot less clear and more prone to bugs than, say,
"UTMIP_PLL_ACTIVE_DLY_COUNT(udc_delay_table[phy->freq_sel].active_dly)".

It'd be even better to combine all three tables into one; something
like:
static const struct udc_... udc_table[] = {
        { .freq = 12000000,
          .delay = {
            .enable = 0x02,
            .stable = 0x2f,
            .active = 0x04
          },
          .xtal_freq_cnt = 0x76,
          .debounce = 0x7530,
        },
        [... etc]
};

[...]

> +static int utmip_pad_open(struct tegra_usb_phy *phy)
> +{
> +       phy->pad_clk = clk_get_sys("utmip-pad", NULL);
> +       if (IS_ERR(phy->pad_clk)) {
> +               pr_err("%s: can't get utmip pad clock\n", __func__);
> +               return -1;

return PTR_ERR(phy->pad_clk);

> +       }
> +
> +       if (phy->instance == 0) {
> +               phy->pad_regs = phy->regs;
> +       } else {
> +               phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
> +               if (!phy->pad_regs) {
> +                       pr_err("%s: can't remap usb registers\n", __func__);
> +                       clk_put(phy->pad_clk);
> +                       return -ENOMEM;
> +               }
> +       }
> +       return 0;
> +}

[...]

> +static int utmip_pad_power_off(struct tegra_usb_phy *phy)
> +{
> +       unsigned long val, flags;
> +       void __iomem *base = phy->pad_regs;
> +
> +       if (!utmip_pad_count) {
> +               pr_err("%s: utmip pad already powered off\n", __func__);
> +               return -1;

return -EINVAL?

> +       }
> +
> +       clk_enable(phy->pad_clk);
> +
> +       spin_lock_irqsave(&utmip_pad_lock, flags);
> +
> +       if (--utmip_pad_count == 0) {
> +               val = readl(base + UTMIP_BIAS_CFG0);
> +               val |= UTMIP_OTGPD | UTMIP_BIASPD;
> +               writel(val, base + UTMIP_BIAS_CFG0);
> +       }
> +
> +       spin_unlock_irqrestore(&utmip_pad_lock, flags);
> +
> +       clk_disable(phy->pad_clk);
> +
> +       return 0;
> +}
> +

[...]

> +static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
> +{
> +       unsigned long val;
> +       void __iomem *base = phy->regs;
> +       struct tegra_ulpi_config *config = phy->config;
> +
> +       gpio_direction_output(config->reset_gpio, 0);
> +       msleep(5);
> +       gpio_direction_output(config->reset_gpio, 1);
> +
> +       clk_enable(phy->clk);
> +       msleep(1);

This msleep seems excessive.  Does it take that long for the clock to
settle?

> +
> +       val = readl(base + USB_SUSP_CTRL);
> +       val |= UHSIC_RESET;
> +       writel(val, base + USB_SUSP_CTRL);
> +
> +       val = readl(base + ULPI_TIMING_CTRL_0);
> +       val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
> +       writel(val, base + ULPI_TIMING_CTRL_0);
> +
> +       val = readl(base + USB_SUSP_CTRL);
> +       val |= ULPI_PHY_ENABLE;
> +       writel(val, base + USB_SUSP_CTRL);
> +
> +       val = 0;
> +       writel(val, base + ULPI_TIMING_CTRL_1);
> +
> +       val |= ULPI_DATA_TRIMMER_SEL(4);
> +       val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
> +       val |= ULPI_DIR_TRIMMER_SEL(4);
> +       writel(val, base + ULPI_TIMING_CTRL_1);
> +       udelay(10);
> +
> +       val |= ULPI_DATA_TRIMMER_LOAD;
> +       val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
> +       val |= ULPI_DIR_TRIMMER_LOAD;
> +       writel(val, base + ULPI_TIMING_CTRL_1);
> +
> +       val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0);
> +       writel(val, base + ULPI_VIEWPORT);
> +
> +       if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) {
> +               pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__);
> +               return;

Why does this function return void if it can fail?
tegra_usb_phy_power_on() can propagate the error.

> +       }
> +
> +       /* Fix VbusInvalid due to floating VBUS */
> +       ulpi_viewport_write(phy, 0x08, 0x40);
> +       ulpi_viewport_write(phy, 0x0B, 0x80);
> +
> +       val = readl(base + USB_PORTSC1);
> +       val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
> +       writel(val, base + USB_PORTSC1);
> +
> +       val = readl(base + USB_SUSP_CTRL);
> +       val |= USB_SUSP_CLR;
> +       writel(val, base + USB_SUSP_CTRL);
> +       udelay(100);
> +
> +       val = readl(base + USB_SUSP_CTRL);
> +       val &= ~USB_SUSP_CLR;
> +       writel(val, base + USB_SUSP_CTRL);
> +}

[...]

> +struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
> +                       void *config, enum tegra_usb_phy_mode phy_mode)
> +{
> +       struct tegra_usb_phy *phy;
> +       struct tegra_ulpi_config *ulpi_config;
> +       unsigned long parent_rate;
> +       int freq_sel;
> +       int err;
> +
> +       phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
> +       if (!phy)
> +               return ERR_PTR(-ENOMEM);
> +
> +       phy->instance = instance;
> +       phy->regs = regs;
> +       phy->config = config;
> +       phy->mode = phy_mode;
> +
> +       if (!phy->config) {
> +               if (instance == 1) {

Instead of all of the (instance == 1) checks here and below (I count
eleven separate places this is checked), it might be more readable to
have a macro or static inline function such as bool phy_is_ulpi(phy).

> +                       pr_err("%s: ulpi phy configuration missing", __func__);
> +                       err = -EINVAL;
> +                       goto err0;
> +               } else {
> +                       phy->config = &utmip_default[instance];
> +               }
> +       }
> +
> +       phy->pll_u = clk_get_sys(NULL, "pll_u");
> +       if (IS_ERR(phy->pll_u)) {
> +               pr_err("Can't get pll_u clock\n");
> +               err = PTR_ERR(phy->pll_u);
> +               goto err0;
> +       }
> +       clk_enable(phy->pll_u);
> +
> +       parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
> +       for (freq_sel = 0; freq_sel < ARRAY_SIZE(udc_freq_table); freq_sel++) {
> +               if (udc_freq_table[freq_sel] == parent_rate)
> +                       break;
> +       }
> +       if (freq_sel == ARRAY_SIZE(udc_freq_table)) {
> +               pr_err("invalid pll_u parent rate %ld\n", parent_rate);
> +               err = -EINVAL;
> +               goto err1;
> +       }
> +       phy->freq_sel = freq_sel;
> +
> +       if (phy->instance == 1) {
> +               ulpi_config = config;
> +               phy->clk = clk_get_sys(NULL, ulpi_config->clk);
> +               if (IS_ERR(phy->clk)) {
> +                       pr_err("%s: can't get ulpi clock\n", __func__);
> +                       err = -ENXIO;
> +                       goto err1;
> +               }
> +               tegra_gpio_enable(ulpi_config->reset_gpio);
> +               gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
> +               gpio_direction_output(ulpi_config->reset_gpio, 0);
> +       } else {
> +               err = utmip_pad_open(phy);
> +               if (err < 0)
> +                       goto err1;
> +       }
> +
> +       return phy;
> +
> +err1:
> +       clk_disable(phy->pll_u);
> +       clk_put(phy->pll_u);
> +err0:
> +       kfree(phy);
> +       return ERR_PTR(err);
> +}

[...]

The rest seems okay.

> --
> 1.7.3.1
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs
       [not found]     ` <1297228927-23497-2-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  7:05       ` rmorell-DDmLM1+adcrQT0dZR+AlfA
@ 2011-02-09  8:44       ` Matthieu CASTET
  1 sibling, 0 replies; 13+ messages in thread
From: Matthieu CASTET @ 2011-02-09  8:44 UTC (permalink / raw)
  To: Benoit Goby
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA

Hello,

Benoit Goby a écrit :
> Interface used by Tegra's gadget driver and ehci driver
> to power on and configure the USB PHYs.
> 
> + */
> +
> +#include <linux/resource.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/gpio.h>
> +#include <asm/mach-types.h>
> +#include <mach/usb_phy.h>
> +#include <mach/iomap.h>
> +
> +#define USB_USBSTS             0x144
> +#define   USB_USBSTS_PCI       (1 << 2)
> +
> +#define ULPI_VIEWPORT          0x170
It look like your core is a arc/chipidea/mips one that is already used 
on other product (msm) for example.

Can't you merge with them ?

for example
 > +static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
 > +{
 > +       unsigned long timeout = 2000;
 > +       do {
 > +               if ((readl(reg) & mask) == result)
 > +                       return 0;
 > +               udelay(1);
 > +               timeout--;
 > +       } while (timeout);
 > +       return -1;
 > +}
 > +static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, 
u8 data)
 > +{
 > +       unsigned long val;
 > +       void __iomem *base = phy->regs;
 > +
 > +       val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0);
 > +       val |= ULPI_ADDR(addr) | ULPI_DATA_WR(data);
 > +       writel(val, base + ULPI_VIEWPORT);
 > +
 > +       if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_RUN, 0))
 > +               pr_err("%s: timeout accessing ulpi phy\n", __func__);
 > +}

look very similar to what is in drivers/usb/otg/msm72k_otg.c


+static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
+{
+       struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+       int cnt = 0;
+
+       /* initiate write operation */
+       writel(ULPI_RUN | ULPI_WRITE |
+              ULPI_ADDR(reg) | ULPI_DATA(val),
+              USB_ULPI_VIEWPORT);
+
+       /* wait for completion */
+       while (cnt < ULPI_IO_TIMEOUT_USEC) {
+               if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
+                       break;
+               udelay(1);
+               cnt++;
+       }
+
+       if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+               dev_err(otg->dev, "ulpi_write: timeout\n");
+               return -ETIMEDOUT;
+       }
+       return 0;
+}


Matthieu
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs
       [not found]     ` <1297228927-23497-4-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
@ 2011-02-09  9:01       ` Matthieu CASTET
       [not found]         ` <4D5257FA.1030605-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
  2011-02-09 19:43       ` David Daney
  1 sibling, 1 reply; 13+ messages in thread
From: Matthieu CASTET @ 2011-02-09  9:01 UTC (permalink / raw)
  To: Benoit Goby
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA

Hi,

Benoit Goby a écrit :
> Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
_clk);
> +}
> +
> +static int tegra_ehci_hub_control(
> +       struct usb_hcd  *hcd,
> +       u16             typeReq,
> +       u16             wValue,
> +       u16             wIndex,
> +       char            *buf,
> +       u16             wLength
> +)
> +{
> +       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
> +       struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
> +       u32 __iomem     *status_reg;
> +       u32             temp;
> +       unsigned long   flags;
> +       int             retval = 0;
> +
> +       status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
> +
> +       spin_lock_irqsave(&ehci->lock, flags);
> +
> +       /*
> +        * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits
> +        * that are write on clear, by writing back the register read value, so
> +        * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits
> +        */
> +       if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) {
> +               temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
> +               ehci_writel(ehci, temp & ~PORT_PE, status_reg);
> +               goto done;
> +       }
> +
> +       else if (typeReq == GetPortStatus) {
> +               temp = ehci_readl(ehci, status_reg);
> +               if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
> +                       /* resume completed */
> +                       tegra->port_resuming = 0;
> +                       tegra_usb_phy_postresume(tegra->phy);
> +               }
> +       }
> +
> +       else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
> +               temp = ehci_readl(ehci, status_reg);
> +               if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
> +                       retval = -EPIPE;
> +                       goto done;
> +               }
> +
> +               /* After above check the port must be connected.
> +                * Set appropriate bit thus could put phy into low power
> +                * mode if we have hostpc feature
> +                */
> +               temp &= ~PORT_WKCONN_E;
> +               temp |= PORT_WKDISC_E | PORT_WKOC_E;
> +               ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
> +               if (handshake(ehci, status_reg, PORT_SUSPEND,
> +                                               PORT_SUSPEND, 5000))
> +                       pr_err("%s: timeout waiting for SUSPEND\n", __func__);
> +               set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
> +               goto done;
> +       }
> +
> +       /*
> +        * Tegra host controller will time the resume operation to clear the bit
> +        * when the port control state switches to HS or FS Idle. This behavior
> +        * is different from EHCI where the host controller driver is required
> +        * to set this bit to a zero after the resume duration is timed in the
> +        * driver.
> +        */
> +       else if (typeReq == ClearPortFeature &&
> +                                       wValue == USB_PORT_FEAT_SUSPEND) {
> +               temp = ehci_readl(ehci, status_reg);
> +               if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
> +                       retval = -EPIPE;
> +                       goto done;
> +               }
> +
> +               if (!(temp & PORT_SUSPEND))
> +                       goto done;
> +
> +               tegra_usb_phy_preresume(tegra->phy);
> +
> +               ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
> +
> +               temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
> +               /* start resume signalling */
> +               ehci_writel(ehci, temp | PORT_RESUME, status_reg);
> +
> +               spin_unlock_irqrestore(&ehci->lock, flags);
> +               msleep(20);
> +               spin_lock_irqsave(&ehci->lock, flags);
> +
> +               /* polling PORT_RESUME until the controller clear this bit */
> +               if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
> +                       pr_err("%s: timeout waiting for RESUME\n", __func__);
> +
> +               /* polling PORT_SUSPEND until the controller clear this bit */
> +               if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
> +                       pr_err("%s: timeout waiting for SUSPEND\n", __func__);
> +
> +               ehci->reset_done[wIndex-1] = 0;
> +
> +               tegra->port_resuming = 1;
> +               goto done;
> +       }
> +
> +       spin_unlock_irqrestore(&ehci->lock, flags);
> +
> +       /* Handle the hub control events here */
> +       return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
> +done:
> +       spin_unlock_irqrestore(&ehci->lock, flags);
> +       return retval;
> +}
> +
I don't know enough usb layer, but can't you done something similar than 
in msm otg : let's the otg layer handle the power management ?

http://www.spinics.net/lists/linux-usb/msg41247.html


> +static int tegra_ehci_reset(struct usb_hcd *hcd)
> +{
> +       unsigned long temp;
> +       int usec = 250*1000; /* see ehci_reset */
> +
> +       temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
> +       temp |= TEGRA_USB_USBCMD_RESET;
> +       writel(temp, hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
> +
> +       do {
> +               temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
> +               if (!(temp & TEGRA_USB_USBCMD_RESET))
> +                       break;
> +               udelay(1);
> +               usec--;
> +       } while (usec);
> +
> +       if (!usec)
> +               return -ETIMEDOUT;
> +
> +       /* Set to Host mode by setting bit 0-1 of USB device mode register */
> +       temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
> +       writel((temp | TEGRA_USB_USBMODE_HOST),
> +                       (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
> +
> +       return 0;
> +}
Why can't you use tdi_reset that already this for you ?



Matthieu
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk
       [not found]     ` <1297228927-23497-3-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
@ 2011-02-09 16:01       ` Matthieu CASTET
       [not found]         ` <4D52BA75.5010409-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 13+ messages in thread
From: Matthieu CASTET @ 2011-02-09 16:01 UTC (permalink / raw)
  To: Benoit Goby
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Gary King,
	Alan Stern

Benoit Goby a écrit :
> From: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> Tegra quirk: Resetting the controller has the side effect of resetting
> the PHY. Only reset the controller when doing so won't also reset the
> phy.
> 
> Signed-off-by: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/usb/host/ehci-hcd.c |    3 ++-
>  drivers/usb/host/ehci.h     |    1 +
>  2 files changed, 3 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> index 30515d3..7afa345 100644
> --- a/drivers/usb/host/ehci-hcd.c
> +++ b/drivers/usb/host/ehci-hcd.c
> @@ -275,7 +275,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
>  
>  	command |= CMD_RESET;
>  	dbg_cmd (ehci, "reset", command);
> -	ehci_writel(ehci, command, &ehci->regs->command);
> +	if (!ehci->controller_resets_phy)
> +		ehci_writel(ehci, command, &ehci->regs->command);
>  	ehci_to_hcd(ehci)->state = HC_STATE_HALT;
>  	ehci->next_statechange = jiffies;
>  	retval = handshake (ehci, &ehci->regs->command,
> diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
> index f86d3fa..8854491 100644
> --- a/drivers/usb/host/ehci.h
> +++ b/drivers/usb/host/ehci.h
> @@ -134,6 +134,7 @@ struct ehci_hcd {			/* one per controller */
>  	unsigned		amd_pll_fix:1;
>  	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
>  	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
> +	unsigned		controller_resets_phy:1; /* Tegra quirk */
>  
>  	/* required for usb32 quirk */
>  	#define OHCI_CTRL_HCFS          (3 << 6)
Instead of something that prevent reset, shouldn't tdi platform have a 
callback to restore phy config ?

And I think we should use ehci_is_TDI(ehci) condition. No need to 
introduce another flags. The problem is common to all tdi core.

Matthieu


PS : This may help to remove ehci_msm_run.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs
       [not found]     ` <1297228927-23497-4-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
  2011-02-09  9:01       ` Matthieu CASTET
@ 2011-02-09 19:43       ` David Daney
  1 sibling, 0 replies; 13+ messages in thread
From: David Daney @ 2011-02-09 19:43 UTC (permalink / raw)
  To: Benoit Goby
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA

On 02/08/2011 09:22 PM, Benoit Goby wrote:
> Signed-off-by: Benoit Goby<benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
> ---
>   drivers/usb/Kconfig           |    1 +
>   drivers/usb/host/Kconfig      |    8 +
>   drivers/usb/host/ehci-hcd.c   |    5 +
>   drivers/usb/host/ehci-tegra.c |  673 +++++++++++++++++++++++++++++++++++++++++
>   include/linux/tegra_usb.h     |   35 +++
>   5 files changed, 722 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/usb/host/ehci-tegra.c
>   create mode 100644 include/linux/tegra_usb.h
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index fceea5e..a2fa2a8 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -59,6 +59,7 @@ config USB_ARCH_HAS_EHCI
>   	default y if PPC_MPC512x
>   	default y if SOC_AU1200
>   	default y if ARCH_IXP4XX
> +	default y if ARCH_TEGRA
>   	default y if ARCH_W90X900

This doesn't belong here.

Do 'select USB_ARCH_HAS_EHCI' in your arch specific Kconfig instead.

David Daney
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk
       [not found]         ` <4D52BA75.5010409-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
@ 2011-02-10  2:46           ` Pavan Kondeti
  0 siblings, 0 replies; 13+ messages in thread
From: Pavan Kondeti @ 2011-02-10  2:46 UTC (permalink / raw)
  To: Matthieu CASTET
  Cc: Benoit Goby, David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Gary King,
	Alan Stern

Hi Matthieu,

On 2/9/2011 9:31 PM, Matthieu CASTET wrote:
> Benoit Goby a écrit :
>> From: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>
>> Tegra quirk: Resetting the controller has the side effect of resetting
>> the PHY. Only reset the controller when doing so won't also reset the
>> phy.
>>
>> Signed-off-by: Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Benoit Goby <benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
>> ---
>>  drivers/usb/host/ehci-hcd.c |    3 ++-
>>  drivers/usb/host/ehci.h     |    1 +
>>  2 files changed, 3 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
>> index 30515d3..7afa345 100644
>> --- a/drivers/usb/host/ehci-hcd.c
>> +++ b/drivers/usb/host/ehci-hcd.c
>> @@ -275,7 +275,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
>>  
>>  	command |= CMD_RESET;
>>  	dbg_cmd (ehci, "reset", command);
>> -	ehci_writel(ehci, command, &ehci->regs->command);
>> +	if (!ehci->controller_resets_phy)
>> +		ehci_writel(ehci, command, &ehci->regs->command);
>>  	ehci_to_hcd(ehci)->state = HC_STATE_HALT;
>>  	ehci->next_statechange = jiffies;
>>  	retval = handshake (ehci, &ehci->regs->command,
>> diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
>> index f86d3fa..8854491 100644
>> --- a/drivers/usb/host/ehci.h
>> +++ b/drivers/usb/host/ehci.h
>> @@ -134,6 +134,7 @@ struct ehci_hcd {			/* one per controller */
>>  	unsigned		amd_pll_fix:1;
>>  	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
>>  	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
>> +	unsigned		controller_resets_phy:1; /* Tegra quirk */
>>  
>>  	/* required for usb32 quirk */
>>  	#define OHCI_CTRL_HCFS          (3 << 6)
> Instead of something that prevent reset, shouldn't tdi platform have a 
> callback to restore phy config ?
> 
> And I think we should use ehci_is_TDI(ehci) condition. No need to 
> introduce another flags. The problem is common to all tdi core.
> 
> Matthieu
> 
> 
> PS : This may help to remove ehci_msm_run.

The reason for having ehci_msm_run is to configure some MSM specific
registers (not just the HC mode bit) after controller reset. In the
current code, there is no way HCD can register it's own tdi_reset
method. If we can have such mechanism, then ehci_msm_run() can be removed.

Thanks,
Pavan

-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs
       [not found]         ` <4D5257FA.1030605-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
@ 2011-02-12  0:59           ` Benoit Goby
  0 siblings, 0 replies; 13+ messages in thread
From: Benoit Goby @ 2011-02-12  0:59 UTC (permalink / raw)
  To: Matthieu CASTET
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Robert Morell, Olof Johansson,
	Erik Gilling, linux-tegra-u79uwXL29TY76Z2rM5mHXA

On Wed, Feb 9, 2011 at 1:01 AM, Matthieu CASTET
<matthieu.castet-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org> wrote:
> Benoit Goby a écrit :
>> +static int tegra_ehci_hub_control(
>> +       struct usb_hcd  *hcd,
>> +       u16             typeReq,
>> +       u16             wValue,
>> +       u16             wIndex,
>> +       char            *buf,
>> +       u16             wLength
>> +)
>> +{
>> +
>
> I don't know enough usb layer, but can't you done something similar than in
> msm otg : let's the otg layer handle the power management ?
>
> http://www.spinics.net/lists/linux-usb/msg41247.html
>

This function overrides ehci_hub_control to fix tegra specific quirks
when we suspend/resume the root hub. I don't see how this could be
handled at the otg level.

Benoit
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs
       [not found]         ` <20110209070540.GA17627-f3YH7lVHJt/FT5IIyIEb6QC/G2K4zDHf@public.gmane.org>
@ 2011-02-12  3:01           ` Benoit Goby
  0 siblings, 0 replies; 13+ messages in thread
From: Benoit Goby @ 2011-02-12  3:01 UTC (permalink / raw)
  To: rmorell-DDmLM1+adcrQT0dZR+AlfA
  Cc: David Brownell, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Olof Johansson, Erik Gilling,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

On Tue, Feb 8, 2011 at 11:05 PM,  <rmorell-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> wrote:
> On Tue, Feb 08, 2011 at 09:22:04PM -0800, Benoit Goby wrote:
>
>> +static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
>> +{
>> +       unsigned long val;
>> +       void __iomem *base = phy->regs;
>> +       struct tegra_ulpi_config *config = phy->config;
>> +
>> +       gpio_direction_output(config->reset_gpio, 0);
>> +       msleep(5);
>> +       gpio_direction_output(config->reset_gpio, 1);
>> +
>> +       clk_enable(phy->clk);
>> +       msleep(1);
>
> This msleep seems excessive.  Does it take that long for the clock to
> settle?
>

I tried to use smaller delays but got enumeration issues with delays
<1ms, so I'm keeping the msleep.

Benoit
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2011-02-12  3:01 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-09  5:22 [PATCH 0/4] Tegra EHCI driver Benoit Goby
     [not found] ` <1297228927-23497-1-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
2011-02-09  5:22   ` [PATCH 1/4] [ARM] tegra: Add support for Tegra USB PHYs Benoit Goby
     [not found]     ` <1297228927-23497-2-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
2011-02-09  7:05       ` rmorell-DDmLM1+adcrQT0dZR+AlfA
     [not found]         ` <20110209070540.GA17627-f3YH7lVHJt/FT5IIyIEb6QC/G2K4zDHf@public.gmane.org>
2011-02-12  3:01           ` Benoit Goby
2011-02-09  8:44       ` Matthieu CASTET
2011-02-09  5:22   ` [PATCH 2/4] usb: host: ehci-hcd: Add controller_resets_phy quirk Benoit Goby
     [not found]     ` <1297228927-23497-3-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
2011-02-09 16:01       ` Matthieu CASTET
     [not found]         ` <4D52BA75.5010409-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
2011-02-10  2:46           ` Pavan Kondeti
2011-02-09  5:22   ` [PATCH 3/4] usb: host: Add EHCI driver for NVIDIA Tegra SoCs Benoit Goby
     [not found]     ` <1297228927-23497-4-git-send-email-benoit-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
2011-02-09  9:01       ` Matthieu CASTET
     [not found]         ` <4D5257FA.1030605-ITF29qwbsa/QT0dZR+AlfA@public.gmane.org>
2011-02-12  0:59           ` Benoit Goby
2011-02-09 19:43       ` David Daney
2011-02-09  5:22   ` [PATCH 4/4] USB: ehci: tegra: Align DMA transfers to 32 bytes Benoit Goby

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.