From a7eafcfe45427235804ef60e9983a84fbce3ea07 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:27 +0200 Subject: usb: mv_udc: Unbreak the mv_udc driver The mv_udc driver is broken for a while and doesn't even compile. This patch fixes the issues and gets the driver into working state again. This driver was tested on Freescale i.MX233/i.MX28 . Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/serial/usbtty.h | 2 ++ drivers/usb/gadget/gadget_chips.h | 2 +- drivers/usb/gadget/mv_udc.c | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index bbabb32..e243a8e 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -22,6 +22,8 @@ #include #elif defined(CONFIG_DW_UDC) #include +#elif defined(CONFIG_MV_UDC) +#include #endif #include diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f038747..aa54b85 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -144,7 +144,7 @@ #define gadget_is_m66592(g) 0 #endif -#ifdef CONFIG_USB_GADGET_MV +#ifdef CONFIG_MV_UDC #define gadget_is_mv(g) (!strcmp("mv_udc", (g)->name)) #else #define gadget_is_mv(g) 0 diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 7fa5288..93e5389 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -17,6 +17,10 @@ #include #include +#if CONFIG_USB_MAX_CONTROLLER_COUNT > 1 +#error This driver only supports one single controller. +#endif + #ifndef DEBUG #define DBG(x...) do {} while (0) #else @@ -453,6 +457,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) { struct mv_udc *udc = controller.udc; int retval; + void *ctrl; if (!driver || driver->speed < USB_SPEED_FULL @@ -463,7 +468,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) } if (!mvudc_probe()) { - usb_lowlevel_init(); + usb_lowlevel_init(0, &ctrl); /* select ULPI phy */ writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc); } -- cgit v1.1 From 6368c9194585ca7a1e35fb6806ff7d062e16299f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:28 +0200 Subject: usb: mv_udc: Clean up mv_udc.h Do a coding-style cleanup of this file and throw away useless defined values. These values were likely a result of a copy-paste job. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- include/usb/mv_udc.h | 60 ++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index c7696e6..f7405e9 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -14,28 +14,14 @@ #include #include -/* Endpoint 0 states */ -#define EP0_IDLE 0 -#define EP0_IN_DATA 1 -#define EP0_OUT_DATA 2 -#define EP0_XFER_COMPLETE 3 - +#define NUM_ENDPOINTS 6 /* Endpoint parameters */ #define MAX_ENDPOINTS 4 -#define EP_MAX_PACKET_SIZE 0x200 +#define EP_MAX_PACKET_SIZE 0x200 #define EP0_MAX_PACKET_SIZE 64 -#define UDC_OUT_ENDPOINT 0x02 -#define UDC_OUT_PACKET_SIZE EP_MAX_PACKET_SIZE -#define UDC_IN_ENDPOINT 0x01 -#define UDC_IN_PACKET_SIZE EP_MAX_PACKET_SIZE -#define UDC_INT_ENDPOINT 0x05 -#define UDC_INT_PACKET_SIZE EP_MAX_PACKET_SIZE -#define UDC_BULK_PACKET_SIZE EP_MAX_PACKET_SIZE - -#define NUM_ENDPOINTS 6 -#define REQ_COUNT 12 + struct mv_ep { struct usb_ep ep; struct usb_request req; @@ -46,7 +32,7 @@ struct mv_ep { struct mv_udc { u32 pad0[80]; #define MICRO_8FRAME 0x8 -#define USBCMD_ITC(x) (((x > 0xff) ? 0xff : x) << 16) +#define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16) #define USBCMD_FS2 (1 << 15) #define USBCMD_RST (1 << 1) #define USBCMD_RUN (1) @@ -62,25 +48,25 @@ struct mv_udc { u32 epinitaddr; /* 0x158 */ u32 pad2[10]; #define PTS_ENABLE 2 -#define PTS(x) ((x & 0x3) << 30) +#define PTS(x) (((x) & 0x3) << 30) #define PFSC (1 << 24) u32 portsc; /* 0x184 */ u32 pad3[8]; #define USBMODE_DEVICE 2 u32 usbmode; /* 0x1a8 */ u32 epstat; /* 0x1ac */ -#define EPT_TX(x) (1 << ((x & 0xffff) + 16)) -#define EPT_RX(x) (1 << (x & 0xffff)) +#define EPT_TX(x) (1 << (((x) & 0xffff) + 16)) +#define EPT_RX(x) (1 << ((x) & 0xffff)) u32 epprime; /* 0x1b0 */ u32 epflush; /* 0x1b4 */ u32 pad4; u32 epcomp; /* 0x1bc */ -#define CTRL_TXE (1 << 23) -#define CTRL_TXR (1 << 22) -#define CTRL_RXE (1 << 7) -#define CTRL_RXR (1 << 6) -#define CTRL_TXT_BULK (2 << 18) -#define CTRL_RXT_BULK (2 << 2) +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_RXT_BULK (2 << 2) u32 epctrl[16]; /* 0x1c0 */ }; @@ -92,7 +78,7 @@ struct mv_drv { struct ept_queue_head { unsigned config; - unsigned current; /* read-only */ + unsigned current; /* read-only */ unsigned next; unsigned info; @@ -111,9 +97,9 @@ struct ept_queue_head { unsigned reserved_4; }; -#define CONFIG_MAX_PKT(n) ((n) << 16) -#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ -#define CONFIG_IOS (1 << 15) /* IRQ on setup */ +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ struct ept_queue_item { unsigned next; @@ -127,12 +113,12 @@ struct ept_queue_item { }; #define TERMINATE 1 -#define INFO_BYTES(n) ((n) << 16) -#define INFO_IOC (1 << 15) -#define INFO_ACTIVE (1 << 7) -#define INFO_HALTED (1 << 6) -#define INFO_BUFFER_ERROR (1 << 5) -#define INFO_TX_ERROR (1 << 3) +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TX_ERROR (1 << 3) extern int usb_lowlevel_init(int index, void **controller); #endif /* __MV_UDC_H__ */ -- cgit v1.1 From 532d846f891f31e2582428d7572b98103e3ec268 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:29 +0200 Subject: usb: mv_udc: Move endpoint array into driver data The endpoints are operated on a per-controller basis, move the endpoint array into controller's private data. Also shuffle the struct mv_ep structure definition just above the definition of the struct mv_drv so they're well grouped together. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 48 ++++++++++++++++++++++----------------------- include/usb/mv_udc.h | 17 ++++++++-------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 93e5389..a667cf9 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -83,10 +83,8 @@ static struct usb_ep_ops mv_ep_ops = { .free_request = mv_ep_free_request, }; -static struct mv_ep ep[2 * NUM_ENDPOINTS]; static struct mv_drv controller = { .gadget = { - .ep0 = &ep[0].ep, .name = "mv_udc", }, }; @@ -207,7 +205,7 @@ static void handle_ep_complete(struct mv_ep *ep) static void handle_setup(void) { - struct usb_request *req = &ep[0].req; + struct usb_request *req = &controller.ep[0].req; struct mv_udc *udc = controller.udc; struct ept_queue_head *head; struct usb_ctrlrequest r; @@ -230,11 +228,11 @@ static void handle_setup(void) if ((r.wValue == 0) && (r.wLength == 0)) { req->length = 0; for (i = 0; i < NUM_ENDPOINTS; i++) { - if (!ep[i].desc) + if (!controller.ep[i].desc) continue; - num = ep[i].desc->bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK; - in = (ep[i].desc->bEndpointAddress + num = controller.ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (controller.ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; if ((num == _num) && (in == _in)) { ep_enable(num, in); @@ -290,10 +288,11 @@ static void stop_activity(void) for (i = 0; i < NUM_ENDPOINTS; i++) { if (i != 0) writel(0, &udc->epctrl[i]); - if (ep[i].desc) { - num = ep[i].desc->bEndpointAddress + if (controller.ep[i].desc) { + num = controller.ep[i].desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - in = (ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; + in = (controller.ep[i].desc->bEndpointAddress + & USB_DIR_IN) != 0; head = epts + (num * 2) + (in); head->info = INFO_ACTIVE; } @@ -324,8 +323,8 @@ void udc_irq(void) if (bit == 2) { controller.gadget.speed = USB_SPEED_HIGH; for (i = 1; i < NUM_ENDPOINTS && n; i++) - if (ep[i].desc) - ep[i].ep.maxpacket = 512; + if (controller.ep[i].desc) + controller.ep[i].ep.maxpacket = 512; } else { controller.gadget.speed = USB_SPEED_FULL; } @@ -344,14 +343,14 @@ void udc_irq(void) writel(n, &udc->epcomp); for (i = 0; i < NUM_ENDPOINTS && n; i++) { - if (ep[i].desc) { - num = ep[i].desc->bEndpointAddress + if (controller.ep[i].desc) { + num = controller.ep[i].desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - in = (ep[i].desc->bEndpointAddress + in = (controller.ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; bit = (in) ? EPT_TX(num) : EPT_RX(num); if (n & bit) - handle_ep_complete(&ep[i]); + handle_ep_complete(&controller.ep[i]); } } } @@ -436,19 +435,20 @@ static int mvudc_probe(void) } INIT_LIST_HEAD(&controller.gadget.ep_list); - ep[0].ep.maxpacket = 64; - ep[0].ep.name = "ep0"; - ep[0].desc = &ep0_in_desc; + controller.gadget.ep0 = &controller.ep[0].ep; + controller.ep[0].ep.maxpacket = 64; + controller.ep[0].ep.name = "ep0"; + controller.ep[0].desc = &ep0_in_desc; INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { if (i != 0) { - ep[i].ep.maxpacket = 512; - ep[i].ep.name = "ep-"; - list_add_tail(&ep[i].ep.ep_list, + controller.ep[i].ep.maxpacket = 512; + controller.ep[i].ep.name = "ep-"; + list_add_tail(&controller.ep[i].ep.ep_list, &controller.gadget.ep_list); - ep[i].desc = NULL; + controller.ep[i].desc = NULL; } - ep[i].ep.ops = &mv_ep_ops; + controller.ep[i].ep.ops = &mv_ep_ops; } return 0; } diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index f7405e9..f92ca49 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -22,13 +22,6 @@ #define EP_MAX_PACKET_SIZE 0x200 #define EP0_MAX_PACKET_SIZE 64 -struct mv_ep { - struct usb_ep ep; - struct usb_request req; - struct list_head queue; - const struct usb_endpoint_descriptor *desc; -}; - struct mv_udc { u32 pad0[80]; #define MICRO_8FRAME 0x8 @@ -70,10 +63,18 @@ struct mv_udc { u32 epctrl[16]; /* 0x1c0 */ }; +struct mv_ep { + struct usb_ep ep; + struct usb_request req; + struct list_head queue; + const struct usb_endpoint_descriptor *desc; +}; + struct mv_drv { struct usb_gadget gadget; - struct usb_gadget_driver *driver; + struct usb_gadget_driver *driver; struct mv_udc *udc; + struct mv_ep ep[2 * NUM_ENDPOINTS]; }; struct ept_queue_head { -- cgit v1.1 From 2ea4b44832eee9a85167bd2a54a7b603618d80b0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:30 +0200 Subject: usb: mv_udc: Clean up the EP initialization Move the constant values that are programmed into mv_ep.ep into separate static const structure so they can be memcpy()'d when the initialization happens. Moveover, we only every init NUM_ENDPOINTS, not 2 * NUM_ENDPOINTS, so fix this bug as well. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 38 ++++++++++++++++++++++++++------------ include/usb/mv_udc.h | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index a667cf9..8dd776b 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -83,6 +83,20 @@ static struct usb_ep_ops mv_ep_ops = { .free_request = mv_ep_free_request, }; +/* Init values for USB endpoints. */ +static const struct usb_ep mv_ep_init[2] = { + [0] = { /* EP 0 */ + .maxpacket = 64, + .name = "ep0", + .ops = &mv_ep_ops, + }, + [1] = { /* EP 1..n */ + .maxpacket = 512, + .name = "ep-", + .ops = &mv_ep_ops, + }, +}; + static struct mv_drv controller = { .gadget = { .name = "mv_udc", @@ -435,21 +449,21 @@ static int mvudc_probe(void) } INIT_LIST_HEAD(&controller.gadget.ep_list); - controller.gadget.ep0 = &controller.ep[0].ep; - controller.ep[0].ep.maxpacket = 64; - controller.ep[0].ep.name = "ep0"; + + /* Init EP 0 */ + memcpy(&controller.ep[0].ep, &mv_ep_init[0], sizeof(*mv_ep_init)); controller.ep[0].desc = &ep0_in_desc; + controller.gadget.ep0 = &controller.ep[0].ep; INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); - for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { - if (i != 0) { - controller.ep[i].ep.maxpacket = 512; - controller.ep[i].ep.name = "ep-"; - list_add_tail(&controller.ep[i].ep.ep_list, - &controller.gadget.ep_list); - controller.ep[i].desc = NULL; - } - controller.ep[i].ep.ops = &mv_ep_ops; + + /* Init EP 1..n */ + for (i = 1; i < NUM_ENDPOINTS; i++) { + memcpy(&controller.ep[i].ep, &mv_ep_init[1], + sizeof(*mv_ep_init)); + list_add_tail(&controller.ep[i].ep.ep_list, + &controller.gadget.ep_list); } + return 0; } diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index f92ca49..ffddb75 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -74,7 +74,7 @@ struct mv_drv { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct mv_udc *udc; - struct mv_ep ep[2 * NUM_ENDPOINTS]; + struct mv_ep ep[NUM_ENDPOINTS]; }; struct ept_queue_head { -- cgit v1.1 From b959655f18d9a6bd8d3ccdbd764421a671f31ce2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:31 +0200 Subject: usb: ehci: Split out struct ehci_ctrl definition Move the struct ehci_ctrl defition from ehci-hcd.c into ehci.h so it can be re-used by drivers. In particular, the mv_udc driver can benefit from this move. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/host/ehci-hcd.c | 11 +---------- drivers/usb/host/ehci.h | 13 +++++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 706cf0c..05d3f0b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,16 +36,7 @@ #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 #endif -static struct ehci_ctrl { - struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ - struct ehci_hcor *hcor; - int rootdev; - uint16_t portreset; - struct QH qh_list __aligned(USB_DMA_MINALIGN); - struct QH periodic_queue __aligned(USB_DMA_MINALIGN); - uint32_t *periodic_list; - int ntds; -} ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; +static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; #define ALIGN_END_ADDR(type, ptr, size) \ ((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d090f0a..bd52afe 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -22,6 +22,8 @@ #ifndef USB_EHCI_H #define USB_EHCI_H +#include + #if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 #endif @@ -252,6 +254,17 @@ struct QH { }; }; +struct ehci_ctrl { + struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ + struct ehci_hcor *hcor; + int rootdev; + uint16_t portreset; + struct QH qh_list __aligned(USB_DMA_MINALIGN); + struct QH periodic_queue __aligned(USB_DMA_MINALIGN); + uint32_t *periodic_list; + int ntds; +}; + /* Low level init functions */ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor); int ehci_hcd_stop(int index); -- cgit v1.1 From be7ed2533d4acb9bc67989c4ffddb5814202ba02 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:32 +0200 Subject: usb: mv_udc: Make use of struct ehci_ctrl The usb_lowlevel_init() call already fills and passes back struct ehci_ctrl , which readily contains correctly determined address of the port register block address computed from values from controller configuration registers. Leverage this and make use of this value as this makes the code mode universal, but also gets us rid of the CONFIG_USB_REG_BASE configuration option. Moreover, this patch cleans up the usb_gadget_register_driver() call a little by correcting the error handling. Note the usb_lowlevel_init() and mvudc_probe() are now called in reversed order, but this has no impact on the code. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 41 +++++++++++++++++++++++------------------ include/usb/mv_udc.h | 6 +++--- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 8dd776b..6d69d6d 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -118,7 +118,7 @@ static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req) static void ep_enable(int num, int in) { struct ept_queue_head *head; - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; unsigned n; head = epts + 2*num + in; @@ -154,7 +154,7 @@ static int mv_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; struct ept_queue_item *item; struct ept_queue_head *head; unsigned phys; @@ -220,7 +220,7 @@ static void handle_ep_complete(struct mv_ep *ep) static void handle_setup(void) { struct usb_request *req = &controller.ep[0].req; - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; struct ept_queue_head *head; struct usb_ctrlrequest r; int status = 0; @@ -293,7 +293,7 @@ static void stop_activity(void) { int i, num, in; struct ept_queue_head *head; - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; writel(readl(&udc->epcomp), &udc->epcomp); writel(readl(&udc->epstat), &udc->epstat); writel(0xffffffff, &udc->epflush); @@ -315,7 +315,7 @@ static void stop_activity(void) void udc_irq(void) { - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; unsigned n = readl(&udc->usbsts); writel(n, &udc->usbsts); int bit, i, num, in; @@ -373,7 +373,7 @@ void udc_irq(void) int usb_gadget_handle_interrupts(void) { u32 value; - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; value = readl(&udc->usbsts); if (value) @@ -384,7 +384,7 @@ int usb_gadget_handle_interrupts(void) static int mv_pullup(struct usb_gadget *gadget, int is_on) { - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; if (is_on) { /* RESET */ writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); @@ -412,7 +412,7 @@ static int mv_pullup(struct usb_gadget *gadget, int is_on) void udc_disconnect(void) { - struct mv_udc *udc = controller.udc; + struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; /* disable pullup */ stop_activity(); writel(USBCMD_FS2, &udc->usbcmd); @@ -427,7 +427,6 @@ static int mvudc_probe(void) int i; controller.gadget.ops = &mv_udc_ops; - controller.udc = (struct mv_udc *)CONFIG_USB_REG_BASE; epts = memalign(PAGE_SIZE, QH_MAXNUM * sizeof(struct ept_queue_head)); memset(epts, 0, QH_MAXNUM * sizeof(struct ept_queue_head)); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { @@ -469,9 +468,8 @@ static int mvudc_probe(void) int usb_gadget_register_driver(struct usb_gadget_driver *driver) { - struct mv_udc *udc = controller.udc; - int retval; - void *ctrl; + struct mv_udc *udc; + int ret; if (!driver || driver->speed < USB_SPEED_FULL @@ -481,15 +479,22 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) return -EINVAL; } - if (!mvudc_probe()) { - usb_lowlevel_init(0, &ctrl); + ret = usb_lowlevel_init(0, (void **)&controller.ctrl); + if (ret) + return ret; + + ret = mvudc_probe(); + if (!ret) { + udc = (struct mv_udc *)controller.ctrl->hcor; + /* select ULPI phy */ writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc); } - retval = driver->bind(&controller.gadget); - if (retval) { - DBG("driver->bind() returned %d\n", retval); - return retval; + + ret = driver->bind(&controller.gadget); + if (ret) { + DBG("driver->bind() returned %d\n", ret); + return ret; } controller.driver = driver; diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index ffddb75..4b8ad7e 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -14,6 +14,8 @@ #include #include +#include "../../drivers/usb/host/ehci.h" + #define NUM_ENDPOINTS 6 /* Endpoint parameters */ @@ -23,7 +25,6 @@ #define EP0_MAX_PACKET_SIZE 64 struct mv_udc { - u32 pad0[80]; #define MICRO_8FRAME 0x8 #define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16) #define USBCMD_FS2 (1 << 15) @@ -73,7 +74,7 @@ struct mv_ep { struct mv_drv { struct usb_gadget gadget; struct usb_gadget_driver *driver; - struct mv_udc *udc; + struct ehci_ctrl *ctrl; struct mv_ep ep[NUM_ENDPOINTS]; }; @@ -121,5 +122,4 @@ struct ept_queue_item { #define INFO_BUFFER_ERROR (1 << 5) #define INFO_TX_ERROR (1 << 3) -extern int usb_lowlevel_init(int index, void **controller); #endif /* __MV_UDC_H__ */ -- cgit v1.1 From d76630386d59d203ad4594d7e91be1f2e258ab93 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:33 +0200 Subject: usb: mv_udc: Clean up the initial variable check Clean up the code that checks the validity of a USB gadget driver in usb_gadget_register_driver(). Moreover, limit the speed of the driver to either FULL or HIGH, this is more precise and once we have xHCI support, also more correct. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 6d69d6d..bed5898 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -471,13 +471,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) struct mv_udc *udc; int ret; - if (!driver - || driver->speed < USB_SPEED_FULL - || !driver->bind - || !driver->setup) { - DBG("bad parameter.\n"); + if (!driver) + return -EINVAL; + if (!driver->bind || !driver->setup || !driver->disconnect) + return -EINVAL; + if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) return -EINVAL; - } ret = usb_lowlevel_init(0, (void **)&controller.ctrl); if (ret) -- cgit v1.1 From f646317739592a67847e0707c5e1072bfafaba7a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:34 +0200 Subject: usb: mv_udc: Remove QH_MAXNUM macro The QH_MAXNUM is used in absolutelly incorrect manner and is not even needed. Remove it and correctly replace it's occurance with 2 * NUM_ENDPOINTS . Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index bed5898..210373d 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -44,7 +44,7 @@ static const char *reqname(unsigned r) #endif #define PAGE_SIZE 4096 -#define QH_MAXNUM 32 + static struct usb_endpoint_descriptor ep0_out_desc = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, @@ -425,10 +425,11 @@ static int mvudc_probe(void) { struct ept_queue_head *head; int i; + const int num = 2 * NUM_ENDPOINTS; controller.gadget.ops = &mv_udc_ops; - epts = memalign(PAGE_SIZE, QH_MAXNUM * sizeof(struct ept_queue_head)); - memset(epts, 0, QH_MAXNUM * sizeof(struct ept_queue_head)); + epts = memalign(PAGE_SIZE, num * sizeof(struct ept_queue_head)); + memset(epts, 0, num * sizeof(struct ept_queue_head)); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { /* * For item0 and item1, they are served as ep0 -- cgit v1.1 From fe48f05817a0c44724b6e4c6af04c1843f387cea Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:35 +0200 Subject: usb: mv_udc: Init mv_drv.gadget.ops statically There is no need to init this field at runtime, so init it statically. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 210373d..c9cd24a 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -98,8 +98,9 @@ static const struct usb_ep mv_ep_init[2] = { }; static struct mv_drv controller = { - .gadget = { - .name = "mv_udc", + .gadget = { + .name = "mv_udc", + .ops = &mv_udc_ops, }, }; @@ -427,7 +428,6 @@ static int mvudc_probe(void) int i; const int num = 2 * NUM_ENDPOINTS; - controller.gadget.ops = &mv_udc_ops; epts = memalign(PAGE_SIZE, num * sizeof(struct ept_queue_head)); memset(epts, 0, num * sizeof(struct ept_queue_head)); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { -- cgit v1.1 From ab52df19c1195699516430be14b6be2d27229444 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:36 +0200 Subject: usb: mv_udc: Move QH and qTD into mv_drv Both the endpoint queue head and the endpoint item list is a controller specific thing. Move them both into controller private data. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 26 +++++++++++++------------- include/usb/mv_udc.h | 2 ++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index c9cd24a..9ff6bbb 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -59,8 +59,6 @@ static struct usb_endpoint_descriptor ep0_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; -struct ept_queue_head *epts; -struct ept_queue_item *items[2 * NUM_ENDPOINTS]; static int mv_pullup(struct usb_gadget *gadget, int is_on); static int mv_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc); @@ -121,7 +119,7 @@ static void ep_enable(int num, int in) struct ept_queue_head *head; struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; unsigned n; - head = epts + 2*num + in; + head = controller.epts + 2*num + in; n = readl(&udc->epctrl[num]); if (in) @@ -162,8 +160,8 @@ static int mv_ep_queue(struct usb_ep *ep, int bit, num, len, in; num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; - item = items[2 * num + in]; - head = epts + 2 * num + in; + item = controller.items[2 * num + in]; + head = controller.epts + 2 * num + in; phys = (unsigned)req->buf; len = req->length; @@ -198,7 +196,7 @@ static void handle_ep_complete(struct mv_ep *ep) in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; if (num == 0) ep->desc = &ep0_out_desc; - item = items[2 * num + in]; + item = controller.items[2 * num + in]; if (item->info & 0xff) printf("EP%d/%s FAIL nfo=%x pg0=%x\n", @@ -227,7 +225,7 @@ static void handle_setup(void) int status = 0; int num, in, _num, _in, i; char *buf; - head = epts; + head = controller.epts + 2 * 0 + 0; flush_cache((unsigned long)head, sizeof(struct ept_queue_head)); memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest)); @@ -308,7 +306,7 @@ static void stop_activity(void) & USB_ENDPOINT_NUMBER_MASK; in = (controller.ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; - head = epts + (num * 2) + (in); + head = controller.epts + (num * 2) + (in); head->info = INFO_ACTIVE; } } @@ -391,7 +389,7 @@ static int mv_pullup(struct usb_gadget *gadget, int is_on) writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); udelay(200); - writel((unsigned) epts, &udc->epinitaddr); + writel((unsigned) controller.epts, &udc->epinitaddr); /* select DEVICE mode */ writel(USBMODE_DEVICE, &udc->usbmode); @@ -428,14 +426,15 @@ static int mvudc_probe(void) int i; const int num = 2 * NUM_ENDPOINTS; - epts = memalign(PAGE_SIZE, num * sizeof(struct ept_queue_head)); - memset(epts, 0, num * sizeof(struct ept_queue_head)); + controller.epts = memalign(PAGE_SIZE, + num * sizeof(struct ept_queue_head)); + memset(controller.epts, 0, num * sizeof(struct ept_queue_head)); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { /* * For item0 and item1, they are served as ep0 * out&in seperately */ - head = epts + i; + head = controller.epts + i; if (i < 2) head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE) | CONFIG_ZLT | CONFIG_IOS; @@ -445,7 +444,8 @@ static int mvudc_probe(void) head->next = TERMINATE; head->info = 0; - items[i] = memalign(PAGE_SIZE, sizeof(struct ept_queue_item)); + controller.items[i] = memalign(PAGE_SIZE, + sizeof(struct ept_queue_item)); } INIT_LIST_HEAD(&controller.gadget.ep_list); diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index 4b8ad7e..a9869f0 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -75,6 +75,8 @@ struct mv_drv { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct ehci_ctrl *ctrl; + struct ept_queue_head *epts; + struct ept_queue_item *items[2 * NUM_ENDPOINTS]; struct mv_ep ep[NUM_ENDPOINTS]; }; -- cgit v1.1 From ab65da1446d8ba423514c2236291962adf2bd2a6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:37 +0200 Subject: usb: mv_udc: Properly align the endpoint QH and qTD list The endpoint QH list has to be aligned to 10-bit boundary. We also have to make sure the list is aligned on a cacheline boundary. Make sure it is. Furthermore, check if the memory allocation for the QH list didn't fail. Moveover, improve the comment about the QH list structure. Finally, the qTD item list has to be aligned only to 5-bit boundary, not 10-bit as it is now, fix this as well. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 9ff6bbb..bbf4418 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -43,8 +43,6 @@ static const char *reqname(unsigned r) } #endif -#define PAGE_SIZE 4096 - static struct usb_endpoint_descriptor ep0_out_desc = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, @@ -424,15 +422,27 @@ static int mvudc_probe(void) { struct ept_queue_head *head; int i; + const int num = 2 * NUM_ENDPOINTS; - controller.epts = memalign(PAGE_SIZE, - num * sizeof(struct ept_queue_head)); - memset(controller.epts, 0, num * sizeof(struct ept_queue_head)); + const int eplist_min_align = 4096; + const int eplist_align = roundup(eplist_min_align, ARCH_DMA_MINALIGN); + const int eplist_raw_sz = num * sizeof(struct ept_queue_head); + const int eplist_sz = roundup(eplist_raw_sz, ARCH_DMA_MINALIGN); + + /* The QH list must be aligned to 4096 bytes. */ + controller.epts = memalign(eplist_align, eplist_sz); + if (!controller.epts) + return -ENOMEM; + memset(controller.epts, 0, eplist_sz); + for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { /* - * For item0 and item1, they are served as ep0 - * out&in seperately + * Configure QH for each endpoint. The structure of the QH list + * is such that each two subsequent fields, N and N+1 where N is + * even, in the QH list represent QH for one endpoint. The Nth + * entry represents OUT configuration and the N+1th entry does + * represent IN configuration of the endpoint. */ head = controller.epts + i; if (i < 2) @@ -444,7 +454,7 @@ static int mvudc_probe(void) head->next = TERMINATE; head->info = 0; - controller.items[i] = memalign(PAGE_SIZE, + controller.items[i] = memalign(roundup(32, ARCH_DMA_MINALIGN), sizeof(struct ept_queue_item)); } -- cgit v1.1 From 5804b8859a3317d3deb1e6adafc2a2604d276f1c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:38 +0200 Subject: usb: mv_udc: Add cacheline length check Check the length of system cacheline at compile-time and fail if the system uses too long cachelines. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index bbf4418..1ead2f2 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -21,6 +21,16 @@ #error This driver only supports one single controller. #endif +/* + * Check if the system has too long cachelines. If the cachelines are + * longer then 128b, the driver will not be able flush/invalidate data + * cache over separate QH entries. We use 128b because one QH entry is + * 64b long and there are always two QH list entries for each endpoint. + */ +#if ARCH_DMA_MINALIGN > 128 +#error This driver can not work on systems with caches longer than 128b +#endif + #ifndef DEBUG #define DBG(x...) do {} while (0) #else -- cgit v1.1 From b5cd45bfad3a25db25b40cfda9e7db80e5ee65f7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:39 +0200 Subject: usb: mv_udc: Implement better QH accessor The code for retrieving QH for particular endpoint is hard to understand, moreover it's duplicated all over the driver. Move the code into single nice and documented function. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 1ead2f2..04787be 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -110,6 +110,19 @@ static struct mv_drv controller = { }, }; +/** + * mv_get_qh() - return queue head for endpoint + * @ep_num: Endpoint number + * @dir_in: Direction of the endpoint (IN = 1, OUT = 0) + * + * This function returns the QH associated with particular endpoint + * and it's direction. + */ +static struct ept_queue_head *mv_get_qh(int ep_num, int dir_in) +{ + return &controller.epts[(ep_num * 2) + dir_in]; +} + static struct usb_request * mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { @@ -127,7 +140,7 @@ static void ep_enable(int num, int in) struct ept_queue_head *head; struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; unsigned n; - head = controller.epts + 2*num + in; + head = mv_get_qh(num, in); n = readl(&udc->epctrl[num]); if (in) @@ -169,7 +182,7 @@ static int mv_ep_queue(struct usb_ep *ep, num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; item = controller.items[2 * num + in]; - head = controller.epts + 2 * num + in; + head = mv_get_qh(num, in); phys = (unsigned)req->buf; len = req->length; @@ -233,7 +246,7 @@ static void handle_setup(void) int status = 0; int num, in, _num, _in, i; char *buf; - head = controller.epts + 2 * 0 + 0; + head = mv_get_qh(0, 0); /* EP0 OUT */ flush_cache((unsigned long)head, sizeof(struct ept_queue_head)); memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest)); @@ -314,7 +327,7 @@ static void stop_activity(void) & USB_ENDPOINT_NUMBER_MASK; in = (controller.ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; - head = controller.epts + (num * 2) + (in); + head = mv_get_qh(num, in); head->info = INFO_ACTIVE; } } @@ -397,7 +410,7 @@ static int mv_pullup(struct usb_gadget *gadget, int is_on) writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); udelay(200); - writel((unsigned) controller.epts, &udc->epinitaddr); + writel((unsigned)controller.epts, &udc->epinitaddr); /* select DEVICE mode */ writel(USBMODE_DEVICE, &udc->usbmode); -- cgit v1.1 From 8a095a68ce8e7989dec6af4011f0f41bbc554f22 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:40 +0200 Subject: usb: mv_udc: Improve allocation of qTD items Allocate the qTD items all at once instead of allocating them separately. Moreover, make sure each qTD is properly aligned to 32-bytes boundary and that cache can be safely flushed over each qTD touple. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 25 +++++++++++++++++++++++-- include/usb/mv_udc.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 04787be..dbdf878 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -444,6 +444,7 @@ void udc_disconnect(void) static int mvudc_probe(void) { struct ept_queue_head *head; + uint8_t *imem; int i; const int num = 2 * NUM_ENDPOINTS; @@ -453,12 +454,29 @@ static int mvudc_probe(void) const int eplist_raw_sz = num * sizeof(struct ept_queue_head); const int eplist_sz = roundup(eplist_raw_sz, ARCH_DMA_MINALIGN); + const int ilist_align = roundup(ARCH_DMA_MINALIGN, 32); + const int ilist_ent_raw_sz = 2 * sizeof(struct ept_queue_item); + const int ilist_ent_sz = roundup(ilist_ent_raw_sz, ARCH_DMA_MINALIGN); + const int ilist_sz = NUM_ENDPOINTS * ilist_ent_sz; + /* The QH list must be aligned to 4096 bytes. */ controller.epts = memalign(eplist_align, eplist_sz); if (!controller.epts) return -ENOMEM; memset(controller.epts, 0, eplist_sz); + /* + * Each qTD item must be 32-byte aligned, each qTD touple must be + * cacheline aligned. There are two qTD items for each endpoint and + * only one of them is used for the endpoint at time, so we can group + * them together. + */ + controller.items_mem = memalign(ilist_align, ilist_sz); + if (!controller.items_mem) { + free(controller.epts); + return -ENOMEM; + } + for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { /* * Configure QH for each endpoint. The structure of the QH list @@ -477,8 +495,11 @@ static int mvudc_probe(void) head->next = TERMINATE; head->info = 0; - controller.items[i] = memalign(roundup(32, ARCH_DMA_MINALIGN), - sizeof(struct ept_queue_item)); + imem = controller.items_mem + ((i >> 1) * ilist_ent_sz); + if (i & 1) + imem += sizeof(struct ept_queue_item); + + controller.items[i] = (struct ept_queue_item *)imem; } INIT_LIST_HEAD(&controller.gadget.ep_list); diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index a9869f0..c1761b0 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -77,6 +77,7 @@ struct mv_drv { struct ehci_ctrl *ctrl; struct ept_queue_head *epts; struct ept_queue_item *items[2 * NUM_ENDPOINTS]; + uint8_t *items_mem; struct mv_ep ep[NUM_ENDPOINTS]; }; -- cgit v1.1 From ede709c0d293d980ceb6596132aa39941e3e1c7f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:41 +0200 Subject: usb: mv_udc: Implement better qTD item accessor The code for retrieving qTD item for particular endpoint is hard to understand, moreover it's duplicated all over the driver. Move the code into single nice and documented function. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index dbdf878..9ec575a 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -123,6 +123,19 @@ static struct ept_queue_head *mv_get_qh(int ep_num, int dir_in) return &controller.epts[(ep_num * 2) + dir_in]; } +/** + * mv_get_qtd() - return queue item for endpoint + * @ep_num: Endpoint number + * @dir_in: Direction of the endpoint (IN = 1, OUT = 0) + * + * This function returns the QH associated with particular endpoint + * and it's direction. + */ +static struct ept_queue_item *mv_get_qtd(int ep_num, int dir_in) +{ + return controller.items[(ep_num * 2) + dir_in]; +} + static struct usb_request * mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { @@ -181,7 +194,7 @@ static int mv_ep_queue(struct usb_ep *ep, int bit, num, len, in; num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; - item = controller.items[2 * num + in]; + item = mv_get_qtd(num, in); head = mv_get_qh(num, in); phys = (unsigned)req->buf; len = req->length; @@ -217,7 +230,7 @@ static void handle_ep_complete(struct mv_ep *ep) in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; if (num == 0) ep->desc = &ep0_out_desc; - item = controller.items[2 * num + in]; + item = mv_get_qtd(num, in); if (item->info & 0xff) printf("EP%d/%s FAIL nfo=%x pg0=%x\n", -- cgit v1.1 From f19a343e732f4fd7ca0b0e067a1bfba930621553 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:42 +0200 Subject: usb: mv_udc: Add proper cache management Implement functions to flush/invalidate dcache over QH and qTDs and make use of them where appropriate. Also use them to replace the old incorrect cache management attempt. This is the first step towards making this driver work with data cache enabled. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 82 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 9ec575a..8a5593a 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -136,6 +136,68 @@ static struct ept_queue_item *mv_get_qtd(int ep_num, int dir_in) return controller.items[(ep_num * 2) + dir_in]; } +/** + * mv_flush_qh - flush cache over queue head + * @ep_num: Endpoint number + * + * This function flushes cache over QH for particular endpoint. + */ +static void mv_flush_qh(int ep_num) +{ + struct ept_queue_head *head = mv_get_qh(ep_num, 0); + const uint32_t start = (uint32_t)head; + const uint32_t end = start + 2 * sizeof(*head); + + flush_dcache_range(start, end); +} + +/** + * mv_invalidate_qh - invalidate cache over queue head + * @ep_num: Endpoint number + * + * This function invalidates cache over QH for particular endpoint. + */ +static void mv_invalidate_qh(int ep_num) +{ + struct ept_queue_head *head = mv_get_qh(ep_num, 0); + uint32_t start = (uint32_t)head; + uint32_t end = start + 2 * sizeof(*head); + + invalidate_dcache_range(start, end); +} + +/** + * mv_flush_qtd - flush cache over queue item + * @ep_num: Endpoint number + * + * This function flushes cache over qTD pair for particular endpoint. + */ +static void mv_flush_qtd(int ep_num) +{ + struct ept_queue_item *item = mv_get_qtd(ep_num, 0); + const uint32_t start = (uint32_t)item; + const uint32_t end_raw = start + 2 * sizeof(*item); + const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN); + + flush_dcache_range(start, end); +} + +/** + * mv_invalidate_qtd - invalidate cache over queue item + * @ep_num: Endpoint number + * + * This function invalidates cache over qTD pair for particular endpoint. + */ +static void mv_invalidate_qtd(int ep_num) +{ + struct ept_queue_item *item = mv_get_qtd(ep_num, 0); + const uint32_t start = (uint32_t)item; + const uint32_t end_raw = start + 2 * sizeof(*item); + const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + static struct usb_request * mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { @@ -161,8 +223,10 @@ static void ep_enable(int num, int in) else n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK); - if (num != 0) + if (num != 0) { head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT; + mv_flush_qh(num); + } writel(n, &udc->epctrl[num]); } @@ -215,8 +279,9 @@ static int mv_ep_queue(struct usb_ep *ep, else bit = EPT_RX(num); - flush_cache(phys, len); - flush_cache((unsigned long)item, sizeof(struct ept_queue_item)); + mv_flush_qh(num); + mv_flush_qtd(num); + writel(bit, &udc->epprime); return 0; @@ -231,7 +296,8 @@ static void handle_ep_complete(struct mv_ep *ep) if (num == 0) ep->desc = &ep0_out_desc; item = mv_get_qtd(num, in); - + mv_invalidate_qtd(num); + if (item->info & 0xff) printf("EP%d/%s FAIL nfo=%x pg0=%x\n", num, in ? "in" : "out", item->info, item->page0); @@ -261,7 +327,7 @@ static void handle_setup(void) char *buf; head = mv_get_qh(0, 0); /* EP0 OUT */ - flush_cache((unsigned long)head, sizeof(struct ept_queue_head)); + mv_invalidate_qh(0); memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest)); writel(EPT_RX(0), &udc->epstat); DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest), @@ -342,6 +408,7 @@ static void stop_activity(void) & USB_DIR_IN) != 0; head = mv_get_qh(num, in); head->info = INFO_ACTIVE; + mv_flush_qh(num); } } } @@ -513,6 +580,11 @@ static int mvudc_probe(void) imem += sizeof(struct ept_queue_item); controller.items[i] = (struct ept_queue_item *)imem; + + if (i & 1) { + mv_flush_qh(i - 1); + mv_flush_qtd(i - 1); + } } INIT_LIST_HEAD(&controller.gadget.ep_list); -- cgit v1.1 From 6dd30af0fa5c2f509f6f789789151268bdfcde0c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 Jul 2013 03:16:43 +0200 Subject: usb: mv_udc: Add bounce buffer The requests sent to the controller are not properly cache aligned most of the time, thus implement a simple bounce buffer to avoid problem with cache. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Lei Wen Cc: Otavio Salvador Cc: Stefano Babic --- drivers/usb/gadget/mv_udc.c | 84 +++++++++++++++++++++++++++++++++++++++++---- include/usb/mv_udc.h | 6 +++- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index 8a5593a..7574e31 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -247,6 +247,71 @@ static int mv_ep_disable(struct usb_ep *ep) return 0; } +static int mv_bounce(struct mv_ep *ep) +{ + uint32_t addr = (uint32_t)ep->req.buf; + uint32_t ba; + + /* Input buffer address is not aligned. */ + if (addr & (ARCH_DMA_MINALIGN - 1)) + goto align; + + /* Input buffer length is not aligned. */ + if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) + goto align; + + /* The buffer is well aligned, only flush cache. */ + ep->b_len = ep->req.length; + ep->b_buf = ep->req.buf; + goto flush; + +align: + /* Use internal buffer for small payloads. */ + if (ep->req.length <= 64) { + ep->b_len = 64; + ep->b_buf = ep->b_fast; + } else { + ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN); + ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len); + if (!ep->b_buf) + return -ENOMEM; + } + + memcpy(ep->b_buf, ep->req.buf, ep->req.length); + +flush: + ba = (uint32_t)ep->b_buf; + flush_dcache_range(ba, ba + ep->b_len); + + return 0; +} + +static void mv_debounce(struct mv_ep *ep) +{ + uint32_t addr = (uint32_t)ep->req.buf; + uint32_t ba = (uint32_t)ep->b_buf; + + invalidate_dcache_range(ba, ba + ep->b_len); + + /* Input buffer address is not aligned. */ + if (addr & (ARCH_DMA_MINALIGN - 1)) + goto copy; + + /* Input buffer length is not aligned. */ + if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) + goto copy; + + /* The buffer is well aligned, only invalidate cache. */ + return; + +copy: + memcpy(ep->req.buf, ep->b_buf, ep->req.length); + + /* Large payloads use allocated buffer, free it. */ + if (ep->req.length > 64) + free(ep->b_buf); +} + static int mv_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { @@ -254,25 +319,27 @@ static int mv_ep_queue(struct usb_ep *ep, struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; struct ept_queue_item *item; struct ept_queue_head *head; - unsigned phys; - int bit, num, len, in; + int bit, num, len, in, ret; num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; item = mv_get_qtd(num, in); head = mv_get_qh(num, in); - phys = (unsigned)req->buf; len = req->length; + ret = mv_bounce(mv_ep); + if (ret) + return ret; + item->next = TERMINATE; item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; - item->page0 = phys; - item->page1 = (phys & 0xfffff000) + 0x1000; + item->page0 = (uint32_t)mv_ep->b_buf; + item->page1 = ((uint32_t)mv_ep->b_buf & 0xfffff000) + 0x1000; head->next = (unsigned) item; head->info = 0; - DBG("ept%d %s queue len %x, buffer %x\n", - num, in ? "in" : "out", len, phys); + DBG("ept%d %s queue len %x, buffer %p\n", + num, in ? "in" : "out", len, mv_ep->b_buf); if (in) bit = EPT_TX(num); @@ -303,6 +370,9 @@ static void handle_ep_complete(struct mv_ep *ep) num, in ? "in" : "out", item->info, item->page0); len = (item->info >> 16) & 0x7fff; + + mv_debounce(ep); + ep->req.length -= len; DBG("ept%d %s complete %x\n", num, in ? "in" : "out", len); diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h index c1761b0..c71516c 100644 --- a/include/usb/mv_udc.h +++ b/include/usb/mv_udc.h @@ -66,9 +66,13 @@ struct mv_udc { struct mv_ep { struct usb_ep ep; - struct usb_request req; struct list_head queue; const struct usb_endpoint_descriptor *desc; + + struct usb_request req; + uint8_t *b_buf; + uint32_t b_len; + uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN); }; struct mv_drv { -- cgit v1.1 From 6bed7ce569fc409843ff2537937ea1b5fd0f694d Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 18 Jul 2013 13:19:14 +0200 Subject: dfu: Implementation of target reset after communication with dfu-util's -R switch This patch extends dfu code to support transmission with -R switch specified at dfu-util. When -R is specified, the extra USB_REQ_DFU_DETACH request is sent after successful data transmission. Then dfu resources are released and reset command is issued. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park --- common/cmd_dfu.c | 14 +++++++++++++- drivers/dfu/dfu.c | 11 +++++++++++ drivers/usb/gadget/f_dfu.c | 2 ++ include/dfu.h | 2 ++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c index db066ac..793c422 100644 --- a/common/cmd_dfu.c +++ b/common/cmd_dfu.c @@ -19,8 +19,8 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { const char *str_env; char *s = "dfu"; + int ret, i = 0; char *env_bkp; - int ret; if (argc < 3) return CMD_RET_USAGE; @@ -49,6 +49,15 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) g_dnl_register(s); while (1) { + if (dfu_reset()) + /* + * This extra number of usb_gadget_handle_interrupts() + * calls is necessary to assure correct transmission + * completion with dfu-util + */ + if (++i == 10) + goto exit; + if (ctrlc()) goto exit; @@ -60,6 +69,9 @@ done: dfu_free_entities(); free(env_bkp); + if (dfu_reset()) + run_command("reset", 0); + return CMD_RET_SUCCESS; } diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index b8870ec..d73d510 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -16,9 +16,20 @@ #include #include +static bool dfu_reset_request; static LIST_HEAD(dfu_list); static int dfu_alt_num; +bool dfu_reset(void) +{ + return dfu_reset_request; +} + +void dfu_trigger_reset() +{ + dfu_reset_request = true; +} + static int dfu_find_alt_num(const char *s) { int i = 0; diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 5321a68..37d04a1 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -312,6 +312,8 @@ static int state_dfu_idle(struct f_dfu *f_dfu, DFU_STATE_dfuMANIFEST_WAIT_RST; to_runtime_mode(f_dfu); f_dfu->dfu_state = DFU_STATE_appIDLE; + + dfu_trigger_reset(); break; default: f_dfu->dfu_state = DFU_STATE_dfuERROR; diff --git a/include/dfu.h b/include/dfu.h index 6c7d227..1d4006d 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -109,6 +109,8 @@ const char *dfu_get_dev_type(enum dfu_device_type t); const char *dfu_get_layout(enum dfu_layout l); struct dfu_entity *dfu_get_entity(int alt); char *dfu_extract_token(char** e, int *n); +void dfu_trigger_reset(void); +bool dfu_reset(void); int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); -- cgit v1.1 From bb1f327d0a6ec9236c172fdc0912ace7c8e2ea83 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 22 Jul 2013 11:14:37 +0300 Subject: usb: ehci-omap: Don't softreset USB High-speed Host (UHH) Module Fixes NFS root problems with Beagle (3530 ES1.0) when used with external USB-ethernet adapter and "USB start" command used within u-boot. Soft resetting the UHH module causes instability issues on all OMAPs so we just avoid it. See OMAP36xx Errata i571: USB host EHCI may stall when entering smart-standby mode i660: USBHOST Configured In Smart-Idle Can Lead To a Deadlock On OMAP4/5, soft-resetting the UHH module can put it into Smart-Idle mode and lead to a deadlock. On OMAP3 this doesn't seem to be the case but still instabilities are observed on beagle (3530 ES1.0) if soft-reset is used. e.g. NFS root failures with Linux kernel. Signed-off-by: Roger Quadros --- drivers/usb/host/ehci-omap.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index a47e078..032d5e5 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -28,18 +28,21 @@ static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE; static int omap_uhh_reset(void) { - unsigned long init = get_timer(0); - - /* perform UHH soft reset, and wait until reset is complete */ - writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc); - - /* Wait for UHH reset to complete */ - while (!(readl(&uhh->syss) & OMAP_UHH_SYSSTATUS_EHCI_RESETDONE)) - if (get_timer(init) > CONFIG_SYS_HZ) { - debug("OMAP UHH error: timeout resetting ehci\n"); - return -EL3RST; - } - +/* + * Soft resetting the UHH module causes instability issues on + * all OMAPs so we just avoid it. + * + * See OMAP36xx Errata + * i571: USB host EHCI may stall when entering smart-standby mode + * i660: USBHOST Configured In Smart-Idle Can Lead To a Deadlock + * + * On OMAP4/5, soft-resetting the UHH module will put it into + * Smart-Idle mode and lead to a deadlock. + * + * On OMAP3, this doesn't seem to be the case but still instabilities + * are observed on beagle (3530 ES1.0) if soft-reset is used. + * e.g. NFS root failures with Linux kernel. + */ return 0; } -- cgit v1.1 From 0adc331b37b71093a047607faf1ed1b60e572017 Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Mon, 29 Jul 2013 13:27:39 +0300 Subject: usb_hub: fix power cycling logic When power cycling the hub ports, a misbehaving port will prevent all ports from being powered on because we quit at the first sign of trouble. Skip problematic ports instead of failing the entire power on. Cc: Marek Vasut Cc: Igor Grinberg Signed-off-by: Nikita Kiryanov --- common/usb_hub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/usb_hub.c b/common/usb_hub.c index 754d436..a11b401 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -110,7 +110,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub) ret = usb_get_port_status(dev, i + 1, portsts); if (ret < 0) { debug("port %d: get_port_status failed\n", i + 1); - return; + continue; } /* @@ -125,7 +125,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub) portstatus = le16_to_cpu(portsts->wPortStatus); if (portstatus & (USB_PORT_STAT_POWER << 1)) { debug("port %d: Port power change failed\n", i + 1); - return; + continue; } } -- cgit v1.1 From 8bc3603675f7bf4dfa4eb6bdaf2aa0a8ddce9fa6 Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Mon, 29 Jul 2013 13:27:40 +0300 Subject: ehci-hcd: fix memory leak in lowlevel init usb_lowlevel_init() allocates a new periodic_list each time it is invoked, without freeing the original list. Since it is initialized later on in the code, just reuse the first-allocated list in future invocations of usb_lowlevel_init. Cc: Marek Vasut Cc: Igor Grinberg Signed-off-by: Nikita Kiryanov --- drivers/usb/host/ehci-hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 05d3f0b..fdad739 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -945,7 +945,9 @@ int usb_lowlevel_init(int index, void **controller) * Split Transactions will be spread across microframes using * S-mask and C-mask. */ - ehcic[index].periodic_list = memalign(4096, 1024*4); + if (ehcic[index].periodic_list == NULL) + ehcic[index].periodic_list = memalign(4096, 1024 * 4); + if (!ehcic[index].periodic_list) return -ENOMEM; for (i = 0; i < 1024; i++) { -- cgit v1.1