All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lucas Stach <dev@lynxeye.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v3 2/2] tegra20: add USB ULPI init code
Date: Wed, 26 Sep 2012 00:35:32 +0200	[thread overview]
Message-ID: <1348612532-24419-3-git-send-email-dev@lynxeye.de> (raw)
In-Reply-To: <1348612532-24419-1-git-send-email-dev@lynxeye.de>

This adds the required code to set up a ULPI USB port. It is
mostly a port of the Linux ULPI setup code with some tweaks
added for more correctness, discovered along the way of
debugging this.

To use this both CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT
have to be set in the board configuration file.

v2:
- move all controller init stuff in the respective functions to
  make them self contained
- let board define ULPI_REF_CLK to account for the possibility
  that some ULPI phys need a other ref clk than 24MHz
- don't touch ULPI regs directly, use ULPI framework functions
- don't hide error messages under debug()

v3:
- apply last comments from Igor, which make code still a bit cleaner
- add description of CONFIG_ULPI_REF_CLK to README

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 README                                  |   3 +
 arch/arm/cpu/armv7/tegra20/usb.c        | 152 +++++++++++++++++++++++++++-----
 arch/arm/include/asm/arch-tegra20/usb.h |  29 ++++--
 3 Dateien ge?ndert, 157 Zeilen hinzugef?gt(+), 27 Zeilen entfernt(-)

diff --git a/README b/README
index 4428205..3919143 100644
--- a/README
+++ b/README
@@ -1239,6 +1239,9 @@ The following options need to be configured:
 		viewport is supported.
 		To enable the ULPI layer support, define CONFIG_USB_ULPI and
 		CONFIG_USB_ULPI_VIEWPORT in your board configuration file.
+		If your ULPI phy needs a different reference clock than the
+		standard 24 MHz then you have to define CONFIG_ULPI_REF_CLK to
+		the appropiate value in Hz.
 
 - MMC Support:
 		The MMC controller on the Intel PXA is supported. To
diff --git a/arch/arm/cpu/armv7/tegra20/usb.c b/arch/arm/cpu/armv7/tegra20/usb.c
index cac0918..86e52f6 100644
--- a/arch/arm/cpu/armv7/tegra20/usb.c
+++ b/arch/arm/cpu/armv7/tegra20/usb.c
@@ -32,9 +32,17 @@
 #include <asm/arch/sys_proto.h>
 #include <asm/arch/uart.h>
 #include <asm/arch/usb.h>
+#include <usb/ulpi.h>
 #include <libfdt.h>
 #include <fdtdec.h>
 
+#ifdef CONFIG_USB_ULPI
+	#ifndef CONFIG_USB_ULPI_VIEWPORT
+	#error	"To use CONFIG_USB_ULPI on Tegra Boards you have to also \
+			define CONFIG_USB_ULPI_VIEWPORT"
+	#endif
+#endif
+
 enum {
 	USB_PORTS_MAX	= 4,			/* Maximum ports we allow */
 };
@@ -68,11 +76,13 @@ enum dr_mode {
 struct fdt_usb {
 	struct usb_ctlr *reg;	/* address of registers in physical memory */
 	unsigned utmi:1;	/* 1 if port has external tranceiver, else 0 */
+	unsigned ulpi:1;	/* 1 if port has external ULPI transceiver */
 	unsigned enabled:1;	/* 1 to enable, 0 to disable */
 	unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
 	enum dr_mode dr_mode;	/* dual role mode */
 	enum periph_id periph_id;/* peripheral id */
 	struct fdt_gpio_state vbus_gpio;	/* GPIO for vbus enable */
+	struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */
 };
 
 static struct fdt_usb port[USB_PORTS_MAX];	/* List of valid USB ports */
@@ -188,8 +198,8 @@ void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr)
 	 */
 }
 
