[U-Boot] [PATCH v2 5/5] tegra20: add USB ULPI init code
Lucas Stach
dev at lynxeye.de
Tue Aug 21 22:18:57 CEST 2012
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()
Signed-off-by: Lucas Stach <dev at lynxeye.de>
---
arch/arm/cpu/armv7/tegra20/usb.c | 154 +++++++++++++++++++++++++++-----
arch/arm/include/asm/arch-tegra20/usb.h | 29 ++++--
2 Dateien geändert, 155 Zeilen hinzugefügt(+), 28 Zeilen entfernt(-)
diff --git a/arch/arm/cpu/armv7/tegra20/usb.c b/arch/arm/cpu/armv7/tegra20/usb.c
index 77966e5..351300b 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 */
@@ -187,8 +197,8 @@ static void usbf_reset_controller(struct fdt_usb *config,
*/
}
-/* 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;
@@ -297,16 +307,109 @@ 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;
+}
+
+/* set up the ULPI USB controller with the parameters provided */
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+ struct usb_ctlr *usbctlr)
+{
+#ifdef CONFIG_USB_ULPI
+/* if board file does not set a ULPI reference frequency we default to 24MHz */
+#ifndef ULPI_REF_CLK
+#define ULPI_REF_CLK 24000000
+#endif
+ 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, 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);
+
+ setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
+ /* Wait for the phy clock to become valid in 100 ms */
+ 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
+ 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[])
@@ -327,24 +430,25 @@ 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");
- 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 (init_utmi_usb_controller(config, usbctlr, timing)) {
+ printf("tegrausb: Cannot init port\n");
+ return -1;
+ }
+ }
+
+ if (config->ulpi) {
+ if (init_ulpi_usb_controller(config, usbctlr)) {
+ printf("tegrausb: Cannot init port\n");
+ return -1;
+ }
}
+
port[port_count++] = *config;
return 0;
@@ -411,6 +515,7 @@ static int fdt_decode_usb(const void *blob, int node,
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");
@@ -420,10 +525,13 @@ static int fdt_decode_usb(const void *blob, int node,
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
More information about the U-Boot
mailing list