[U-Boot] [PATCH 1/1] USB: EHCI: Initialize multiple USB controllers at once

Add support for command line "usb reset" or "usb start" to initialize , "usb stop" to stop multiple USB controllers at once. Other commands like "usb tree" also support multiple controllers.
New added definitions to be defined in header file are: CONFIG_USB_INIT_MULTI CONFIG_USB_MAX_CONTROLLER_COUNT
Signed-off-by: Jim Lin jilin@nvidia.com --- common/cmd_usb.c | 10 +++ common/usb.c | 100 +++++++++++++++++++++++++++- common/usb_hub.c | 4 + drivers/usb/host/ehci-hcd.c | 150 +++++++++++++++++++++++++++++----------- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci.h | 5 ++ include/usb.h | 10 +++ 7 files changed, 237 insertions(+), 44 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 9eba271..4c01a78 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -553,7 +553,17 @@ int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } if (strncmp(argv[1], "tree", 4) == 0) { printf("\nDevice Tree:\n"); +#ifdef CONFIG_USB_INIT_MULTI + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); + if (dev == NULL) + break; + if (dev->parent == NULL) + usb_show_tree(dev); + } +#else usb_show_tree(usb_get_dev_index(0)); +#endif return 0; } if (strncmp(argv[1], "inf", 3) == 0) { diff --git a/common/usb.c b/common/usb.c index 1ec30bc..9fb0407 100644 --- a/common/usb.c +++ b/common/usb.c @@ -64,6 +64,10 @@ #define USB_HUB_DEBUG 0 #endif
+#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + #define USB_PRINTF(fmt, args...) debug_cond(USB_DEBUG, fmt, ##args) #define USB_HUB_PRINTF(fmt, args...) debug_cond(USB_HUB_DEBUG, fmt, ##args)
@@ -81,6 +85,86 @@ char usb_started; /* flag for the started/stopped USB status */ */ static void usb_scan_devices(void);
+#ifdef CONFIG_USB_INIT_MULTI +/*************************************************************************** + * Init USB Device + */ + +int usb_init(void) +{ + void *ctrl; + int i; + struct usb_device *dev; + + running = 0; + dev_index = 0; + asynch_allowed = 1; + usb_hub_reset(); + + /* first make all devices unknown */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + memset(&usb_dev[i], 0, sizeof(struct usb_device)); + usb_dev[i].devnum = -1; + } + + /* init low_level USB */ + printf("USB: "); + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + /* init low_level USB */ + ctrl = usb_lowlevel_init(i); + /* + * if lowlevel init is OK, scan the bus for devices + * i.e. search HUBs and configure them + */ + if (ctrl) { + running = 1; + + printf("scanning bus for devices... "); + dev = usb_alloc_new_device(ctrl); + /* + * device 0 is always present + * (root hub, so let it analyze) + */ + if (dev) + usb_new_device(dev); + } + } + + if (running) { + if (!dev_index) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", dev_index); +#ifdef CONFIG_USB_KEYBOARD + drv_usb_kbd_init(); +#endif + USB_PRINTF("scan end\n"); + usb_started = 1; + return 0; + } else { + printf("Error, couldn't init Lowlevel part\n"); + usb_started = 0; + return -1; + } +} + +/****************************************************************************** + * Stop USB this stops the LowLevel Part and deregisters USB devices. + */ +int usb_stop(void) +{ + int i; + + if (usb_started) { + asynch_allowed = 1; + usb_started = 0; + usb_hub_reset(); + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) + usb_lowlevel_stop(i); + } + return 0; +} +#else /*************************************************************************** * Init USB Device */ @@ -126,6 +210,7 @@ int usb_stop(void) } return res; } +#endif
/* * disables the asynch behaviour of the control message. This is used for data @@ -747,11 +832,18 @@ struct usb_device *usb_get_dev_index(int index) return &usb_dev[index]; }
- +#ifdef CONFIG_USB_INIT_MULTI +/* Save input pointer 'controller' into device structure. + * returns a pointer of a new device structure or NULL, if + * no device struct is available + */ +struct usb_device *usb_alloc_new_device(void *controller) +#else /* returns a pointer of a new device structure or NULL, if * no device struct is available */ struct usb_device *usb_alloc_new_device(void) +#endif { int i; USB_PRINTF("New Device %d\n", dev_index); @@ -765,11 +857,13 @@ struct usb_device *usb_alloc_new_device(void) for (i = 0; i < USB_MAXCHILDREN; i++) usb_dev[dev_index].children[i] = NULL; usb_dev[dev_index].parent = NULL; +#ifdef CONFIG_USB_INIT_MULTI + usb_dev[dev_index].controller = controller; +#endif dev_index++; return &usb_dev[dev_index - 1]; }
- /* * By the time we get here, the device has gotten a new device ID * and is in the default state. We need to identify the thing and @@ -940,6 +1034,7 @@ int usb_new_device(struct usb_device *dev) return 0; }
+#ifndef CONFIG_USB_INIT_MULTI /* build device Tree */ static void usb_scan_devices(void) { @@ -964,5 +1059,6 @@ static void usb_scan_devices(void) #endif USB_PRINTF("scan end\n"); } +#endif
/* EOF */ diff --git a/common/usb_hub.c b/common/usb_hub.c index e0edaad..6c27761 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -243,7 +243,11 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port) mdelay(200);
/* Allocate a new device struct for it */ +#ifdef CONFIG_USB_INIT_MULTI + usb = usb_alloc_new_device(dev->controller); +#else usb = usb_alloc_new_device(); +#endif
if (portstatus & USB_PORT_STAT_HIGH_SPEED) usb->speed = USB_SPEED_HIGH; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 38d6ae0..35e4913 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -29,13 +29,23 @@
#include "ehci.h"
-int rootdev; +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +static struct ehci_ctrl { + struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ + volatile struct ehci_hcor *hcor; + int rootdev; + uint16_t portreset; + struct QH qh_list __attribute__((aligned(32))); + struct QH qh_pool __attribute__((aligned(32))); + struct qTD td_pool[3] __attribute__((aligned (32))); + int ntds; +} ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ volatile struct ehci_hcor *hcor;
-static uint16_t portreset; -static struct QH qh_list __attribute__((aligned(32))); - static struct descriptor { struct usb_hub_descriptor hub; struct usb_device_descriptor device; @@ -266,25 +276,22 @@ out: return ret; }
-static void *ehci_alloc(size_t sz, size_t align) +static void *ehci_alloc(struct ehci_ctrl *ctrl, size_t sz, size_t align) { - static struct QH qh __attribute__((aligned(32))); - static struct qTD td[3] __attribute__((aligned (32))); - static int ntds; void *p;
switch (sz) { case sizeof(struct QH): - p = &qh; - ntds = 0; + p = &ctrl->qh_pool; + ctrl->ntds = 0; break; case sizeof(struct qTD): - if (ntds == 3) { + if (ctrl->ntds == 3) { debug("out of TDs\n"); return NULL; } - p = &td[ntds]; - ntds++; + p = &ctrl->td_pool[ctrl->ntds]; + ctrl->ntds++; break; default: debug("unknown allocation size\n"); @@ -336,6 +343,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t cmd; int timeout; int ret = 0; + struct ehci_ctrl *ctrl; +#ifdef CONFIG_USB_INIT_MULTI + ctrl = dev->controller; +#else + ctrl = &ehcic[0]; +#endif + hcor = ctrl->hcor;
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -346,12 +360,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index));
- qh = ehci_alloc(sizeof(struct QH), 32); + qh = (struct QH *) ehci_alloc(ctrl, sizeof(struct QH), 32); if (qh == NULL) { debug("unable to allocate QH\n"); return -1; } - qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | @@ -376,7 +390,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); + td = ehci_alloc(ctrl, sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate SETUP td\n"); goto fail; @@ -398,7 +412,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, }
if (length > 0 || req == NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); + td = ehci_alloc(ctrl, sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate DATA td\n"); goto fail; @@ -422,7 +436,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, }
if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); + td = ehci_alloc(ctrl, sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate ACK td\n"); goto fail; @@ -440,10 +454,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, tdp = &td->qt_next; }
- qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); + ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
/* Flush dcache */ - ehci_flush_dcache(&qh_list); + ehci_flush_dcache(&ctrl->qh_list);
usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); @@ -466,7 +480,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ - ehci_invalidate_dcache(&qh_list); + ehci_invalidate_dcache(&ctrl->qh_list); token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; @@ -490,7 +504,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, goto fail; }
- qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list + | QH_LINK_TYPE_QH);
token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(token & 0x80)) { @@ -561,6 +576,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int len, srclen; uint32_t reg; uint32_t *status_reg; + struct ehci_ctrl *ctrl; +#ifdef CONFIG_USB_INIT_MULTI + ctrl = dev->controller; +#else + ctrl = &ehcic[0]; +#endif + hcor = ctrl->hcor; + hccr = ctrl->hccr;
if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { printf("The request port(%d) is not configured\n", @@ -633,7 +656,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): debug("USB_REQ_SET_ADDRESS\n"); - rootdev = le16_to_cpu(req->value); + ctrl->rootdev = le16_to_cpu(req->value); break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: debug("USB_REQ_SET_CONFIGURATION\n"); @@ -683,7 +706,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; if (reg & EHCI_PS_OCC) tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; - if (portreset & (1 << le16_to_cpu(req->index))) + if (ctrl->portreset & (1 << le16_to_cpu(req->index))) tmpbuf[2] |= USB_PORT_STAT_C_RESET;
srcptr = tmpbuf; @@ -735,7 +758,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) - portreset |= + ctrl->portreset |= 1 << le16_to_cpu(req->index); else printf("port(%d) reset error\n", @@ -768,7 +791,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: - portreset &= ~(1 << le16_to_cpu(req->index)); + ctrl->portreset &= ~(1 << le16_to_cpu(req->index)); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); @@ -804,39 +827,64 @@ unknown: return -1; }
+#ifdef CONFIG_USB_INIT_MULTI +int usb_lowlevel_stop(int index) +{ + return ehci_hcd_stop(index); +} +#else int usb_lowlevel_stop(void) { return ehci_hcd_stop(); } +#endif
-int usb_lowlevel_init(void) +void *usb_lowlevel_multi_init(int index) { uint32_t reg; uint32_t cmd; + struct QH *qh_list; + +#ifdef CONFIG_USB_INIT_MULTI + if (ehci_hcd_init(index, &hccr, (struct ehci_hcor **)&hcor) != 0) + return NULL; + + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return NULL;
+#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + if (ehci_hcd_init(index, &hccr, (struct ehci_hcor **)&hcor) != 0) + return NULL; +#endif +#else if (ehci_hcd_init() != 0) - return -1; + return NULL;
/* EHCI spec section 4.1 */ if (ehci_reset() != 0) - return -1; + return NULL;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) if (ehci_hcd_init() != 0) - return -1; + return NULL; #endif +#endif + ehcic[index].hccr = hccr; + ehcic[index].hcor = hcor; + qh_list = &ehcic[index].qh_list;
/* Set head of reclaim list */ - memset(&qh_list, 0, sizeof(qh_list)); - qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); - qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); + memset(qh_list, 0, sizeof(*qh_list)); + qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); + qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40);
/* Set async. queue head pointer. */ - ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list);
reg = ehci_readl(&hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); @@ -868,10 +916,24 @@ int usb_lowlevel_init(void) reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
- rootdev = 0; + ehcic[index].rootdev = 0; + + return ehcic + index; +}
+#ifdef CONFIG_USB_INIT_MULTI +void *usb_lowlevel_init(int index) +{ + return usb_lowlevel_multi_init(index); +} +#else +int usb_lowlevel_init(void) +{ + if (usb_lowlevel_multi_init(0) == NULL) + return -1; return 0; } +#endif
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, @@ -889,14 +951,20 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) { + struct ehci_ctrl *ctrl;
if (usb_pipetype(pipe) != PIPE_CONTROL) { debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); return -1; }
- if (usb_pipedevice(pipe) == rootdev) { - if (rootdev == 0) +#ifdef CONFIG_USB_INIT_MULTI + ctrl = dev->controller; +#else + ctrl = &ehcic[0]; +#endif + if (usb_pipedevice(pipe) == ctrl->rootdev) { + if (ctrl->rootdev == 0) dev->speed = USB_SPEED_HIGH; return ehci_submit_root(dev, pipe, buffer, length, setup); } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a7e105b..f7fd6a7 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 NVIDIA Corporation + * Copyright (c) 2009-2012 NVIDIA Corporation * * See file CREDITS for list of people who contributed to this * project. diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc00ce4..059827a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -201,7 +201,12 @@ struct QH { };
/* Low level init functions */ +#ifdef CONFIG_USB_INIT_MULTI +int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor); +int ehci_hcd_stop(int index); +#else int ehci_hcd_init(void); int ehci_hcd_stop(void); +#endif
#endif /* USB_EHCI_H */ diff --git a/include/usb.h b/include/usb.h index 48e4bcd..0ac6ed4 100644 --- a/include/usb.h +++ b/include/usb.h @@ -128,6 +128,7 @@ struct usb_device { int portnr; struct usb_device *parent; struct usb_device *children[USB_MAXCHILDREN]; + void *controller; /* hardware controller private data */ };
/********************************************************************** @@ -141,8 +142,13 @@ struct usb_device { defined(CONFIG_USB_OMAP3) || defined(CONFIG_USB_DA8XX) || \ defined(CONFIG_USB_BLACKFIN) || defined(CONFIG_USB_AM35X)
+#ifdef CONFIG_USB_INIT_MULTI +void *usb_lowlevel_init(int index); +int usb_lowlevel_stop(int index); +#else int usb_lowlevel_init(void); int usb_lowlevel_stop(void); +#endif int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len); int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, @@ -370,7 +376,11 @@ void usb_hub_reset(void); int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat);
+#ifdef CONFIG_USB_INIT_MULTI +struct usb_device *usb_alloc_new_device(void *controller); +#else struct usb_device *usb_alloc_new_device(void); +#endif int usb_new_device(struct usb_device *dev);
#endif /*_USB_H_ */ -- 1.7.3
nvpublic

Dear Jim Lin,
In message 4B9C9637D5087840A465BDCB251780E9E2D6EDA3EB@HKMAIL02.nvidia.com you wrote:
Add support for command line "usb reset" or "usb start" to initialize , "usb stop" to stop multiple USB controllers at once. Other commands like "usb tree" also support multiple controllers.
Why would this be needed?
U-Boot is not multi-tasking, so you can always access only a single USB device at a time ayway. And it is a decoumented design principle that U-Boot must not initialize any devices it does not use itself.
So why?
Best regards,
Wolfgang Denk
participants (2)
-
Jim Lin
-
Wolfgang Denk