diff options
author | Wolfgang Denk <wd@denx.de> | 2011-12-12 07:58:58 +0100 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2011-12-12 07:58:58 +0100 |
commit | b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db (patch) | |
tree | b8b649483183d93dedfcb5512d0b9720aa91644f /drivers | |
parent | 4f1a2cd1637027f31de7796aedb1fa5fc0ec0f97 (diff) | |
parent | d98d8bc1c913a5a1aea6b17365f90c430d1fc95a (diff) | |
download | u-boot-imx-b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db.zip u-boot-imx-b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db.tar.gz u-boot-imx-b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-usb
* 'master' of git://git.denx.de/u-boot-usb:
USB: efikamx: Enable USB on EfikaMX and EfikaSB
USB: Add generic ULPI layer and a viewport
USB: EHCI: Allow EHCI post-powerup configuration in board files
USB: mx51evk: add end enable USB host support on port 1
USB: mx53loco: add end enable USB host support on port 1
USB: MX5: Add MX5 usb post-init callback
USB: MX5: Abstract out mx51 USB pixmux configuration
USB: MX5: add generic USB EHCI support for mx51 and mx53
USB: MX5: add helper functions to enable USB clocks
usb:gadget:s5p Enable the USB Gadget framework at GONI
usb:gadget:s5p USB Device Controller (UDC) implementation
ehci: speed up initialization
usb: add help for missing start subcommand
cosmetic: remove excess whitespace from usb command help
usb: align usb_endpoint_descriptor to 16-bit boundary
usbtty: init endpoints prior to startup events
pxa: convert pxa27x_udc to use read and write functions
pxa: activate the first usb host port on pxa27x by default
pxa: fix usb host register mismatch
ehci-fsl: correct size of ehci caplength
USB: Add usb_event_poll() to get keyboards working with EHCI
USB: gadaget: add Marvell controller support
USB: Fix complaints about strict aliasing in OHCI-HCD
USB: Drop dead code from usb_kbd.c
USB: Rework usb_kbd.c
USB: Add functionality to poll the USB keyboard via control EP
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/serial/usbtty.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/gadget_chips.h | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/mv_udc.c | 499 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa27x_udc.c | 183 | ||||
-rw-r--r-- | drivers/usb/gadget/regs-otg.h | 271 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg.c | 892 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 1444 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 47 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mx5.c | 255 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 75 | ||||
-rw-r--r-- | drivers/usb/ulpi/Makefile | 44 | ||||
-rw-r--r-- | drivers/usb/ulpi/ulpi-viewport.c | 118 | ||||
-rw-r--r-- | drivers/usb/ulpi/ulpi.c | 227 |
15 files changed, 3951 insertions, 121 deletions
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)); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e72713..64b091f 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,9 +26,14 @@ 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 +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 <leiwen@marvell.com> + * + * 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 <common.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/types.h> +#include <usb/mv_udc.h> + +#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("<UEI %x>\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/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 <common.h> #include <config.h> #include <asm/byteorder.h> -#include <usbdcore.h> -#include <usbdcore_ep0.h> +#include <usbdevice.h> #include <asm/arch/hardware.h> +#include <asm/io.h> #include <usb/pxa27x_udc.h> +#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; } 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 <herbert@13thfloor.at> + * + * Registers remapping: + * Lukasz Majewski <l.majewski@samsumg.com> + * + * 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 <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * 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 <common.h> +#include <asm/errno.h> +#include <linux/list.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/io.h> + +#include <asm/mach-types.h> +#include <asm/arch/gpio.h> + +#include "regs-otg.h" +#include <usb/s3c_udc.h> +#include <usb/lin_gadget_compat.h> + +/***********************************************************/ + +#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 <usb/s3c_udc.h> + +#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 <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * 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<<DEPCTL_NEXT_EP_BIT)); + + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->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/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-hcd.c b/drivers/usb/host/ehci-hcd.c index 2197119..3f7bc2c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -26,6 +26,10 @@ #include <asm/io.h> #include <malloc.h> #include <watchdog.h> +#ifdef CONFIG_USB_KEYBOARD +#include <stdio_dev.h> +extern unsigned char new[]; +#endif #include "ehci.h" @@ -48,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 */ @@ -201,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; @@ -709,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 @@ -895,5 +907,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 */ diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c new file mode 100644 index 0000000..68a673e --- /dev/null +++ b/drivers/usb/host/ehci-mx5.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * 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 <common.h> +#include <usb.h> +#include <errno.h> +#include <linux/compiler.h> +#include <usb/ehci-fsl.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/mx5x_pins.h> +#include <asm/arch/iomux.h> + +#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) + +/* 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; + 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; +} + +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; +#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); + + /* Do board specific post-initialization */ + board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT); + + return 0; +} + +int ehci_hcd_stop(void) +{ + return 0; +} + + 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; 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 <fermata7@gmail.com> +# 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 <fermata7@gmail.com> + * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Jana Rapava <fermata7@gmail.com> + * Igor Grinberg <grinberg@compulab.co.il> + * + * 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 <common.h> +#include <asm/io.h> +#include <usb/ulpi.h> + +/* 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 <fermata7@gmail.com> + * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Jana Rapava <fermata7@gmail.com> + * Igor Grinberg <grinberg@compulab.co.il> + * + * Based on: + * linux/drivers/usb/otg/ulpi.c + * Generic ULPI USB transceiver support + * + * Original Copyright follow: + * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de> + * + * Based on sources from + * + * Sascha Hauer <s.hauer@pengutronix.de> + * 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 <common.h> +#include <exports.h> +#include <usb/ulpi.h> + +#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); +} |