]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - arch/arm/cpu/armv7/tegra20/usb.c
mmc:sdhci:fix: Change default interrupts enabled at SDHCI initialization
[karo-tx-uboot.git] / arch / arm / cpu / armv7 / tegra20 / usb.c
index 178bb130c28e7523762ffc66d2d8cfc0fac06c90..1bccf2b0b2c5bd82b22989f8c22f9bf96ec27757 100644 (file)
 #include <common.h>
 #include <asm/io.h>
 #include <asm-generic/gpio.h>
-#include <asm/arch/tegra20.h>
-#include <asm/arch/clk_rst.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/pinmux.h>
-#include <asm/arch/sys_proto.h>
-#include <asm/arch/uart.h>
+#include <asm/arch/tegra.h>
 #include <asm/arch/usb.h>
+#include <usb/ulpi.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/sys_proto.h>
+#include <asm/arch-tegra/uart.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,16 +76,17 @@ 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 */
 static unsigned port_count;                    /* Number of available ports */
-static int port_current;                       /* Current port (-1 = none) */
 
 /*
  * This table has USB timing parameters for each Oscillator frequency we
@@ -137,24 +146,29 @@ static const u8 utmip_elastic_limit = 16;
 /* UTMIP High Speed Sync Start Delay */
 static const u8 utmip_hs_sync_start_delay = 9;
 
-/* Put the port into host mode (this only works for OTG ports) */
+/* Put the port into host mode */
 static void set_host_mode(struct fdt_usb *config)
 {
-       if (config->dr_mode == DR_MODE_OTG) {
-               /* Check whether remote host from USB1 is driving VBus */
-               if (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)
-                       return;
-
-               /*
-                * If not driving, we set the GPIO to enable VBUS. We assume
-                * that the pinmux is set up correctly for this.
-                */
-               if (fdt_gpio_isvalid(&config->vbus_gpio)) {
-                       fdtdec_setup_gpio(&config->vbus_gpio);
-                       gpio_direction_output(config->vbus_gpio.gpio, 1);
-                       debug("set_host_mode: GPIO %d high\n",
-                             config->vbus_gpio.gpio);
-               }
+       /*
+        * If we are an OTG port, check if remote host is driving VBus and
+        * bail out in this case.
+        */
+       if (config->dr_mode == DR_MODE_OTG &&
+               (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS))
+               return;
+
+       /*
+        * If not driving, we set the GPIO to enable VBUS. We assume
+        * that the pinmux is set up correctly for this.
+        */
+       if (fdt_gpio_isvalid(&config->vbus_gpio)) {
+               fdtdec_setup_gpio(&config->vbus_gpio);
+               gpio_direction_output(config->vbus_gpio.gpio,
+                       (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
+                                0 : 1);
+               debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio,
+                       (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
+                               "low" : "high");
        }
 }
 
@@ -183,8 +197,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;
@@ -293,18 +307,116 @@ 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[])
 {
        clock_start_pll(CLOCK_ID_USB,
@@ -323,53 +435,45 @@ 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;
 }
 
-int tegrausb_start_port(unsigned portnum, u32 *hccr, u32 *hcor)
+int tegrausb_start_port(int portnum, u32 *hccr, u32 *hcor)
 {
        struct usb_ctlr *usbctlr;
 
        if (portnum >= port_count)
                return -1;
-       tegrausb_stop_port();
        set_host_mode(&port[portnum]);
 
        usbctlr = port[portnum].reg;
        *hccr = (u32)&usbctlr->cap_length;
        *hcor = (u32)&usbctlr->usb_cmd;
-       port_current = portnum;
        return 0;
 }
 
-int tegrausb_stop_port(void)
+int tegrausb_stop_port(int portnum)
 {
        struct usb_ctlr *usbctlr;
 
-       if (port_current == -1)
-               return -1;
-
-       usbctlr = port[port_current].reg;
+       usbctlr = port[portnum].reg;
 
        /* Stop controller */
        writel(0, &usbctlr->usb_cmd);
@@ -378,7 +482,7 @@ int tegrausb_stop_port(void)
        /* Initiate controller reset */
        writel(2, &usbctlr->usb_cmd);
        udelay(1000);
-       port_current = -1;
+
        return 0;
 }
 
@@ -407,6 +511,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");
@@ -416,10 +521,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;
 }
@@ -454,7 +562,6 @@ int board_usb_init(const void *blob)
                        return -1;
                set_host_mode(&config);
        }
-       port_current = -1;
 
        return 0;
 }