--- linux-2.6.16.19/drivers/usb/core/hub.c 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/core/hub.c 2006-06-07 10:49:18.000000000 -0600 @@ -1266,6 +1266,7 @@ static inline void show_string(struct us #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" +static int __usb_suspend_device(struct usb_device *, int port1); #endif /** @@ -1369,8 +1370,6 @@ int usb_new_device(struct usb_device *ud * (Includes HNP test device.) */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - static int __usb_suspend_device(struct usb_device *, - int port1); err = __usb_suspend_device(udev, udev->bus->otg_port); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); --- linux-2.6.16.19/drivers/usb/gadget/arcotg_udc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/arcotg_udc.c 2006-06-08 21:32:10.000000000 -0600 @@ -0,0 +1,2958 @@ +/* + * driver/usb/gadget/arcotg_udc.c + * + * USB Device Controller Driver + * Driver for ARC OTG USB module in the i.MX31 platform, etc. + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Based on mpc-udc.c + * Author: Li Yang (leoli@freescale.com) + * Jiang Bo (Tanya.jiang@freescale.com) + * Based on bare board code of Dave Liu and Shlomi Gridish. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if 0 +#define DEBUG +#define VERBOSE +#define DUMP_QUEUES +#endif + +#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 "arcotg_udc.h" +#include +#include + +extern void gpio_usbotg_hs_active(void); +extern void gpio_usbotg_fs_active(void); + +static int timeout; + +#undef USB_TRACE + +#define DRIVER_DESC "ARC USBOTG Device Controller driver" +#define DRIVER_AUTHOR "Freescale Semiconductor" +#define DRIVER_VERSION "1 August 2005" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +static const char driver_name[] = "arc_udc"; +static const char driver_desc[] = DRIVER_DESC; + +volatile static struct usb_dr_device *usb_slave_regs = NULL; + +/* it is initialized in probe() */ +static struct arcotg_udc *udc_controller = NULL; + +/* ep_qh_base store the base address before 2K align */ +static struct ep_queue_head *ep_qh_base = NULL; + +/*ep name is important in gadget, it should obey the convention of ep_match()*/ +/* even numbered EPs are OUT or setup, odd are IN/INTERRUPT */ +static const char *const ep_name[] = { + "ep0-control", NULL, /* everyone has ep0 */ + /* 7 configurable endpoints */ + "ep1out", + "ep1in", + "ep2out", + "ep2in", + "ep3out", + "ep3in", + "ep4out", + "ep4in", + "ep5out", + "ep5in", + "ep6out", + "ep6in", + "ep7out", + "ep7in" +}; +static struct usb_endpoint_descriptor arcotg_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, +}; + +static int arcotg_udc_suspend(struct device *pdev, pm_message_t state); +static int arcotg_udc_resume(struct device *pdev); + +/******************************************************************** + * Internal Used Function +********************************************************************/ + +#ifdef DUMP_QUEUES +static void dump_ep_queue(struct arcotg_ep *ep) +{ + int ep_index = ep_index(ep) * 2 + ep_is_in(ep); + struct arcotg_req *req; + struct ep_td_struct *dtd; + + VDBG("ep=0x%p index=%d", ep, ep_index); + + if (list_empty(&ep->queue)) { + VDBG("empty"); + return; + } + + list_for_each_entry(req, &ep->queue, queue) { + VDBG(" req=0x%p dTD count=%d", req, req->dtd_count); + VDBG(" dTD head=0x%p tail=0x%p", req->head, req->tail); + + dtd = req->head; + /* printk("dtd=0x%p\n", dtd); */ + + while (dtd) { + consistent_sync(dtd, sizeof(struct ep_td_struct), + DMA_FROM_DEVICE); + + VDBG(" dTD 0x%p: active=%d " + "size_ioc_sts=0x%x", + dtd, + (dtd->size_ioc_sts & DTD_STATUS_ACTIVE) == + DTD_STATUS_ACTIVE, dtd->size_ioc_sts); + + if (le32_to_cpu(dtd->next_td_ptr) & DTD_NEXT_TERMINATE) + break; /* end of dTD list */ + + dtd = (struct ep_td_struct *) + phys_to_virt(le32_to_cpu(dtd->next_td_ptr) & + DTD_ADDR_MASK); + } + } +} +#else +static inline void dump_ep_queue(struct arcotg_ep *ep) +{ +} +#endif + +/*----------------------------------------------------------------- + * done() - retire a request; caller blocked irqs + * @status : when req->req.status is -EINPROGRESSS, it is input para + * else it will be a output parameter + * req->req.status : in ep_queue() it will be set as -EINPROGRESS + *--------------------------------------------------------------*/ +static void done(struct arcotg_ep *ep, struct arcotg_req *req, int status) +{ + struct arcotg_udc *udc = NULL; + unsigned char stopped = ep->stopped; + + udc = (struct arcotg_udc *)ep->udc; + /* the req->queue pointer is used by ep_queue() func, in which + * the request will be added into a udc_ep->queue 'd tail + * so here the req will be dropped from the ep->queue + */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + VDBG("req=0x%p mapped=%x", req, req->mapped); + + if (req->mapped) { + VDBG("calling dma_unmap_single(buf,%s) req=0x%p " + "a=0x%x len=%d", + ep_is_in(ep) ? "to_dvc" : "from_dvc", + req, req->req.dma, req->req.length); + + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + VDBG("req=0x%p set req.dma=0x%x", req, req->req.dma); + } else { + if ((req->req.length != 0) + && (req->req.dma != DMA_ADDR_INVALID)) { + VDBG("calling dma_sync_single_for_cpu(buf,%s) " + "req=0x%p dma=0x%x len=%d", + ep_is_in(ep) ? "to_dvc" : "from_dvc", req, + req->req.dma, req->req.length); + + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + } + } + + if (status && (status != -ESHUTDOWN)) { + VDBG("complete %s req 0c%p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + + spin_unlock(&ep->udc->lock); + + /* this complete() should a func implemented by gadget layer, + * eg fsg->bulk_in_complete() */ + if (req->req.complete) { + VDBG("calling gadget's complete() req=0x%p", req); + req->req.complete(&ep->ep, &req->req); + VDBG("back from gadget's complete()"); + } + + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/*----------------------------------------------------------------- + * nuke(): delete all requests related to this ep + * called by ep_disable() within spinlock held + * add status paramter? + *--------------------------------------------------------------*/ +static void nuke(struct arcotg_ep *ep, int status) +{ + VDBG(""); + ep->stopped = 1; + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct arcotg_req *req = NULL; + + req = list_entry(ep->queue.next, struct arcotg_req, queue); + + done(ep, req, status); + } + dump_ep_queue(ep); +} + +/*------------------------------------------------------------------ + Internal Hardware related function + ------------------------------------------------------------------*/ + +/* @qh_addr is the aligned virt addr of ep QH addr + * it is used to set endpointlistaddr Reg */ +static int dr_controller_setup(void *qh_addr, struct device *dev) +{ + unsigned int tmp = 0, portctrl = 0; + struct arc_usb_config *config; + + config = udc_controller->config; + + DBG("dev=0x%p config=0x%p", dev, config); + + /* before here, make sure usb_slave_regs has been initialized */ + if (!qh_addr) + return -EINVAL; + + /* Stop and reset the usb controller */ + tmp = le32_to_cpu(usb_slave_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + usb_slave_regs->usbcmd = cpu_to_le32(tmp); + + tmp = le32_to_cpu(usb_slave_regs->usbcmd); + tmp |= USB_CMD_CTRL_RESET; + usb_slave_regs->usbcmd = cpu_to_le32(tmp); + + /* Wait for reset to complete */ + timeout = 10000000; + while ((le32_to_cpu(usb_slave_regs->usbcmd) & USB_CMD_CTRL_RESET) && + --timeout) { + continue; + } + if (timeout == 0) { + printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + return -ETIMEDOUT; + } + + /* Set the controller as device mode */ + tmp = le32_to_cpu(usb_slave_regs->usbmode); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; + usb_slave_regs->usbmode = cpu_to_le32(tmp); + + /* Clear the setup status */ + usb_slave_regs->usbsts = 0; + + tmp = virt_to_phys(qh_addr); + tmp &= USB_EP_LIST_ADDRESS_MASK; + usb_slave_regs->endpointlistaddr = cpu_to_le32(tmp); + + VDBG("vir[qh_base]=0x%p phy[qh_base]=0x%8x epla_reg=0x%8x", + qh_addr, (int)tmp, le32_to_cpu(usb_slave_regs->endpointlistaddr)); + + portctrl = le32_to_cpu(usb_slave_regs->portsc1); + portctrl &= ~PORTSCX_PHY_TYPE_SEL; + + portctrl |= udc_controller->xcvr_type; + + usb_slave_regs->portsc1 = cpu_to_le32(portctrl); + + if (config->set_vbus_power) + config->set_vbus_power(0); + + return 0; +} + +/* just Enable DR irq reg and Set Dr controller Run */ +static void dr_controller_run(struct arcotg_udc *udc) +{ + u32 tmp; + + DBG(); + + /*Enable DR irq reg */ + tmp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN | + USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN | + USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + usb_slave_regs->usbintr = le32_to_cpu(tmp); + + /* Clear stopped bit */ + udc->stopped = 0; + + /* Set the controller as device mode */ + tmp = le32_to_cpu(usb_slave_regs->usbmode); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + usb_slave_regs->usbmode = cpu_to_le32(tmp); + + /* Set controller to Run */ + tmp = le32_to_cpu(usb_slave_regs->usbcmd); + tmp |= USB_CMD_RUN_STOP; + usb_slave_regs->usbcmd = le32_to_cpu(tmp); + + return; +} + +static void dr_controller_stop(struct arcotg_udc *udc) +{ + unsigned int tmp; + + DBG(); + + /* if we're in OTG mode, and the Host is currently using the port, + * stop now and don't rip the controller out from under the + * ehci driver + */ + if (udc->gadget.is_otg) { + if (!(usb_slave_regs->otgsc & OTGSC_STS_USB_ID)) { + DBG("Leaving early"); + return; + } + } + + /* disable all INTR */ + usb_slave_regs->usbintr = 0; + + /* Set stopped bit for isr */ + udc->stopped = 1; + + /* set controller to Stop */ + tmp = le32_to_cpu(usb_slave_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + usb_slave_regs->usbcmd = le32_to_cpu(tmp); +} + +void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = le32_to_cpu(usb_slave_regs->endptctrl[ep_num]); + if (dir) { + if (ep_num) + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl |= + ((unsigned int)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num) + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl |= + ((unsigned int)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT); + } + + usb_slave_regs->endptctrl[ep_num] = cpu_to_le32(tmp_epctrl); + + /* wait for the write reg to finish */ + timeout = 10000000; + while ((!(le32_to_cpu(usb_slave_regs->endptctrl[ep_num]) & + (tmp_epctrl & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)))) + && --timeout) { + continue; + } + if (timeout == 0) { + printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + } +} + +static void dr_ep_change_stall(unsigned char ep_num, unsigned char dir, + int value) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = le32_to_cpu(usb_slave_regs->endptctrl[ep_num]); + + if (value) { + /* set the stall bit */ + if (dir) + tmp_epctrl |= EPCTRL_TX_EP_STALL; + else + tmp_epctrl |= EPCTRL_RX_EP_STALL; + } else { + /* clear the stall bit and reset data toggle */ + if (dir) { + tmp_epctrl &= ~EPCTRL_TX_EP_STALL; + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + tmp_epctrl &= ~EPCTRL_RX_EP_STALL; + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + } + + } + usb_slave_regs->endptctrl[ep_num] = cpu_to_le32(tmp_epctrl); +} + +/******************************************************************** + Internal Structure Build up functions +********************************************************************/ + +/*------------------------------------------------------------------ +* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH + * @zlt: Zero Length Termination Select + * @mult: Mult field + ------------------------------------------------------------------*/ +static void struct_ep_qh_setup(void *handle, unsigned char ep_num, + unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, + unsigned int zlt, unsigned char mult) +{ + struct arcotg_udc *udc = NULL; + struct ep_queue_head *p_QH = NULL; + unsigned int tmp = 0; + + udc = (struct arcotg_udc *)handle; + + p_QH = &udc->ep_qh[2 * ep_num + dir]; + + /* set the Endpoint Capabilites Reg of QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | + EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | + (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + break; + default: + VDBG("error ep type is %d", ep_type); + return; + } + p_QH->max_pkt_length = le32_to_cpu(tmp); + +#ifdef CONFIG_ARM + VDBG("doing consistent_sync(QH=0x%p, l=%d)", + p_QH, sizeof(struct ep_queue_head)); + consistent_sync(p_QH, sizeof(struct ep_queue_head), DMA_TO_DEVICE); +#else + flush_dcache_range((unsigned long)p_QH, (unsigned long)p_QH + + sizeof(struct ep_queue_head)); +#endif + + return; +} + +/* This function only to make code looks good + * it is a collection of struct_ep_qh_setup and dr_ep_setup for ep0 + * ep0 should set OK before the bind() of gadget layer + */ +static void ep0_dr_and_qh_setup(struct arcotg_udc *udc) +{ + /* the intialization of an ep includes: fields in QH, Regs, + * arcotg_ep struct */ + struct_ep_qh_setup(udc, 0, USB_RECV, + USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, + 0, 0); + struct_ep_qh_setup(udc, 0, USB_SEND, + USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, + 0, 0); + dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL); + dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL); + + return; + +} + +/*********************************************************************** + Endpoint Management Functions +***********************************************************************/ + +/*------------------------------------------------------------------------- + * when configurations are set, or when interface settings change + * for example the do_set_interface() in gadget layer, + * the driver will enable or disable the relevant endpoints + * ep0 will not use this func it is enable in probe() +-------------------------------------------------------------------------*/ +static int arcotg_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct arcotg_udc *udc = NULL; + struct arcotg_ep *ep = NULL; + unsigned short max = 0; + unsigned char mult = 0, zlt = 0; + int retval = 0; + unsigned long flags = 0; + char *val = NULL; /* for debug */ + + ep = container_of(_ep, struct arcotg_ep, ep); + + VDBG("%s ep.name=%s", __FUNCTION__, ep->ep.name); + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] || + (desc->bDescriptorType != USB_DT_ENDPOINT)) + /* FIXME: add judge for ep->bEndpointAddress */ + return -EINVAL; + + udc = ep->udc; + + if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + retval = -EINVAL; + + /* check the max package size validate for this endpoint */ + /* Refer to USB2.0 spec table 9-13, + */ + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_BULK: + if (strstr(ep->ep.name, "-iso") + || strstr(ep->ep.name, "-int")) + goto en_done; + mult = 0; + zlt = 1; + switch (udc->gadget.speed) { + case USB_SPEED_HIGH: + if ((max == 128) || (max == 256) || (max == 512)) + break; + default: + switch (max) { + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + case USB_SPEED_LOW: + goto en_done; + } + } + break; + case USB_ENDPOINT_XFER_INT: + if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ + goto en_done; + mult = 0; + zlt = 1; + switch (udc->gadget.speed) { + case USB_SPEED_HIGH: + if (max <= 1024) + break; + case USB_SPEED_FULL: + if (max <= 64) + break; + default: + if (max <= 8) + break; + goto en_done; + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (strstr(ep->ep.name, "-bulk") || strstr(ep->ep.name, "-int")) + goto en_done; + mult = (unsigned char) + (1 + ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 0x03)); + zlt = 0; + switch (udc->gadget.speed) { + case USB_SPEED_HIGH: + if (max <= 1024) + break; + case USB_SPEED_FULL: + if (max <= 1023) + break; + default: + goto en_done; + } + break; + case USB_ENDPOINT_XFER_CONTROL: + if (strstr(ep->ep.name, "-iso") || strstr(ep->ep.name, "-int")) + goto en_done; + mult = 0; + zlt = 1; + switch (udc->gadget.speed) { + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + switch (max) { + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + goto en_done; + } + case USB_SPEED_LOW: + switch (max) { + case 1: + case 2: + case 4: + case 8: + break; + default: + goto en_done; + } + default: + goto en_done; + } + break; + + default: + goto en_done; + } + + /* here initialize variable of ep */ + spin_lock_irqsave(&udc->lock, flags); + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + /* hardware special operation */ + + /* Init EPx Queue Head (Ep Capabilites field in QH + * according to max, zlt, mult) */ + struct_ep_qh_setup((void *)udc, (unsigned char)ep_index(ep), + (unsigned char) + ((desc->bEndpointAddress & USB_DIR_IN) ? + USB_SEND : USB_RECV), (unsigned char) + (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK), + max, zlt, mult); + + /* Init endpoint x at here */ + /* 83xx RM chapter 16.3.2.24, here init the endpoint ctrl reg */ + dr_ep_setup((unsigned char)ep_index(ep), + (unsigned char)((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char)(desc->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK)); + + /* Now HW will be NAKing transfers to that EP, + * until a buffer is queued to it. */ + + /* should have stop the lock */ + spin_unlock_irqrestore(&udc->lock, flags); + retval = 0; + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_BULK: + val = "bulk"; + break; + case USB_ENDPOINT_XFER_ISOC: + val = "iso"; + break; + case USB_ENDPOINT_XFER_INT: + val = "intr"; + break; + default: + val = "ctrl"; + break; + } + + VDBG("enabled %s (ep%d%s-%s) maxpacket %d", ep->ep.name, + ep->desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", val, max); + en_done: + return retval; +} + +/*--------------------------------------------------------------------- + * @ep : the ep being unconfigured. May not be ep0 + * Any pending and uncomplete req will complete with status (-ESHUTDOWN) +*---------------------------------------------------------------------*/ +static int arcotg_ep_disable(struct usb_ep *_ep) +{ + struct arcotg_udc *udc = NULL; + struct arcotg_ep *ep = NULL; + unsigned long flags = 0; + + ep = container_of(_ep, struct arcotg_ep, ep); + if (!_ep || !ep->desc) { + VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + udc = (struct arcotg_udc *)ep->udc; + + spin_lock_irqsave(&udc->lock, flags); + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + VDBG("disabled %s OK", _ep->name); + return 0; +} + +/*--------------------------------------------------------------------- + * allocate a request object used by this endpoint + * the main operation is to insert the req->queue to the eq->queue + * Returns the request, or null if one could not be allocated +*---------------------------------------------------------------------*/ +static struct usb_request *arcotg_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct arcotg_req *req = NULL; + + req = kmalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + memset(req, 0, sizeof *req); + req->req.dma = DMA_ADDR_INVALID; + VDBG("req=0x%p set req.dma=0x%x", req, req->req.dma); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void arcotg_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct arcotg_req *req; + + req = container_of(_req, struct arcotg_req, req); + VDBG("req=0x%p", req); + + if (_req) + kfree(req); +} + +/*------------------------------------------------------------------ + * Allocate an I/O buffer for the ep->req->buf + * @len: length of the desired buffer + * @dma: pointer to the buffer's DMA address; must be valid + * when gadget layer calls this function, ma is &req->dma + * @gfp_flags : GFP_* flags to use + * Returns a new buffer, or null if one could not be allocated +*---------------------------------------------------------------------*/ +static void *arcotg_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t * dma, gfp_t gfp_flags) +{ + void *retval = NULL; + + if (!bytes) + return 0; + + retval = kmalloc(bytes, gfp_flags); + + if (retval) + *dma = virt_to_phys(retval); + + VDBG("ep=%s buffer=0x%p dma=0x%x len=%d", + _ep->name, retval, *dma, bytes); + + return retval; +} + +/*------------------------------------------------------------------ + * Free an I/O buffer for the ep->req->buf + * @dma:for 834x, we will not touch dma field +*---------------------------------------------------------------------*/ +static void arcotg_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + VDBG("buf=0x%p dma=0x%x", buf, dma); + if (buf) + kfree(buf); +} + +/*-------------------------------------------------------------------------*/ + +/* + * link req's dTD queue to the end of ep's QH's dTD queue. + */ +static int arcotg_queue_td(struct arcotg_ep *ep, struct arcotg_req *req) +{ + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; + + /* VDBG("QH addr Register 0x%8x", usb_slave_regs->endpointlistaddr); */ + /* VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ + + VDBG("queue req=0x%p to ep index %d", req, i); + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* Add td to the end */ + struct arcotg_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct arcotg_req, queue); + + /* about to write to dtd, make sure we have latest copy */ + VDBG("doing consistent_sync(dtd=0x%p, l=%d)", + lastreq->tail, sizeof(struct ep_td_struct)); + consistent_sync(lastreq->tail, sizeof(struct ep_td_struct), + DMA_FROM_DEVICE); + + lastreq->tail->next_td_ptr = + cpu_to_le32(virt_to_phys(req->head) & DTD_ADDR_MASK); + + VDBG("ep's queue not empty. lastreq=0x%p", lastreq); + + /* make sure h/w sees our change */ + VDBG("doing consistent_sync(dtd=0x%p, l=%d)", + lastreq->tail, sizeof(struct ep_td_struct)); + consistent_sync(lastreq->tail, sizeof(struct ep_td_struct), + DMA_TO_DEVICE); + + /* Read prime bit, if 1 goto done */ + if (usb_slave_regs->endpointprime & cpu_to_le32(bitmask)) { + VDBG("ep's already primed"); + goto out; + } + + timeout = 10000000; + do { + /* Set ATDTW bit in USBCMD */ + usb_slave_regs->usbcmd |= cpu_to_le32(USB_CMD_ATDTW); + + /* Read correct status bit */ + tmp_stat = le32_to_cpu(usb_slave_regs->endptstatus) & + bitmask; + + } while ((!(usb_slave_regs->usbcmd & + cpu_to_le32(USB_CMD_ATDTW))) + && --timeout); + + if (timeout == 0) { + printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + } + + /* Write ATDTW bit to 0 */ + usb_slave_regs->usbcmd &= cpu_to_le32(~USB_CMD_ATDTW); + + if (tmp_stat) + goto out; + + /* fall through to Case 1: List is empty */ + } + + /* Write dQH next pointer and terminate bit to 0 */ + temp = + virt_to_phys((void *)req->head) & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + dQH->next_dtd_ptr = cpu_to_le32(temp); + + /* Clear active and halt bit */ + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE | + EP_QUEUE_HEAD_STATUS_HALT)); + dQH->size_ioc_int_sts &= temp; + +#ifdef CONFIG_ARM + VDBG("doing consistent_sync(QH=0x%p, l=%d, to_dvc)", + dQH, sizeof(struct ep_queue_head)); + consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_TO_DEVICE); +#else + flush_dcache_range((unsigned long)dQH, (unsigned long)dQH + + sizeof(struct ep_queue_head)); +#endif + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + temp = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + VDBG("setting endpointprime. temp=0x%x (bitmask=0x%x)", temp, bitmask); + usb_slave_regs->endpointprime |= cpu_to_le32(temp); + + out: + return 0; +} + +static int +arcotg_build_dtd(struct arcotg_req *req, unsigned max, + struct ep_td_struct **address) +{ + unsigned length; + u32 swap_temp; + struct ep_td_struct *dtd; + + /* how big will this packet be? */ + length = min(req->req.length - req->req.actual, max); + + /* Assume CACHELINE alignment guarantees 32-byte alignment */ + dtd = kmalloc(sizeof(struct ep_td_struct), GFP_KERNEL | GFP_DMA); + + /* check alignment - must be 32 byte aligned (bits 4:0 == 0) */ + if ((u32) dtd & ~DTD_ADDR_MASK) + panic("Can not allocate aligned memory for dtd"); + + memset(dtd, 0, sizeof(struct ep_td_struct)); + + /* Fill in the transfer size; set interrupt on every dtd; + set active bit */ + swap_temp = ((length << DTD_LENGTH_BIT_POS) | DTD_IOC + | DTD_STATUS_ACTIVE); + + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + VDBG("req=0x%p dtd=0x%p req.dma=0x%x req.length=%d " + "length=%d size_ioc_sts=0x%x", + req, dtd, req->req.dma, req->req.length, + length, dtd->size_ioc_sts); + + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + req->req.actual += length; + *address = dtd; + + return length; +} + +static int arcotg_req_to_dtd(struct arcotg_req *req, struct device *dev) +{ + unsigned max; + unsigned count; + int is_last; + int is_first = 1; + struct ep_td_struct *last_addr = NULL, *addr; + + VDBG("req=0x%p", req); + + max = EP_MAX_LENGTH_TRANSFER; + do { + count = arcotg_build_dtd(req, max, &addr); + + if (is_first) { + is_first = 0; + req->head = addr; + } else { + if (!last_addr) { + /* FIXME last_addr not set. iso only + * case, which we don't do yet + */ + VDBG("*** wiping out something at 0!!"); + } + + last_addr->next_td_ptr = + cpu_to_le32(virt_to_phys(addr)); +#ifdef CONFIG_ARM + VDBG("1 doing consistent_sync(dtd=0x%p, l=%d)", + last_addr, sizeof(struct ep_td_struct)); + consistent_sync(last_addr, sizeof(struct ep_td_struct), + DMA_TO_DEVICE); +#else + flush_dcache_range((unsigned long)last_addr, + (unsigned long)last_addr + + sizeof(struct ep_td_struct)); +#endif + last_addr = addr; + } + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else if (likely(req->req.length != req->req.actual) || + req->req.zero) + is_last = 0; + else + is_last = 1; + + req->dtd_count++; + } while (!is_last); + + addr->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + +#ifdef CONFIG_ARM + VDBG("2 doing consistent_sync(last dtd=0x%p, l=%d)", + addr, sizeof(struct ep_td_struct)); + consistent_sync(addr, sizeof(struct ep_td_struct), DMA_TO_DEVICE); +#else + flush_dcache_range((unsigned long)addr, (unsigned long)addr + + sizeof(struct ep_td_struct)); +#endif + + req->tail = addr; + + return 0; +} + +/* queues (submits) an I/O request to an endpoint */ +static int +arcotg_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep); + struct arcotg_req *req = container_of(_req, struct arcotg_req, req); + struct arcotg_udc *udc; + unsigned long flags; + int is_iso = 0; + + VDBG("_req=0x%p len=%d", _req, _req->length); + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + VDBG("%s, bad params", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep_index(ep))) { + VDBG("%s, bad ep", __FUNCTION__); + return -EINVAL; + } + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, ep_is_in(ep) + ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + req->mapped = 1; + VDBG("called dma_map_single(buffer,%s) req=0x%p " + "buf=0x%p dma=0x%x len=%d", + ep_is_in(ep) ? "to_dvc" : "from_dvc", + req, req->req.buf, req->req.dma, req->req.length); + VDBG("req=0x%p set req.dma=0x%x", req, req->req.dma); + } else { + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + + req->mapped = 0; + VDBG("called dma_sync_single_for_device(buffer,%s) " + "req=0x%p buf=0x%p dma=0x%x len=%d", + ep_is_in(ep) ? "to_dvc" : "from_dvc", + req, req->req.buf, req->req.dma, req->req.length); + } + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* push the dtds to device queue */ + if (!arcotg_req_to_dtd(req, udc->gadget.dev.parent)) + arcotg_queue_td(ep, req); + + /* EP0 */ + if ((ep_index(ep) == 0)) { + udc->ep0_state = DATA_STATE_XMIT; + VDBG("ep0_state now DATA_STATE_XMIT"); + } + + /* put this req at the end of the ep's queue */ + /* irq handler advances the queue */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + + dump_ep_queue(ep); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int arcotg_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep); + struct arcotg_req *req; + unsigned long flags; + + VDBG(""); + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + VDBG("req=0x%p", req); + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return 0; + +} + +/*-------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------- + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt 0--clear halt + * Returns zero, or a negative error code. +*----------------------------------------------------------------*/ +static int _arcotg_ep_set_halt(struct usb_ep *_ep, int value) +{ + + struct arcotg_ep *ep = NULL; + unsigned long flags = 0; + int status = -EOPNOTSUPP; /* operation not supported */ + unsigned char ep_dir = 0, ep_num = 0; + struct arcotg_udc *udc = NULL; + + ep = container_of(_ep, struct arcotg_ep, ep); + udc = ep->udc; + if (!_ep || !ep->desc) { + status = -EINVAL; + goto out; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* Attemp to halt IN ep will fail if any transfer requests + are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + status = 0; + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + ep_num = (unsigned char)(ep_index(ep)); + spin_lock_irqsave(&ep->udc->lock, flags); + dr_ep_change_stall(ep_num, ep_dir, value); + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep_index(ep) == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc->ep0_dir = 0; + } + out: + VDBG(" %s %s halt rc=%d", ep->ep.name, value ? "set" : "clear", status); + + return status; +} + +static int arcotg_ep_set_halt(struct usb_ep *_ep, int value) +{ + return (_arcotg_ep_set_halt(_ep, value)); +} + +static struct usb_ep_ops arcotg_ep_ops = { + .enable = arcotg_ep_enable, + .disable = arcotg_ep_disable, + + .alloc_request = arcotg_alloc_request, + .free_request = arcotg_free_request, + + .alloc_buffer = arcotg_alloc_buffer, + .free_buffer = arcotg_free_buffer, + + .queue = arcotg_ep_queue, + .dequeue = arcotg_ep_dequeue, + + .set_halt = arcotg_ep_set_halt, +}; + +/*------------------------------------------------------------------------- + Gadget Driver Layer Operations +-------------------------------------------------------------------------*/ + +/************************************************************************* + Gadget Driver Layer Operations +*************************************************************************/ + +/*---------------------------------------------------------------------- + * Get the current frame number (from DR frame_index Reg ) + *----------------------------------------------------------------------*/ +static int arcotg_get_frame(struct usb_gadget *gadget) +{ + return (int)(le32_to_cpu(usb_slave_regs->frindex) & USB_FRINDEX_MASKS); +} + +/*----------------------------------------------------------------------- + * Tries to wake up the host connected to this gadget + * + * Return : 0-success + * Negative-this feature not enabled by host or not supported by device hw + * FIXME: RM 16.6.2.2.1 DR support this wake-up feature? + -----------------------------------------------------------------------*/ +static int arcotg_wakeup(struct usb_gadget *gadget) +{ + DBG(); + return -ENOTSUPP; +} + +/* sets the device selfpowered feature + * this affects the device status reported by the hw driver + * to reflect that it now has a local power supply + * usually device hw has register for this feature + */ +static int arcotg_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + DBG(); + return -ENOTSUPP; +} + +static int can_pullup(struct arcotg_udc *udc) +{ + return udc->driver && udc->softconnect && udc->vbus_active; +} + +/* + * Notify controller that VBUS is powered, Called by whatever + * detects VBUS sessions + */ +static int arcotg_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct arcotg_udc *udc; + unsigned long flags; + + DBG(); + + udc = container_of(gadget, struct arcotg_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + DBG("VBUS %s\n", is_active ? "on" : "off"); + udc->vbus_active = (is_active != 0); + +#if 0 /* FIXME manipulate pullups?? check other platforms. */ + if (can_pullup(udc)) + usb_slave_regs->usbcmd |= USB_CMD_RUN_STOP; + else + usb_slave_regs->usbcmd &= ~USB_CMD_RUN_STOP; +#endif + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int arcotg_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct arcotg_udc *udc; + + DBG(); + + udc = container_of(gadget, struct arcotg_udc, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + + return -ENOTSUPP; +} + +/* Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnet + */ +static int arcotg_pullup(struct usb_gadget *gadget, int is_on) +{ + struct arcotg_udc *udc; + + DBG(); + + udc = container_of(gadget, struct arcotg_udc, gadget); + udc->softconnect = (is_on != 0); + if (can_pullup(udc)) + usb_slave_regs->usbcmd |= USB_CMD_RUN_STOP; + else + usb_slave_regs->usbcmd &= ~USB_CMD_RUN_STOP; + + return 0; +} + +/* defined in usb_gadget.h */ +static struct usb_gadget_ops arcotg_gadget_ops = { + .get_frame = arcotg_get_frame, + .wakeup = arcotg_wakeup, + .set_selfpowered = arcotg_set_selfpowered, + .vbus_session = arcotg_vbus_session, + .vbus_draw = arcotg_vbus_draw, + .pullup = arcotg_pullup, +}; + +static void Ep0Stall(struct arcotg_udc *udc) +{ + u32 tmp; + + VDBG(""); + /* a protocol stall */ + tmp = le32_to_cpu(usb_slave_regs->endptctrl[0]); + tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + usb_slave_regs->endptctrl[0] = cpu_to_le32(tmp); + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc->ep0_dir = 0; +} + +/* if direction is EP_IN, the status is Device->Host + * if direction is EP_OUT, the status transaction is Device<-Host + */ +static int ep0_prime_status(struct arcotg_udc *udc, int direction) +{ + + struct arcotg_req *req = udc->status_req; + struct arcotg_ep *ep; + int status = 0; + unsigned long flags; + + VDBG(""); + if (direction == EP_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + ep = &udc->eps[0]; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + VDBG("ep0_state now WAIT_FOR_OUT_STATUS"); + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + spin_lock_irqsave(&udc->lock, flags); + + if ((arcotg_req_to_dtd(req, udc->gadget.dev.parent) == 0)) + status = arcotg_queue_td(ep, req); + if (status) + printk(KERN_ERR "Can't get control status request \n"); + + list_add_tail(&req->queue, &ep->queue); + dump_ep_queue(ep); + + spin_unlock_irqrestore(&udc->lock, flags); + + return status; +} + +static int udc_reset_ep_queue(struct arcotg_udc *udc, u8 pipe) +{ + struct arcotg_ep *ep = get_ep_by_pipe(udc, pipe); + + VDBG(""); + /* FIXME: collect completed requests? */ + if (!ep->name) + return 0; + + nuke(ep, -ECONNRESET); + + return 0; +} + +/* + * ch9 Set address + */ +static void ch9SetAddress(struct arcotg_udc *udc, u16 value, u16 index, + u16 length) +{ + VDBG("new address=%d", value); + + /* Save the new address to device struct */ + udc->device_address = (u8) value; + + /* Update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + + /* Status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + Ep0Stall(udc); +} + +/* + * ch9 Get status + */ +static void ch9GetStatus(struct arcotg_udc *udc, u16 value, u16 index, + u16 length) +{ + u16 usb_status = 0; /* fix me to give correct status */ + + struct arcotg_req *req; + struct arcotg_ep *ep; + int status = 0; + unsigned long flags; + + VDBG(""); + ep = &udc->eps[0]; + + req = container_of(arcotg_alloc_request(&ep->ep, GFP_KERNEL), + struct arcotg_req, req); + req->req.length = 2; + req->req.buf = &usb_status; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* data phase */ + if ((arcotg_req_to_dtd(req, udc->gadget.dev.parent) == 0)) + status = arcotg_queue_td(ep, req); + if (status) { + printk(KERN_ERR "Can't respond to getstatus request \n"); + Ep0Stall(udc); + } else { + udc->ep0_state = DATA_STATE_XMIT; + VDBG("ep0_state now DATA_STATE_XMIT"); + } + + list_add_tail(&req->queue, &ep->queue); + dump_ep_queue(ep); + + spin_unlock_irqrestore(&udc->lock, flags); + +} + +/* + * ch9 Set config + */ +static void ch9SetConfig(struct arcotg_udc *udc, u16 value, u16 index, + u16 length) +{ + VDBG("1 calling gadget driver->setup"); + udc->ep0_dir = USB_DIR_IN; + if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff) >= 0) { + /* gadget layer deal with the status phase */ + udc->usb_state = USB_STATE_CONFIGURED; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + VDBG("ep0_state now WAIT_FOR_OUT_STATUS"); + } +} + +static void setup_received_irq(struct arcotg_udc *udc, + struct usb_ctrlrequest *setup) +{ + int handled = 1; /* set to zero if we do not handle the message, */ + /* and should pass it to the gadget driver */ + + VDBG("request=0x%x", setup->bRequest); + /* Fix Endian (udc->local_setup_buff is cpu Endian now) */ + setup->wValue = le16_to_cpu(setup->wValue); + setup->wIndex = le16_to_cpu(setup->wIndex); + setup->wLength = le16_to_cpu(setup->wLength); + + udc_reset_ep_queue(udc, 0); + + /* We asume setup only occurs on EP0 */ + if (setup->bRequestType & USB_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + /* handle class requests */ + switch (setup->bRequest) { + + case USB_BULK_RESET_REQUEST: + udc->ep0_dir = USB_DIR_IN; + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) >= 0) { + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + } + break; + + default: + handled = 0; + break; + } + } else if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + /* handle standard requests */ + switch (setup->bRequest) { + + case USB_REQ_GET_STATUS: + if ((setup-> + bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + ch9GetStatus(udc, setup->wValue, setup->wIndex, + setup->wLength); + break; + + case USB_REQ_SET_ADDRESS: + if (setup->bRequestType != + (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + ch9SetAddress(udc, setup->wValue, setup->wIndex, + setup->wLength); + break; + + case USB_REQ_SET_CONFIGURATION: + if (setup->bRequestType != + (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + /* gadget layer take over the status phase */ + ch9SetConfig(udc, setup->wValue, setup->wIndex, + setup->wLength); + break; + case USB_REQ_SET_INTERFACE: + if (setup->bRequestType != + (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_INTERFACE)) + break; + udc->ep0_dir = USB_DIR_IN; + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) >= 0) + /* gadget layer take over the status phase */ + break; + /* Requests with no data phase */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + { /* status transaction */ + int rc = -EOPNOTSUPP; + + if ((setup->bRequestType & USB_TYPE_MASK) != + USB_TYPE_STANDARD) + break; + + /* we only support set/clear feature for endpoint */ + if (setup->bRequestType == USB_RECIP_ENDPOINT) { + int dir = (setup->wIndex & 0x0080) ? + EP_DIR_IN : EP_DIR_OUT; + int num = (setup->wIndex & 0x000f); + struct arcotg_ep *ep; + + if (setup->wValue != 0 + || setup->wLength != 0 + || (num * 2 + dir) > USB_MAX_PIPES) + break; + ep = &udc->eps[num * 2 + dir]; + + if (setup->bRequest == + USB_REQ_SET_FEATURE) { + VDBG("SET_FEATURE doing set_halt"); + rc = _arcotg_ep_set_halt(&ep-> + ep, 1); + } else { + VDBG("CLEAR_FEATURE doing clear_halt"); + rc = _arcotg_ep_set_halt(&ep-> + ep, 0); + } + + } else if (setup->bRequestType == + USB_RECIP_DEVICE) { + if (!udc->gadget.is_otg) + break; + else if (setup->bRequest == + USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == + USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + rc = 0; + } + if (rc == 0) { + /* send status only if _arcotg_ep_set_halt success */ + if (ep0_prime_status(udc, EP_DIR_IN)) + Ep0Stall(udc); + } + break; + } + default: + handled = 0; + break; + } + } else { + /* vendor requests */ + handled = 0; + } + + if (!handled) { + if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff) + != 0) { + Ep0Stall(udc); + } else if (setup->bRequestType & USB_DIR_IN) { + udc->ep0_state = DATA_STATE_XMIT; + VDBG("ep0_state now DATA_STATE_XMIT"); + } else { + udc->ep0_state = DATA_STATE_RECV; + VDBG("ep0_state now DATA_STATE_RECV"); + } + } +} + +static void ep0_req_complete(struct arcotg_udc *udc, struct arcotg_ep *ep0, + struct arcotg_req *req) +{ + VDBG("req=0x%p ep0_state=0x%x", req, udc->ep0_state); + if (udc->usb_state == USB_STATE_ADDRESS) { + /* Set the new address */ + u32 new_address = (u32) udc->device_address; + usb_slave_regs->deviceaddr = cpu_to_le32(new_address << + USB_DEVICE_ADDRESS_BIT_POS); + VDBG("set deviceaddr to %d", + usb_slave_regs->deviceaddr >> USB_DEVICE_ADDRESS_BIT_POS); + } + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + + done(ep0, req, 0); + /* receive status phase */ + if (ep0_prime_status(udc, EP_DIR_OUT)) + Ep0Stall(udc); + break; + + case DATA_STATE_RECV: + + done(ep0, req, 0); + /* send status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + Ep0Stall(udc); + break; + + case WAIT_FOR_OUT_STATUS: + done(ep0, req, 0); + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + break; + + case WAIT_FOR_SETUP: + VDBG("Unexpected interrupt"); + break; + + default: + Ep0Stall(udc); + break; + } +} + +static void tripwire_handler(struct arcotg_udc *udc, u8 ep_num, u8 * buffer_ptr) +{ + u32 temp; + struct ep_queue_head *qh; + + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + VDBG("doing consistent_sync(QH=0x%p, l=%d)", + qh, sizeof(struct ep_queue_head)); + + consistent_sync(qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE); + + /* Clear bit in ENDPTSETUPSTAT */ + temp = cpu_to_le32(1 << ep_num); + usb_slave_regs->endptsetupstat |= temp; + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = cpu_to_le32(USB_CMD_SUTW); + usb_slave_regs->usbcmd |= temp; + + /* Copy the setup packet to local buffer */ + VDBG("qh=0x%p copy setup buffer from 0x%p to 0x%p", + qh, qh->setup_buffer, buffer_ptr); + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } while (!(le32_to_cpu(usb_slave_regs->usbcmd) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ + temp = le32_to_cpu(usb_slave_regs->usbcmd); + temp &= ~USB_CMD_SUTW; + usb_slave_regs->usbcmd = le32_to_cpu(temp); + + timeout = 10000000; + while ((usb_slave_regs->endptsetupstat & 1) && --timeout) { + continue; + } + if (timeout == 0) { + printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + } +} + +/*process_ep_req(): free the completed Tds for this req */ +/* FIXME: ERROR handling for multi-dtd requests */ +static int process_ep_req(struct arcotg_udc *udc, int pipe, + struct arcotg_req *curr_req) +{ + struct ep_td_struct *curr_td, *tmp_td; + int td_complete, actual, remaining_length, j, tmp; + int status = 0; + int errors = 0; + struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; + int direction = pipe % 2; + + curr_td = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + /* must sync the cache with the device, to get the real data */ + + VDBG("doing consistent_sync(dtd=0x%p, l=%d)", + curr_td, sizeof(struct ep_td_struct)); + consistent_sync(curr_td, sizeof(struct ep_td_struct), DMA_FROM_DEVICE); + + VDBG("doing consistent_sync(QH=0x%p, l=%d)", + curr_qh, sizeof(struct ep_queue_head)); + consistent_sync(curr_qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE); + + VDBG("curr_req=0x%p curr_td=0x%p actual=%d size_ioc_sts=0x%x ", + curr_req, curr_td, actual, curr_td->size_ioc_sts); + + for (j = 0; j < curr_req->dtd_count; j++) { + remaining_length = ((le32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS); + actual -= remaining_length; + + if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & + DTD_ERROR_MASK)) { + if (errors & DTD_STATUS_HALTED) { + printk(KERN_ERR "dTD error %08x \n", errors); + /* Clear the errors and Halt condition */ + tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; + curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + status = -EPIPE; + /*FIXME clearing active bit, update + * nextTD pointer re-prime ep */ + + break; + } + if (errors & DTD_STATUS_DATA_BUFF_ERR) { + VDBG("Transfer overflow"); + status = -EPROTO; + break; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + VDBG("ISO error"); + status = -EILSEQ; + break; + } else + printk(KERN_ERR + "Unknown error has occured (0x%x)!\r\n", + errors); + + } else if (le32_to_cpu(curr_td->size_ioc_sts) & + DTD_STATUS_ACTIVE) { + VDBG("Request not wholly complete dtd=0x%p", curr_td); + status = REQ_UNCOMPLETE; + return status; + } else if (remaining_length) + if (direction) { + VDBG("Transmit dTD remaining length not zero " + "(rl=%d)", remaining_length); + status = -EPROTO; + break; + } else { + td_complete += 1; + break; + } else { + td_complete += 1; + VDBG("dTD transmitted successful "); + } + + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *) + phys_to_virt(le32_to_cpu(curr_td->next_td_ptr) + & DTD_ADDR_MASK); + + } + + if (status) + return status; + + curr_req->req.actual = actual; + + /* Free dtd for completed/error request */ + curr_td = curr_req->head; + for (j = 0; j < curr_req->dtd_count; j++) { + tmp_td = curr_td; + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *) + phys_to_virt(le32_to_cpu(curr_td->next_td_ptr) + & DTD_ADDR_MASK); + VDBG("freeing dtd 0x%p", tmp_td); + kfree(tmp_td); + } + + return status; + +} + +static void dtd_complete_irq(struct arcotg_udc *udc) +{ + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct arcotg_ep *curr_ep; + struct arcotg_req *curr_req, *temp_req; + + VDBG(""); + /* Clear the bits in the register */ + bit_pos = usb_slave_regs->endptcomplete; + usb_slave_regs->endptcomplete = bit_pos; + bit_pos = le32_to_cpu(bit_pos); + + if (!bit_pos) + return; + + for (i = 0; i < USB_MAX_ENDPOINTS * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) + continue; + + curr_ep = get_ep_by_pipe(udc, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + WARN("Invalid EP?"); + continue; + } + + dump_ep_queue(curr_ep); + + /* search all arcotg_reqs of ep */ + list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, + queue) { + status = process_ep_req(udc, i, curr_req); + if (status == REQ_UNCOMPLETE) { + VDBG("Not all tds are completed in the req"); + break; + } + + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else + done(curr_ep, curr_req, status); + + } + + dump_ep_queue(curr_ep); + } +} + +static void port_change_irq(struct arcotg_udc *udc) +{ + u32 speed; + + if (udc->bus_reset) + udc->bus_reset = FALSE; + + /* Bus resetting is finished */ + if (!(le32_to_cpu(usb_slave_regs->portsc1) & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (le32_to_cpu(usb_slave_regs->portsc1) & + PORTSCX_PORT_SPEED_MASK); + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + udc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSCX_PORT_SPEED_FULL: + udc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSCX_PORT_SPEED_LOW: + udc->gadget.speed = USB_SPEED_LOW; + break; + default: + udc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + } + DBG("speed now %d", udc->gadget.speed); + + /* Update USB state */ + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +static void suspend_irq(struct arcotg_udc *udc) +{ + DBG(); + + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver ,serial.c not support this */ + if (udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void resume_irq(struct arcotg_udc *udc) +{ + DBG(); + + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver , serial.c not support this */ + if (udc->driver->resume) + udc->driver->resume(&udc->gadget); + +} + +static int reset_queues(struct arcotg_udc *udc) +{ + u8 pipe; + + DBG("disconnect"); + for (pipe = 0; pipe < udc->max_pipes; pipe++) + udc_reset_ep_queue(udc, pipe); + + /* report disconnect; the driver is already quiesced */ + udc->driver->disconnect(&udc->gadget); + + return 0; +} + +/* + * Interrupt handler for USB reset received + */ + +static void reset_irq(struct arcotg_udc *udc) +{ + u32 temp; + + /* Clear the device address */ + temp = le32_to_cpu(usb_slave_regs->deviceaddr); + temp &= ~USB_DEVICE_ADDRESS_MASK; + usb_slave_regs->deviceaddr = cpu_to_le32(temp); + VDBG("set deviceaddr to %d", + usb_slave_regs->deviceaddr >> USB_DEVICE_ADDRESS_BIT_POS); + udc->device_address = 0; + + /* Clear usb state */ + udc->usb_state = USB_STATE_DEFAULT; + + /* Clear all the setup token semaphores */ + temp = le32_to_cpu(usb_slave_regs->endptsetupstat); + usb_slave_regs->endptsetupstat = cpu_to_le32(temp); + + /* Clear all the endpoint complete status bits */ + temp = le32_to_cpu(usb_slave_regs->endptcomplete); + usb_slave_regs->endptcomplete = cpu_to_le32(temp); + + timeout = 10000000; + /* Wait until all endptprime bits cleared */ + while ((usb_slave_regs->endpointprime) && --timeout) { + continue; + } + if (timeout == 0) { + printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + } + + /* Write 1s to the Flush register */ + usb_slave_regs->endptflush = 0xFFFFFFFF; + + if (le32_to_cpu(usb_slave_regs->portsc1) & PORTSCX_PORT_RESET) { + VDBG("Bus RESET"); + /* Bus is reseting */ + udc->bus_reset = TRUE; + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc->ep0_dir = 0; + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + reset_queues(udc); + } else { + VDBG("Controller reset"); + /* initialize usb hw reg except for regs for EP, not + * touch usbintr reg */ + dr_controller_setup(udc->ep_qh, udc->gadget.dev.parent); + + /* FIXME: Reset all internal used Queues */ + reset_queues(udc); + + ep0_dr_and_qh_setup(udc); + + /* Enable DR IRQ reg, Set Run bit, change udc state */ + dr_controller_run(udc); + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc->ep0_dir = 0; + } +} + +/* + * USB device controller interrupt handler + */ +static irqreturn_t arcotg_udc_irq(int irq, void *_udc, struct pt_regs *r) +{ + struct arcotg_udc *udc = _udc; + u32 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + if (udc->stopped) + return IRQ_NONE; /* ignore irq if we're not running */ + + spin_lock_irqsave(&udc->lock, flags); + irq_src = usb_slave_regs->usbsts & usb_slave_regs->usbintr; + /* Clear notification bits */ + usb_slave_regs->usbsts &= irq_src; + + irq_src = le32_to_cpu(irq_src); + VDBG("irq_src [0x%08x]", irq_src); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + /* Setup packet, we only support ep0 as control ep */ + VDBG("endptsetupstat=0x%x endptcomplete=0x%x", + usb_slave_regs->endptsetupstat, + usb_slave_regs->endptcomplete); + if (usb_slave_regs-> + endptsetupstat & cpu_to_le32(EP_SETUP_STATUS_EP0)) { + tripwire_handler(udc, 0, + (u8 *) (&udc->local_setup_buff)); + setup_received_irq(udc, &udc->local_setup_buff); + status = IRQ_HANDLED; + } + + /* completion of dtd */ + if (usb_slave_regs->endptcomplete) { + dtd_complete_irq(udc); + status = IRQ_HANDLED; + } + } + + /* SOF (for ISO transfer) */ + if (irq_src & USB_STS_SOF) { + status = IRQ_HANDLED; + } + + /* Port Change */ + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_irq(udc); + status = IRQ_HANDLED; + } + + /* Reset Received */ + if (irq_src & USB_STS_RESET) { + reset_irq(udc); + status = IRQ_HANDLED; + } + + /* Sleep Enable (Suspend) */ + if (irq_src & USB_STS_SUSPEND) { + suspend_irq(udc); + status = IRQ_HANDLED; + } else if (udc->resume_state) { + resume_irq(udc); + status = IRQ_HANDLED; + } + + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { + VDBG("Error IRQ %x ", irq_src); + status = IRQ_HANDLED; + } + + if (status != IRQ_HANDLED) { + VDBG("not handled irq_src=0x%x", irq_src); + } + + VDBG("irq_src [0x%08x] done. regs now=0x%08x", irq_src, + usb_slave_regs->usbsts & usb_slave_regs->usbintr); + VDBG("-"); + VDBG("-"); + spin_unlock_irqrestore(&udc->lock, flags); + + return status; +} + +/*----------------------------------------------------------------* + * tell the controller driver about gadget layer driver + * The driver's bind function will be called to bind it to a gadget. + * @driver: for example fsg_driver from file_storage.c +*----------------------------------------------------------------*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int retval = -ENODEV; + unsigned long flags = 0; + + DBG("udc_controller=0x%p", udc_controller); + + /* standard operations */ + if (!udc_controller) + return -ENODEV; + + if (!driver || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->unbind || + !driver->disconnect || !driver->setup) + return -EINVAL; + + if (udc_controller->driver) + return -EBUSY; + + /* lock is needed but whether should use this lock or another */ + spin_lock_irqsave(&udc_controller->lock, flags); + + driver->driver.bus = 0; + /* hook up the driver */ + udc_controller->driver = driver; + udc_controller->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc_controller->lock, flags); + + retval = driver->bind(&udc_controller->gadget); + if (retval) { + VDBG("bind to %s --> %d", driver->driver.name, retval); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + goto out; + } + + + if (udc_controller->transceiver) { + pm_message_t state = {0}; + + /* Suspend the controller until OTG enables it */ + arcotg_udc_suspend((struct device *)NULL, state); + DBG("suspend udc for OTG auto detect \n"); + + /* export udc suspend/resume call to OTG */ + udc_controller->gadget.dev.driver->suspend = arcotg_udc_suspend; + udc_controller->gadget.dev.driver->resume = arcotg_udc_resume; + + /* connect to bus through transceiver */ + retval = otg_set_peripheral(udc_controller->transceiver, + &udc_controller->gadget); + if (retval < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind (&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + return retval; + } + } else { + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc_controller->ep0_dir = 0; + } + + printk(KERN_INFO "arcotg_udc: gadget %s bound to driver %s\n", + udc_controller->gadget.name, driver->driver.name); + + out: + return retval; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct arcotg_ep *loop_ep; + unsigned long flags; + + if (!udc_controller) + return -ENODEV; + + if (!driver || driver != udc_controller->driver) + return -EINVAL; + + if (udc_controller->transceiver) { + (void) otg_set_peripheral(udc_controller->transceiver, 0); + DBG("set peripheral=NULL"); + } else { + /* FIXME + pullup_disable(udc); + */ + } + + /* stop DR, disable intr */ + dr_controller_stop(udc_controller); + + /* in fact, no needed */ + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + VDBG("ep0_state now WAIT_FOR_SETUP"); + udc_controller->ep0_dir = 0; + + /* stand operation */ + spin_lock_irqsave(&udc_controller->lock, flags); + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc_controller->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, + ep.ep_list) + nuke(loop_ep, -ESHUTDOWN); + + /* report disconnect to free up endpoints */ + driver->disconnect(&udc_controller->gadget); + + spin_unlock_irqrestore(&udc_controller->lock, flags); + + /* unbind gadget and unhook driver. */ + driver->unbind(&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + + printk(KERN_INFO "unregistered gadget driver '%s'\r\n", + driver->driver.name); + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*------------------------------------------------------------------------- + PROC File System Support +-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#include + +static const char proc_filename[] = "driver/arcotg_udc"; + +static int arcotg_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t, i; + u32 tmp_reg; + struct arcotg_ep *ep = NULL; + struct arcotg_req *req; + + struct arcotg_udc *udc = udc_controller; + if (off != 0) + return 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* ------basic driver infomation ---- */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "%s version: %s\n" + "Gadget driver: %s\n\n", driver_name, DRIVER_VERSION, + udc->driver ? udc->driver->driver.name : "(none)"); + size -= t; + next += t; + + /* ------ DR Registers ----- */ + tmp_reg = le32_to_cpu(usb_slave_regs->usbcmd); + t = scnprintf(next, size, + "USBCMD reg:\n" "SetupTW: %d\n" "Run/Stop: %s\n\n", + (tmp_reg & USB_CMD_SUTW) ? 1 : 0, + (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop"); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->usbsts); + t = scnprintf(next, size, + "USB Status Reg:\n" "Dr Suspend: %d" + "Reset Received: %d" "System Error: %s" + "USB Error Interrupt: %s\n\n", + (tmp_reg & USB_STS_SUSPEND) ? 1 : 0, + (tmp_reg & USB_STS_RESET) ? 1 : 0, + (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal", + (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err"); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->usbintr); + t = scnprintf(next, size, + "USB Intrrupt Enable Reg:\n" + "Sleep Enable: %d" "SOF Received Enable: %d" + "Reset Enable: %d\n" "System Error Enable: %d" + "Port Change Dectected Enable: %d\n" + "USB Error Intr Enable: %d" + "USB Intr Enable: %d\n\n", + (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, + (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0, + (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0, + (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0, + (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_INT_EN) ? 1 : 0); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->frindex); + t = scnprintf(next, size, + "USB Frame Index Reg:" "Frame Number is 0x%x\n\n", + (tmp_reg & USB_FRINDEX_MASKS)); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->deviceaddr); + t = scnprintf(next, size, + "USB Device Address Reg:" "Device Addr is 0x%x\n\n", + (tmp_reg & USB_DEVICE_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->endpointlistaddr); + t = scnprintf(next, size, + "USB Endpoint List Address Reg:" + "Device Addr is 0x%x\n\n", + (tmp_reg & USB_EP_LIST_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->portsc1); + t = scnprintf(next, size, + "USB Port Status&Control Reg:\n" + "Port Transceiver Type : %s" "Port Speed: %s \n" + "PHY Low Power Suspend: %s" "Port Reset: %s" + "Port Suspend Mode: %s \n" "Over-current Change: %s" + "Port Enable/Disable Change: %s\n" + "Port Enabled/Disabled: %s" + "Current Connect Status: %s\n\n", ( { + char *s; + switch (tmp_reg & + PORTSCX_PTS_FSLS) + { +case PORTSCX_PTS_UTMI: +s = "UTMI"; break; case PORTSCX_PTS_ULPI: +s = "ULPI "; break; case PORTSCX_PTS_FSLS: +s = "FS/LS Serial"; break; default: + s = "None"; break;} + s;} + ), ( { + char *s; switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { +case PORTSCX_PORT_SPEED_FULL: +s = "Full Speed"; break; case PORTSCX_PORT_SPEED_LOW: +s = "Low Speed"; break; case PORTSCX_PORT_SPEED_HIGH: +s = "High Speed"; break; default: + s = "Undefined"; break;} + s;} + ), + (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? + "Normal PHY mode" : "Low power mode", + (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : + "Not in Reset", + (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in", + (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" : + "No", + (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" : + "Not change", + (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" : + "Not correct", + (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ? + "Attached" : "Not-Att") ; + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->usbmode); + t = scnprintf(next, size, "USB Mode Reg:" "Controller Mode is : %s\n\n", ( { + char + *s; + switch + (tmp_reg + & + USB_MODE_CTRL_MODE_HOST) + { +case USB_MODE_CTRL_MODE_IDLE: +s = "Idle"; break; case USB_MODE_CTRL_MODE_DEVICE: +s = "Device Controller"; break; case USB_MODE_CTRL_MODE_HOST: +s = "Host Controller"; break; default: + s + = + "None"; + break;} + s;} + )) ; + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_slave_regs->endptsetupstat); + t = scnprintf(next, size, + "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n", + (tmp_reg & EP_SETUP_STATUS_MASK)); + size -= t; + next += t; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) { + tmp_reg = le32_to_cpu(usb_slave_regs->endptctrl[i]); + t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n", + i, tmp_reg); + size -= t; + next += t; + } + tmp_reg = le32_to_cpu(usb_slave_regs->endpointprime); + t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg); + size -= t; + next += t; + + /* ------arcotg_udc, arcotg_ep, arcotg_request structure information ----- */ + ep = &udc->eps[0]; + t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n", + ep->ep.name, ep_maxpacket(ep), ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length 0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } + } + /* other gadget->eplist ep */ + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + t = scnprintf(next, size, + "\nFor %s Maxpkt is 0x%x index is 0x%x\n", + ep->ep.name, + ep_maxpacket(ep), ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, + "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length" + "0x%x buf %p\n", + &req->req, + req->req.actual, + req->req.length, + req->req.buf); + size -= t; + next += t; + } + } + } +#if 0 /* DDD debug */ + else { + t = scnprintf(next, size, "\nno desc for %s\n", + ep->ep.name); + size -= t; + next += t; + } +#endif + } + + spin_unlock_irqrestore(&udc->lock, flags); + + *eof = 1; + return count - size; +} + +#define create_proc_file() create_proc_read_entry(proc_filename, \ + 0, NULL, arcotg_proc_read, NULL) + +#define remove_proc_file() remove_proc_entry(proc_filename, NULL) + +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +#define create_proc_file() do {} while (0) +#define remove_proc_file() do {} while (0) + +#endif /*CONFIG_USB_GADGET_DEBUG_FILES */ + +/*-------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------- + * Release the ARC OTG specific udc structure + * it is not stand gadget function + * it is called when the last reference to the device is removed; + * it is called from the embedded kobject's release method. + * All device structures registered with the core must have a + * release method, or the kernel prints out scary complaints + *-------------------------------------------------------------------------*/ +static void arcotg_udc_release(struct device *dev) +{ + complete(udc_controller->done); + kfree(ep_qh_base); + ep_qh_base = NULL; + kfree(udc_controller); + udc_controller = NULL; +} + +/****************************************************************** + Internal Structure Build up functions -2 +*******************************************************************/ +/*------------------------------------------------------------------ + * this func will init resource for globle controller + * Return the udc handle on success or Null on failing + ------------------------------------------------------------------*/ +static void *struct_udc_setup(struct platform_device *pdev) +{ + struct arcotg_udc *udc = NULL; + unsigned int tmp_sz = 0; + + udc = (struct arcotg_udc *) + kmalloc(sizeof(struct arcotg_udc), GFP_KERNEL); + VDBG("kmalloc(ucd)=0x%p", udc); + if (udc == NULL) { + printk(KERN_ERR "malloc udc failed\n"); + goto cleanup; + } + + /* Zero out the internal USB state structure */ + memset(udc, 0, sizeof(struct arcotg_udc)); + + /* initialized QHs, take care the 2K align */ + tmp_sz = USB_MAX_PIPES * sizeof(struct ep_queue_head); + + udc->ep_qh = + (struct ep_queue_head *)KMALLOC_ALIGN(tmp_sz, GFP_KERNEL | GFP_DMA, + 2 * 1024, + (void **)&ep_qh_base); + + if (!udc->ep_qh) { + printk(KERN_ERR "malloc QHs for udc failed\n"); + goto cleanup; + } + VDBG("udc->ep_qh=0x%p ep_qh_base=0x%p", udc->ep_qh, ep_qh_base); + + /* Initialize ep0 status request structure */ + /* FIXME: arcotg_alloc_request() ignores ep argument */ + udc->status_req = + container_of(arcotg_alloc_request(NULL, GFP_KERNEL), + struct arcotg_req, req); + + /* allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); + udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + + VDBG("status_req=0x%p status_req->req.buf=0x%p " + "status_req->req.dma=0x%x", + udc->status_req, udc->status_req->req.buf, + udc->status_req->req.dma); + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = 0; + /* initliaze the arcotg_udc lock */ + spin_lock_init(&udc->lock); + + return udc; + + cleanup: + kfree(udc); + return NULL; +} + +/*---------------------------------------------------------------- + * set up the arcotg_ep struct for eps + * ep0out isnot used so do nothing here + * ep0in should be taken care + * It also link this arcotg_ep->ep to gadget->ep_list + *--------------------------------------------------------------*/ +static int struct_ep_setup(void *handle, unsigned char pipe_num) +{ + struct arcotg_udc *udc = (struct arcotg_udc *)handle; + struct arcotg_ep *ep = get_ep_by_pipe(udc, pipe_num); + + /* + VDBG("pipe_num=%d name[%d]=%s", + pipe_num, pipe_num, ep_name[pipe_num]); + */ + ep->udc = udc; + strcpy(ep->name, ep_name[pipe_num]); + ep->ep.name = ep_name[pipe_num]; + ep->ep.ops = &arcotg_ep_ops; + ep->stopped = 0; + + /* for ep0: the desc defined here; + * for other eps, gadget layer called ep_enable with defined desc + */ + /* for ep0: maxP defined in desc + * for other eps, maxP is set by epautoconfig() called by gadget layer + */ + if (pipe_num == 0) { + ep->desc = &arcotg_ep0_desc; + ep->ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + } else { + ep->ep.maxpacket = (unsigned short)~0; + ep->desc = NULL; + } + + /* the queue lists any req for this ep */ + INIT_LIST_HEAD(&ep->queue); + + /* arcotg_ep->ep.ep_list: gadget ep_list hold all of its eps + * so only the first should init--it is ep0' */ + + /* gagdet.ep_list used for ep_autoconfig so no ep0 */ + if (pipe_num != 0) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + + ep->gadget = &udc->gadget; + + return 0; +} + +/* Initialize board specific registers */ +static int board_init(struct device *dev) +{ + struct arc_usb_config *config; + config = (struct arc_usb_config *)dev->platform_data; + + DBG("dev=0x%p config=0x%p", dev, config); + + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (config->platform_init() != 0) + return -EINVAL; + + return 0; +} + +/* Driver probe functions */ + + /* all intialize operations implemented here except Enable usb_intr reg + */ +static int __init arcotg_udc_probe(struct platform_device *pdev) +{ + /* FIXME: add platform_data */ + struct arc_usb_config *config = pdev->dev.platform_data; + unsigned int tmp_status = -ENODEV; + unsigned int i; + u32 id; + u64 rsrc_start, rsrc_len; + + if (strcmp(pdev->name, "arc_udc")) { + VDBG("Wrong device"); + return -ENODEV; + } + + DBG("pdev=0x%p config=0x%p", pdev, config); + + if (board_init(&pdev->dev) != 0) + return -EINVAL; + + /* Initialize the udc structure including QH member and other member */ + udc_controller = (struct arcotg_udc *)struct_udc_setup(pdev); + if (!udc_controller) { + VDBG("udc_controller is NULL"); + return -ENOMEM; + } + VDBG("udc_controller=0x%p", udc_controller); + + udc_controller->config = config; + udc_controller->xcvr_type = config->xcvr_type; + +#ifdef CONFIG_USB_OTG + udc_controller->transceiver = otg_get_transceiver(); + DBG("otg_get_transceiver returns 0x%p", udc_controller->transceiver); +#endif + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + return -ENODEV; + } + + rsrc_start = pdev->resource[0].start; + rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + VDBG(" start=0x%lx end=0x%lx\n", + pdev->resource[0].start, pdev->resource[0].end); + VDBG("rsrc_start=0x%llx rsrc_len=0x%llx\n", rsrc_start, rsrc_len); + +#if 0 /* DDD */ + DBG("doing request_mem_region(start=0x%llx, len=0x%llx)", + rsrc_start, rsrc_len); + if (!request_mem_region(rsrc_start, rsrc_len, driver_name)) { + printk(KERN_ERR "request_mem_region failed\n"); + return -EBUSY; + } +#endif + usb_slave_regs = (struct usb_dr_device *) (int) IO_ADDRESS(rsrc_start); + + DBG("usb_slave_regs = 0x%p", usb_slave_regs); + VDBG("hci_version=0x%x", usb_slave_regs->hciversion); + VDBG("otgsc at 0x%p", &usb_slave_regs->otgsc); + + id = usb_slave_regs->id; + VDBG(KERN_INFO "ARC USBOTG h/w ID=0x%x revision=0x%x\n", + id & 0x3f, id >> 16); + + /* request irq and disable DR */ + tmp_status = request_irq(pdev->resource[1].start, arcotg_udc_irq, + SA_SHIRQ, driver_name, udc_controller); + if (tmp_status != 0) { + printk(KERN_ERR "cannot request irq %d err %d \n", + (int)pdev->resource[1].start, tmp_status); + // DDD free mem_region here + return tmp_status; + } + + if (!udc_controller->transceiver) { + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched*/ + dr_controller_setup(udc_controller->ep_qh, &pdev->dev); + } + + /* here comes the stand operations for probe + * set the arcotg_udc->gadget.xxx + */ + udc_controller->gadget.ops = &arcotg_gadget_ops; + udc_controller->gadget.is_dualspeed = 1; + + /* gadget.ep0 is a pointer */ + udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; + + INIT_LIST_HEAD(&udc_controller->gadget.ep_list); + + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + + /* name: Identifies the controller hardware type. */ + udc_controller->gadget.name = driver_name; + + device_initialize(&udc_controller->gadget.dev); + + strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + + udc_controller->gadget.dev.release = arcotg_udc_release; + udc_controller->gadget.dev.parent = &pdev->dev; + + if (udc_controller->transceiver) { + udc_controller->gadget.is_otg = 1; + } + + /* for an EP, the intialization includes: fields in QH, Regs, + * arcotg_ep struct */ + ep0_dr_and_qh_setup(udc_controller); + for (i = 0; i < USB_MAX_PIPES; i++) { + /* because the ep type is not decide here so + * struct_ep_qh_setup() and dr_ep_setup() + * should be called in ep_enable() + */ + if (ep_name[i] != NULL) + /* setup the arcotg_ep struct and link ep.ep.list + * into gadget.ep_list */ + struct_ep_setup(udc_controller, i); + } + + create_proc_file(); + device_add(&udc_controller->gadget.dev); + VDBG("back from device_add "); + + return 0; +} + +/* Driver removal functions + * Free resources + * Finish pending transaction + */ +static int __exit arcotg_udc_remove(struct platform_device *pdev) +{ + struct arc_usb_config *config; + + DECLARE_COMPLETION(done); + + config = (struct arc_usb_config *)pdev->dev.platform_data; + + if (!udc_controller) + return -ENODEV; + + udc_controller->done = &done; + + if (udc_controller->transceiver) { + put_device(udc_controller->transceiver->dev); + udc_controller->transceiver = 0; + } + + /* DR has been stopped in usb_gadget_unregister_driver() */ + + /* remove proc */ + remove_proc_file(); + + /* free irq */ + free_irq(pdev->resource[1].start, udc_controller); + + /* deinitialize all ep: strcut */ + /* deinitialize ep0: reg and QH */ + + /* Free allocated memory */ + kfree(udc_controller->status_req->req.buf); + kfree(udc_controller->status_req); + + device_unregister(&udc_controller->gadget.dev); + /* free udc --wait for the release() finished */ + wait_for_completion(&done); + +#if 0 /* DDD */ + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); +#endif + + /* + * do platform specific un-initialization: + * release iomux pins, etc. + */ + config->platform_uninit(); + + return 0; +} + +/*----------------------------------------------------------------- + * Modify Power management attributes + * Here we stop the DR controller and disable the irq + -----------------------------------------------------------------*/ +static int arcotg_udc_suspend(struct device *dev, pm_message_t state) +{ + DBG("Suspend. event=%d", state.event); + udc_controller->stopped = 1; + return 0; +} + +/*----------------------------------------------------------------- + * Invoked on USB resume. May be called in_interrupt. + * Here we start the DR controller and enable the irq + *-----------------------------------------------------------------*/ +static int arcotg_udc_resume(struct device *pdev) +{ + DBG("Resume. pdev=0x%p", pdev); + + /*Enable DR irq reg and set controller Run */ + if (udc_controller->stopped) { + dr_controller_setup(udc_controller->ep_qh, + &udc_controller->gadget.dev); + dr_controller_run(udc_controller); + } + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + return 0; +} + +/*------------------------------------------------------------------------- + Register entry point for the peripheral controller driver +--------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .probe = arcotg_udc_probe, + .remove = __exit_p(arcotg_udc_remove), + .driver = { + .name = driver_name, + }, +}; + +static int __init udc_init(void) +{ + int rc; + + printk(KERN_INFO "%s version %s init \n", driver_desc, DRIVER_VERSION); + rc = platform_driver_register(&udc_driver); + VDBG("%s() platform_driver_register returns %d", __FUNCTION__, rc); + return rc; +} + +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); +} + +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); --- linux-2.6.16.19/drivers/usb/gadget/arcotg_udc.h 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/arcotg_udc.h 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,560 @@ +/* + * driver/usb/gadget/arcotg_udc.h + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Based on mpc-udc.h + * Author: Li Yang (leoli@freescale.com) + * Jiang Bo (Tanya.jiang@freescale.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Freescale USB device/endpoint management registers + */ +#ifndef __ARCOTG_UDC_H +#define __ARCOTG_UDC_H + +#define TRUE 1 +#define FALSE 0 + +/* ### define USB registers here + */ +#define USB_MAX_ENDPOINTS 8 +#define USB_MAX_PIPES (USB_MAX_ENDPOINTS*2) +#define USB_MAX_CTRL_PAYLOAD 64 +#define USB_DR_SYS_OFFSET 0x400 + +#define USB_DR_OFFSET 0x3100 + + /* USB DR device mode registers (Little Endian) */ +struct usb_dr_device { + /* Capability register */ + u32 id; + u32 res1[63]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u32 res2[5]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u32 res3[6]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u32 res4; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u32 res5; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u32 res6[6]; + u32 configflag; /* Configure Flag Register */ + u32 portsc1; /* Port 1 Status and Control Register */ + u32 res7[7]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[8 * 2]; /* Endpoint Control Registers */ +} __attribute__ ((packed)); + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_MASKS (0x3fff) +/* USB CMD Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x00000001) +#define USB_CMD_CTRL_RESET (0x00000002) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x00000010) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x00000020) +#define USB_CMD_INT_AA_DOORBELL (0x00000040) +#define USB_CMD_ASP (0x00000300) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x00000800) +#define USB_CMD_SUTW (0x00002000) +#define USB_CMD_ATDTW (0x00004000) +#define USB_CMD_ITC (0x00FF0000) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x00000000) +#define USB_CMD_FRAME_SIZE_512 (0x00000004) +#define USB_CMD_FRAME_SIZE_256 (0x00000008) +#define USB_CMD_FRAME_SIZE_128 (0x0000000C) +#define USB_CMD_FRAME_SIZE_64 (0x00008000) +#define USB_CMD_FRAME_SIZE_32 (0x00008004) +#define USB_CMD_FRAME_SIZE_16 (0x00008008) +#define USB_CMD_FRAME_SIZE_8 (0x0000800C) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x00000000) +#define USB_CMD_ASP_01 (0x00000100) +#define USB_CMD_ASP_10 (0x00000200) +#define USB_CMD_ASP_11 (0x00000300) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00000000) +#define USB_CMD_ITC_1_MICRO_FRM (0x00010000) +#define USB_CMD_ITC_2_MICRO_FRM (0x00020000) +#define USB_CMD_ITC_4_MICRO_FRM (0x00040000) +#define USB_CMD_ITC_8_MICRO_FRM (0x00080000) +#define USB_CMD_ITC_16_MICRO_FRM (0x00100000) +#define USB_CMD_ITC_32_MICRO_FRM (0x00200000) +#define USB_CMD_ITC_64_MICRO_FRM (0x00400000) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB STS Register Bit Masks */ +#define USB_STS_INT (0x00000001) +#define USB_STS_ERR (0x00000002) +#define USB_STS_PORT_CHANGE (0x00000004) +#define USB_STS_FRM_LST_ROLL (0x00000008) +#define USB_STS_SYS_ERR (0x00000010) +#define USB_STS_IAA (0x00000020) +#define USB_STS_RESET (0x00000040) +#define USB_STS_SOF (0x00000080) +#define USB_STS_SUSPEND (0x00000100) +#define USB_STS_HC_HALTED (0x00001000) +#define USB_STS_RCL (0x00002000) +#define USB_STS_PERIODIC_SCHEDULE (0x00004000) +#define USB_STS_ASYNC_SCHEDULE (0x00008000) + +/* USB INTR Register Bit Masks */ +#define USB_INTR_INT_EN (0x00000001) +#define USB_INTR_ERR_INT_EN (0x00000002) +#define USB_INTR_PTC_DETECT_EN (0x00000004) +#define USB_INTR_FRM_LST_ROLL_EN (0x00000008) +#define USB_INTR_SYS_ERR_EN (0x00000010) +#define USB_INTR_ASYN_ADV_EN (0x00000020) +#define USB_INTR_RESET_EN (0x00000040) +#define USB_INTR_SOF_EN (0x00000080) +#define USB_INTR_DEVICE_SUSPEND (0x00000100) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0xFE000000) +#define USB_DEVICE_ADDRESS_BIT_POS (25) + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_MASK (0xfffff800) + +/* PORTSCX Register Bit Masks */ +#define PORTSCX_CURRENT_CONNECT_STATUS (0x00000001) +#define PORTSCX_CONNECT_STATUS_CHANGE (0x00000002) +#define PORTSCX_PORT_ENABLE (0x00000004) +#define PORTSCX_PORT_EN_DIS_CHANGE (0x00000008) +#define PORTSCX_OVER_CURRENT_ACT (0x00000010) +#define PORTSCX_OVER_CURRENT_CHG (0x00000020) +#define PORTSCX_PORT_FORCE_RESUME (0x00000040) +#define PORTSCX_PORT_SUSPEND (0x00000080) +#define PORTSCX_PORT_RESET (0x00000100) +#define PORTSCX_LINE_STATUS_BITS (0x00000C00) +#define PORTSCX_PORT_POWER (0x00001000) +#define PORTSCX_PORT_INDICTOR_CTRL (0x0000C000) +#define PORTSCX_PORT_TEST_CTRL (0x000F0000) +#define PORTSCX_WAKE_ON_CONNECT_EN (0x00100000) +#define PORTSCX_WAKE_ON_CONNECT_DIS (0x00200000) +#define PORTSCX_WAKE_ON_OVER_CURRENT (0x00400000) +#define PORTSCX_PHY_LOW_POWER_SPD (0x00800000) +#define PORTSCX_PORT_FORCE_FULL_SPEED (0x01000000) +#define PORTSCX_PORT_SPEED_MASK (0x0C000000) +#define PORTSCX_PORT_WIDTH (0x10000000) +#define PORTSCX_PHY_TYPE_SEL (0xC0000000) + +/* bit 11-10 are line status */ +#define PORTSCX_LINE_STATUS_SE0 (0x00000000) +#define PORTSCX_LINE_STATUS_JSTATE (0x00000400) +#define PORTSCX_LINE_STATUS_KSTATE (0x00000800) +#define PORTSCX_LINE_STATUS_UNDEF (0x00000C00) +#define PORTSCX_LINE_STATUS_BIT_POS (10) + +/* bit 15-14 are port indicator control */ +#define PORTSCX_PIC_OFF (0x00000000) +#define PORTSCX_PIC_AMBER (0x00004000) +#define PORTSCX_PIC_GREEN (0x00008000) +#define PORTSCX_PIC_UNDEF (0x0000C000) +#define PORTSCX_PIC_BIT_POS (14) + +/* bit 19-16 are port test control */ +#define PORTSCX_PTC_DISABLE (0x00000000) +#define PORTSCX_PTC_JSTATE (0x00010000) +#define PORTSCX_PTC_KSTATE (0x00020000) +#define PORTSCX_PTC_SEQNAK (0x00030000) +#define PORTSCX_PTC_PACKET (0x00040000) +#define PORTSCX_PTC_FORCE_EN (0x00050000) +#define PORTSCX_PTC_BIT_POS (16) + +/* bit 27-26 are port speed */ +#define PORTSCX_PORT_SPEED_FULL (0x00000000) +#define PORTSCX_PORT_SPEED_LOW (0x04000000) +#define PORTSCX_PORT_SPEED_HIGH (0x08000000) +#define PORTSCX_PORT_SPEED_UNDEF (0x0C000000) +#define PORTSCX_SPEED_BIT_POS (26) + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSCX_PTW (0x10000000) +#define PORTSCX_PTW_8BIT (0x00000000) +#define PORTSCX_PTW_16BIT (0x10000000) + +/* bit 31-30 are port transceiver select */ +#define PORTSCX_PTS_UTMI (0x00000000) +#define PORTSCX_PTS_ULPI (0x80000000) +#define PORTSCX_PTS_FSLS (0xC0000000) +#define PORTSCX_PTS_BIT_POS (30) + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x00000000) +#define USB_MODE_CTRL_MODE_DEVICE (0x00000002) +#define USB_MODE_CTRL_MODE_HOST (0x00000003) +#define USB_MODE_CTRL_MODE_RSV (0x00000001) +#define USB_MODE_SETUP_LOCK_OFF (0x00000008) +#define USB_MODE_STREAM_DISABLE (0x00000010) +/* Endpoint Flush Register */ +#define EPFLUSH_TX_OFFSET (0x00010000) +#define EPFLUSH_RX_OFFSET (0x00000000) + +/* Endpoint Setup Status bit masks */ +#define EP_SETUP_STATUS_MASK (0x0000003F) +#define EP_SETUP_STATUS_EP0 (0x00000001) + +/* ENDPOINTCTRLx Register Bit Masks */ +#define EPCTRL_TX_ENABLE (0x00800000) +#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000) /* Not EP0 */ +#define EPCTRL_TX_DATA_TOGGLE_INH (0x00200000) /* Not EP0 */ +#define EPCTRL_TX_TYPE (0x000C0000) +#define EPCTRL_TX_DATA_SOURCE (0x00020000) /* Not EP0 */ +#define EPCTRL_TX_EP_STALL (0x00010000) +#define EPCTRL_RX_ENABLE (0x00000080) +#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040) /* Not EP0 */ +#define EPCTRL_RX_DATA_TOGGLE_INH (0x00000020) /* Not EP0 */ +#define EPCTRL_RX_TYPE (0x0000000C) +#define EPCTRL_RX_DATA_SINK (0x00000002) /* Not EP0 */ +#define EPCTRL_RX_EP_STALL (0x00000001) + +/* bit 19-18 and 3-2 are endpoint type */ +#define EPCTRL_EP_TYPE_CONTROL (0) +#define EPCTRL_EP_TYPE_ISO (1) +#define EPCTRL_EP_TYPE_BULK (2) +#define EPCTRL_EP_TYPE_INTERRUPT (3) +#define EPCTRL_TX_EP_TYPE_SHIFT (18) +#define EPCTRL_RX_EP_TYPE_SHIFT (2) + +/* SNOOPn Register Bit Masks */ +#define SNOOP_ADDRESS_MASK (0xFFFFF000) +#define SNOOP_SIZE_ZERO (0x00) /* snooping disable */ +#define SNOOP_SIZE_4KB (0x0B) /* 4KB snoop size */ +#define SNOOP_SIZE_8KB (0x0C) +#define SNOOP_SIZE_16KB (0x0D) +#define SNOOP_SIZE_32KB (0x0E) +#define SNOOP_SIZE_64KB (0x0F) +#define SNOOP_SIZE_128KB (0x10) +#define SNOOP_SIZE_256KB (0x11) +#define SNOOP_SIZE_512KB (0x12) +#define SNOOP_SIZE_1MB (0x13) +#define SNOOP_SIZE_2MB (0x14) +#define SNOOP_SIZE_4MB (0x15) +#define SNOOP_SIZE_8MB (0x16) +#define SNOOP_SIZE_16MB (0x17) +#define SNOOP_SIZE_32MB (0x18) +#define SNOOP_SIZE_64MB (0x19) +#define SNOOP_SIZE_128MB (0x1A) +#define SNOOP_SIZE_256MB (0x1B) +#define SNOOP_SIZE_512MB (0x1C) +#define SNOOP_SIZE_1GB (0x1D) +#define SNOOP_SIZE_2GB (0x1E) /* 2GB snoop size */ + +/* pri_ctrl Register Bit Masks */ +#define PRI_CTRL_PRI_LVL1 (0x0000000C) +#define PRI_CTRL_PRI_LVL0 (0x00000003) + +/* si_ctrl Register Bit Masks */ +#define SI_CTRL_ERR_DISABLE (0x00000010) +#define SI_CTRL_IDRC_DISABLE (0x00000008) +#define SI_CTRL_RD_SAFE_EN (0x00000004) +#define SI_CTRL_RD_PREFETCH_DISABLE (0x00000002) +#define SI_CTRL_RD_PREFEFETCH_VAL (0x00000001) + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB (0x00000004) +#define USB_CTRL_ULPI_INT0EN (0x00000001) + +/* Endpoint Queue Head data struct + * Rem: all the variables of qh are LittleEndian Mode + * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr + */ +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len + and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ + u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ + u32 res1; + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; /* pad out to 64 bytes */ +}; + +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS (30) +#define EP_QUEUE_HEAD_ZLT_SEL (0x20000000) +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS (16) +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS (0x00008000) +#define EP_QUEUE_HEAD_NEXT_TERMINATE (0x00000001) +#define EP_QUEUE_HEAD_IOC (0x00008000) +#define EP_QUEUE_HEAD_MULTO (0x00000C00) +#define EP_QUEUE_HEAD_STATUS_HALT (0x00000040) +#define EP_QUEUE_HEAD_STATUS_ACTIVE (0x00000080) +#define EP_QUEUE_CURRENT_OFFSET_MASK (0x00000FFF) +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK (0xFFFFFFE0) +#define EP_QUEUE_FRINDEX_MASK (0x000007FF) +#define EP_MAX_LENGTH_TRANSFER (0x4000) + +/* Endpoint Transfer Descriptor data struct */ +/* Rem: all the variables of td are LittleEndian Mode */ +struct ep_td_struct { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set + indicate invalid */ + u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; /* make it an even 8 words */ +}; + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE (0x00000001) +#define DTD_IOC (0x00008000) +#define DTD_STATUS_ACTIVE (0x00000080) +#define DTD_STATUS_HALTED (0x00000040) +#define DTD_STATUS_DATA_BUFF_ERR (0x00000020) +#define DTD_STATUS_TRANSACTION_ERR (0x00000008) +#define DTD_RESERVED_FIELDS (0x80007300) +#define DTD_ADDR_MASK (0xFFFFFFE0) +#define DTD_PACKET_SIZE (0x7FFF0000) +#define DTD_LENGTH_BIT_POS (16) +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) + +/* -----------------------------------------------------------------------*/ +/* ##### enum data +*/ +typedef enum { + e_ULPI, + e_UTMI_8BIT, + e_UTMI_16BIT, + e_SERIAL +} e_PhyInterface; + +/*-------------------------------------------------------------------------*/ + +/* ### driver private data, Need rework + */ +struct arcotg_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct arcotg_ep *ep; + unsigned mapped; + + struct ep_td_struct *head, *tail; /* For dTD List + this is a BigEndian Virtual addr */ + unsigned int dtd_count; +}; + +#define REQ_UNCOMPLETE (1) + +struct arcotg_ep { + struct usb_ep ep; + struct list_head queue; + struct arcotg_udc *udc; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + u8 already_seen; + u8 setup_stage; + u32 last_io; /* timestamp */ + + char name[14]; +#if 0 + u16 maxpacket; + u8 bEndpointAddress; + u8 bmAttributes; +#endif + unsigned double_buf:1; + unsigned stopped:1; + unsigned fnf:1; + unsigned has_dma:1; + u8 ackwait; + u8 dma_channel; + u16 dma_counter; + int lch; + + struct timer_list timer; + +}; + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +struct arcotg_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct arcotg_ep eps[USB_MAX_ENDPOINTS * 2]; + struct usb_ctrlrequest local_setup_buff; + spinlock_t lock; + struct arc_usb_config *config; + u32 xcvr_type; + struct otg_transceiver *transceiver; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned stopped:1; + + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct arcotg_req *status_req; /* ep0 status request */ + + u32 max_pipes; /* Device max pipes */ + u32 max_use_endpts; /* Max endpointes to be used */ + u32 bus_reset; /* Device is bus reseting */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ + u32 usb_next_state; /* USB next state */ + u32 ep0_state; /* Enpoint zero state */ + u32 ep0_dir; /* Enpoint zero direction: can be + USB_DIR_IN or USB_DIR_OUT */ + u32 usb_sof_count; /* SOF count */ + u32 errors; /* USB ERRORs count */ + u8 device_address; /* Device USB address */ + + struct completion *done; /* to make sure release() is done */ +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +# if 0 /* jiffie-timestamped version */ +# define DBG(fmt, args...) \ + printk("j=%lu [%s] " fmt "\n", jiffies, __FUNCTION__, ## args) +# else +# define DBG(fmt, args...) \ + printk("[%s] " fmt "\n", __FUNCTION__, ## args) +# endif +#else +#define DBG(fmt, args...) do{}while(0) +#endif + +#if 0 +static void dump_msg(const char *label, const u8 * buf, unsigned int length) +{ + unsigned int start, num, i; + char line[52], *p; + + if (length >= 512) + return; + DBG("%s, length %u:\n", label, length); + start = 0; + while (length > 0) { + num = min(length, 16u); + p = line; + for (i = 0; i < num; ++i) { + if (i == 8) + *p++ = ' '; + sprintf(p, " %02x", buf[i]); + p += 3; + } + *p = 0; + printk(KERN_DEBUG "%6x: %s\n", start, line); + buf += num; + start += num; + length -= num; + } +} +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +/*-------------------------------------------------------------------------*/ + +/* ### Add board specific defines here + */ + +/* + * ### pipe direction macro from device view + */ +#define USB_RECV (0) /* OUT EP */ +#define USB_SEND (1) /* IN EP */ + +/* + * ### internal used help routines. + */ +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN ):((EP)->desc->bEndpointAddress \ + & USB_DIR_IN)==USB_DIR_IN) + +#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \ + &udc->eps[pipe]) +/* + * memory kmalloc with alignning size and zero the content + * @base :output parameter. It store the base address before align. + * Return value it the address after align + * + */ +static void *KMALLOC_ALIGN(size_t size, int flags, unsigned int align, + void **base) +{ +/* #define MY_ALIGN(n) ((n)+(-(n) & (align - 1))) */ + + *base = kmalloc(size + align, flags); + if (*base == NULL) + return NULL; + memset(*base, 0, (size + align)); + return (void *)ALIGN((unsigned int)(*base), align); +} + +/* Bulk only class request */ +#define USB_BULK_RESET_REQUEST 0xff + +#endif /* __ARCOTG_UDC_H */ --- linux-2.6.16.19/drivers/usb/gadget/ether.c 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/ether.c 2006-06-07 10:49:18.000000000 -0600 @@ -253,6 +253,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethern #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_ARC +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. --- linux-2.6.16.19/drivers/usb/gadget/gadget_chips.h 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/gadget_chips.h 2006-06-07 10:49:18.000000000 -0600 @@ -75,6 +75,12 @@ #define gadget_is_pxa27x(g) 0 #endif +#ifdef CONFIG_USB_GADGET_ARC +#define gadget_is_arcotg(g) !strcmp("arc_udc", (g)->name) +#else +#define gadget_is_arcotg(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_S3C2410 #define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) #else @@ -143,5 +149,7 @@ static inline int usb_gadget_controller_ return 0x13; else if (gadget_is_imx(gadget)) return 0x14; + else if (gadget_is_arcotg(gadget)) + return 0x18; return -ENOENT; } --- linux-2.6.16.19/drivers/usb/gadget/Kconfig 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/Kconfig 2006-06-07 10:49:18.000000000 -0600 @@ -176,16 +176,28 @@ config USB_OMAP default USB_GADGET select USB_GADGET_SELECTED -config USB_OTG - boolean "OTG Support" - depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD +config USB_GADGET_ARC + boolean "ARC USB Device Controller" + depends on ARCH_MXC + select USB_GADGET_DUALSPEED + select USB_GADGET_ARC_OTGHS help - The most notable feature of USB OTG is support for a - "Dual-Role" device, which can act as either a device - or a host. The initial role choice can be changed - later, when two dual-role devices talk to each other. + Some Freescale processors have an ARC High Speed + USBOTG controller, which supports device mode. + This driver supports the controller in the i.MX27 or i.MX31, + and should work with controllers + in other Freescale processors too, given minor tweaks. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "arc_udc" and force all + gadget drivers to also be dynamically linked. - Select this only if your OMAP board has a Mini-AB connector. +config USB_ARC + tristate + depends on USB_GADGET_ARC + select ISP1504_MXC + default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_DUMMY_HCD @@ -222,6 +234,18 @@ config USB_DUMMY_HCD endchoice +config USB_OTG + boolean "OTG Support" + depends on (USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD) || \ + (USB_GADGET_ARC && (ARCH_MX3 || ARCH_MX27) && USB_EHCI_HCD) + help + The most notable feature of USB OTG is support for a + "Dual-Role" device, which can act as either a device + or a host. The initial role choice can be changed + later, when two dual-role devices talk to each other. + + Select this only if your board has a Mini-AB connector. + config USB_GADGET_DUALSPEED bool depends on USB_GADGET @@ -230,6 +254,33 @@ config USB_GADGET_DUALSPEED Means that gadget drivers should include extra descriptors and code to handle dual-speed controllers. +choice + prompt "Select OTG transceiver" + depends on USB_GADGET_ARC + +config USB_GADGET_ARC_OTGFS + bool "Full Speed" + # disable until FS is actually possible. + depends on 0 + help + The ARC OTG controller can be connected to either a FS or + a HS transceiver. + + Enable this configuration option if you want to use the HS + transceiver. + +config USB_GADGET_ARC_OTGHS + bool "High Speed" + help + The ARC OTG controller can be connected to either a FS or + a HS transceiver. + + Enable this configuration option if you want to use the HS + transceiver. + +endchoice + + # # USB Gadget Drivers # --- linux-2.6.16.19/drivers/usb/gadget/Makefile 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/gadget/Makefile 2006-06-07 10:49:18.000000000 -0600 @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +obj-$(CONFIG_USB_ARC) += arcotg_udc.o # # USB gadget drivers --- linux-2.6.16.19/drivers/usb/host/ehci-arc.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/host/ehci-arc.c 2006-06-21 11:21:16.000000000 -0600 @@ -0,0 +1,522 @@ +/* + * drivers/usb/host/ehci-arc.c + * + * Copyright 2005-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* Note: this file is #included by ehci-hcd.c */ + +#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC +MODULE_DESCRIPTION("ARC EHCI USB Host Controller Driver"); + +#include +#include +#include +#include +#include + +#include + +#undef dbg +#undef vdbg + +#if 0 +#define dbg printk +#else +#define dbg(fmt, ...) do {} while (0) +#endif + +#if 0 +#define vdbg dbg +#else +#define vdbg(fmt, ...) do {} while (0) +#endif + +static char *enp[] __initdata = { NULL, NULL, NULL }; +static int enp_nargs __initdata = 0; +static int g_enable_h1 = 0, g_enable_h2 = 0, g_enable_otg = 0; +module_param_array_named(port, enp, charp, &enp_nargs, 0); +MODULE_PARM_DESC(port, "Describes the ports to enable (h1,h2,otg)"); + +/* + * Parse module parameters. + * + * The "port" parameter can be used to specify which + * port(s) to use. It's a comma-separated list which + * can include any of "h1", "h2", or "otg". If a port + * is specified, and it's enabled via Kconfig, it will + * be used. If no port parameter is given, all ports + * specified at Kconfig time will be used. + */ +static void parse_modparams(void) +{ + int i; + + dbg("n_ports=%d\n", enp_nargs); + if (!enp_nargs ) { + g_enable_h1 = 1; + g_enable_h2 = 1; + g_enable_otg = 1; + } else { + for (i=0; i < enp_nargs; i++) { + dbg("%s\n", enp[i]); + if (!strcmp(enp[i],"h1")) g_enable_h1 = 1; + if (!strcmp(enp[i],"h2")) g_enable_h2 = 1; + if (!strcmp(enp[i],"otg")) g_enable_otg = 1; + } + } + dbg("H1=%d H2=%d OTG=%d\n", + g_enable_h1, g_enable_h2, g_enable_otg); +} + +static int ehci_hcd_arc_probe(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct arc_usb_config *config; + int retval; + + config = (struct arc_usb_config *)pdev->dev.platform_data; + if (!config) { + dev_err(&pdev->dev, + "No platform data for %s.\n", pdev->dev.bus_id); + return -ENODEV; + } + + if (pdev->resource[0].flags != IORESOURCE_MEM || + pdev->resource[1].flags != IORESOURCE_IRQ) { + dev_err(&pdev->dev, + "Incorrect platform resources for %s.\n", + pdev->dev.bus_id); + return -ENODEV; + } + + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (config->platform_init() != 0) { + retval = -EINVAL; + goto err1; + } + + hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + retval = -ENOMEM; + goto err2; + } + + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + vdbg(" start=0x%lx end=0x%lx\n", + pdev->resource[0].start, pdev->resource[0].end); + vdbg("rsrc_start=0x%llx rsrc_len=0x%llx\n", + hcd->rsrc_start, hcd->rsrc_len); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start + 0x100); + hcd->irq = pdev->resource[1].start; + hcd->power_budget = config->power_budget; + + vdbg("hcd->regs=0x%p hcd->irq=%d\n", hcd->regs, hcd->irq); + + retval = usb_add_hcd(hcd, hcd->irq, SA_SHIRQ); + if (retval != 0) { + goto err3; + } + +#if defined(CONFIG_USB_OTG) + if (config->does_otg) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + vdbg("pdev=0x%p hcd=0x%p ehci=0x%p\n", pdev, hcd, ehci); + + ehci->transceiver=otg_get_transceiver(); + + if (ehci->transceiver) { + retval = otg_set_host(ehci->transceiver, + &ehci_to_hcd(ehci)->self); + dev_dbg(ehci->transceiver->dev, + "init %s transceiver, retval %d\n", + ehci->transceiver->label,retval); + if (retval) { + if (ehci->transceiver) + put_device(ehci->transceiver->dev); + goto err3; + } + } else { + printk(KERN_ERR "can't find transceiver\n"); + retval = -ENODEV; + goto err3; + } + } +#endif + + return retval; + + err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + err2: + config->platform_uninit(); + err1: + dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval); + return retval; +} + +static void ehci_hcd_arc_remove(struct usb_hcd *hcd, + struct platform_device *pdev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct arc_usb_config *config; + config = (struct arc_usb_config *)pdev->dev.platform_data; + + dbg("%s pdev=0x%p config=0x%p\n", __FUNCTION__, pdev, config); + + if (HC_IS_RUNNING(hcd->state)) + hcd->state = HC_STATE_QUIESCING; + + if (ehci->transceiver) { + (void) otg_set_host(ehci->transceiver, 0); + put_device(ehci->transceiver->dev); + } + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + /* + * do platform specific un-initialization: + * release iomux pins, etc. + */ + config->platform_uninit(); +} + +static void ehci_arc_reset(struct ehci_hcd *ehci) +{ + struct device *dev = ehci_to_hcd(ehci)->self.controller; + struct arc_usb_config *config = + (struct arc_usb_config *)dev->platform_data; + + dbg("%s ehci->regs=0x%p\n", __FUNCTION__, ehci->regs); + dbg("%s config=0x%p config->name=%s\n", __FUNCTION__, + config, config->name); + + writel(USBMODE_CM_HOST, config->usbmode); + + dbg("%s(): set %s xcvr=0x%x usbmode=0x%x portsc1 at 0x%p\n", + __FUNCTION__, config->name, config->xcvr_type, + config->usbmode, &ehci->regs->port_status[0]); +} + +static int arc_usb_setup(struct usb_hcd *hcd) +{ + struct arc_usb_config *config; + struct device *dev = hcd->self.controller; + int retval = 0; + + config = (struct arc_usb_config *)dev->platform_data; + if (!config) { + dev_err(dev, "No platform data for %s.\n", dev->bus_id); + return -ENODEV; + } + + return retval; +} + +/* called after powerup, by probe or system-pm "wakeup" */ +static int ehci_hcd_arc_reinit(struct ehci_hcd *ehci) +{ + int retval = 0; + + if ((retval = arc_usb_setup(ehci_to_hcd(ehci)))) { + return retval; + } + ehci_port_power(ehci, 0); + + return retval; +} + +/* called during probe() after chip reset completes */ +static int ehci_hcd_arc_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + int retval; + + /* EHCI registers start at offset 0x00 */ + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + vdbg("caps=0x%p regs=0x%p\n", ehci->caps, ehci->regs); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + ehci_arc_reset(ehci); + + /* cache this readonly data; minimize reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->is_tdi_rh_tt = 1; + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + retval = ehci_hcd_arc_reinit(ehci); + + return retval; +} + +static const struct hc_driver ehci_arc_hc_driver = { + .description = hcd_name, + .product_desc = "ARC EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_hcd_arc_setup, + .start = ehci_run, + .stop = ehci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +}; + + +/* This following code is used only for ARC USB OTG module*/ + +volatile static struct ehci_regs usb_ehci_regs; + +/* suspend/resume, section 4.3 */ + +/* These routines rely on the bus (pci, platform, etc) + * to handle powerdown and wakeup, and currently also on + * transceivers that don't need any software attention to set up + * the right sort of wakeup. + * + * They're also used for turning on/off the port when doing OTG. + */ +static int ehci_arc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct arc_usb_config *config = + (struct arc_usb_config *)pdev->dev.platform_data; + u32 cmd; + + dbg("%s pdev=0x%p config=0x%p ehci=0x%p hcd=0x%p\n", + __FUNCTION__, pdev, config, ehci, hcd); + dbg("%s ehci->regs=0x%p hcd->regs=0x%p hcd->state=%d\n", + __FUNCTION__, ehci->regs, hcd->regs, hcd->state); + + hcd->state = HC_STATE_HALT; /* ignore non-host interrupts */ + + cmd = readl(&ehci->regs->command); + cmd &= ~CMD_RUN; + writel(cmd, &ehci->regs->command); + + memcpy((void *)&usb_ehci_regs, ehci->regs, sizeof(struct ehci_regs)); + usb_ehci_regs.port_status[0] &= + cpu_to_le32(~(PORT_PEC | PORT_OCC | PORT_CSC)); + + if (config->set_vbus_power) + config->set_vbus_power(0); + + return 0; +} + +static int ehci_arc_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 cmd; + struct arc_usb_config *config = + (struct arc_usb_config *)pdev->dev.platform_data; + + dbg("%s pdev=0x%p config=0x%p ehci=0x%p hcd=0x%p\n", + __FUNCTION__, pdev, config, ehci, hcd); + vdbg("%s ehci->regs=0x%p hcd->regs=0x%p\n", + __FUNCTION__, ehci->regs, hcd->regs); + + + writel(USBMODE_CM_HOST, config->usbmode); + memcpy(ehci->regs, (void *)&usb_ehci_regs, sizeof(struct ehci_regs)); + + hcd->state = HC_STATE_RUNNING; + + cmd = readl(&ehci->regs->command); + cmd |= CMD_RUN; + writel(cmd, &ehci->regs->command); + + if (config->set_vbus_power) + config->set_vbus_power(1); + + return 0; +} + +static int ehci_hcd_drv_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return ehci_hcd_arc_probe(&ehci_arc_hc_driver, pdev); +} + +static int __init_or_module ehci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + ehci_hcd_arc_remove(hcd, pdev); + + return 0; +} + +#if defined(CONFIG_USB_EHCI_ARC_H1) +static struct platform_driver arc_ehcih1_driver = { + .probe = ehci_hcd_drv_probe, + .remove = ehci_hcd_drv_remove, + + .suspend = ehci_arc_suspend, + .resume = ehci_arc_resume , + .driver = { + .name = "ehci_h1", + }, +}; +#endif /* CONFIG_USB_EHCI_ARC_H1 */ + +#if defined(CONFIG_USB_EHCI_ARC_H2) +static struct platform_driver arc_ehcih2_driver = { + .probe = ehci_hcd_drv_probe, + .remove = ehci_hcd_drv_remove, + + .suspend = ehci_arc_suspend, + .resume = ehci_arc_resume , + .driver = { + .name = "ehci_h2", + }, +}; +#endif /* CONFIG_USB_EHCI_ARC_H2 */ + +#if defined(CONFIG_USB_EHCI_ARC_OTGHS) +static struct platform_driver arc_ehci_otg_hs_driver = { + .probe = ehci_hcd_drv_probe, + .remove = ehci_hcd_drv_remove, + + .suspend = ehci_arc_suspend, + .resume = ehci_arc_resume, + .driver = { + .name = "ehci_otghs", + }, +}; +#endif /* CONFIG_USB_EHCI_ARC_OTGHS */ + +/*-------------------------------------------------------------------------*/ + +static int __init arc_ehci_init(void) +{ + int err = 0; + if (usb_disabled()) + return -ENODEV; + + parse_modparams(); + +#if defined(CONFIG_USB_EHCI_ARC_H1) + if (g_enable_h1) { + err = platform_driver_register(&arc_ehcih1_driver); + if (err) + return err; + } +#endif /* CONFIG_USB_EHCI_ARC_H1 */ + +#if defined(CONFIG_USB_EHCI_ARC_H2) + if (g_enable_h2) { + err = platform_driver_register(&arc_ehcih2_driver); + if (err) + return err; + } +#endif /* CONFIG_USB_EHCI_ARC_H2 */ + +#if defined(CONFIG_USB_EHCI_ARC_OTGHS) + if (g_enable_otg) { + err = platform_driver_register(&arc_ehci_otg_hs_driver); + if (err) + return err; + } +#endif /* CONFIG_USB_EHCI_ARC_OTGHS */ + + return err; +} + +static void __exit arc_ehci_exit(void) +{ + dbg("driver_unregister %s, %s\n", hcd_name, DRIVER_VERSION); + +#if defined(CONFIG_USB_EHCI_ARC_H1) + if (g_enable_h1) + platform_driver_unregister(&arc_ehcih1_driver); +#endif + +#if defined(CONFIG_USB_EHCI_ARC_H2) + if (g_enable_h2) + platform_driver_unregister(&arc_ehcih2_driver); +#endif + +#if defined(CONFIG_USB_EHCI_ARC_OTGHS) + if (g_enable_otg) + platform_driver_unregister(&arc_ehci_otg_hs_driver); +#endif + +} + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +module_init(arc_ehci_init); +module_exit(arc_ehci_exit); --- linux-2.6.16.19/drivers/usb/host/ehci-hcd.c 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/host/ehci-hcd.c 2006-06-07 10:49:18.000000000 -0600 @@ -881,6 +881,9 @@ static int ehci_get_frame (struct usb_hc /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_EHCI_ARC +#include "ehci-arc.c" +#else #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC MODULE_DESCRIPTION (DRIVER_INFO); @@ -894,3 +897,4 @@ MODULE_LICENSE ("GPL"); #if !defined(CONFIG_PCI) #error "missing bus glue for ehci-hcd" #endif +#endif /* CONFIG_USB_EHCI_ARC */ --- linux-2.6.16.19/drivers/usb/host/ehci.h 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/host/ehci.h 2006-06-07 10:49:18.000000000 -0600 @@ -98,6 +98,13 @@ struct ehci_hcd { /* one per controlle # define COUNT(x) do {} while (0) #endif u8 sbrn; /* packed release number */ + + /* + * OTG controllers and transceivers need software interaction; + * other external transceivers should be software-transparent + */ + struct otg_transceiver *transceiver; + u32 power_budget; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ --- linux-2.6.16.19/drivers/usb/host/Kconfig 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/host/Kconfig 2006-06-07 10:49:18.000000000 -0600 @@ -6,7 +6,9 @@ comment "USB Host Controller Drivers" config USB_EHCI_HCD tristate "EHCI HCD (USB 2.0) support" - depends on USB && PCI + depends on USB + select ISP1504_MXC if MACH_MX31ADS && \ + (USB_EHCI_ARC_OTGHS || USB_EHCI_ARC_H2) ---help--- The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. @@ -29,6 +31,38 @@ config USB_EHCI_HCD To compile this driver as a module, choose M here: the module will be called ehci-hcd. +config USB_EHCI_ARC + bool "Support for ARC controller" + depends on USB_EHCI_HCD && ARCH_MXC + ---help--- + Some Freescale processors have an ARC High Speed + USBOTG controller, which supports EHCI host mode. + + This driver supports the controller in the i.MX27 or i.MX31, + and should work with controllers in other Freescale + processors too, given minor tweaks. + + Say "y" here to add support for this controller + to the EHCI HCD driver. + +config USB_EHCI_ARC_H1 + bool "Support for Host1 port on ARC controller" + depends on USB_EHCI_ARC + ---help--- + Enable support for the USB Host1 port. + +config USB_EHCI_ARC_H2 + bool "Support for Host2 port on ARC controller" + depends on USB_EHCI_ARC + ---help--- + Enable support for the USB Host2 port. + +config USB_EHCI_ARC_OTGHS + bool "Support for OTG HS Host port on ARC controller" + depends on USB_EHCI_ARC + ---help--- + Enable support for the USB OTG port in HS Host mode. + config USB_EHCI_SPLIT_ISO bool "Full speed ISO transactions (EXPERIMENTAL)" depends on USB_EHCI_HCD && EXPERIMENTAL --- linux-2.6.16.19/drivers/usb/Makefile 2006-03-19 22:53:29.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/Makefile 2006-06-07 10:49:18.000000000 -0600 @@ -78,3 +78,5 @@ obj-$(CONFIG_USB_SISUSBVGA) += misc/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ + +obj-$(CONFIG_USB_OTG) += otg/ --- linux-2.6.16.19/drivers/usb/otg/fsl_otg.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/otg/fsl_otg.c 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,972 @@ +/* + * Copyright 2005-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "fsl_otg.h" + +#ifdef DEBUG +#include +u8 isp1504_read( int reg, volatile u32 *view); +#endif + +#define CONFIG_USB_OTG_DEBUG_FILES +#define DRIVER_VERSION "Revision: 1.0" +#define DRIVER_AUTHOR "Jerry Huang/Leo Li" +#define DRIVER_DESC "ISP1504 USB OTG Driver" +#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC + +MODULE_DESCRIPTION("ARC USB/ISP1504 OTG Transceiver Driver"); + +static const char otg_dr_name[] = "isp1504_arc"; + +#undef HA_DATA_PULSE + +volatile static struct usb_dr_mmap *usb_dr_regs; +static struct fsl_otg *fsl_otg_dev = NULL; +static int srp_wait_done; + +/* FSM timers */ +struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr; + +/* Driver specific timers */ +struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, + *b_srp_wait_tmr, *a_wait_enum_tmr; + +static struct list_head active_timers; + +static struct fsl_otg_config fsl_otg_initdata = { + .otg_port = 1, +}; + + +/** + * usb_bus_start_enum - start immediate enumeration (for OTG) + * @bus: the bus (must use hcd framework) + * @port: 1-based number of port; usually bus->otg_port + * Context: in_interrupt() + * + * Starts enumeration, with an immediate reset followed later by + * khubd identifying and possibly configuring the device. + * This is needed by OTG controller drivers, where it helps meet + * HNP protocol timing requirements for starting a port reset. + */ + +#include "../../../drivers/usb/core/hcd.h" + +int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) +{ + struct usb_hcd *hcd; + int status = -EOPNOTSUPP; + + /* NOTE: since HNP can't start by grabbing the bus's address0_sem, + * boards with root hubs hooked up to internal devices (instead of + * just the OTG port) may need more attention to resetting... + */ + + hcd = container_of (bus, struct usb_hcd, self); + if (port_num && hcd->driver->start_port_reset) + status = hcd->driver->start_port_reset(hcd, port_num); + + /* run khubd shortly after (first) root port reset finishes; + * it may issue others, until at least 50 msecs have passed. + */ + if (status == 0) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10)); + + return status; +} + +int write_ulpi(u8 addr, u8 data) +{ + u32 temp; + temp = 0x60000000 | (addr << 16) | data; + temp = cpu_to_le32(temp); + usb_dr_regs->ulpiview = temp; + return 0; +} + +/* prototype declaration */ +void fsl_otg_add_timer(void *timer); +void fsl_otg_del_timer(void *timer); + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ + +/* Charge vbus for vbus pulsing in SRP */ +void fsl_otg_chrg_vbus(int on) +{ + if (on) + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK & + ~OTGSC_CTRL_VBUS_DISCHARGE) | + OTGSC_CTRL_VBUS_CHARGE); + else + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK & ~OTGSC_CTRL_VBUS_CHARGE)); +} + +/* Discharge vbus through a resistor to ground */ +void fsl_otg_dischrg_vbus(int on) +{ + if (on) + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK) + | OTGSC_CTRL_VBUS_DISCHARGE); + else + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK & + ~OTGSC_CTRL_VBUS_DISCHARGE)); +} + +/* A-device driver vbus, controlled through PP bit in PORTSC */ +void fsl_otg_drv_vbus(int on) +{ + if (on) + usb_dr_regs->portsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS) | PORTSC_PORT_POWER); + else + usb_dr_regs->portsc = + cpu_to_le32(le32_to_cpu(usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER); + +} + +/* Pull-up D+, signalling connect by periperal. Also used in + * data-line pulsing in SRP */ +void fsl_otg_loc_conn(int on) +{ + if (on) + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK) | OTGSC_CTRL_DATA_PULSING); + else + usb_dr_regs->otgsc = + cpu_to_le32(le32_to_cpu(usb_dr_regs->otgsc) & + ~OTGSC_INTSTS_MASK & ~OTGSC_CTRL_DATA_PULSING); +} + +/* Generate SOF by host. This is controlled through suspend/resume the + * port. In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +void fsl_otg_loc_sof(int on) +{ +} + +/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ +void fsl_otg_start_pulse(void) +{ + srp_wait_done = 0; +#ifdef HA_DATA_PULSE + usb_dr_regs->otgsc = + cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK) + | OTGSC_HA_DATA_PULSE); +#else + fsl_otg_loc_conn(1); +#endif + + fsl_otg_add_timer(b_data_pulse_tmr); +} + +void fsl_otg_pulse_vbus(void); + +void b_data_pulse_end(unsigned long foo) +{ +#ifdef HA_DATA_PULSE +#else + fsl_otg_loc_conn(0); +#endif + + /* Do VBUS pulse after data pulse */ + fsl_otg_pulse_vbus(); +} + +void fsl_otg_pulse_vbus(void) +{ + srp_wait_done = 0; + fsl_otg_chrg_vbus(1); + /* start the timer to end vbus charge */ + fsl_otg_add_timer(b_vbus_pulse_tmr); +} + +void b_vbus_pulse_end(unsigned long foo) +{ + fsl_otg_chrg_vbus(0); + + /* As USB3300 using the same a_sess_vld and b_sess_vld voltage + * we need to discharge the bus for a while to distinguish + * residual voltage of vbus pulsing and A device pull up */ + fsl_otg_dischrg_vbus(1); + fsl_otg_add_timer(b_srp_wait_tmr); +} + +void b_srp_end(unsigned long foo) +{ + fsl_otg_dischrg_vbus(0); + srp_wait_done = 1; + + if ((fsl_otg_dev->otg.state == OTG_STATE_B_SRP_INIT) && + fsl_otg_dev->fsm.b_sess_vld) + fsl_otg_dev->fsm.b_srp_done = 1; +} + +/* Workaround for a_host suspending too fast. When a_bus_req=0, + * a_host will start by SRP. It needs to set b_hnp_enable before + * actually suspending to start HNP + */ +void a_wait_enum(unsigned long foo) +{ + VDBG("a_wait_enum timeout\n"); + if (!fsl_otg_dev->otg.host->b_hnp_enable) + fsl_otg_add_timer(a_wait_enum_tmr); + else + otg_statemachine(&fsl_otg_dev->fsm); +} + +/* ------------------------------------------------------*/ + +/* The timeout callback function to set time out bit */ +void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +/* Initialize timers */ +void fsl_otg_init_timers(struct otg_fsm *fsm) +{ + /* FSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, + (unsigned long)&fsm); + + /* device driver used timers */ + b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); + b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, + TB_DATA_PLS, 0); + b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, + TB_VBUS_PLS, 0); + +} + +/* Add timer to timer list */ +void fsl_otg_add_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer; + struct fsl_otg_timer *tmp_timer; + + /* Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +void fsl_otg_del_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer; + struct fsl_otg_timer *tmp_timer, *del_tmp; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); +} + +/* Reduce timer count by 1, and find timeout conditions. + * Called by fsl_otg 1ms timer interrupt + */ +int fsl_otg_tick_timer(void) +{ + struct fsl_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + return expired; +} + +/* Reset controller, not reset the bus */ +void otg_reset_controller(void) +{ + u32 command; + + command = readl(&usb_dr_regs->usbcmd); + command |= UCMD_RESET; + writel(command, &usb_dr_regs->usbcmd); + while(readl(&usb_dr_regs->usbcmd) & UCMD_RESET) + continue; +} + +/* Call suspend/resume routines in host driver */ +int fsl_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev; + struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg); + u32 retval = 0; + pm_message_t state = {0}; + + if (!xceiv->host) + return -ENODEV; + + dev = xceiv->host->controller; + + /* Update a_vbus_vld state as a_vbus_vld int is disabled + * in device mode + */ + fsm->a_vbus_vld = + (le32_to_cpu(usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID) ? 1 : 0; + if (on) { + /* start fsl usb host controller */ + if (otg_dev->host_working) + goto end; + else { + otg_reset_controller(); + VDBG("host on......"); + if (dev->driver->resume) { + retval = dev->driver->resume(dev); + if (fsm->id) { + /* default-b */ + fsl_otg_drv_vbus(1); + /* Workaround: b_host can't driver + * vbus, but PP in PORTSC needs to + * be 1 for host to work. + * So we set drv_vbus bit in + * transceiver to 0 thru ULPI. */ + write_ulpi(0x0c, 0x20); + } + } + + otg_dev->host_working = 1; + } + } else { + /* stop fsl usb host controller */ + if (!otg_dev->host_working) + goto end; + else { + VDBG("host off......"); + if (dev && dev->driver) { + retval = dev->driver->suspend(dev, state); + if (fsm->id) + /* default-b */ + fsl_otg_drv_vbus(0); + } + otg_dev->host_working = 0; + } + } +end: + return retval; +} + +/* Call suspend and resume function in udc driver + * to stop and start udc driver. + */ +int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev = &xceiv->gadget->dev; + pm_message_t state = {0}; + + if (!xceiv->gadget) + return -ENODEV; + + VDBG("gadget %s", on ? "on" : "off"); + + if (on) + dev->driver->resume(dev); + else + dev->driver->suspend(dev, state); + + return 0; +} + + +/* Called by initialization code of host driver. Register host controller + * to the OTG. Suspend host for OTG role detection. + */ +static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + struct device *dev; + pm_message_t state = {0}; + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + otg_p->host = host; + + otg_dev->fsm.a_bus_drop = 0; + otg_dev->fsm.a_bus_req = 1; + + if (host) { + VDBG("host off......\n"); + + otg_p->host->otg_port = fsl_otg_initdata.otg_port; + otg_p->host->is_b_host = otg_dev->fsm.id; + dev = host->controller; + + if (dev && dev->driver) + dev->driver->suspend(dev, state); + } else { /* host driver going away */ + + if (!(le32_to_cpu(otg_dev->dr_mem_map->otgsc) & + OTGSC_STS_USB_ID)) { + /* Mini-A cable connected */ + struct otg_fsm *fsm = &otg_dev->fsm; + + otg_p->state = OTG_STATE_UNDEFINED; + fsm->protocol = PROTO_UNDEF; + } + } + + otg_dev->host_working = 0; + + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* Called by initialization code of udc. Register udc to OTG.*/ +static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, + struct usb_gadget *gadget) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + VDBG("otg_dev 0x%x", (int) otg_dev); + VDBG("fsl_otg_dev 0x%x", (int) fsl_otg_dev); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + if (!gadget) { + if (!otg_dev->otg.default_a) + otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); + usb_gadget_vbus_disconnect(otg_dev->otg.gadget); + otg_dev->otg.gadget = 0; + otg_dev->fsm.b_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + return 0; + } + +#ifdef DEBUG + /* + * debug the initial state of the ID pin when only + * the gadget driver is loaded and no cable is connected. + * sometimes, we get an ID irq right + * after the udc driver's otg_get_transceiver() call + * that indicates that IDpin=0, which means a Mini-A + * connector is attached. not good. + */ + DBG("before: fsm.id ID pin=%d", otg_dev->fsm.id); + otg_dev->fsm.id = (otg_dev->dr_mem_map->otgsc & OTGSC_STS_USB_ID) ? + 1 : 0; + DBG("after: fsm.id ID pin=%d", otg_dev->fsm.id); + DBG("OTGSC=0x%x", otg_dev->dr_mem_map->otgsc); + // if (!otg_dev->fsm.id) { + DBG("1504 OTG Control = 0x%x", + isp1504_read(ISP1504_OTGCTL, + &otg_dev->dr_mem_map->ulpiview)); + // } +#endif + + otg_p->gadget = gadget; + otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; + + otg_dev->fsm.b_bus_req = 1; + + /* start the gadget right away if the ID pin says Mini-B */ + DBG("ID pin=%d", otg_dev->fsm.id); + if (otg_dev->fsm.id == 1) { + fsl_otg_start_host(&otg_dev->fsm, 0); + otg_drv_vbus(&otg_dev->fsm, 0); + fsl_otg_start_gadget(&otg_dev->fsm, 1); + } + + return 0; +} + +/* Set OTG port power, only for B-device */ +static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA) +{ + if (!fsl_otg_dev) + return -ENODEV; + if (otg_p->state == OTG_STATE_B_PERIPHERAL) + printk("FSL OTG:Draw %d mA\n", mA); + + return 0; +} + + +/* Interrupt handler. OTG/host/peripheral share the same int line. + * OTG driver clears OTGSC interrupts and leaves USB interrupts + * intact. It needs to have knowledge of some USB interrupts + * such as port change. + */ +irqreturn_t fsl_otg_isr(int irq, void *dev_id, struct pt_regs * regs) +{ + struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; + struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; + u32 otg_int_src, otg_sc; + int trigger = 0; + + otg_sc = le32_to_cpu(usb_dr_regs->otgsc); + otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); + + /* Only clear otg interrupts */ + usb_dr_regs->otgsc |= cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK); + + /*FIXME: ID change not generate when init to 0 */ + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + + /* process OTG interrupts */ + if (otg_int_src) { + if (otg_int_src & OTGSC_IS_USB_ID) { + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; + trigger = 1; + DBG("IRQ=ID now=%d", fsm->id); + if (fsm->id) { /* switch to gadget */ + fsl_otg_start_host(fsm, 0); + otg_drv_vbus(fsm, 0); + fsl_otg_start_gadget(fsm, 1); + + } else { /* switch to host */ + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); + } + return IRQ_HANDLED; + } + } + + return IRQ_NONE; +} + +static struct otg_fsm_ops fsl_otg_ops = { + .chrg_vbus = fsl_otg_chrg_vbus, + .drv_vbus = fsl_otg_drv_vbus, + .loc_conn = fsl_otg_loc_conn, + .loc_sof = fsl_otg_loc_sof, + .start_pulse = fsl_otg_start_pulse, + + .add_timer = fsl_otg_add_timer, + .del_timer = fsl_otg_del_timer, + + .start_host = fsl_otg_start_host, + .start_gadget = fsl_otg_start_gadget, +}; + +/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ +int fsl_otg_config(struct platform_device *pdev) +{ + int status; + struct fsl_otg *fsl_otg_tc; + struct arc_xcvr_config *config; + + config = (struct arc_xcvr_config *)pdev->dev.platform_data; + + DBG(); + + if (fsl_otg_dev) + return 0; + + /* allocate space to fsl otg device */ + fsl_otg_tc = kmalloc(sizeof(struct fsl_otg), GFP_KERNEL); + if (!fsl_otg_tc) + return -ENODEV; + + memset(fsl_otg_tc, 0, sizeof(struct fsl_otg)); + + fsl_otg_tc->dr_mem_map = config->regs; + DBG("set dr_mem_map to 0x%p", config->regs); + + INIT_LIST_HEAD(&active_timers); + fsl_otg_init_timers(&fsl_otg_tc->fsm); + + /* Set OTG state machine operations */ + fsl_otg_tc->fsm.ops = &fsl_otg_ops; + + /* record initial state of ID pin */ + fsl_otg_tc->fsm.id = (fsl_otg_tc->dr_mem_map->otgsc & OTGSC_STS_USB_ID) + ? 1 : 0; + DBG("initial ID pin=%d", fsl_otg_tc->fsm.id); + DBG("OTGSC=0x%x", fsl_otg_tc->dr_mem_map->otgsc); + DBG("1504 OTGCTL = 0x%x\n", + isp1504_read(ISP1504_OTGCTL, &fsl_otg_tc->dr_mem_map->ulpiview)); + + /* initialize the otg structure */ + fsl_otg_tc->otg.label = DRIVER_DESC; + fsl_otg_tc->otg.set_host = fsl_otg_set_host; + fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral; + fsl_otg_tc->otg.set_power = fsl_otg_set_power; + + fsl_otg_dev = fsl_otg_tc; + + /* Store the otg transceiver */ + status = otg_set_transceiver(&fsl_otg_tc->otg); + if (status) { + printk(KERN_WARNING ": unable to register OTG transceiver.\n"); + return status; + } + + return 0; +} + +/* OTG Initialization*/ +int usb_otg_start(struct platform_device *pdev) +{ + struct fsl_otg *p_otg; + struct otg_transceiver *otg_trans = otg_get_transceiver(); + struct otg_fsm *fsm; + int status; + u32 temp; + + DBG(); + + p_otg = container_of(otg_trans, struct fsl_otg, otg); + fsm = &p_otg->fsm; + + /* Initialize the state machine structure with default values */ + SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); + fsm->transceiver = &p_otg->otg; + + usb_dr_regs = p_otg->dr_mem_map; + DBG("set usb_dr_regs to 0x%p", usb_dr_regs); + + /*request irq*/ + p_otg->irq = pdev->resource[0].start; + DBG("requesting irq %d", p_otg->irq); + status = request_irq(p_otg->irq, fsl_otg_isr, SA_SHIRQ, "isp1504", + p_otg); + if (status) { + dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n", + p_otg->irq,status); + kfree(p_otg); + return status; + } + + + /* + * The ID input is FALSE when a Mini-A plug is inserted + * in the Mini-AB receptacle. Otherwise, this input is TRUE. + */ + if (le32_to_cpu(p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) + p_otg->otg.state = OTG_STATE_UNDEFINED; /* not Mini-A */ + else + p_otg->otg.state = OTG_STATE_A_IDLE; /* Mini-A */ + + /* enable OTG interrupt */ + temp = readl(&p_otg->dr_mem_map->otgsc); + + temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + temp |= OTGSC_IE_USB_ID; + writel(temp,&p_otg->dr_mem_map->otgsc); + + return 0; +} + +static int board_init(struct platform_device *pdev) +{ + struct arc_xcvr_config *config; + config = (struct arc_xcvr_config *)pdev->dev.platform_data; + + /* platform specific init: check the clock, grab/config pins, etc. */ + if (config->platform_init() != 0) + return -EINVAL; + + return 0; +} + +/*------------------------------------------------------------------------- + PROC File System Support +-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_OTG_DEBUG_FILES + +#include + +static const char proc_filename[] = "driver/isp1504_otg"; + +static int otg_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + struct otg_fsm *fsm = &fsl_otg_dev->fsm; + char *buf = page; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t; + u32 tmp_reg; + + if (off != 0) + return 0; + + spin_lock_irqsave(&fsm->lock, flags); + + /* ------basic driver infomation ---- */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "isp1504_otg version: %s\n\n", + DRIVER_VERSION); + size -= t; + next += t; + + /* ------ Registers ----- */ + tmp_reg = le32_to_cpu(usb_dr_regs->otgsc); + t = scnprintf(next, size, "OTGSC reg: %x\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_dr_regs->portsc); + t = scnprintf(next, size, "PORTSC reg: %x\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_dr_regs->usbmode); + t = scnprintf(next, size, "USBMODE reg: %x\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_dr_regs->usbcmd); + t = scnprintf(next, size, "USBCMD reg: %x\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = le32_to_cpu(usb_dr_regs->usbsts); + t = scnprintf(next, size, "USBSTS reg: %x\n", tmp_reg); + size -= t; + next += t; + + /* ------ State ----- */ + t = scnprintf(next, size, + "OTG state: %s\n\n", + state_string(fsl_otg_dev->otg.state)); + size -= t; + next += t; + +#ifdef DEBUG + /* ------ State Machine Variables ----- */ + t = scnprintf(next, size, "a_bus_req: %d\n", fsm->a_bus_req); + size -= t; + next += t; + + t = scnprintf(next, size, "b_bus_req: %d\n", fsm->b_bus_req); + size -= t; + next += t; + + t = scnprintf(next, size, "a_bus_resume: %d\n", fsm->a_bus_resume); + size -= t; + next += t; + + t = scnprintf(next, size, "a_bus_suspend: %d\n", fsm->a_bus_suspend); + size -= t; + next += t; + + t = scnprintf(next, size, "a_conn: %d\n", fsm->a_conn); + size -= t; + next += t; + + t = scnprintf(next, size, "a_sess_vld: %d\n", fsm->a_sess_vld); + size -= t; + next += t; + + t = scnprintf(next, size, "a_srp_det: %d\n", fsm->a_srp_det); + size -= t; + next += t; + + t = scnprintf(next, size, "a_vbus_vld: %d\n", fsm->a_vbus_vld); + size -= t; + next += t; + + t = scnprintf(next, size, "b_bus_resume: %d\n", fsm->b_bus_resume); + size -= t; + next += t; + + t = scnprintf(next, size, "b_bus_suspend: %d\n", fsm->b_bus_suspend); + size -= t; + next += t; + + t = scnprintf(next, size, "b_conn: %d\n", fsm->b_conn); + size -= t; + next += t; + + t = scnprintf(next, size, "b_se0_srp: %d\n", fsm->b_se0_srp); + size -= t; + next += t; + + t = scnprintf(next, size, "b_sess_end: %d\n", fsm->b_sess_end); + size -= t; + next += t; + + t = scnprintf(next, size, "b_sess_vld: %d\n", fsm->b_sess_vld); + size -= t; + next += t; + + t = scnprintf(next, size, "id: %d\n", fsm->id); + size -= t; + next += t; +#endif + + spin_unlock_irqrestore(&fsm->lock, flags); + + *eof = 1; + return count - size; +} + +#define create_proc_file() create_proc_read_entry(proc_filename, \ + 0, NULL, otg_proc_read, NULL) + +#define remove_proc_file() remove_proc_entry(proc_filename, NULL) + +#else /* !CONFIG_USB_OTG_DEBUG_FILES */ + +#define create_proc_file() do {} while (0) +#define remove_proc_file() do {} while (0) + +#endif /*CONFIG_USB_OTG_DEBUG_FILES */ + + +static int __init fsl_otg_probe(struct platform_device *pdev) +{ + int status; + + DBG("pdev=0x%p", pdev); + + if (!pdev) + return -ENODEV; + + /* Initialize the clock, multiplexing pin and PHY interface */ + board_init(pdev); + + /* configure the OTG */ + status = fsl_otg_config(pdev); + if (status) { + printk(KERN_INFO "Couldn't init OTG module\n"); + return -status; + } + + /* start OTG */ + status = usb_otg_start(pdev); + + create_proc_file(); + return status; +} + +static int __exit fsl_otg_remove(struct platform_device *pdev) +{ + u32 ie; + struct arc_xcvr_config *config; + + config = (struct arc_xcvr_config *)pdev->dev.platform_data; + + DBG("pdev=0x%p config=0x%p", pdev, config); + + otg_set_transceiver(NULL); + + /* disable and clear OTGSC interrupts */ + ie = readl(&usb_dr_regs->otgsc); + ie &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + ie |= OTGSC_INTERRUPT_STATUS_BITS_MASK; + writel(ie, &usb_dr_regs->otgsc); + + free_irq(fsl_otg_dev->irq, fsl_otg_dev); + + kfree(fsl_otg_dev); + + remove_proc_file(); + + config->platform_uninit(); + + fsl_otg_dev = NULL; + return 0; +} + +struct platform_driver fsl_otg_driver = { + .probe = fsl_otg_probe, + .remove = fsl_otg_remove, + .driver = { + .name = "isp1504_arc", + .owner = THIS_MODULE, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init fsl_usb_otg_init(void) +{ + printk(KERN_INFO "driver %s, %s\n", otg_dr_name, DRIVER_VERSION); + return platform_driver_register(&fsl_otg_driver); +} + +static void __exit fsl_usb_otg_exit(void) +{ + platform_driver_unregister(&fsl_otg_driver); +} + +module_init(fsl_usb_otg_init); +module_exit(fsl_usb_otg_exit); + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); --- linux-2.6.16.19/drivers/usb/otg/fsl_otg.h 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/otg/fsl_otg.h 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,241 @@ +/* + * Copyright 2005-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "otg_fsm.h" +#include + + /* USB Command Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x1<<0 ) +#define USB_CMD_CTRL_RESET (0x1<<1 ) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4 ) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5 ) +#define USB_CMD_INT_AA_DOORBELL (0x1<<6 ) +#define USB_CMD_ASP (0x3<<8 ) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11 ) +#define USB_CMD_SUTW (0x1<<13 ) +#define USB_CMD_ATDTW (0x1<<14 ) +#define USB_CMD_ITC (0xFF<<16) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) +#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x0<<8) +#define USB_CMD_ASP_01 (0x1<<8) +#define USB_CMD_ASP_10 (0x2<<8) +#define USB_CMD_ASP_11 (0x3<<8) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) +#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) +#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) +#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) +#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) +#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) +#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) +#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB Status Register Bit Masks */ +#define USB_STS_INT (0x1<<0 ) +#define USB_STS_ERR (0x1<<1 ) +#define USB_STS_PORT_CHANGE (0x1<<2 ) +#define USB_STS_FRM_LST_ROLL (0x1<<3 ) +#define USB_STS_SYS_ERR (0x1<<4 ) +#define USB_STS_IAA (0x1<<5 ) +#define USB_STS_RESET_RECEIVED (0x1<<6 ) +#define USB_STS_SOF (0x1<<7 ) +#define USB_STS_DCSUSPEND (0x1<<8 ) +#define USB_STS_HC_HALTED (0x1<<12) +#define USB_STS_RCL (0x1<<13) +#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) +#define USB_STS_ASYNC_SCHEDULE (0x1<<15) + +/* USB Interrupt Enable Register Bit Masks */ +#define USB_INTR_INT_EN (0x1<<0 ) +#define USB_INTR_ERR_INT_EN (0x1<<1 ) +#define USB_INTR_PC_DETECT_EN (0x1<<2 ) +#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3 ) +#define USB_INTR_SYS_ERR_EN (0x1<<4 ) +#define USB_INTR_ASYN_ADV_EN (0x1<<5 ) +#define USB_INTR_RESET_EN (0x1<<6 ) +#define USB_INTR_SOF_EN (0x1<<7 ) +#define USB_INTR_DEVICE_SUSPEND (0x1<<8 ) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) +#define USB_DEVICE_ADDRESS_BIT_POS (25) + + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) +#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) +#define USB_MODE_CTRL_MODE_HOST (0x3<<0) +#define USB_MODE_CTRL_MODE_RSV (0x1<<0) +#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) +#define USB_MODE_STREAM_DISABLE (0x1<<4) + + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ + +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 + * This is only used to get out of + * OTG_STATE_A_WAIT_BCON state if there was + * no connection for these many milliseconds + */ + +/* A-Idle to B-Disconnect */ +/* It is necessary for this timer to be more than 750 ms because of a bug in OPT + * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated + * in the test description + */ +#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ + +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ + +/* B-device timing constants */ + + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ +#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ +#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ + +/* SRP Initiate Time */ +#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ + +/* SRP Fail Time */ +#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ + +/* SRP result wait time */ +#define TB_SRP_WAIT (60) + +/* VBus time */ +#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ + +/* Discharge time */ +/* This time should be less than 10ms. It varies from system to system. */ +#define TB_VBUS_DSCHRG (8) + +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ + +/* A bus suspend timer before we can switch to b_wait_aconn */ +#define TB_A_SUSPEND (7) +#define TB_BUS_RESUME (12) + +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ + + +#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state=newstate) + +struct usb_dr_mmap { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[8]; + u32 ulpiview; /* ULPI register access */ + u8 res7[12]; + u32 configflag; /* Configure Flag Register */ + u32 portsc; /* Port 1 Status and Control Register */ + u8 res8[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ + u8 res9[552]; + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res10[236]; + u32 control; /* General Purpose Control Register */ +}; + + +struct fsl_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +struct fsl_otg_timer inline *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct fsl_otg_timer *timer; + timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +struct fsl_otg { + struct otg_transceiver otg; + struct otg_fsm fsm; + struct usb_dr_mmap *dr_mem_map; + + /*used for usb host*/ + u8 host_working; + u8 on_off; + + int irq; +}; + +struct fsl_otg_config { + u8 otg_port; +}; + +extern const char *state_string(enum usb_otg_state state); +extern int otg_set_resources(struct resource *resources, int num); --- linux-2.6.16.19/drivers/usb/otg/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/otg/Makefile 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,6 @@ +# +# Makefile for USB OTG controller driver +# +# USB transceiver +isp1504_arc-objs := fsl_otg.o otg_fsm.o +obj-$(CONFIG_ISP1504_MXC_OTG) += isp1504_arc.o --- linux-2.6.16.19/drivers/usb/otg/otg_fsm.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/otg/otg_fsm.c 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,380 @@ +/* + * Copyright 2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "otg_fsm.h" + + +/* Defined by device specific driver, for different timer implementation */ +extern void *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, *a_wait_enum_tmr; + +const char *state_string(enum usb_otg_state state) +{ + switch (state) { + case OTG_STATE_A_IDLE: return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; + case OTG_STATE_A_HOST: return "a_host"; + case OTG_STATE_A_SUSPEND: return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; + case OTG_STATE_B_IDLE: return "b_idle"; + case OTG_STATE_B_SRP_INIT: return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; + case OTG_STATE_B_HOST: return "b_host"; + default: return "UNDEFINED"; + } +} + +const char *protocol_string(int p) +{ + switch (p) { + case PROTO_HOST: return "Host"; + case PROTO_GADGET: return "Peripheral"; + default: return "undef"; + } +} + + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Change role from %s to %s", + protocol_string(fsm->protocol), + protocol_string(protocol)); + + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 0); + if (ret) return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 1); + if (ret) return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed = 0; + +/* Called when leaving a state. Do state clean up jobs here */ +void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, b_se0_srp_tmr); + fsm->b_se0_srp = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, b_ase0_brst_tmr); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, a_wait_vrise_tmr); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, a_wait_bcon_tmr); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, a_aidl_bdis_tmr); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req = 0; + break; + case OTG_STATE_A_PERIPHERAL: + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + + +/* Called when entering a state */ +int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->transceiver->state == new_state) + return 0; + + VDBG("chg state to %s", state_string(new_state)); + + otg_leave_state(fsm, fsm->transceiver->state); + + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_se0_srp_tmr); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_srp_fail_tmr); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, b_ase0_brst_tmr); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->transceiver->host, + fsm->transceiver->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_bcon_tmr); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req) + otg_add_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_aidl_bdis_tmr); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->transceiver->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + unsigned long flags; + + spin_lock_irqsave(&fsm->lock, flags); + + state = fsm->transceiver->state; + state_changed = 0; + /* State machine state change judgement */ + + VDBG("top: curr state=%s", state_string(state)); + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + VDBG("gadget: %p", fsm->transceiver->gadget); + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->transceiver->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && + fsm->transceiver->gadget->b_hnp_enable && + fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || + fsm->a_wait_vrise_tmout) { + otg_set_state (fsm, OTG_STATE_A_WAIT_BCON); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if ((!fsm->a_bus_req || fsm->a_suspend_req) && + fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->id || fsm->a_bus_req || + (!fsm->a_sess_vld && !fsm->b_conn)) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + spin_unlock_irqrestore(&fsm->lock, flags); + + return state_changed; +} + --- linux-2.6.16.19/drivers/usb/otg/otg_fsm.h 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/drivers/usb/otg/otg_fsm.h 2006-06-07 10:28:21.000000000 -0600 @@ -0,0 +1,154 @@ +/* + * Copyright 2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#if 0 +#define DEBUG 1 +#define VERBOSE 1 +#endif + +#ifdef DEBUG +/* +#define DBG(fmt, args...) printk("[%s] " fmt "\n", \ + __FUNCTION__, ## args) +*/ +#define DBG(fmt, args...) printk("j=%lu [%s] " fmt "\n", \ + jiffies, __FUNCTION__, ## args) + +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do{}while(0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + struct otg_fsm_ops *ops; + struct otg_transceiver *transceiver; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + spinlock_t lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)( int on); + void (*drv_vbus)(int on); + void (*loc_conn)(int on); + void (*loc_sof)(int on); + void (*start_pulse)(void); + void (*add_timer)(void *timer); + void (*del_timer)(void *timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + fsm->ops->chrg_vbus(on); +} + +static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(on); + } +} + +static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(on); + } +} + +static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(on); + } +} + +static inline void otg_start_pulse(struct otg_fsm *fsm) +{ + fsm->ops->start_pulse(); +} + +static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->add_timer(timer); +} + +static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->del_timer(timer); +} + +int otg_statemachine(struct otg_fsm *fsm); + --- linux-2.6.16.19/arch/arm/mach-mx3/usb.c 1969-12-31 17:00:00.000000000 -0700 +++ linux-2.6.16-mx31/arch/arm/mach-mx3/usb.c 2006-06-21 11:21:11.000000000 -0600 @@ -0,0 +1,700 @@ +/* + * Copyright 2005-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PBC3_CLEAR (PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR) +#define PBC3_SET (PBC_BASE_ADDRESS + PBC_BCTRL3_SET) + +#undef DEBUG +#undef VERBOSE + +#ifdef DEBUG +#define dbg(fmt, args...) printk("%s: " fmt "\n", __FUNCTION__, ## args) +#else +#define dbg(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define vdbg dbg +#else +#define vdbg(fmt, args...) do {} while (0) +#endif + +extern int gpio_usbh1_active(void); +extern void gpio_usbh1_inactive(void); +extern int gpio_usbh2_active(void); +extern void gpio_usbh2_inactive(void); +extern int gpio_usbotg_hs_active(void); +extern void gpio_usbotg_hs_inactive(void); +extern int gpio_usbotg_fs_active(void); +extern void gpio_usbotg_fs_inactive(void); + +#if defined(CONFIG_USB_EHCI_ARC) +/* The dmamask must be set for EHCI to work */ +static u64 ehci_dmamask = ~(u32) 0; +#endif + +#if defined(CONFIG_USB_EHCI_ARC) || defined(CONFIG_USB_GADGET_ARC) +static void usb_release(struct device *dev) +{ + /* normally not freed */ +} + +/* + * make sure USB_CLK is running at 60 MHz +/- 1000 Hz + */ +static int check_usbclk(void) +{ + unsigned long clk; + + clk = mxc_get_clocks(USB_CLK); + if ((clk < 59999000) || (clk > 60001000)) { + printk(KERN_ERR "USB_CLK=%lu, should be 60MHz\n", clk); + return -1; + } + return 0; +} +#endif /* CONFIG_USB_EHCI_ARC || CONFIG_USB_GADGET_ARC */ + + +#if defined(CONFIG_ISP1504_MXC) || defined(CONFIG_ISP1504_MXC_MODULE) +/* + * read ULPI register 'reg' thru VIEWPORT register 'view' + */ +u8 isp1504_read(int reg, volatile u32 *view) +{ + u32 data; + + /* make sure interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + printk("\nSS=0\n"); + __raw_writel(ULPIVW_WU, view ); + do { /* wait for wakeup */ + data = __raw_readl(view); + } while (data & ULPIVW_WU); + } + + /* read the register */ + __raw_writel((ULPIVW_RUN | (reg << ULPIVW_ADDR_SHIFT)), view ); + + do { /* wait for completion */ + data = __raw_readl(view); + } while (data & ULPIVW_RUN); + + return (u8) (data >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK; +} +EXPORT_SYMBOL(isp1504_read); + +/* + * set bits into OTG ISP1504 register 'reg' thru VIEWPORT register 'view' + */ +void isp1504_set(u8 bits, int reg, volatile u32 *view) +{ + u32 data; + + /* make sure interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + printk("\nSS=0\n"); + __raw_writel(ULPIVW_WU, view ); + do { /* wait for wakeup */ + data = __raw_readl(view); + } while (data & ULPIVW_WU); + } + + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_SET) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */ + continue; +} +EXPORT_SYMBOL(isp1504_set); + +/* + * clear bits in OTG ISP1504 register 'reg' thru VIEWPORT register 'view' + */ +void isp1504_clear(u8 bits, int reg, volatile u32 *view) +{ + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_CLEAR) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */ + continue; +} +EXPORT_SYMBOL(isp1504_clear); + + +static void set_vbus_power(int on, volatile u32 *view) +{ + dbg("on=%d view=0x%p", on, view); + + vdbg("ULPI Vendor ID 0x%x Product ID 0x%x", + (isp1504_read(ISP1504_VID_HIGH, view) << 8) | + isp1504_read(ISP1504_VID_LOW, view ), + (isp1504_read(ISP1504_PID_HIGH, view) << 8) | + isp1504_read(ISP1504_PID_LOW, view)); + + vdbg("OTG Control before = 0x%x", + isp1504_read(ISP1504_OTGCTL, view)); + + if (on) { + isp1504_set(DRV_VBUS_EXT | /* enable external Vbus */ + DRV_VBUS | /* enable internal Vbus */ + USE_EXT_VBUS_IND | /* use external indicator */ + CHRG_VBUS, /* charge Vbus */ + ISP1504_OTGCTL, view); + + } else { + isp1504_clear(DRV_VBUS_EXT | /* disable external Vbus */ + DRV_VBUS, /* disable internal Vbus */ + ISP1504_OTGCTL, view); + + isp1504_set(USE_EXT_VBUS_IND | /* use external indicator */ + DISCHRG_VBUS, /* discharge Vbus */ + ISP1504_OTGCTL, view); + } + + vdbg("OTG Control after = 0x%x\n", + isp1504_read(ISP1504_OTGCTL, view)); +} +#endif + +#if defined(CONFIG_USB_EHCI_ARC_OTGHS) || defined(CONFIG_USB_GADGET_ARC_OTGHS) +static int otg_used = 0; /* OTG use-count */ + +static void otg_hs_set_xcvr(void) +{ + UOG_PORTSC1 &= ~PORTSC_PTS_MASK; /* set ULPI xcvr */ + UOG_PORTSC1 |= PORTSC_PTS_ULPI; + + /* need to reset the controller here so that the ID pin + * is correctly detected. + */ + UOG_USBCMD |= UCMD_RESET; + + /* allow controller to reset, plus let the ULPI + * transceiver reset too. + */ + mdelay(10); +} + +static int otg_hs_init(void) +{ + if (!otg_used) { + if (check_usbclk() != 0) + return -EINVAL; + + dbg("grab OTG-HS pins"); + if (gpio_usbotg_hs_active()) /* grab our pins */ + return -EINVAL; + + /* enable OTG/HS */ + __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC3_CLEAR); + + /* disable OTG/FS */ + __raw_writew(PBC_BCTRL3_OTG_FS_EN, PBC3_SET); + + USBCTRL &= ~UCTRL_BPE; /* disable bypass mode */ + USBCTRL |= UCTRL_OUIE | /* ULPI intr enable */ + UCTRL_OWIE | /* OTG wakeup intr enable */ + UCTRL_OPM; /* power mask */ + + otg_hs_set_xcvr(); /* set transciever type */ + } + + otg_used++; + + return 0; +} + +static void otg_hs_uninit(void) +{ + dbg(); + + otg_used--; + if (!otg_used) { + + /* disable OTG/HS */ + __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC3_SET); + + dbg("free OTG-HS pins"); + gpio_usbotg_hs_inactive(); /* release our pins */ + } +} + +static void otg_hs_set_vbus_power(int on) +{ + dbg("on=%d", on); + set_vbus_power(on, &UOG_ULPIVIEW); +} + + +#if defined(CONFIG_USB_EHCI_ARC_OTGHS) +static struct arc_usb_config otg_hs_config = { + .name = "OTG_HS", + .platform_init = otg_hs_init, + .platform_uninit = otg_hs_uninit, + .set_vbus_power = otg_hs_set_vbus_power, + .xcvr_type = PORTSC_PTS_ULPI, + .usbmode = (u32) &UOG_USBMODE, + .does_otg = 1, + .power_budget = 150, /* 150 mA max power */ +}; + +static struct resource otg_hs_resources[] = { + { + .start = (u32) (USB_OTGREGS_BASE), + .end = (u32) (USB_OTGREGS_BASE + 0x1ff), + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device otg_hs_device = { + .name = "ehci_otghs", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &ehci_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &otg_hs_config, + }, + .num_resources = ARRAY_SIZE(otg_hs_resources), + .resource = otg_hs_resources, +}; +#endif /* CONFIG_USB_EHCI_ARC_OTGHS */ +#endif /* CONFIG_USB_EHCI_ARC_OTGHS || CONFIG_USB_GADGET_ARC_OTGHS */ + +#ifdef CONFIG_USB_EHCI_ARC_OTGFS +static void otg_fs_set_xcvr(void) +{ + UOG_PORTSC1 &= ~PORTSC_PTS_MASK; /* set serial xcvr */ + UOG_PORTSC1 |= PORTSC_PTS_SERIAL; +} + +static int otg_fs_init(void) +{ + dbg("grab OTG-FS pins"); + + if (check_usbclk() != 0) + return -EINVAL; + + if (gpio_usbotg_fs_active()) /* grab our pins */ + return -EINVAL; + + __raw_writew(PBC_BCTRL3_OTG_FS_SEL, PBC3_SET); + __raw_writew(PBC_BCTRL3_OTG_FS_EN, PBC3_CLEAR); /* enable OTG/FS */ + __raw_writew(PBC_BCTRL3_OTG_VBUS_EN, PBC3_CLEAR); /* enable OTG VBUS */ + + otg_fs_set_xcvr(); /* set transceiver type */ + + USBCTRL &= ~(UCTRL_OSIC_MASK | UCTRL_BPE); /* disable bypass mode */ + USBCTRL |= UCTRL_OSIC_SU6 | /* single-ended/unidir */ + UCTRL_OWIE | UCTRL_OPM; /* power mask */ + + return 0; +} + +static void otg_fs_uninit(void) +{ + dbg(); + + __raw_writew(PBC_BCTRL3_OTG_FS_SEL, PBC3_CLEAR); /* OTG_FS_SEL */ + __raw_writew(PBC_BCTRL3_OTG_FS_EN, PBC3_SET); /* disable OTG/FS */ + __raw_writew(PBC_BCTRL3_OTG_VBUS_EN, PBC3_SET); /* disable OTG VBUS */ + + gpio_usbotg_fs_inactive(); /* release our pins */ +} +#endif /* CONFIG_USB_EHCI_ARC_OTGFS */ + +/* Host 1 */ +#ifdef CONFIG_USB_EHCI_ARC_H1 +static void usbh1_set_xcvr(void) +{ + UH1_PORTSC1 &= ~PORTSC_PTS_MASK; + UH1_PORTSC1 |= PORTSC_PTS_SERIAL; +} + +static int usbh1_init(void) +{ + dbg("grab H1 pins"); + + if (check_usbclk() != 0) + return -EINVAL; + + if (gpio_usbh1_active()) + return -EINVAL; + + usbh1_set_xcvr(); + + __raw_writew(PBC_BCTRL3_FSH_EN, PBC3_CLEAR); /* enable FSH */ + __raw_writew(PBC_BCTRL3_FSH_SEL, PBC3_SET); /* Group B */ + __raw_writew(PBC_BCTRL3_FSH_MOD, PBC3_CLEAR); /* single ended */ + __raw_writew(PBC_BCTRL3_FSH_VBUS_EN, PBC3_CLEAR); /* enable FSH VBUS */ + + USBCTRL &= ~(UCTRL_H1SIC_MASK | UCTRL_BPE); /* disable bypass mode */ + USBCTRL |= UCTRL_H1SIC_SU6 | /* single-ended / unidir */ + UCTRL_H1WIE | UCTRL_H1DT | /* disable H1 TLL */ + UCTRL_H1PM; /* power mask */ + + return 0; +} + +static void usbh1_uninit(void) +{ + dbg(); + + __raw_writew(PBC_BCTRL3_FSH_EN, PBC3_SET); /* disable FSH */ + __raw_writew(PBC_BCTRL3_FSH_VBUS_EN, PBC3_SET); /* disable FSH VBUS */ + + gpio_usbh1_inactive(); /* release our pins */ +} + +static struct arc_usb_config usbh1_config = { + .name = "USBH1", + .platform_init = usbh1_init, + .platform_uninit = usbh1_uninit, + .xcvr_type = PORTSC_PTS_SERIAL, + .usbmode = (u32) &UH1_USBMODE, + .power_budget = 150, /* 150 mA max power */ +}; + +static struct resource usbh1_resources[] = { + { + .start = (u32) (USB_H1REGS_BASE), + .end = (u32) (USB_H1REGS_BASE + 0x1ff), + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbh1_device = { + .name = "ehci_h1", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &ehci_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usbh1_config, + }, + .num_resources = ARRAY_SIZE(usbh1_resources), + .resource = usbh1_resources, +}; +#endif /* CONFIG_USB_EHCI_ARC_H1 */ + +#ifdef CONFIG_USB_EHCI_ARC_H2 +static void usbh2_set_xcvr(void) +{ + UH2_PORTSC1 &= ~PORTSC_PTS_MASK; /* set ULPI xcvr */ + UH2_PORTSC1 |= PORTSC_PTS_ULPI; +} + +static int usbh2_init(void) +{ + dbg("grab H2 pins"); + if (check_usbclk() != 0) + return -EINVAL; + + /* abort the init if NAND card is present */ + if ((__raw_readw(PBC_BASE_ADDRESS + PBC_BSTAT1) & + PBC_BSTAT1_NF_DET) == 0) { + printk(KERN_ERR "USBH2 port not configured: " + "pin conflict with NAND flash.\n"); + return -EINVAL; + } + + /* + * If the ATA interface is enabled, turning on + * PBC_BCTRL3_HSH_SEL will make it unusable. + * To avoid that, abort now if ATA is enabled. + */ + if ((__raw_readw(PBC_BASE_ADDRESS + PBC_BCTRL2_SET) & + PBC_BCTRL2_ATA_EN) == 0) { + printk(KERN_ERR "USBH2 port not configured: " + "pin conflict with ATA.\n"); + return -EINVAL; + } + + if (gpio_usbh2_active()) /* grab our pins */ + return -EINVAL; + + __raw_writew(PBC_BCTRL3_HSH_SEL, PBC3_SET); /* enable HSH select */ + __raw_writew(PBC_BCTRL3_HSH_EN, PBC3_CLEAR); /* enable HSH */ + + usbh2_set_xcvr(); /* set transceiver type */ + + USBCTRL &= ~(UCTRL_H2SIC_MASK | UCTRL_BPE); /* disable bypass mode */ + USBCTRL |= UCTRL_H2WIE | /* wakeup intr enable */ + UCTRL_H2UIE | /* ULPI intr enable */ + UCTRL_H2DT | /* disable H2 TLL */ + UCTRL_H2PM; /* power mask */ + + dbg("success"); + return 0; +} + +static void usbh2_uninit(void) +{ + dbg(); + + __raw_writew(PBC_BCTRL3_HSH_SEL, PBC3_CLEAR); /* disable HSH select */ + __raw_writew(PBC_BCTRL3_HSH_EN, PBC3_SET); /* disable HSH */ + + gpio_usbh2_inactive(); /* release our pins */ +} + +static void usbh2_set_vbus_power(int on) +{ + dbg("on=%d", on); + set_vbus_power(on, &UH2_ULPIVIEW); +} + +static struct arc_usb_config usbh2_config = { + .name = "USBH2", + .platform_init = usbh2_init, + .platform_uninit = usbh2_uninit, + .set_vbus_power = usbh2_set_vbus_power, + .xcvr_type = PORTSC_PTS_ULPI, + .usbmode = (u32) &UH2_USBMODE, + .power_budget = 150, /* 150 mA max power */ +}; + +static struct resource usbh2_resources[] = { + { + .start = (u32) (USB_H2REGS_BASE), + .end = (u32) (USB_H2REGS_BASE + 0x1ff), + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbh2_device = { + .name = "ehci_h2", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &ehci_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usbh2_config, + }, + .num_resources = ARRAY_SIZE(usbh2_resources), + .resource = usbh2_resources, +}; +#endif /* CONFIG_USB_EHCI_ARC_H2 */ + + +#ifdef CONFIG_USB_GADGET_ARC +static struct arc_usb_config udc_hs_config = { + .name = "udc_otg_hs", + .platform_init = otg_hs_init, + .platform_uninit = otg_hs_uninit, + .set_vbus_power = otg_hs_set_vbus_power, + .xcvr_type = PORTSC_PTS_ULPI, + .usbmode = (u32) &UOG_USBMODE, +}; + +static struct resource udc_resources[] = { + { + .start = (u32) (USB_OTGREGS_BASE), + .end = (u32) (USB_OTGREGS_BASE + 0x1ff), + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB3, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dmamask = ~(u32) 0; +static struct platform_device udc_device = { + .name = "arc_udc", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &udc_dmamask, + .coherent_dma_mask = 0xffffffff, +#if defined CONFIG_USB_GADGET_ARC_OTGHS + .platform_data = &udc_hs_config, +#elif defined CONFIG_USB_GADGET_ARC_OTGFS + .platform_data = &udc_fs_config, +#else +#error "No OTG port configured." +#endif + }, + .num_resources = ARRAY_SIZE(udc_resources), + .resource = udc_resources, +}; +#endif /* CONFIG_USB_GADGET_ARC */ + +#if defined(CONFIG_USB_OTG) +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver driver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * releasing that count. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + dbg("%s xceiv=0x%p\n", __FUNCTION__, xceiv); + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} +EXPORT_SYMBOL(otg_get_transceiver); + +int otg_set_transceiver(struct otg_transceiver *x) +{ + dbg("%s xceiv=0x%p x=0x%p\n", __FUNCTION__, xceiv, x); + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} +EXPORT_SYMBOL(otg_set_transceiver); + +static void +isp1504_release(struct device *dev) +{ + /* normally not freed */ +} + + +static struct arc_xcvr_config isp1504_config = { + .name = "isp1504", + .platform_init = otg_hs_init, + .platform_uninit = otg_hs_uninit, + .regs = (void *)&UOG_ID, +}; + + +static struct resource isp1504_resources[] = { + { + .start = INT_USB3, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 isp1504_dmamask = ~(u32)0; +static struct platform_device isp1504_device = { + .name = "isp1504_arc", + .id = -1, + .dev = { + .release = isp1504_release, + .dma_mask = &isp1504_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &isp1504_config, + }, + .num_resources = ARRAY_SIZE(isp1504_resources), + .resource = isp1504_resources, +}; +#endif /* CONFIG_USB_OTG */ + +static int __init mx3_usb_init(void) +{ + int rc; + + rc = 0; + dbg(); + +#if defined(CONFIG_USB_OTG) + rc = platform_device_register(&isp1504_device); + if (rc) { + pr_debug("can't register isp1504 dvc, %d\n", status); + } else { + dbg("isp1504: platform_device_register succeeded."); + dbg("isp1504_device=0x%p resources=0x%p.", + &isp1504_device, isp1504_device.resource); + } +#endif + +#ifdef CONFIG_USB_EHCI_ARC_H1 + rc = platform_device_register(&usbh1_device); + if (rc) { + pr_debug("can't register USBH1 Host, %d\n", status); + } else { + printk(KERN_INFO "usb: USBH1 registered\n"); + dbg("usbh1_device=0x%p dev=0x%p resources=0x%p config=0x%p", + &usbh1_device, &usbh1_device.dev, + usbh1_device.resource, usbh1_device.dev.platform_data); + } +#endif + +#ifdef CONFIG_USB_EHCI_ARC_H2 + rc = platform_device_register(&usbh2_device); + if (rc) { + pr_debug("can't register USBH2 Host, %d\n", status); + } else { + printk(KERN_INFO "usb: USBH2 registered\n"); + dbg("usbh2_device=0x%p dev=0x%p resources=0x%p.", + &usbh2_device, &usbh2_device.dev, usbh2_device.resource); + } +#endif + +#ifdef CONFIG_USB_EHCI_ARC_OTGHS + rc = platform_device_register(&otg_hs_device); + if (rc) { + pr_debug("can't register OTG HS Host, %d\n", status); + } else { + printk(KERN_INFO "usb: OTG_HS Host registered\n"); + dbg("otg_hs_device at 0x%p dev=0x%p resources=0x%p.", + &otg_hs_device, &otg_hs_device.dev, otg_hs_device.resource); + } +#endif + +#ifdef CONFIG_USB_GADGET_ARC + rc = platform_device_register(&udc_device); + if (rc) + pr_debug("can't register OTG Gadget, %d\n", status); + else + printk(KERN_INFO "usb: OTG Gadget registered\n"); +#endif + return 0; +} + +subsys_initcall(mx3_usb_init);