From patchwork Sat Feb 12 15:07:06 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcel Janssen X-Patchwork-Id: 82922 X-Patchwork-Delegate: linux@bohmer.net Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id E5857B7125 for ; Sun, 13 Feb 2011 02:07:32 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id A6559281DE; Sat, 12 Feb 2011 16:07:29 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3Gx9LQalusSG; Sat, 12 Feb 2011 16:07:29 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 753DE281CB; Sat, 12 Feb 2011 16:07:22 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 29ACD281B1 for ; Sat, 12 Feb 2011 16:07:19 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 5vnSXRn5krub for ; Sat, 12 Feb 2011 16:07:16 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from smtpq3.gn.mail.iss.as9143.net (smtpq3.gn.mail.iss.as9143.net [212.54.34.166]) by theia.denx.de (Postfix) with ESMTP id 049CC281A3 for ; Sat, 12 Feb 2011 16:07:14 +0100 (CET) Received: from [212.54.34.140] (helo=smtp9.gn.mail.iss.as9143.net) by smtpq3.gn.mail.iss.as9143.net with esmtp (Exim 4.71) (envelope-from ) id 1PoH45-0002vq-V3; Sat, 12 Feb 2011 16:07:13 +0100 Received: from 5419b2be.cm-5-2c.dynamic.ziggo.nl ([84.25.178.190] helo=localhost.localdomain) by smtp9.gn.mail.iss.as9143.net with esmtp (Exim 4.71) (envelope-from ) id 1PoH40-0008RM-PV; Sat, 12 Feb 2011 16:07:08 +0100 From: Marcel Janssen To: u-boot@lists.denx.de Date: Sat, 12 Feb 2011 16:07:06 +0100 Message-Id: <1297523226-19696-1-git-send-email-korgull@home.nl> X-Mailer: git-send-email 1.7.3.4 To: u-boot@lists.denx.de X-ZiggoSMTP-MailScanner-Information: Please contact the ISP for more information X-ZiggoSMTP-MailScanner-ID: 1PoH40-0008RM-PV X-ZiggoSMTP-MailScanner: Found to be clean X-ZiggoSMTP-MailScanner-SpamCheck: geen spam, SpamAssassin (niet cached, score=3.453, vereist 5, ALL_TRUSTED -1.00, BAYES_00 -1.90, RDNS_DYNAMIC 0.98, SARE_OBFU_PART_ONA 0.90, SARE_OBFU_VALUE 0.53, TO_NO_BRKTS_DIRECT 3.48, TW_CF 0.08, TW_CR 0.08, TW_EG 0.08, TW_GF 0.08, TW_VB 0.08, TW_XF 0.08) X-ZiggoSMTP-MailScanner-SpamScore: sss X-ZiggoSMTP-MailScanner-From: korgull@home.nl Subject: [U-Boot] [PATCH 1/4] Add Atmel USBA UDC X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Marcel Signed-off-by: Marcel --- drivers/usb/gadget/atmel_usba_udc.c | 1586 +++++++++++++++++++++++++++++++++++ include/usb/atmel_usba_udc.h | 469 +++++++++++ 2 files changed, 2055 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/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c new file mode 100644 index 0000000..ff9c469 --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -0,0 +1,1586 @@ +/* + * Driver for the Atmel USBA high speed USB device controller + * Copyright (C) 2011 Marcel Janssen + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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); + //__raw_writesb(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 void +request_complete_list(struct usba_ep *ep, struct list_head *list, int status) +{ + struct usba_request *req, *tmp_req; + debug("Request complete list\n"); + list_for_each_entry_safe(req, tmp_req, list, queue) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } +} +*/ + +static int +usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct usba_ep *ep;//to_usba_ep(_ep); + struct usba_udc *udc; + unsigned long ept_cfg, maxpacket; //regvalue; + unsigned int nr_trans; + //int i; + + 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( (ep->index == 1) || (ep->index == 2) || (ep->index == 3) ) + +{ + i = ep->index; + // 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 the register for 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); +} +*/ + 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);//USBA_BK_NUMBER_ONE 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);//USBA_BK_NUMBER_DOUBLE); + 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);//USBA_BK_NUMBER_DOUBLE); + 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;//to_usba_ep(_ep); + struct usba_udc *udc; + //LIST_HEAD(req_list); + + 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); + //if (!ep->desc) { + // /* REVISIT because this driver disables endpoints in + // * reset_all_endpoints() before calling disconnect(), + // * most gadget drivers would trigger this non-error ... + // */ + // debug("ep%d unknown speed\n",ep->index); + // if (udc->gadget.speed != USB_SPEED_UNKNOWN) + // return -EINVAL; + //} + ep->desc = NULL; + + //list_splice_init(&ep->queue, &req_list); + + 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)); + + //request_complete_list(ep, &req_list, -ESHUTDOWN); + + 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 : did you initialize the gadget well ?\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); + //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);//to_usba_ep(_ep); + struct usba_request *req = container_of(_req, struct usba_request, req);//to_usba_req(_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. + */ + //list_del_init(&req->queue); + + /* 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);//to_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, + //.ep_list = LIST_HEAD_INIT(the_udc.gadget.ep_list), + .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);//to_usba_ep(udc->gadget.ep0); + list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { + list_del_init(&req->queue); + request_complete(ep, req, -ECONNRESET); + } + + //NOTE: normally, the next call to the gadget driver is in + // charge of disabling endpoints... usually disconnect(). + // The exception would be entering a high speed test mode. + // + // FIXME remove this code ... and retest thoroughly. + // + 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);//to_usba_ep(udc->gadget.ep0); + + 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; +} + +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 :call 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); + + /* printk(KERN_DEBUG "setup: %d: %02x.%02x\n", + ep->state, crq.crq.bRequestType, + crq.crq.bRequest); */ + + 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); + + //debug("%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); + + 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 (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->setup) { + DBG("bad parameter.\n"); + return -EINVAL; + }*/ + + 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); + // todo: anyway allow registering the driver, maybe a later + // bringup may succeed. postpone pullup the eth_init? + // + + //check vbus + //value = at91_get_gpio_value(udc->board.vbus_pin); + 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); + // initialiser une queue par endpoint + INIT_LIST_HEAD(&ep->queue); + } +} + +int usba_udc_probe(struct platform_data *pdata) +{ + //struct usba_platform_data *pdata = pdev->dev.platform_data; + //struct resource *regs, *fifo; + //struct clk *pclk, *hclk; + struct usba_udc *udc; + //struct usba_request *req; + int i; + // needed to disable DMA + at91_pmc_t *pmc = (at91_pmc_t *)AT91_PMC_BASE; + u32 regvalue;//, regvalue2; + + 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"); + //at91_pmc_t *pmc = (at91_pmc_t *)AT91_PMC_BASE; + 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;//the_udc.ep[0].ep.maxpacket; + udc->ep[0].udc = &the_udc; + udc->ep[0].fifo_size = 64;//the_udc.ep[0].fifo_size; + 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;//the_udc.ep[0].can_isoc; + udc->ep[0].can_dma = 0;//the_udc.ep[0].can_dma; + + 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);//udc->ep[0].fifo + udc->ep[0].fifo_size + ((i-1) * 0x10000);//0x10000);//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; + //INIT_LIST_HEAD(&ep->queue); + ep->fifo_size = 1024; + ep->index = i; + if( (i == 1) || (i==2) ) + { + ep->nr_banks = 1;//the_udc.ep[i].nr_banks; + ep->is_pingpong = 0; + } + else + { + ep->nr_banks = 1;//the_udc.ep[i].nr_banks; + ep->is_pingpong = 0; + } + ep->can_isoc = 0;//the_udc.ep[i].can_isoc; + ep->can_dma = 0;//the_udc.ep[i].can_dma; + + //list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + } + + + /* we don't use DMA : this is from the g45 spec + * + * // Reset IP UDPHS + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~AT91C_UDPHS_EN_UDPHS; + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_EN_UDPHS; + // With OR without DMA !!! + for( i=1; i<=((AT91C_BASE_UDPHS->UDPHS_IPFEATURES & + AT91C_UDPHS_DMA_CHANNEL_NBR)>>4); i++ ) { + // RESET endpoint canal DMA: + // DMA stop channel command + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; + // STOP command + // Disable endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLDIS |= 0XFFFFFFFF; + // Reset endpoint config + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLCFG = 0; + // Reset DMA channel (Buff count and Control field) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0x02; + // NON STOP command + // Reset DMA channel 0 (STOP) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; + // STOP command + // Clear DMA channel status (read the register for clear it) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS = + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS; + } + */ + + // This code disables DMA + // Reset IP UDPHS + 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 the register for 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); + debug("UDPHS_DMACONTROL = 0x%lx\n", regvalue); + regvalue = readl(AT91SAM9G45_BASE_UDPHS + 0x100 + (0x20*i) + UDPHS_EPTCTLDIS); + debug("UDPHS_EPTCTLDIS = 0x%lx\n", regvalue); + regvalue = readl(AT91SAM9G45_BASE_UDPHS + 0x100 + (0x20*i) + UDPHS_EPTCFG); + debug("UDPHS_EPTCFG = 0x%lx\n", regvalue); + regvalue = readl(AT91SAM9G45_BASE_UDPHS + (UDPHS_DMA+ 0x10*i) + UDPHS_DMASTATUS); + debug("UDPHS_DMASTATUS = 0x%lx\n", regvalue); + regvalue = readl(AT91SAM9G45_BASE_UDPHS + 0xF0); + debug("UDPHS_IPNAME1 = 0x%lx\n", regvalue); + } + + 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..1664c89 --- /dev/null +++ b/include/usb/atmel_usba_udc.h @@ -0,0 +1,469 @@ +/* + * 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 +#include +#include + +#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, +}; +/* + EP_STATE_IDLE, + EP_STATE_SETUP, + EP_STATE_IN_DATA, + EP_STATE_OUT_DATA, + EP_STATE_SET_ADDR_STATUS, + EP_STATE_RX_STATUS, + EP_STATE_TX_STATUS, + EP_STATE_HALT, +*/ + +/*struct usba_dma_desc { + dma_addr_t next; + dma_addr_t addr; + u32 ctrl; +}; +*/ + +struct usba_ep { + int state; + struct usb_ep ep; + struct list_head queue; + struct usba_udc *udc; + void __iomem *fifo; + //void __iomem *creg; + 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_ep { + int state; + //void __iomem *ep_regs; + void __iomem *dma_regs; + void __iomem *fifo; + struct usb_ep ep; + struct usba_udc *udc; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + + u16 fifo_size; + u8 nr_banks; + u8 index; + unsigned int can_dma:1; + unsigned int can_isoc:1; + unsigned int is_isoc:1; + unsigned int is_in:1; + +#ifdef CONFIG_USB_GADGET_DEBUG_FS + u32 last_dma_status; + struct dentry *debugfs_dir; + struct dentry *debugfs_queue; + struct dentry *debugfs_dma_status; + struct dentry *debugfs_state; +#endif +}; +*/ + +struct usba_request { + struct usb_request req; + struct list_head queue; + unsigned int last_transaction:1; + unsigned int submitted:1; + //u32 ctrl; + unsigned in_use; +}; + +/*struct usba_request { + struct usb_request req; + struct list_head queue; + + u32 ctrl; + + unsigned int submitted:1; + unsigned int last_transaction:1; + unsigned int using_dma:1; + unsigned int mapped:1; +}; +*/ + +/* 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; + /*struct clk *fclk;*/ +}; + +/* + * 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; + //void __iomem *udp_baseaddr; + struct usb_gadget gadget; + struct usba_ep ep[USBA_NR_ENDPOINTS]; + struct usb_gadget_driver *driver; + //struct platform_device *pdev; + struct usba_udc_data board; + unsigned vbus:1; + //unsigned enabled: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);//struct platform_device *pdev); + +#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) +//VDBG(x...) printf(__FUNCTION__ , 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) // UDPHS Control Register +#define AT91C_UDPHS_EN_UDPHS (0x1 << 8) // (UDPHS) UDPHS Enable +#define UDPHS_IPFEATURES (248) // UDPHS Features Register +#define AT91C_UDPHS_DMA_CHANNEL_NBR (0x7 << 4) // (UDPHS) Number of DMA Channels +#define UDPHS_DMACONTROL ( 8) // UDPHS DMA Channel Control Register +#define UDPHS_EPT (256) // UDPHS Endpoint struct +#define AT91C_BASE_UDPHS_EPT_0 (0xFFF78100) // (UDPHS_EPT_0) Base Address +#define UDPHS_EPTCTLDIS ( 8) // UDPHS Endpoint Control Disable Register +#define UDPHS_EPTCFG ( 0) // UDPHS Endpoint Config Register +#define UDPHS_EPTCTL (12) // UDPHS Endpoint Control Register +#define UDPHS_DMA (768) // UDPHS DMA channel struct (not use [0]) +#define AT91C_BASE_UDPHS_DMA_1 (0xFFF78310) +#define AT91C_BASE_UDPHS_DMA_1 (0xFFF78310) // (UDPHS_DMA_1) Base Address +#define AT91C_BASE_UDPHS_DMA_2 (0xFFF78320) // (UDPHS_DMA_2) Base Address +#define AT91C_BASE_UDPHS_DMA_3 (0xFFF78330) // (UDPHS_DMA_3) Base Address +#define AT91C_BASE_UDPHS_DMA_4 (0xFFF78340) // (UDPHS_DMA_4) Base Address +#define AT91C_BASE_UDPHS_DMA_5 (0xFFF78350) // (UDPHS_DMA_5) Base Address +#define AT91C_BASE_UDPHS_DMA_6 (0xFFF78360) // (UDPHS_DMA_6) Base Address +#define UDPHS_DMASTATUS (12) // UDPHS DMA Channel Status Register +#define AT91C_BASE_UDPHS_EPTFIFO (0x00600000) // (UDPHS_EPTFIFO) Base Address +// ========== Register definition for UDPHS_EPTFIFO peripheral ========== +#define AT91C_UDPHS_EPTFIFO_READEPT3 (0x00630000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 3 +#define AT91C_UDPHS_EPTFIFO_READEPT5 (0x00650000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 5 +#define AT91C_UDPHS_EPTFIFO_READEPT1 (0x00610000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 1 +#define AT91C_UDPHS_EPTFIFO_READEPT0 (0x00600000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 0 +#define AT91C_UDPHS_EPTFIFO_READEPT6 (0x00660000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 6 +#define AT91C_UDPHS_EPTFIFO_READEPT2 (0x00620000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 2 +#define AT91C_UDPHS_EPTFIFO_READEPT4 (0x00640000) // (UDPHS_EPTFIFO) FIFO Endpoint Data Register 4 + +#endif /* __LINUX_USB_GADGET_USBA_UDC_H */