From 48c8073ec957b4b472835f4c7b6447f7e518e2f5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 25 Sep 2011 20:00:37 +0100 Subject: USB: Add functionality to poll the USB keyboard via control EP This allows the keyboard to avoid requests via Interrupt Endpoint altogether and run all requests via Control Endpoint. This uses the Get_Report request. Signed-off-by: Marek Vasut Cc: Remy Bohmer Rebased on current code. Signed-off-by: Remy Bohmer diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 69939f0..ef440e4 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -98,6 +98,26 @@ static unsigned char usb_kbd_numkey_shifted[] = { '|', '~', ':', '"', '~', '<', '>', '?' }; +static int usb_kbd_irq_worker(struct usb_device *dev); + +/****************************************************************** + * Interrupt polling + ******************************************************************/ +static inline void usb_kbd_poll_for_event(struct usb_device *dev) +{ +#if defined(CONFIG_SYS_USB_EVENT_POLL) + usb_event_poll(); + usb_kbd_irq_worker(dev); +#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) + struct usb_interface *iface; + iface = &dev->config.if_desc[0]; + usb_get_report(dev, iface->desc.bInterfaceNumber, + 1, 1, new, sizeof(new)); + if (memcmp(old, new, sizeof(new))) + usb_kbd_irq_worker(dev); +#endif +} + /****************************************************************** * Queue handling ******************************************************************/ @@ -121,9 +141,14 @@ static void usb_kbd_put_queue(char data) /* test if a character is in the queue */ static int usb_kbd_testc(void) { -#ifdef CONFIG_SYS_USB_EVENT_POLL - usb_event_poll(); -#endif + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + + dev = stdio_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + + usb_kbd_poll_for_event(usb_kbd_dev); + if (usb_in_pointer == usb_out_pointer) return 0; /* no data */ else @@ -133,11 +158,16 @@ static int usb_kbd_testc(void) static int usb_kbd_getc(void) { char c; - while (usb_in_pointer == usb_out_pointer) { -#ifdef CONFIG_SYS_USB_EVENT_POLL - usb_event_poll(); -#endif - } + + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + + dev = stdio_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + + while (usb_in_pointer == usb_out_pointer) + usb_kbd_poll_for_event(usb_kbd_dev); + if ((usb_out_pointer+1) == USB_KBD_BUFFER_LEN) usb_out_pointer = 0; else @@ -323,15 +353,10 @@ static int usb_kbd_translate(unsigned char scancode, unsigned char modifier, } /* Interrupt service routine */ -static int usb_kbd_irq(struct usb_device *dev) +static int usb_kbd_irq_worker(struct usb_device *dev) { int i, res; - if ((dev->irq_status != 0) || (dev->irq_act_len != 8)) { - USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n", - dev->irq_status, dev->irq_act_len); - return 1; - } res = 0; switch (new[0]) { @@ -362,6 +387,17 @@ static int usb_kbd_irq(struct usb_device *dev) return 1; /* install IRQ Handler again */ } +static int usb_kbd_irq(struct usb_device *dev) +{ + if ((dev->irq_status != 0) || (dev->irq_act_len != 8)) { + USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n", + dev->irq_status, dev->irq_act_len); + return 1; + } + + return usb_kbd_irq_worker(dev); +} + /* probes the USB device dev for keyboard type */ static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) { -- cgit v0.10.2 From 9a8c72a6c42471d503d573988510e8ec71edddaa Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 10 Oct 2011 16:34:26 +0100 Subject: USB: Rework usb_kbd.c * Support dynamic allocation of devices * Passing data via usb device privptr * Reorder functions to avoid forward declarations * Introduce generic polling mechanism to fix musb and ehci-hcd breakage due to using "extern new;" to access keyboard driver data! Signed-off-by: Marek Vasut Acked-by: Mike Frysinger Cc: Remy Bohmer Cc: Wolfgang Denk diff --git a/common/usb_kbd.c b/common/usb_kbd.c index ef440e4..2d78831 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -25,12 +25,18 @@ * */ #include +#include #include #include #include -#undef USB_KBD_DEBUG +#ifdef USB_KBD_DEBUG +#define USB_KBD_PRINTF(fmt, args...) printf(fmt, ##args) +#else +#define USB_KBD_PRINTF(fmt, args...) +#endif + /* * If overwrite_console returns 1, the stdin, stderr and stdout * are switched to the serial port, else the settings in the @@ -45,352 +51,257 @@ int overwrite_console(void) } #endif -#ifdef USB_KBD_DEBUG -#define USB_KBD_PRINTF(fmt, args...) printf(fmt, ##args) -#else -#define USB_KBD_PRINTF(fmt, args...) -#endif - - -#define REPEAT_RATE (40/4) /* 40msec -> 25cps */ -#define REPEAT_DELAY 10 /* 10 x REPEAT_RATE = 400msec */ +/* Keyboard sampling rate */ +#define REPEAT_RATE (40 / 4) /* 40msec -> 25cps */ +#define REPEAT_DELAY 10 /* 10 x REPEAT_RATE = 400msec */ #define NUM_LOCK 0x53 #define CAPS_LOCK 0x39 #define SCROLL_LOCK 0x47 - /* Modifier bits */ -#define LEFT_CNTR 0 -#define LEFT_SHIFT 1 -#define LEFT_ALT 2 -#define LEFT_GUI 3 -#define RIGHT_CNTR 4 -#define RIGHT_SHIFT 5 -#define RIGHT_ALT 6 -#define RIGHT_GUI 7 - -#define USB_KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */ - -static char usb_kbd_buffer[USB_KBD_BUFFER_LEN]; -static int usb_in_pointer; -static int usb_out_pointer; - -unsigned char new[8]; -unsigned char old[8]; -int repeat_delay; +#define LEFT_CNTR (1 << 0) +#define LEFT_SHIFT (1 << 1) +#define LEFT_ALT (1 << 2) +#define LEFT_GUI (1 << 3) +#define RIGHT_CNTR (1 << 4) +#define RIGHT_SHIFT (1 << 5) +#define RIGHT_ALT (1 << 6) +#define RIGHT_GUI (1 << 7) + +/* Size of the keyboard buffer */ +#define USB_KBD_BUFFER_LEN 0x20 + +/* Device name */ #define DEVNAME "usbkbd" -static unsigned char num_lock; -static unsigned char caps_lock; -static unsigned char scroll_lock; -static unsigned char ctrl; - -static unsigned char leds __attribute__((aligned(0x4))); -static unsigned char usb_kbd_numkey[] = { +/* Keyboard maps */ +static const unsigned char usb_kbd_numkey[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\r', 0x1b, '\b', '\t', ' ', '-', '=', '[', ']', '\\', '#', ';', '\'', '`', ',', '.', '/' }; -static unsigned char usb_kbd_numkey_shifted[] = { +static const unsigned char usb_kbd_numkey_shifted[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '\r', 0x1b, '\b', '\t', ' ', '_', '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?' }; -static int usb_kbd_irq_worker(struct usb_device *dev); +/* + * NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this + * order. See usb_kbd_setled() function! + */ +#define USB_KBD_NUMLOCK (1 << 0) +#define USB_KBD_CAPSLOCK (1 << 1) +#define USB_KBD_SCROLLLOCK (1 << 2) +#define USB_KBD_CTRL (1 << 3) -/****************************************************************** - * Interrupt polling - ******************************************************************/ -static inline void usb_kbd_poll_for_event(struct usb_device *dev) -{ -#if defined(CONFIG_SYS_USB_EVENT_POLL) - usb_event_poll(); - usb_kbd_irq_worker(dev); -#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) - struct usb_interface *iface; - iface = &dev->config.if_desc[0]; - usb_get_report(dev, iface->desc.bInterfaceNumber, - 1, 1, new, sizeof(new)); - if (memcmp(old, new, sizeof(new))) - usb_kbd_irq_worker(dev); -#endif -} +#define USB_KBD_LEDMASK \ + (USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK) -/****************************************************************** - * Queue handling - ******************************************************************/ -/* puts character in the queue and sets up the in and out pointer */ -static void usb_kbd_put_queue(char data) -{ - if ((usb_in_pointer+1) == USB_KBD_BUFFER_LEN) { - if (usb_out_pointer == 0) - return; /* buffer full */ - else - usb_in_pointer = 0; - } else { - if ((usb_in_pointer+1) == usb_out_pointer) - return; /* buffer full */ - usb_in_pointer++; - } - usb_kbd_buffer[usb_in_pointer] = data; - return; -} +struct usb_kbd_pdata { + uint32_t repeat_delay; -/* test if a character is in the queue */ -static int usb_kbd_testc(void) -{ - struct stdio_dev *dev; - struct usb_device *usb_kbd_dev; + uint32_t usb_in_pointer; + uint32_t usb_out_pointer; + uint8_t usb_kbd_buffer[USB_KBD_BUFFER_LEN]; - dev = stdio_get_by_name("usbkbd"); - usb_kbd_dev = (struct usb_device *)dev->priv; + uint8_t new[8]; + uint8_t old[8]; - usb_kbd_poll_for_event(usb_kbd_dev); + uint8_t flags; +}; - if (usb_in_pointer == usb_out_pointer) - return 0; /* no data */ - else - return 1; -} -/* gets the character from the queue */ -static int usb_kbd_getc(void) +/* Generic keyboard event polling. */ +void usb_kbd_generic_poll(void) { - char c; - struct stdio_dev *dev; struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; - dev = stdio_get_by_name("usbkbd"); + /* Get the pointer to USB Keyboard device pointer */ + dev = stdio_get_by_name(DEVNAME); usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); - while (usb_in_pointer == usb_out_pointer) - usb_kbd_poll_for_event(usb_kbd_dev); - - if ((usb_out_pointer+1) == USB_KBD_BUFFER_LEN) - usb_out_pointer = 0; - else - usb_out_pointer++; - c = usb_kbd_buffer[usb_out_pointer]; - return (int)c; - + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, data->new, + maxp > 8 ? 8 : maxp, ep->bInterval); } -/* forward decleration */ -static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum); - -/* search for keyboard and register it if found */ -int drv_usb_kbd_init(void) +/* Puts character in the queue and sets up the in and out pointer. */ +static void usb_kbd_put_queue(struct usb_kbd_pdata *data, char c) { - int error, i; - struct stdio_dev usb_kbd_dev, *old_dev; - struct usb_device *dev; - char *stdinname = getenv("stdin"); + if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) { + /* Check for buffer full. */ + if (data->usb_out_pointer == 0) + return; - usb_in_pointer = 0; - usb_out_pointer = 0; - /* scan all USB Devices */ - for (i = 0 ; i < USB_MAX_DEVICE ; i++) { - dev = usb_get_dev_index(i); /* get device */ - if (dev == NULL) - return -1; - if (dev->devnum != -1) { - /* Ok, we found a keyboard */ - if (usb_kbd_probe(dev, 0) == 1) { - /* check, if it is already registered */ - USB_KBD_PRINTF("USB KBD found set up " - "device.\n"); - old_dev = stdio_get_by_name(DEVNAME); - if (old_dev) { - /* already registered, just return ok */ - USB_KBD_PRINTF("USB KBD is already " - "registered.\n"); - return 1; - } - /* register the keyboard */ - USB_KBD_PRINTF("USB KBD register.\n"); - memset(&usb_kbd_dev, 0, - sizeof(struct stdio_dev)); - strcpy(usb_kbd_dev.name, DEVNAME); - usb_kbd_dev.flags = DEV_FLAGS_INPUT | - DEV_FLAGS_SYSTEM; - usb_kbd_dev.putc = NULL; - usb_kbd_dev.puts = NULL; - usb_kbd_dev.getc = usb_kbd_getc; - usb_kbd_dev.tstc = usb_kbd_testc; - usb_kbd_dev.priv = (void *)dev; - error = stdio_register(&usb_kbd_dev); - if (error == 0) { - /* - * check if this is the standard - * input device - */ - if (strcmp(stdinname, DEVNAME) == 0) { - /* reassign the console */ - if (overwrite_console()) - return 1; - error = console_assign(stdin, - DEVNAME); - if (error == 0) - return 1; - else - return error; - } - return 1; - } - return error; - } - } - } - /* no USB Keyboard found */ - return -1; -} + data->usb_in_pointer = 0; + } else { + /* Check for buffer full. */ + if (data->usb_in_pointer == data->usb_out_pointer - 1) + return; + data->usb_in_pointer++; + } -/* deregistering the keyboard */ -int usb_kbd_deregister(void) -{ -#ifdef CONFIG_SYS_STDIO_DEREGISTER - return stdio_deregister(DEVNAME); -#else - return 1; -#endif + data->usb_kbd_buffer[data->usb_in_pointer] = c; } -/************************************************************************** - * Low Level drivers +/* + * Set the LEDs. Since this is used in the irq routine, the control job is + * issued with a timeout of 0. This means, that the job is queued without + * waiting for job completion. */ - -/* set the LEDs. Since this is used in the irq routine, the control job - is issued with a timeout of 0. This means, that the job is queued without - waiting for job completion */ - static void usb_kbd_setled(struct usb_device *dev) { - struct usb_interface *iface; - iface = &dev->config.if_desc[0]; - leds = 0; - if (scroll_lock != 0) - leds |= 1; - leds <<= 1; - if (caps_lock != 0) - leds |= 1; - leds <<= 1; - if (num_lock != 0) - leds |= 1; + struct usb_interface *iface = &dev->config.if_desc[0]; + struct usb_kbd_pdata *data = dev->privptr; + uint32_t leds = data->flags & USB_KBD_LEDMASK; + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x200, iface->desc.bInterfaceNumber, (void *)&leds, 1, 0); - } - -#define CAPITAL_MASK 0x20 +#define CAPITAL_MASK 0x20 /* Translate the scancode in ASCII */ -static int usb_kbd_translate(unsigned char scancode, unsigned char modifier, - int pressed) +static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode, + unsigned char modifier, int pressed) { - unsigned char keycode; + uint8_t keycode = 0; + /* Key released */ if (pressed == 0) { - /* key released */ - repeat_delay = 0; + data->repeat_delay = 0; return 0; } + if (pressed == 2) { - repeat_delay++; - if (repeat_delay < REPEAT_DELAY) + data->repeat_delay++; + if (data->repeat_delay < REPEAT_DELAY) return 0; - repeat_delay = REPEAT_DELAY; + + data->repeat_delay = REPEAT_DELAY; } - keycode = 0; - if ((scancode > 3) && (scancode <= 0x1d)) { /* alpha numeric values */ - keycode = scancode - 4 + 0x61; - if (caps_lock) - /* switch to capital Letters */ + + /* Alphanumeric values */ + if ((scancode > 3) && (scancode <= 0x1d)) { + keycode = scancode - 4 + 'a'; + + if (data->flags & USB_KBD_CAPSLOCK) keycode &= ~CAPITAL_MASK; - if (((modifier&(1 << LEFT_SHIFT)) != 0) || - ((modifier&(1 << RIGHT_SHIFT)) != 0)) { + + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) { + /* Handle CAPSLock + Shift pressed simultaneously */ if (keycode & CAPITAL_MASK) - /* switch to capital Letters */ keycode &= ~CAPITAL_MASK; else - /* switch to non capital Letters */ keycode |= CAPITAL_MASK; } } - if ((scancode > 0x1d) && (scancode < 0x3A)) { - if (((modifier&(1 << LEFT_SHIFT)) != 0) || - ((modifier&(1 << RIGHT_SHIFT)) != 0)) /* shifted */ - keycode = usb_kbd_numkey_shifted[scancode-0x1e]; - else /* non shifted */ - keycode = usb_kbd_numkey[scancode-0x1e]; + + if ((scancode > 0x1d) && (scancode < 0x3a)) { + /* Shift pressed */ + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) + keycode = usb_kbd_numkey_shifted[scancode - 0x1e]; + else + keycode = usb_kbd_numkey[scancode - 0x1e]; } - if (ctrl) + if (data->flags & USB_KBD_CTRL) keycode = scancode - 0x3; if (pressed == 1) { if (scancode == NUM_LOCK) { - num_lock = ~num_lock; + data->flags ^= USB_KBD_NUMLOCK; return 1; } + if (scancode == CAPS_LOCK) { - caps_lock = ~caps_lock; + data->flags ^= USB_KBD_CAPSLOCK; return 1; } if (scancode == SCROLL_LOCK) { - scroll_lock = ~scroll_lock; + data->flags ^= USB_KBD_SCROLLLOCK; return 1; } } - if (keycode != 0) { + + /* Report keycode if any */ + if (keycode) { USB_KBD_PRINTF("%c", keycode); - usb_kbd_put_queue(keycode); + usb_kbd_put_queue(data, keycode); } + return 0; } +static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up) +{ + uint32_t res = 0; + struct usb_kbd_pdata *data = dev->privptr; + uint8_t *new; + uint8_t *old; + + if (up) { + new = data->old; + old = data->new; + } else { + new = data->new; + old = data->old; + } + + if ((old[i] > 3) && (memscan(new + 2, old[i], 6) == new + 8)) + res |= usb_kbd_translate(data, old[i], data->new[0], up); + + return res; +} + /* Interrupt service routine */ static int usb_kbd_irq_worker(struct usb_device *dev) { - int i, res; + struct usb_kbd_pdata *data = dev->privptr; + int i, res = 0; - res = 0; - - switch (new[0]) { - case 0x0: /* No combo key pressed */ - ctrl = 0; - break; - case 0x01: /* Left Ctrl pressed */ - case 0x10: /* Right Ctrl pressed */ - ctrl = 1; - break; - } + /* No combo key pressed */ + if (data->new[0] == 0x00) + data->flags &= ~USB_KBD_CTRL; + /* Left or Right Ctrl pressed */ + else if ((data->new[0] == LEFT_CNTR) || (data->new[0] == RIGHT_CNTR)) + data->flags |= USB_KBD_CTRL; for (i = 2; i < 8; i++) { - if (old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) - res |= usb_kbd_translate(old[i], new[0], 0); - - if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) - res |= usb_kbd_translate(new[i], new[0], 1); + res |= usb_kbd_service_key(dev, i, 0); + res |= usb_kbd_service_key(dev, i, 1); } - if ((new[2] > 3) && (old[2] == new[2])) /* still pressed */ - res |= usb_kbd_translate(new[2], new[0], 2); + /* Key is still pressed */ + if ((data->new[2] > 3) && (data->old[2] == data->new[2])) + res |= usb_kbd_translate(data, data->new[2], data->new[0], 2); + if (res == 1) usb_kbd_setled(dev); - memcpy(&old[0], &new[0], 8); + memcpy(data->old, data->new, 8); - return 1; /* install IRQ Handler again */ + return 1; } +/* Keyboard interrupt handler */ static int usb_kbd_irq(struct usb_device *dev) { if ((dev->irq_status != 0) || (dev->irq_act_len != 8)) { - USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n", + USB_KBD_PRINTF("USB KBD: Error %lX, len %d\n", dev->irq_status, dev->irq_act_len); return 1; } @@ -398,50 +309,202 @@ static int usb_kbd_irq(struct usb_device *dev) return usb_kbd_irq_worker(dev); } -/* probes the USB device dev for keyboard type */ +/* Interrupt polling */ +static inline void usb_kbd_poll_for_event(struct usb_device *dev) +{ +#if defined(CONFIG_SYS_USB_EVENT_POLL) + usb_event_poll(); + usb_kbd_irq_worker(dev); +#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) + struct usb_interface *iface; + struct usb_kbd_pdata *data = dev->privptr; + iface = &dev->config.if_desc[0]; + usb_get_report(dev, iface->desc.bInterfaceNumber, + 1, 1, data->new, sizeof(data->new)); + if (memcmp(data->old, data->new, sizeof(data->new))) + usb_kbd_irq_worker(dev); +#endif +} + +/* test if a character is in the queue */ +static int usb_kbd_testc(void) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + + dev = stdio_get_by_name(DEVNAME); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + usb_kbd_poll_for_event(usb_kbd_dev); + + return !(data->usb_in_pointer == data->usb_out_pointer); +} + +/* gets the character from the queue */ +static int usb_kbd_getc(void) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + + dev = stdio_get_by_name(DEVNAME); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + while (data->usb_in_pointer == data->usb_out_pointer) + usb_kbd_poll_for_event(usb_kbd_dev); + + if (data->usb_out_pointer == USB_KBD_BUFFER_LEN - 1) + data->usb_out_pointer = 0; + else + data->usb_out_pointer++; + + return data->usb_kbd_buffer[data->usb_out_pointer]; +} + +/* probes the USB device dev for keyboard type. */ static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_interface *iface; struct usb_endpoint_descriptor *ep; + struct usb_kbd_pdata *data; int pipe, maxp; if (dev->descriptor.bNumConfigurations != 1) return 0; + iface = &dev->config.if_desc[ifnum]; if (iface->desc.bInterfaceClass != 3) return 0; + if (iface->desc.bInterfaceSubClass != 1) return 0; + if (iface->desc.bInterfaceProtocol != 1) return 0; + if (iface->desc.bNumEndpoints != 1) return 0; ep = &iface->ep_desc[0]; + /* Check if endpoint 1 is interrupt endpoint */ if (!(ep->bEndpointAddress & 0x80)) return 0; + if ((ep->bmAttributes & 3) != 3) return 0; - USB_KBD_PRINTF("USB KBD found set protocol...\n"); - /* ok, we found a USB Keyboard, install it */ - /* usb_kbd_get_hid_desc(dev); */ - usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); - USB_KBD_PRINTF("USB KBD found set idle...\n"); - usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE, 0); - memset(&new[0], 0, 8); - memset(&old[0], 0, 8); - repeat_delay = 0; + + USB_KBD_PRINTF("USB KBD: found set protocol...\n"); + + data = malloc(sizeof(struct usb_kbd_pdata)); + if (!data) { + printf("USB KBD: Error allocating private data\n"); + return 0; + } + + /* Clear private data */ + memset(data, 0, sizeof(struct usb_kbd_pdata)); + + /* Insert private data into USB device structure */ + dev->privptr = data; + + /* Set IRQ handler */ + dev->irq_handle = usb_kbd_irq; + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); maxp = usb_maxpacket(dev, pipe); - dev->irq_handle = usb_kbd_irq; - USB_KBD_PRINTF("USB KBD enable interrupt pipe...\n"); - usb_submit_int_msg(dev, pipe, &new[0], maxp > 8 ? 8 : maxp, + + /* We found a USB Keyboard, install it. */ + usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); + + USB_KBD_PRINTF("USB KBD: found set idle...\n"); + usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE, 0); + + USB_KBD_PRINTF("USB KBD: enable interrupt pipe...\n"); + usb_submit_int_msg(dev, pipe, data->new, maxp > 8 ? 8 : maxp, ep->bInterval); + + /* Success. */ return 1; } +/* Search for keyboard and register it if found. */ +int drv_usb_kbd_init(void) +{ + struct stdio_dev usb_kbd_dev, *old_dev; + struct usb_device *dev; + char *stdinname = getenv("stdin"); + int error, i; + + /* Scan all USB Devices */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + /* Get USB device. */ + dev = usb_get_dev_index(i); + if (!dev) + return -1; + + if (dev->devnum == -1) + continue; + + /* Try probing the keyboard */ + if (usb_kbd_probe(dev, 0) != 1) + continue; + + /* We found a keyboard, check if it is already registered. */ + USB_KBD_PRINTF("USB KBD: found set up device.\n"); + old_dev = stdio_get_by_name(DEVNAME); + if (old_dev) { + /* Already registered, just return ok. */ + USB_KBD_PRINTF("USB KBD: is already registered.\n"); + return 1; + } + + /* Register the keyboard */ + USB_KBD_PRINTF("USB KBD: register.\n"); + memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); + strcpy(usb_kbd_dev.name, DEVNAME); + usb_kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + usb_kbd_dev.putc = NULL; + usb_kbd_dev.puts = NULL; + usb_kbd_dev.getc = usb_kbd_getc; + usb_kbd_dev.tstc = usb_kbd_testc; + usb_kbd_dev.priv = (void *)dev; + error = stdio_register(&usb_kbd_dev); + if (error) + return error; + + /* Check if this is the standard input device. */ + if (strcmp(stdinname, DEVNAME)) + return 1; + + /* Reassign the console */ + if (overwrite_console()) + return 1; + + error = console_assign(stdin, DEVNAME); + if (error) + return error; + + return 1; + } + + /* No USB Keyboard found */ + return -1; +} + +/* Deregister the keyboard. */ +int usb_kbd_deregister(void) +{ +#ifdef CONFIG_SYS_STDIO_DEREGISTER + return stdio_deregister(DEVNAME); +#else + return 1; +#endif +} #if 0 struct usb_hid_descriptor { -- cgit v0.10.2 From 5a1219efe912855f0fc64471d31c1c392d8ad488 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 10 Oct 2011 17:34:27 +0200 Subject: USB: Drop dead code from usb_kbd.c Signed-off-by: Marek Vasut Acked-by: Mike Frysinger Cc: Remy Bohmer Cc: Wolfgang Denk diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 2d78831..75107c9 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -505,382 +505,3 @@ int usb_kbd_deregister(void) return 1; #endif } - -#if 0 -struct usb_hid_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; /* 0x21 for HID */ - unsigned short bcdHID; /* release number */ - unsigned char bCountryCode; - unsigned char bNumDescriptors; - unsigned char bReportDescriptorType; - unsigned short wDescriptorLength; -} __packed; - -/* - * We parse each description item into this structure. Short items data - * values are expanded to 32-bit signed int, long items contain a pointer - * into the data area. - */ - -struct hid_item { - unsigned char format; - unsigned char size; - unsigned char type; - unsigned char tag; - union { - unsigned char u8; - char s8; - unsigned short u16; - short s16; - unsigned long u32; - long s32; - unsigned char *longdata; - } data; -}; - -/* - * HID report item format - */ - -#define HID_ITEM_FORMAT_SHORT 0 -#define HID_ITEM_FORMAT_LONG 1 - -/* - * Special tag indicating long items - */ - -#define HID_ITEM_TAG_LONG 15 - - -static struct usb_hid_descriptor usb_kbd_hid_desc; - -void usb_kbd_display_hid(struct usb_hid_descriptor *hid) -{ - printf("USB_HID_DESC:\n"); - printf(" bLenght 0x%x\n", hid->bLength); - printf(" bcdHID 0x%x\n", hid->bcdHID); - printf(" bCountryCode %d\n", hid->bCountryCode); - printf(" bNumDescriptors 0x%x\n", hid->bNumDescriptors); - printf(" bReportDescriptorType 0x%x\n", hid->bReportDescriptorType); - printf(" wDescriptorLength 0x%x\n", hid->wDescriptorLength); -} - - -/* - * Fetch a report description item from the data stream. We support long - * items, though they are not used yet. - */ - -static int fetch_item(unsigned char *start, unsigned char *end, - struct hid_item *item) -{ - if ((end - start) > 0) { - unsigned char b = *start++; - item->type = (b >> 2) & 3; - item->tag = (b >> 4) & 15; - if (item->tag == HID_ITEM_TAG_LONG) { - item->format = HID_ITEM_FORMAT_LONG; - if ((end - start) >= 2) { - item->size = *start++; - item->tag = *start++; - if ((end - start) >= item->size) { - item->data.longdata = start; - start += item->size; - return item->size; - } - } - } else { - item->format = HID_ITEM_FORMAT_SHORT; - item->size = b & 3; - switch (item->size) { - case 0: - return item->size; - case 1: - if ((end - start) >= 1) { - item->data.u8 = *start++; - return item->size; - } - break; - case 2: - if ((end - start) >= 2) { - item->data.u16 = le16_to_cpu( - (unsigned short *)start); - start += 2; - return item->size; - } - case 3: - item->size++; - if ((end - start) >= 4) { - item->data.u32 = le32_to_cpu( - (unsigned long *)start); - start += 4; - return item->size; - } - } - } - } - return -1; -} - -/* - * HID report descriptor item type (prefix bit 2, 3) - */ - -#define HID_ITEM_TYPE_MAIN 0 -#define HID_ITEM_TYPE_GLOBAL 1 -#define HID_ITEM_TYPE_LOCAL 2 -#define HID_ITEM_TYPE_RESERVED 3 -/* - * HID report descriptor main item tags - */ - -#define HID_MAIN_ITEM_TAG_INPUT 8 -#define HID_MAIN_ITEM_TAG_OUTPUT 9 -#define HID_MAIN_ITEM_TAG_FEATURE 11 -#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 -#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 -/* - * HID report descriptor main item contents - */ - -#define HID_MAIN_ITEM_CONSTANT 0x001 -#define HID_MAIN_ITEM_VARIABLE 0x002 -#define HID_MAIN_ITEM_RELATIVE 0x004 -#define HID_MAIN_ITEM_WRAP 0x008 -#define HID_MAIN_ITEM_NONLINEAR 0x010 -#define HID_MAIN_ITEM_NO_PREFERRED 0x020 -#define HID_MAIN_ITEM_NULL_STATE 0x040 -#define HID_MAIN_ITEM_VOLATILE 0x080 -#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 - -/* - * HID report descriptor collection item types - */ - -#define HID_COLLECTION_PHYSICAL 0 -#define HID_COLLECTION_APPLICATION 1 -#define HID_COLLECTION_LOGICAL 2 -/* - * HID report descriptor global item tags - */ - -#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 -#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 -#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 -#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 -#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 -#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 -#define HID_GLOBAL_ITEM_TAG_UNIT 6 -#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 -#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 -#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 -#define HID_GLOBAL_ITEM_TAG_PUSH 10 -#define HID_GLOBAL_ITEM_TAG_POP 11 - -/* - * HID report descriptor local item tags - */ - -#define HID_LOCAL_ITEM_TAG_USAGE 0 -#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 -#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 -#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 -#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 -#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 -#define HID_LOCAL_ITEM_TAG_DELIMITER 10 - - -static void usb_kbd_show_item(struct hid_item *item) -{ - switch (item->type) { - case HID_ITEM_TYPE_MAIN: - switch (item->tag) { - case HID_MAIN_ITEM_TAG_INPUT: - printf("Main Input"); - break; - case HID_MAIN_ITEM_TAG_OUTPUT: - printf("Main Output"); - break; - case HID_MAIN_ITEM_TAG_FEATURE: - printf("Main Feature"); - break; - case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: - printf("Main Begin Collection"); - break; - case HID_MAIN_ITEM_TAG_END_COLLECTION: - printf("Main End Collection"); - break; - default: - printf("Main reserved %d", item->tag); - break; - } - break; - case HID_ITEM_TYPE_GLOBAL: - switch (item->tag) { - case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: - printf("- Global Usage Page"); - break; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: - printf("- Global Logical Minimum"); - break; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: - printf("- Global Logical Maximum"); - break; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: - printf("- Global physical Minimum"); - break; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: - printf("- Global physical Maximum"); - break; - case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: - printf("- Global Unit Exponent"); - break; - case HID_GLOBAL_ITEM_TAG_UNIT: - printf("- Global Unit"); - break; - case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: - printf("- Global Report Size"); - break; - case HID_GLOBAL_ITEM_TAG_REPORT_ID: - printf("- Global Report ID"); - break; - case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: - printf("- Global Report Count"); - break; - case HID_GLOBAL_ITEM_TAG_PUSH: - printf("- Global Push"); - break; - case HID_GLOBAL_ITEM_TAG_POP: - printf("- Global Pop"); - break; - default: - printf("- Global reserved %d", item->tag); - break; - } - break; - case HID_ITEM_TYPE_LOCAL: - switch (item->tag) { - case HID_LOCAL_ITEM_TAG_USAGE: - printf("-- Local Usage"); - break; - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: - printf("-- Local Usage Minimum"); - break; - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: - printf("-- Local Usage Maximum"); - break; - case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX: - printf("-- Local Designator Index"); - break; - case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM: - printf("-- Local Designator Minimum"); - break; - case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM: - printf("-- Local Designator Maximum"); - break; - case HID_LOCAL_ITEM_TAG_STRING_INDEX: - printf("-- Local String Index"); - break; - case HID_LOCAL_ITEM_TAG_STRING_MINIMUM: - printf("-- Local String Minimum"); - break; - case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM: - printf("-- Local String Maximum"); - break; - case HID_LOCAL_ITEM_TAG_DELIMITER: - printf("-- Local Delimiter"); - break; - default: - printf("-- Local reserved %d", item->tag); - break; - } - break; - default: - printf("--- reserved %d", item->type); - break; - } - switch (item->size) { - case 1: - printf(" %d", item->data.u8); - break; - case 2: - printf(" %d", item->data.u16); - break; - case 4: - printf(" %ld", item->data.u32); - break; - } - printf("\n"); -} - - -static int usb_kbd_get_hid_desc(struct usb_device *dev) -{ - unsigned char buffer[256]; - struct usb_descriptor_header *head; - struct usb_config_descriptor *config; - int index, len, i; - unsigned char *start, *end; - struct hid_item item; - - if (usb_get_configuration_no(dev, &buffer[0], 0) == -1) - return -1; - head = (struct usb_descriptor_header *)&buffer[0]; - if (head->bDescriptorType != USB_DT_CONFIG) { - printf(" ERROR: NOT USB_CONFIG_DESC %x\n", - head->bDescriptorType); - return -1; - } - index = head->bLength; - config = (struct usb_config_descriptor *)&buffer[0]; - len = le16_to_cpu(config->wTotalLength); - /* - * Ok the first entry must be a configuration entry, - * now process the others - */ - head = (struct usb_descriptor_header *)&buffer[index]; - while (index+1 < len) { - if (head->bDescriptorType == USB_DT_HID) { - printf("HID desc found\n"); - memcpy(&usb_kbd_hid_desc, &buffer[index], - buffer[index]); - le16_to_cpus(&usb_kbd_hid_desc.bcdHID); - le16_to_cpus(&usb_kbd_hid_desc.wDescriptorLength); - usb_kbd_display_hid(&usb_kbd_hid_desc); - len = 0; - break; - } - index += head->bLength; - head = (struct usb_descriptor_header *)&buffer[index]; - } - if (len > 0) - return -1; - len = usb_kbd_hid_desc.wDescriptorLength; - index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], - len); - if (index < 0) { - printf("reading report descriptor failed\n"); - return -1; - } - printf(" report descriptor (size %u, read %d)\n", len, index); - start = &buffer[0]; - end = &buffer[len]; - i = 0; - do { - index = fetch_item(start, end, &item); - i += index; - i++; - if (index >= 0) - usb_kbd_show_item(&item); - - start += index; - start++; - } while (index >= 0); - -} - -#endif -- cgit v0.10.2 From 5f6aa03fda2a0a79940765865c1e4266be8a75f8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 7 Oct 2011 02:00:13 +0200 Subject: USB: Fix complaints about strict aliasing in OHCI-HCD Signed-off-by: Marek Vasut diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 60c9595..cf906b4 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1262,12 +1262,19 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, int len = 0; int stat = 0; __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; + union { + void *ptr; + __u8 *u8; + __u16 *u16; + __u32 *u32; + } databuf; __u16 bmRType_bReq; __u16 wValue; __u16 wIndex; __u16 wLength; + databuf.u32 = (__u32 *)datab; + #ifdef DEBUG pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); @@ -1297,20 +1304,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, */ case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16(1); + databuf.u16[0] = cpu_to_le16(1); OK(2); case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16(0); + databuf.u16[0] = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16(0); + databuf.u16[0] = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32( + databuf.u32[0] = cpu_to_le32( RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); OK(4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT); + databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT); OK(4); case RH_CLEAR_FEATURE | RH_ENDPOINT: @@ -1374,14 +1381,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_dev_des), wLength)); - data_buf = root_hub_dev_des; OK(len); + databuf.ptr = root_hub_dev_des; OK(len); case (0x02): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_config_des), wLength)); - data_buf = root_hub_config_des; OK(len); + databuf.ptr = root_hub_config_des; OK(len); case (0x03): /* string descriptors */ if (wValue == 0x0300) { len = min_t(unsigned int, @@ -1389,7 +1396,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index0), wLength)); - data_buf = root_hub_str_index0; + databuf.ptr = root_hub_str_index0; OK(len); } if (wValue == 0x0301) { @@ -1398,7 +1405,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index1), wLength)); - data_buf = root_hub_str_index1; + databuf.ptr = root_hub_str_index1; OK(len); } default: @@ -1410,41 +1417,45 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, { __u32 temp = roothub_a(&gohci); - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; + databuf.u8[0] = 9; /* min length; */ + databuf.u8[1] = 0x29; + databuf.u8[2] = temp & RH_A_NDP; #ifdef CONFIG_AT91C_PQFP_UHPBUG - data_buf [2] = (data_buf [2] == 2) ? 1:0; + databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0; #endif - data_buf [3] = 0; + databuf.u8[3] = 0; if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; + databuf.u8[3] |= 0x1; if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; + databuf.u8[3] |= 0x10; else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; + databuf.u8[3] |= 0x8; - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; + /* corresponds to databuf.u8[4-7] */ + databuf.u8[1] = 0; + databuf.u8[5] = (temp & RH_A_POTPGT) >> 24; temp = roothub_b(&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; + databuf.u8[7] = temp & RH_B_DR; + if (databuf.u8[2] < 7) { + databuf.u8[8] = 0xff; } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; + databuf.u8[0] += 2; + databuf.u8[8] = (temp & RH_B_DR) >> 8; + databuf.u8[10] = databuf.u8[9] = 0xff; } len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); + min_t(unsigned int, databuf.u8[0], wLength)); OK(len); } - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1); + case RH_GET_CONFIGURATION: + databuf.u8[0] = 0x01; + OK(1); - case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0); + case RH_SET_CONFIGURATION: + WR_RH_STAT(0x10000); + OK(0); default: dbg("unsupported root hub command"); @@ -1458,8 +1469,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, #endif len = min_t(int, len, leni); - if (data != data_buf) - memcpy(data, data_buf, len); + if (data != databuf.ptr) + memcpy(data, databuf.ptr, len); dev->act_len = len; dev->status = stat; -- cgit v0.10.2 From 26cc5129ee647efdb88570b82994c0d19f1a2a8c Mon Sep 17 00:00:00 2001 From: Lei Wen Date: Wed, 5 Oct 2011 08:11:40 -0700 Subject: USB: gadaget: add Marvell controller support Signed-off-by: Lei Wen diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e72713..820ca9b 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o +COBJS-$(CONFIG_MV_UDC) += mv_udc.o else # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 9bb7e2e..5d7b638 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -150,6 +150,11 @@ #define gadget_is_m66592(g) 0 #endif +#ifdef CONFIG_USB_GADGET_MV +#define gadget_is_mv(g) (!strcmp("mv_udc", (g)->name)) +#else +#define gadget_is_mv(g) 0 +#endif /* * CONFIG_USB_GADGET_SX2 @@ -216,5 +221,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x20; else if (gadget_is_m66592(gadget)) return 0x21; + else if (gadget_is_mv(gadget)) + return 0x22; return -ENOENT; } diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c new file mode 100644 index 0000000..cdbdcfa --- /dev/null +++ b/drivers/usb/gadget/mv_udc.c @@ -0,0 +1,499 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG +#define DBG(x...) do {} while (0) +#else +#define DBG(x...) printf(x) +static const char *reqname(unsigned r) +{ + switch (r) { + case USB_REQ_GET_STATUS: return "GET_STATUS"; + case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE"; + case USB_REQ_SET_FEATURE: return "SET_FEATURE"; + case USB_REQ_SET_ADDRESS: return "SET_ADDRESS"; + case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR"; + case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR"; + case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION"; + case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION"; + case USB_REQ_GET_INTERFACE: return "GET_INTERFACE"; + case USB_REQ_SET_INTERFACE: return "SET_INTERFACE"; + default: return "*UNKNOWN*"; + } +} +#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, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static struct usb_endpoint_descriptor ep0_in_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .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); +static int mv_ep_disable(struct usb_ep *ep); +static int mv_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags); +static struct usb_request * +mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags); +static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req); + +static struct usb_gadget_ops mv_udc_ops = { + .pullup = mv_pullup, +}; + +static struct usb_ep_ops mv_ep_ops = { + .enable = mv_ep_enable, + .disable = mv_ep_disable, + .queue = mv_ep_queue, + .alloc_request = mv_ep_alloc_request, + .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", + }, +}; + +static struct usb_request * +mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) +{ + struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); + return &mv_ep->req; +} + +static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + return; +} + +static void ep_enable(int num, int in) +{ + struct ept_queue_head *head; + struct mv_udc *udc = controller.udc; + unsigned n; + head = epts + 2*num + in; + + n = readl(&udc->epctrl[num]); + if (in) + n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK); + else + n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK); + + if (num != 0) + head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT; + writel(n, &udc->epctrl[num]); +} + +static int mv_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); + int num, in; + num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep_enable(num, in); + mv_ep->desc = desc; + return 0; +} + +static int mv_ep_disable(struct usb_ep *ep) +{ + return 0; +} + +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 ept_queue_item *item; + struct ept_queue_head *head; + unsigned phys; + 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; + phys = (unsigned)req->buf; + len = req->length; + + item->next = TERMINATE; + item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; + item->page0 = phys; + item->page1 = (phys & 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); + + if (in) + bit = EPT_TX(num); + else + bit = EPT_RX(num); + + flush_cache(phys, len); + flush_cache((unsigned long)item, sizeof(struct ept_queue_item)); + writel(bit, &udc->epprime); + + return 0; +} + +static void handle_ep_complete(struct mv_ep *ep) +{ + struct ept_queue_item *item; + int num, in, len; + num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + if (num == 0) + ep->desc = &ep0_out_desc; + item = items[2 * num + in]; + + if (item->info & 0xff) + printf("EP%d/%s FAIL nfo=%x pg0=%x\n", + num, in ? "in" : "out", item->info, item->page0); + + len = (item->info >> 16) & 0x7fff; + ep->req.length -= len; + DBG("ept%d %s complete %x\n", + num, in ? "in" : "out", len); + ep->req.complete(&ep->ep, &ep->req); + if (num == 0) { + ep->req.length = 0; + usb_ep_queue(&ep->ep, &ep->req, 0); + ep->desc = &ep0_in_desc; + } +} + +#define SETUP(type, request) (((type) << 8) | (request)) + +static void handle_setup(void) +{ + struct usb_request *req = &ep[0].req; + struct mv_udc *udc = controller.udc; + struct ept_queue_head *head; + struct usb_ctrlrequest r; + int status = 0; + int num, in, _num, _in, i; + char *buf; + head = epts; + + flush_cache((unsigned long)head, sizeof(struct ept_queue_head)); + 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), + r.bRequestType, r.bRequest, r.wIndex, r.wValue); + + switch (SETUP(r.bRequestType, r.bRequest)) { + case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): + _num = r.wIndex & 15; + _in = !!(r.wIndex & 0x80); + + if ((r.wValue == 0) && (r.wLength == 0)) { + req->length = 0; + for (i = 0; i < NUM_ENDPOINTS; i++) { + if (!ep[i].desc) + continue; + num = ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (ep[i].desc->bEndpointAddress + & USB_DIR_IN) != 0; + if ((num == _num) && (in == _in)) { + ep_enable(num, in); + usb_ep_queue(controller.gadget.ep0, + req, 0); + break; + } + } + } + return; + + case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS): + /* + * write address delayed (will take effect + * after the next IN txn) + */ + writel((r.wValue << 25) | (1 << 24), &udc->devaddr); + req->length = 0; + usb_ep_queue(controller.gadget.ep0, req, 0); + return; + + case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS): + req->length = 2; + buf = (char *)req->buf; + buf[0] = 1 << USB_DEVICE_SELF_POWERED; + buf[1] = 0; + usb_ep_queue(controller.gadget.ep0, req, 0); + return; + } + /* pass request up to the gadget driver */ + if (controller.driver) + status = controller.driver->setup(&controller.gadget, &r); + else + status = -ENODEV; + + if (!status) + return; + DBG("STALL reqname %s type %x value %x, index %x\n", + reqname(r.bRequest), r.bRequestType, r.wValue, r.wIndex); + writel((1<<16) | (1 << 0), &udc->epctrl[0]); +} + +static void stop_activity(void) +{ + int i, num, in; + struct ept_queue_head *head; + struct mv_udc *udc = controller.udc; + writel(readl(&udc->epcomp), &udc->epcomp); + writel(readl(&udc->epstat), &udc->epstat); + writel(0xffffffff, &udc->epflush); + + /* error out any pending reqs */ + for (i = 0; i < NUM_ENDPOINTS; i++) { + if (i != 0) + writel(0, &udc->epctrl[i]); + if (ep[i].desc) { + num = ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; + head = epts + (num * 2) + (in); + head->info = INFO_ACTIVE; + } + } +} + +void udc_irq(void) +{ + struct mv_udc *udc = controller.udc; + unsigned n = readl(&udc->usbsts); + writel(n, &udc->usbsts); + int bit, i, num, in; + + n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI); + if (n == 0) + return; + + if (n & STS_URI) { + DBG("-- reset --\n"); + stop_activity(); + } + if (n & STS_SLI) + DBG("-- suspend --\n"); + + if (n & STS_PCI) { + DBG("-- portchange --\n"); + bit = (readl(&udc->portsc) >> 26) & 3; + 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; + } else { + controller.gadget.speed = USB_SPEED_FULL; + } + } + + if (n & STS_UEI) + printf("\n", readl(&udc->epcomp)); + + if ((n & STS_UI) || (n & STS_UEI)) { + n = readl(&udc->epstat); + if (n & EPT_RX(0)) + handle_setup(); + + n = readl(&udc->epcomp); + if (n != 0) + writel(n, &udc->epcomp); + + for (i = 0; i < NUM_ENDPOINTS && n; i++) { + if (ep[i].desc) { + num = ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (ep[i].desc->bEndpointAddress + & USB_DIR_IN) != 0; + bit = (in) ? EPT_TX(num) : EPT_RX(num); + if (n & bit) + handle_ep_complete(&ep[i]); + } + } + } +} + +int usb_gadget_handle_interrupts(void) +{ + u32 value; + struct mv_udc *udc = controller.udc; + + value = readl(&udc->usbsts); + if (value) + udc_irq(); + + return value; +} + +static int mv_pullup(struct usb_gadget *gadget, int is_on) +{ + struct mv_udc *udc = controller.udc; + if (is_on) { + /* RESET */ + writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); + udelay(200); + + writel((unsigned) epts, &udc->epinitaddr); + + /* select DEVICE mode */ + writel(USBMODE_DEVICE, &udc->usbmode); + + writel(0xffffffff, &udc->epflush); + + /* Turn on the USB connection by enabling the pullup resistor */ + writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd); + } else { + stop_activity(); + writel(USBCMD_FS2, &udc->usbcmd); + udelay(800); + if (controller.driver) + controller.driver->disconnect(gadget); + } + + return 0; +} + +void udc_disconnect(void) +{ + struct mv_udc *udc = controller.udc; + /* disable pullup */ + stop_activity(); + writel(USBCMD_FS2, &udc->usbcmd); + udelay(800); + if (controller.driver) + controller.driver->disconnect(&controller.gadget); +} + +static int mvudc_probe(void) +{ + struct ept_queue_head *head; + 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++) { + /* + * For item0 and item1, they are served as ep0 + * out&in seperately + */ + head = epts + i; + if (i < 2) + head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE) + | CONFIG_ZLT | CONFIG_IOS; + else + head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) + | CONFIG_ZLT; + head->next = TERMINATE; + head->info = 0; + + items[i] = memalign(PAGE_SIZE, sizeof(struct ept_queue_item)); + } + + INIT_LIST_HEAD(&controller.gadget.ep_list); + ep[0].ep.maxpacket = 64; + ep[0].ep.name = "ep0"; + 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.gadget.ep_list); + ep[i].desc = NULL; + } + ep[i].ep.ops = &mv_ep_ops; + } + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct mv_udc *udc = controller.udc; + int retval; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->setup) { + DBG("bad parameter.\n"); + return -EINVAL; + } + + if (!mvudc_probe()) { + usb_lowlevel_init(); + /* 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; + } + controller.driver = driver; + + return 0; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + return 0; +} diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h new file mode 100644 index 0000000..51d36c3 --- /dev/null +++ b/include/usb/mv_udc.h @@ -0,0 +1,151 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + +#ifndef __MV_UDC_H__ +#define __MV_UDC_H__ + +#include +#include +#include +#include + +/* Endpoint 0 states */ +#define EP0_IDLE 0 +#define EP0_IN_DATA 1 +#define EP0_OUT_DATA 2 +#define EP0_XFER_COMPLETE 3 + + +/* Endpoint parameters */ +#define MAX_ENDPOINTS 4 +#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; + struct list_head queue; + const struct usb_endpoint_descriptor *desc; +}; + +struct mv_udc { + u32 pad0[80]; +#define MICRO_8FRAME 0x8 +#define USBCMD_ITC(x) (((x > 0xff) ? 0xff : x) << 16) +#define USBCMD_FS2 (1 << 15) +#define USBCMD_RST (1 << 1) +#define USBCMD_RUN (1) + u32 usbcmd; /* 0x140 */ +#define STS_SLI (1 << 8) +#define STS_URI (1 << 6) +#define STS_PCI (1 << 2) +#define STS_UEI (1 << 1) +#define STS_UI (1 << 0) + u32 usbsts; /* 0x144 */ + u32 pad1[3]; + u32 devaddr; /* 0x154 */ + u32 epinitaddr; /* 0x158 */ + u32 pad2[10]; +#define PTS_ENABLE 2 +#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)) + 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) + u32 epctrl[16]; /* 0x1c0 */ +}; + +struct mv_drv { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct mv_udc *udc; +}; + +struct ept_queue_head { + unsigned config; + unsigned current; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + 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 */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#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) + +extern int usb_lowlevel_init(void); +#endif /* __MV_UDC_H__ */ -- cgit v0.10.2 From 7555d5ec29a52290d706440ce64fa02a53a3c4cf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 25 Sep 2011 21:07:56 +0200 Subject: USB: Add usb_event_poll() to get keyboards working with EHCI Signed-off-by: Marek Vasut Cc: Remy Bohmer diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2197119..b4c9db8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -26,6 +26,10 @@ #include #include #include +#ifdef CONFIG_USB_KEYBOARD +#include +extern unsigned char new[]; +#endif #include "ehci.h" @@ -895,5 +899,32 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - return -1; + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = stdio_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); } +#endif /* CONFIG_SYS_USB_EVENT_POLL */ -- cgit v0.10.2 From 0255f2d87c3e5c4a2f593746daa512024f56ce70 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 17 Oct 2011 20:16:09 +0200 Subject: ehci-fsl: correct size of ehci caplength According to the EHCI specification the Capability Register Length has a size of 8 bits. Signed-off-by: Wolfgang Grandegger Acked-by: Kumar Gala diff --git a/include/usb/ehci-fsl.h b/include/usb/ehci-fsl.h index 67600ed..f50a8f2 100644 --- a/include/usb/ehci-fsl.h +++ b/include/usb/ehci-fsl.h @@ -186,35 +186,36 @@ struct usb_ehci { u32 gptimer1_ctrl; /* 0x08C - General Purpose Timer 1 control */ u32 sbuscfg; /* 0x090 - System Bus Interface Control */ u8 res2[0x6C]; - u16 caplength; /* 0x100 - Capability Register Length */ + u8 caplength; /* 0x100 - Capability Register Length */ + u8 res3[0x1]; u16 hciversion; /* 0x102 - Host Interface Version */ u32 hcsparams; /* 0x104 - Host Structural Parameters */ u32 hccparams; /* 0x108 - Host Capability Parameters */ - u8 res3[0x14]; + u8 res4[0x14]; u32 dciversion; /* 0x120 - Device Interface Version */ u32 dciparams; /* 0x124 - Device Controller Params */ - u8 res4[0x18]; + u8 res5[0x18]; u32 usbcmd; /* 0x140 - USB Command */ u32 usbsts; /* 0x144 - USB Status */ u32 usbintr; /* 0x148 - USB Interrupt Enable */ u32 frindex; /* 0x14C - USB Frame Index */ - u8 res5[0x4]; + u8 res6[0x4]; u32 perlistbase; /* 0x154 - Periodic List Base - USB Device Address */ u32 ep_list_addr; /* 0x158 - Next Asynchronous List - End Point Address */ - u8 res6[0x4]; + u8 res7[0x4]; u32 burstsize; /* 0x160 - Programmable Burst Size */ #define FSL_EHCI_TXPBURST(X) ((X) << 8) #define FSL_EHCI_RXPBURST(X) (X) u32 txfilltuning; /* 0x164 - Host TT Transmit pre-buffer packet tuning */ - u8 res7[0x8]; + u8 res8[0x8]; u32 ulpi_viewpoint; /* 0x170 - ULPI Reister Access */ - u8 res8[0xc]; + u8 res9[0xc]; u32 config_flag; /* 0x180 - Configured Flag Register */ u32 portsc; /* 0x184 - Port status/control */ - u8 res9[0x1C]; + u8 res10[0x1C]; u32 otgsc; /* 0x1a4 - Oo-The-Go status and control */ u32 usbmode; /* 0x1a8 - USB Device Mode */ u32 epsetupstat; /* 0x1ac - End Point Setup Status */ @@ -228,18 +229,18 @@ struct usb_ehci { u32 epctrl3; /* 0x1cc - End Point Control 3 */ u32 epctrl4; /* 0x1d0 - End Point Control 4 */ u32 epctrl5; /* 0x1d4 - End Point Control 5 */ - u8 res10[0x28]; + u8 res11[0x28]; u32 usbgenctrl; /* 0x200 - USB General Control */ u32 isiphyctrl; /* 0x204 - On-Chip PHY Control */ - u8 res11[0x1F8]; + u8 res12[0x1F8]; u32 snoop1; /* 0x400 - Snoop 1 */ u32 snoop2; /* 0x404 - Snoop 2 */ u32 age_cnt_limit; /* 0x408 - Age Count Threshold */ u32 prictrl; /* 0x40c - Priority Control */ u32 sictrl; /* 0x410 - System Interface Control */ - u8 res12[0xEC]; + u8 res13[0xEC]; u32 control; /* 0x500 - Control */ - u8 res13[0xafc]; + u8 res14[0xafc]; }; #endif /* _EHCI_FSL_H */ -- cgit v0.10.2 From 1c0a14ebb7beb5d1d8f9893aea95e42eded95c1c Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Mon, 17 Oct 2011 17:22:46 +0200 Subject: pxa: fix usb host register mismatch Signed-off-by: Stefan Herbrechtsmeier CC: Marek Vasut CC: Remy Bohmer diff --git a/arch/arm/cpu/pxa/usb.c b/arch/arm/cpu/pxa/usb.c index 83022e2..e95e0df 100644 --- a/arch/arm/cpu/pxa/usb.c +++ b/arch/arm/cpu/pxa/usb.c @@ -72,7 +72,7 @@ int usb_cpu_stop(void) udelay(11); writel(readl(UHCHR) & ~UHCHR_FHR, UHCHR); - writel(readl(UHCCOMS) | UHCHR_FHR, UHCCOMS); + writel(readl(UHCCOMS) | UHCCOMS_HCR, UHCCOMS); udelay(10); #if defined(CONFIG_CPU_MONAHANS) diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index 8527c68..0c3cd6d 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -835,6 +835,8 @@ typedef void (*ExcpHndlr) (void) ; #define UHCHIE 0x4C000068 #define UHCHIT 0x4C00006C +#define UHCCOMS_HCR (1<<0) + #define UHCHR_FSBIR (1<<0) #define UHCHR_FHR (1<<1) #define UHCHR_CGR (1<<2) -- cgit v0.10.2 From 66a181864d910afd7188ddbd747c70da96f7c505 Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Mon, 17 Oct 2011 17:22:47 +0200 Subject: pxa: activate the first usb host port on pxa27x by default The pxa27x has 3 usb host ports. Activate all by default. Signed-off-by: Stefan Herbrechtsmeier CC: Marek Vasut CC: Remy Bohmer diff --git a/arch/arm/cpu/pxa/usb.c b/arch/arm/cpu/pxa/usb.c index e95e0df..307fc6c 100644 --- a/arch/arm/cpu/pxa/usb.c +++ b/arch/arm/cpu/pxa/usb.c @@ -55,7 +55,7 @@ int usb_cpu_init(void) while (readl(UHCHR) & UHCHR_FSBIR) udelay(1); -#if defined(CONFIG_CPU_MONAHANS) +#if defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X) writel(readl(UHCHR) & ~UHCHR_SSEP0, UHCHR); #endif #if defined(CONFIG_CPU_PXA27X) @@ -75,7 +75,7 @@ int usb_cpu_stop(void) writel(readl(UHCCOMS) | UHCCOMS_HCR, UHCCOMS); udelay(10); -#if defined(CONFIG_CPU_MONAHANS) +#if defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X) writel(readl(UHCHR) | UHCHR_SSEP0, UHCHR); #endif #if defined(CONFIG_CPU_PXA27X) -- cgit v0.10.2 From bdbcdc897f9e854ca8a4701418dec048ef376431 Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Mon, 17 Oct 2011 17:22:48 +0200 Subject: pxa: convert pxa27x_udc to use read and write functions Signed-off-by: Stefan Herbrechtsmeier CC: Marek Vasut CC: Remy Bohmer diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index 0c3cd6d..b81b42c 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -645,7 +645,7 @@ typedef void (*ExcpHndlr) (void) ; #define UDCOTGICR_IEIDR (1 << 1) /* OTG ID Change Rising Edge Interrupt Enable */ #define UDCOTGICR_IEIDF (1 << 0) /* OTG ID Change Falling Edge Interrupt Enable */ -#define UDCCSN(x) __REG2(0x40600100, (x) << 2) +#define UDCCSN(x) (0x40600100 + ((x) << 2)) #define UDCCSR0 0x40600100 /* UDC Control/Status register - Endpoint 0 */ #define UDCCSR0_SA (1 << 7) /* Setup Active */ @@ -693,7 +693,7 @@ typedef void (*ExcpHndlr) (void) ; #define UDCCSR_PC (1 << 1) /* Packet Complete */ #define UDCCSR_FS (1 << 0) /* FIFO needs service */ -#define UDCBCN(x) __REG2(0x40600200, (x)<<2) +#define UDCBCN(x) (0x40600200 + ((x) << 2)) #define UDCBCR0 0x40600200 /* Byte Count Register - EP0 */ #define UDCBCRA 0x40600204 /* Byte Count Register - EPA */ #define UDCBCRB 0x40600208 /* Byte Count Register - EPB */ @@ -719,7 +719,7 @@ typedef void (*ExcpHndlr) (void) ; #define UDCBCRW 0x40600258 /* Byte Count Register - EPW */ #define UDCBCRX 0x4060025C /* Byte Count Register - EPX */ -#define UDCDN(x) __REG2(0x40600300, (x)<<2) +#define UDCDN(x) (0x40600300 + ((x) << 2)) #define UDCDR0 0x40600300 /* Data Register - EP0 */ #define UDCDRA 0x40600304 /* Data Register - EPA */ #define UDCDRB 0x40600308 /* Data Register - EPB */ @@ -745,7 +745,7 @@ typedef void (*ExcpHndlr) (void) ; #define UDCDRW 0x40600358 /* Data Register - EPW */ #define UDCDRX 0x4060035C /* Data Register - EPX */ -#define UDCCN(x) __REG2(0x40600400, (x)<<2) +#define UDCCN(x) (0x40600400 + ((x) << 2)) #define UDCCRA 0x40600404 /* Configuration register EPA */ #define UDCCRB 0x40600408 /* Configuration register EPB */ #define UDCCRC 0x4060040C /* Configuration register EPC */ diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index e6ccd49..0d3a988 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -26,11 +26,13 @@ #include #include #include -#include -#include +#include #include +#include #include +#include "ep0.h" + /* number of endpoints on this UDC */ #define UDC_MAX_ENDPOINTS 24 @@ -50,7 +52,7 @@ static void udc_dump_buffer(char *name, u8 *buf, int len) static inline void udc_ack_int_UDCCR(int mask) { - USIR1 = mask | USIR1; + writel(readl(USIR1) | mask, USIR1); } /* @@ -68,9 +70,7 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint) { struct urb *urb = endpoint->tx_urb; int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; - u32 *addr32 = (u32 *) &UDCDN(ep_num); u32 *data32 = (u32 *) urb->buffer; - u8 *addr8 = (u8 *) &UDCDN(ep_num); u8 *data8 = (u8 *) urb->buffer; unsigned int i, n, w, b, is_short; int timeout = 2000; /* 2ms */ @@ -98,26 +98,28 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint) /* Prepare for data send */ if (ep_num) - UDCCSN(ep_num) = UDCCSR_PC; + writel(UDCCSR_PC ,UDCCSN(ep_num)); for (i = 0; i < w; i++) - *addr32 = data32[endpoint->sent/4 + i]; + writel(data32[endpoint->sent / 4 + i], UDCDN(ep_num)); + for (i = 0; i < b; i++) - *addr8 = data8[endpoint->sent + w*4 + i]; + writeb(data8[endpoint->sent + w * 4 + i], UDCDN(ep_num)); /* Set "Packet Complete" if less data then tx_packetSize */ if (is_short) - UDCCSN(ep_num) = ep_num ? UDCCSR_SP : UDCCSR0_IPR; + writel(ep_num ? UDCCSR_SP : UDCCSR0_IPR, UDCCSN(ep_num)); /* Wait for data sent */ - while (!(UDCCSN(ep_num) & (ep_num ? UDCCSR_PC : UDCCSR0_IPR))) { - if (ep_num) { + if (ep_num) { + while (!(readl(UDCCSN(ep_num)) & UDCCSR_PC)) { if (timeout-- == 0) return -1; else udelay(1); - }; + } } + endpoint->last = n; if (ep_num) { @@ -127,7 +129,7 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint) endpoint->last -= n; } - if ((endpoint->tx_urb->actual_length - endpoint->sent) <= 0) { + if (endpoint->sent >= urb->actual_length) { urb->actual_length = 0; endpoint->sent = 0; endpoint->last = 0; @@ -148,7 +150,6 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint) { struct urb *urb = endpoint->rcv_urb; int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; - u32 *addr32 = (u32 *) &UDCDN(ep_num); u32 *data32 = (u32 *) urb->buffer; unsigned int i, n, is_short ; @@ -160,15 +161,15 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint) endpoint->rcv_packetSize); #endif - if (UDCCSN(ep_num) & UDCCSR_BNE) - n = UDCBCN(ep_num) & 0x3ff; + if (readl(UDCCSN(ep_num)) & UDCCSR_BNE) + n = readl(UDCBCN(ep_num)) & 0x3ff; else /* zlp */ n = 0; is_short = n != endpoint->rcv_packetSize; usbdbg("n %d%s", n, is_short ? "-s" : ""); for (i = 0; i < n; i += 4) - data32[urb->actual_length/4 + i/4] = *addr32; + data32[urb->actual_length / 4 + i / 4] = readl(UDCDN(ep_num)); udc_dump_buffer("urb read", (u8 *) data32, urb->actual_length + n); usbd_rcv_complete(endpoint, n, 0); @@ -178,27 +179,35 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint) static int udc_read_urb_ep0(void) { - u32 *addr32 = (u32 *) &UDCDN(0); u32 *data32 = (u32 *) ep0_urb->buffer; - u8 *addr8 = (u8 *) &UDCDN(0); u8 *data8 = (u8 *) ep0_urb->buffer; unsigned int i, n, w, b; - n = UDCBCR0; + usbdbg("read urb on ep 0"); +#if defined(USBDDBG) && defined(USBDPARANOIA) + usbdbg("urb: buf %p, buf_len %d, actual_len %d", + ep0_urb->buffer, ep0_urb->buffer_length, ep0_urb->actual_length); +#endif + + n = readl(UDCBCR0); w = n / 4; b = n % 4; for (i = 0; i < w; i++) { - data32[ep0_urb->actual_length/4 + i] = *addr32; - ep0_urb->actual_length += 4; + data32[ep0_urb->actual_length / 4 + i] = readl(UDCDN(0)); +// ep0_urb->actual_length += 4; } for (i = 0; i < b; i++) { - data8[ep0_urb->actual_length + w*4 + i] = *addr8; - ep0_urb->actual_length++; + data8[ep0_urb->actual_length + w * 4 + i] = readb(UDCDN(0)); +// ep0_urb->actual_length++; } - UDCCSR0 = UDCCSR0_OPC | UDCCSR0_IPR; + ep0_urb->actual_length += n; + + udc_dump_buffer("urb read", (u8 *) data32, ep0_urb->actual_length); + + writel(UDCCSR0_OPC | UDCCSR0_IPR, UDCCSR0); if (ep0_urb->actual_length == ep0_urb->device_request.wLength) return 1; @@ -207,7 +216,7 @@ static int udc_read_urb_ep0(void) static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) { - u32 udccsr0 = UDCCSR0; + u32 udccsr0 = readl(UDCCSR0); u32 *data = (u32 *) &ep0_urb->device_request; int i; @@ -216,7 +225,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) /* Clear stall status */ if (udccsr0 & UDCCSR0_SST) { usberr("clear stall status"); - UDCCSR0 = UDCCSR0_SST; + writel(UDCCSR0_SST, UDCCSR0); ep0state = EP0_IDLE; } @@ -227,8 +236,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) switch (ep0state) { case EP0_IDLE: - - udccsr0 = UDCCSR0; + udccsr0 = readl(UDCCSR0); /* Start control request? */ if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) == (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) { @@ -238,15 +246,15 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) */ usbdbg("try reading SETUP packet"); for (i = 0; i < 2; i++) { - if ((UDCCSR0 & UDCCSR0_RNE) == 0) { + if ((readl(UDCCSR0) & UDCCSR0_RNE) == 0) { usberr("setup packet too short:%d", i); goto stall; } - data[i] = UDCDR0; + data[i] = readl(UDCDR0); } - UDCCSR0 |= (UDCCSR0_OPC | UDCCSR0_SA); - if ((UDCCSR0 & UDCCSR0_RNE) != 0) { + writel(readl(UDCCSR0) | UDCCSR0_OPC | UDCCSR0_SA, UDCCSR0); + if ((readl(UDCCSR0) & UDCCSR0_RNE) != 0) { usberr("setup packet too long"); goto stall; } @@ -261,7 +269,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) (u8 *)data, 8); goto stall; } - UDCCSR0 = UDCCSR0_IPR; + writel(UDCCSR0_IPR, UDCCSR0); ep0state = EP0_IDLE; } else { /* Check direction */ @@ -274,7 +282,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) ep0_urb->buffer_length = sizeof(ep0_urb->buffer_data); ep0_urb->actual_length = 0; - UDCCSR0 = UDCCSR0_IPR; + writel(UDCCSR0_IPR, UDCCSR0); } else { /* The ep0_recv_setup function has * already placed our response packet @@ -289,9 +297,9 @@ stall: , (u8 *) data, 8); ep0state = EP0_IDLE; - UDCCSR0 = UDCCSR0_SA | + writel(UDCCSR0_SA | UDCCSR0_OPC | UDCCSR0_FST | - UDCCS0_FTF; + UDCCS0_FTF, UDCCSR0); return; } @@ -317,7 +325,7 @@ stall: * - IPR cleared * - OPC got set, without SA (likely status stage) */ - UDCCSR0 = udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC); + writel(udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC), UDCCSR0); } break; @@ -351,7 +359,7 @@ read_complete: case EP0_IN_DATA: /* GET_DESCRIPTOR etc */ if (udccsr0 & UDCCSR0_OPC) { - UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF; + writel(UDCCSR0_OPC | UDCCSR0_FTF, UDCCSR0); usberr("ep0in premature status"); ep0state = EP0_IDLE; } else { @@ -364,14 +372,14 @@ read_complete: break; case EP0_XFER_COMPLETE: - UDCCSR0 = UDCCSR0_IPR; + writel(UDCCSR0_IPR, UDCCSR0); ep0state = EP0_IDLE; break; default: usbdbg("Default\n"); } - USIR0 = USIR0_IR0; + writel(USIR0_IR0, USIR0); } static void udc_handle_ep(struct usb_endpoint_instance *endpoint) @@ -380,33 +388,33 @@ static void udc_handle_ep(struct usb_endpoint_instance *endpoint) int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; int ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT; - u32 flags = UDCCSN(ep_num) & (UDCCSR_SST | UDCCSR_TRN); + u32 flags = readl(UDCCSN(ep_num)) & (UDCCSR_SST | UDCCSR_TRN); if (flags) - UDCCSN(ep_num) = flags; + writel(flags, UDCCSN(ep_num)); if (ep_isout) udc_read_urb(endpoint); else udc_write_urb(endpoint); - UDCCSN(ep_num) = UDCCSR_PC; + writel(UDCCSR_PC, UDCCSN(ep_num)); } static void udc_state_changed(void) { int config, interface, alternate; - UDCCR |= UDCCR_SMAC; + writel(readl(UDCCR) | UDCCR_SMAC, UDCCR); - config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; - interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S; - alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S; + config = (readl(UDCCR) & UDCCR_ACN) >> UDCCR_ACN_S; + interface = (readl(UDCCR) & UDCCR_AIN) >> UDCCR_AIN_S; + alternate = (readl(UDCCR) & UDCCR_AAISN) >> UDCCR_AAISN_S; usbdbg("New UDC settings are: conf %d - inter %d - alter %d", config, interface, alternate); usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); - UDCISR1 = UDCISR1_IRCC; + writel(UDCISR1_IRCC, UDCISR1); } void udc_irq(void) @@ -419,7 +427,7 @@ void udc_irq(void) do { handled = 0; /* Suspend Interrupt Request */ - if (USIR1 & UDCCR_SUSIR) { + if (readl(USIR1) & UDCCR_SUSIR) { usbdbg("Suspend\n"); udc_ack_int_UDCCR(UDCCR_SUSIR); handled = 1; @@ -427,35 +435,35 @@ void udc_irq(void) } /* Resume Interrupt Request */ - if (USIR1 & UDCCR_RESIR) { + if (readl(USIR1) & UDCCR_RESIR) { udc_ack_int_UDCCR(UDCCR_RESIR); handled = 1; usbdbg("USB resume\n"); } - if (USIR1 & (1<<31)) { + if (readl(USIR1) & (1<<31)) { handled = 1; udc_state_changed(); } /* Reset Interrupt Request */ - if (USIR1 & UDCCR_RSTIR) { + if (readl(USIR1) & UDCCR_RSTIR) { udc_ack_int_UDCCR(UDCCR_RSTIR); handled = 1; usbdbg("Reset\n"); usbd_device_event_irq(udc_device, DEVICE_RESET, 0); } else { - if (USIR0) - usbdbg("UISR0: %x \n", USIR0); + if (readl(USIR0)) + usbdbg("UISR0: %x \n", readl(USIR0)); - if (USIR0 & 0x2) - USIR0 = 0x2; + if (readl(USIR0) & 0x2) + writel(0x2, USIR0); /* Control traffic */ - if (USIR0 & USIR0_IR0) { + if (readl(USIR0) & USIR0_IR0) { handled = 1; + writel(USIR0_IR0, USIR0); udc_handle_ep0(udc_device->bus->endpoint_array); - USIR0 = USIR0_IR0; } endpoint = udc_device->bus->endpoint_array; @@ -464,11 +472,11 @@ void udc_irq(void) USB_ENDPOINT_NUMBER_MASK; if (!ep_num) continue; - udcisr0 = UDCISR0; + udcisr0 = readl(UDCISR0); if (udcisr0 & UDCISR_INT(ep_num, UDC_INT_PACKETCMP)) { - UDCISR0 = UDCISR_INT(ep_num, - UDC_INT_PACKETCMP); + writel(UDCISR_INT(ep_num, UDC_INT_PACKETCMP), + UDCISR0); udc_handle_ep(&endpoint[i]); } } @@ -485,21 +493,21 @@ void udc_irq(void) static inline void udc_set_mask_UDCCR(int mask) { - UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); + writel((readl(UDCCR) & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR); } static inline void udc_clear_mask_UDCCR(int mask) { - UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); + writel((readl(UDCCR) & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR); } static void pio_irq_enable(int ep_num) { if (ep_num < 16) - UDCICR0 |= 3 << (ep_num * 2); + writel(readl(UDCICR0) | 3 << (ep_num * 2), UDCICR0); else { ep_num -= 16; - UDCICR1 |= 3 << (ep_num * 2); + writel(readl(UDCICR1) | 3 << (ep_num * 2), UDCICR1); } } @@ -589,22 +597,26 @@ void udc_setup_ep(struct usb_device_instance *device, unsigned int id, tmp |= (ep_size << UDCCONR_MPS_S) & UDCCONR_MPS; tmp |= UDCCONR_EE; - UDCCN(ep_num) = tmp; + writel(tmp, UDCCN(ep_num)); - usbdbg("UDCCR%c = %x", 'A' + ep_num-1, UDCCN(ep_num)); - usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, UDCCSN(ep_num)); + //usbdbg + usbdbg("UDCCR%c = %x", 'A' + ep_num-1, readl(UDCCN(ep_num))); + usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, readl(UDCCSN(ep_num))); } -#define CONFIG_USB_DEV_PULLUP_GPIO 87 - /* Connect the USB device to the bus */ void udc_connect(void) { usbdbg("UDC connect"); +#ifdef CONFIG_USB_DEV_PULLUP_GPIO /* Turn on the USB connection by enabling the pullup resistor */ set_GPIO_mode(CONFIG_USB_DEV_PULLUP_GPIO | GPIO_OUT); - GPSR(CONFIG_USB_DEV_PULLUP_GPIO) = GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO); + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else + /* Host port 2 transceiver D+ pull up enable */ + writel(readl(UP2OCR) | UP2OCR_DPPUE, UP2OCR); +#endif } /* Disconnect the USB device to the bus */ @@ -612,8 +624,13 @@ void udc_disconnect(void) { usbdbg("UDC disconnect"); +#ifdef CONFIG_USB_DEV_PULLUP_GPIO /* Turn off the USB connection by disabling the pullup resistor */ - GPCR(CONFIG_USB_DEV_PULLUP_GPIO) = GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO); + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPCR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else + /* Host port 2 transceiver D+ pull up disable */ + writel(readl(UP2OCR) & ~UP2OCR_DPPUE, UP2OCR); +#endif } /* Switch on the UDC */ @@ -621,15 +638,14 @@ void udc_enable(struct usb_device_instance *device) { ep0state = EP0_IDLE; - CKEN |= CKEN11_USB; /* enable endpoint 0, A, B's Packet Complete Interrupt. */ - UDCICR0 = 0x0000003f; - UDCICR1 = 0xa8000000; + writel(0xffffffff, UDCICR0); + writel(0xa8000000, UDCICR1); /* clear the interrupt status/control registers */ - UDCISR0 = 0xffffffff; - UDCISR1 = 0xffffffff; + writel(0xffffffff, UDCISR0); + writel(0xffffffff, UDCISR1); /* set UDC-enable */ udc_set_mask_UDCCR(UDCCR_UDE); @@ -652,7 +668,7 @@ void udc_disable(void) udc_clear_mask_UDCCR(UDCCR_UDE); /* Disable clock for USB device */ - CKEN &= ~CKEN11_USB; + writel(readl(CKEN) & ~CKEN11_USB, CKEN); /* Free ep0 URB */ if (ep0_urb) { @@ -689,14 +705,15 @@ int udc_init(void) udc_device = NULL; usbdbg("PXA27x usbd start"); + /* Enable clock for USB device */ + writel(readl(CKEN) | CKEN11_USB, CKEN); + /* Disable the UDC */ udc_clear_mask_UDCCR(UDCCR_UDE); - /* Disable clock for USB device */ - CKEN &= ~CKEN11_USB; - /* Disable IRQs: we don't use them */ - UDCICR0 = UDCICR1 = 0; + writel(0, UDCICR0); + writel(0, UDCICR1); return 0; } -- cgit v0.10.2 From 241d9a61b3c073aec7632af1333631ab61455cc4 Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Mon, 17 Oct 2011 17:22:49 +0200 Subject: usbtty: init endpoints prior to startup events On some usb device controllers (pxa) the endpoint configuration must be programmed prior to enable it. Signed-off-by: Stefan Herbrechtsmeier CC: Marek Vasut CC: Remy Bohmer diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index cffd5a2..e2e87fe 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -554,11 +554,11 @@ int drv_usbtty_init (void) usbtty_init_strings (); usbtty_init_instances (); + usbtty_init_endpoints (); + udc_startup_events (device_instance);/* Enable dev, init udc pointers */ udc_connect (); /* Enable pullup for host detection */ - usbtty_init_endpoints (); - /* Device initialization */ memset (&usbttydev, 0, sizeof (usbttydev)); -- cgit v0.10.2 From 9792987721c7980453fe6447c3fa6593b44f8458 Mon Sep 17 00:00:00 2001 From: Stefan Kristiansson Date: Thu, 20 Oct 2011 22:25:37 +0300 Subject: usb: align usb_endpoint_descriptor to 16-bit boundary The usb_endpoint_descriptor struct is 7 bytes large and is defined as an array (ep_desc[USB_MAXENDPOINTS]) in the usb_interface struct in include/usb.h This fact will result in that every odd index in that array will start at an uneven address, this in turn makes accesses to u16 wMaxPacketSize unaligned. Such accesses are illegal on the OpenRISC architecture (as well as other architectures) and will render a bus error. Setting the aligned(2) attribute on usb_endpoint_descriptor will force wMaxPacketSize to a 16-bit boundary. Signed-off-by: Stefan Kristiansson diff --git a/include/usbdescriptors.h b/include/usbdescriptors.h index 2dec3b9..392fcf5 100644 --- a/include/usbdescriptors.h +++ b/include/usbdescriptors.h @@ -199,7 +199,7 @@ struct usb_endpoint_descriptor { u8 bmAttributes; u16 wMaxPacketSize; u8 bInterval; -} __attribute__ ((packed)); +} __attribute__ ((packed)) __attribute__ ((aligned(2))); struct usb_interface_descriptor { u8 bLength; -- cgit v0.10.2 From b3813a221bc9770f67f54603c8e508b7fb93a7cd Mon Sep 17 00:00:00 2001 From: Veli-Pekka Peltola Date: Wed, 2 Nov 2011 16:59:55 +0200 Subject: cosmetic: remove excess whitespace from usb command help Signed-off-by: Veli-Pekka Peltola Cc: Remy Bohmer diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 8c87265..aecde13 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -701,10 +701,10 @@ U_BOOT_CMD( usb, 5, 1, do_usb, "USB sub-system", "reset - reset (rescan) USB controller\n" - "usb stop [f] - stop USB [f]=force stop\n" - "usb tree - show USB device tree\n" + "usb stop [f] - stop USB [f]=force stop\n" + "usb tree - show USB device tree\n" "usb info [dev] - show available USB devices\n" - "usb storage - show details of USB storage devices\n" + "usb storage - show details of USB storage devices\n" "usb dev [dev] - show or set current USB storage device\n" "usb part [dev] - print partition table of one or all USB storage" " devices\n" @@ -726,7 +726,7 @@ U_BOOT_CMD( usb, 5, 1, do_usb, "USB sub-system", "reset - reset (rescan) USB controller\n" - "usb tree - show USB device tree\n" - "usb info [dev] - show available USB devices" + "usb tree - show USB device tree\n" + "usb info [dev] - show available USB devices" ); #endif -- cgit v0.10.2 From 1af9f9633759a5f60e8b8bd4fcdb17a3d1aece5f Mon Sep 17 00:00:00 2001 From: Veli-Pekka Peltola Date: Wed, 2 Nov 2011 16:59:56 +0200 Subject: usb: add help for missing start subcommand Signed-off-by: Veli-Pekka Peltola Cc: Remy Bohmer diff --git a/common/cmd_usb.c b/common/cmd_usb.c index aecde13..d4ec2a2 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -700,7 +700,8 @@ int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD( usb, 5, 1, do_usb, "USB sub-system", - "reset - reset (rescan) USB controller\n" + "start - start (scan) USB controller\n" + "usb reset - reset (rescan) USB controller\n" "usb stop [f] - stop USB [f]=force stop\n" "usb tree - show USB device tree\n" "usb info [dev] - show available USB devices\n" @@ -725,7 +726,8 @@ U_BOOT_CMD( U_BOOT_CMD( usb, 5, 1, do_usb, "USB sub-system", - "reset - reset (rescan) USB controller\n" + "start - start (scan) USB controller\n" + "usb reset - reset (rescan) USB controller\n" "usb tree - show USB device tree\n" "usb info [dev] - show available USB devices" ); -- cgit v0.10.2 From 5f4b4f2fed3ab8590c8c06b78642f8c1467acacf Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Mon, 5 Dec 2011 14:52:22 -0800 Subject: ehci: speed up initialization According to EHCI specification v1.0, the controller should stabilize the power on a port at most 20 ms after the port power bit transition. So, we put this setting in the virtual descriptor corresponding field, (bPwrOn2PwrGood = 10 => 10 x 2ms = 20ms), this saves about 500ms at each controller initialization/enumeration. Signed-off-by: Vincent Palatin diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b4c9db8..1cee158 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -52,7 +52,7 @@ static struct descriptor { 0x29, /* bDescriptorType: hub descriptor */ 2, /* bNrPorts -- runtime modified */ 0, /* wHubCharacteristics */ - 0xff, /* bPwrOn2PwrGood */ + 10, /* bPwrOn2PwrGood */ 0, /* bHubCntrCurrent */ {}, /* Device removable */ {} /* at most 7 ports! XXX */ -- cgit v0.10.2 From 38517a7821d531aabe8e14a3806a7913b14a57b2 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 27 Oct 2011 10:36:46 +0200 Subject: usb:gadget:s5p USB Device Controller (UDC) implementation This commit provides UDC driver support for Samsung's SoC family of processors. Signed-off-by: Lukasz Majewski Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Cc: Minkyu Kang Cc: Remy Bohmer diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 820ca9b..64b091f 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,6 +26,10 @@ include $(TOPDIR)/config.mk LIB := $(obj)libusb_gadget.o # new USB gadget layer dependencies +ifdef CONFIG_USB_GADGET +COBJS-y += epautoconf.o config.o usbstring.o +COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o diff --git a/drivers/usb/gadget/regs-otg.h b/drivers/usb/gadget/regs-otg.h new file mode 100644 index 0000000..3737e45 --- /dev/null +++ b/drivers/usb/gadget/regs-otg.h @@ -0,0 +1,271 @@ +/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h + * + * Copyright (C) 2004 Herbert Poetzl + * + * Registers remapping: + * Lukasz Majewski + * + * This include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H +#define __ASM_ARCH_REGS_USB_OTG_HS_H + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_phy { + u32 phypwr; + u32 phyclk; + u32 rstcon; +}; + +/* Device Logical IN Endpoint-Specific Registers */ +struct s3c_dev_in_endp { + u32 diepctl; + u8 res1[4]; + u32 diepint; + u8 res2[4]; + u32 dieptsiz; + u32 diepdma; + u8 res3[4]; + u32 diepdmab; +}; + +/* Device Logical OUT Endpoint-Specific Registers */ +struct s3c_dev_out_endp { + u32 doepctl; + u8 res1[4]; + u32 doepint; + u8 res2[4]; + u32 doeptsiz; + u32 doepdma; + u8 res3[4]; + u32 doepdmab; +}; + +struct ep_fifo { + u32 fifo; + u8 res[4092]; +}; + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_reg { + /* Core Global Registers */ + u32 gotgctl; /* OTG Control & Status */ + u32 gotgint; /* OTG Interrupt */ + u32 gahbcfg; /* Core AHB Configuration */ + u32 gusbcfg; /* Core USB Configuration */ + u32 grstctl; /* Core Reset */ + u32 gintsts; /* Core Interrupt */ + u32 gintmsk; /* Core Interrupt Mask */ + u32 grxstsr; /* Receive Status Debug Read/Status Read */ + u32 grxstsp; /* Receive Status Debug Pop/Status Pop */ + u32 grxfsiz; /* Receive FIFO Size */ + u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */ + u8 res1[216]; + u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */ + u8 res2[1728]; + /* Device Configuration */ + u32 dcfg; /* Device Configuration Register */ + u32 dctl; /* Device Control */ + u32 dsts; /* Device Status */ + u8 res3[4]; + u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */ + u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */ + u32 daint; /* Device All Endpoints Interrupt */ + u32 daintmsk; /* Device All Endpoints Interrupt Mask */ + u8 res4[224]; + struct s3c_dev_in_endp in_endp[16]; + struct s3c_dev_out_endp out_endp[16]; + u8 res5[768]; + struct ep_fifo ep[16]; +}; + +/*===================================================================== */ +/*definitions related to CSR setting */ + +/* S3C_UDC_OTG_GOTGCTL */ +#define B_SESSION_VALID (0x1<<19) +#define A_SESSION_VALID (0x1<<18) + +/* S3C_UDC_OTG_GAHBCFG */ +#define PTXFE_HALF (0<<8) +#define PTXFE_ZERO (1<<8) +#define NPTXFE_HALF (0<<7) +#define NPTXFE_ZERO (1<<7) +#define MODE_SLAVE (0<<5) +#define MODE_DMA (1<<5) +#define BURST_SINGLE (0<<1) +#define BURST_INCR (1<<1) +#define BURST_INCR4 (3<<1) +#define BURST_INCR8 (5<<1) +#define BURST_INCR16 (7<<1) +#define GBL_INT_UNMASK (1<<0) +#define GBL_INT_MASK (0<<0) + +/* S3C_UDC_OTG_GRSTCTL */ +#define AHB_MASTER_IDLE (1u<<31) +#define CORE_SOFT_RESET (0x1<<0) + +/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */ +#define INT_RESUME (1u<<31) +#define INT_DISCONN (0x1<<29) +#define INT_CONN_ID_STS_CNG (0x1<<28) +#define INT_OUT_EP (0x1<<19) +#define INT_IN_EP (0x1<<18) +#define INT_ENUMDONE (0x1<<13) +#define INT_RESET (0x1<<12) +#define INT_SUSPEND (0x1<<11) +#define INT_EARLY_SUSPEND (0x1<<10) +#define INT_NP_TX_FIFO_EMPTY (0x1<<5) +#define INT_RX_FIFO_NOT_EMPTY (0x1<<4) +#define INT_SOF (0x1<<3) +#define INT_DEV_MODE (0x0<<0) +#define INT_HOST_MODE (0x1<<1) +#define INT_GOUTNakEff (0x01<<7) +#define INT_GINNakEff (0x01<<6) + +#define FULL_SPEED_CONTROL_PKT_SIZE 8 +#define FULL_SPEED_BULK_PKT_SIZE 64 + +#define HIGH_SPEED_CONTROL_PKT_SIZE 64 +#define HIGH_SPEED_BULK_PKT_SIZE 512 + +#define RX_FIFO_SIZE (1024*4) +#define NPTX_FIFO_SIZE (1024*4) +#define PTX_FIFO_SIZE (1536*1) + +#define DEPCTL_TXFNUM_0 (0x0<<22) +#define DEPCTL_TXFNUM_1 (0x1<<22) +#define DEPCTL_TXFNUM_2 (0x2<<22) +#define DEPCTL_TXFNUM_3 (0x3<<22) +#define DEPCTL_TXFNUM_4 (0x4<<22) + +/* Enumeration speed */ +#define USB_HIGH_30_60MHZ (0x0<<1) +#define USB_FULL_30_60MHZ (0x1<<1) +#define USB_LOW_6MHZ (0x2<<1) +#define USB_FULL_48MHZ (0x3<<1) + +/* S3C_UDC_OTG_GRXSTSP STATUS */ +#define OUT_PKT_RECEIVED (0x2<<17) +#define OUT_TRANSFER_COMPLELTED (0x3<<17) +#define SETUP_TRANSACTION_COMPLETED (0x4<<17) +#define SETUP_PKT_RECEIVED (0x6<<17) +#define GLOBAL_OUT_NAK (0x1<<17) + +/* S3C_UDC_OTG_DCTL device control register */ +#define NORMAL_OPERATION (0x1<<0) +#define SOFT_DISCONNECT (0x1<<1) + +/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */ +#define DAINT_OUT_BIT (16) +#define DAINT_MASK (0xFFFF) + +/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device + control IN/OUT endpoint 0 control register */ +#define DEPCTL_EPENA (0x1<<31) +#define DEPCTL_EPDIS (0x1<<30) +#define DEPCTL_SETD1PID (0x1<<29) +#define DEPCTL_SETD0PID (0x1<<28) +#define DEPCTL_SNAK (0x1<<27) +#define DEPCTL_CNAK (0x1<<26) +#define DEPCTL_STALL (0x1<<21) +#define DEPCTL_TYPE_BIT (18) +#define DEPCTL_TYPE_MASK (0x3<<18) +#define DEPCTL_CTRL_TYPE (0x0<<18) +#define DEPCTL_ISO_TYPE (0x1<<18) +#define DEPCTL_BULK_TYPE (0x2<<18) +#define DEPCTL_INTR_TYPE (0x3<<18) +#define DEPCTL_USBACTEP (0x1<<15) +#define DEPCTL_NEXT_EP_BIT (11) +#define DEPCTL_MPS_BIT (0) +#define DEPCTL_MPS_MASK (0x7FF) + +#define DEPCTL0_MPS_64 (0x0<<0) +#define DEPCTL0_MPS_32 (0x1<<0) +#define DEPCTL0_MPS_16 (0x2<<0) +#define DEPCTL0_MPS_8 (0x3<<0) +#define DEPCTL_MPS_BULK_512 (512<<0) +#define DEPCTL_MPS_INT_MPS_16 (16<<0) + +#define DIEPCTL0_NEXT_EP_BIT (11) + + +/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint + common interrupt mask register */ +/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */ +#define BACK2BACK_SETUP_RECEIVED (0x1<<6) +#define INTKNEPMIS (0x1<<5) +#define INTKN_TXFEMP (0x1<<4) +#define NON_ISO_IN_EP_TIMEOUT (0x1<<3) +#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3) +#define AHB_ERROR (0x1<<2) +#define EPDISBLD (0x1<<1) +#define TRANSFER_DONE (0x1<<0) + +#define USB_PHY_CTRL_EN0 (0x1 << 0) + +/* OPHYPWR */ +#define PHY_0_SLEEP (0x1 << 5) +#define OTG_DISABLE_0 (0x1 << 4) +#define ANALOG_PWRDOWN (0x1 << 3) +#define FORCE_SUSPEND_0 (0x1 << 0) + +/* URSTCON */ +#define HOST_SW_RST (0x1 << 4) +#define PHY_SW_RST1 (0x1 << 3) +#define PHYLNK_SW_RST (0x1 << 2) +#define LINK_SW_RST (0x1 << 1) +#define PHY_SW_RST0 (0x1 << 0) + +/* OPHYCLK */ +#define COMMON_ON_N1 (0x1 << 7) +#define COMMON_ON_N0 (0x1 << 4) +#define ID_PULLUP0 (0x1 << 2) +#define CLK_SEL_24MHZ (0x3 << 0) +#define CLK_SEL_12MHZ (0x2 << 0) +#define CLK_SEL_48MHZ (0x0 << 0) + +/* Device Configuration Register DCFG */ +#define DEV_SPEED_HIGH_SPEED_20 (0x0 << 0) +#define DEV_SPEED_FULL_SPEED_20 (0x1 << 0) +#define DEV_SPEED_LOW_SPEED_11 (0x2 << 0) +#define DEV_SPEED_FULL_SPEED_11 (0x3 << 0) +#define EP_MISS_CNT(x) (x << 18) +#define DEVICE_ADDRESS(x) (x << 4) + +/* Core Reset Register (GRSTCTL) */ +#define TX_FIFO_FLUSH (0x1 << 5) +#define RX_FIFO_FLUSH (0x1 << 4) +#define TX_FIFO_NUMBER(x) (x << 6) +#define TX_FIFO_FLUSH_ALL TX_FIFO_NUMBER(0x10) + +/* Masks definitions */ +#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\ + | INT_RESET | INT_SUSPEND) +#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE) +#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE) +#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\ + | GBL_INT_UNMASK) + +/* Device Endpoint X Transfer Size Register (DIEPTSIZX) */ +#define DIEPT_SIZ_PKT_CNT(x) (x << 19) +#define DIEPT_SIZ_XFER_SIZE(x) (x << 0) + +/* Device OUT Endpoint X Transfer Size Register (DOEPTSIZX) */ +#define DOEPT_SIZ_PKT_CNT(x) (x << 19) +#define DOEPT_SIZ_XFER_SIZE(x) (x << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP0 (0x7F << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP (0x7FFF << 0) + +/* Device Endpoint-N Control Register (DIEPCTLn/DOEPCTLn) */ +#define DIEPCTL_TX_FIFO_NUM(x) (x << 22) +#define DIEPCTL_TX_FIFO_NUM_MASK (~DIEPCTL_TX_FIFO_NUM(0xF)) + +/* Device ALL Endpoints Interrupt Register (DAINT) */ +#define DAINT_IN_EP_INT(x) (x << 0) +#define DAINT_OUT_EP_INT(x) (x << 16) +#endif diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c new file mode 100644 index 0000000..5a3ac78 --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -0,0 +1,892 @@ +/* + * drivers/usb/gadget/s3c_udc_otg.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2008 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski + * + * Ported to u-boot: + * Marek Szyprowski + * Lukasz Majewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "regs-otg.h" +#include +#include + +/***********************************************************/ + +#define OTG_DMA_MODE 1 + +#undef DEBUG_S3C_UDC_SETUP +#undef DEBUG_S3C_UDC_EP0 +#undef DEBUG_S3C_UDC_ISR +#undef DEBUG_S3C_UDC_OUT_EP +#undef DEBUG_S3C_UDC_IN_EP +#undef DEBUG_S3C_UDC + +/* #define DEBUG_S3C_UDC_SETUP */ +/* #define DEBUG_S3C_UDC_EP0 */ +/* #define DEBUG_S3C_UDC_ISR */ +/* #define DEBUG_S3C_UDC_OUT_EP */ +/* #define DEBUG_S3C_UDC_IN_EP */ +/* #define DEBUG_S3C_UDC */ + +#include + +#define EP0_CON 0 +#define EP_MASK 0xF + +#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR) \ + || defined(DEBUG_S3C_UDC_OUT_EP) +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV", + "WAIT_FOR_COMPLETE", + "WAIT_FOR_OUT_COMPLETE", + "WAIT_FOR_IN_COMPLETE", + "WAIT_FOR_NULL_COMPLETE", +}; +#endif + +#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics" +#define DRIVER_VERSION "15 March 2009" + +struct s3c_udc *the_controller; + +static const char driver_name[] = "s3c-udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* Max packet size*/ +static unsigned int ep0_fifo_size = 64; +static unsigned int ep_fifo_size = 512; +static unsigned int ep_fifo_size2 = 1024; +static int reset_available = 1; + +static struct usb_ctrlrequest *usb_ctrl; +static dma_addr_t usb_ctrl_dma_addr; + +/* + Local declarations. +*/ +static int s3c_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *); +static int s3c_ep_disable(struct usb_ep *ep); +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +static void s3c_free_request(struct usb_ep *ep, struct usb_request *); + +static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags); +static int s3c_dequeue(struct usb_ep *ep, struct usb_request *); +static int s3c_fifo_status(struct usb_ep *ep); +static void s3c_fifo_flush(struct usb_ep *ep); +static void s3c_ep0_read(struct s3c_udc *dev); +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep); +static void s3c_handle_ep0(struct s3c_udc *dev); +static int s3c_ep0_write(struct s3c_udc *dev); +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req); +static void done(struct s3c_ep *ep, struct s3c_request *req, int status); +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver); +static int udc_enable(struct s3c_udc *dev); +static void udc_set_address(struct s3c_udc *dev, unsigned char address); +static void reconfig_usbd(void); +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); +static void nuke(struct s3c_ep *ep, int status); +static int s3c_udc_set_halt(struct usb_ep *_ep, int value); +static void s3c_udc_set_nak(struct s3c_ep *ep); + +static struct usb_ep_ops s3c_ep_ops = { + .enable = s3c_ep_enable, + .disable = s3c_ep_disable, + + .alloc_request = s3c_alloc_request, + .free_request = s3c_free_request, + + .queue = s3c_queue, + .dequeue = s3c_dequeue, + + .set_halt = s3c_udc_set_halt, + .fifo_status = s3c_fifo_status, + .fifo_flush = s3c_fifo_flush, +}; + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +/***********************************************************/ + +void __iomem *regs_otg; +struct s3c_usbotg_reg *reg; +struct s3c_usbotg_phy *phy; +static unsigned int usb_phy_ctrl; + +void otg_phy_init(struct s3c_udc *dev) +{ + dev->pdata->phy_control(1); + + /*USB PHY0 Enable */ + printf("USB PHY0 Enable\n"); + + /* Enable PHY */ + writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl); + + if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */ + writel((readl(&phy->phypwr) + &~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + else /* C110 GONI */ + writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + + writel((readl(&phy->phyclk) &~(ID_PULLUP0 | COMMON_ON_N0)) | + CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */ + + writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST)) + | PHY_SW_RST0, &phy->rstcon); + udelay(10); + writel(readl(&phy->rstcon) + &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon); + udelay(10); +} + +void otg_phy_off(struct s3c_udc *dev) +{ + /* reset controller just in case */ + writel(PHY_SW_RST0, &phy->rstcon); + udelay(20); + writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon); + udelay(20); + + writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN + | FORCE_SUSPEND_0, &phy->phypwr); + + writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl); + + writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)), + &phy->phyclk); + + udelay(10000); + + dev->pdata->phy_control(0); +} + +/***********************************************************/ + +#include "s3c_udc_otg_xfer_dma.c" + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct s3c_udc *dev) +{ + DEBUG_SETUP("%s: %p\n", __func__, dev); + + udc_set_address(dev, 0); + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->usb_address = 0; + + otg_phy_off(dev); +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct s3c_udc *dev) +{ + unsigned int i; + + DEBUG_SETUP("%s: %p\n", __func__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + /* basic endpoint records init */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x) (x / 8) +#define MAXP2BYTES(x) (x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static int udc_enable(struct s3c_udc *dev) +{ + DEBUG_SETUP("%s: %p\n", __func__, dev); + + otg_phy_init(dev); + reconfig_usbd(); + + DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", + readl(®->gintmsk)); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + return 0; +} + +/* + Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + int retval = 0; + unsigned long flags; + + DEBUG_SETUP("%s: %s\n", __func__, "no name"); + + if (!driver + || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + /* first hook up the driver ... */ + dev->driver = driver; + spin_unlock_irqrestore(&dev->lock, flags); + + if (retval) { /* TODO */ + printf("target device_add failed, error %d\n", retval); + return retval; + } + + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG_SETUP("%s: bind to driver --> error %d\n", + dev->gadget.name, retval); + dev->driver = 0; + return retval; + } + + enable_irq(IRQ_OTG); + + DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name); + udc_enable(dev); + + return 0; +} + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + disable_irq(IRQ_OTG); + + udc_disable(dev); + return 0; +} + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct s3c_ep *ep, struct s3c_request *req, int status) +{ + unsigned int stopped = ep->stopped; + + DEBUG("%s: %s %p, req = %p, stopped = %d\n", + __func__, ep->ep.name, ep, &req->req, stopped); + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) { + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + +#ifdef DEBUG_S3C_UDC + printf("calling complete callback\n"); + { + int i, len = req->req.length; + + printf("pkt[%d] = ", req->req.length); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)req->req.buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + DEBUG("callback completed\n"); + + ep->stopped = stopped; +} + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct s3c_ep *ep, int status) +{ + struct s3c_request *req; + + DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + done(ep, req, status); + } +} + +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +static void reconfig_usbd(void) +{ + /* 2. Soft-reset OTG Core and then unreset again. */ + int i; + unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); + + DEBUG(2, "Reseting OTG controller\n"); + + writel(0<<15 /* PHY Low Power Clock sel*/ + |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/ + |0x5<<10 /* Turnaround time*/ + |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ + /* 1:SRP enable] H1= 1,1*/ + |0<<7 /* Ulpi DDR sel*/ + |0<<6 /* 0: high speed utmi+, 1: full speed serial*/ + |0<<4 /* 0: utmi+, 1:ulpi*/ + |1<<3 /* phy i/f 0:8bit, 1:16bit*/ + |0x7<<0, /* HS/FS Timeout**/ + ®->gusbcfg); + + /* 3. Put the OTG device core in the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp |= SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + udelay(20); + + /* 4. Make the OTG device core exit from the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp = uTemp & ~SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + /* 5. Configure OTG Core to initial settings of device mode.*/ + /* [][1: full speed(30Mhz) 0:high speed]*/ + writel(EP_MISS_CNT(1) | DEV_SPEED_HIGH_SPEED_20, ®->dcfg); + + mdelay(1); + + /* 6. Unmask the core interrupts*/ + writel(GINTMSK_INIT, ®->gintmsk); + + /* 7. Set NAK bit of EP0, EP1, EP2*/ + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[EP0_CON].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[EP0_CON].diepctl); + + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); + } + + /* 8. Unmask EPO interrupts*/ + writel(((1 << EP0_CON) << DAINT_OUT_BIT) + | (1 << EP0_CON), ®->daintmsk); + + /* 9. Unmask device OUT EP common interrupts*/ + writel(DOEPMSK_INIT, ®->doepmsk); + + /* 10. Unmask device IN EP common interrupts*/ + writel(DIEPMSK_INIT, ®->diepmsk); + + /* 11. Set Rx FIFO Size (in 32-bit words) */ + writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); + + /* 12. Set Non Periodic Tx FIFO Size */ + writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, + ®->gnptxfsiz); + + for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++) + writel((PTX_FIFO_SIZE >> 2) << 16 | + ((RX_FIFO_SIZE + NPTX_FIFO_SIZE + + PTX_FIFO_SIZE*(i-1)) >> 2) << 0, + ®->dieptxf[i-1]); + + /* Flush the RX FIFO */ + writel(RX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & RX_FIFO_FLUSH) + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* Flush all the Tx FIFO's */ + writel(TX_FIFO_FLUSH_ALL, ®->grstctl); + writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & TX_FIFO_FLUSH) + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* 13. Clear NAK bit of EP0, EP1, EP2*/ + /* For Slave mode*/ + /* EP0: Control OUT */ + writel(DEPCTL_EPDIS | DEPCTL_CNAK, + ®->out_endp[EP0_CON].doepctl); + + /* 14. Initialize OTG Link Core.*/ + writel(GAHBCFG_INIT, ®->gahbcfg); +} + +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed) +{ + unsigned int ep_ctrl; + int i; + + if (speed == USB_SPEED_HIGH) { + ep0_fifo_size = 64; + ep_fifo_size = 512; + ep_fifo_size2 = 1024; + dev->gadget.speed = USB_SPEED_HIGH; + } else { + ep0_fifo_size = 64; + ep_fifo_size = 64; + ep_fifo_size2 = 64; + dev->gadget.speed = USB_SPEED_FULL; + } + + dev->ep[0].ep.maxpacket = ep0_fifo_size; + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) + dev->ep[i].ep.maxpacket = ep_fifo_size; + + /* EP0 - Control IN (64 bytes)*/ + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); + + /* EP0 - Control OUT (64 bytes)*/ + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|(0<<0), ®->out_endp[EP0_CON].doepctl); +} + +static int s3c_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { + + DEBUG("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + + DEBUG("%s: %s type mismatch\n", __func__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) + || !desc->wMaxPacketSize) { + + DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + + DEBUG("%s: bogus device state\n", __func__); + return -ESHUTDOWN; + } + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + /* Reset halt state */ + s3c_udc_set_nak(ep); + s3c_udc_set_halt(_ep, 0); + + spin_lock_irqsave(&ep->dev->lock, flags); + s3c_udc_ep_activate(ep); + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n", + __func__, _ep->name, ep->stopped, ep->ep.maxpacket); + return 0; +} + +/* + * Disable EP + */ +static int s3c_ep_disable(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s: %s not enabled\n", __func__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* Nuke all pending requests */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __func__, _ep->name); + return 0; +} + +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct s3c_request *req; + + DEBUG("%s: %s %p\n", __func__, ep->name, ep); + + req = kmalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_request *req; + + DEBUG("%s: %p\n", __func__, ep); + + req = container_of(_req, struct s3c_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* dequeue JUST ONE request */ +static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_ep *ep; + struct s3c_request *req; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/* + * Return bytes in EP FIFO + */ +static int s3c_fifo_status(struct usb_ep *_ep) +{ + int count = 0; + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep) { + DEBUG("%s: bad ep\n", __func__); + return -ENODEV; + } + + DEBUG("%s: %d\n", __func__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + return count; +} + +/* + * Flush EP FIFO + */ +static void s3c_fifo_flush(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s: bad ep\n", __func__); + return; + } + + DEBUG("%s: %d\n", __func__, ep_index(ep)); +} + +static const struct usb_gadget_ops s3c_udc_ops = { + /* current versions must always be self-powered */ +}; + +static struct s3c_udc memory = { + .usb_address = 0, + .gadget = { + .ops = &s3c_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &s3c_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo_num = 1, + }, + + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo_num = 2, + }, + + .ep[3] = { + .ep = { + .name = "ep3in-int", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo_num = 3, + }, +}; + +/* + * probe - binds to the platform device + */ + +int s3c_udc_probe(struct s3c_plat_otg_data *pdata) +{ + struct s3c_udc *dev = &memory; + int retval = 0, i; + + DEBUG("%s: %p\n", __func__, pdata); + + dev->pdata = pdata; + + phy = (struct s3c_usbotg_phy *)pdata->regs_phy; + reg = (struct s3c_usbotg_reg *)pdata->regs_otg; + usb_phy_ctrl = pdata->usb_phy_ctrl; + + /* regs_otg = (void *)pdata->regs_otg; */ + + dev->gadget.is_dualspeed = 1; /* Hack only*/ + dev->gadget.is_otg = 0; + dev->gadget.is_a_peripheral = 0; + dev->gadget.b_hnp_enable = 0; + dev->gadget.a_hnp_support = 0; + dev->gadget.a_alt_hnp_support = 0; + + the_controller = dev; + + for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) { + dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); + dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i]; + invalidate_dcache_range((unsigned long) dev->dma_buf[i], + (unsigned long) (dev->dma_buf[i] + + DMA_BUFFER_SIZE)); + } + usb_ctrl = dev->dma_buf[0]; + usb_ctrl_dma_addr = dev->dma_addr[0]; + + udc_reinit(dev); + + return retval; +} + +int usb_gadget_handle_interrupts() +{ + u32 intr_status = readl(®->gintsts); + u32 gintmsk = readl(®->gintmsk); + + if (intr_status & gintmsk) + return s3c_udc_irq(1, (void *)the_controller); + return 0; +} diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c new file mode 100644 index 0000000..2553857 --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -0,0 +1,1444 @@ +/* + * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2009 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski + * + * Ported to u-boot: + * Marek Szyprowski + * Lukasz Majewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static u8 clear_feature_num; +int clear_feature_flag; + +/* Bulk-Only Mass Storage Reset (class-specific request) */ +#define GET_MAX_LUN_REQUEST 0xFE +#define BOT_RESET_REQUEST 0xFF + +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) +{ + u32 ep_ctrl; + + flush_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); + writel(DIEPT_SIZ_PKT_CNT(1), ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + dev->ep0state = WAIT_FOR_IN_COMPLETE; +} + +void s3c_udc_pre_setup(void) +{ + u32 ep_ctrl; + + DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__); + + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + +static inline void s3c_ep0_complete_out(void) +{ + u32 ep_ctrl; + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + + DEBUG_IN_EP("%s : Prepare Complete Out packet.\n", __func__); + + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->out_endp[EP0_CON].doepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + + +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + + buf = req->req.buf + req->req.actual; + + length = min(req->req.length - req->req.actual, (int)ep->ep.maxpacket); + + ep->len = length; + ep->dma_buf = buf; + + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], + (unsigned long) ep->dev->dma_buf[ep_num] + + DMA_BUFFER_SIZE); + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + pktcnt = 1; + ctrl = readl(®->out_endp[ep_num].doepctl); + + writel(the_controller->dma_addr[ep_index(ep)+1], + ®->out_endp[ep_num].doepdma); + writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length), + ®->out_endp[ep_num].doeptsiz); + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); + + DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x," + "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->out_endp[ep_num].doepdma), + readl(®->out_endp[ep_num].doeptsiz), + readl(®->out_endp[ep_num].doepctl), + buf, pktcnt, length); + return 0; + +} + +int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl = 0; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + + buf = req->req.buf + req->req.actual; + length = req->req.length - req->req.actual; + + if (ep_num == EP0_CON) + length = min_t(length, (u32)ep_maxpacket(ep)); + + ep->len = length; + ep->dma_buf = buf; + memcpy(p, ep->dma_buf, length); + + flush_dcache_range((unsigned long) p , + (unsigned long) p + DMA_BUFFER_SIZE); + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + /* Flush the endpoint's Tx FIFO */ + writel(TX_FIFO_NUMBER(ep->fifo_num), ®->grstctl); + writel(TX_FIFO_NUMBER(ep->fifo_num) | TX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & TX_FIFO_FLUSH) + ; + + writel(the_controller->dma_addr[ep_index(ep)+1], + ®->in_endp[ep_num].diepdma); + writel(DIEPT_SIZ_PKT_CNT(pktcnt) | DIEPT_SIZ_XFER_SIZE(length), + ®->in_endp[ep_num].dieptsiz); + + ctrl = readl(®->in_endp[ep_num].diepctl); + + /* Write the FIFO number to be used for this endpoint */ + ctrl &= DIEPCTL_TX_FIFO_NUM_MASK; + ctrl |= DIEPCTL_TX_FIFO_NUM(ep->fifo_num); + + /* Clear reserved (Next EP) bits */ + ctrl = (ctrl&~(EP_MASK<in_endp[ep_num].diepctl); + + DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x," + "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->in_endp[ep_num].diepdma), + readl(®->in_endp[ep_num].dieptsiz), + readl(®->in_endp[ep_num].diepctl), + buf, pktcnt, length); + + return length; +} + +static void complete_rx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req = NULL; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + + if (list_empty(&ep->queue)) { + DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + ep_tsr = readl(®->out_endp[ep_num].doeptsiz); + + if (ep_num == EP0_CON) + xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP0); + else + xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP); + + xfer_size = ep->len - xfer_size; + + invalidate_dcache_range((unsigned long) p, + (unsigned long) p + DMA_BUFFER_SIZE); + + memcpy(ep->dma_buf, p, ep->len); + + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + is_short = (xfer_size < ep->ep.maxpacket); + + DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, " + "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (is_short || req->req.actual == req->req.length) { + if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { + DEBUG_OUT_EP(" => Send ZLP\n"); + s3c_udc_ep0_zlp(dev); + /* packet will be completed in complete_tx() */ + dev->ep0state = WAIT_FOR_IN_COMPLETE; + } else { + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct s3c_request, queue); + DEBUG_OUT_EP("%s: Next Rx request start...\n", + __func__); + setdma_rx(ep, req); + } + } + } else + setdma_rx(ep, req); +} + +static void complete_tx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + u32 last; + + if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) { + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + return; + } + + if (list_empty(&ep->queue)) { + DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + + ep_tsr = readl(®->in_endp[ep_num].dieptsiz); + + xfer_size = ep->len; + is_short = (xfer_size < ep->ep.maxpacket); + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + + DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, " + "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (ep_num == 0) { + if (dev->ep0state == DATA_STATE_XMIT) { + DEBUG_IN_EP("%s: ep_num = %d, ep0stat ==" + "DATA_STATE_XMIT\n", + __func__, ep_num); + last = write_fifo_ep0(ep, req); + if (last) + dev->ep0state = WAIT_FOR_COMPLETE; + } else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) { + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_SETUP; + } else if (dev->ep0state == WAIT_FOR_COMPLETE) { + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + } else { + DEBUG_IN_EP("%s: ep_num = %d, invalid ep state\n", + __func__, ep_num); + } + return; + } + + if (req->req.actual == req->req.length) + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + DEBUG_IN_EP("%s: Next Tx request start...\n", __func__); + setdma_tx(ep, req); + } +} + +static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + + DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n", + __func__, req); + + if (ep_is_in(ep)) + setdma_tx(ep, req); + else + setdma_rx(ep, req); + } else { + DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num); + + return; + } + +} + +static void process_ep_in_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n", + __func__, ep_intr); + + ep_intr &= DAINT_MASK; + + while (ep_intr) { + if (ep_intr & DAINT_IN_EP_INT(1)) { + ep_intr_status = readl(®->in_endp[ep_num].diepint); + DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->in_endp[ep_num].diepint); + + if (ep_intr_status & TRANSFER_DONE) { + complete_tx(dev, ep_num); + + if (ep_num == 0) { + if (dev->ep0state == + WAIT_FOR_IN_COMPLETE) + dev->ep0state = WAIT_FOR_SETUP; + + if (dev->ep0state == WAIT_FOR_SETUP) + s3c_udc_pre_setup(); + + /* continue transfer after + set_clear_halt for DMA mode */ + if (clear_feature_flag == 1) { + s3c_udc_check_tx_queue(dev, + clear_feature_num); + clear_feature_flag = 0; + } + } + } + } + ep_num++; + ep_intr >>= 1; + } +} + +static void process_ep_out_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n", + __func__, ep_intr); + + ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; + + while (ep_intr) { + if (ep_intr & 0x1) { + ep_intr_status = readl(®->out_endp[ep_num].doepint); + DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->out_endp[ep_num].doepint); + + if (ep_num == 0) { + if (ep_intr_status & TRANSFER_DONE) { + if (dev->ep0state != + WAIT_FOR_OUT_COMPLETE) + complete_rx(dev, ep_num); + else { + dev->ep0state = WAIT_FOR_SETUP; + s3c_udc_pre_setup(); + } + } + + if (ep_intr_status & + CTRL_OUT_EP_SETUP_PHASE_DONE) { + DEBUG_OUT_EP("SETUP packet arrived\n"); + s3c_handle_ep0(dev); + } + } else { + if (ep_intr_status & TRANSFER_DONE) + complete_rx(dev, ep_num); + } + } + ep_num++; + ep_intr >>= 1; + } +} + +/* + * usb client interrupt handler. + */ +static int s3c_udc_irq(int irq, void *_dev) +{ + struct s3c_udc *dev = _dev; + u32 intr_status; + u32 usb_status, gintmsk; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + intr_status = readl(®->gintsts); + gintmsk = readl(®->gintmsk); + + DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x," + "DAINT : 0x%x, DAINTMSK : 0x%x\n", + __func__, intr_status, state_names[dev->ep0state], gintmsk, + readl(®->daint), readl(®->daintmsk)); + + if (!intr_status) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + if (intr_status & INT_ENUMDONE) { + DEBUG_ISR("\tSpeed Detection interrupt\n"); + + writel(INT_ENUMDONE, ®->gintsts); + usb_status = (readl(®->dsts) & 0x6); + + if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { + DEBUG_ISR("\t\tFull Speed Detection\n"); + set_max_pktsize(dev, USB_SPEED_FULL); + + } else { + DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n", + usb_status); + set_max_pktsize(dev, USB_SPEED_HIGH); + } + } + + if (intr_status & INT_EARLY_SUSPEND) { + DEBUG_ISR("\tEarly suspend interrupt\n"); + writel(INT_EARLY_SUSPEND, ®->gintsts); + } + + if (intr_status & INT_SUSPEND) { + usb_status = readl(®->dsts); + DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status); + writel(INT_SUSPEND, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + + /* HACK to let gadget detect disconnected state */ + if (dev->driver->disconnect) { + spin_unlock_irqrestore(&dev->lock, flags); + dev->driver->disconnect(&dev->gadget); + spin_lock_irqsave(&dev->lock, flags); + } + } + } + + if (intr_status & INT_RESUME) { + DEBUG_ISR("\tResume interrupt\n"); + writel(INT_RESUME, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) { + + dev->driver->resume(&dev->gadget); + } + } + + if (intr_status & INT_RESET) { + usb_status = readl(®->gotgctl); + DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); + writel(INT_RESET, ®->gintsts); + + if ((usb_status & 0xc0000) == (0x3 << 18)) { + if (reset_available) { + DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", + reset_available); + reconfig_usbd(); + dev->ep0state = WAIT_FOR_SETUP; + reset_available = 0; + s3c_udc_pre_setup(); + } else + reset_available = 1; + + } else { + reset_available = 1; + DEBUG_ISR("\t\tRESET handling skipped\n"); + } + } + + if (intr_status & INT_IN_EP) + process_ep_in_intr(dev); + + if (intr_status & INT_OUT_EP) + process_ep_out_intr(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} + +/** Queue one request + * Kickstart transfer if needed + */ +static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_request *req; + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u32 ep_num, gintsts; + + req = container_of(_req, struct s3c_request, req); + if (unlikely(!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + + DEBUG("%s: bad params\n", __func__); + return -EINVAL; + } + + ep = container_of(_ep, struct s3c_ep, ep); + + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + + DEBUG("%s: bad ep: %s, %d, %x\n", __func__, + ep->ep.name, !ep->desc, _ep); + return -EINVAL; + } + + ep_num = ep_index(ep); + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + + DEBUG("%s: bogus device state %p\n", __func__, dev->driver); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p" + "Q empty = %d, stopped = %d\n", + __func__, _ep->name, ep_is_in(ep) ? "in" : "out", + _req, _req->length, _req->buf, + list_empty(&ep->queue), ep->stopped); + +#ifdef DEBUG_S3C_UDC + { + int i, len = _req->length; + + printf("pkt = "); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)_req->buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + if (list_empty(&ep->queue) && !ep->stopped) { + + if (ep_num == 0) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + s3c_ep0_kick(dev, ep); + req = 0; + + } else if (ep_is_in(ep)) { + gintsts = readl(®->gintsts); + DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_tx(ep, req); + } else { + gintsts = readl(®->gintsts); + DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_rx(ep, req); + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + DEBUG_EP0("%s: max = %d\n", __func__, max); + + count = setdma_tx(ep, req); + + /* last packet is usually short (or a zlp) */ + if (likely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual + count) + || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, + ep->ep.name, count, + is_last ? "/L" : "", + req->req.length - req->req.actual - count, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + ep->dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + return 0; +} + +int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) +{ + u32 bytes; + + bytes = sizeof(struct usb_ctrlrequest); + + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_index(ep)], + (unsigned long) ep->dev->dma_buf[ep_index(ep)] + + DMA_BUFFER_SIZE); + + DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__, + bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); + + return bytes; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function + * after it decodes a set address setup packet. + */ +static void udc_set_address(struct s3c_udc *dev, unsigned char address) +{ + u32 ctrl = readl(®->dcfg); + writel(DEVICE_ADDRESS(address) | ctrl, ®->dcfg); + + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, readl(®->dcfg)); + + dev->usb_address = address; +} + +static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) +{ + struct s3c_udc *dev; + u32 ep_ctrl = 0; + + dev = ep->dev; + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); + + DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", + __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); + /* + * The application can only set this bit, and the core clears it, + * when a SETUP token is received for this endpoint + */ + dev->ep0state = WAIT_FOR_SETUP; + + s3c_udc_pre_setup(); +} + +static void s3c_ep0_read(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + int ret; + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + + } else { + DEBUG("%s: ---> BUG\n", __func__); + BUG(); + return; + } + + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length == 0) { + /* zlp for Set_configuration, Set_interface, + * or Bulk-Only mass storge reset */ + + ep->len = 0; + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", + __func__, usb_ctrl->bRequest); + return; + } + + ret = setdma_rx(ep, req); +} + +/* + * DATA_STATE_XMIT + */ +static int s3c_ep0_write(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct s3c_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __func__); + return 0; + } + + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length - req->req.actual == ep0_fifo_size) { + /* Next write will end with the packet size, */ + /* so we need Zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if ((ret == 1) && !need_zlp) { + /* Last packet */ + dev->ep0state = WAIT_FOR_COMPLETE; + DEBUG_EP0("%s: finished, waiting for status\n", __func__); + + } else { + dev->ep0state = DATA_STATE_XMIT; + DEBUG_EP0("%s: not finished\n", __func__); + } + + return 1; +} + +u16 g_status; + +int s3c_udc_get_status(struct s3c_udc *dev, + struct usb_ctrlrequest *crq) +{ + u8 ep_num = crq->wIndex & 0x7F; + u32 ep_ctrl; + u32 *p = the_controller->dma_buf[1]; + + DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); + printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); + switch (crq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + g_status = 0; + DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_DEVICE: + g_status = 0x1; /* Self powered */ + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_ENDPOINT: + if (crq->wLength > 2) { + DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n"); + return 1; + } + + g_status = dev->ep[ep_num].stopped; + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", + g_status); + + break; + + default: + return 1; + } + + memcpy(p, &g_status, sizeof(g_status)); + + flush_dcache_range((unsigned long) p, + (unsigned long) p + DMA_BUFFER_SIZE); + + writel(the_controller->dma_addr[1], ®->in_endp[EP0_CON].diepdma); + writel(DIEPT_SIZ_PKT_CNT(1) | DIEPT_SIZ_XFER_SIZE(2), + ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + dev->ep0state = WAIT_FOR_NULL_COMPLETE; + + return 0; +} + +static void s3c_udc_set_nak(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + + +void s3c_udc_ep_set_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* set the stall bit */ + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + /* + * USB Spec 9.4.5: For endpoints using data toggle, regardless + * of whether an endpoint has the Halt feature set, a + * ClearFeature(ENDPOINT_HALT) request always results in the + * data toggle being reinitialized to DATA0. + */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +static int s3c_udc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON || + ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) { + DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name); + return -EINVAL; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + DEBUG("%s: %s queue not empty, req = %p\n", + __func__, ep->ep.name, + list_entry(ep->queue.next, struct s3c_request, queue)); + + return -EAGAIN; + } + + dev = ep->dev; + DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value); + + spin_lock_irqsave(&dev->lock, flags); + + if (value == 0) { + ep->stopped = 0; + s3c_udc_ep_clear_stall(ep); + } else { + if (ep_num == 0) + dev->ep0state = WAIT_FOR_SETUP; + + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +void s3c_udc_ep_activate(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0, daintmsk = 0; + + ep_num = ep_index(ep); + + /* Read DEPCTLn register */ + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + daintmsk = 1 << ep_num; + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + daintmsk = (1 << ep_num) << DAINT_OUT_BIT; + } + + DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n", + __func__, ep_num, ep_ctrl, ep_is_in(ep)); + + /* If the EP is already active don't change the EP Control + * register. */ + if (!(ep_ctrl & DEPCTL_USBACTEP)) { + ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | + (ep->bmAttributes << DEPCTL_TYPE_BIT); + ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | + (ep->ep.maxpacket << DEPCTL_MPS_BIT); + ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); + + if (ep_is_in(ep)) { + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->in_endp[ep_num].diepctl)); + } else { + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->out_endp[ep_num].doepctl)); + } + } + + /* Unmask EP Interrtupt */ + writel(readl(®->daintmsk)|daintmsk, ®->daintmsk); + DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(®->daintmsk)); + +} + +static int s3c_udc_clear_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + dev = ep->dev; + DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", + __func__, ep_num, ep_is_in(ep), clear_feature_flag); + + if (usb_ctrl->wLength != 0) { + DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + + case USB_DEVICE_TEST_MODE: + DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); + /** @todo Add CLEAR_FEATURE for TEST modes. */ + break; + } + + s3c_udc_ep0_zlp(dev); + break; + + case USB_RECIP_ENDPOINT: + DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", + usb_ctrl->wValue); + + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + + s3c_udc_ep0_zlp(dev); + + s3c_udc_ep_clear_stall(ep); + s3c_udc_ep_activate(ep); + ep->stopped = 0; + + clear_feature_num = ep_num; + clear_feature_flag = 1; + } + break; + } + + return 0; +} + +static int s3c_udc_set_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + dev = ep->dev; + + DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", + __func__, ep_num); + + if (usb_ctrl->wLength != 0) { + DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + case USB_DEVICE_B_HNP_ENABLE: + DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); + break; + + case USB_DEVICE_A_HNP_SUPPORT: + /* RH port supports HNP */ + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); + break; + + case USB_DEVICE_A_ALT_HNP_SUPPORT: + /* other RH port does */ + DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); + break; + } + + s3c_udc_ep0_zlp(dev); + return 0; + + case USB_RECIP_INTERFACE: + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n"); + break; + + case USB_RECIP_ENDPOINT: + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + s3c_udc_ep0_zlp(dev); + return 0; + } + + return 1; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + */ +void s3c_ep0_setup(struct s3c_udc *dev) +{ + struct s3c_ep *ep = &dev->ep[0]; + int i, bytes, is_in; + u8 ep_num; + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + bytes = s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); + + DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x" + "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", + __func__, usb_ctrl->bRequestType, + (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", + usb_ctrl->bRequest, + usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); + +#ifdef DEBUG_S3C_UDC + { + int i, len = sizeof(*usb_ctrl); + char *p = usb_ctrl; + + printf("pkt = "); + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)p)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && + usb_ctrl->wLength != 1) { + DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid", + __func__); + DEBUG_SETUP("wLength = %d, setup returned\n", + usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && + usb_ctrl->wLength != 0) { + /* Bulk-Only *mass storge reset of class-specific request */ + DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n", + __func__, usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } + + /* Set direction of EP0 */ + if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + is_in = 1; + + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + is_in = 0; + } + /* cope with automagic for some standard requests. */ + dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + + /* Handle some SETUP packets ourselves */ + if (dev->req_std) { + switch (usb_ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n", + __func__, usb_ctrl->wValue); + if (usb_ctrl->bRequestType + != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + udc_set_address(dev, usb_ctrl->wValue); + return; + + case USB_REQ_SET_CONFIGURATION: + DEBUG_SETUP("=====================================\n"); + DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) { + reset_available = 1; + dev->req_config = 1; + } + break; + + case USB_REQ_GET_DESCRIPTOR: + DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", + __func__); + break; + + case USB_REQ_SET_INTERFACE: + DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) { + reset_available = 1; + dev->req_config = 1; + } + break; + + case USB_REQ_GET_CONFIGURATION: + DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", + __func__); + break; + + case USB_REQ_GET_STATUS: + if (!s3c_udc_get_status(dev, usb_ctrl)) + return; + + break; + + case USB_REQ_CLEAR_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep)) + return; + + break; + + case USB_REQ_SET_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_set_feature(&dev->ep[ep_num].ep)) + return; + + break; + + default: + DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x" + "happened.\n", __func__, usb_ctrl->bRequest); + break; + } + } + + + if (likely(dev->driver)) { + /* device-2-host (IN) or no data setup command, + * process immediately */ + DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n", + __func__); + + spin_unlock(&dev->lock); + i = dev->driver->setup(&dev->gadget, usb_ctrl); + spin_lock(&dev->lock); + + if (i < 0) { + if (dev->req_config) { + DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n", + (u32)usb_ctrl->bRequest, i); + return; + } + + /* setup processing failed, force stall */ + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + DEBUG_SETUP("\tdev->driver->setup failed (%d)," + " bRequest = %d\n", + i, usb_ctrl->bRequest); + + + } else if (dev->req_pending) { + dev->req_pending = 0; + DEBUG_SETUP("\tdev->req_pending...\n"); + } + + DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]); + + } +} + +/* + * handle ep0 interrupt + */ +static void s3c_handle_ep0(struct s3c_udc *dev) +{ + if (dev->ep0state == WAIT_FOR_SETUP) { + DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__); + s3c_ep0_setup(dev); + + } else { + DEBUG_OUT_EP("%s: strange state!!(state = %s)\n", + __func__, state_names[dev->ep0state]); + } +} + +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) +{ + DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + s3c_ep0_write(dev); + + } else { + dev->ep0state = DATA_STATE_RECV; + s3c_ep0_read(dev); + } +} diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h new file mode 100644 index 0000000..9b31511 --- /dev/null +++ b/include/usb/lin_gadget_compat.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics + * Lukasz Majewski + * + * This is a Linux kernel compatibility layer for USB Gadget + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __LIN_COMPAT_H__ +#define __LIN_COMPAT_H__ + +/* common */ +#define spin_lock_init(...) +#define spin_lock(...) +#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0) +#define spin_unlock(...) +#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) +#define disable_irq(...) +#define enable_irq(...) + +#define mutex_init(...) +#define mutex_lock(...) +#define mutex_unlock(...) + +#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \ + , __FILE__, __LINE__); } + +#define KERN_WARNING +#define KERN_ERR +#define KERN_NOTICE +#define KERN_DEBUG + +#define GFP_KERNEL 0 + +#define IRQ_HANDLED 1 + +#define ENOTSUPP 524 /* Operation is not supported */ + +#define kmalloc(size, type) memalign(CONFIG_SYS_CACHELINE_SIZE, size) +#define kfree(addr) free(addr) +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + +#define __iomem +#define min_t min +#define dma_cache_maint(addr, size, mode) cache_flush() +void cache_flush(void); + +#endif /* __LIN_COMPAT_H__ */ diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h new file mode 100644 index 0000000..14dadc8 --- /dev/null +++ b/include/usb/s3c_udc.h @@ -0,0 +1,175 @@ +/* + * drivers/usb/gadget/s3c_udc.h + * Samsung S3C on-chip full/high speed USB device controllers + * Copyright (C) 2005 for Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __S3C_USB_GADGET +#define __S3C_USB_GADGET + +#include +#include +#include +#include +#include + +#define PHY0_SLEEP (1 << 5) + +/*-------------------------------------------------------------------------*/ +/* DMA bounce buffer size, 16K is enough even for mass storage */ +#define DMA_BUFFER_SIZE (4096*4) + +#define EP0_FIFO_SIZE 64 +#define EP_FIFO_SIZE 512 +#define EP_FIFO_SIZE2 1024 +/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */ +#define S3C_MAX_ENDPOINTS 4 +#define S3C_MAX_HW_ENDPOINTS 16 + +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 +#define WAIT_FOR_COMPLETE 5 +#define WAIT_FOR_OUT_COMPLETE 6 +#define WAIT_FOR_IN_COMPLETE 7 +#define WAIT_FOR_NULL_COMPLETE 8 + +#define TEST_J_SEL 0x1 +#define TEST_K_SEL 0x2 +#define TEST_SE0_NAK_SEL 0x3 +#define TEST_PACKET_SEL 0x4 +#define TEST_FORCE_ENABLE_SEL 0x5 + +/* ************************************************************************* */ +/* IO + */ + +enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +}; + +struct s3c_ep { + struct usb_ep ep; + struct s3c_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + int len; + void *dma_buf; + + u8 stopped; + u8 bEndpointAddress; + u8 bmAttributes; + + enum ep_type ep_type; + int fifo_num; +}; + +struct s3c_request { + struct usb_request req; + struct list_head queue; +}; + +struct s3c_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct s3c_plat_otg_data *pdata; + + void *dma_buf[S3C_MAX_ENDPOINTS+1]; + dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1]; + + int ep0state; + struct s3c_ep ep[S3C_MAX_ENDPOINTS]; + + unsigned char usb_address; + + unsigned req_pending:1, req_std:1, req_config:1; +}; + +extern struct s3c_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +/*-------------------------------------------------------------------------*/ +/* #define DEBUG_UDC */ +#ifdef DEBUG_UDC +#define DBG(stuff...) printf("udc: " stuff) +#else +#define DBG(stuff...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_SETUP +#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_SETUP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_EP0 +#define DEBUG_EP0(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_EP0(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC +#define DEBUG(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_ISR +#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_ISR(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_OUT_EP +#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_OUT_EP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_IN_EP +#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_IN_EP(fmt, args...) do {} while (0) +#endif + +#define ERR(stuff...) printf("ERR udc: " stuff) +#define WARN(stuff...) printf("WARNING udc: " stuff) +#define INFO(stuff...) printf("INFO udc: " stuff) + +extern void otg_phy_init(struct s3c_udc *dev); +extern void otg_phy_off(struct s3c_udc *dev); + +extern void s3c_udc_ep_set_stall(struct s3c_ep *ep); +extern int s3c_udc_probe(struct s3c_plat_otg_data *pdata); + +struct s3c_plat_otg_data { + int (*phy_control)(int on); + unsigned int regs_phy; + unsigned int regs_otg; + unsigned int usb_phy_ctrl; + unsigned int usb_flags; +}; +#endif -- cgit v0.10.2 From a954da2902e2e0fc54942691d6e0d64a7a1b3c3a Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 27 Oct 2011 10:36:47 +0200 Subject: usb:gadget:s5p Enable the USB Gadget framework at GONI This commit enables support for USB Gadgets on the GONI reference target. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Cc: Minkyu Kang Cc: Remy Bohmer diff --git a/arch/arm/include/asm/arch-s5pc1xx/cpu.h b/arch/arm/include/asm/arch-s5pc1xx/cpu.h index e74959f..e699fc4 100644 --- a/arch/arm/include/asm/arch-s5pc1xx/cpu.h +++ b/arch/arm/include/asm/arch-s5pc1xx/cpu.h @@ -55,6 +55,10 @@ #define S5PC110_VIC1_BASE 0xF2100000 #define S5PC110_VIC2_BASE 0xF2200000 #define S5PC110_VIC3_BASE 0xF2300000 +#define S5PC110_OTG_BASE 0xEC000000 +#define S5PC110_PHY_BASE 0xEC100000 +#define S5PC110_USB_PHY_CONTROL 0xE010E80C + #ifndef __ASSEMBLY__ #include diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c index e191bfb..e8fb1ea 100644 --- a/board/samsung/goni/goni.c +++ b/board/samsung/goni/goni.c @@ -26,7 +26,9 @@ #include #include #include - +#include +#include +#include DECLARE_GLOBAL_DATA_PTR; static struct s5pc110_gpio *s5pc110_gpio; @@ -100,3 +102,47 @@ int board_mmc_init(bd_t *bis) return s5p_mmc_init(0, 4); } #endif + +#ifdef CONFIG_USB_GADGET +static int s5pc1xx_phy_control(int on) +{ + int ret; + static int status; + struct pmic *p = get_pmic(); + + if (pmic_probe(p)) + return -1; + + if (on && !status) { + ret = pmic_set_output(p, MAX8998_REG_ONOFF1, + MAX8998_LDO3, LDO_ON); + ret = pmic_set_output(p, MAX8998_REG_ONOFF2, + MAX8998_LDO8, LDO_ON); + if (ret) { + puts("MAX8998 LDO setting error!\n"); + return -1; + } + status = 1; + } else if (!on && status) { + ret = pmic_set_output(p, MAX8998_REG_ONOFF1, + MAX8998_LDO3, LDO_OFF); + ret = pmic_set_output(p, MAX8998_REG_ONOFF2, + MAX8998_LDO8, LDO_OFF); + if (ret) { + puts("MAX8998 LDO setting error!\n"); + return -1; + } + status = 0; + } + udelay(10000); + + return 0; +} + +struct s3c_plat_otg_data s5pc110_otg_data = { + .phy_control = s5pc1xx_phy_control, + .regs_phy = S5PC110_PHY_BASE, + .regs_otg = S5PC110_OTG_BASE, + .usb_phy_ctrl = S5PC110_USB_PHY_CONTROL, +}; +#endif diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h index 3434de7..56b5547 100644 --- a/include/configs/s5p_goni.h +++ b/include/configs/s5p_goni.h @@ -237,5 +237,8 @@ #define CONFIG_SYS_I2C_SPEED 50000 #define CONFIG_I2C_MULTI_BUS #define CONFIG_SYS_MAX_I2C_BUS 7 +#define CONFIG_USB_GADGET +#define CONFIG_USB_GADGET_S3C_UDC_OTG +#define CONFIG_USB_GADGET_DUALSPEED #endif /* __CONFIG_H */ -- cgit v0.10.2 From 5d2947a3fc8d130cfa39fccdefb87082abbf0e9b Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 11 Nov 2011 14:03:34 +0100 Subject: USB: MX5: add helper functions to enable USB clocks Signed-off-by: Wolfgang Grandegger Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu V2: Fix spacing in crm_regs.h diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 933ce05..e92f106 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -50,6 +50,78 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE; +void set_usboh3_clk(void) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->cscmr1) & + ~MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK; + reg |= 1 << MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET; + writel(reg, &mxc_ccm->cscmr1); + + reg = readl(&mxc_ccm->cscdr1); + reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK; + reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK; + reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET; + reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET; + + writel(reg, &mxc_ccm->cscdr1); +} + +void enable_usboh3_clk(unsigned char enable) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->CCGR2); + if (enable) + reg |= 1 << MXC_CCM_CCGR2_CG14_OFFSET; + else + reg &= ~(1 << MXC_CCM_CCGR2_CG14_OFFSET); + writel(reg, &mxc_ccm->CCGR2); +} + +void set_usb_phy1_clk(void) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->cscmr1); + reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; + writel(reg, &mxc_ccm->cscmr1); +} + +void enable_usb_phy1_clk(unsigned char enable) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->CCGR4); + if (enable) + reg |= 1 << MXC_CCM_CCGR4_CG5_OFFSET; + else + reg &= ~(1 << MXC_CCM_CCGR4_CG5_OFFSET); + writel(reg, &mxc_ccm->CCGR4); +} + +void set_usb_phy2_clk(void) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->cscmr1); + reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; + writel(reg, &mxc_ccm->cscmr1); +} + +void enable_usb_phy2_clk(unsigned char enable) +{ + unsigned int reg; + + reg = readl(&mxc_ccm->CCGR4); + if (enable) + reg |= 1 << MXC_CCM_CCGR4_CG6_OFFSET; + else + reg &= ~(1 << MXC_CCM_CCGR4_CG6_OFFSET); + writel(reg, &mxc_ccm->CCGR4); +} + /* * Calculate the frequency of PLLn. */ diff --git a/arch/arm/include/asm/arch-mx5/clock.h b/arch/arm/include/asm/arch-mx5/clock.h index 1f8a537..ea972a3 100644 --- a/arch/arm/include/asm/arch-mx5/clock.h +++ b/arch/arm/include/asm/arch-mx5/clock.h @@ -40,4 +40,9 @@ u32 imx_get_uartclk(void); u32 imx_get_fecclk(void); unsigned int mxc_get_clock(enum mxc_clock clk); +void set_usb_phy2_clk(void); +void enable_usb_phy2_clk(unsigned char enable); +void set_usboh3_clk(void); +void enable_usboh3_clk(unsigned char enable); + #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/include/asm/arch-mx5/crm_regs.h b/arch/arm/include/asm/arch-mx5/crm_regs.h index fcc0e36..bdeafbc 100644 --- a/arch/arm/include/asm/arch-mx5/crm_regs.h +++ b/arch/arm/include/asm/arch-mx5/crm_regs.h @@ -195,7 +195,10 @@ struct mxc_ccm_reg { /* Define the bits in register CCGRx */ #define MXC_CCM_CCGR_CG_MASK 0x3 +#define MXC_CCM_CCGR4_CG5_OFFSET 10 +#define MXC_CCM_CCGR4_CG6_OFFSET 12 #define MXC_CCM_CCGR5_CG5_OFFSET 10 +#define MXC_CCM_CCGR2_CG14_OFFSET 28 /* Define the bits in register CLPCR */ #define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18) -- cgit v0.10.2 From 1ca56202532395f26fc59a87e62c9cab1541f9f3 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 11 Nov 2011 14:03:36 +0100 Subject: USB: MX5: add generic USB EHCI support for mx51 and mx53 It's derived from ehci-mxc and uses the header files of the ehci-fsl interface. The callback board_ehci_hcd_init() has been introduced to allow for board-specific setup when USB is started. Signed-off-by: Wolfgang Grandegger CC: Stefano Babic CC: Remy Bohmer diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 09abb75..77e217f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o endif COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o +COBJS-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_KIRKWOOD) += ehci-kirkwood.o diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c new file mode 100644 index 0000000..4c961d3 --- /dev/null +++ b/drivers/usb/host/ehci-mx5.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2009 Daniel Mack + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" +#include "ehci-core.h" + +#define MX5_USBOTHER_REGS_OFFSET 0x800 + + +#define MXC_OTG_OFFSET 0 +#define MXC_H1_OFFSET 0x200 +#define MXC_H2_OFFSET 0x400 + +#define MXC_USBCTRL_OFFSET 0 +#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 +#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc +#define MXC_USB_CTRL_1_OFFSET 0x10 +#define MXC_USBH2CTRL_OFFSET 0x14 + +/* USB_CTRL */ +#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */ +#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */ + +/* USB_PHY_CTRL_FUNC */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */ + +/* USBH2CTRL */ +#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) + +/* USB_CTRL_1 */ +#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ + unsigned int v; + void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR; + void __iomem *usbother_base; + int ret = 0; + + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + switch (port) { + case 0: /* OTG port */ + if (flags & MXC_EHCI_INTERNAL_PHY) { + v = __raw_readl(usbother_base + + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + /* OC/USBPWR is not used */ + v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; + else + /* OC/USBPWR is used */ + v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; + __raw_writel(v, usbother_base + + MXC_USB_PHY_CTR_FUNC_OFFSET); + + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MXC_OTG_UCTRL_OPM_BIT; + else + v &= ~MXC_OTG_UCTRL_OPM_BIT; + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + } + break; + case 1: /* Host 1 Host ULPI */ +#ifdef CONFIG_MX51 + /* The clock for the USBH1 ULPI port will come externally + from the PHY. */ + v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET); + __raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + + MXC_USB_CTRL_1_OFFSET); +#endif + + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ + else + v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + + break; + case 2: /* Host 2 ULPI */ + v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ + else + v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ + + __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); + break; + } + + return ret; +} + +int ehci_hcd_init(void) +{ + struct usb_ehci *ehci; +#ifdef CONFIG_MX53 + struct clkctl *sc_regs = (struct clkctl *)CCM_BASE_ADDR; + u32 reg; + + reg = __raw_readl(&sc_regs->cscmr1) & ~(1 << 26); + /* derive USB PHY clock multiplexer from PLL3 */ + reg |= 1 << 26; + __raw_writel(reg, &sc_regs->cscmr1); +#endif + + set_usboh3_clk(); + enable_usboh3_clk(1); + set_usb_phy2_clk(); + enable_usb_phy2_clk(1); + mdelay(1); + + /* do board specific initialization */ + board_ehci_hcd_init(CONFIG_MXC_USB_PORT); + + ehci = (struct usb_ehci *)(OTG_BASE_ADDR + + (0x200 * CONFIG_MXC_USB_PORT)); + hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + hcor = (struct ehci_hcor *)((uint32_t)hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + setbits_le32(&ehci->usbmode, CM_HOST); + + __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); + setbits_le32(&ehci->portsc, USB_EN); + + mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); + + mdelay(10); + + return 0; +} + +int ehci_hcd_stop(void) +{ + return 0; +} + + diff --git a/include/usb/ehci-fsl.h b/include/usb/ehci-fsl.h index f50a8f2..4ce0ab3 100644 --- a/include/usb/ehci-fsl.h +++ b/include/usb/ehci-fsl.h @@ -243,4 +243,14 @@ struct usb_ehci { u8 res14[0xafc]; }; +/* + * For MXC SOCs + */ +#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5) +#define MXC_EHCI_TTL_ENABLED (1 << 6) +#define MXC_EHCI_INTERNAL_PHY (1 << 7) + +/* Board-specific initialization */ +int board_ehci_hcd_init(int port); + #endif /* _EHCI_FSL_H */ -- cgit v0.10.2 From 0f8c86b5498d7e61760c7b85ddfeb068b48fc828 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 24 Nov 2011 04:22:17 +0100 Subject: USB: MX5: Abstract out mx51 USB pixmux configuration Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 4c961d3..7b88137 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -58,6 +58,77 @@ /* USB_CTRL_1 */ #define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) +/* USB pin configuration */ +#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ + PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ + PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) + +#ifdef CONFIG_MX51 +/* + * Configure the MX51 USB H1 IOMUX + */ +void setup_iomux_usb_h1(void) +{ + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG); + + mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG); +} + +/* + * Configure the MX51 USB H2 IOMUX + */ +void setup_iomux_usb_h2(void) +{ + mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG); + + mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG); +} +#endif + int mxc_set_usbcontrol(int port, unsigned int flags) { unsigned int v; diff --git a/include/usb/ehci-fsl.h b/include/usb/ehci-fsl.h index 4ce0ab3..2869302 100644 --- a/include/usb/ehci-fsl.h +++ b/include/usb/ehci-fsl.h @@ -253,4 +253,10 @@ struct usb_ehci { /* Board-specific initialization */ int board_ehci_hcd_init(int port); +/* CPU-specific abstracted-out IOMUX init */ +#ifdef CONFIG_MX51 +void setup_iomux_usb_h1(void); +void setup_iomux_usb_h2(void); +#endif + #endif /* _EHCI_FSL_H */ -- cgit v0.10.2 From 1b80f2706ea29047b8be42bd8341c99b05bfa20b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 24 Nov 2011 05:14:00 +0100 Subject: USB: MX5: Add MX5 usb post-init callback This is useful for USB Transceivers init etc. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 7b88137..68a673e 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "ehci.h" #include "ehci-core.h" @@ -198,6 +199,13 @@ int mxc_set_usbcontrol(int port, unsigned int flags) return ret; } +void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +} + +void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) + __attribute((weak, alias("__board_ehci_hcd_postinit"))); + int ehci_hcd_init(void) { struct usb_ehci *ehci; @@ -217,7 +225,7 @@ int ehci_hcd_init(void) enable_usb_phy2_clk(1); mdelay(1); - /* do board specific initialization */ + /* Do board specific initialization */ board_ehci_hcd_init(CONFIG_MXC_USB_PORT); ehci = (struct usb_ehci *)(OTG_BASE_ADDR + @@ -231,9 +239,11 @@ int ehci_hcd_init(void) setbits_le32(&ehci->portsc, USB_EN); mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); - mdelay(10); + /* Do board specific post-initialization */ + board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT); + return 0; } -- cgit v0.10.2 From 45cf6ada60cb19fcf0b88e98235504b687f3e25f Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 11 Nov 2011 14:03:37 +0100 Subject: USB: mx53loco: add end enable USB host support on port 1 Signed-off-by: Wolfgang Grandegger Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/board/freescale/mx53loco/mx53loco.c b/board/freescale/mx53loco/mx53loco.c index 3cf4195..57170ce 100644 --- a/board/freescale/mx53loco/mx53loco.c +++ b/board/freescale/mx53loco/mx53loco.c @@ -78,6 +78,16 @@ static void setup_iomux_uart(void) PAD_CTL_ODE_OPENDRAIN_ENABLE); } +#ifdef CONFIG_USB_EHCI_MX5 +void board_ehci_hcd_init(int port) +{ + /* request VBUS power enable pin, GPIO[8}, gpio7 */ + mxc_request_iomux(MX53_PIN_ATA_DA_2, IOMUX_CONFIG_ALT1); + gpio_direction_output(IOMUX_TO_GPIO(MX53_PIN_ATA_DA_2), 0); + gpio_set_value(IOMUX_TO_GPIO(MX53_PIN_ATA_DA_2), 1); +} +#endif + static void setup_iomux_fec(void) { /*FEC_MDIO*/ diff --git a/include/configs/mx53loco.h b/include/configs/mx53loco.h index 537649e..34a4edd 100644 --- a/include/configs/mx53loco.h +++ b/include/configs/mx53loco.h @@ -72,6 +72,19 @@ #define CONFIG_CMD_MII #define CONFIG_CMD_NET +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_CMD_FAT +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX5 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_SMSC95XX +#define CONFIG_MXC_USB_PORT 1 +#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW) +#define CONFIG_MXC_USB_FLAGS 0 + /* allow to overwrite serial and ethaddr */ #define CONFIG_ENV_OVERWRITE #define CONFIG_CONS_INDEX 1 -- cgit v0.10.2 From 055d969332d511a33c0bce248cf1adf90a51a9fb Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 11 Nov 2011 14:03:38 +0100 Subject: USB: mx51evk: add end enable USB host support on port 1 Signed-off-by: Wolfgang Grandegger Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/board/freescale/mx51evk/mx51evk.c b/board/freescale/mx51evk/mx51evk.c index e5b0929..13c5941 100644 --- a/board/freescale/mx51evk/mx51evk.c +++ b/board/freescale/mx51evk/mx51evk.c @@ -35,6 +35,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -172,6 +173,64 @@ static void setup_iomux_spi(void) } #endif +#ifdef CONFIG_USB_EHCI_MX5 +#define MX51EVK_USBH1_HUB_RST IOMUX_TO_GPIO(MX51_PIN_GPIO1_7) /* GPIO1_7 */ +#define MX51EVK_USBH1_STP IOMUX_TO_GPIO(MX51_PIN_USBH1_STP) /* GPIO1_27 */ +#define MX51EVK_USB_CLK_EN_B IOMUX_TO_GPIO(MX51_PIN_EIM_D18) /* GPIO2_1 */ +#define MX51EVK_USB_PHY_RESET IOMUX_TO_GPIO(MX51_PIN_EIM_D21) /* GPIO2_5 */ + +#define USBH1_PAD (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | \ + PAD_CTL_100K_PU | PAD_CTL_PUE_PULL | \ + PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE) +#define GPIO_PAD (PAD_CTL_DRV_HIGH | PAD_CTL_PKE_ENABLE | \ + PAD_CTL_SRE_FAST) +#define NO_PAD (1 << 16) + +static void setup_usb_h1(void) +{ + setup_iomux_usb_h1(); + + /* GPIO_1_7 for USBH1 hub reset */ + mxc_request_iomux(MX51_PIN_GPIO1_7, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_GPIO1_7, NO_PAD); + + /* GPIO_2_1 */ + mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT1); + mxc_iomux_set_pad(MX51_PIN_EIM_D17, GPIO_PAD); + + /* GPIO_2_5 for USB PHY reset */ + mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT1); + mxc_iomux_set_pad(MX51_PIN_EIM_D21, GPIO_PAD); +} + +void board_ehci_hcd_init(int port) +{ + /* Set USBH1_STP to GPIO and toggle it */ + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USBH1_PAD); + + gpio_direction_output(MX51EVK_USBH1_STP, 0); + gpio_direction_output(MX51EVK_USB_PHY_RESET, 0); + mdelay(10); + gpio_set_value(MX51EVK_USBH1_STP, 1); + + /* Set back USBH1_STP to be function */ + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USBH1_PAD); + + /* De-assert USB PHY RESETB */ + gpio_set_value(MX51EVK_USB_PHY_RESET, 1); + + /* Drive USB_CLK_EN_B line low */ + gpio_direction_output(MX51EVK_USB_CLK_EN_B, 0); + + /* Reset USB hub */ + gpio_direction_output(MX51EVK_USBH1_HUB_RST, 0); + mdelay(2); + gpio_set_value(MX51EVK_USBH1_HUB_RST, 1); +} +#endif + static void power_init(void) { unsigned int val; @@ -394,6 +453,9 @@ int board_early_init_f(void) { setup_iomux_uart(); setup_iomux_fec(); +#ifdef CONFIG_USB_EHCI_MX5 + setup_usb_h1(); +#endif return 0; } diff --git a/include/configs/mx51evk.h b/include/configs/mx51evk.h index dd53f48..3b18a18 100644 --- a/include/configs/mx51evk.h +++ b/include/configs/mx51evk.h @@ -110,6 +110,19 @@ #define CONFIG_CMD_MII #define CONFIG_CMD_NET +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_CMD_FAT +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX5 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_SMSC95XX +#define CONFIG_MXC_USB_PORT 1 +#define CONFIG_MXC_USB_PORTSC PORT_PTS_ULPI +#define CONFIG_MXC_USB_FLAGS MXC_EHCI_POWER_PINS_ENABLED + /* allow to overwrite serial and ethaddr */ #define CONFIG_ENV_OVERWRITE #define CONFIG_CONS_INDEX 1 -- cgit v0.10.2 From 3874b6d674d194c8d34b4f7242337c9f2120ca9a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 11 Jul 2011 02:37:01 +0200 Subject: USB: EHCI: Allow EHCI post-powerup configuration in board files This patch allows USB to work on some hosts, which need additional frobing after the host was powered up via regular USB powerup sequence. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1cee158..3f7bc2c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -205,6 +205,14 @@ static inline void ehci_invalidate_dcache(struct QH *qh) } #endif /* CONFIG_EHCI_DCACHE */ +void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ + mdelay(50); +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) + __attribute__((weak, alias("__ehci_powerup_fixup"))); + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -713,8 +721,8 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * usb 2.0 specification say 50 ms resets on * root */ - wait_ms(50); - /* terminate the reset */ + ehci_powerup_fixup(status_reg, ®); + ehci_writel(status_reg, reg & ~EHCI_PS_PR); /* * A host controller must terminate the reset -- cgit v0.10.2 From f93022c3b11f191141c9128ee88d297cec3b9330 Mon Sep 17 00:00:00 2001 From: Jana Rapava Date: Mon, 5 Dec 2011 11:07:00 +0200 Subject: USB: Add generic ULPI layer and a viewport Add partial ULPI specification implementation that should be enough to interface the ULPI PHYs in the boot loader context. Add a viewport implementation for Chipidea/ARC based controllers. Signed-off-by: Jana Rapava Signed-off-by: Igor Grinberg Cc: Remy Bohmer Cc: Stefano Babic Cc: Wolfgang Grandegger Cc: Simon Glass diff --git a/Makefile b/Makefile index 29e3dbb..bf8524d 100644 --- a/Makefile +++ b/Makefile @@ -286,6 +286,7 @@ LIBS += drivers/usb/gadget/libusb_gadget.o LIBS += drivers/usb/host/libusb_host.o LIBS += drivers/usb/musb/libusb_musb.o LIBS += drivers/usb/phy/libusb_phy.o +LIBS += drivers/usb/ulpi/libusb_ulpi.o LIBS += drivers/video/libvideo.o LIBS += drivers/watchdog/libwatchdog.o LIBS += common/libcommon.o diff --git a/drivers/usb/ulpi/Makefile b/drivers/usb/ulpi/Makefile new file mode 100644 index 0000000..d43b229 --- /dev/null +++ b/drivers/usb/ulpi/Makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2011 Jana Rapava +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc. +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libusb_ulpi.o + +COBJS-$(CONFIG_USB_ULPI) += ulpi.o +COBJS-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi-viewport.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/ulpi/ulpi-viewport.c b/drivers/usb/ulpi/ulpi-viewport.c new file mode 100644 index 0000000..fa2e004 --- /dev/null +++ b/drivers/usb/ulpi/ulpi-viewport.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi_viewport.c + * + * Original Copyright follow: + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +/* ULPI viewport control bits */ +#define ULPI_SS (1 << 27) +#define ULPI_RWCTRL (1 << 29) +#define ULPI_RWRUN (1 << 30) +#define ULPI_WU (1 << 31) + +/* + * Wait for the ULPI request to complete + * + * @ulpi_viewport - the address of the viewport + * @mask - expected value to wait for + * + * returns 0 on mask match, ULPI_ERROR on time out. + */ +static int ulpi_wait(u32 ulpi_viewport, u32 mask) +{ + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + /* Wait for the bits in mask to become zero. */ + while (--timeout) { + if ((readl(ulpi_viewport) & mask) == 0) + return 0; + + udelay(1); + } + + return ULPI_ERROR; +} + +/* + * Wake the ULPI PHY up for communication + * + * returns 0 on success. + */ +static int ulpi_wakeup(u32 ulpi_viewport) +{ + int err; + + if (readl(ulpi_viewport) & ULPI_SS) + return 0; /* already awake */ + + writel(ULPI_WU, ulpi_viewport); + + err = ulpi_wait(ulpi_viewport, ULPI_WU); + if (err) + printf("ULPI wakeup timed out\n"); + + return err; +} + +/* + * Issue a ULPI read/write request + * + * @value - the ULPI request + */ +static int ulpi_request(u32 ulpi_viewport, u32 value) +{ + int err; + + err = ulpi_wakeup(ulpi_viewport); + if (err) + return err; + + writel(value, ulpi_viewport); + + err = ulpi_wait(ulpi_viewport, ULPI_RWRUN); + if (err) + printf("ULPI request timed out\n"); + + return err; +} + +u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value) +{ + u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); + + return ulpi_request(ulpi_viewport, val); +} + +u32 ulpi_read(u32 ulpi_viewport, u8 *reg) +{ + u32 err; + u32 val = ULPI_RWRUN | ((u32)reg << 16); + + err = ulpi_request(ulpi_viewport, val); + if (err) + return err; + + return (readl(ulpi_viewport) >> 8) & 0xff; +} diff --git a/drivers/usb/ulpi/ulpi.c b/drivers/usb/ulpi/ulpi.c new file mode 100644 index 0000000..805e29d --- /dev/null +++ b/drivers/usb/ulpi/ulpi.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi.c + * Generic ULPI USB transceiver support + * + * Original Copyright follow: + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#define ULPI_ID_REGS_COUNT 4 +#define ULPI_TEST_VALUE 0x55 /* 0x55 == 0b01010101 */ + +static struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +static int ulpi_integrity_check(u32 ulpi_viewport) +{ + u32 err, val, tval = ULPI_TEST_VALUE; + int i; + + /* Use the 'special' test value to check all bits */ + for (i = 0; i < 2; i++, tval <<= 1) { + err = ulpi_write(ulpi_viewport, &ulpi->scratch, tval); + if (err) + return err; + + val = ulpi_read(ulpi_viewport, &ulpi->scratch); + if (val != tval) { + printf("ULPI integrity check failed\n"); + return val; + } + } + + return 0; +} + +int ulpi_init(u32 ulpi_viewport) +{ + u32 val, id = 0; + u8 *reg = &ulpi->product_id_high; + int i; + + /* Assemble ID from four ULPI ID registers (8 bits each). */ + for (i = 0; i < ULPI_ID_REGS_COUNT; i++) { + val = ulpi_read(ulpi_viewport, reg - i); + if (val == ULPI_ERROR) + return val; + + id = (id << 8) | val; + } + + /* Split ID into vendor and product ID. */ + debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff); + + return ulpi_integrity_check(ulpi_viewport); +} + +int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed) +{ + u8 tspeed = ULPI_FC_FULL_SPEED; + u32 val; + + switch (speed) { + case ULPI_FC_HIGH_SPEED: + case ULPI_FC_FULL_SPEED: + case ULPI_FC_LOW_SPEED: + case ULPI_FC_FS4LS: + tspeed = speed; + break; + default: + printf("ULPI: %s: wrong transceiver speed specified, " + "falling back to full speed\n", __func__); + } + + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous speed setting */ + val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; + + return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_set_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind) +{ + u32 flags = ULPI_OTG_DRVVBUS; + u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + if (ext_power) + flags |= ULPI_OTG_DRVVBUS_EXT; + if (ext_ind) + flags |= ULPI_OTG_EXTVBUSIND; + + return ulpi_write(ulpi_viewport, reg, flags); +} + +int ulpi_set_pd(u32 ulpi_viewport, int enable) +{ + u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; + u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + return ulpi_write(ulpi_viewport, reg, val); +} + +int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode) +{ + u8 topmode = ULPI_FC_OPMODE_NORMAL; + u32 val; + + switch (opmode) { + case ULPI_FC_OPMODE_NORMAL: + case ULPI_FC_OPMODE_NONDRIVING: + case ULPI_FC_OPMODE_DISABLE_NRZI: + case ULPI_FC_OPMODE_NOSYNC_NOEOP: + topmode = opmode; + break; + default: + printf("ULPI: %s: wrong OpMode specified, " + "falling back to OpMode Normal\n", __func__); + } + + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous opmode setting */ + val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; + + return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode) +{ + switch (smode) { + case ULPI_IFACE_6_PIN_SERIAL_MODE: + case ULPI_IFACE_3_PIN_SERIAL_MODE: + break; + default: + printf("ULPI: %s: unrecognized Serial Mode specified\n", + __func__); + return ULPI_ERROR; + } + + return ulpi_write(ulpi_viewport, &ulpi->iface_ctrl_set, smode); +} + +int ulpi_suspend(u32 ulpi_viewport) +{ + u32 err; + + err = ulpi_write(ulpi_viewport, &ulpi->function_ctrl_clear, + ULPI_FC_SUSPENDM); + if (err) + printf("ULPI: %s: failed writing the suspend bit\n", __func__); + + return err; +} + +/* + * Wait for ULPI PHY reset to complete. + * Actual wait for reset must be done in a view port specific way, + * because it involves checking the DIR line. + */ +static int __ulpi_reset_wait(u32 ulpi_viewport) +{ + u32 val; + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + /* Wait for the RESET bit to become zero */ + while (--timeout) { + /* + * This function is generic and suppose to work + * with any viewport, so we cheat here and don't check + * for the error of ulpi_read(), if there is one, then + * there will be a timeout. + */ + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (!(val & ULPI_FC_RESET)) + return 0; + + udelay(1); + } + + printf("ULPI: %s: reset timed out\n", __func__); + + return ULPI_ERROR; +} +int ulpi_reset_wait(u32) __attribute__((weak, alias("__ulpi_reset_wait"))); + +int ulpi_reset(u32 ulpi_viewport) +{ + u32 err; + + err = ulpi_write(ulpi_viewport, + &ulpi->function_ctrl_set, ULPI_FC_RESET); + if (err) { + printf("ULPI: %s: failed writing reset bit\n", __func__); + return err; + } + + return ulpi_reset_wait(ulpi_viewport); +} diff --git a/include/usb/ulpi.h b/include/usb/ulpi.h new file mode 100644 index 0000000..d871290 --- /dev/null +++ b/include/usb/ulpi.h @@ -0,0 +1,298 @@ +/* + * Generic ULPI interface. + * + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Register offsets taken from: + * linux/include/linux/usb/ulpi.h + * + * Original Copyrights follow: + * Copyright (C) 2010 Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * version 2 of that License. + */ + +#ifndef __USB_ULPI_H__ +#define __USB_ULPI_H__ + +#define ULPI_ERROR (1 << 8) /* overflow from any register value */ + +#ifndef CONFIG_USB_ULPI_TIMEOUT +#define CONFIG_USB_ULPI_TIMEOUT 1000 /* timeout in us */ +#endif + +/* + * Initialize the ULPI transciever and check the interface integrity. + * @ulpi_viewport - the address of the ULPI viewport register. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_init(u32 ulpi_viewport); + +/* + * Select transceiver speed. + * @speed - ULPI_FC_HIGH_SPEED, ULPI_FC_FULL_SPEED (default), + * ULPI_FC_LOW_SPEED, ULPI_FC_FS4LS + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed); + +/* + * Enable/disable VBUS. + * @ext_power - external VBUS supply is used (default is false) + * @ext_indicator - external VBUS over-current indicator is used + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_enable_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind); + +/* + * Enable/disable pull-down resistors on D+ and D- USB lines. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_set_pd(u32 ulpi_viewport, int enable); + +/* + * Select OpMode. + * @opmode - ULPI_FC_OPMODE_NORMAL (default), ULPI_FC_OPMODE_NONDRIVING, + * ULPI_FC_OPMODE_DISABLE_NRZI, ULPI_FC_OPMODE_NOSYNC_NOEOP + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode); + +/* + * Switch to Serial Mode. + * @smode - ULPI_IFACE_6_PIN_SERIAL_MODE or ULPI_IFACE_3_PIN_SERIAL_MODE + * + * returns 0 on success, ULPI_ERROR on failure. + * + * Notes: + * Switches immediately to Serial Mode. + * To return from Serial Mode, STP line needs to be asserted. + */ +int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode); + +/* + * Put PHY into low power mode. + * + * returns 0 on success, ULPI_ERROR on failure. + * + * Notes: + * STP line must be driven low to keep the PHY in suspend. + * To resume the PHY, STP line needs to be asserted. + */ +int ulpi_suspend(u32 ulpi_viewport); + +/* + * Reset the transceiver. ULPI interface and registers are not affected. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_reset(u32 ulpi_viewport); + + +/* ULPI access methods below must be implemented for each ULPI viewport. */ + +/* + * Write to the ULPI PHY register via the viewport. + * @reg - the ULPI register (one of the fields in struct ulpi_regs). + * @value - the value - only 8 lower bits are used, others ignored. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value); + +/* + * Read the ULPI PHY register content via the viewport. + * @reg - the ULPI register (one of the fields in struct ulpi_regs). + * + * returns register content on success, ULPI_ERROR on failure. + */ +u32 ulpi_read(u32 ulpi_viewport, u8 *reg); + +/* + * Wait for the reset to complete. + * The Link must not attempt to access the PHY until the reset has + * completed and DIR line is de-asserted. + */ +int ulpi_reset_wait(u32 ulpi_viewport); + +/* Access Extended Register Set (indicator) */ +#define ACCESS_EXT_REGS_OFFSET 0x2f /* read-write */ +/* Vendor-specific */ +#define VENDOR_SPEC_OFFSET 0x30 + +/* + * Extended Register Set + * + * Addresses 0x00-0x3F map directly to Immediate Register Set. + * Addresses 0x40-0x7F are reserved. + * Addresses 0x80-0xff are vendor-specific. + */ +#define EXT_VENDOR_SPEC_OFFSET 0x80 + +/* ULPI registers, bits and offsets definitions */ +struct ulpi_regs { + /* Vendor ID and Product ID: 0x00 - 0x03 Read-only */ + u8 vendor_id_low; + u8 vendor_id_high; + u8 product_id_low; + u8 product_id_high; + /* Function Control: 0x04 - 0x06 Read */ + u8 function_ctrl; /* 0x04 Write */ + u8 function_ctrl_set; /* 0x05 Set */ + u8 function_ctrl_clear; /* 0x06 Clear */ + /* Interface Control: 0x07 - 0x09 Read */ + u8 iface_ctrl; /* 0x07 Write */ + u8 iface_ctrl_set; /* 0x08 Set */ + u8 iface_ctrl_clear; /* 0x09 Clear */ + /* OTG Control: 0x0A - 0x0C Read */ + u8 otg_ctrl; /* 0x0A Write */ + u8 otg_ctrl_set; /* 0x0B Set */ + u8 otg_ctrl_clear; /* 0x0C Clear */ + /* USB Interrupt Enable Rising: 0x0D - 0x0F Read */ + u8 usb_ie_rising; /* 0x0D Write */ + u8 usb_ie_rising_set; /* 0x0E Set */ + u8 usb_ie_rising_clear; /* 0x0F Clear */ + /* USB Interrupt Enable Falling: 0x10 - 0x12 Read */ + u8 usb_ie_falling; /* 0x10 Write */ + u8 usb_ie_falling_set; /* 0x11 Set */ + u8 usb_ie_falling_clear; /* 0x12 Clear */ + /* USB Interrupt Status: 0x13 Read-only */ + u8 usb_int_status; + /* USB Interrupt Latch: 0x14 Read-only with auto-clear */ + u8 usb_int_latch; + /* Debug: 0x15 Read-only */ + u8 debug; + /* Scratch Register: 0x16 - 0x18 Read */ + u8 scratch; /* 0x16 Write */ + u8 scratch_set; /* 0x17 Set */ + u8 scratch_clear; /* 0x18 Clear */ + /* + * Optional Carkit registers: + * Carkit Control: 0x19 - 0x1B Read + */ + u8 carkit_ctrl; /* 0x19 Write */ + u8 carkit_ctrl_set; /* 0x1A Set */ + u8 carkit_ctrl_clear; /* 0x1B Clear */ + /* Carkit Interrupt Delay: 0x1C Read, Write */ + u8 carkit_int_delay; + /* Carkit Interrupt Enable: 0x1D - 0x1F Read */ + u8 carkit_ie; /* 0x1D Write */ + u8 carkit_ie_set; /* 0x1E Set */ + u8 carkit_ie_clear; /* 0x1F Clear */ + /* Carkit Interrupt Status: 0x20 Read-only */ + u8 carkit_int_status; + /* Carkit Interrupt Latch: 0x21 Read-only with auto-clear */ + u8 carkit_int_latch; + /* Carkit Pulse Control: 0x22 - 0x24 Read */ + u8 carkit_pulse_ctrl; /* 0x22 Write */ + u8 carkit_pulse_ctrl_set; /* 0x23 Set */ + u8 carkit_pulse_ctrl_clear; /* 0x24 Clear */ + /* + * Other optional registers: + * Transmit Positive Width: 0x25 Read, Write + */ + u8 transmit_pos_width; + /* Transmit Negative Width: 0x26 Read, Write */ + u8 transmit_neg_width; + /* Receive Polarity Recovery: 0x27 Read, Write */ + u8 recv_pol_recovery; + /* + * Addresses 0x28 - 0x2E are reserved, so we use offsets + * for immediate registers with higher addresses + */ +}; + +/* + * Register Bits + */ + +/* Function Control */ +#define ULPI_FC_XCVRSEL_MASK (3 << 0) +#define ULPI_FC_HIGH_SPEED (0 << 0) +#define ULPI_FC_FULL_SPEED (1 << 0) +#define ULPI_FC_LOW_SPEED (2 << 0) +#define ULPI_FC_FS4LS (3 << 0) +#define ULPI_FC_TERMSELECT (1 << 2) +#define ULPI_FC_OPMODE_MASK (3 << 3) +#define ULPI_FC_OPMODE_NORMAL (0 << 3) +#define ULPI_FC_OPMODE_NONDRIVING (1 << 3) +#define ULPI_FC_OPMODE_DISABLE_NRZI (2 << 3) +#define ULPI_FC_OPMODE_NOSYNC_NOEOP (3 << 3) +#define ULPI_FC_RESET (1 << 5) +#define ULPI_FC_SUSPENDM (1 << 6) + +/* Interface Control */ +#define ULPI_IFACE_6_PIN_SERIAL_MODE (1 << 0) +#define ULPI_IFACE_3_PIN_SERIAL_MODE (1 << 1) +#define ULPI_IFACE_CARKITMODE (1 << 2) +#define ULPI_IFACE_CLOCKSUSPENDM (1 << 3) +#define ULPI_IFACE_AUTORESUME (1 << 4) +#define ULPI_IFACE_EXTVBUS_COMPLEMENT (1 << 5) +#define ULPI_IFACE_PASSTHRU (1 << 6) +#define ULPI_IFACE_PROTECT_IFC_DISABLE (1 << 7) + +/* OTG Control */ +#define ULPI_OTG_ID_PULLUP (1 << 0) +#define ULPI_OTG_DP_PULLDOWN (1 << 1) +#define ULPI_OTG_DM_PULLDOWN (1 << 2) +#define ULPI_OTG_DISCHRGVBUS (1 << 3) +#define ULPI_OTG_CHRGVBUS (1 << 4) +#define ULPI_OTG_DRVVBUS (1 << 5) +#define ULPI_OTG_DRVVBUS_EXT (1 << 6) +#define ULPI_OTG_EXTVBUSIND (1 << 7) + +/* + * USB Interrupt Enable Rising, + * USB Interrupt Enable Falling, + * USB Interrupt Status and + * USB Interrupt Latch + */ +#define ULPI_INT_HOST_DISCONNECT (1 << 0) +#define ULPI_INT_VBUS_VALID (1 << 1) +#define ULPI_INT_SESS_VALID (1 << 2) +#define ULPI_INT_SESS_END (1 << 3) +#define ULPI_INT_IDGRD (1 << 4) + +/* Debug */ +#define ULPI_DEBUG_LINESTATE0 (1 << 0) +#define ULPI_DEBUG_LINESTATE1 (1 << 1) + +/* Carkit Control */ +#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0) +#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1) +#define ULPI_CARKIT_CTRL_TXDEN (1 << 2) +#define ULPI_CARKIT_CTRL_RXDEN (1 << 3) +#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4) +#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5) +#define ULPI_CARKIT_CTRL_MICEN (1 << 6) + +/* Carkit Interrupt Enable */ +#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0) +#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1) +#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2) +#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3) +#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4) + +/* Carkit Interrupt Status and Latch */ +#define ULPI_CARKIT_INT_IDFLOAT (1 << 0) +#define ULPI_CARKIT_INT_CARINTDET (1 << 1) +#define ULPI_CARKIT_INT_DP (1 << 2) + +/* Carkit Pulse Control*/ +#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0) +#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1) +#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) +#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) + + +#endif /* __USB_ULPI_H__ */ -- cgit v0.10.2 From d98d8bc1c913a5a1aea6b17365f90c430d1fc95a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Jun 2011 21:46:07 +0200 Subject: USB: efikamx: Enable USB on EfikaMX and EfikaSB Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Remy Bohmer Cc: Wolfgang Grandegger Cc: Jason Liu diff --git a/board/efikamx/Makefile b/board/efikamx/Makefile index bd1056e..fdd188e 100644 --- a/board/efikamx/Makefile +++ b/board/efikamx/Makefile @@ -29,6 +29,10 @@ LIB = $(obj)lib$(BOARD).o COBJS := efikamx.o +ifdef CONFIG_CMD_USB +COBJS += efikamx-usb.o +endif + SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) SOBJS := $(addprefix $(obj),$(SOBJS)) diff --git a/board/efikamx/efikamx-usb.c b/board/efikamx/efikamx-usb.c new file mode 100644 index 0000000..840bd9a --- /dev/null +++ b/board/efikamx/efikamx-usb.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Marek Vasut + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../drivers/usb/host/ehci.h" + +/* USB pin configuration */ +#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ + PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ + PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) + +/* + * Configure the USB H1 and USB H2 IOMUX + */ +void setup_iomux_usb(void) +{ + setup_iomux_usb_h1(); + + if (machine_is_efikasb()) + setup_iomux_usb_h2(); + + /* USB PHY reset */ + mxc_request_iomux(MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT1); + mxc_iomux_set_pad(MX51_PIN_EIM_D27, PAD_CTL_PKE_ENABLE | + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + + /* USB HUB reset */ + mxc_request_iomux(MX51_PIN_GPIO1_5, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_GPIO1_5, PAD_CTL_PKE_ENABLE | + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + + /* WIFI EN (act low) */ + mxc_request_iomux(MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A22, 0); + /* WIFI RESET */ + mxc_request_iomux(MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A16, 0); + /* BT EN (act low) */ + mxc_request_iomux(MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A17, 0); +} + +/* + * Enable devices connected to USB BUSes + */ +static void efika_usb_enable_devices(void) +{ + /* Enable Bluetooth */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 0); + udelay(10000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 1); + + /* Enable WiFi */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A22), 1); + udelay(10000); + + /* Reset the WiFi chip */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 0); + udelay(10000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 1); +} + +/* + * Reset USB HUB (or HUBs on EfikaSB) + */ +static void efika_usb_hub_reset(void) +{ + /* HUB reset */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); +} + +/* + * Reset USB PHY (or PHYs on EfikaSB) + */ +static void efika_usb_phy_reset(void) +{ + /* SMSC 3317 PHY reset */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 1); +} + +static void efika_ehci_init(struct usb_ehci *ehci, uint32_t stp_gpio, + uint32_t alt0, uint32_t alt1) +{ + int ret; + struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + + mxc_request_iomux(stp_gpio, alt0); + mxc_iomux_set_pad(stp_gpio, PAD_CTL_DRV_HIGH | + PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); + gpio_direction_output(IOMUX_TO_GPIO(stp_gpio), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(stp_gpio), 1); + udelay(1000); + + mxc_request_iomux(stp_gpio, alt1); + mxc_iomux_set_pad(stp_gpio, USB_PAD_CONFIG); + udelay(10000); + + ret = ulpi_init((u32)&ehci->ulpi_viewpoint); + if (ret) { + printf("Efika USB ULPI initialization failed\n"); + return; + } + + /* ULPI set flags */ + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl, + ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN | + ULPI_OTG_EXTVBUSIND); + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->function_ctrl, + ULPI_FC_FULL_SPEED | ULPI_FC_OPMODE_NORMAL | + ULPI_FC_SUSPENDM); + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->iface_ctrl, 0); + + /* Set VBus */ + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); + + /* + * Set VBusChrg + * + * NOTE: This violates USB specification, but otherwise, USB on Efika + * doesn't work. + */ + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, + ULPI_OTG_CHRGVBUS); +} + +int board_ehci_hcd_init(int port) +{ + /* Init iMX51 EHCI */ + efika_usb_phy_reset(); + efika_usb_hub_reset(); + efika_usb_enable_devices(); + + return 0; +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ + uint32_t port = OTG_BASE_ADDR + (0x200 * CONFIG_MXC_USB_PORT); + struct usb_ehci *ehci = (struct usb_ehci *)port; + struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + + ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, + ULPI_OTG_CHRGVBUS); + + wait_ms(50); + + /* terminate the reset */ + *reg = ehci_readl(status_reg); + *reg |= EHCI_PS_PE; +} + +void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ + uint32_t tmp; + + if (port == 0) { + /* Adjust UTMI PHY frequency to 24MHz */ + tmp = readl(OTG_BASE_ADDR + 0x80c); + tmp = (tmp & ~0x3) | 0x01; + writel(tmp, OTG_BASE_ADDR + 0x80c); + } else if (port == 1) { + efika_ehci_init(ehci, MX51_PIN_USBH1_STP, + IOMUX_CONFIG_ALT2, IOMUX_CONFIG_ALT0); + } else if ((port == 2) && machine_is_efikasb()) { + efika_ehci_init(ehci, MX51_PIN_EIM_A26, + IOMUX_CONFIG_ALT1, IOMUX_CONFIG_ALT2); + } + + if (port) + mdelay(10); +} diff --git a/board/efikamx/efikamx.c b/board/efikamx/efikamx.c index 3d2cc1a..1f6c457 100644 --- a/board/efikamx/efikamx.c +++ b/board/efikamx/efikamx.c @@ -540,6 +540,15 @@ static inline void setup_iomux_ata(void) { } #endif /* + * EHCI USB + */ +#ifdef CONFIG_CMD_USB +extern void setup_iomux_usb(void); +#else +static inline void setup_iomux_usb(void) { } +#endif + +/* * LED configuration */ void setup_iomux_led(void) @@ -688,6 +697,12 @@ int board_late_init(void) setup_iomux_led(); setup_iomux_ata(); + setup_iomux_usb(); + + if (machine_is_efikasb()) + setenv("preboot", "usb reset ; setenv stdin usbkbd\0"); + + setup_iomux_led(); efikamx_toggle_led(EFIKAMX_LED_BLUE); diff --git a/include/configs/efikamx.h b/include/configs/efikamx.h index 2b069d6..522487b 100644 --- a/include/configs/efikamx.h +++ b/include/configs/efikamx.h @@ -44,6 +44,10 @@ #define CONFIG_SYS_TEXT_BASE 0x97800000 +#define CONFIG_L2_OFF +#define CONFIG_SYS_ICACHE_OFF +#define CONFIG_SYS_DCACHE_OFF + /* * Bootloader Components Configuration */ @@ -53,6 +57,8 @@ #define CONFIG_CMD_FAT #define CONFIG_CMD_EXT2 #define CONFIG_CMD_IDE +#define CONFIG_CMD_NET +#define CONFIG_CMD_DATE #undef CONFIG_CMD_IMLS /* @@ -175,17 +181,46 @@ #endif /* + * USB + */ +#define CONFIG_CMD_USB +#ifdef CONFIG_CMD_USB +#define CONFIG_USB_EHCI /* Enable EHCI USB support */ +#define CONFIG_USB_EHCI_MX5 +#define CONFIG_USB_ULPI +#define CONFIG_USB_ULPI_VIEWPORT +#define CONFIG_MXC_USB_PORT 1 +#if (CONFIG_MXC_USB_PORT == 0) +#define CONFIG_MXC_USB_PORTSC (1 << 28) +#define CONFIG_MXC_USB_FLAGS MXC_EHCI_INTERNAL_PHY +#else +#define CONFIG_MXC_USB_PORTSC (2 << 30) +#define CONFIG_MXC_USB_FLAGS 0 +#endif +#define CONFIG_EHCI_IS_TDI +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_KEYBOARD +#define CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP +#define CONFIG_PREBOOT +/* USB NET */ +#ifdef CONFIG_CMD_NET +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_NET_MULTI +#define CONFIG_CMD_PING +#define CONFIG_CMD_DHCP +#endif +#endif /* CONFIG_CMD_USB */ + +/* * Filesystems */ #ifdef CONFIG_CMD_FAT #define CONFIG_DOS_PARTITION +#ifdef CONFIG_CMD_NET +#define CONFIG_CMD_NFS +#endif #endif - -#undef CONFIG_CMD_PING -#undef CONFIG_CMD_DHCP -#undef CONFIG_CMD_NET -#undef CONFIG_CMD_NFS -#define CONFIG_CMD_DATE /* * Miscellaneous configurable options -- cgit v0.10.2