From: Fushen Chen <fchen@apm.com>
To: linux-usb@vger.kernel.org
Cc: linuxppc-dev@ozlabs.org, gregkh@suse.de,
Mark Miesfeld <mmiesfeld@apm.com>, Fushen Chen <fchen@apm.com>
Subject: [PATCH 7/9] Add Synopsys DesignWare HS USB OTG Controller driver.
Date: Tue, 29 Jun 2010 14:27:00 -0700 [thread overview]
Message-ID: <12778468242153-git-send-email-fchen@apm.com> (raw)
In-Reply-To: <12778468242972-git-send-email-fchen@apm.com>
The Peripheral Controller Driver (PCD) is responsible for translating
requests from the Function Driver into the appropriate actions on the
DWC OTG controller.
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
drivers/usb/otg/dwc_otg_pcd.c | 1749 +++++++++++++++++++++++++++++++++++++++++
drivers/usb/otg/dwc_otg_pcd.h | 159 ++++
2 files changed, 1908 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/otg/dwc_otg_pcd.c
create mode 100644 drivers/usb/otg/dwc_otg_pcd.h
diff --git a/drivers/usb/otg/dwc_otg_pcd.c b/drivers/usb/otg/dwc_otg_pcd.c
new file mode 100644
index 0000000..152c0a6
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_pcd.c
@@ -0,0 +1,1749 @@
+/*
+ * DesignWare HS OTG controller driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * Based on versions provided by AMCC and Synopsis which are:
+ * Copyright (C) 2009-2010 AppliedMicro(www.apm.com)
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file implements the Peripheral Controller Driver.
+ *
+ * The Peripheral Controller Driver (PCD) is responsible for
+ * translating requests from the Function Driver into the appropriate
+ * actions on the DWC_otg controller. It isolates the Function Driver
+ * from the specifics of the controller by providing an API to the
+ * Function Driver.
+ *
+ * The Peripheral Controller Driver for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.
+ * (Gadget Driver is the Linux terminology for a Function Driver.)
+ *
+ * The Linux Gadget API is defined in the header file linux/usb/gadget.h. The
+ * USB EP operations API is defined in the structure usb_ep_ops and the USB
+ * Controller API is defined in the structure usb_gadget_ops
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+
+/*
+ * Static PCD pointer for use in usb_gadget_register_driver and
+ * usb_gadget_unregister_driver. Initialized in dwc_otg_pcd_init.
+ */
+static struct dwc_pcd *s_pcd;
+
+static inline int need_stop_srp_timer(struct core_if *core_if)
+{
+ if (core_if->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS ||
+ !core_if->core_params->i2c_enable)
+ return core_if->srp_timer_started ? 1 : 0;
+ return 0;
+}
+
+/**
+ * Tests if the module is set to FS or if the PHY_TYPE is FS. If so, then the
+ * gadget should not report as dual-speed capable.
+ */
+static inline int check_is_dual_speed(struct core_if *core_if)
+{
+ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL ||
+ (core_if->hwcfg2.b.hs_phy_type == 2 &&
+ core_if->hwcfg2.b.fs_phy_type == 1 &&
+ core_if->core_params->ulpi_fs_ls))
+ return 0;
+ return 1;
+}
+
+/**
+ * Tests if driver is OTG capable.
+ */
+static inline int check_is_otg(struct core_if *core_if)
+{
+ if (core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE ||
+ core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST ||
+ core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE ||
+ core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
+ return 0;
+ return 1;
+}
+
+/**
+ * This function completes a request. It calls the request call back.
+ */
+void request_done(struct pcd_ep *ep, struct pcd_request *req, int status)
+{
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (GET_CORE_IF(ep->pcd)->dma_enable) {
+ if (req->mapped) {
+ dma_unmap_single(ep->pcd->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep->dwc_ep.is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else {
+ dma_sync_single_for_cpu(ep->pcd->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep->dwc_ep.is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ }
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&ep->pcd->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->pcd->lock);
+
+ if (ep->pcd->request_pending > 0)
+ --ep->pcd->request_pending;
+ ep->stopped = stopped;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Finally, when the current request is done, mark this endpoint
+ * as not active, so that new requests can be processed.
+ */
+ if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 0;
+}
+
+/**
+ * This function terminates all the requsts in the EP request queue.
+ */
+void request_nuke(struct pcd_ep *ep)
+{
+ struct pcd_request *req;
+
+ ep->stopped = 1;
+
+ /* called with irqs blocked?? */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+ request_done(ep, req, -ESHUTDOWN);
+ }
+}
+
+/*
+ * The following sections briefly describe the behavior of the Gadget
+ * API endpoint operations implemented in the DWC_otg driver
+ * software. Detailed descriptions of the generic behavior of each of
+ * these functions can be found in the Linux header file
+ * include/linux/usb_gadget.h.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
+ * function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper
+ * functions. Within each section, the corresponding DWC_otg PCD
+ * function name is specified.
+ *
+ */
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_perio_tx_fifo(struct core_if *core_if)
+{
+ u32 mask = 1;
+ u32 i;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) {
+ if (!(mask & core_if->p_tx_msk)) {
+ core_if->p_tx_msk |= mask;
+ return i + 1;
+ }
+ mask <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_perio_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+ core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1)))
+ ^ core_if->p_tx_msk;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_tx_fifo(struct core_if *core_if)
+{
+ u32 mask = 1;
+ u32 i;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) {
+ if (!(mask & core_if->tx_msk)) {
+ core_if->tx_msk |= mask;
+ return i + 1;
+ }
+ mask <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+ core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1)))
+ ^ core_if->tx_msk;
+}
+
+/**
+ * Sets an in endpoint's tx fifo based on the hardware configuration.
+ */
+static void set_in_ep_tx_fifo(struct dwc_pcd *pcd, struct pcd_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ if (pcd->otg_dev->core_if->en_multiple_tx_fifo) {
+ ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if);
+ } else {
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* If ISOC EP then assign a Periodic Tx FIFO. */
+ if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC)
+ ep->dwc_ep.tx_fifo_num =
+ assign_perio_tx_fifo(pcd->otg_dev->core_if);
+ }
+}
+
+/**
+ * This function activates an EP. The Device EP control register for
+ * the EP is configured as defined in the ep structure. Note: This function is
+ * not used for EP0.
+ */
+void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ union depctl_data depctl;
+ u32 *addr;
+ union daint_data daintmsk = {.d32 = 0};
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = &dev_if->in_ep_regs[ep->num]->diepctl;
+ daintmsk.ep.in = 1 << ep->num;
+ } else {
+ addr = &dev_if->out_ep_regs[ep->num]->doepctl;
+ daintmsk.ep.out = 1 << ep->num;
+ }
+
+ /* If the EP is already active don't change the EP Control register */
+ depctl.d32 = dwc_read_reg32(addr);
+ if (!depctl.b.usbactep) {
+ depctl.b.mps = ep->maxpacket;
+ depctl.b.eptype = ep->type;
+ depctl.b.txfnum = ep->tx_fifo_num;
+ depctl.b.setd0pid = 1;
+ depctl.b.usbactep = 1;
+ dwc_write_reg32(addr, depctl.d32);
+ }
+
+ /* Enable the Interrupt for this EP */
+ dwc_modify_reg32(&dev_if->dev_global_regs->daintmsk, 0, daintmsk.d32);
+
+ ep->stall_clear_flag = 0;
+}
+/**
+ * This function is called by the Gadget Driver for each EP to be
+ * configured for the current configuration (SET_CONFIGURATION).
+ *
+ * This function initializes the dwc_otg_ep_t data structure, and then
+ * calls dwc_otg_ep_activate.
+ */
+static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pcd_ep *ep = NULL;
+ struct dwc_pcd *pcd = NULL;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !desc || ep->desc || desc->bDescriptorType !=
+ USB_DT_ENDPOINT) {
+ printk(KERN_WARNING "%s, bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ep == &ep->pcd->ep0) {
+ printk(KERN_WARNING "%s, bad ep(0)\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check FIFO size */
+ if (!desc->wMaxPacketSize) {
+ printk(KERN_WARNING "%s, bad %s maxpacket\n", __func__,
+ _ep->name);
+ return -ERANGE;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ printk(KERN_WARNING "%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&pcd->lock, flags);
+ ep->desc = desc;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Activate the EP */
+ ep->stopped = 0;
+ ep->dwc_ep.is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+ ep->dwc_ep.maxpacket = ep->ep.maxpacket;
+ ep->dwc_ep.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (ep->dwc_ep.is_in)
+ set_in_ep_tx_fifo(pcd, ep, desc);
+
+ /* Set initial data PID. */
+ if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)
+ ep->dwc_ep.data_pid_start = 0;
+
+ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+/**
+ * This function deactivates an EP. This is done by clearing the USB Active EP
+ * bit in the Device EP control register. Note: This function is not used for
+ * EP0. EP0 cannot be deactivated.
+ */
+static void dwc_otg_ep_deactivate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ union depctl_data depctl = {.d32 = 0};
+ u32 *addr;
+ union daint_data daintmsk = {.d32 = 0};
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+ daintmsk.ep.in = 1 << ep->num;
+ } else {
+ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+ daintmsk.ep.out = 1 << ep->num;
+ }
+
+ depctl.b.usbactep = 0;
+ dwc_write_reg32(addr, depctl.d32);
+
+ /* Disable the Interrupt for this EP */
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->daintmsk,
+ daintmsk.d32, 0);
+}
+/**
+ * This function is called when an EP is disabled due to disconnect or
+ * change in configuration. Any pending requests will terminate with a
+ * status of -ESHUTDOWN.
+ *
+ * This function modifies the dwc_otg_ep_t data structure for this EP,
+ * and then calls dwc_otg_ep_deactivate.
+ */
+static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep)
+{
+ struct pcd_ep *ep;
+ struct core_if *core_if;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !ep->desc)
+ return -EINVAL;
+
+ core_if = ep->pcd->otg_dev->core_if;
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ request_nuke(ep);
+ dwc_otg_ep_deactivate(core_if, &ep->dwc_ep);
+
+ ep->desc = NULL;
+ ep->stopped = 1;
+ if (ep->dwc_ep.is_in) {
+ release_perio_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+ release_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+ }
+
+ spin_unlock_irqrestore(&ep->pcd->lock, flags);
+
+ return 0;
+}
+
+/**
+ * This function allocates a request object to use with the specified
+ * endpoint.
+ */
+static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct pcd_request *req;
+
+ if (!_ep) {
+ printk(KERN_WARNING "%s() Invalid EP\n", __func__);
+ return 0;
+ }
+
+ req = kzalloc(sizeof(struct pcd_request), gfp_flags);
+ if (!req) {
+ printk(KERN_WARNING "%s() request allocation failed\n",
+ __func__);
+ return 0;
+ }
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+/**
+ * This function frees a request object.
+ */
+static void dwc_otg_pcd_free_request(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct pcd_request *req;
+
+ if (!_ep || !_req) {
+ printk(KERN_WARNING "%s() nvalid ep or req argument\n",
+ __func__);
+ return;
+ }
+
+ req = container_of(_req, struct pcd_request, req);
+ kfree(req);
+}
+
+/*
+ * In dedicated Tx FIFO mode, enable the Non-Periodic Tx FIFO empty interrupt.
+ * Otherwise, enable the Tx FIFO epmty interrupt. The data will be written into
+ * the fifo by the ISR.
+ */
+static void enable_tx_fifo_empty_intr(struct core_if *c_if, struct dwc_ep *ep)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ struct device_if *d_if = c_if->dev_if;
+ struct core_global_regs *glbl_regs = c_if->core_global_regs;
+
+ if (!c_if->en_multiple_tx_fifo) {
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&glbl_regs->gintsts, intr_mask.d32, 0);
+ dwc_modify_reg32(&glbl_regs->gintmsk, intr_mask.d32,
+ intr_mask.d32);
+ } else if (ep->xfer_len) {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ u32 fifoemptymsk = 1 << ep->num;
+ dwc_modify_reg32(&d_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ 0, fifoemptymsk);
+ }
+}
+
+static void set_next_ep(struct device_if *dev_if, u8 num)
+{
+ union depctl_data depctl;
+
+ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl);
+ depctl.b.nextep = num;
+
+ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, depctl.d32);
+}
+
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be loaded into the
+ * appropriate Tx FIFO in the ISR. For OUT transfers, the packets are unloaded
+ * from the Rx FIFO in the ISR.
+ *
+ */
+void dwc_otg_ep_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+ union depctl_data depctl;
+ union deptsiz_data deptsiz;
+ struct device_if *d_if = c_if->dev_if;
+ struct core_global_regs *glbl_regs = c_if->core_global_regs;
+
+ if (ep->is_in) {
+ struct device_in_ep_regs *in_regs = d_if->in_ep_regs[ep->num];
+ union gnptxsts_data gtxstatus;
+ gtxstatus.d32 = dwc_read_reg32(&glbl_regs->gnptxsts);
+
+ if (!c_if->en_multiple_tx_fifo && !gtxstatus.b.nptxqspcavail)
+ return;
+
+ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl));
+ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz));
+
+ /* Zero Length Packet? */
+ if (!ep->xfer_len) {
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ /*
+ * Program the transfer size and packet count as
+ * follows:
+ *
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger
+ * transfers into multiple transfers of 1024 bytes sized
+ * messages. I happens often, that transfers of 4096
+ * bytes are required (zero-gadget,
+ * file_storage-gadget).
+ */
+ if (dwc_has_feature(c_if, DWC_LIMITED_XFER)) {
+ if (ep->xfer_len > MAX_XFER_LEN) {
+ ep->bytes_pending = ep->xfer_len
+ - MAX_XFER_LEN;
+ ep->xfer_len = MAX_XFER_LEN;
+ }
+ }
+
+ deptsiz.b.xfersize = ep->xfer_len;
+ deptsiz.b.pktcnt = (ep->xfer_len - 1 + ep->maxpacket) /
+ ep->maxpacket;
+ }
+ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+ if (c_if->dma_enable)
+ dwc_write_reg32(&in_regs->diepdma, ep->dma_addr);
+ else if (ep->type != DWC_OTG_EP_TYPE_ISOC)
+ enable_tx_fifo_empty_intr(c_if, ep);
+
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ dwc_write_reg32(&in_regs->diepctl, depctl.d32);
+
+ if (c_if->dma_enable)
+ set_next_ep(d_if, ep->num);
+ } else {
+ struct device_out_ep_regs *out_regs =
+ d_if->out_ep_regs[ep->num];
+
+ depctl.d32 = dwc_read_reg32(&out_regs->doepctl);
+ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ *
+ * pktcnt = N
+ * xfersize = N * maxpacket
+ */
+ if (!ep->xfer_len) {
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ deptsiz.b.pktcnt = (ep->xfer_len + ep->maxpacket - 1) /
+ ep->maxpacket;
+ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+ }
+ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32);
+
+ if (c_if->dma_enable)
+ dwc_write_reg32(&out_regs->doepdma, ep->dma_addr);
+
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (ep->even_odd_frame)
+ depctl.b.setd1pid = 1;
+ else
+ depctl.b.setd0pid = 1;
+ }
+
+ /* EP enable */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ dwc_write_reg32(&out_regs->doepctl, depctl.d32);
+ }
+}
+
+/**
+ * This function does the setup for a data transfer for EP0 and starts
+ * the transfer. For an IN transfer, the packets will be loaded into
+ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
+ * unloaded from the Rx FIFO in the ISR.
+ */
+void dwc_otg_ep0_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+ union depctl_data depctl;
+ union deptsiz0_data deptsiz;
+ struct device_if *d_if = c_if->dev_if;
+ struct core_global_regs *glbl_regs = c_if->core_global_regs;
+
+ ep->total_len = ep->xfer_len;
+
+ if (ep->is_in) {
+ struct device_in_ep_regs *in_regs = d_if->in_ep_regs[0];
+ union gnptxsts_data gtxstatus;
+
+ gtxstatus.d32 = dwc_read_reg32(&glbl_regs->gnptxsts);
+
+ if (!c_if->en_multiple_tx_fifo && !gtxstatus.b.nptxqspcavail)
+ return;
+
+ depctl.d32 = dwc_read_reg32(&in_regs->diepctl);
+ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz);
+
+ /* Zero Length Packet? */
+ if (!ep->xfer_len) {
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ /*
+ * Program the transfer size and packet count as
+ * follows:
+ *
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+ if (ep->xfer_len > ep->maxpacket) {
+ ep->xfer_len = ep->maxpacket;
+ deptsiz.b.xfersize = ep->maxpacket;
+ } else {
+ deptsiz.b.xfersize = ep->xfer_len;
+ }
+ deptsiz.b.pktcnt = 1;
+ }
+ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+ if (c_if->dma_enable)
+ dwc_write_reg32(&in_regs->diepdma, ep->dma_addr);
+
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ dwc_write_reg32(&in_regs->diepctl, depctl.d32);
+
+ if (!c_if->dma_enable)
+ enable_tx_fifo_empty_intr(c_if, ep);
+ } else {
+ struct device_out_ep_regs *out_regs =
+ d_if->out_ep_regs[ep->num];
+
+ depctl.d32 = dwc_read_reg32(&out_regs->doepctl);
+ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ *
+ * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
+ * pktcnt = N
+ */
+ if (!ep->xfer_len) {
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ deptsiz.b.pktcnt = (ep->xfer_len + ep->maxpacket - 1) /
+ ep->maxpacket;
+ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+ }
+ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32);
+
+ if (c_if->dma_enable)
+ dwc_write_reg32(&out_regs->doepdma, ep->dma_addr);
+
+ /* EP enable */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ dwc_write_reg32(&out_regs->doepctl, depctl.d32);
+ }
+}
+
+/**
+ * This function is used to submit an I/O Request to an EP.
+ *
+ * - When the request completes the request's completion callback
+ * is called to return the request to the driver.
+ * - An EP, except control EPs, may have multiple requests
+ * pending.
+ * - Once submitted the request cannot be examined or modified.
+ * - Each request is turned into one or more packets.
+ * - A BULK EP can queue any amount of data; the transfer is
+ * packetized.
+ * - Zero length Packets are specified with the request 'zero'
+ * flag.
+ */
+static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ int prevented = 0;
+ struct pcd_request *req;
+ struct pcd_ep *ep;
+ struct dwc_pcd *pcd;
+ struct core_if *core_if;
+ unsigned long flags = 0;
+
+ req = container_of(_req, struct pcd_request, req);
+ if (!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue)) {
+ printk(KERN_WARNING "%s, bad params\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+ printk(KERN_WARNING "%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ printk(KERN_WARNING "%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+ core_if = pcd->otg_dev->core_if;
+
+ if (!core_if->core_params->opt) {
+ if (ep->dwc_ep.num != 0) {
+ printk(KERN_ERR "%s queue req %p, len %d buf %p\n",
+ _ep->name, _req, _req->length, _req->buf);
+ }
+ }
+
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ if (_req->dma == DMA_ADDR_INVALID) {
+ _req->dma = dma_map_single(pcd->gadget.dev.parent,
+ _req->buf, _req->length, ep->dwc_ep.is_in ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(pcd->gadget.dev.parent,
+ _req->dma, _req->length, ep->dwc_ep.is_in ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+ }
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* Start the transfer */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ /* EP0 Transfer? */
+ if (ep->dwc_ep.num == 0) {
+ switch (pcd->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ break;
+ case EP0_OUT_DATA_PHASE:
+ if (pcd->request_config) {
+ /* Complete STATUS PHASE */
+ ep->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_STATUS;
+ }
+ break;
+ default:
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return -EL2HLT;
+ }
+
+ ep->dwc_ep.dma_addr = _req->dma;
+ ep->dwc_ep.start_xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_len = _req->length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ } else {
+ /* Setup and start the Transfer */
+ ep->dwc_ep.dma_addr = _req->dma;
+ ep->dwc_ep.start_xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_buff = _req->buf;
+ ep->dwc_ep.xfer_len = _req->length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+ }
+
+ if (req || prevented) {
+ ++pcd->request_pending;
+ list_add_tail(&req->queue, &ep->queue);
+
+ if (ep->dwc_ep.is_in && ep->stopped && !core_if->dma_enable) {
+ /*
+ * Device IN endpoint interrupt mask register is laid
+ * out exactly the same as the device IN endpoint
+ * interrupt register.
+ */
+ union diepint_data diepmsk = {.d32 = 0};
+ diepmsk.b.intktxfemp = 1;
+
+ dwc_modify_reg32(
+ &core_if->dev_if->dev_global_regs->diepmsk,
+ 0, diepmsk.d32);
+ }
+ }
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+/**
+ * This function cancels an I/O request from an EP.
+ */
+static int dwc_otg_pcd_ep_dequeue(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct pcd_request *req;
+ struct pcd_ep *ep;
+ struct dwc_pcd *pcd;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || !_req || (!ep->desc && ep->dwc_ep.num != 0)) {
+ printk(KERN_WARNING "%s, bad argument\n", __func__);
+ return -EINVAL;
+ }
+
+ pcd = ep->pcd;
+ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+ printk(KERN_WARNING "%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&pcd->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(&pcd->lock, flags);
+ return -EINVAL;
+ }
+
+ if (!list_empty(&req->queue))
+ request_done(ep, req, -ECONNRESET);
+ else
+ req = 0;
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+
+ return req ? 0 : -EOPNOTSUPP;
+}
+
+/**
+ * Set the EP STALL.
+ */
+void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+ union depctl_data depctl;
+ u32 *depctl_addr;
+
+ if (ep->is_in) {
+ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+ depctl.d32 = dwc_read_reg32(depctl_addr);
+
+ /* set the disable and stall bits */
+ if (depctl.b.epena)
+ depctl.b.epdis = 1;
+ depctl.b.stall = 1;
+ dwc_write_reg32(depctl_addr, depctl.d32);
+ } else {
+ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+ depctl.d32 = dwc_read_reg32(depctl_addr);
+
+ /* set the stall bit */
+ depctl.b.stall = 1;
+ dwc_write_reg32(depctl_addr, depctl.d32);
+ }
+}
+
+/**
+ * Clear the EP STALL.
+ */
+void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+ union depctl_data depctl;
+ u32 *depctl_addr;
+
+ if (ep->is_in == 1)
+ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+ else
+ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+
+ depctl.d32 = dwc_read_reg32(depctl_addr);
+
+ /* clear the stall bits */
+ depctl.b.stall = 0;
+
+ /*
+ * USB Spec 9.4.5: For endpoints using data toggle, regardless
+ * of whether an endpoint has the Halt feature set, a
+ * ClearFeature(ENDPOINT_HALT) request always results in the
+ * data toggle being reinitialized to DATA0.
+ */
+ if (ep->type == DWC_OTG_EP_TYPE_INTR ||
+ ep->type == DWC_OTG_EP_TYPE_BULK)
+ depctl.b.setd0pid = 1; /* DATA0 */
+
+ dwc_write_reg32(depctl_addr, depctl.d32);
+}
+
+/**
+ * usb_ep_set_halt stalls an endpoint.
+ *
+ * usb_ep_clear_halt clears an endpoint halt and resets its data
+ * toggle.
+ *
+ * Both of these functions are implemented with the same underlying
+ * function. The behavior depends on the val argument:
+ * - 0 means clear_halt.
+ * - 1 means set_halt,
+ * - 2 means clear stall lock flag.
+ * - 3 means set stall lock flag.
+ */
+static int dwc_otg_pcd_ep_set_halt(struct usb_ep *_ep, int val)
+{
+ int retval = 0;
+ unsigned long flags;
+ struct pcd_ep *ep = NULL;
+
+ ep = container_of(_ep, struct pcd_ep, ep);
+ if (!_ep || (!ep->desc && ep != &ep->pcd->ep0) ||
+ ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ printk(KERN_WARNING "%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->pcd->lock, flags);
+
+ if (ep->dwc_ep.is_in && !list_empty(&ep->queue)) {
+ printk(KERN_WARNING "%s() %s XFer In process\n", __func__,
+ _ep->name);
+ retval = -EAGAIN;
+ } else if (val == 0) {
+ dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+ } else if (val == 1) {
+ if (ep->dwc_ep.num == 0)
+ ep->pcd->ep0state = EP0_STALL;
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+ } else if (val == 2) {
+ ep->dwc_ep.stall_clear_flag = 0;
+ } else if (val == 3) {
+ ep->dwc_ep.stall_clear_flag = 1;
+ }
+
+ spin_unlock_irqrestore(&ep->pcd->lock, flags);
+ return retval;
+}
+
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+ .enable = dwc_otg_pcd_ep_enable,
+ .disable = dwc_otg_pcd_ep_disable,
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+ .queue = dwc_otg_pcd_ep_queue,
+ .dequeue = dwc_otg_pcd_ep_dequeue,
+ .set_halt = dwc_otg_pcd_ep_set_halt,
+ .fifo_status = 0,
+ .fifo_flush = 0,
+};
+
+/**
+ * Gets the current USB frame number from the DTS register. This is the frame
+ * number from the last SOF packet.
+ */
+static u32 dwc_otg_get_frame_number(struct core_if *core_if)
+{
+ union dsts_data dsts;
+ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
+ return dsts.b.soffn;
+}
+/**
+ * The following gadget operations will be implemented in the DWC_otg
+ * PCD. Functions in the API that are not described below are not
+ * implemented.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_gadget_ops. The Gadget Driver calls the
+ * wrapper function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper functions
+ * (except for ioctl, which doesn't have a wrapper function). Within
+ * each section, the corresponding DWC_otg PCD function name is
+ * specified.
+ *
+ */
+
+/**
+ *Gets the USB Frame number of the last SOF.
+ */
+static int dwc_otg_pcd_get_frame(struct usb_gadget *_gadget)
+{
+ struct dwc_pcd *pcd;
+
+ if (!_gadget) {
+ return -ENODEV;
+ } else {
+ pcd = container_of(_gadget, struct dwc_pcd, gadget);
+ dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+ }
+
+ return 0;
+}
+
+/**
+ * This function is called when the SRP timer expires. The SRP should complete
+ * within 6 seconds.
+ */
+static void srp_timeout(unsigned long data)
+{
+ union gotgctl_data gotgctl;
+ struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+ struct core_if *core_if = pcd->otg_dev->core_if;
+ u32 *addr = otg_ctl_reg(pcd);
+
+ gotgctl.d32 = dwc_read_reg32(addr);
+ core_if->srp_timer_started = 0;
+
+ if (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS &&
+ core_if->core_params->i2c_enable) {
+ printk(KERN_INFO "SRP Timeout\n");
+
+ if (core_if->srp_success && gotgctl.b.bsesvld) {
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
+ core_if->pcd_cb->resume_wakeup(
+ core_if->pcd_cb->p);
+
+ /* Clear Session Request */
+ gotgctl.d32 = 0;
+ gotgctl.b.sesreq = 1;
+ dwc_modify_reg32(addr, gotgctl.d32, 0);
+
+ core_if->srp_success = 0;
+ } else {
+ printk(KERN_ERR "Device not connected/responding\n");
+ gotgctl.b.sesreq = 0;
+ dwc_write_reg32(addr, gotgctl.d32);
+ }
+ } else if (gotgctl.b.sesreq) {
+ printk(KERN_INFO "SRP Timeout\n");
+ printk(KERN_ERR "Device not connected/responding\n");
+
+ gotgctl.b.sesreq = 0;
+ dwc_write_reg32(addr, gotgctl.d32);
+ } else {
+ printk(KERN_INFO " SRP GOTGCTL=%0x\n", gotgctl.d32);
+ }
+}
+
+
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ */
+static void dwc_otg_pcd_start_srp_timer(struct dwc_pcd *pcd)
+{
+ struct timer_list *srp_timer = &pcd->srp_timer;
+
+ GET_CORE_IF(pcd)->srp_timer_started = 1;
+
+ init_timer(srp_timer);
+ srp_timer->function = srp_timeout;
+ srp_timer->data = (unsigned long)pcd;
+ srp_timer->expires = jiffies + (HZ * 6);
+
+ add_timer(srp_timer);
+}
+
+static void dwc_otg_pcd_initiate_srp(struct dwc_pcd *pcd)
+{
+ union gotgctl_data mem;
+ union gotgctl_data val;
+ u32 *addr = otg_ctl_reg(pcd);
+
+ val.d32 = dwc_read_reg32(addr);
+ if (val.b.sesreq) {
+ printk(KERN_ERR "Session Request Already active!\n");
+ return;
+ }
+
+ printk(KERN_NOTICE "Session Request Initated\n");
+ mem.d32 = dwc_read_reg32(addr);
+ mem.b.sesreq = 1;
+ dwc_write_reg32(addr, mem.d32);
+
+ /* Start the SRP timer */
+ dwc_otg_pcd_start_srp_timer(pcd);
+ return;
+}
+
+static void dwc_otg_pcd_remote_wakeup(struct dwc_pcd *pcd, int set)
+{
+ union dctl_data dctl = {.d32 = 0};
+ u32 *addr = dev_ctl_reg(pcd);
+
+ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
+ if (pcd->remote_wakeup_enable) {
+ if (set) {
+ dctl.b.rmtwkupsig = 1;
+ dwc_modify_reg32(addr, 0, dctl.d32);
+ mdelay(1);
+
+ dwc_modify_reg32(addr, dctl.d32, 0);
+ }
+ }
+ }
+}
+
+/**
+ * Initiates Session Request Protocol (SRP) to wakeup the host if no
+ * session is in progress. If a session is already in progress, but
+ * the device is suspended, remote wakeup signaling is started.
+ *
+ */
+static int dwc_otg_pcd_wakeup(struct usb_gadget *_gadget)
+{
+ unsigned long flags;
+ struct dwc_pcd *pcd;
+ union dsts_data dsts;
+ union gotgctl_data gotgctl;
+
+ if (!_gadget)
+ return -ENODEV;
+ else
+ pcd = container_of(_gadget, struct dwc_pcd, gadget);
+
+ spin_lock_irqsave(&pcd->lock, flags);
+
+ /*
+ * This function starts the Protocol if no session is in progress. If
+ * a session is already in progress, but the device is suspended,
+ * remote wakeup signaling is started.
+ */
+
+ /* Check if valid session */
+ gotgctl.d32 = dwc_read_reg32(otg_ctl_reg(pcd));
+ if (gotgctl.b.bsesvld) {
+ /* Check if suspend state */
+ dsts.d32 = dwc_read_reg32(dev_sts_reg(pcd));
+ if (dsts.b.suspsts)
+ dwc_otg_pcd_remote_wakeup(pcd, 1);
+ } else {
+ dwc_otg_pcd_initiate_srp(pcd);
+ }
+
+ spin_unlock_irqrestore(&pcd->lock, flags);
+ return 0;
+}
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+ .get_frame = dwc_otg_pcd_get_frame,
+ .wakeup = dwc_otg_pcd_wakeup,
+ /* not selfpowered */
+};
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset)
+{
+ if (!pcd->gadget.is_otg)
+ return;
+
+ if (reset) {
+ pcd->b_hnp_enable = 0;
+ pcd->a_hnp_support = 0;
+ pcd->a_alt_hnp_support = 0;
+ }
+
+ pcd->gadget.b_hnp_enable = pcd->b_hnp_enable;
+ pcd->gadget.a_hnp_support = pcd->a_hnp_support;
+ pcd->gadget.a_alt_hnp_support = pcd->a_alt_hnp_support;
+}
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int _irq, void *dev)
+{
+ struct dwc_pcd *pcd = dev;
+ int retval = IRQ_NONE;
+
+ retval = dwc_otg_pcd_handle_intr(pcd);
+ return IRQ_RETVAL(retval);
+}
+
+/**
+ * PCD Callback function for initializing the PCD when switching to
+ * device mode.
+ */
+static int dwc_otg_pcd_start_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+ /* Initialize the Core for Device mode. */
+ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd)))
+ dwc_otg_core_dev_init(GET_CORE_IF(pcd));
+
+ return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ */
+static int dwc_otg_pcd_stop_cb(void *_p)
+{
+ dwc_otg_pcd_stop((struct dwc_pcd *)_p);
+ return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param _p void pointer to the <code>struct dwc_pcd</code>
+ */
+static int dwc_otg_pcd_suspend_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+ if (pcd->driver && pcd->driver->resume) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->suspend(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ */
+static int dwc_otg_pcd_resume_cb(void *_p)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+ struct core_if *core_if = pcd->otg_dev->core_if;
+
+ if (pcd->driver && pcd->driver->resume) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->resume(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+
+ /* Maybe stop the SRP timeout timer. */
+ if (need_stop_srp_timer(core_if)) {
+ core_if->srp_timer_started = 0;
+ del_timer_sync(&pcd->srp_timer);
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static struct cil_callbacks pcd_callbacks = {
+ .start = dwc_otg_pcd_start_cb,
+ .stop = dwc_otg_pcd_stop_cb,
+ .suspend = dwc_otg_pcd_suspend_cb,
+ .resume_wakeup = dwc_otg_pcd_resume_cb,
+ .p = 0, /* Set at registration */
+};
+
+/**
+ * Tasklet
+ *
+ */
+static void start_xfer_tasklet_func(unsigned long data)
+{
+ struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+ union depctl_data diepctl;
+ int num = pcd->otg_dev->core_if->dev_if->num_in_eps;
+ u32 i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcd->lock, flags);
+ diepctl.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, 0));
+
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ start_next_request(&pcd->ep0);
+ }
+
+ for (i = 0; i < num; i++) {
+ union depctl_data diepctl;
+ diepctl.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, i));
+
+ if (pcd->in_ep[i].queue_sof) {
+ pcd->in_ep[i].queue_sof = 0;
+ start_next_request(&pcd->in_ep[i]);
+ }
+ }
+ spin_unlock_irqrestore(&pcd->lock, flags);
+}
+
+static struct tasklet_struct start_xfer_tasklet = {
+ .next = NULL,
+ .state = 0,
+ .count = ATOMIC_INIT(0),
+ .func = start_xfer_tasklet_func,
+ .data = 0,
+};
+
+/**
+ * This function initialized the pcd Dp structures to there default
+ * state.
+ */
+void __devinit dwc_otg_pcd_reinit(struct dwc_pcd *pcd)
+{
+ static const char *names[] = {
+ "ep0", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in",
+ "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in",
+ "ep12in", "ep13in", "ep14in", "ep15in", "ep1out", "ep2out",
+ "ep3out", "ep4out", "ep5out", "ep6out", "ep7out", "ep8out",
+ "ep9out", "ep10out", "ep11out", "ep12out", "ep13out",
+ "ep14out", "ep15out"
+ };
+
+ u32 i;
+ int in_ep_cntr, out_ep_cntr;
+ u32 hwcfg1;
+ u32 num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
+ u32 num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
+ struct pcd_ep *ep;
+
+ INIT_LIST_HEAD(&pcd->gadget.ep_list);
+
+ pcd->gadget.ep0 = &pcd->ep0.ep;
+ pcd->gadget.speed = USB_SPEED_UNKNOWN;
+ INIT_LIST_HEAD(&pcd->gadget.ep0->ep_list);
+
+ /* Initialize the EP0 structure. */
+ ep = &pcd->ep0;
+
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.num = 0;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ /* Init the usb_ep structure. */
+ ep->ep.name = names[0];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* Initialize the EP structures. */
+ in_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3;
+
+ for (i = 1; in_ep_cntr < num_in_eps; i++) {
+ if (!(hwcfg1 & 0x1)) {
+ struct pcd_ep *ep = &pcd->in_ep[in_ep_cntr];
+ in_ep_cntr++;
+
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.is_in = 1;
+ ep->dwc_ep.num = i;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ ep->ep.name = names[i];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ out_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2;
+ for (i = 1; out_ep_cntr < num_out_eps; i++) {
+ if (!(hwcfg1 & 0x1)) {
+ struct pcd_ep *ep = &pcd->out_ep[out_ep_cntr];
+ out_ep_cntr++;
+
+ /* Init EP structure */
+ ep->desc = NULL;
+ ep->pcd = pcd;
+ ep->stopped = 1;
+
+ /* Init DWC ep structure */
+ ep->dwc_ep.is_in = 0;
+ ep->dwc_ep.num = i;
+ ep->dwc_ep.active = 0;
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ /* Control until ep is actvated */
+ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ ep->dwc_ep.dma_addr = 0;
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = 0;
+ ep->queue_sof = 0;
+
+ ep->ep.name = names[15 + i];
+ ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+ ep->ep.maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ /* remove ep0 from the list. There is a ep0 pointer. */
+ list_del_init(&pcd->ep0.ep.ep_list);
+
+ pcd->ep0state = EP0_DISCONNECT;
+ pcd->ep0.ep.maxpacket = MAX_EP0_SIZE;
+ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
+ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+}
+
+/**
+ * This function releases the Gadget device.
+ * required by device_unregister().
+ */
+static void dwc_otg_pcd_gadget_release(struct device *dev)
+{
+ printk(KERN_INFO "%s(%p)\n", __func__, dev);
+}
+
+/**
+ * Allocates the buffers for the setup packets when the PCD portion of the
+ * driver is first initialized.
+ */
+static int __devinit init_pkt_buffs(struct device *dev, struct dwc_pcd *pcd)
+{
+ if (pcd->otg_dev->core_if->dma_enable) {
+ pcd->dwc_pool = dma_pool_create("dwc_otg_pcd", dev,
+ sizeof(*pcd->setup_pkt) * 5, 32, 0);
+ if (!pcd->dwc_pool)
+ return -ENOMEM;
+ pcd->setup_pkt = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+ &pcd->setup_pkt_dma_handle);
+ if (!pcd->setup_pkt)
+ goto error;
+ pcd->status_buf = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+ &pcd->status_buf_dma_handle);
+ if (!pcd->status_buf)
+ goto error1;
+ } else {
+ pcd->setup_pkt = kmalloc(sizeof(*pcd->setup_pkt) * 5,
+ GFP_KERNEL);
+ if (!pcd->setup_pkt)
+ return -ENOMEM;
+ pcd->status_buf = kmalloc(sizeof(u16), GFP_KERNEL);
+ if (!pcd->status_buf) {
+ kfree(pcd->setup_pkt);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+
+error1:
+ dma_pool_free(pcd->dwc_pool, pcd->setup_pkt, pcd->setup_pkt_dma_handle);
+error:
+ dma_pool_destroy(pcd->dwc_pool);
+ return -ENOMEM;
+}
+
+/**
+ * This function initializes the PCD portion of the driver.
+ */
+int __devinit dwc_otg_pcd_init(struct device *dev)
+{
+ static char pcd_name[] = "dwc_otg_pcd";
+ struct dwc_pcd *pcd;
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+ struct core_if *core_if = otg_dev->core_if;
+ int retval = 0;
+
+ /* Allocate PCD structure */
+ pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+ if (!pcd) {
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&pcd->lock);
+
+ otg_dev->pcd = pcd;
+ s_pcd = pcd;
+ pcd->gadget.name = pcd_name;
+
+ dev_set_name(&pcd->gadget.dev, "gadget");
+ pcd->otg_dev = otg_dev;
+ pcd->gadget.dev.parent = dev;
+ pcd->gadget.dev.release = dwc_otg_pcd_gadget_release;
+ pcd->gadget.ops = &dwc_otg_pcd_ops;
+
+ if (core_if->hwcfg4.b.ded_fifo_en)
+ printk(KERN_INFO "Dedicated Tx FIFOs mode\n");
+ else
+ printk(KERN_INFO "Shared Tx FIFO mode\n");
+
+ pcd->gadget.is_dualspeed = check_is_dual_speed(core_if);
+ pcd->gadget.is_otg = check_is_otg(core_if);
+
+ /* Register the gadget device */
+ retval = device_register(&pcd->gadget.dev);
+
+ /* Initialized the Core for Device mode. */
+ if (dwc_otg_is_device_mode(core_if))
+ dwc_otg_core_dev_init(core_if);
+
+ /* Initialize EP structures */
+ dwc_otg_pcd_reinit(pcd);
+
+ /* Register the PCD Callbacks. */
+ dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
+
+ /* Setup interupt handler */
+ retval = request_irq(otg_dev->irq, dwc_otg_pcd_irq, IRQF_SHARED,
+ pcd->gadget.name, pcd);
+ if (retval) {
+ printk(KERN_ERR "request of irq%d failed\n", otg_dev->irq);
+ retval = -EBUSY;
+ goto err_cleanup;
+ }
+
+ /* Initialize the DMA buffer for SETUP packets */
+ retval = init_pkt_buffs(dev, pcd);
+ if (retval)
+ goto err_cleanup;
+
+ /* Initialize tasklet */
+ start_xfer_tasklet.data = (unsigned long) pcd;
+ pcd->start_xfer_tasklet = &start_xfer_tasklet;
+ return 0;
+
+err_cleanup:
+ kfree(pcd);
+ otg_dev->pcd = 0;
+ s_pcd = 0;
+
+err:
+ return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void __devexit dwc_otg_pcd_remove(struct device *dev)
+{
+ struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+ struct dwc_pcd *pcd = otg_dev->pcd;
+
+ /* Free the IRQ */
+ free_irq(otg_dev->irq, pcd);
+
+ /* start with the driver above us */
+ if (pcd->driver) {
+ /* should have been done already by driver model core */
+ printk(KERN_WARNING "driver '%s' is still registered\n",
+ pcd->driver->driver.name);
+ usb_gadget_unregister_driver(pcd->driver);
+ }
+ if (pcd->start_xfer_tasklet)
+ tasklet_kill(pcd->start_xfer_tasklet);
+ tasklet_kill(&pcd->test_mode_tasklet);
+
+ device_unregister(&pcd->gadget.dev);
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ dma_pool_free(pcd->dwc_pool, pcd->setup_pkt,
+ pcd->setup_pkt_dma_handle);
+ dma_pool_free(pcd->dwc_pool, pcd->status_buf,
+ pcd->status_buf_dma_handle);
+ dma_pool_destroy(pcd->dwc_pool);
+ } else {
+ kfree(pcd->setup_pkt);
+ kfree(pcd->status_buf);
+ }
+ kfree(pcd);
+ otg_dev->pcd = 0;
+}
+
+/**
+ * This function registers a gadget driver with the PCD.
+ *
+ * When a driver is successfully registered, it will receive control
+ * requests including set_configuration(), which enables non-control
+ * requests. then usb traffic follows until a disconnect is reported.
+ * then a host may connect again, or the driver might get unbound.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int retval;
+
+ if (!driver || driver->speed == USB_SPEED_UNKNOWN || !driver->bind ||
+ !driver->unbind || !driver->disconnect ||
+ !driver->setup)
+ return -EINVAL;
+
+ if (s_pcd == 0)
+ return -ENODEV;
+
+ if (s_pcd->driver != 0)
+ return -EBUSY;
+
+ /* hook up the driver */
+ s_pcd->driver = driver;
+ s_pcd->gadget.dev.driver = &driver->driver;
+
+ retval = driver->bind(&s_pcd->gadget);
+ if (retval) {
+ printk(KERN_ERR "bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ s_pcd->driver = 0;
+ s_pcd->gadget.dev.driver = 0;
+ return retval;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/**
+ * This function unregisters a gadget driver
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ if (!s_pcd)
+ return -ENODEV;
+ if (!driver || driver != s_pcd->driver)
+ return -EINVAL;
+
+ driver->unbind(&s_pcd->gadget);
+ s_pcd->driver = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
diff --git a/drivers/usb/otg/dwc_otg_pcd.h b/drivers/usb/otg/dwc_otg_pcd.h
new file mode 100644
index 0000000..93e2434
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_pcd.h
@@ -0,0 +1,159 @@
+/*
+ * DesignWare HS OTG controller driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * Based on versions provided by AMCC and Synopsis which are:
+ * Copyright (C) 2009-2010 AppliedMicro(www.apm.com)
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(__DWC_PCD_H__)
+#define __DWC_PCD_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+struct lm_device;
+struct dwc_otg_device;
+
+#include "dwc_otg_cil.h"
+
+/*
+ * This file contains the structures, constants, and interfaces for
+ * the Perpherial Contoller Driver (PCD).
+ *
+ * The Peripheral Controller Driver (PCD) for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used. For
+ * the Mass Storage Function driver the File-backed USB Storage Gadget
+ * (FBS) driver will be used. The FBS driver supports the
+ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
+ * transports.
+ *
+ */
+
+/* Invalid DMA Address */
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+/* Maxpacket size for EP0 */
+#define MAX_EP0_SIZE 64
+/* Maxpacket size for any EP */
+#define MAX_PACKET_SIZE 1024
+
+/*
+ * Get the pointer to the core_if from the pcd pointer.
+ */
+#define GET_CORE_IF(_pcd) (_pcd->otg_dev->core_if)
+
+/*
+ * DWC_otg request structure.
+ * This structure is a list of requests.
+ */
+struct pcd_request {
+ struct usb_request req; /* USB Request. */
+ struct list_head queue; /* queue of these requests. */
+ unsigned mapped:1;
+};
+
+static inline u32 *in_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+ return (u32 *) &GET_CORE_IF(pd)->dev_if->in_ep_regs[i]->diepint;
+}
+static inline u32 *out_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+ return (u32 *) &GET_CORE_IF(pd)->dev_if->out_ep_regs[i]->doepint;
+}
+static inline u32 *in_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+ return (u32 *) &GET_CORE_IF(pd)->dev_if->in_ep_regs[i]->diepctl;
+}
+
+static inline u32 *out_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+ return (u32 *) &GET_CORE_IF(pd)->dev_if->out_ep_regs[i]->doepctl;
+}
+
+static inline u32 *dev_ctl_reg(struct dwc_pcd *pd)
+{
+ return (u32 *) &(GET_CORE_IF(pd)->dev_if->dev_global_regs->dctl);
+}
+
+static inline u32 *dev_diepmsk_reg(struct dwc_pcd *pd)
+{
+ return (u32 *) &(GET_CORE_IF(pd)->dev_if->dev_global_regs->diepmsk);
+}
+
+static inline u32 *dev_sts_reg(struct dwc_pcd *pd)
+{
+ return (u32 *) &(GET_CORE_IF(pd)->dev_if->dev_global_regs->dsts);
+}
+
+static inline u32 *otg_ctl_reg(struct dwc_pcd *pd)
+{
+ return (u32 *) &(GET_CORE_IF(pd)->core_global_regs->gotgctl);
+}
+
+extern int __init dwc_otg_pcd_init(struct device *dev);
+
+/*
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+extern void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_pcd_remove(struct device *dev);
+extern int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd);
+extern void dwc_otg_pcd_stop(struct dwc_pcd *pcd);
+extern void request_nuke(struct pcd_ep *ep);
+extern void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset);
+extern void dwc_otg_ep0_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+
+extern void request_done(struct pcd_ep *ep, struct pcd_request *req,
+ int _status);
+
+extern void start_next_request(struct pcd_ep *ep);
+
+#endif
--
1.6.0.1
next prev parent reply other threads:[~2010-06-29 22:22 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-06-29 21:26 [PATCH 1/9] Add Synopsys DesignWare HS USB OTG Controller driver Fushen Chen
2010-06-29 21:26 ` [PATCH 2/9] " Fushen Chen
2010-06-29 21:26 ` [PATCH 3/9] " Fushen Chen
2010-06-29 21:26 ` [PATCH 4/9] " Fushen Chen
2010-06-29 21:26 ` [PATCH 5/9] " Fushen Chen
2010-06-29 21:26 ` [PATCH 6/9] " Fushen Chen
2010-06-29 21:27 ` Fushen Chen [this message]
2010-06-29 21:27 ` [PATCH 8/9] " Fushen Chen
2010-06-29 21:27 ` [PATCH 9/9] " Fushen Chen
2010-06-29 23:13 ` [PATCH 4/9] " Wolfgang Denk
2010-06-29 23:22 ` David Daney
2010-06-30 5:31 ` Wolfgang Denk
2010-06-29 22:41 ` [PATCH 1/9] " David Brownell
2010-06-29 23:13 ` David Daney
2010-06-30 7:06 ` Anton Vorontsov
2010-06-30 9:53 ` Daniel Glöckner
2010-06-30 21:51 ` fushen chen
2010-06-30 9:27 ` Stefan Roese
2010-06-30 20:16 ` fushen chen
2010-07-01 9:20 ` Stefan Roese
2010-06-30 10:10 ` Sergei Shtylyov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=12778468242153-git-send-email-fchen@apm.com \
--to=fchen@apm.com \
--cc=gregkh@suse.de \
--cc=linux-usb@vger.kernel.org \
--cc=linuxppc-dev@ozlabs.org \
--cc=mmiesfeld@apm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.