-/* set up the USB controller with the parameters provided */
-static int init_usb_controller(struct fdt_usb *config,
+/* set up the UTMI USB controller with the parameters provided */
+static int init_utmi_usb_controller(struct fdt_usb *config,
 				struct usb_ctlr *usbctlr, const u32 timing[])
 {
 	u32 val;
@@ -298,17 +308,114 @@ static int init_usb_controller(struct fdt_usb *config,
 	if (!loop_count)
 		return -1;
 
-	return 0;
-}
+	/* Disable ICUSB FS/LS transceiver */
+	clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
+
+	/* Select UTMI parallel interface */
+	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
+			PTS_UTMI << PTS_SHIFT);
+	clrbits_le32(&usbctlr->port_sc1, STS);
 
-static void power_up_port(struct usb_ctlr *usbctlr)
-{
 	/* Deassert power down state */
 	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
 		UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN);
 	clrbits_le32(&usbctlr->utmip_xcvr_cfg1, UTMIP_FORCE_PDDISC_POWERDOWN |
 		UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN);
+
+	return 0;
+}
+#ifdef CONFIG_USB_ULPI
+/* if board file does not set a ULPI reference frequency we default to 24MHz */
+#ifndef CONFIG_ULPI_REF_CLK
+#define CONFIG_ULPI_REF_CLK 24000000
+#endif
+
+/* set up the ULPI USB controller with the parameters provided */
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+				struct usb_ctlr *usbctlr)
+{
+	u32 val;
+	int loop_count;
+	struct ulpi_viewport ulpi_vp;
+
+	/* set up ULPI reference clock on pllp_out4 */
+	clock_enable(PERIPH_ID_DEV2_OUT);
+	clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK);
+
+	/* reset ULPI phy */
+	if (fdt_gpio_isvalid(&config->phy_reset_gpio)) {
+		fdtdec_setup_gpio(&config->phy_reset_gpio);
+		gpio_direction_output(config->phy_reset_gpio.gpio, 0);
+		mdelay(5);
+		gpio_set_value(config->phy_reset_gpio.gpio, 1);
+	}
+
+	/* Reset the usb controller */
+	clock_enable(config->periph_id);
+	usbf_reset_controller(config, usbctlr);
+
+	/* enable pinmux bypass */
+	setbits_le32(&usbctlr->ulpi_timing_ctrl_0,
+			ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
+
+	/* Select ULPI parallel interface */
+	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, PTS_ULPI << PTS_SHIFT);
+
+	/* enable ULPI transceiver */
+	setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
+
+	/* configure ULPI transceiver timings */
+	val = 0;
+	writel(val, &usbctlr->ulpi_timing_ctrl_1);
+
+	val |= ULPI_DATA_TRIMMER_SEL(4);
+	val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+	val |= ULPI_DIR_TRIMMER_SEL(4);
+	writel(val, &usbctlr->ulpi_timing_ctrl_1);
+	udelay(10);
+
+	val |= ULPI_DATA_TRIMMER_LOAD;
+	val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+	val |= ULPI_DIR_TRIMMER_LOAD;
+	writel(val, &usbctlr->ulpi_timing_ctrl_1);
+
+	/* set up phy for host operation with external vbus supply */
+	ulpi_vp.port_num = 0;
+	ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport;
+
+	if (ulpi_init(&ulpi_vp)) {
+		printf("Tegra ULPI viewport init failed\n");
+		return -1;
+	}
+
+	ulpi_set_vbus(&ulpi_vp, 1, 1);
+	ulpi_set_vbus_indicator(&ulpi_vp, 1, 1, 0);
+
+	/* enable wakeup events */
+	setbits_le32(&usbctlr->port_sc1, WKCN | WKDS | WKOC);
+
+	/* Enable and wait for the phy clock to become valid in 100 ms */
+	setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
+	for (loop_count = 100000; loop_count != 0; loop_count--) {
+		if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID)
+			break;
+		udelay(1);
+	}
+	if (!loop_count)
+		return -1;
+	clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
+
+	return 0;
 }
