[U-Boot] [PATCH v2 1/4] Add Atmel USBA UDC
Marcel Janssen
korgull at home.nl
Sun Feb 13 22:52:18 CET 2011
From: Marcel <korgull at home.nl>
Atmel USBA UDC cleanup
Atmel USBA UDC cleanup
more cleanup of Atmel USBA UDC
Some more cleaning of Atmel USBA UDC
further cleaning of Atmel USBA UDC
Signed-off-by: Marcel <korgull at home.nl>
---
drivers/usb/gadget/Makefile | 1 +
drivers/usb/gadget/atmel_usba_udc.c | 1438 +++++++++++++++++++++++++++++++++++
include/usb/atmel_usba_udc.h | 398 ++++++++++
3 files changed, 1837 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/gadget/atmel_usba_udc.c
create mode 100644 include/usb/atmel_usba_udc.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 0846233..024844d 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -29,6 +29,7 @@ LIB := $(obj)libusb_gadget.o
ifdef CONFIG_USB_ETHER
COBJS-y += ether.o epautoconf.o config.o usbstring.o
COBJS-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
+COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_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/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
new file mode 100644
index 0000000..6d02de6
--- /dev/null
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -0,0 +1,1438 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2011 Marcel Janssen, Admesy B.V.
+ * Copyright (C) 2005-2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <linux/list.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/mtd/compat.h>
+#include <linux/porting-compat.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/at91_pmc.h>
+#include <asm/arch/io.h>
+
+#include <usb/atmel_usba_udc.h>
+
+#define __iomem
+
+#define memcpy_toio(a, b, c) memcpy((a), (b), (c))
+#define memcpy_fromio(a, b, c) memcpy((a), (b), (c))
+#define REQ_COUNT 8
+#define MAX_REQ (REQ_COUNT-1)
+
+static struct usba_udc the_udc;
+#define true 1
+#define false 0
+static const char driver_name[] = "atmel_usba_udc";
+static const char ep0name[] = "ep0";
+
+
+static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
+{
+ unsigned int transaction_len;
+
+ transaction_len = req->req.length - req->req.actual;
+ req->last_transaction = 1;
+ if (transaction_len > ep->ep.maxpacket) {
+ transaction_len = ep->ep.maxpacket;
+ req->last_transaction = 0;
+ } else if (transaction_len == ep->ep.maxpacket && req->req.zero)
+ req->last_transaction = 0;
+
+ debug("%s: submit_transaction, req %p (length %d)%s\n",
+ ep->ep.name, req, transaction_len,
+ req->last_transaction ? ", done" : "");
+
+ memcpy_toio(ep->fifo, req->req.buf + req->req.actual, transaction_len);
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ req->req.actual += transaction_len;
+}
+
+static void submit_request(struct usba_ep *ep, struct usba_request *req)
+{
+
+ req->req.actual = 0;
+ req->submitted = 1;
+
+ debug("%s : submit_request: req %p (length %d)\n",
+ ep->ep.name, req, req->req.length);
+ next_fifo_transaction(ep, req);
+ if (req->last_transaction) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+ } else {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+ }
+}
+
+static void submit_next_request(struct usba_ep *ep)
+{
+ struct usba_request *req;
+
+ if (list_empty(&ep->queue)) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);
+ return;
+ }
+ req = list_entry(ep->queue.next, struct usba_request, queue);
+ if (!req->submitted)
+ submit_request(ep, req);
+ debug("%s : submit_next_request: req %p (length %d)\n",
+ ep->ep.name, req, req->req.length);
+}
+
+static void send_status(struct usba_udc *udc, struct usba_ep *ep)
+{
+ debug("Send USBA status\n");
+ ep->state = STATUS_STAGE_IN;
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+}
+
+static void receive_data(struct usba_ep *ep)
+{
+ struct usba_udc *udc = ep->udc;
+ struct usba_request *req;
+ unsigned long status;
+ unsigned int bytecount, nr_busy;
+ int is_complete = 0;
+
+ status = usba_ep_readl(ep, STA);
+ nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+ debug("receive data: nr_busy=%u\n", nr_busy);
+
+ while (nr_busy > 0) {
+ if (list_empty(&ep->queue)) {
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ break;
+ }
+ req = list_entry(ep->queue.next,
+ struct usba_request, queue);
+
+ bytecount = USBA_BFEXT(BYTE_COUNT, status);
+
+ if (status & (1 << 31))
+ is_complete = 1;
+ if (req->req.actual + bytecount >= req->req.length) {
+ is_complete = 1;
+ bytecount = req->req.length - req->req.actual;
+ }
+
+ memcpy_fromio(req->req.buf + req->req.actual,
+ ep->fifo, bytecount);
+ req->req.actual += bytecount;
+
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+
+ if (is_complete) {
+ debug("%s: request done\n", ep->ep.name);
+ req->req.status = 0;
+ list_del_init(&req->queue);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ req->req.complete(&ep->ep, &req->req);
+ debug("request->complete done\n");
+ }
+
+ status = usba_ep_readl(ep, STA);
+ nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+ debug("nr_busy = %d\n", nr_busy);
+ if (is_complete && ep_is_control(ep)) {
+ send_status(udc, ep);
+ break;
+ }
+ debug("rcv : bytecount=%d actual=%d\n", req->req.actual);
+ }
+}
+
+static void
+request_complete(struct usba_ep *ep, struct usba_request *req, int status)
+{
+
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+
+ debug("%s: req %p complete: status %d, actual %u\n",
+ ep->ep.name, req, req->req.status, req->req.actual);
+
+ req->req.complete(&ep->ep, &req->req);
+ debug("request complete finished\n");
+}
+
+static int
+usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct usba_ep *ep;
+ struct usba_udc *udc;
+ unsigned long ept_cfg, maxpacket;
+ unsigned int nr_trans;
+
+ ep = container_of(_ep, struct usba_ep, ep);
+ udc = ep->udc;
+
+ debug("Endpoint enable ep %d\n", ep->index);
+ maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
+
+ if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
+ || ep->index == 0
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || maxpacket == 0
+ || maxpacket > ep->fifo_size) {
+ debug("ep_enable: Invalid argument");
+ return -EINVAL;
+ }
+
+ ep->is_isoc = 0;
+ ep->is_in = 0;
+
+ if (maxpacket <= 8)
+ ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+ else
+ /* LSB is bit 1, not 0 */
+ ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
+
+ debug("%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
+ ep->ep.name, ept_cfg, maxpacket);
+
+ if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ ep->is_in = 1;
+ ept_cfg |= USBA_EPT_DIR_IN;
+ debug("%s: is IN endpoint\n", ep->ep.name);
+ }
+
+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ debug("%s: is CTRL endpoint\n", ep->ep.name);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ debug("%s: is ISOC endpoint\n", ep->ep.name);
+ if (!ep->can_isoc)
+ return -EINVAL;
+
+
+ /*
+ * Bits 11:12 specify number of _additional_
+ * transactions per microframe.
+ */
+ nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1;
+ if (nr_trans > 3)
+ return -EINVAL;
+
+ ep->is_isoc = 1;
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+
+ /*
+ * Do triple-buffering on high-bandwidth iso endpoints.
+ */
+ if (nr_trans > 1 && ep->nr_banks == 3)
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
+ else
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+ ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ debug("%s: is BULK endpoint\n", ep->ep.name);
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ debug("%s: is INT endpoint\n", ep->ep.name);
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ break;
+ }
+
+ if (ep->desc) {
+ debug("ep%d already enabled\n", ep->index);
+ return -EBUSY;
+ }
+
+ ep->desc = desc;
+ ep->ep.maxpacket = maxpacket;
+
+ usba_ep_writel(ep, CFG, ept_cfg);
+ debug("ep%d CFG = 0x%lx\n", ep->index, ept_cfg);
+ usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+
+ debug("ep%d not using DMA\n", ep->index);
+ usba_writel(udc, INT_ENB, (usba_readl(udc, INT_ENB)
+ | USBA_BF(EPT_INT, 1 << ep->index)));
+
+ debug("EPT_CFG%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, CFG));
+ debug("INT_ENB after init: %#08lx\n",
+ (unsigned long)usba_readl(udc, INT_ENB));
+ debug("INT_STA after init: %#08lx\n",
+ (unsigned long)usba_readl(udc, INT_STA));
+ debug("EPT_CTL_ENB%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, CTL_ENB));
+ debug("EPT_CTL_DIS%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, CTL_DIS));
+ debug("EPT_CTL%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, CTL));
+ debug("EPT_STA%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, STA));
+ return 0;
+}
+
+static int usba_ep_disable(struct usb_ep *_ep)
+{
+ struct usba_ep *ep;
+ struct usba_udc *udc;
+
+ ep = container_of(_ep, struct usba_ep, ep);
+ udc = ep->udc;
+
+ if (ep == &ep->udc->ep[0])
+ return -EINVAL;
+
+ debug("Endpoint disable ep %d\n", ep->index);
+
+ ep->desc = NULL;
+
+ usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
+ usba_writel(udc, INT_ENB,
+ usba_readl(udc, INT_ENB)
+ & ~USBA_BF(EPT_INT, 1 << ep->index));
+
+ return 0;
+}
+
+#define REQ_COUNT 8
+#define MAX_REQ (REQ_COUNT-1)
+static struct usba_request req_pool[REQ_COUNT];
+
+static void init_requests(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_REQ; i++)
+ req_pool[i].in_use = 0;
+}
+
+static void free_request(struct usba_request *ptr)
+{
+ if (ptr < &req_pool[0] && ptr > &req_pool[REQ_COUNT]) {
+ debug("%s: ptr 0x%p does not specify valid req\n", \
+ __func__, ptr);
+ if (!(ptr->in_use))
+ debug("%s: ptr not marked as \"in_use\"\n", __func__);
+
+ hang();
+ }
+ ptr->in_use = 0;
+}
+
+static struct usba_request *alloc_request(void)
+{
+ int i;
+ struct usba_request *ptr = NULL;
+
+ for (i = 0; i < MAX_REQ; i++) {
+ if (!req_pool[i].in_use) {
+ ptr = &req_pool[i];
+ req_pool[i].in_use = 1;
+ DBG("alloc_req: request[%d]\n", i);
+ break;
+ }
+ }
+ if (!ptr)
+ DBG("panic: no more free req buffers\n");
+ return ptr;
+}
+
+static struct usb_request *
+usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct usba_request *req;
+
+ req = alloc_request();
+
+ if (!req) {
+ debug("ALLOC REQUEST FAILLED : %s\n", _ep->name);
+ return NULL;
+ }
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+static void usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct usba_request *req;
+
+ debug("FREE request for %s\n", _ep->name);
+ req = container_of(_req, struct usba_request, req);
+
+ free_request(req);
+}
+
+static int
+usba_ep_queue(struct usb_ep *_ep,
+ struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct usba_request *req = to_usba_req(_req);
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_udc *udc = ep->udc;
+ int ret;
+
+ debug("%s: queue req %p, len %u\n",
+ ep->ep.name, req, _req->length);
+
+ if (!_req || !_req->complete
+ || !_req->buf || !list_empty(&req->queue)) {
+
+ debug("invalid request\n");
+ if (!_req)
+ debug("NO REQUEST\n");
+ if (!_req->complete)
+ debug("NO REQUEST COMPLETE\n");
+ if (!_req->buf)
+ debug("NO REQUEST BUF\n");
+ if (!list_empty(&req->queue))
+ debug("NO LIST EMPTY\n");
+ return -EINVAL;
+ }
+
+ if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+ DBG("invalid ep\n");
+ return -EINVAL;
+ }
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || !ep->desc)
+ return -ESHUTDOWN;
+
+ req->submitted = 0;
+ req->last_transaction = 0;
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* May have received a reset since last time we checked */
+ ret = -ESHUTDOWN;
+
+ if (ep->desc) {
+ list_add_tail(&req->queue, &ep->queue);
+
+ if ((!ep_is_control(ep) && ep->is_in) ||
+ (ep_is_control(ep) && (ep->state == DATA_STAGE_IN ||
+ ep->state == STATUS_STAGE_IN))) {
+
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+ debug("WE TRANSMITTED DATA ep= %s\n", ep->ep.name);
+ } else {
+ debug("WE RECEIVED OUT DATA ep= %s\n", ep->ep.name);
+ usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+ /*FIXME may be needed, not sure yet */
+ /*usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);*/
+ }
+ ret = 0;
+ }
+ return ret;
+}
+
+static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct usba_ep *ep = container_of(_ep, struct usba_ep, ep);
+ struct usba_request *req = container_of(_req, struct usba_request, req);
+
+ debug("ep_dequeue: %s, req %p\n",
+ ep->ep.name, req);
+
+ if (!_ep || ep->ep.name == ep0name) {
+ DBG("invalid dequeue request\n");
+ if (!_ep)
+ debug("NO ENDPOINT\n");
+ if (ep->ep.name == ep0name)
+ debug("Cannot dequeue on ep0\n");
+ return -EINVAL;
+ }
+ /*
+ * Errors should stop the queue from advancing until the
+ * completion function returns.
+ */
+
+ /* 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)
+ return -EINVAL;
+ request_complete(ep, req, -ECONNRESET);
+
+ /* Process the next request if any */
+ submit_next_request(ep);
+
+ return 0;
+}
+
+static int usba_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct usba_ep *ep = container_of(_ep, struct usba_ep, ep);
+ int ret = 0;
+
+ debug("Endpoint halt\n");
+ if (!ep->desc) {
+ debug("Attempted to halt uninitialized ep %s\n",
+ ep->ep.name);
+ return -ENODEV;
+ }
+ if (ep->is_isoc) {
+ debug("Attempted to halt isochronous ep %s\n",
+ ep->ep.name);
+ return -ENOTTY;
+ }
+
+ /*
+ * We can't halt IN endpoints while there are still data to be
+ * transferred
+ */
+ if (!list_empty(&ep->queue)
+ || ((value && ep->is_in && (usba_ep_readl(ep, STA)
+ & USBA_BF(BUSY_BANKS, -1L))))) {
+ ret = -EAGAIN;
+ } else {
+ if (value)
+ usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+ else
+ usba_ep_writel(ep, CLR_STA,
+ USBA_FORCE_STALL | USBA_TOGGLE_CLR);
+ usba_ep_readl(ep, STA);
+ }
+
+ return ret;
+}
+
+static const struct usb_ep_ops usba_ep_ops = {
+ .enable = usba_ep_enable,
+ .disable = usba_ep_disable,
+ .alloc_request = usba_ep_alloc_request,
+ .free_request = usba_ep_free_request,
+ .queue = usba_ep_queue,
+ .dequeue = usba_ep_dequeue,
+ .set_halt = usba_ep_set_halt,
+};
+
+static int usba_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+
+ return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM));
+}
+
+static int usba_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ u32 ctrl;
+ int ret = -EINVAL;
+ debug("UDC wakeup\n");
+
+ if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+ ctrl = usba_readl(udc, CTRL);
+ usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ debug("UDC set selfpowered\n");
+ if (is_selfpowered)
+ udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+ else
+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops usba_udc_ops = {
+ .get_frame = usba_udc_get_frame,
+ .wakeup = usba_udc_wakeup,
+ .set_selfpowered = usba_udc_set_selfpowered,
+};
+
+static struct usb_endpoint_descriptor usba_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+ /* FIXME: I have no idea what to put here */
+ .bInterval = 1,
+};
+
+static struct usba_udc the_udc = {
+ .regs = (unsigned *) AT91SAM9G45_BASE_UDPHS,
+ .fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO,
+ .gadget = {
+ .ops = &usba_udc_ops,
+ .ep0 = &the_udc.ep[0].ep,
+ .is_dualspeed = 1,
+ .name = driver_name,
+ },
+
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &usba_ep_ops,
+ .maxpacket = 64,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 0,
+ .fifo_size = 64,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[1] = {
+ .ep = {
+ .name = "ep1",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 1,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[2] = {
+ .ep = {
+ .name = "ep2",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 2,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[3] = {
+ .ep = {
+ .name = "ep3",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 3,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[4] = {
+ .ep = {
+ .name = "ep4",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 4,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[5] = {
+ .ep = {
+ .name = "ep5",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 5,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+ .ep[6] = {
+ .ep = {
+ .name = "ep6",
+ .ops = &usba_ep_ops,
+ .maxpacket = 1024,
+ },
+ .udc = &the_udc,
+ .is_pingpong = 0,
+ .int_mask = 1 << 6,
+ .fifo_size = 1024,
+ .nr_banks = 1,
+ .can_dma = 0,
+ .can_isoc = 0,
+ },
+};
+
+static void reset_all_endpoints(struct usba_udc *udc)
+{
+
+ struct usba_ep *ep;
+ struct usba_request *req, *tmp_req;
+ debug("USBA : reset all endpoints\n");
+ usba_writel(udc, EPT_RST, ~0UL);
+
+ ep = container_of(udc->gadget.ep0, struct usba_ep, ep);
+ list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, -ECONNRESET);
+ }
+
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc)
+ usba_ep_disable(&ep->ep);
+ }
+}
+
+static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
+{
+ struct usba_ep *ep;
+ debug("get EP by Address\n");
+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+ return container_of(udc->gadget.ep0, struct usba_ep, ep);
+
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+ if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+ == (wIndex & USB_ENDPOINT_NUMBER_MASK))
+ return ep;
+ }
+ return NULL;
+}
+
+/* Called with interrupts disabled and udc->lock held */
+static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep)
+{
+ debug("Set protocol stall\n");
+ usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+ ep->state = WAIT_FOR_SETUP;
+}
+
+static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep)
+{
+ debug("IS STALLED\n");
+ if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL)
+ return 1;
+ return 0;
+}
+
+static inline void set_address(struct usba_udc *udc, unsigned int addr)
+{
+ u32 regval;
+ debug("USBA : set address %d\n", addr);
+ regval = usba_readl(udc, CTRL);
+ regval = USBA_BFINS(DEV_ADDR, addr, regval);
+ usba_writel(udc, CTRL, regval);
+}
+
+/* Avoid overly long expressions */
+static inline char feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq)
+{
+ if (crq->wValue == __constant_cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP))
+ return true;
+ return false;
+}
+
+static inline char feature_is_ep_halt(struct usb_ctrlrequest *crq)
+{
+ if (crq->wValue == __constant_cpu_to_le16(USB_ENDPOINT_HALT))
+ return true;
+ return false;
+}
+
+static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
+ struct usb_ctrlrequest *crq)
+{
+ int retval = 0;
+
+ debug("USBA : EP0 setup\n");
+ switch (crq->bRequest) {
+ case USB_REQ_GET_STATUS: {
+ u16 status;
+ debug("USBA : USB_REQ_GET_STATUS\n");
+ if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {
+ status = cpu_to_le16(udc->devstatus);
+ } else if (crq->bRequestType
+ == (USB_DIR_IN | USB_RECIP_INTERFACE)) {
+ status = cpu_to_le16(0);
+ } else if (crq->bRequestType
+ == (USB_DIR_IN | USB_RECIP_ENDPOINT)) {
+ struct usba_ep *target;
+
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ status = 0;
+ if (is_stalled(udc, target))
+ status |= cpu_to_le16(1);
+ } else {
+ debug("Delegate : USB_REQ_GET_STATUS\n");
+ goto delegate;
+ }
+ /* Write directly to the FIFO. No queueing is done. */
+ if (crq->wLength != cpu_to_le16(sizeof(status)))
+ goto stall;
+ ep->state = DATA_STAGE_IN;
+ debug("Writing to FIFO\n");
+ __raw_writew(status, ep->fifo);
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ break;
+ }
+
+ case USB_REQ_CLEAR_FEATURE: {
+ debug("USBA : USB_REQ_CLEAR_FEATURE\n");
+ if (crq->bRequestType == USB_RECIP_DEVICE) {
+ if (feature_is_dev_remote_wakeup(crq))
+ udc->devstatus
+ &= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+ else
+ /* Can't CLEAR_FEATURE TEST_MODE */
+ goto stall;
+ } else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+ struct usba_ep *target;
+
+ if (crq->wLength != cpu_to_le16(0)
+ || !feature_is_ep_halt(crq))
+ goto stall;
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL);
+ if (target->index != 0)
+ usba_ep_writel(target, CLR_STA,
+ USBA_TOGGLE_CLR);
+ } else {
+ goto delegate;
+ }
+
+ send_status(udc, ep);
+ break;
+ }
+
+ case USB_REQ_SET_FEATURE: {
+ debug("USBA : USB_REQ_SET_FEATURE\n");
+ if (crq->bRequestType == USB_RECIP_DEVICE) {
+ if (feature_is_dev_remote_wakeup(crq))
+ udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+ else
+ goto stall;
+ } else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+ struct usba_ep *target;
+
+ if (crq->wLength != cpu_to_le16(0)
+ || !feature_is_ep_halt(crq))
+ goto stall;
+
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ usba_ep_writel(target, SET_STA, USBA_FORCE_STALL);
+ } else
+ goto delegate;
+
+ send_status(udc, ep);
+ break;
+ }
+
+ case USB_REQ_SET_ADDRESS:
+ debug("USBA : USB_REQ_SET_ADDRESS\n");
+ if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+ goto delegate;
+
+ set_address(udc, le16_to_cpu(crq->wValue));
+ send_status(udc, ep);
+ ep->state = STATUS_STAGE_ADDR;
+ break;
+
+ default:
+delegate:
+ debug("deligate SETUP Type = %4x %4x %4x\n",
+ crq->bRequest,
+ crq->wValue,
+ crq->wIndex);
+
+ retval = udc->driver->setup(&udc->gadget, crq);
+ }
+
+ return retval;
+
+stall:
+ debug("udc: %s: Invalid setup request: %02x.%02x v%04x i%04x l%d, "
+ "halting endpoint...\n",
+ ep->ep.name, crq->bRequestType, crq->bRequest,
+ le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex),
+ le16_to_cpu(crq->wLength));
+ set_protocol_stall(udc, ep);
+ return -1;
+}
+
+static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+ struct usba_request *req;
+ u32 epstatus;
+ u32 epctrl;
+
+restart:
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+
+ debug("%s [%d]: s/%08x c/%08x\n",
+ ep->ep.name, ep->state, epstatus, epctrl);
+
+ req = NULL;
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct usba_request, queue);
+
+ if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+ debug("usba_control_irq : USBA_TX_PK_RDY\n");
+ if (req->submitted)
+ next_fifo_transaction(ep, req);
+ else
+ submit_request(ep, req);
+
+ if (req->last_transaction) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+ }
+ goto restart;
+ }
+ if ((epstatus & epctrl) & USBA_TX_COMPLETE) {
+ debug("usba_control_irq : USBA_TX_COMPLETE\n");
+ usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE);
+
+ switch (ep->state) {
+ case DATA_STAGE_IN:
+ debug("USBA : DATA Stage in\n");
+ usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = STATUS_STAGE_OUT;
+ break;
+ case STATUS_STAGE_ADDR:
+ debug("USBA : Status stage addr\n");
+ /* Activate our new address */
+ usba_writel(udc, CTRL, (usba_readl(udc, CTRL)
+ | USBA_FADDR_EN));
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = WAIT_FOR_SETUP;
+ break;
+ case STATUS_STAGE_IN:
+ debug("USBA : Status stage in\n");
+ if (req) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, 0);
+ submit_next_request(ep);
+ }
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = WAIT_FOR_SETUP;
+ break;
+ case STATUS_STAGE_TEST:
+ debug("USBA : test mode not supported\n");
+ ep->state = WAIT_FOR_SETUP;
+ break;
+ default:
+ debug("udc: %s: TXRDY: Invalid endpoint state %d, "
+ "halting endpoint...\n",
+ ep->ep.name, ep->state);
+ set_protocol_stall(udc, ep);
+ break;
+ }
+
+ goto restart;
+ }
+ if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+ debug("USBA : USBA_RX_BK_RDY\n");
+ switch (ep->state) {
+ case STATUS_STAGE_OUT:
+ debug("%s : USBA :STATUS_STAGE_OUT\n", ep->ep.name);
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+
+ if (req) {
+ list_del_init(&req->queue);
+ debug("%s :USBA:request complete\n",
+ ep->ep.name);
+ request_complete(ep, req, 0);
+ }
+ ep->state = WAIT_FOR_SETUP;
+ break;
+
+ case DATA_STAGE_OUT:
+ debug("%s : DATA STAGE OUT\n", ep->ep.name);
+ receive_data(ep);
+ break;
+
+ default:
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ debug("udc: %s: RXRDY: Invalid endpoint state %d, "
+ "halting endpoint...\n",
+ ep->ep.name, ep->state);
+ set_protocol_stall(udc, ep);
+ break;
+ }
+
+ goto restart;
+ }
+ if (epstatus & USBA_RX_SETUP) {
+ debug("USBA : USBA_RX_SETUP\n");
+ union {
+ struct usb_ctrlrequest crq;
+ unsigned long data[2];
+ } crq;
+ unsigned int pkt_len;
+ int ret;
+
+ if (ep->state != WAIT_FOR_SETUP) {
+ /*
+ * Didn't expect a SETUP packet at this
+ * point. Clean up any pending requests (which
+ * may be successful).
+ */
+ int status = -EPROTO;
+
+ /*
+ * RXRDY and TXCOMP are dropped when SETUP
+ * packets arrive. Just pretend we received
+ * the status packet.
+ */
+ if (ep->state == STATUS_STAGE_OUT
+ || ep->state == STATUS_STAGE_IN) {
+ debug("DROPPING DATA %s\n", ep->ep.name);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ status = 0;
+ }
+
+ if (req) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, status);
+ }
+ }
+
+ pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+ debug("Packet length: %u\n", pkt_len);
+ if (pkt_len != sizeof(crq)) {
+ debug("udc: Invalid packet length %u "
+ "(expected %zu)\n", pkt_len, sizeof(crq));
+ set_protocol_stall(udc, ep);
+ return;
+ }
+
+ debug("Copying ctrl request from 0x%p:\n", ep->fifo);
+ memcpy_fromio(crq.data, ep->fifo, sizeof(crq));
+
+ /* Free up one bank in the FIFO so that we can
+ * generate or receive a reply right away. */
+ usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP);
+
+ if (crq.crq.bRequestType & USB_DIR_IN) {
+ /*
+ * The USB 2.0 spec states that "if wLength is
+ * zero, there is no data transfer phase."
+ * However, testusb #14 seems to actually
+ * expect a data phase even if wLength = 0...
+ */
+ ep->state = DATA_STAGE_IN;
+ } else {
+ if (crq.crq.wLength != cpu_to_le16(0))
+ ep->state = DATA_STAGE_OUT;
+ else
+ ep->state = STATUS_STAGE_IN;
+ }
+
+ ret = -1;
+ if (ep->index == 0)
+ ret = handle_ep0_setup(udc, ep, &crq.crq);
+ else {
+ debug("driver setup %d %d\n",
+ crq.crq.wIndex, crq.crq.wValue);
+ ret = udc->driver->setup(&udc->gadget, &crq.crq);
+ }
+
+ if (ret < 0) {
+ /* Let the host know that we failed */
+ set_protocol_stall(udc, ep);
+ }
+ }
+}
+
+static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+ struct usba_request *req;
+ u32 epstatus;
+ u32 epctrl;
+
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+
+ while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+ debug("%s: TX PK ready\n", ep->ep.name);
+
+ if (list_empty(&ep->queue)) {
+ debug("ep_irq: queue empty\n");
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ return;
+ }
+
+ req = list_entry(ep->queue.next, struct usba_request, queue);
+
+ if (req->submitted) {
+ debug("TX next trans %s\n", ep->ep.name);
+ next_fifo_transaction(ep, req);
+ } else {
+ debug("TX submit %s\n", ep->ep.name);
+ submit_request(ep, req);
+ }
+ if (req->last_transaction) {
+ debug("last transaction %s\n", ep->ep.name);
+ list_del_init(&req->queue);
+ submit_next_request(ep);
+ request_complete(ep, req, 0);
+ }
+
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+ }
+ debug("%s status = %lx\n", ep->ep.name, epstatus);
+ if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+ debug("%s RX data ready\n", ep->ep.name);
+ receive_data(ep);
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ }
+}
+
+int usb_gadget_handle_interrupts(void)
+{
+ struct usba_udc *udc = &the_udc;
+ u32 status;
+ u32 ep_status;
+
+ status = usba_readl(udc, INT_STA);
+ debug("irq, status=%#08x\n", status);
+
+ if (status & USBA_DET_SUSPEND) {
+ debug("USBA : USBA_DET_SUSPEND\n");
+ usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver && udc->driver->suspend) {
+ udc->driver->suspend(&udc->gadget);
+ }
+ }
+
+ if (status & USBA_WAKE_UP) {
+ debug("USBA : USBA_WAKE_UP\n");
+ usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+ }
+
+ if (status & USBA_END_OF_RESUME) {
+ debug("USBA : USBA_END_OF_RESUME\n");
+ usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver && udc->driver->resume) {
+ udc->driver->resume(&udc->gadget);
+ }
+ }
+
+ ep_status = USBA_BFEXT(EPT_INT, status);
+ if (ep_status) {
+ int i;
+
+ for (i = 0; i < USBA_NR_ENDPOINTS; i++)
+ if (ep_status & (1 << i)) {
+ if (ep_is_control(&udc->ep[i])) {
+ debug("EP %d = control\n", i);
+ usba_control_irq(udc, &udc->ep[i]);
+ } else {
+ debug("EP = %d\n", i);
+ usba_ep_irq(udc, &udc->ep[i]);
+ }
+ }
+ }
+
+ if (status & USBA_END_OF_RESET) {
+ struct usba_ep *ep0;
+ debug("USBA : USBA_END_OF_RESET\n");
+ usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+ reset_all_endpoints(udc);
+
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver->disconnect) {
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->driver->disconnect(&udc->gadget);
+ }
+
+ if (status & USBA_HIGH_SPEED) {
+ debug("USBA : High speed\n");
+ udc->gadget.speed = USB_SPEED_HIGH;
+ } else {
+ debug("USBA : Full speed\n");
+ udc->gadget.speed = USB_SPEED_FULL;
+ }
+
+ ep0 = &udc->ep[0];
+ ep0->desc = &usba_ep0_desc;
+ ep0->state = WAIT_FOR_SETUP;
+ usba_ep_writel(ep0, CFG,
+ (USBA_BF(EPT_SIZE, EP0_EPT_SIZE)
+ | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL)
+ | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
+ usba_ep_writel(ep0, CTL_ENB,
+ USBA_EPT_ENABLE | USBA_RX_SETUP);
+ usba_writel(udc, INT_ENB,
+ (usba_readl(udc, INT_ENB)
+ | USBA_BF(EPT_INT, 1)
+ | USBA_DET_SUSPEND
+ | USBA_END_OF_RESUME));
+
+ /*
+ * Unclear why we hit this irregularly, e.g. in usbtest,
+ * but it's clearly harmless...
+ */
+ if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) {
+ ERR("ODD: EP0 configuration is invalid!\n");
+ debug("ERROR : ep0 config invalid\n");
+ }
+ debug("USBA : USBA_END_OF_RESET - finished\n");
+ }
+ return IRQ_HANDLED;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usba_udc *udc = &the_udc;
+
+ if (!driver)
+ return -ENODEV;
+ if (driver != udc->driver || !driver->unbind)
+ return -EINVAL;
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ reset_all_endpoints(udc);
+
+ usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+
+ if (udc->driver->disconnect)
+ udc->driver->disconnect(&udc->gadget);
+
+ driver->unbind(&udc->gadget);
+ udc->driver = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct usba_udc *udc = &the_udc;
+ int retval = -1;
+
+ debug("USBA : registering driver\n");
+
+ if (udc->driver) {
+ debug("UDC already has a gadget driver\n");
+ return -EBUSY;
+ }
+
+ udc->driver = driver;
+
+ if (udc->driver)
+ retval = driver->bind(&udc->gadget);
+
+ if (retval) {
+ debug("driver->bind() returned %d\n", retval);
+ udc->driver = NULL;
+ return retval;
+ }
+
+ debug("Enabling controller\n");
+ usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+ usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+
+ debug("USBA register finished\n");
+ return 0;
+}
+
+/* reinit == restore inital software state */
+static void udc_reinit(struct usba_udc *udc)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+ for (i = 0; i < USBA_NR_ENDPOINTS; i++) {
+ struct usba_ep *ep = &udc->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->desc = NULL;
+ ep->stopped = 0;
+ ep->fifo_bank = 0;
+ ep->ep.maxpacket = ep->maxpacket;
+ ep->ep_regs = (void __iomem *) udc->regs + USBA_EPT_BASE(i);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+int usba_udc_probe(struct platform_data *pdata)
+{
+ struct usba_udc *udc;
+ int i;
+ /* needed to disable DMA */
+ at91_pmc_t *pmc = (at91_pmc_t *)AT91_PMC_BASE;
+ u32 regvalue;
+
+ udc = &the_udc;
+ udc->regs = (unsigned *) AT91SAM9G45_BASE_UDPHS;
+ udc->fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO;
+
+ usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+
+ debug("USBA : Probing USB controller\n");
+ writel(readl(&pmc->pcer) | (1 << AT91SAM9G45_ID_UDPHS), &pmc->pcer);
+ writel(readl(&pmc->uckr) |
+ AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr);
+
+ debug("USBA : Enable controller\n");
+ usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+ usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+
+ init_requests();
+ udc_reinit(udc);
+
+ the_udc.gadget.ep0 = &udc->ep[0].ep;
+
+ udc->ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0);
+ udc->ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0);
+ udc->ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0);
+ udc->ep[0].ep.ops = &usba_ep_ops;
+ udc->ep[0].ep.name = the_udc.ep[0].ep.name;
+ udc->ep[0].ep.maxpacket = 64;
+ udc->ep[0].udc = &the_udc;
+ udc->ep[0].fifo_size = 64;
+ udc->ep[0].nr_banks = the_udc.ep[0].nr_banks;
+ udc->ep[0].index = 0;
+ udc->ep[0].is_pingpong = 0;
+ udc->ep[0].can_isoc = 0;
+ udc->ep[0].can_dma = 0;
+
+ for (i = 1; i < USBA_NR_ENDPOINTS; i++) {
+ struct usba_ep *ep = &udc->ep[i];
+
+ ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
+ ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
+ ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
+ ep->ep.ops = &usba_ep_ops;
+ ep->ep.name = the_udc.ep[i].ep.name;
+ ep->ep.maxpacket = 1024;
+ ep->udc = &the_udc;
+ ep->fifo_size = 1024;
+ ep->index = i;
+ if ((i == 1) || (i == 2)) {
+ ep->nr_banks = 1;
+ ep->is_pingpong = 0;
+ } else {
+ ep->nr_banks = 1;
+ ep->is_pingpong = 0;
+ }
+ ep->can_isoc = 0;
+ ep->can_dma = 0;
+ }
+
+ /* This code disables DMA : see g45 spec */
+ regvalue = usba_readl(udc, CTRL);
+ usba_writel(udc, CTRL, (regvalue & ~AT91C_UDPHS_EN_UDPHS));
+ usba_writel(udc, CTRL, (regvalue | AT91C_UDPHS_EN_UDPHS));
+
+ regvalue = usba_readl(udc, CTRL);
+ debug("UDPHS_CTRL after reset 0x%lx\n", regvalue);
+ for (i = 1; i <= (((AT91SAM9G45_BASE_UDPHS + UDPHS_IPFEATURES) &
+ AT91C_UDPHS_DMA_CHANNEL_NBR) >> 4); i++) {
+ debug("UDPHS ep %d disable DMA\n", i);
+ /* DMA stop channel command */
+ __raw_writel(0x00,
+ AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+ /* Disable endpoint */
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ 0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+ __raw_writel((regvalue | 0XFFFFFFFF),
+ AT91SAM9G45_BASE_UDPHS +
+ 0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+ /* Reset endpoint config */
+ __raw_writel(0x00,
+ AT91SAM9G45_BASE_UDPHS +
+ 0x100 + (0x20*i) + UDPHS_EPTCFG);
+ /* Reset DMA channel (Buff count and Control field) */
+ __raw_writel(0x02,
+ AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+ /* DMA stop channel command */
+ __raw_writel(0x00,
+ AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+ /* Clear DMA channel status (read register to clear it) */
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+ __raw_writel(regvalue,
+ AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ 0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ 0x100 + (0x20*i) + UDPHS_EPTCFG);
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+ (UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+ regvalue = readl(AT91SAM9G45_BASE_UDPHS + 0xF0);
+ }
+ debug("USBA : Probing finished\n");
+ return 0;
+}
+
+MODULE_DESCRIPTION("Atmel usba udc driver");
+MODULE_AUTHOR("Marcel Janssen");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel_usba_udc");
+
diff --git a/include/usb/atmel_usba_udc.h b/include/usb/atmel_usba_udc.h
new file mode 100644
index 0000000..b165003
--- /dev/null
+++ b/include/usb/atmel_usba_udc.h
@@ -0,0 +1,398 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2005-2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
+#define __LINUX_USB_GADGET_USBA_UDC_H__
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/list.h>
+
+#define __iomem
+
+/* USB register offsets */
+#define USBA_CTRL 0x0000
+#define USBA_FNUM 0x0004
+#define USBA_INT_ENB 0x0010
+#define USBA_INT_STA 0x0014
+#define USBA_INT_CLR 0x0018
+#define USBA_EPT_RST 0x001c
+#define USBA_TST 0x00e0
+
+/* USB endpoint register offsets */
+#define USBA_EPT_CFG 0x0000
+#define USBA_EPT_CTL_ENB 0x0004
+#define USBA_EPT_CTL_DIS 0x0008
+#define USBA_EPT_CTL 0x000c
+#define USBA_EPT_SET_STA 0x0014
+#define USBA_EPT_CLR_STA 0x0018
+#define USBA_EPT_STA 0x001c
+
+/* USB DMA register offsets */
+#define USBA_DMA_NXT_DSC 0x0000
+#define USBA_DMA_ADDRESS 0x0004
+#define USBA_DMA_CONTROL 0x0008
+#define USBA_DMA_STATUS 0x000c
+
+/* Bitfields in CTRL */
+#define USBA_DEV_ADDR_OFFSET 0
+#define USBA_DEV_ADDR_SIZE 7
+#define USBA_FADDR_EN (1 << 7)
+#define USBA_EN_USBA (1 << 8)
+#define USBA_DETACH (1 << 9)
+#define USBA_REMOTE_WAKE_UP (1 << 10)
+#define USBA_PULLD_DIS (1 << 11)
+
+#if defined(CONFIG_AVR32)
+#define USBA_ENABLE_MASK USBA_EN_USBA
+#define USBA_DISABLE_MASK 0
+#elif defined(CONFIG_ARCH_AT91)
+#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS)
+#define USBA_DISABLE_MASK USBA_DETACH
+#endif /* CONFIG_ARCH_AT91 */
+
+/* Bitfields in FNUM */
+#define USBA_MICRO_FRAME_NUM_OFFSET 0
+#define USBA_MICRO_FRAME_NUM_SIZE 3
+#define USBA_FRAME_NUMBER_OFFSET 3
+#define USBA_FRAME_NUMBER_SIZE 11
+#define USBA_FRAME_NUM_ERROR (1 << 31)
+
+/* Bitfields in INT_ENB/INT_STA/INT_CLR */
+#define USBA_HIGH_SPEED (1 << 0)
+#define USBA_DET_SUSPEND (1 << 1)
+#define USBA_MICRO_SOF (1 << 2)
+#define USBA_SOF (1 << 3)
+#define USBA_END_OF_RESET (1 << 4)
+#define USBA_WAKE_UP (1 << 5)
+#define USBA_END_OF_RESUME (1 << 6)
+#define USBA_UPSTREAM_RESUME (1 << 7)
+#define USBA_EPT_INT_OFFSET 8
+#define USBA_EPT_INT_SIZE 16
+#define USBA_DMA_INT_OFFSET 24
+#define USBA_DMA_INT_SIZE 8
+
+/* Bitfields in EPT_RST */
+#define USBA_RST_OFFSET 0
+#define USBA_RST_SIZE 16
+
+/* Bitfields in USBA_TST */
+#define USBA_SPEED_CFG_OFFSET 0
+#define USBA_SPEED_CFG_SIZE 2
+#define USBA_TST_J_MODE (1 << 2)
+#define USBA_TST_K_MODE (1 << 3)
+#define USBA_TST_PKT_MODE (1 << 4)
+#define USBA_OPMODE2 (1 << 5)
+
+/* Bitfields in EPT_CFG */
+#define USBA_EPT_SIZE_OFFSET 0
+#define USBA_EPT_SIZE_SIZE 3
+#define USBA_EPT_DIR_IN (1 << 3)
+#define USBA_EPT_TYPE_OFFSET 4
+#define USBA_EPT_TYPE_SIZE 2
+#define USBA_BK_NUMBER_OFFSET 6
+#define USBA_BK_NUMBER_SIZE 2
+#define USBA_NB_TRANS_OFFSET 8
+#define USBA_NB_TRANS_SIZE 2
+#define USBA_EPT_MAPPED (1 << 31)
+
+/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
+#define USBA_EPT_ENABLE (1 << 0)
+#define USBA_AUTO_VALID (1 << 1)
+#define USBA_INTDIS_DMA (1 << 3)
+#define USBA_NYET_DIS (1 << 4)
+#define USBA_DATAX_RX (1 << 6)
+#define USBA_MDATA_RX (1 << 7)
+/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
+#define USBA_BUSY_BANK_IE (1 << 18)
+
+/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
+#define USBA_FORCE_STALL (1 << 5)
+#define USBA_TOGGLE_CLR (1 << 6)
+#define USBA_TOGGLE_SEQ_OFFSET 6
+#define USBA_TOGGLE_SEQ_SIZE 2
+#define USBA_ERR_OVFLW (1 << 8)
+#define USBA_RX_BK_RDY (1 << 9)
+#define USBA_KILL_BANK (1 << 9)
+#define USBA_TX_COMPLETE (1 << 10)
+#define USBA_TX_PK_RDY (1 << 11)
+#define USBA_ISO_ERR_TRANS (1 << 11)
+#define USBA_RX_SETUP (1 << 12)
+#define USBA_ISO_ERR_FLOW (1 << 12)
+#define USBA_STALL_SENT (1 << 13)
+#define USBA_ISO_ERR_CRC (1 << 13)
+#define USBA_ISO_ERR_NBTRANS (1 << 13)
+#define USBA_NAK_IN (1 << 14)
+#define USBA_ISO_ERR_FLUSH (1 << 14)
+#define USBA_NAK_OUT (1 << 15)
+#define USBA_CURRENT_BANK_OFFSET 16
+#define USBA_CURRENT_BANK_SIZE 2
+#define USBA_BUSY_BANKS_OFFSET 18
+#define USBA_BUSY_BANKS_SIZE 2
+#define USBA_BYTE_COUNT_OFFSET 20
+#define USBA_BYTE_COUNT_SIZE 11
+#define USBA_SHORT_PACKET (1 << 31)
+
+/* Bitfields in DMA_CONTROL */
+#define USBA_DMA_CH_EN (1 << 0)
+#define USBA_DMA_LINK (1 << 1)
+#define USBA_DMA_END_TR_EN (1 << 2)
+#define USBA_DMA_END_BUF_EN (1 << 3)
+#define USBA_DMA_END_TR_IE (1 << 4)
+#define USBA_DMA_END_BUF_IE (1 << 5)
+#define USBA_DMA_DESC_LOAD_IE (1 << 6)
+#define USBA_DMA_BURST_LOCK (1 << 7)
+#define USBA_DMA_BUF_LEN_OFFSET 16
+#define USBA_DMA_BUF_LEN_SIZE 16
+
+/* Bitfields in DMA_STATUS */
+#define USBA_DMA_CH_ACTIVE (1 << 1)
+#define USBA_DMA_END_TR_ST (1 << 4)
+#define USBA_DMA_END_BUF_ST (1 << 5)
+#define USBA_DMA_DESC_LOAD_ST (1 << 6)
+
+/* Constants for SPEED_CFG */
+#define USBA_SPEED_CFG_NORMAL 0
+#define USBA_SPEED_CFG_FORCE_HIGH 2
+#define USBA_SPEED_CFG_FORCE_FULL 3
+
+/* Constants for EPT_SIZE */
+#define USBA_EPT_SIZE_8 0
+#define USBA_EPT_SIZE_16 1
+#define USBA_EPT_SIZE_32 2
+#define USBA_EPT_SIZE_64 3
+#define USBA_EPT_SIZE_128 4
+#define USBA_EPT_SIZE_256 5
+#define USBA_EPT_SIZE_512 6
+#define USBA_EPT_SIZE_1024 7
+
+/* Constants for EPT_TYPE */
+#define USBA_EPT_TYPE_CONTROL 0
+#define USBA_EPT_TYPE_ISO 1
+#define USBA_EPT_TYPE_BULK 2
+#define USBA_EPT_TYPE_INT 3
+
+/* Constants for BK_NUMBER */
+#define USBA_BK_NUMBER_ZERO 0
+#define USBA_BK_NUMBER_ONE 1
+#define USBA_BK_NUMBER_DOUBLE 2
+#define USBA_BK_NUMBER_TRIPLE 3
+
+/* Bit manipulation macros */
+#define USBA_BF(name, value) \
+ (((value) & ((1 << USBA_##name##_SIZE) - 1)) \
+ << USBA_##name##_OFFSET)
+#define USBA_BFEXT(name, value) \
+ (((value) >> USBA_##name##_OFFSET) \
+ & ((1 << USBA_##name##_SIZE) - 1))
+#define USBA_BFINS(name, value, old) \
+ (((old) & ~(((1 << USBA_##name##_SIZE) - 1) \
+ << USBA_##name##_OFFSET)) \
+ | USBA_BF(name, value))
+
+/* Register access macros */
+#define usba_readl(udc, reg) \
+ __raw_readl((udc)->regs + USBA_##reg)
+#define usba_writel(udc, reg, value) \
+ __raw_writel((value), (udc)->regs + USBA_##reg)
+#define usba_ep_readl(ep, reg) \
+ __raw_readl((ep)->ep_regs + USBA_EPT_##reg)
+#define usba_ep_writel(ep, reg, value) \
+ __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
+#define usba_dma_readl(ep, reg) \
+ __raw_readl((ep)->dma_regs + USBA_DMA_##reg)
+#define usba_dma_writel(ep, reg, value) \
+ __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
+
+/* Calculate base address for a given endpoint or DMA controller */
+#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
+#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10)
+#define USBA_FIFO_BASE(x) ((x) << 16)
+
+/* Synth parameters */
+#define USBA_NR_ENDPOINTS 7
+
+#define EP0_FIFO_SIZE 64
+#define EP0_EPT_SIZE USBA_EPT_SIZE_64
+#define EP0_NR_BANKS 1
+
+/*
+ * REVISIT: Try to eliminate this value. Can we rely on req->mapped to
+ * provide this information?
+ */
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+#define FIFO_IOMEM_ID 0
+#define CTRL_IOMEM_ID 1
+
+#define DBG_ERR 0x0001 /* report all error returns */
+#define DBG_HW 0x0002 /* debug hardware initialization */
+#define DBG_GADGET 0x0004 /* calls to/from gadget driver */
+#define DBG_INT 0x0008 /* interrupts */
+#define DBG_BUS 0x0010 /* report changes in bus state */
+#define DBG_QUEUE 0x0020 /* debug request queue processing */
+#define DBG_FIFO 0x0040 /* debug FIFO contents */
+#define DBG_DMA 0x0080 /* debug DMA handling */
+#define DBG_REQ 0x0100 /* print out queued request length */
+#define DBG_ALL 0xffff
+#define DBG_NONE 0x0000
+
+#define DEBUG_LEVEL (DBG_ERR)
+
+enum usba_ctrl_state {
+ WAIT_FOR_SETUP,
+ DATA_STAGE_IN,
+ DATA_STAGE_OUT,
+ STATUS_STAGE_IN,
+ STATUS_STAGE_OUT,
+ STATUS_STAGE_ADDR,
+ STATUS_STAGE_TEST,
+};
+
+struct usba_ep {
+ int state;
+ struct usb_ep ep;
+ struct list_head queue;
+ struct usba_udc *udc;
+ void __iomem *fifo;
+ void __iomem *dma_regs;
+ void __iomem *ep_regs;
+
+ unsigned maxpacket:16;
+ u8 int_mask;
+ unsigned is_pingpong:1;
+ u8 index;
+ u16 fifo_size;
+ u8 nr_banks;
+ unsigned stopped:1;
+ unsigned int can_dma:1;
+ unsigned int can_isoc:1;
+ unsigned is_in:1;
+ unsigned is_isoc:1;
+ unsigned fifo_bank:1;
+
+ const struct usb_endpoint_descriptor *desc;
+};
+
+struct usba_request {
+ struct usb_request req;
+ struct list_head queue;
+ unsigned int last_transaction:1;
+ unsigned int submitted:1;
+ unsigned in_use;
+};
+
+/* USB Device */
+struct usba_udc_data {
+ u8 vbus_pin; /* high == host powering us */
+ u8 pullup_pin; /* high == D+ pulled up */
+};
+
+struct platform_data {
+ struct usba_udc_data board;
+ unsigned udc_clk;
+};
+
+/*
+ * driver is non-SMP, and just blocks IRQs whenever it needs
+ * access protection for chip registers or driver state
+ */
+
+struct usba_udc {
+ void __iomem *regs;
+ void __iomem *fifo;
+ struct usb_gadget gadget;
+ struct usba_ep ep[USBA_NR_ENDPOINTS];
+ struct usb_gadget_driver *driver;
+ struct usba_udc_data board;
+ unsigned vbus:1;
+ int irq;
+ int vbus_pin;
+ struct clk *pclk;
+ struct clk *hclk;
+ u16 devstatus;
+ int vbus_prev;
+};
+
+static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct usba_ep, ep);
+}
+
+static inline struct usba_request *to_usba_req(struct usb_request *req)
+{
+ return container_of(req, struct usba_request, req);
+}
+
+static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct usba_udc, gadget);
+}
+
+int usba_udc_probe(struct platform_data *pdata);
+
+#define ep_is_control(ep) ((ep)->index == 0)
+#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE)
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG (stuff...) printf("udc: " stuff)
+#else
+#define DBG (stuff...) do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG(x...) printf(x)
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
+#ifdef PACKET_TRACE
+# define PACKET VDBG
+#else
+# define PACKET(stuff...) do {} while (0)
+#endif
+
+#define ERR(stuff...) printf("ERR udc: " stuff)
+#define WARN(stuff...) printf("WARNING udc: " stuff)
+#define INFO(stuff...) printf("INFO udc: " stuff)
+
+/* following defines come form At91Bootstrap.
+ * They're needed for disabling DMA for the USB device controller
+ */
+#define UDPHS_CTRL (0)
+#define AT91C_UDPHS_EN_UDPHS (0x1 << 8)
+#define UDPHS_IPFEATURES (248)
+#define AT91C_UDPHS_DMA_CHANNEL_NBR (0x7 << 4)
+#define UDPHS_DMACONTROL (8)
+#define UDPHS_EPT (256)
+#define AT91C_BASE_UDPHS_EPT_0 (0xFFF78100)
+#define UDPHS_EPTCTLDIS (8)
+#define UDPHS_EPTCFG (0)
+#define UDPHS_EPTCTL (12)
+#define UDPHS_DMA (768)
+#define AT91C_BASE_UDPHS_DMA_1 (0xFFF78310)
+#define AT91C_BASE_UDPHS_DMA_1 (0xFFF78310)
+#define AT91C_BASE_UDPHS_DMA_2 (0xFFF78320)
+#define AT91C_BASE_UDPHS_DMA_3 (0xFFF78330)
+#define AT91C_BASE_UDPHS_DMA_4 (0xFFF78340)
+#define AT91C_BASE_UDPHS_DMA_5 (0xFFF78350)
+#define AT91C_BASE_UDPHS_DMA_6 (0xFFF78360)
+#define UDPHS_DMASTATUS (12)
+#define AT91C_BASE_UDPHS_EPTFIFO (0x00600000)
+#define AT91C_UDPHS_EPTFIFO_READEPT3 (0x00630000)
+#define AT91C_UDPHS_EPTFIFO_READEPT5 (0x00650000)
+#define AT91C_UDPHS_EPTFIFO_READEPT1 (0x00610000)
+#define AT91C_UDPHS_EPTFIFO_READEPT0 (0x00600000)
+#define AT91C_UDPHS_EPTFIFO_READEPT6 (0x00660000)
+#define AT91C_UDPHS_EPTFIFO_READEPT2 (0x00620000)
+#define AT91C_UDPHS_EPTFIFO_READEPT4 (0x00640000)
+
+#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
--
1.7.3.4
More information about the U-Boot
mailing list