diff options
author | Simon Glass <sjg@chromium.org> | 2011-06-13 10:58:13 -0700 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2011-08-29 10:39:24 -0700 |
commit | 01d1f9a370e1917b772023a2501acebc72ee6ab5 (patch) | |
tree | 3061e9d759ebd93ef99e3276b13c93f26ea75001 | |
parent | f8081a7f4eceb454b55d387d7689933acd9f68c5 (diff) |
tegra2: Run-time assignment of USB port numbers
USB ports relied on CONFIG_TEGRA2_USBx macros to select the ordering.
This change records the order that the ports are configured and uses
that to select ports by number. This properly honors CONFIG_TEGRA2_USBx
when not using an FDT.
Also removed direct access to the USB peripheral address from the Tegra2
EHCI driver.
BUG=chromium-os:11623
TEST=usb start; run usb_boot
Change-Id: Ib906ec4483bcd95ff9564411f44dc0a2e48fff8c
Reviewed-on: http://gerrit.chromium.org/gerrit/2537
Tested-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Anton Staaf <robotboy@chromium.org>
Reviewed-by: Tom Warren <twarren@nvidia.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/usb.h | 19 | ||||
-rw-r--r-- | board/nvidia/common/usb.c | 107 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 20 |
3 files changed, 130 insertions, 16 deletions
diff --git a/arch/arm/include/asm/arch-tegra2/usb.h b/arch/arm/include/asm/arch-tegra2/usb.h index 2e3bf752ca7..bbbe3c70dd4 100644 --- a/arch/arm/include/asm/arch-tegra2/usb.h +++ b/arch/arm/include/asm/arch-tegra2/usb.h @@ -214,4 +214,23 @@ void usb_set_host_mode(void); /* Setup USB on the board */ int board_usb_init(const void *blob); +/** + * Start up the given port number (ports are numbered from 0 on each board). + * This returns values for the appropriate hccr and hcor addresses to use for + * USB EHCI operations. + * + * @param portnum port number to start + * @param hccr returns start address of EHCI HCCR registers + * @param hcor returns start address of EHCI HCOR registers + * @return 0 if ok, -1 on error (generally invalid port number) + */ +int tegrausb_start_port(unsigned portnum, u32 *hccr, u32 *hcor); + +/** + * Stop the current port + * + * @return 0 if ok, -1 if no port was active + */ +int tegrausb_stop_port(void); + #endif /* _TEGRA_USB_H_ */ diff --git a/board/nvidia/common/usb.c b/board/nvidia/common/usb.c index 5f4f345fd16..1838b0f4385 100644 --- a/board/nvidia/common/usb.c +++ b/board/nvidia/common/usb.c @@ -35,6 +35,18 @@ #include <asm/arch/usb.h> #include <fdt_decode.h> +enum { + USB_PORTS_MAX = 4, /* Maximum ports we allow */ +}; + +struct usb_port { + struct usb_ctlr *reg; +}; + +static struct usb_port 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) */ + /* Record which controller can switch from host to device mode */ static struct usb_ctlr *host_dev_ctlr; @@ -276,9 +288,23 @@ static void config_clock(const int params[]) /* TODO: what should we do with stable_time? */ } -static void config_port(enum periph_id id, struct usb_ctlr *usbctlr, +/** + * Add a new USB port to the list of available ports + * + * @param id peripheral id of port (PERIPH_ID_USB3, for example) + * @param usbctlr register address of controller + * @param params timing parameters + * @param utmi 1 if it has an external UTMI transceiver + * @return 0 if ok, -1 if error (too many ports) + */ +static int add_port(enum periph_id id, struct usb_ctlr *usbctlr, const int params[], int utmi) { + if (port_count == USB_PORTS_MAX) { + debug("tegrausb: Cannot register more than %d ports\n", + USB_PORTS_MAX); + return -1; + } init_usb_controller(id, usbctlr, params); if (utmi) { /* Disable ICUSB FS/LS transceiver */ @@ -289,6 +315,72 @@ static void config_port(enum periph_id id, struct usb_ctlr *usbctlr, bf_writel(STS, 0, &usbctlr->port_sc1); power_up_port(usbctlr); } + port[port_count++].reg = usbctlr; + return 0; +} + +#ifndef CONFIG_OF_CONTROL +static int probe_port(struct usb_ctlr *usbctlr, const int params[]) +{ + enum periph_id id; + int utmi = 0; + + /* + * Get the periph id. Port 1 has an internal transceiver, port 3 is + * external + */ + switch ((u32)usbctlr) { + case NV_PA_USB1_BASE: + id = PERIPH_ID_USBD; + break; + + case NV_PA_USB3_BASE: + id = PERIPH_ID_USB3; + utmi = 1; + break; + + default: + printf("tegrausb: probe_port: no such port %p\n", usbctlr); + return -1; + } + + return add_port(id, usbctlr, params, utmi); +} +#endif + +int tegrausb_start_port(unsigned portnum, u32 *hccr, u32 *hcor) +{ + struct usb_ctlr *usbctlr; + + if (portnum >= port_count) + return -1; + tegrausb_stop_port(); + + usbctlr = port[portnum].reg; + *hccr = (u32)&usbctlr->cap_length; + *hcor = (u32)&usbctlr->usb_cmd; + port_current = portnum; + return 0; +} + +int tegrausb_stop_port(void) +{ + struct usb_ctlr *usbctlr; + + if (port_current == -1) + return -1; + + usbctlr = port[port_current].reg; + + /* Stop controller */ + writel(0, &usbctlr->usb_cmd); + udelay(1000); + + /* Initiate controller reset */ + writel(2, &usbctlr->usb_cmd); + udelay(1000); + port_current = -1; + return 0; } int board_usb_init(const void *blob) @@ -320,8 +412,9 @@ int board_usb_init(const void *blob) return -1; host_dev_ctlr = config.reg; } - config_port(config.periph_id, config.reg, config.params, - config.utmi); + if (add_port(config.periph_id, config.reg, config.params, + config.utmi)) + return -1; } while (node); #else enum clock_osc_freq freq; @@ -338,12 +431,10 @@ int board_usb_init(const void *blob) #ifdef CONFIG_TEGRA2_USB1_HOST host_dev_ctlr = (struct usb_ctlr *)NV_PA_USB1_BASE; #endif - /* Port 1 has an internal transceiver, port 3 is external */ - config_port(PERIPH_ID_USBD, (struct usb_ctlr *)NV_PA_USB1_BASE, - params, 0); - config_port(PERIPH_ID_USB3, (struct usb_ctlr *)NV_PA_USB3_BASE, - params, 1); + probe_port((struct usb_ctlr *)CONFIG_TEGRA2_USB0, params); + probe_port((struct usb_ctlr *)CONFIG_TEGRA2_USB1, params); #endif /* CONFIG_OF_CONTROL */ usb_set_host_mode(); + port_current = -1; return 0; } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 553e0ebf7c3..3a48c26cdc3 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -36,10 +36,17 @@ */ int ehci_hcd_init(void) { - /* EHCI registers start at offset 0x100. For now support only port 0*/ - hccr = (struct ehci_hccr *)(CONFIG_TEGRA2_USB0 + 0x100); - hcor = (struct ehci_hcor *)((uint32_t) hccr - + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + u32 our_hccr, our_hcor; + + /* + * Select the first port, as we don't have a way of selecting others + * yet + */ + if (tegrausb_start_port(0, &our_hccr, &our_hcor)) + return -1; + + hccr = (struct ehci_hccr *)our_hccr; + hcor = (struct ehci_hcor *)our_hcor; return 0; } @@ -51,9 +58,6 @@ int ehci_hcd_init(void) int ehci_hcd_stop(void) { usb_set_host_mode(); - ehci_writel(&hcor->or_usbcmd, 0); - udelay(1000); - ehci_writel(&hcor->or_usbcmd, 2); - udelay(1000); + tegrausb_stop_port(); return 0; } |