+#else
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+				struct usb_ctlr *usbctlr)
+{
+	printf("No code to set up ULPI controller, please enable"
+			"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
+	return -1;
+}
+#endif
 
 static void config_clock(const u32 timing[])
 {
@@ -328,24 +435,21 @@ static int add_port(struct fdt_usb *config, const u32 timing[])
 	struct usb_ctlr *usbctlr = config->reg;
 
 	if (port_count == USB_PORTS_MAX) {
-		debug("tegrausb: Cannot register more than %d ports\n",
+		printf("tegrausb: Cannot register more than %d ports\n",
 		      USB_PORTS_MAX);
 		return -1;
 	}
-	if (init_usb_controller(config, usbctlr, timing)) {
-		debug("tegrausb: Cannot init port\n");
+
+	if (config->utmi && init_utmi_usb_controller(config, usbctlr, timing)) {
+		printf("tegrausb: Cannot init port\n");
 		return -1;
 	}
-	if (config->utmi) {
-		/* Disable ICUSB FS/LS transceiver */
-		clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
-
-		/* Select UTMI parallel interface */
-		clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
-				PTS_UTMI << PTS_SHIFT);
-		clrbits_le32(&usbctlr->port_sc1, STS);
-		power_up_port(usbctlr);
+
+	if (config->ulpi && init_ulpi_usb_controller(config, usbctlr)) {
+		printf("tegrausb: Cannot init port\n");
+		return -1;
 	}
+
 	port[port_count++] = *config;
 
 	return 0;
@@ -412,6 +516,7 @@ int fdt_decode_usb(const void *blob, int node, unsigned osc_frequency_mhz,
 
 	phy = fdt_getprop(blob, node, "phy_type", NULL);
 	config->utmi = phy && 0 == strcmp("utmi", phy);
+	config->ulpi = phy && 0 == strcmp("ulpi", phy);
 	config->enabled = fdtdec_get_is_enabled(blob, node);
 	config->has_legacy_mode = fdtdec_get_bool(blob, node,
 						  "nvidia,has-legacy-mode");
@@ -421,10 +526,13 @@ int fdt_decode_usb(const void *blob, int node, unsigned osc_frequency_mhz,
 		return -FDT_ERR_NOTFOUND;
 	}
 	fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio);
-	debug("enabled=%d, legacy_mode=%d, utmi=%d, periph_id=%d, vbus=%d, "
-	      "dr_mode=%d\n", config->enabled, config->has_legacy_mode,
-	      config->utmi, config->periph_id, config->vbus_gpio.gpio,
-	      config->dr_mode);
+	fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio",
+			&config->phy_reset_gpio);
+	debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, "
+		"vbus=%d, phy_reset=%d, dr_mode=%d\n",
+		config->enabled, config->has_legacy_mode, config->utmi,
+		config->ulpi, config->periph_id, config->vbus_gpio.gpio,
+		config->phy_reset_gpio.gpio, config->dr_mode);
 
 	return 0;
 }
diff --git a/arch/arm/include/asm/arch-tegra20/usb.h b/arch/arm/include/asm/arch-tegra20/usb.h
index 638033b..bd89d66 100644
--- a/arch/arm/include/asm/arch-tegra20/usb.h
+++ b/arch/arm/include/asm/arch-tegra20/usb.h
@@ -100,10 +100,12 @@ struct usb_ctlr {
 
 	/* 0x410 */
 	uint usb1_legacy_ctrl;
-	uint reserved12[3];
+	uint reserved12[4];
 
-	/* 0x420 */
-	uint reserved13[56];
+	/* 0x424 */
+	uint ulpi_timing_ctrl_0;
+	uint ulpi_timing_ctrl_1;
+	uint reserved13[53];
 
 	/* 0x500 */
 	uint reserved14[64 * 3];
@@ -144,10 +146,24 @@ struct usb_ctlr {
 #define VBUS_SENSE_CTL_AB_SESS_VLD		2
 #define VBUS_SENSE_CTL_A_SESS_VLD		3
 
+/* USB2_IF_ULPI_TIMING_CTRL_0 */
+#define ULPI_OUTPUT_PINMUX_BYP			(1 << 10)
+#define ULPI_CLKOUT_PINMUX_BYP			(1 << 11)
+
+/* USB2_IF_ULPI_TIMING_CTRL_1 */
+#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)
+
 /* USBx_IF_USB_SUSP_CTRL_0 */
+#define ULPI_PHY_ENB				(1 << 13)
 #define UTMIP_PHY_ENB			        (1 << 12)
 #define UTMIP_RESET			        (1 << 11)
 #define USB_PHY_CLK_VALID			(1 << 7)
+#define USB_SUSP_CLR				(1 << 5)
 
 /* USBx_UTMIP_MISC_CFG1 */
 #define UTMIP_PLLU_STABLE_COUNT_SHIFT		6
@@ -203,12 +219,15 @@ struct usb_ctlr {
 /* SB2_CONTROLLER_2_USB2D_PORTSC1_0 */
 #define PTS_SHIFT				30
 #define PTS_MASK				(3U << PTS_SHIFT)
-#define PTS_UTMI	0
+#define PTS_UTMI		0
 #define PTS_RESERVED	1
-#define PTS_ULP		2
+#define PTS_ULPI		2
 #define PTS_ICUSB_SER	3
 
 #define STS					(1 << 29)
+#define WKOC				(1 << 22)
+#define WKDS				(1 << 21)
+#define WKCN				(1 << 20)
 
 /* USBx_UTMIP_XCVR_CFG0_0 */
 #define UTMIP_FORCE_PD_POWERDOWN		(1 << 14)
-- 
1.7.11.4

  parent reply	other threads:[~2012-09-25 22:35 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-09-25 22:35 [U-Boot] [PATCH v3 0/2] Tegra 2 USB ULPI series Lucas Stach
2012-09-25 22:35 ` [U-Boot] [PATCH v3 1/2] usb: ulpi: add indicator configuration function Lucas Stach
2012-09-28  8:15   ` Igor Grinberg
2012-09-28 14:46     ` Lucas Stach
2012-10-02  9:14       ` Igor Grinberg
2012-09-25 22:35 ` Lucas Stach [this message]
2012-09-28  8:24   ` [U-Boot] [PATCH v3 2/2] tegra20: add USB ULPI init code Igor Grinberg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1348612532-24419-3-git-send-email-dev@lynxeye.de \
    --to=dev@lynxeye.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.