* [PATCH 7/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: Fushen Chen @ 2010-07-12 23:16 UTC (permalink / raw)
To: linux-usb; +Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <12789766062832-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 | 160 ++++
2 files changed, 1909 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..f0c3b04
--- /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)
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * 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);
+ msleep(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..dee651c
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_pcd.h
@@ -0,0 +1,160 @@
+/*
+ * 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)
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * 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.7.1
^ permalink raw reply related
* [PATCH 9/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: Fushen Chen @ 2010-07-12 23:16 UTC (permalink / raw)
To: linux-usb; +Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <1278976606696-git-send-email-fchen@apm.com>
Interfaces with the DWC HS OTG core by reading from and writing to
the Control and Status Register (CSR).
CSRs are classified as follows:
- Core Global Registers
- Device Mode Registers
- Device Global Registers
- Device Endpoint Specific Registers
- Host Mode Registers
- Host Global Registers
- Host Port CSRs
- Host Channel Specific Registers
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
drivers/usb/otg/dwc_otg_regs.h | 3283 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 3283 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/otg/dwc_otg_regs.h
diff --git a/drivers/usb/otg/dwc_otg_regs.h b/drivers/usb/otg/dwc_otg_regs.h
new file mode 100644
index 0000000..86c7406
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_regs.h
@@ -0,0 +1,3283 @@
+/*
+ * 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.
+ */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+#include <linux/types.h>
+
+/*
+ * This file contains the data structures for accessing the DWC_otg core
+ * registers.
+ *
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/*
+ * DWC_otg Core registers. The core_global_regs structure defines the
+ * size and relative field offsets for the Core Global registers.
+ */
+struct core_global_regs {
+ /* OTG Control and Status Register. Offset: 000h */
+ u32 gotgctl;
+ /* OTG Interrupt Register. Offset: 004h */
+ u32 gotgint;
+ /* Core AHB Configuration Register. Offset: 008h */
+ u32 gahbcfg;
+
+#define DWC_GLBINTRMASK 0x0001
+#define DWC_DMAENABLE 0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY 0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000
+#define DWC_PTXEMPTYLVL_EMPTY 0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000
+
+ /* Core USB Configuration Register. Offset: 00Ch */
+ u32 gusbcfg;
+ /* Core Reset Register. Offset: 010h */
+ u32 grstctl;
+ /* Core Interrupt Register. Offset: 014h */
+ u32 gintsts;
+ /* Core Interrupt Mask Register. Offset: 018h */
+ u32 gintmsk;
+ /*
+ * Receive Status Queue Read Register
+ * (Read Only) Offset: 01Ch
+ */
+ u32 grxstsr;
+ /*
+ * Receive Status Queue Read & POP Register
+ * (Read Only) Offset: 020h
+ */
+ u32 grxstsp;
+ /* Receive FIFO Size Register. Offset: 024h */
+ u32 grxfsiz;
+ /* Non Periodic Transmit FIFO Size Register. Offset: 028h */
+ u32 gnptxfsiz;
+ /*
+ * Non Periodic Transmit FIFO/Queue Status Register
+ * (Read Only). Offset: 02Ch
+ */
+ u32 gnptxsts;
+ /* I2C Access Register. Offset: 030h */
+ u32 gi2cctl;
+ /* PHY Vendor Control Register. Offset: 034h */
+ u32 gpvndctl;
+ /* General Purpose Input/Output Register. Offset: 038h */
+ u32 ggpio;
+ /* User ID Register. Offset: 03Ch */
+ u32 guid;
+ /* Synopsys ID Register (Read Only). Offset: 040h */
+ u32 gsnpsid;
+ /* User HW Config1 Register (Read Only). Offset: 044h */
+ u32 ghwcfg1;
+ /* User HW Config2 Register (Read Only). Offset: 048h */
+ u32 ghwcfg2;
+#define DWC_SLAVE_ONLY_ARCH 0
+#define DWC_EXT_DMA_ARCH 1
+#define DWC_INT_DMA_ARCH 2
+
+#define DWC_MODE_HNP_SRP_CAPABLE 0
+#define DWC_MODE_SRP_ONLY_CAPABLE 1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE 2
+#define DWC_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_MODE_SRP_CAPABLE_HOST 5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST 6
+
+ /* User HW Config3 Register (Read Only). Offset: 04Ch */
+ u32 ghwcfg3;
+ /* User HW Config4 Register (Read Only). Offset: 050h */
+ u32 ghwcfg4;
+ /* Reserved Offset: 054h-0FFh */
+ u32 reserved[43];
+ /* Host Periodic Transmit FIFO Size Register. Offset: 100h */
+ u32 hptxfsiz;
+
+ /*
+ * Device Periodic Transmit FIFO#n Register, if dedicated fifos are
+ * disabled. Otherwise Device Transmit FIFO#n Register.
+ *
+ * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15)
+ */
+ u32 dptxfsiz_dieptxf[15];
+};
+
+
+#if defined(CONFIG_DWC_OTG_REG_LE)
+/*
+ * This union represents the bit fields of the Core OTG Controland Status
+ * Register (GOTGCTL). Set the bits using the bit fields then write the d32
+ * value to the register.
+ */
+union gotgctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_21:11;
+ unsigned currmod:1;
+ unsigned bsesvld:1;
+ unsigned asesvld:1;
+ unsigned reserved17:1;
+ unsigned conidsts:1;
+ unsigned reserved1_12:4;
+ unsigned devhnpen:1;
+ unsigned hstsethnpen:1;
+ unsigned hnpreq:1;
+ unsigned hstnegscs:1;
+ unsigned reserved07_02:6;
+ unsigned sesreq:1;
+ unsigned sesreqscs:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT). Set/clear the bits using the bit fields then write the d32
+ * value to the register.
+ */
+union gotgint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Current Mode */
+ unsigned reserved31_20:12;
+ /* Debounce Done */
+ unsigned debdone:1;
+ /* A-Device Timeout Change */
+ unsigned adevtoutchng:1;
+ /* Host Negotiation Detected */
+ unsigned hstnegdet:1;
+ unsigned reserver16_10:7;
+ /* Host Negotiation Success Status Change */
+ unsigned hstnegsucstschng:1;
+ /* Session Request Success Status Change */
+ unsigned sesreqsucstschng:1;
+ unsigned reserved3_7:5;
+ /* Session End Detected */
+ unsigned sesenddet:1;
+ unsigned reserved01_00:2;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core AHB Configuration Register
+ * (GAHBCFG). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gahbcfg_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved9_31:23;
+ unsigned ptxfemplvl:1;
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+
+ unsigned nptxfemplvl_txfemplvl:1;
+ unsigned reserved:1;
+ unsigned dmaenable:1;
+#define DWC_GAHBCFG_DMAENABLE 1
+
+ unsigned hburstlen:4;
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
+
+ unsigned glblintrmsk:1;
+#define DWC_GAHBCFG_GLBINT_ENABLE 1
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core USB Configuration Register
+ * (GUSBCFG). Set the bits using the bit fields then write the d32 value to the
+ * register.
+ */
+union gusbcfg_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned corrupt_tx_packet:1;
+ unsigned force_device_mode:1;
+ unsigned force_host_mode:1;
+ unsigned reserved23_28:6;
+ unsigned term_sel_dl_pulse:1;
+ unsigned ulpi_int_vbus_indicator:1;
+ unsigned ulpi_ext_vbus_drv:1;
+ unsigned ulpi_clk_sus_m:1;
+ unsigned ulpi_auto_res:1;
+ unsigned ulpi_fsls:1;
+
+ unsigned otgutmifssel:1;
+ unsigned phylpwrclksel:1;
+ unsigned nptxfrwnden:1;
+ unsigned usbtrdtim:4;
+ unsigned hnpcap:1;
+ unsigned srpcap:1;
+ unsigned ddrsel:1;
+ unsigned physel:1;
+ unsigned fsintf:1;
+ unsigned ulpi_utmi_sel:1;
+ unsigned phyif:1;
+ unsigned toutcal:3;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core Reset Register (GRSTCTL).
+ * Set/clear the bits using the bit fields then write the d32 value to the
+ * register.
+ */
+union grstctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /*
+ * AHB Master Idle. Indicates the AHB Master State Machine is
+ * in IDLE condition.
+ */
+ unsigned ahbidle:1;
+
+ /*
+ * DMA Request Signal. Indicated DMA request is in probress.
+ Used for debug purpose.
+ */
+ unsigned dmareq:1;
+
+ /* Reserved */
+ unsigned reserved29_11:19;
+
+ /*
+ * TxFIFO Number (TxFNum) (Device and Host).
+ *
+ * This is the FIFO number which needs to be flushed,
+ * using the TxFIFO Flush bit. This field should not
+ * be changed until the TxFIFO Flush bit is cleared by
+ * the core.
+ * - 0x0:Non Periodic TxFIFO Flush
+ * - 0x1 : Periodic TxFIFO #1 Flush in device mode
+ * or Periodic TxFIFO in host mode
+ * - 0x2 : Periodic TxFIFO #2 Flush in device mode.
+ * - ...
+ * - 0xF : Periodic TxFIFO #15 Flush in device mode
+ * - 0x10: Flush all the Transmit NonPeriodic and
+ * Transmit Periodic FIFOs in the core
+ */
+ unsigned txfnum:5;
+#define DWC_GRSTCTL_TXFNUM_ALL 0x10
+
+ /*
+ * TxFIFO Flush (TxFFlsh) (Device and Host).
+ *
+ * This bit is used to selectively flush a single or all
+ * transmit FIFOs. The application must first ensure that the
+ * core is not in the middle of a transaction.
+ *
+ * The application should write into this bit, only after
+ * making sure that neither the DMA engine is writing into the
+ * TxFIFO nor the MAC is reading the data out of the FIFO.
+ *
+ * The application should wait until the core clears this bit,
+ * before performing any operations. This bit will takes 8
+ * clocks (slowest of PHY or AHB clock) to clear.
+ */
+ unsigned txfflsh:1;
+
+ /*
+ * RxFIFO Flush (RxFFlsh) (Device and Host)
+ *
+ * The application can flush the entire Receive FIFO using this
+ * bit.
+ *
+ * The application must first ensure that the core is not in the
+ * middle of a transaction.
+ *
+ * The application should write into this bit, only after making
+ * sure that neither the DMA engine is reading from the RxFIFO
+ * nor the MAC is writing the data in to the FIFO.
+ *
+ * The application should wait until the bit is cleared before
+ * performing any other operations. This bit will takes 8 clocks
+ * (slowest of PHY or AHB clock) to clear.
+ */
+ unsigned rxfflsh:1;
+
+ /*
+ * In Token Sequence Learning Queue Flush
+ * (INTknQFlsh) (Device Only)
+ */
+ unsigned intknqflsh:1;
+
+ /*
+ * Host Frame Counter Reset (Host Only)<br>
+ *
+ * The application can reset the (micro)frame number
+ * counter inside the core, using this bit. When the
+ * (micro)frame counter is reset, the subsequent SOF
+ * sent out by the core, will have a (micro)frame
+ * number of 0.
+ */
+ unsigned hstfrm:1;
+
+ /*
+ * Hclk Soft Reset
+ *
+ * The application uses this bit to reset the control logic in
+ * the AHB clock domain. Only AHB clock domain pipelines are
+ * reset.
+ */
+ unsigned hsftrst:1;
+
+ /*
+ * Core Soft Reset (CSftRst) (Device and Host)
+ *
+ * The application can flush the control logic in the
+ * entire core using this bit. This bit resets the
+ * pipelines in the AHB Clock domain as well as the
+ * PHY Clock domain.
+ *
+ * The state machines are reset to an IDLE state, the
+ * control bits in the CSRs are cleared, all the
+ * transmit FIFOs and the receive FIFO are flushed.
+ *
+ * The status mask bits that control the generation of
+ * the interrupt, are cleared, to clear the
+ * interrupt. The interrupt status bits are not
+ * cleared, so the application can get the status of
+ * any events that occurred in the core after it has
+ * set this bit.
+ *
+ * Any transactions on the AHB are terminated as soon
+ * as possible following the protocol. Any
+ * transactions on the USB are terminated immediately.
+ *
+ * The configuration settings in the CSRs are
+ * unchanged, so the software doesn't have to
+ * reprogram these registers (Device
+ * Configuration/Host Configuration/Core System
+ * Configuration/Core PHY Configuration).
+ *
+ * The application can write to this bit, any time it
+ * wants to reset the core. This is a self clearing
+ * bit and the core clears this bit after all the
+ * necessary logic is reset in the core, which may
+ * take several clocks, depending on the current state
+ * of the core.
+ */
+ unsigned csftrst:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core Interrupt Mask Register
+ * (GINTMSK). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gintmsk_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned wkupintr:1;
+ unsigned sessreqintr:1;
+ unsigned disconnect:1;
+ unsigned conidstschng:1;
+ unsigned reserved27:1;
+ unsigned ptxfempty:1;
+ unsigned hcintr:1;
+ unsigned portintr:1;
+ unsigned reserved23_22:2;
+ unsigned incomplisoout:1;
+ unsigned incomplisoin:1;
+ unsigned outepintr:1;
+ unsigned inepintr:1;
+ unsigned epmismatch:1;
+ unsigned reserved16:1;
+ unsigned eopframe:1;
+ unsigned isooutdrop:1;
+ unsigned enumdone:1;
+ unsigned usbreset:1;
+ unsigned usbsuspend:1;
+ unsigned erlysuspend:1;
+ unsigned i2cintr:1;
+ unsigned reserved08:1;
+ unsigned goutnakeff:1;
+ unsigned ginnakeff:1;
+ unsigned nptxfempty:1;
+ unsigned rxstsqlvl:1;
+ unsigned sofintr:1;
+ unsigned otgintr:1;
+ unsigned modemismatch:1;
+ unsigned reserved00:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core Interrupt Register
+ * (GINTSTS). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gintsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+#define DWC_SOF_INTR_MASK 0x0008
+ struct {
+#define DWC_HOST_MODE 1
+ unsigned wkupintr:1;
+ unsigned sessreqintr:1;
+ unsigned disconnect:1;
+ unsigned conidstschng:1;
+ unsigned reserved27:1;
+ unsigned ptxfempty:1;
+ unsigned hcintr:1;
+ unsigned portintr:1;
+ unsigned reserved22_23:2;
+ unsigned incomplisoout:1;
+ unsigned incomplisoin:1;
+ unsigned outepintr:1;
+ unsigned inepint:1;
+ unsigned epmismatch:1;
+ unsigned intokenrx:1;
+ unsigned eopframe:1;
+ unsigned isooutdrop:1;
+ unsigned enumdone:1;
+ unsigned usbreset:1;
+ unsigned usbsuspend:1;
+ unsigned erlysuspend:1;
+ unsigned i2cintr:1;
+ unsigned reserved8:1;
+ unsigned goutnakeff:1;
+ unsigned ginnakeff:1;
+ unsigned nptxfempty:1;
+ unsigned rxstsqlvl:1;
+ unsigned sofintr:1;
+ unsigned otgintr:1;
+ unsigned modemismatch:1;
+ unsigned curmode:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32
+ * element then read out the bits using the bit elements.
+ */
+union device_grxsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:7;
+ unsigned fn:4;
+ unsigned pktsts:4;
+#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */
+#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */
+#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */
+#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */
+#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */
+
+ unsigned dpid:2;
+ unsigned bcnt:11;
+ unsigned epnum:4;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32
+ * element then read out the bits using the bit elements.
+ */
+union host_grxsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_21:11;
+ unsigned pktsts:4;
+#define DWC_GRXSTS_PKTSTS_IN 0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7
+
+ unsigned dpid:2;
+ unsigned bcnt:11;
+ unsigned chnum:4;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn). Read the register into the d32 element then
+ * read out the bits using the bit elements.
+ */
+union fifosize_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned depth:16;
+ unsigned startaddr:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Non-Periodic Transmit FIFO/Queue
+ * Status Register (GNPTXSTS). Read the register into the d32 element then read
+ * out the bits using the bit elements.
+ */
+union gnptxsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:1;
+ /* Top of the Non-Periodic Transmit Request Queue
+ * - bits 30:27 - Channel/EP Number
+ * - bits 26:25 - Token Type
+ * - 2'b00 - IN/OUT
+ * - 2'b01 - Zero Length OUT
+ * - 2'b10 - PING/Complete Split
+ * - 2'b11 - Channel Halt
+ * - bit 24 - Terminate (Last entry for the selected
+ * channel/EP)
+ */
+ unsigned nptxqtop_chnep:4;
+ unsigned nptxqtop_token:2;
+ unsigned nptxqtop_terminate:1;
+ unsigned nptxqspcavail:8;
+ unsigned nptxfspcavail:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Transmit FIFO Status Register
+ * (DTXFSTS). Read the register into the d32 element then read out the bits
+ * using the bit elements.
+ */
+union dtxfsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:16;
+ unsigned txfspcavail:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the I2C Control Register (I2CCTL).
+ * Read the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union gi2cctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned bsydne:1;
+ unsigned rw:1;
+ unsigned reserved:2;
+ unsigned i2cdevaddr:2;
+ unsigned i2csuspctl:1;
+ unsigned ack:1;
+ unsigned i2cen:1;
+ unsigned addr:7;
+ unsigned regaddr:8;
+ unsigned rwdata:8;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config1 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg1_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned ep_dir15:2;
+ unsigned ep_dir14:2;
+ unsigned ep_dir13:2;
+ unsigned ep_dir12:2;
+ unsigned ep_dir11:2;
+ unsigned ep_dir10:2;
+ unsigned ep_dir9:2;
+ unsigned ep_dir8:2;
+ unsigned ep_dir7:2;
+ unsigned ep_dir6:2;
+ unsigned ep_dir5:2;
+ unsigned ep_dir4:2;
+ unsigned ep_dir3:2;
+ unsigned ep_dir2:2;
+ unsigned ep_dir1:2;
+ unsigned ep_dir0:2;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config2 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg2_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* GHWCFG2 */
+ unsigned reserved31:1;
+ unsigned dev_token_q_depth:5;
+ unsigned host_perio_tx_q_depth:2;
+ unsigned nonperio_tx_q_depth:2;
+ unsigned rx_status_q_depth:2;
+ unsigned dynamic_fifo:1;
+ unsigned perio_ep_supported:1;
+ unsigned num_host_chan:4;
+ unsigned num_dev_ep:4;
+ unsigned fs_phy_type:2;
+ unsigned hs_phy_type:2;
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+
+ unsigned point2point:1;
+ unsigned architecture:2;
+ unsigned op_mode:3;
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config3 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg3_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* GHWCFG3 */
+ unsigned dfifo_depth:16;
+ unsigned reserved15_13:3;
+ unsigned ahb_phy_clock_synch:1;
+ unsigned synch_reset_type:1;
+ unsigned optional_features:1;
+ unsigned vendor_ctrl_if:1;
+ unsigned i2c:1;
+ unsigned otg_func:1;
+ unsigned packet_size_cntr_width:3;
+ unsigned xfer_size_cntr_width:4;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config4 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg4_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_30:2;
+ unsigned num_in_eps:4;
+ unsigned ded_fifo_en:1;
+
+ unsigned session_end_filt_en:1;
+ unsigned b_valid_filt_en:1;
+ unsigned a_valid_filt_en:1;
+ unsigned vbus_valid_filt_en:1;
+ unsigned iddig_filt_en:1;
+ unsigned num_dev_mode_ctrl_ep:4;
+ unsigned utmi_phy_data_width:2;
+ unsigned min_ahb_freq:9;
+ unsigned power_optimiz:1;
+ unsigned num_dev_perio_in_ep:4;
+ } b;
+};
+
+/*
+ * Device Global Registers. Offsets 800h-BFFh
+ *
+ * The following structures define the size and relative field offsets for the
+ * Device Mode Registers.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */
+ /* Device Configuration Register. Offset: 800h */
+ u32 dcfg;
+ /* Device Control Register. Offset: 804h */
+ u32 dctl;
+ /* Device Status Register (Read Only). Offset: 808h */
+ u32 dsts;
+ /* Reserved. Offset: 80Ch */
+ u32 unused;
+ /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */
+ u32 diepmsk;
+ /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */
+ u32 doepmsk;
+ /* Device All Endpoints Interrupt Register. Offset: 818h */
+ u32 daint;
+ /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */
+ u32 daintmsk;
+ /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */
+ u32 dtknqr1;
+ /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */
+ u32 dtknqr2;
+ /* Device VBUS discharge Register. Offset: 828h */
+ u32 dvbusdis;
+ /* Device VBUS Pulse Register. Offset: 82Ch */
+ u32 dvbuspulse;
+ /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */
+ u32 dtknqr3_dthrctl;
+ /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */
+ u32 dtknqr4_fifoemptymsk;
+};
+
+/*
+ * This union represents the bit fields in the Device Configuration
+ * Register. Read the register into the d32 member then
+ * set/clear the bits using the bit elements. Write the
+ * d32 member to the dcfg register.
+ */
+union dcfg_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved0_8:9;
+ unsigned epmscnt:5;
+ /* In Endpoint Mis-match count */
+ unsigned reserved17_13:5;
+ /* Periodic Frame Interval */
+ unsigned perfrint:2;
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+
+ /* Device Addresses */
+ unsigned devaddr:7;
+ unsigned reserved3:1;
+ /* Non Zero Length Status OUT Handshake */
+ unsigned nzstsouthshk:1;
+#define DWC_DCFG_SEND_STALL 1
+
+ /* Device Speed */
+ unsigned devspd:2;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Control Register. Read
+ * the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union dctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_12:21;
+ /* Clear Global OUT NAK */
+ unsigned cgoutnak:1;
+ /* Set Global OUT NAK */
+ unsigned sgoutnak:1;
+ /* Clear Global Non-Periodic IN NAK */
+ unsigned cgnpinnak:1;
+ /* Set Global Non-Periodic IN NAK */
+ unsigned sgnpinnak:1;
+ /* Test Control */
+ unsigned tstctl:3;
+ /* Global OUT NAK Status */
+ unsigned goutnaksts:1;
+ /* Global Non-Periodic IN NAK Status */
+ unsigned gnpinnaksts:1;
+ /* Soft Disconnect */
+ unsigned sftdiscon:1;
+ /* Remote Wakeup */
+ unsigned rmtwkupsig:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Status Register. Read the
+ * register into the d32 member then set/clear the bits using the bit elements.
+ */
+union dsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_22:10;
+ /* Frame or Microframe Number of the received SOF */
+ unsigned soffn:14;
+ unsigned reserved07_04:4;
+ /* Erratic Error */
+ unsigned errticerr:1;
+ /* Enumerated Speed */
+ unsigned enumspd:2;
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
+ /* Suspend Status */
+ unsigned suspsts:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device IN EP Interrupt Register
+ * and the Device IN EP Common Mask Register.
+ *
+ * Read the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union diepint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_08:23;
+ unsigned txfifoundrn:1;
+ /* IN Endpoint HAK Effective mask */
+ unsigned emptyintr:1;
+ /* IN Endpoint NAK Effective mask */
+ unsigned inepnakeff:1;
+ /* IN Token Received with EP mismatch mask */
+ unsigned intknepmis:1;
+ /* IN Token received with TxF Empty mask */
+ unsigned intktxfemp:1;
+ /* TimeOUT Handshake mask (non-ISOC EPs) */
+ unsigned timeout:1;
+ /* AHB Error mask */
+ unsigned ahberr:1;
+ /* Endpoint disable mask */
+ unsigned epdisabled:1;
+ /* Transfer complete mask */
+ unsigned xfercompl:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device OUT EP Interrupt Register
+ * and Device OUT EP Common Interrupt Mask Register.
+ *
+ * Read the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union doepint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31_04:28; /* Docs say reserved is 27 bits */
+
+ /* There is 1 bit missing here, not used? */
+
+ /* Setup Phase Done (control EPs) */
+ unsigned setup:1;
+ /* AHB Error */
+ unsigned ahberr:1;
+ /* Endpoint disable */
+ unsigned epdisabled:1;
+ /* Transfer complete */
+ unsigned xfercompl:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device All EP Interrupt and Mask
+ * Registers. Read the register into the d32 member then set/clear the bits
+ * using the bit elements.
+ */
+union daint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* OUT Endpoint bits */
+ unsigned out:16;
+ /* IN Endpoint bits */
+ unsigned in:16;
+ } ep;
+ struct {
+ /* OUT Endpoint bits */
+ unsigned outep15:1;
+ unsigned outep14:1;
+ unsigned outep13:1;
+ unsigned outep12:1;
+ unsigned outep11:1;
+ unsigned outep10:1;
+ unsigned outep9:1;
+ unsigned outep8:1;
+ unsigned outep7:1;
+ unsigned outep6:1;
+ unsigned outep5:1;
+ unsigned outep4:1;
+ unsigned outep3:1;
+ unsigned outep2:1;
+ unsigned outep1:1;
+ unsigned outep0:1;
+ /* IN Endpoint bits */
+ unsigned inep15:1;
+ unsigned inep14:1;
+ unsigned inep13:1;
+ unsigned inep12:1;
+ unsigned inep11:1;
+ unsigned inep10:1;
+ unsigned inep9:1;
+ unsigned inep8:1;
+ unsigned inep7:1;
+ unsigned inep6:1;
+ unsigned inep5:1;
+ unsigned inep4:1;
+ unsigned inep3:1;
+ unsigned inep2:1;
+ unsigned inep1:1;
+ unsigned inep0:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device IN Token Queue Read
+ * Registers. Read the register into the d32 member. READ-ONLY Register
+ */
+union dtknq1_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* EP Numbers of IN Tokens 0 ... 4 */
+ unsigned epnums0_5:24;
+ /* write pointer has wrapped. */
+ unsigned wrap_bit:1;
+ /* Reserved */
+ unsigned reserved05_06:2;
+ /* In Token Queue Write Pointer */
+ unsigned intknwptr:5;
+ } b;
+};
+
+/*
+ * This union represents Threshold control Register. Read and write the register
+ * into the d32 member. READ-WRITABLE Register
+ */
+union dthrctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Reserved */
+ unsigned reserved26_31:6;
+ /* Rx Thr. Length */
+ unsigned rx_thr_len:9;
+ /* Rx Thr. Enable */
+ unsigned rx_thr_en:1;
+ /* Reserved */
+ unsigned reserved11_15:5;
+ /* Tx Thr. Length */
+ unsigned tx_thr_len:9;
+ /* ISO Tx Thr. Enable */
+ unsigned iso_thr_en:1;
+ /* non ISO Tx Thr. Enable */
+ unsigned non_iso_thr_en:1;
+ } b;
+};
+
+/*
+ * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+ struct device_in_ep_regs {
+ /*
+ * Device IN Endpoint Control Register.
+ * Offset:900h + (ep_num * 20h) + 00h
+ */
+ u32 diepctl;
+ /* Reserved. Offset:900h + (ep_num * 20h) + 04h */
+ u32 reserved04;
+ /*
+ * Device IN Endpoint Interrupt Register.
+ * Offset:900h + (ep_num * 20h) + 08h
+ */
+ u32 diepint;
+ /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /* Device IN Endpoint Transfer Size Register.
+ * Offset:900h + (ep_num * 20h) + 10h
+ */
+ u32 dieptsiz;
+ /*
+ * Device IN Endpoint DMA Address Register.
+ * Offset:900h + (ep_num * 20h) + 14h
+ */
+ u32 diepdma;
+ /* Reserved.
+ * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch
+ */
+ u32 dtxfsts;
+ /*
+ * Reserved.
+ * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch
+ */
+ u32 reserved18;
+};
+
+/*
+ * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_out_ep_regs {
+ /*
+ * Device OUT Endpoint Control Register.
+ * Offset:B00h + (ep_num * 20h) + 00h
+ */
+ u32 doepctl;
+ /*
+ * Device OUT Endpoint Frame number Register.
+ * Offset: B00h + (ep_num * 20h) + 04h
+ */
+ u32 doepfn;
+ /*
+ * Device OUT Endpoint Interrupt Register.
+ * Offset:B00h + (ep_num * 20h) + 08h
+ */
+ u32 doepint;
+ /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /*
+ * Device OUT Endpoint Transfer Size Register.
+ * Offset: B00h + (ep_num * 20h) + 10h
+ */
+ u32 doeptsiz;
+ /*
+ * Device OUT Endpoint DMA Address Register.
+ * Offset:B00h + (ep_num * 20h) + 14h
+ */
+ u32 doepdma;
+ /*
+ * Reserved.
+ * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch
+ */
+ u32 unused[2];
+};
+
+/*
+ * This union represents the bit fields in the Device EP Control Register. Read
+ * the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union depctl_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Endpoint Enable */
+ unsigned epena:1;
+ /* Endpoint Disable */
+ unsigned epdis:1;
+
+ /*
+ * Set DATA1 PID (INTR/Bulk IN and OUT endpoints) Writing to
+ * this field sets the Endpoint DPID (DPID) field in this
+ * register to DATA1 Set Odd (micro)frame (SetOddFr) (ISO IN and
+ * OUT Endpoints) Writing to this field sets the Even/Odd
+ * (micro)frame (EO_FrNum) field to odd (micro) frame.
+ */
+ unsigned setd1pid:1;
+ /*
+ * Set DATA0 PID (INTR/Bulk IN and OUT endpoints) Writing to
+ * this field sets the Endpoint DPID (DPID) field in this
+ * register to DATA0. Set Even (micro)frame (SetEvenFr) (ISO IN
+ * and OUT Endpoints) Writing to this field sets the Even/Odd
+ * (micro)frame (EO_FrNum) field to even (micro) frame.
+ */
+ unsigned setd0pid:1;
+
+ /* Set NAK */
+ unsigned snak:1;
+ /* Clear NAK */
+ unsigned cnak:1;
+
+ /*
+ * Tx Fifo Number
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved
+ */
+ unsigned txfnum:4;
+
+ /* Stall Handshake */
+ unsigned stall:1;
+
+ /* Snoop Mode
+ * OUT EPn/OUT EP0
+ * IN EPn/IN EP0 - reserved
+ */
+ unsigned snp:1;
+
+ /* Endpoint Type
+ * 2'b00: Control
+ * 2'b01: Isochronous
+ * 2'b10: Bulk
+ * 2'b11: Interrupt
+ */
+ unsigned eptype:2;
+
+ /* NAK Status */
+ unsigned naksts:1;
+
+ /*
+ * Endpoint DPID (INTR/Bulk IN and OUT endpoints) This field
+ * contains the PID of the packet going to be received or
+ * transmitted on this endpoint. The application should program
+ * the PID of the first packet going to be received or
+ * transmitted on this endpoint, after the endpoint is
+ * activated. Applications use the SetD1PID and SetD0PID fields
+ * of this register to program either D0 or D1 PID.
+ *
+ * The encoding for this field is
+ * - 0: D0
+ * - 1: D1
+ */
+ unsigned dpid:1;
+
+ /* USB Active Endpoint */
+ unsigned usbactep:1;
+
+ /*
+ * Next Endpoint
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved
+ */
+ unsigned nextep:4;
+
+ /*
+ * Maximum Packet Size
+ * IN/OUT EPn
+ * IN/OUT EP0 - 2 bits
+ * 2'b00: 64 Bytes
+ * 2'b01: 32
+ * 2'b10: 16
+ * 2'b11: 8
+ */
+ unsigned mps:11;
+#define DWC_DEP0CTL_MPS_64 0
+#define DWC_DEP0CTL_MPS_32 1
+#define DWC_DEP0CTL_MPS_16 2
+#define DWC_DEP0CTL_MPS_8 3
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device EP Transfer Size Register.
+ * Read the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union deptsiz_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * Correct ther register layout for the 405EZ Ultra
+ * USB device implementation.
+ */
+#ifdef CONFIG_DWC_LIMITED_XFER_SIZE
+ struct {
+ unsigned reserved:1;
+ /* Multi Count - Periodic IN endpoints */
+ unsigned mc:2;
+ unsigned reserved1:5;
+ /* Packet Count */
+ unsigned pktcnt:5;
+ unsigned reserved2:8;
+ /* Transfer size */
+ unsigned xfersize:11;
+ } b;
+#else
+ struct {
+ unsigned reserved:1;
+ /* Multi Count - Periodic IN endpoints */
+ unsigned mc:2;
+ /* Packet Count */
+ unsigned pktcnt:10;
+ /* Transfer size */
+ unsigned xfersize:19;
+ } b;
+#endif
+};
+
+/*
+ * This union represents the bit fields in the Device EP 0 Transfer Size
+ * Register. Read the register into the d32 member then set/clear the bits
+ * using the bit elements.
+ */
+union deptsiz0_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved31:1; /* device*/
+ /*Setup Packet Count (DOEPTSIZ0 Only) */
+ unsigned supcnt:2;
+ /* Reserved */
+ unsigned reserved28_20:9;
+ /* Packet Count */
+ unsigned pktcnt:1;
+ /* Reserved */
+ unsigned reserved18_7:12;
+ /* Transfer size */
+ unsigned xfersize:7;
+ } b;
+};
+
+#define MAX_PERIO_FIFOS 15 /* Max periodic FIFOs */
+#define MAX_TX_FIFOS 15 /* Max non-periodic FIFOs */
+
+/* Maximum number of Endpoints/HostChannels */
+#if defined(CONFIG_460EX)
+#define MAX_EPS_CHANNELS 12
+#else
+#define MAX_EPS_CHANNELS 4
+#endif
+
+/*
+ * The device_if structure contains information needed to manage the DWC_otg
+ * controller acting in device mode. It represents the programming view of the
+ * device-specific aspects of the controller.
+ */
+struct device_if {
+ /* Device Global Registers starting at offset 800h */
+ struct device_global_regs *dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+ /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */
+ struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+ /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+ struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+ /* Device configuration information */
+ /* Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */
+ u8 speed;
+ /* Number # of Tx EP range: 0-15 exept ep0 */
+ u8 num_in_eps;
+ /* Number # of Rx EP range: 0-15 exept ep 0*/
+ u8 num_out_eps;
+
+ /* Size of periodic FIFOs (Bytes) */
+ u16 perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+ /* Size of Tx FIFOs (Bytes) */
+ u16 tx_fifo_size[MAX_TX_FIFOS];
+
+ /* Thresholding enable flags and length varaiables */
+ u16 rx_thr_en;
+ u16 iso_tx_thr_en;
+ u16 non_iso_tx_thr_en;
+ u16 rx_thr_length;
+ u16 tx_thr_length;
+};
+
+/*
+ * This union represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the d32 member then set/clear the
+ * bits using the bit elements.
+ */
+union pcgcctl_data {
+ u32 d32;
+ struct {
+ unsigned reserved31_05:27;
+ /* PHY Suspended */
+ unsigned physuspended:1;
+ /* Reset Power Down Modules */
+ unsigned rstpdwnmodule:1;
+ /* Power Clamp */
+ unsigned pwrclmp:1;
+ /* Gate Hclk */
+ unsigned gatehclk:1;
+ /* Stop Pclk */
+ unsigned stoppclk:1;
+ } b;
+};
+
+/*
+ * Host Mode Register Structures
+ */
+
+/*
+ * The Host Global Registers structure defines the size and relative field
+ * offsets for the Host Mode Global Registers. Host Global Registers offsets
+ * 400h-7FFh.
+*/
+struct host_global_regs {
+ /* Host Configuration Register. Offset: 400h */
+ u32 hcfg;
+ /* Host Frame Interval Register. Offset: 404h */
+ u32 hfir;
+ /* Host Frame Number / Frame Remaining Register. Offset: 408h */
+ u32 hfnum;
+ /* Reserved. Offset: 40Ch */
+ u32 reserved40C;
+ /* Host Periodic Transmit FIFO/ Queue Status Register. Offset: 410h */
+ u32 hptxsts;
+ /* Host All Channels Interrupt Register. Offset: 414h */
+ u32 haint;
+ /* Host All Channels Interrupt Mask Register. Offset: 418h */
+ u32 haintmsk;
+};
+
+/*
+ * This union represents the bit fields in the Host Configuration Register. Read
+ * the register into the d32 member then set/clear the bits using the bit
+ * elements. Write the d32 member to the hcfg register.
+ */
+union hcfg_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+#define DWC_HCFG_30_60_MHZ 0
+#define DWC_HCFG_48_MHZ 1
+#define DWC_HCFG_6_MHZ 2
+ /* FS/LS Only Support */
+ unsigned fslssupp:1;
+ /* FS/LS Phy Clock Select */
+ unsigned fslspclksel:2;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+union hfir_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:16;
+ unsigned frint:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+union hfnum_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+ unsigned frrem:16;
+ unsigned frnum:16;
+ } b;
+};
+
+union hptxsts_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned ptxqtop_odd:1;
+ unsigned ptxqtop_chnum:4;
+ unsigned ptxqtop_token:2;
+ unsigned ptxqtop_terminate:1;
+ unsigned ptxqspcavail:8;
+ unsigned ptxfspcavail:16;
+ /*
+ * Top of the Periodic Transmit Request Queue
+ * - bit 24 - Terminate (last entry for the selected channel)
+ * - bits 26:25 - Token Type
+ * - 2'b00 - Zero length
+ * - 2'b01 - Ping
+ * - 2'b10 - Disable
+ * - bits 30:27 - Channel Number
+ * - bit 31 - Odd/even microframe
+ */
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hprt0 register.
+ */
+union hprt0_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED 2
+ unsigned reserved19_31:13;
+ unsigned prtspd:2;
+ unsigned prttstctl:4;
+ unsigned prtpwr:1;
+ unsigned prtlnsts:2;
+ unsigned reserved9:1;
+ unsigned prtrst:1;
+ unsigned prtsusp:1;
+ unsigned prtres:1;
+ unsigned prtovrcurrchng:1;
+ unsigned prtovrcurract:1;
+ unsigned prtenchng:1;
+ unsigned prtena:1;
+ unsigned prtconndet:1;
+ unsigned prtconnsts:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host All Interrupt Register.
+ */
+union haint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:16;
+ unsigned ch15:1;
+ unsigned ch14:1;
+ unsigned ch13:1;
+ unsigned ch12:1;
+ unsigned ch11:1;
+ unsigned ch10:1;
+ unsigned ch9:1;
+ unsigned ch8:1;
+ unsigned ch7:1;
+ unsigned ch6:1;
+ unsigned ch5:1;
+ unsigned ch4:1;
+ unsigned ch3:1;
+ unsigned ch2:1;
+ unsigned ch1:1;
+ unsigned ch0:1;
+ } b;
+ struct {
+ unsigned reserved:16;
+ unsigned chint:16;
+ } b2;
+};
+
+/*
+ * This union represents the bit fields in the Host All Interrupt Register.
+ */
+union haintmsk_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:16;
+ unsigned ch15:1;
+ unsigned ch14:1;
+ unsigned ch13:1;
+ unsigned ch12:1;
+ unsigned ch11:1;
+ unsigned ch10:1;
+ unsigned ch9:1;
+ unsigned ch8:1;
+ unsigned ch7:1;
+ unsigned ch6:1;
+ unsigned ch5:1;
+ unsigned ch4:1;
+ unsigned ch3:1;
+ unsigned ch2:1;
+ unsigned ch1:1;
+ unsigned ch0:1;
+ } b;
+ struct {
+ unsigned reserved:16;
+ unsigned chint:16;
+ } b2;
+};
+
+/*
+ * Host Channel Specific Registers. 500h-5FCh
+ */
+struct dwc_hc_regs { /* CONFIG_DWC_OTG_REG_LE */
+ /*
+ * Host Channel 0 Characteristic Register.
+ * Offset: 500h + (chan_num * 20h) + 00h
+ */
+ u32 hcchar;
+ /*
+ * Host Channel 0 Split Control Register.
+ * Offset: 500h + (chan_num * 20h) + 04h
+ */
+ u32 hcsplt;
+ /*
+ * Host Channel 0 Interrupt Register.
+ * Offset: 500h + (chan_num * 20h) + 08h
+ */
+ u32 hcint;
+ /*
+ * Host Channel 0 Interrupt Mask Register.
+ * Offset: 500h + (chan_num * 20h) + 0Ch
+ */
+ u32 hcintmsk;
+ /*
+ * Host Channel 0 Transfer Size Register.
+ * Offset: 500h + (chan_num * 20h) + 10h
+ */
+ u32 hctsiz;
+ /*
+ * Host Channel 0 DMA Address Register.
+ * Offset: 500h + (chan_num * 20h) + 14h
+ */
+ u32 hcdma;
+ /*
+ * Reserved.
+ * Offset: 500h + (chan_num * 20h) + 18h - 500h + (chan_num * 20h) + 1Ch
+ */
+ u32 reserved[2];
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcchar register.
+ */
+union hcchar_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Channel enable */
+ unsigned chen:1;
+ /* Channel disable */
+ unsigned chdis:1;
+ /*
+ * Frame to transmit periodic transaction.
+ * 0: even, 1: odd
+ */
+ unsigned oddfrm:1;
+ /* Device address */
+ unsigned devaddr:7;
+ /* Packets per frame for periodic transfers. 0 is reserved. */
+ unsigned multicnt:2;
+ /* 0: Control, 1: Isoc, 2: Bulk, 3: Intr */
+ unsigned eptype:2;
+ /* 0: Full/high speed device, 1: Low speed device */
+ unsigned lspddev:1;
+ unsigned reserved:1;
+ /* 0: OUT, 1: IN */
+ unsigned epdir:1;
+ /* Endpoint number */
+ unsigned epnum:4;
+ /* Maximum packet size in bytes */
+ unsigned mps:11;
+ } b;
+};
+
+union hcsplt_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Split Enble */
+ unsigned spltena:1;
+ /* Reserved */
+ unsigned reserved:14;
+ /* Do Complete Split */
+ unsigned compsplt:1;
+ /* Transaction Position */
+ unsigned xactpos:2;
+#define DWC_HCSPLIT_XACTPOS_MID 0
+#define DWC_HCSPLIT_XACTPOS_END 1
+#define DWC_HCSPLIT_XACTPOS_BEGIN 2
+#define DWC_HCSPLIT_XACTPOS_ALL 3
+
+ /* Hub Address */
+ unsigned hubaddr:7;
+ /* Port Address */
+ unsigned prtaddr:7;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+union hcint_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ /* Reserved */
+ unsigned reserved:21;
+ /* Data Toggle Error */
+ unsigned datatglerr:1;
+ /* Frame Overrun */
+ unsigned frmovrun:1;
+ /* Babble Error */
+ unsigned bblerr:1;
+ /* Transaction Err */
+ unsigned xacterr:1;
+ /* NYET Response Received */
+ unsigned nyet:1;
+ /* ACK Response Received */
+ unsigned ack:1;
+ /* NAK Response Received */
+ unsigned nak:1;
+ /* STALL Response Received */
+ unsigned stall:1;
+ /* AHB Error */
+ unsigned ahberr:1;
+ /* Channel Halted */
+ unsigned chhltd:1;
+ /* Transfer Complete */
+ unsigned xfercomp:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the d32 member then set/clear the bits
+ * using the bit elements. Write the d32 member to the hcchar register.
+ */
+union hctsiz_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+#define DWC_HCTSIZ_DATA0 0
+#define DWC_HCTSIZ_DATA1 2
+#define DWC_HCTSIZ_DATA2 1
+#define DWC_HCTSIZ_MDATA 3
+#define DWC_HCTSIZ_SETUP 3
+
+ /* Do PING protocol when 1 */
+ unsigned dopng:1;
+ /*
+ * Packet ID for next data packet
+ * 0: DATA0
+ * 1: DATA2
+ * 2: DATA1
+ * 3: MDATA (non-Control), SETUP (Control)
+ */
+ unsigned pid:2;
+ /* Data packets to transfer */
+ unsigned pktcnt:10;
+ /* Total transfer size in bytes */
+ unsigned xfersize:19;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcintmsk register.
+ */
+union hcintmsk_data { /* CONFIG_DWC_OTG_REG_LE */
+ u32 d32;
+ struct {
+ unsigned reserved:21;
+ unsigned datatglerr:1;
+ unsigned frmovrun:1;
+ unsigned bblerr:1;
+ unsigned xacterr:1;
+ unsigned nyet:1;
+ unsigned ack:1;
+ unsigned nak:1;
+ unsigned stall:1;
+ unsigned ahberr:1;
+ unsigned chhltd:1;
+ unsigned xfercompl:1;
+ } b;
+};
+
+/*
+ * OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information needed to
+ * manage the DWC_otg controller acting in host mode. It represents the
+ * programming view of the host-specific aspects of the controller.
+ */
+struct dwc_host_if { /* CONFIG_DWC_OTG_REG_LE */
+ /* Host Global Registers starting at offset 400h.*/
+ struct host_global_regs *host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
+
+ /* Host Port 0 Control and Status Register */
+ u32 *hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
+
+ /* Host Channel Specific Registers at offsets 500h-5FCh. */
+ struct dwc_hc_regs *hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
+#define DWC_OTG_CHAN_REGS_OFFSET 0x20
+
+ /* Host configuration information */
+ /* Number of Host Channels (range: 1-16) */
+ u8 num_host_channels;
+ /* Periodic EPs supported (0: no, 1: yes) */
+ u8 perio_eps_supported;
+ /* Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+ u16 perio_tx_fifo_size;
+};
+
+#else /* CONFIG_DWC_OTG_REG_LE not defined */
+
+/*
+ * This union represents the bit fields of the Core OTG Control
+ * and Status Register (GOTGCTL). Set the bits using the bit
+ * fields then write the d32 value to the register.
+ */
+union gotgctl_data {
+ u32 d32;
+ struct {
+ unsigned sesreqscs:1;
+ unsigned sesreq:1;
+ unsigned reserved2_7:6;
+ unsigned hstnegscs:1;
+ unsigned hnpreq:1;
+ unsigned hstsethnpen:1;
+ unsigned devhnpen:1;
+ unsigned reserved12_15:4;
+ unsigned conidsts:1;
+ unsigned reserved17:1;
+ unsigned asesvld:1;
+ unsigned bsesvld:1;
+ unsigned currmod:1;
+ unsigned reserved21_31:11;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT). Set/clear the bits using the bit fields then write the d32
+ * value to the register.
+ */
+union gotgint_data {
+ u32 d32;
+ struct {
+ /* Current Mode */
+ unsigned reserved0_1:2;
+
+ /* Session End Detected */
+ unsigned sesenddet:1;
+
+ unsigned reserved3_7:5;
+
+ /* Session Request Success Status Change */
+ unsigned sesreqsucstschng:1;
+ /* Host Negotiation Success Status Change */
+ unsigned hstnegsucstschng:1;
+
+ unsigned reserver10_16:7;
+
+ /* Host Negotiation Detected */
+ unsigned hstnegdet:1;
+ /* A-Device Timeout Change */
+ unsigned adevtoutchng:1;
+ /* Debounce Done */
+ unsigned debdone:1;
+
+ unsigned reserved31_20:12;
+
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core AHB Configuration Register
+ * (GAHBCFG). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gahbcfg_data {
+ u32 d32;
+ struct {
+ unsigned glblintrmsk:1;
+#define DWC_GAHBCFG_GLBINT_ENABLE 1
+
+ unsigned hburstlen:4;
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
+
+ unsigned dmaenable:1;
+#define DWC_GAHBCFG_DMAENABLE 1
+ unsigned reserved:1;
+ unsigned nptxfemplvl_txfemplvl:1;
+ unsigned ptxfemplvl:1;
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+ unsigned reserved9_31:23;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core USB Configuration Register
+ * (GUSBCFG). Set the bits using the bit fields then write the d32 value to
+ * the register.
+ */
+union gusbcfg_data {
+ u32 d32;
+ struct {
+ unsigned toutcal:3;
+ unsigned phyif:1;
+ unsigned ulpi_utmi_sel:1;
+ unsigned fsintf:1;
+ unsigned physel:1;
+ unsigned ddrsel:1;
+ unsigned srpcap:1;
+ unsigned hnpcap:1;
+ unsigned usbtrdtim:4;
+ unsigned nptxfrwnden:1;
+ unsigned phylpwrclksel:1;
+ unsigned otgutmifssel:1;
+ unsigned ulpi_fsls:1;
+ unsigned ulpi_auto_res:1;
+ unsigned ulpi_clk_sus_m:1;
+ unsigned ulpi_ext_vbus_drv:1;
+ unsigned ulpi_int_vbus_indicator:1;
+ unsigned term_sel_dl_pulse:1;
+ unsigned reserved23_28:6;
+ unsigned force_host_mode:1;
+ unsigned force_device_mode:1;
+ unsigned corrupt_tx_packet:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core Reset Register (GRSTCTL).
+ * Set/clear the bits using the bit fields then write the d32 value to the
+ * register.
+ */
+union grstctl_data {
+ u32 d32;
+ struct {
+ /*
+ * Core Soft Reset (CSftRst) (Device and Host)
+ *
+ * The application can flush the control logic in the entire
+ * core using this bit. This bit resets the pipelines in the AHB
+ * Clock domain as well as the PHY Clock domain.
+ *
+ * The state machines are reset to an IDLE state, the control
+ * bits in the CSRs are cleared, all the transmit FIFOs and the
+ * receive FIFO are flushed.
+ *
+ * The status mask bits that control the generation of the
+ * interrupt, are cleared, to clear the interrupt. The interrupt
+ * status bits are not cleared, so the application can get the
+ * status of any events that occurred in the core after it has
+ * set this bit.
+ *
+ * Any transactions on the AHB are terminated as soon as
+ * possible following the protocol. Any transactions on the USB
+ * are terminated immediately.
+ *
+ * The configuration settings in the CSRs are unchanged, so the
+ * software doesn't have to reprogram these registers (Device
+ * Configuration/Host Configuration/Core System
+ * Configuration/Core PHY Configuration).
+ *
+ * The application can write to this bit, any time it wants to
+ * reset the core. This is a self clearing bit and the core
+ * clears this bit after all the necessary logic is reset in the
+ * core, which may take several clocks, depending on the current
+ * state of the core.
+ */
+ unsigned csftrst:1;
+ /*
+ * Hclk Soft Reset
+ *
+ * The application uses this bit to reset the control logic in
+ * the AHB clock domain. Only AHB clock domain pipelines are
+ * reset.
+ */
+ unsigned hsftrst:1;
+ /*
+ * Host Frame Counter Reset (Host Only)<br>
+ *
+ * The application can reset the (micro)frame number counter
+ * inside the core, using this bit. When the (micro)frame
+ * counter is reset, the subsequent SOF sent out by the core,
+ * will have a (micro)frame number of 0.
+ */
+ unsigned hstfrm:1;
+ /*
+ * In Token Sequence Learning Queue Flush (INTknQFlsh) (Device
+ * Only)
+ */
+ unsigned intknqflsh:1;
+ /*
+ * RxFIFO Flush (RxFFlsh) (Device and Host)
+ *
+ * The application can flush the entire Receive FIFO using this
+ * bit.
+ *
+ * The application must first ensure that the core is not in the
+ * middle of a transaction.
+ *
+ * The application should write into this bit, only after making
+ * sure that neither the DMA engine is reading from the RxFIFO
+ * nor the MAC is writing the data in to the FIFO.
+ *
+ * The application should wait until the bit is cleared before
+ * performing any other operations. This bit will takes 8 clocks
+ * (slowest of PHY or AHB clock) to clear.
+ */
+ unsigned rxfflsh:1;
+ /*
+ * TxFIFO Flush (TxFFlsh) (Device and Host).
+ *
+ * This bit is used to selectively flush a single or all
+ * transmit FIFOs. The application must first ensure that the
+ * core is not in the middle of a transaction.
+ *
+ * The application should write into this bit, only after making
+ * sure that neither the DMA engine is writing into the TxFIFO
+ * nor the MAC is reading the data out of the FIFO.
+ *
+ * The application should wait until the core clears this bit,
+ * before performing any operations. This bit will takes 8
+ * clocks (slowest of PHY or AHB clock) to clear.
+ */
+ unsigned txfflsh:1;
+
+ /*
+ * TxFIFO Number (TxFNum) (Device and Host).
+ *
+ * This is the FIFO number which needs to be flushed, using the
+ * TxFIFO Flush bit. This field should not be changed until the
+ * TxFIFO Flush bit is cleared by the core.
+ * - 0x0 : Non Periodic TxFIFO Flush
+ * - 0x1 : Periodic TxFIFO #1 Flush in device mode
+ * or Periodic TxFIFO in host mode
+ * - 0x2 : Periodic TxFIFO #2 Flush in device mode.
+ * - ...
+ * - 0xF : Periodic TxFIFO #15 Flush in device mode
+ * - 0x10: Flush all the Transmit NonPeriodic and
+ * Transmit Periodic FIFOs in the core
+ */
+ unsigned txfnum:5;
+#define DWC_GRSTCTL_TXFNUM_ALL 0x10
+
+ /* Reserved */
+ unsigned reserved11_29:19;
+ /*
+ * DMA Request Signal. Indicated DMA request is in progress.
+ * Used for debug purpose.
+ */
+ unsigned dmareq:1;
+ /*
+ * AHB Master Idle. Indicates the AHB Master State Machine is
+ * in IDLE condition.
+ */
+ unsigned ahbidle:1;
+ } b;
+};
+
+
+/*
+ * This union represents the bit fields of the Core Interrupt Mask Register
+ * (GINTMSK). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gintmsk_data {
+ u32 d32;
+ struct {
+ unsigned reserved0:1;
+ unsigned modemismatch:1;
+ unsigned otgintr:1;
+ unsigned sofintr:1;
+ unsigned rxstsqlvl:1;
+ unsigned nptxfempty:1;
+ unsigned ginnakeff:1;
+ unsigned goutnakeff:1;
+ unsigned reserved8:1;
+ unsigned i2cintr:1;
+ unsigned erlysuspend:1;
+ unsigned usbsuspend:1;
+ unsigned usbreset:1;
+ unsigned enumdone:1;
+ unsigned isooutdrop:1;
+ unsigned eopframe:1;
+ unsigned reserved16:1;
+ unsigned epmismatch:1;
+ unsigned inepintr:1;
+ unsigned outepintr:1;
+ unsigned incomplisoin:1;
+ unsigned incomplisoout:1;
+ unsigned reserved22_23:2;
+ unsigned portintr:1;
+ unsigned hcintr:1;
+ unsigned ptxfempty:1;
+ unsigned reserved27:1;
+ unsigned conidstschng:1;
+ unsigned disconnect:1;
+ unsigned sessreqintr:1;
+ unsigned wkupintr:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields of the Core Interrupt Register
+ * (GINTSTS). Set/clear the bits using the bit fields then write the d32 value
+ * to the register.
+ */
+union gintsts_data {
+ u32 d32;
+#define DWC_SOF_INTR_MASK 0x0008
+
+ struct {
+#define DWC_HOST_MODE 1
+ unsigned curmode:1;
+ unsigned modemismatch:1;
+ unsigned otgintr:1;
+ unsigned sofintr:1;
+ unsigned rxstsqlvl:1;
+ unsigned nptxfempty:1;
+ unsigned ginnakeff:1;
+ unsigned goutnakeff:1;
+ unsigned reserved8:1;
+ unsigned i2cintr:1;
+ unsigned erlysuspend:1;
+ unsigned usbsuspend:1;
+ unsigned usbreset:1;
+ unsigned enumdone:1;
+ unsigned isooutdrop:1;
+ unsigned eopframe:1;
+ unsigned intokenrx:1;
+ unsigned epmismatch:1;
+ unsigned inepint:1;
+ unsigned outepintr:1;
+ unsigned incomplisoin:1;
+ unsigned incomplisoout:1;
+ unsigned reserved22_23:2;
+ unsigned portintr:1;
+ unsigned hcintr:1;
+ unsigned ptxfempty:1;
+ unsigned reserved27:1;
+ unsigned conidstschng:1;
+ unsigned disconnect:1;
+ unsigned sessreqintr:1;
+ unsigned wkupintr:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 element then
+ * read out the bits using the bit elements.
+ */
+union device_grxsts_data {
+ u32 d32;
+ struct {
+ unsigned epnum:4;
+ unsigned bcnt:11;
+ unsigned dpid:2;
+
+#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */
+#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */
+#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */
+#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */
+#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */
+ unsigned pktsts:4;
+ unsigned fn:4;
+ unsigned reserved:7;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 element then
+ * read out the bits using the bit elements.
+ */
+union host_grxsts_data {
+ u32 d32;
+ struct {
+ unsigned chnum:4;
+ unsigned bcnt:11;
+ unsigned dpid:2;
+
+ unsigned pktsts:4;
+#define DWC_GRXSTS_PKTSTS_IN 0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7
+
+ unsigned reserved:11;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the d32 element then
+ * read out the bits using the bit elements.
+ */
+union fifosize_data {
+ u32 d32;
+ struct {
+ unsigned startaddr:16;
+ unsigned depth:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Non-Periodic Transmit FIFO/Queue
+ * Status Register (GNPTXSTS). Read the register into the d32 element then read
+ * out the bits using the bit elements.
+ */
+union gnptxsts_data {
+ u32 d32;
+ struct {
+ unsigned nptxfspcavail:16;
+ unsigned nptxqspcavail:8;
+ /*
+ * Top of the Non-Periodic Transmit Request Queue
+ * - bit 24 - Terminate (Last entry for the selected
+ * channel/EP)
+ * - bits 26:25 - Token Type
+ * - 2'b00 - IN/OUT
+ * - 2'b01 - Zero Length OUT
+ * - 2'b10 - PING/Complete Split
+ * - 2'b11 - Channel Halt
+ * - bits 30:27 - Channel/EP Number
+ */
+ unsigned nptxqtop_terminate:1;
+ unsigned nptxqtop_token:2;
+ unsigned nptxqtop_chnep:4;
+ unsigned reserved:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Transmit FIFO Status Register
+ * (DTXFSTS). Read the register into the d32 element then read out the bits
+ * using the bit elements.
+ */
+union dtxfsts_data {
+ u32 d32;
+ struct {
+ unsigned txfspcavail:16;
+ unsigned reserved:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the I2C Control Register (I2CCTL).
+ * Read the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union gi2cctl_data {
+ u32 d32;
+ struct {
+ unsigned rwdata:8;
+ unsigned regaddr:8;
+ unsigned addr:7;
+ unsigned i2cen:1;
+ unsigned ack:1;
+ unsigned i2csuspctl:1;
+ unsigned i2cdevaddr:2;
+ unsigned reserved:2;
+ unsigned rw:1;
+ unsigned bsydne:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config1 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg1_data {
+ u32 d32;
+ struct {
+ unsigned ep_dir0:2;
+ unsigned ep_dir1:2;
+ unsigned ep_dir2:2;
+ unsigned ep_dir3:2;
+ unsigned ep_dir4:2;
+ unsigned ep_dir5:2;
+ unsigned ep_dir6:2;
+ unsigned ep_dir7:2;
+ unsigned ep_dir8:2;
+ unsigned ep_dir9:2;
+ unsigned ep_dir10:2;
+ unsigned ep_dir11:2;
+ unsigned ep_dir12:2;
+ unsigned ep_dir13:2;
+ unsigned ep_dir14:2;
+ unsigned ep_dir15:2;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config2 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg2_data {
+ u32 d32;
+ struct {
+ /* GHWCFG2 */
+ unsigned op_mode:3;
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
+
+ unsigned architecture:2;
+ unsigned point2point:1;
+ unsigned hs_phy_type:2;
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+
+ unsigned fs_phy_type:2;
+ unsigned num_dev_ep:4;
+ unsigned num_host_chan:4;
+ unsigned perio_ep_supported:1;
+ unsigned dynamic_fifo:1;
+ unsigned rx_status_q_depth:2;
+ unsigned nonperio_tx_q_depth:2;
+ unsigned host_perio_tx_q_depth:2;
+ unsigned dev_token_q_depth:5;
+ unsigned reserved31:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config3 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg3_data {
+ u32 d32;
+ struct {
+ /* GHWCFG3 */
+ unsigned xfer_size_cntr_width:4;
+ unsigned packet_size_cntr_width:3;
+ unsigned otg_func:1;
+ unsigned i2c:1;
+ unsigned vendor_ctrl_if:1;
+ unsigned optional_features:1;
+ unsigned synch_reset_type:1;
+ unsigned reserved15_12:4;
+ unsigned dfifo_depth:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the User HW Config4 Register. Read
+ * the register into the d32 element then read out the bits using the bit
+ * elements.
+ */
+union hwcfg4_data {
+ u32 d32;
+ struct {
+ unsigned num_dev_perio_in_ep:4;
+ unsigned power_optimiz:1;
+ unsigned min_ahb_freq:9;
+ unsigned utmi_phy_data_width:2;
+ unsigned num_dev_mode_ctrl_ep:4;
+ unsigned iddig_filt_en:1;
+ unsigned vbus_valid_filt_en:1;
+ unsigned a_valid_filt_en:1;
+ unsigned b_valid_filt_en:1;
+ unsigned session_end_filt_en:1;
+ unsigned ded_fifo_en:1;
+ unsigned num_in_eps:4;
+ unsigned reserved31_30:2;
+ } b;
+};
+
+/*
+ * Device Global Registers. Offsets 800h-BFFh
+ *
+ * The following structures define the size and relative field offsets for the
+ * Device Mode Registers.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_global_regs {
+ /* Device Configuration Register. Offset 800h */
+ u32 dcfg;
+ /* Device Control Register. Offset: 804h */
+ u32 dctl;
+ /* Device Status Register (Read Only). Offset: 808h */
+ u32 dsts;
+ /* Reserved. Offset: 80Ch */
+ u32 unused;
+ /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */
+ u32 diepmsk;
+ /* Device OUT Endpoint Common Interrupt MaskRegister. Offset: 814h */
+ u32 doepmsk;
+ /* Device All Endpoints Interrupt Register. Offset: 818h */
+ u32 daint;
+ /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */
+ u32 daintmsk;
+ /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */
+ u32 dtknqr1;
+ /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */
+ u32 dtknqr2;
+ /* Device VBUS discharge Register. Offset: 828h */
+ u32 dvbusdis;
+ /* Device VBUS Pulse Register. Offset: 82Ch */
+ u32 dvbuspulse;
+ /*
+ * Device IN Token Queue Read Register-3 (Read Only).
+ * Device Thresholding control register (Read/Write)
+ * Offset: 830h
+ */
+ u32 dtknqr3_dthrctl;
+ /*
+ * Device IN Token Queue Read Register-4 (Read Only).
+ * Device IN EPs empty Inr. Mask Register (Read/Write)
+ * Offset: 834h
+ */
+ u32 dtknqr4_fifoemptymsk;
+};
+
+/*
+ * This union represents the bit fields in the Device Configuration Register.
+ * Read the register into the d32 member then set/clear the bits using the bit
+ * elements. Write the d32 member to the dcfg register.
+ */
+union dcfg_data {
+ u32 d32;
+ struct {
+ /* Device Speed */
+ unsigned devspd:2;
+ /* Non Zero Length Status OUT Handshake */
+ unsigned nzstsouthshk:1;
+#define DWC_DCFG_SEND_STALL 1
+
+ unsigned reserved3:1;
+ /* Device Addresses */
+ unsigned devaddr:7;
+ /* Periodic Frame Interval */
+ unsigned perfrint:2;
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+
+ unsigned reserved13_17:5;
+ /* In Endpoint Mis-match count */
+ unsigned epmscnt:4;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Control Register. Read
+ * the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union dctl_data {
+ u32 d32;
+ struct {
+ /* Remote Wakeup */
+ unsigned rmtwkupsig:1;
+ /* Soft Disconnect */
+ unsigned sftdiscon:1;
+ /* Global Non-Periodic IN NAK Status */
+ unsigned gnpinnaksts:1;
+ /* Global OUT NAK Status */
+ unsigned goutnaksts:1;
+ /* Test Control */
+ unsigned tstctl:3;
+ /* Set Global Non-Periodic IN NAK */
+ unsigned sgnpinnak:1;
+ /* Clear Global Non-Periodic IN NAK */
+ unsigned cgnpinnak:1;
+ /* Set Global OUT NAK */
+ unsigned sgoutnak:1;
+ /* Clear Global OUT NAK */
+ unsigned cgoutnak:1;
+ unsigned reserved:21;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device Status Register. Read the
+ * register into the d32 member then set/clear the bits using the bit elements.
+ */
+union dsts_data {
+ u32 d32;
+ struct {
+ /* Suspend Status */
+ unsigned suspsts:1;
+ /* Enumerated Speed */
+ unsigned enumspd:2;
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
+
+ /* Erratic Error */
+ unsigned errticerr:1;
+ unsigned reserved4_7:4;
+ /* Frame or Microframe Number of the received SOF */
+ unsigned soffn:14;
+ unsigned reserved22_31:10;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device IN EP Interrupt Register
+ * and the Device IN EP Common Mask Register. Read the register into the d32
+ * member then set/clear the bits using the bit elements.
+ */
+union diepint_data {
+ u32 d32;
+ struct {
+ /* Transfer complete mask */
+ unsigned xfercompl:1;
+ /* Endpoint disable mask */
+ unsigned epdisabled:1;
+ /* AHB Error mask */
+ unsigned ahberr:1;
+ /* TimeOUT Handshake mask (non-ISOC EPs) */
+ unsigned timeout:1;
+ /* IN Token received with TxF Empty mask */
+ unsigned intktxfemp:1;
+ /* IN Token Received with EP mismatch mask */
+ unsigned intknepmis:1;
+ /* IN Endpoint HAK Effective mask */
+ unsigned inepnakeff:1;
+ /* IN Endpoint HAK Effective mask */
+ unsigned emptyintr:1;
+ unsigned txfifoundrn:1;
+ unsigned reserved08_31:23;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device OUT EP Interrupt
+ * Registerand Device OUT EP Common Interrupt Mask Register. Read the register
+ * into the d32 member then set/clear the bits using the bit elements.
+ */
+union doepint_data {
+ u32 d32;
+ struct {
+ /* Transfer complete */
+ unsigned xfercompl:1;
+ /* Endpoint disable */
+ unsigned epdisabled:1;
+ /* AHB Error */
+ unsigned ahberr:1;
+ /* Setup Phase Done (contorl EPs) */
+ unsigned setup:1;
+ unsigned reserved04_31:28;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device All EP Interrupt and Mask
+ * Registers. Read the register into the d32 member then set/clear the bits
+ * using the bit elements.
+ */
+union daint_data {
+ u32 d32;
+ struct {
+ /* IN Endpoint bits */
+ unsigned in:16;
+ /* OUT Endpoint bits */
+ unsigned out:16;
+ } ep;
+ struct {
+ /* IN Endpoint bits */
+ unsigned inep0:1;
+ unsigned inep1:1;
+ unsigned inep2:1;
+ unsigned inep3:1;
+ unsigned inep4:1;
+ unsigned inep5:1;
+ unsigned inep6:1;
+ unsigned inep7:1;
+ unsigned inep8:1;
+ unsigned inep9:1;
+ unsigned inep10:1;
+ unsigned inep11:1;
+ unsigned inep12:1;
+ unsigned inep13:1;
+ unsigned inep14:1;
+ unsigned inep15:1;
+ /* OUT Endpoint bits */
+ unsigned outep0:1;
+ unsigned outep1:1;
+ unsigned outep2:1;
+ unsigned outep3:1;
+ unsigned outep4:1;
+ unsigned outep5:1;
+ unsigned outep6:1;
+ unsigned outep7:1;
+ unsigned outep8:1;
+ unsigned outep9:1;
+ unsigned outep10:1;
+ unsigned outep11:1;
+ unsigned outep12:1;
+ unsigned outep13:1;
+ unsigned outep14:1;
+ unsigned outep15:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device IN Token Queue Read
+ * Registers. Read the register into the d32 member. READ-ONLY Register
+ */
+union dtknq1_data {
+ u32 d32;
+ struct {
+ /* In Token Queue Write Pointer */
+ unsigned intknwptr:5;
+ /* Reserved */
+ unsigned reserved05_06:2;
+ /* write pointer has wrapped. */
+ unsigned wrap_bit:1;
+ /* EP Numbers of IN Tokens 0 ... 4 */
+ unsigned epnums0_5:24;
+ } b;
+};
+
+/*
+ * This union represents Threshold control Register Read and write the register
+ * into the d32 member. READ-WRITABLE Register
+ */
+union dthrctl_data {
+ u32 d32;
+ struct {
+ /* non ISO Tx Thr. Enable */
+ unsigned non_iso_thr_en:1;
+ /* ISO Tx Thr. Enable */
+ unsigned iso_thr_en:1;
+ /* Tx Thr. Length */
+ unsigned tx_thr_len:9;
+ /* Reserved */
+ unsigned reserved11_15:5;
+ /* Rx Thr. Enable */
+ unsigned rx_thr_en:1;
+ /* Rx Thr. Length */
+ unsigned rx_thr_len:9;
+ /* Reserved */
+ unsigned reserved26_31:6;
+ } b;
+};
+
+/*
+ * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_in_ep_regs {
+ /*
+ * Device IN Endpoint Control Register.
+ * Offset: 900h + (ep_num * 20h) + 00h
+ */
+ u32 diepctl;
+ /* Reserved. Offset:900h + (ep_num * 20h) + 04h */
+ u32 reserved04;
+ /*
+ * Device IN Endpoint Interrupt Register.
+ * Offset: 900h + (ep_num * 20h) + 08h
+ */
+ u32 diepint;
+ /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /*
+ * Device IN Endpoint Transfer Size Register.
+ * Offset: 900h + (ep_num * 20h) + 10h
+ */
+ u32 dieptsiz;
+ /*
+ * Device IN Endpoint DMA Address Register.
+ * Offset: 900h + (ep_num * 20h) + 14h
+ */
+ u32 diepdma;
+ /*
+ * Device IN Endpoint Transmit FIFO Status Register.
+ * Offset: 900h + (ep_num * 20h) + 18h
+ */
+ u32 dtxfsts;
+ /*
+ * Reserved.
+ * Offset: 900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch
+ */
+ u32 reserved18;
+};
+
+/*
+ * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_out_ep_regs {
+ /*
+ * Device OUT Endpoint Control Register.
+ * Offset: B00h + (ep_num * 20h) + 00h
+ */
+ u32 doepctl;
+ /*
+ * Device OUT Endpoint Frame number Register.
+ * Offset: B00h + (ep_num * 20h) + 04h
+ */
+ u32 doepfn;
+ /*
+ * Device OUT Endpoint Interrupt Register.
+ * Offset: B00h + (ep_num * 20h) + 08h
+ */
+ u32 doepint;
+ /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /*
+ * Device OUT Endpoint Transfer Size Register.
+ * Offset: B00h + (ep_num * 20h) + 10h
+ */
+ u32 doeptsiz;
+ /*
+ * Device OUT Endpoint DMA Address Register.
+ * Offset: B00h + (ep_num * 20h) + 14h
+ */
+ u32 doepdma;
+ /*
+ * Reserved.
+ * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch
+ */
+ u32 unused[2];
+};
+
+/*
+ * This union represents the bit fields in the Device EP Control Register. Read
+ * the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union depctl_data {
+ u32 d32;
+ struct {
+ /* Maximum Packet Size
+ * IN/OUT EPn
+ * IN/OUT EP0 - 2 bits
+ * 2'b00: 64 Bytes
+ * 2'b01: 32
+ * 2'b10: 16
+ * 2'b11: 8
+ */
+ unsigned mps:11;
+#define DWC_DEP0CTL_MPS_64 0
+#define DWC_DEP0CTL_MPS_32 1
+#define DWC_DEP0CTL_MPS_16 2
+#define DWC_DEP0CTL_MPS_8 3
+
+ /*
+ * Next Endpoint
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved
+ */
+ unsigned nextep:4;
+ /* USB Active Endpoint */
+ unsigned usbactep:1;
+ /*
+ * Endpoint DPID (INTR/Bulk IN and OUT endpoints) This field
+ * contains the PID of the packet going to be received or
+ * transmitted on this endpoint. The application should program
+ * the PID of the first packet going to be received or
+ * transmitted on this endpoint , after the endpoint is
+ * activated. Application use the SetD1PID and SetD0PID fields
+ * of this register to program either D0 or D1 PID.
+ *
+ * The encoding for this field is
+ * - 0: D0
+ * - 1: D1
+ */
+ unsigned dpid:1;
+ /* NAK Status */
+ unsigned naksts:1;
+ /* Endpoint Type
+ * 2'b00: Control
+ * 2'b01: Isochronous
+ * 2'b10: Bulk
+ * 2'b11: Interrupt
+ */
+ unsigned eptype:2;
+ /*
+ * Snoop Mode
+ * OUT EPn/OUT EP0
+ * IN EPn/IN EP0 - reserved
+ */
+ unsigned snp:1;
+ /* Stall Handshake */
+ unsigned stall:1;
+ /*
+ * Tx Fifo Number
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved
+ */
+ unsigned txfnum:4;
+ /* Clear NAK */
+ unsigned cnak:1;
+ /* Set NAK */
+ unsigned snak:1;
+ /*
+ * Set DATA0 PID (INTR/Bulk IN and OUT endpoints)
+ *
+ * Writing to this field sets the Endpoint DPID (DPID) field in
+ * this register to DATA0. Set Even (micro)frame (SetEvenFr)
+ * (ISO IN and OUT Endpoints)
+ *
+ * Writing to this field sets the Even/Odd (micro)frame
+ * (EO_FrNum) field to even (micro) frame.
+ */
+ unsigned setd0pid:1;
+ /*
+ * Set DATA1 PID (INTR/Bulk IN and OUT endpoints)
+ *
+ * Writing to this field sets the Endpoint DPID (DPID) field in
+ * this register to DATA1 Set Odd (micro)frame (SetOddFr) (ISO
+ * IN and OUT Endpoints)
+ *
+ * Writing to this field sets the Even/Odd (micro)frame
+ * (EO_FrNum) field to odd (micro) frame.
+ */
+ unsigned setd1pid:1;
+ /* Endpoint Disable */
+ unsigned epdis:1;
+ /* Endpoint Enable */
+ unsigned epena:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device EP Transfer Size Register.
+ * Read the register into the d32 member then set/clear the bits using the bit
+ * elements.
+ */
+union deptsiz_data {
+ u32 d32;
+ struct {
+ /* Transfer size */
+ unsigned xfersize:19;
+ /* Packet Count */
+ unsigned pktcnt:10;
+ /* Multi Count - Periodic IN endpoints */
+ unsigned mc:2;
+ unsigned reserved:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Device EP 0 Transfer Size
+ * Register. Read the register into the d32 member then set/clear the bits
+ * using the bit elements.
+ */
+union deptsiz0_data {
+ u32 d32;
+ struct {
+ /* Transfer size */
+ unsigned xfersize:7;
+ /* Reserved */
+ unsigned reserved7_18:12;
+ /* Packet Count */
+ unsigned pktcnt:2;
+ /* Reserved */
+ unsigned reserved21_28:9;
+ /* Setup Packet Count (DOEPTSIZ0 Only) */
+ unsigned supcnt:2;
+ unsigned reserved31;
+ } b;
+};
+
+#define MAX_PERIO_FIFOS 15 /* Max periodic FIFOs */
+#define MAX_TX_FIFOS 15 /* Max non-periodic FIFOs */
+#define MAX_EPS_CHANNELS 4 /* Max Endpoints/HostChannels */
+
+/*
+ * The device_if structure contains information needed to manage the
+ * DWC_otg controller acting in device mode. It represents the programming view
+ * of the device-specific aspects of the controller.
+ */
+struct device_if {
+ /* Device Global Registers starting at offset 800h */
+ struct device_global_regs *dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+ /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */
+ struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS/2];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+ /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+ struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS/2];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+ /* Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */
+ u8 speed;
+ /* Number # of Tx EP range: 0-15 exept ep0 */
+ u8 num_in_eps;
+ /* Number # of Rx EP range: 0-15 exept ep0 */
+ u8 num_out_eps;
+
+ /* Size of periodic FIFOs (Bytes) */
+ u16 perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+ /* Size of Tx FIFOs (Bytes) */
+ u16 tx_fifo_size[MAX_TX_FIFOS];
+
+ /* Thresholding enable flags and length varaiables */
+ u16 rx_thr_en;
+ u16 iso_tx_thr_en;
+ u16 non_iso_tx_thr_en;
+ u16 rx_thr_length;
+ u16 tx_thr_length;
+};
+
+/*
+ * The Host Global Registers structure defines the size and relative
+ * field offsets for the Host Mode Global Registers. Host Global
+ * Registers offsets 400h-7FFh.
+*/
+struct host_global_regs {
+ /* Host Configuration Register. Offset: 400h */
+ u32 hcfg;
+ /* Host Frame Interval Register. Offset: 404h */
+ u32 hfir;
+ /* Host Frame Number / Frame Remaining Register. Offset: 408h */
+ u32 hfnum;
+ /* Reserved. Offset: 40Ch */
+ u32 reserved40C;
+ /* Host Periodic Transmit FIFO/ Queue Status Register. Offset: 410h */
+ u32 hptxsts;
+ /* Host All Channels Interrupt Register. Offset: 414h */
+ u32 haint;
+ /* Host All Channels Interrupt Mask Register. Offset: 418h */
+ u32 haintmsk;
+};
+
+/*
+ * This union represents the bit fields in the Host Configuration Register.
+ * Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcfg register.
+ */
+union hcfg_data {
+ u32 d32;
+ struct {
+ /* FS/LS Phy Clock Select */
+ unsigned fslspclksel:2;
+#define DWC_HCFG_30_60_MHZ 0
+#define DWC_HCFG_48_MHZ 1
+#define DWC_HCFG_6_MHZ 2
+
+ /* FS/LS Only Support */
+ unsigned fslssupp:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+union hfir_data {
+ u32 d32;
+ struct {
+ unsigned frint:16;
+ unsigned reserved:16;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+union hfnum_data {
+ u32 d32;
+ struct {
+ unsigned frnum:16;
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+ unsigned frrem:16;
+ } b;
+};
+
+union hptxsts_data {
+ u32 d32;
+ struct {
+ unsigned ptxfspcavail:16;
+ unsigned ptxqspcavail:8;
+ /*
+ * Top of the Periodic Transmit Request Queue
+ * - bit 24 - Terminate (last entry of selected channel)
+ * - bits 26:25 - Token Type
+ * - 2'b00 - Zero length
+ * - 2'b01 - Ping
+ * - 2'b10 - Disable
+ * - bits 30:27 - Channel Number
+ * - bit 31 - Odd/even microframe
+ */
+ unsigned ptxqtop_terminate:1;
+ unsigned ptxqtop_token:2;
+ unsigned ptxqtop_chnum:4;
+ unsigned ptxqtop_odd:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hprt0 register.
+ */
+union hprt0_data {
+ u32 d32;
+ struct {
+ unsigned prtconnsts:1;
+ unsigned prtconndet:1;
+ unsigned prtena:1;
+ unsigned prtenchng:1;
+ unsigned prtovrcurract:1;
+ unsigned prtovrcurrchng:1;
+ unsigned prtres:1;
+ unsigned prtsusp:1;
+ unsigned prtrst:1;
+ unsigned reserved9:1;
+ unsigned prtlnsts:2;
+ unsigned prtpwr:1;
+ unsigned prttstctl:4;
+ unsigned prtspd:2;
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED 2
+ unsigned reserved19_31:13;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host All Interrupt Register.
+ */
+union haint_data {
+ u32 d32;
+ struct {
+ unsigned ch0:1;
+ unsigned ch1:1;
+ unsigned ch2:1;
+ unsigned ch3:1;
+ unsigned ch4:1;
+ unsigned ch5:1;
+ unsigned ch6:1;
+ unsigned ch7:1;
+ unsigned ch8:1;
+ unsigned ch9:1;
+ unsigned ch10:1;
+ unsigned ch11:1;
+ unsigned ch12:1;
+ unsigned ch13:1;
+ unsigned ch14:1;
+ unsigned ch15:1;
+ unsigned reserved:16;
+ } b;
+
+ struct {
+ unsigned chint:16;
+ unsigned reserved:16;
+ } b2;
+};
+
+/*
+ * This union represents the bit fields in the Host All Interrupt Register.
+ */
+union haintmsk_data {
+ u32 d32;
+ struct {
+ unsigned ch0:1;
+ unsigned ch1:1;
+ unsigned ch2:1;
+ unsigned ch3:1;
+ unsigned ch4:1;
+ unsigned ch5:1;
+ unsigned ch6:1;
+ unsigned ch7:1;
+ unsigned ch8:1;
+ unsigned ch9:1;
+ unsigned ch10:1;
+ unsigned ch11:1;
+ unsigned ch12:1;
+ unsigned ch13:1;
+ unsigned ch14:1;
+ unsigned ch15:1;
+ unsigned reserved:16;
+ } b;
+
+ struct {
+ unsigned chint:16;
+ unsigned reserved:16;
+ } b2;
+};
+
+/*
+ * Host Channel Specific Registers. 500h-5FCh
+ */
+struct dwc_hc_regs {
+ /*
+ * Host Channel 0 Characteristic Register.
+ * Offset: 500h + (chan_num * 20h) + 00h
+ */
+ u32 hcchar;
+ /*
+ * Host Channel 0 Split Control Register.
+ * Offset: 500h + (chan_num * 20h) + 04h
+ */
+ u32 hcsplt;
+ /*
+ * Host Channel 0 Interrupt Register.
+ * Offset: 500h + (chan_num * 20h) + 08h
+ */
+ u32 hcint;
+ /*
+ * Host Channel 0 Interrupt Mask Register.
+ * Offset: 500h + (chan_num * 20h) + 0Ch
+ */
+ u32 hcintmsk;
+ /*
+ * Host Channel 0 Transfer Size Register.
+ * Offset: 500h + (chan_num * 20h) + 10h
+ */
+ u32 hctsiz;
+ /*
+ * Host Channel 0 DMA Address Register.
+ * Offset: 500h + (chan_num * 20h) + 14h
+ */
+ u32 hcdma;
+ /* Reserved.
+ * Offset: 500h + (chan_num * 20h) + 18h - 500h + (chan_num * 20h) + 1Ch
+ */
+ u32 reserved[2];
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcchar register.
+ */
+union hcchar_data {
+ u32 d32;
+ struct {
+ /* Maximum packet size in bytes */
+ unsigned mps:11;
+ /* Endpoint number */
+ unsigned epnum:4;
+ /* 0: OUT, 1: IN */
+ unsigned epdir:1;
+ unsigned reserved:1;
+ /* 0: Full/high speed device, 1: Low speed device */
+ unsigned lspddev:1;
+ /* 0: Control, 1: Isoc, 2: Bulk, 3: Intr */
+ unsigned eptype:2;
+ /* Packets per frame for periodic transfers. 0 is reserved. */
+ unsigned multicnt:2;
+ /* Device address */
+ unsigned devaddr:7;
+ /*
+ * Frame to transmit periodic transaction.
+ * 0: even, 1: odd
+ */
+ unsigned oddfrm:1;
+ /* Channel disable */
+ unsigned chdis:1;
+ /* Channel enable */
+ unsigned chen:1;
+ } b;
+};
+
+union hcsplt_data {
+ u32 d32;
+ struct {
+ /* Port Address */
+ unsigned prtaddr:7;
+ /* Hub Address */
+ unsigned hubaddr:7;
+ /* Transaction Position */
+ unsigned xactpos:2;
+#define DWC_HCSPLIT_XACTPOS_MID 0
+#define DWC_HCSPLIT_XACTPOS_END 1
+#define DWC_HCSPLIT_XACTPOS_BEGIN 2
+#define DWC_HCSPLIT_XACTPOS_ALL 3
+
+ /* Do Complete Split */
+ unsigned compsplt:1;
+ /* Reserved */
+ unsigned reserved:14;
+ /* Split Enble */
+ unsigned spltena:1;
+ } b;
+};
+
+
+/*
+ * This union represents the bit fields in the Host All Interrupt Register.
+ */
+union hcint_data {
+ u32 d32;
+ struct {
+ /* Transfer Complete */
+ unsigned xfercomp:1;
+ /* Channel Halted */
+ unsigned chhltd:1;
+ /* AHB Error */
+ unsigned ahberr:1;
+ /* STALL Response Received */
+ unsigned stall:1;
+ /* NAK Response Received */
+ unsigned nak:1;
+ /* ACK Response Received */
+ unsigned ack:1;
+ /* NYET Response Received */
+ unsigned nyet:1;
+ /* Transaction Err */
+ unsigned xacterr:1;
+ /* Babble Error */
+ unsigned bblerr:1;
+ /* Frame Overrun */
+ unsigned frmovrun:1;
+ /* Data Toggle Error */
+ unsigned datatglerr:1;
+ /* Reserved */
+ unsigned reserved:21;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcchar register.
+ */
+union hctsiz_data {
+ u32 d32;
+ struct {
+ /* Total transfer size in bytes */
+ unsigned xfersize:19;
+ /* Data packets to transfer */
+ unsigned pktcnt:10;
+ /*
+ * Packet ID for next data packet
+ * 0: DATA0
+ * 1: DATA2
+ * 2: DATA1
+ * 3: MDATA (non-Control), SETUP (Control)
+ */
+ unsigned pid:2;
+#define DWC_HCTSIZ_DATA0 0
+#define DWC_HCTSIZ_DATA1 2
+#define DWC_HCTSIZ_DATA2 1
+#define DWC_HCTSIZ_MDATA 3
+#define DWC_HCTSIZ_SETUP 3
+
+ /* Do PING protocol when 1 */
+ unsigned dopng:1;
+ } b;
+};
+
+/*
+ * This union represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements. Write the d32 member to the hcintmsk register.
+ */
+union hcintmsk_data {
+ u32 d32;
+ struct {
+ unsigned xfercompl:1;
+ unsigned chhltd:1;
+ unsigned ahberr:1;
+ unsigned stall:1;
+ unsigned nak:1;
+ unsigned ack:1;
+ unsigned nyet:1;
+ unsigned xacterr:1;
+ unsigned bblerr:1;
+ unsigned frmovrun:1;
+ unsigned datatglerr:1;
+ unsigned reserved:21;
+ } b;
+};
+
+/* OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information needed to
+ * manage the DWC_otg controller acting in host mode. It represents the
+ * programming view of the host-specific aspects of the controller.
+ */
+struct dwc_host_if {
+ /* Host Global Registers starting at offset 400h.*/
+ struct host_global_regs *host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
+
+ /* Host Port 0 Control and Status Register */
+ u32 *hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
+
+ /* Host Channel Specific Registers at offsets 500h-5FCh. */
+ struct dwc_hc_regs *hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
+#define DWC_OTG_CHAN_REGS_OFFSET 0x20
+
+ /* Host configuration information */
+ /* Number of Host Channels (range: 1-16) */
+ u8 num_host_channels;
+ /* Periodic EPs supported (0: no, 1: yes) */
+ u8 perio_eps_supported;
+ /* Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+ u16 perio_tx_fifo_size;
+};
+
+/*
+ * This union represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the d32 member then set/clear the bits using
+ * the bit elements.
+ */
+union pcgcctl_data {
+ u32 d32;
+ struct {
+ /* Stop Pclk */
+ unsigned stoppclk:1;
+ /* Gate Hclk */
+ unsigned gatehclk:1;
+ /* Power Clamp */
+ unsigned pwrclmp:1;
+ /* Reset Power Down Modules */
+ unsigned rstpdwnmodule:1;
+ /* PHY Suspended */
+ unsigned physuspended:1;
+ unsigned reserved:27;
+ } b;
+};
+
+#endif /* CONFIG_DWC_OTG_REG_LE */
+#endif
--
1.7.1
^ permalink raw reply related
* [PATCH 8/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: Fushen Chen @ 2010-07-12 23:16 UTC (permalink / raw)
To: linux-usb; +Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <12789766063456-git-send-email-fchen@apm.com>
Implements the DWC OTG Peripheral Controller Driver (PCD)
Interrupt Service routine.
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
drivers/usb/otg/dwc_otg_pcd_intr.c | 2273 ++++++++++++++++++++++++++++++++++++
1 files changed, 2273 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/otg/dwc_otg_pcd_intr.c
diff --git a/drivers/usb/otg/dwc_otg_pcd_intr.c b/drivers/usb/otg/dwc_otg_pcd_intr.c
new file mode 100644
index 0000000..487e44c
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_pcd_intr.c
@@ -0,0 +1,2273 @@
+/*
+ * 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)
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+
+/**
+ * This function returns pointer to in ep struct with number num
+ */
+static struct pcd_ep *get_in_ep(struct dwc_pcd *pcd, u32 num)
+{
+ u32 i;
+ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+
+ if (num == 0) {
+ return &pcd->ep0;
+ } else {
+ for (i = 0; i < num_in_eps; ++i) {
+ if (pcd->in_ep[i].dwc_ep.num == num)
+ return &pcd->in_ep[i];
+ }
+ }
+ return 0;
+}
+
+/**
+ * This function returns pointer to out ep struct with number num
+ */
+static struct pcd_ep *get_out_ep(struct dwc_pcd *pcd, u32 num)
+{
+ u32 i;
+ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+ if (num == 0) {
+ return &pcd->ep0;
+ } else {
+ for (i = 0; i < num_out_eps; ++i) {
+ if (pcd->out_ep[i].dwc_ep.num == num)
+ return &pcd->out_ep[i];
+ }
+ }
+ return 0;
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+static struct pcd_ep *get_ep_by_addr(struct dwc_pcd *pcd, u16 index)
+{
+ struct pcd_ep *ep;
+
+ if (!(index & USB_ENDPOINT_NUMBER_MASK))
+ return &pcd->ep0;
+
+ list_for_each_entry(ep, &pcd->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((index ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+
+ if ((index & 0x0f) == (bEndpointAddress & 0x0f))
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void start_next_request(struct pcd_ep *ep)
+{
+ struct pcd_request *req = NULL;
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ /* Setup and start the Transfer */
+ ep->dwc_ep.start_xfer_buff = req->req.buf;
+ ep->dwc_ep.xfer_buff = req->req.buf;
+ ep->dwc_ep.xfer_len = req->req.length;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.dma_addr = req->req.dma;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * When a new transfer will be started, mark this
+ * endpoint as active. This way it will be blocked
+ * for further transfers, until the current transfer
+ * is finished.
+ */
+ if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 1;
+
+ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+ }
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+static int dwc_otg_pcd_handle_sof_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ union gintsts_data gintsts;
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.sofintr = 1;
+ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function reads the 8 bytes of the setup packet from the Rx FIFO into the
+ * destination buffer. It is called from the Rx Status Queue Level (RxStsQLvl)
+ * interrupt routine when a SETUP packet has been received in Slave mode.
+ */
+static void dwc_otg_read_setup_packet(struct core_if *core_if, u32 *dest)
+{
+ dest[0] = dwc_read_datafifo32(core_if->data_fifo[0]);
+ dest[1] = dwc_read_datafifo32(core_if->data_fifo[0]);
+}
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO. The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ * info
+ * -# If Receive FIFO is empty then skip to step Clear the interrupt
+ * and exit
+ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ * SETUP data to the buffer
+ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ * to the destination buffer
+ */
+static int dwc_otg_pcd_handle_rx_status_q_level_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct core_global_regs *global_regs = core_if->core_global_regs;
+ union gintmsk_data gintmask = {.d32 = 0};
+ union device_grxsts_data status;
+ struct pcd_ep *ep;
+ union gintsts_data gintsts;
+
+ /* Disable the Rx Status Queue Level interrupt */
+ gintmask.b.rxstsqlvl = 1;
+ dwc_modify_reg32(&global_regs->gintmsk, gintmask.d32, 0);
+
+ /* Get the Status from the top of the FIFO */
+ status.d32 = dwc_read_reg32(&global_regs->grxstsp);
+
+ /* Get pointer to EP structure */
+ ep = get_out_ep(pcd, status.b.epnum);
+
+ switch (status.b.pktsts) {
+ case DWC_DSTS_GOUT_NAK:
+ break;
+ case DWC_STS_DATA_UPDT:
+ if (status.b.bcnt && ep->dwc_ep.xfer_buff) {
+ dwc_otg_read_packet(core_if, ep->dwc_ep.xfer_buff,
+ status.b.bcnt);
+ ep->dwc_ep.xfer_count += status.b.bcnt;
+ ep->dwc_ep.xfer_buff += status.b.bcnt;
+ }
+ break;
+ case DWC_STS_XFER_COMP:
+ break;
+ case DWC_DSTS_SETUP_COMP:
+ break;
+ case DWC_DSTS_SETUP_UPDT:
+ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+ ep->dwc_ep.xfer_count += status.b.bcnt;
+ break;
+ default:
+ break;
+ }
+
+ /* Enable the Rx Status Queue Level interrupt */
+ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmask.d32);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.rxstsqlvl = 1;
+ dwc_write_reg32(&global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received. This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ */
+static int get_ep_of_last_in_token(struct core_if *core_if)
+{
+ struct device_global_regs *regs = core_if->dev_if->dev_global_regs;
+ const u32 TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+
+ /* Number of Token Queue Registers */
+ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+ union dtknq1_data dtknqr1;
+ u32 in_tkn_epnums[4];
+ int ndx;
+ u32 i;
+ u32 *addr = ®s->dtknqr1;
+ int epnum = 0;
+
+ /* Read the DTKNQ Registers */
+ for (i = 0; i <= DTKNQ_REG_CNT; i++) {
+ in_tkn_epnums[i] = dwc_read_reg32(addr);
+
+ if (addr == ®s->dvbusdis)
+ addr = ®s->dtknqr3_dthrctl;
+ else
+ ++addr;
+ }
+
+ /* Copy the DTKNQR1 data to the bit field. */
+ dtknqr1.d32 = in_tkn_epnums[0];
+
+ /* Get the EP numbers */
+ in_tkn_epnums[0] = dtknqr1.b.epnums0_5;
+ ndx = dtknqr1.b.intknwptr - 1;
+
+ if (ndx == -1) {
+ /*
+ * Calculate the max queue position.
+ */
+ int cnt = TOKEN_Q_DEPTH;
+
+ if (TOKEN_Q_DEPTH <= 6)
+ cnt = TOKEN_Q_DEPTH - 1;
+ else if (TOKEN_Q_DEPTH <= 14)
+ cnt = TOKEN_Q_DEPTH - 7;
+ else if (TOKEN_Q_DEPTH <= 22)
+ cnt = TOKEN_Q_DEPTH - 15;
+ else
+ cnt = TOKEN_Q_DEPTH - 23;
+
+ epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+ } else {
+ if (ndx <= 5) {
+ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 13) {
+ ndx -= 6;
+ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 21) {
+ ndx -= 14;
+ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 29) {
+ ndx -= 22;
+ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+ }
+ }
+
+ return epnum;
+}
+
+static inline int count_dwords(struct pcd_ep *ep, u32 len)
+{
+ if (len > ep->dwc_ep.maxpacket)
+ len = ep->dwc_ep.maxpacket;
+ return (len + 3) / 4;
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the EP. For
+ * non-periodic EPs the non-periodic Tx FIFO is written. For periodic EPs the
+ * periodic Tx FIFO associated with the EP is written with all packets for the
+ * next micro-frame.
+ *
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned. The last packet, if
+ * short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * ep->xfer_count is a multiple of ep->maxpacket until the last packet
+ *
+ * FIFO access is DWORD
+ */
+static void dwc_otg_ep_write_packet(struct core_if *core_if, struct dwc_ep *ep,
+ int dma)
+{
+ u32 i;
+ u32 byte_count;
+ u32 dword_count;
+ u32 *fifo;
+ u32 *data_buff = (u32 *) ep->xfer_buff;
+
+ if (ep->xfer_count >= ep->xfer_len)
+ return;
+
+ /* Find the byte length of the packet either short packet or MPS */
+ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket)
+ byte_count = ep->xfer_len - ep->xfer_count;
+ else
+ byte_count = ep->maxpacket;
+
+ /*
+ * Find the DWORD length, padded by extra bytes as neccessary if MPS
+ * is not a multiple of DWORD
+ */
+ dword_count = (byte_count + 3) / 4;
+
+ fifo = core_if->data_fifo[ep->num];
+
+ if (!dma)
+ for (i = 0; i < dword_count; i++, data_buff++)
+ dwc_write_datafifo32(fifo, *data_buff);
+
+ ep->xfer_count += byte_count;
+ ep->xfer_buff += byte_count;
+ ep->dma_addr += byte_count;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_np_tx_fifo_empty_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct core_global_regs *global_regs = core_if->core_global_regs;
+ union gnptxsts_data txstatus = {.d32 = 0 };
+ union gintsts_data gintsts = {.d32 = 0};
+ int epnum = 0;
+ struct pcd_ep *ep;
+ u32 len;
+ int dwords;
+
+ /* Get the epnum from the IN Token Learning Queue. */
+ epnum = get_ep_of_last_in_token(core_if);
+ ep = get_in_ep(pcd, epnum);
+
+ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts);
+
+ /*
+ * While there is space in the queue, space in the FIFO, and data to
+ * tranfer, write packets to the Tx FIFO
+ */
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ while (txstatus.b.nptxqspcavail > 0 &&
+ txstatus.b.nptxfspcavail > dwords &&
+ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * When a new transfer will be started, mark this
+ * endpoint as active. This way it will be blocked
+ * for further transfers, until the current transfer
+ * is finished.
+ */
+ if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+ ep->dwc_ep.active = 1;
+
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts);
+ }
+
+ /* Clear nptxfempty interrupt */
+ gintsts.b.nptxfempty = 1;
+ dwc_write_reg32(&global_regs->gintsts, gintsts.d32);
+
+ /* Re-enable tx-fifo empty interrupt, if packets are stil pending */
+ if (len)
+ dwc_modify_reg32(&global_regs->gintmsk, 0, gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int write_empty_tx_fifo(struct dwc_pcd *pcd, u32 epnum)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_in_ep_regs *regs;
+ union dtxfsts_data txstatus = {.d32 = 0};
+ struct pcd_ep *ep;
+ u32 len;
+ int dwords;
+ union diepint_data diepint;
+
+ ep = get_in_ep(pcd, epnum);
+ regs = core_if->dev_if->in_ep_regs[epnum];
+
+ txstatus.d32 = dwc_read_reg32(®s->dtxfsts);
+
+ /*
+ * While there is space in the queue, space in the FIFO and data to
+ * tranfer, write packets to the Tx FIFO
+ */
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ while (txstatus.b.txfspcavail > dwords && ep->dwc_ep.xfer_count <
+ ep->dwc_ep.xfer_len && ep->dwc_ep.xfer_len != 0) {
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ dwords = count_dwords(ep, len);
+ txstatus.d32 = dwc_read_reg32(®s->dtxfsts);
+ }
+ /* Clear emptyintr */
+ diepint.b.emptyintr = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, epnum), diepint.d32);
+ return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected. It stops any active
+ * requests and informs the Gadget driver of the disconnect.
+ */
+void dwc_otg_pcd_stop(struct dwc_pcd *pcd)
+{
+ int i, num_in_eps, num_out_eps;
+ struct pcd_ep *ep;
+ union gintmsk_data intr_mask = {.d32 = 0};
+
+ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+ /* Don't disconnect drivers more than once */
+ if (pcd->ep0state == EP0_DISCONNECT)
+ return;
+ pcd->ep0state = EP0_DISCONNECT;
+
+ /* Reset the OTG state. */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Disable the NP Tx Fifo Empty Interrupt. */
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Flush the FIFOs */
+ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0);
+ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ ep = &pcd->ep0;
+ request_nuke(ep);
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_in_eps; i++)
+ request_nuke((struct pcd_ep *) &pcd->in_ep[i]);
+
+ /* Prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_out_eps; i++)
+ request_nuke((struct pcd_ep *) &pcd->out_ep[i]);
+
+ /* Report disconnect; the driver is already quiesced */
+ if (pcd->driver && pcd->driver->disconnect) {
+ spin_unlock(&pcd->lock);
+ pcd->driver->disconnect(&pcd->gadget);
+ spin_lock(&pcd->lock);
+ }
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_i2c_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts;
+
+ printk(KERN_INFO "Interrupt handler not implemented for i2cintr\n");
+
+ /* Turn off and clean the interrupt */
+ intr_mask.b.i2cintr = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.d32 = 0;
+ gintsts.b.i2cintr = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_early_suspend_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts;
+
+ printk(KERN_INFO "Early Suspend Detected\n");
+
+ /* Turn off and clean the interrupt */
+ intr_mask.b.erlysuspend = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.d32 = 0;
+ gintsts.b.erlysuspend = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet:
+ *
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ * packets)
+ *
+ * In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ */
+static void ep0_out_start(struct core_if *core_if, struct dwc_pcd *pcd)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ union deptsiz0_data doeptsize0 = {.d32 = 0};
+
+ doeptsize0.b.supcnt = 3;
+ doeptsize0.b.pktcnt = 1;
+ doeptsize0.b.xfersize = 8 * 3;
+ dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, doeptsize0.d32);
+
+ if (core_if->dma_enable) {
+ union depctl_data doepctl = {.d32 = 0};
+
+ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma,
+ pcd->setup_pkt_dma_handle);
+
+ doepctl.b.epena = 1;
+ doepctl.b.usbactep = 1;
+ dwc_write_reg32(out_ep_ctl_reg(pcd, 0), doepctl.d32);
+ }
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected. When the USB Reset
+ * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set
+ * to IDLE.
+ *
+ * Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ *
+ * Unmask the following interrupt bits:
+ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ * - DOEPMSK.SETUP = 1
+ * - DOEPMSK.XferCompl = 1
+ * - DIEPMSK.XferCompl = 1
+ * - DIEPMSK.TimeOut = 1
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ * packets)
+ *
+ * - In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ *
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ *
+ * Note that the bits in the Device IN endpoint mask register (diepmsk) are laid
+ * out exactly the same as the Device IN endpoint interrupt register (diepint.)
+ * Likewise for Device OUT endpoint mask / interrupt registers (doepmsk /
+ * doepint.)
+ */
+static int dwc_otg_pcd_handle_usb_reset_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ union depctl_data doepctl = {.d32 = 0};
+ union daint_data daintmsk = {.d32 = 0};
+ union doepint_data doepmsk = {.d32 = 0};
+ union diepint_data diepmsk = {.d32 = 0};
+ union dcfg_data dcfg = {.d32 = 0};
+ union grstctl_data resetctl = {.d32 = 0};
+ union dctl_data dctl = {.d32 = 0};
+ u32 i;
+ union gintsts_data gintsts = {.d32 = 0 };
+
+ printk(KERN_INFO "USB RESET\n");
+
+ /* reset the HNP settings */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Clear the Remote Wakeup Signalling */
+ dctl.b.rmtwkupsig = 1;
+ dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, 0);
+
+ /* Set NAK for all OUT EPs */
+ doepctl.b.snak = 1;
+ for (i = 0; i <= dev_if->num_out_eps; i++)
+ dwc_write_reg32(out_ep_ctl_reg(pcd, i), doepctl.d32);
+
+ /* Flush the NP Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, 0);
+
+ /* Flush the Learning Queue */
+ resetctl.b.intknqflsh = 1;
+ dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+ daintmsk.b.inep0 = 1;
+ daintmsk.b.outep0 = 1;
+ dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, daintmsk.d32);
+
+ doepmsk.b.setup = 1;
+ doepmsk.b.xfercompl = 1;
+ doepmsk.b.ahberr = 1;
+ doepmsk.b.epdisabled = 1;
+ dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32);
+
+ diepmsk.b.xfercompl = 1;
+ diepmsk.b.timeout = 1;
+ diepmsk.b.epdisabled = 1;
+ diepmsk.b.ahberr = 1;
+ diepmsk.b.intknepmis = 1;
+ dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32);
+
+ /* Reset Device Address */
+ dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.devaddr = 0;
+ dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ /* setup EP0 to receive SETUP packets */
+ ep0_out_start(core_if, pcd);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.usbreset = 1;
+ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ */
+static int get_device_speed(struct dwc_pcd *pcd)
+{
+ union dsts_data dsts;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ dsts.d32 = dwc_read_reg32(dev_sts_reg(pcd));
+
+ switch (dsts.b.enumspd) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ speed = USB_SPEED_HIGH;
+ break;
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ speed = USB_SPEED_FULL;
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ speed = USB_SPEED_LOW;
+ break;
+ }
+ return speed;
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets. It is normally called when the "Enumeration
+ * Done" interrupt occurs.
+ */
+static void dwc_otg_ep0_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ union dsts_data dsts;
+ union depctl_data diepctl;
+ union depctl_data doepctl;
+ union dctl_data dctl = {.d32 = 0};
+
+ /* Read the Device Status and Endpoint 0 Control registers */
+ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts);
+ diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl);
+ doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl);
+
+ /* Set the MPS of the IN EP based on the enumeration speed */
+ switch (dsts.b.enumspd) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_64;
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_8;
+ break;
+ }
+ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+ /* Enable OUT EP for receive */
+ doepctl.b.epena = 1;
+ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+
+ dctl.b.cgnpinnak = 1;
+ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+static int dwc_otg_pcd_handle_enum_done_intr(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+ union gintsts_data gintsts;
+ union gusbcfg_data gusbcfg;
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct core_global_regs *global_regs = core_if->core_global_regs;
+ u32 gsnpsid = global_regs->gsnpsid;
+ u8 utmi16b, utmi8b;
+
+ if (gsnpsid >= (u32)0x4f54260a) {
+ utmi16b = 5;
+ utmi8b = 9;
+ } else {
+ utmi16b = 4;
+ utmi8b = 8;
+ }
+ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 0;
+ pcd->gadget.speed = get_device_speed(pcd);
+
+ gusbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg);
+
+ /* Set USB turnaround time based on device speed and PHY interface. */
+ if (pcd->gadget.speed == USB_SPEED_HIGH) {
+ switch (core_if->hwcfg2.b.hs_phy_type) {
+ case DWC_HWCFG2_HS_PHY_TYPE_ULPI:
+ gusbcfg.b.usbtrdtim = 9;
+ break;
+ case DWC_HWCFG2_HS_PHY_TYPE_UTMI:
+ if (core_if->hwcfg4.b.utmi_phy_data_width == 0)
+ gusbcfg.b.usbtrdtim = utmi8b;
+ else if (core_if->hwcfg4.b.utmi_phy_data_width == 1)
+ gusbcfg.b.usbtrdtim = utmi16b;
+ else if (core_if->core_params->phy_utmi_width == 8)
+ gusbcfg.b.usbtrdtim = utmi8b;
+ else
+ gusbcfg.b.usbtrdtim = utmi16b;
+ break;
+ case DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI:
+ if (gusbcfg.b.ulpi_utmi_sel == 1) {
+ gusbcfg.b.usbtrdtim = 9;
+ } else {
+ if (core_if->core_params->phy_utmi_width == 16)
+ gusbcfg.b.usbtrdtim = utmi16b;
+ else
+ gusbcfg.b.usbtrdtim = utmi8b;
+ }
+ break;
+ }
+ } else {
+ /* Full or low speed */
+ gusbcfg.b.usbtrdtim = 9;
+ }
+ dwc_write_reg32(&global_regs->gusbcfg, gusbcfg.d32);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.enumdone = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+static int dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts;
+
+ printk(KERN_INFO "Interrupt Handler not implemented for ISOC Out "
+ "Dropped\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.isooutdrop = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.d32 = 0;
+ gintsts.b.isooutdrop = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions. If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_end_periodic_frame_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts;
+
+ printk(KERN_INFO "Interrupt handler not implemented for End of "
+ "Periodic Portion of Micro-Frame Interrupt");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.eopframe = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.d32 = 0;
+ gintsts.b.eopframe = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received. The non-periodic Tx FIFO is flushed,
+ * so it can be reloaded in the order seen in the IN Token Queue.
+ */
+static int dwc_otg_pcd_handle_ep_mismatch_intr(struct core_if *core_if)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts;
+
+ printk(KERN_INFO "Interrupt handler not implemented for End Point "
+ "Mismatch\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.epmismatch = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.d32 = 0;
+ gintsts.b.epmismatch = 1;
+ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+
+/**
+ * This funcion stalls EP0.
+ */
+static void ep0_do_stall(struct dwc_pcd *pcd, const int val)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+ struct usb_ctrlrequest *ctrl = &pcd->setup_pkt->req;
+
+ printk(KERN_WARNING "req %02x.%02x protocol STALL; err %d\n",
+ ctrl->bRequestType, ctrl->bRequest, val);
+
+ ep0->dwc_ep.is_in = 1;
+ dwc_otg_ep_set_stall(pcd->otg_dev->core_if, &ep0->dwc_ep);
+
+ pcd->ep0.stopped = 1;
+ pcd->ep0state = EP0_IDLE;
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static void do_gadget_setup(struct dwc_pcd *pcd,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret = 0;
+
+ if (pcd->driver && pcd->driver->setup) {
+ spin_unlock(&pcd->lock);
+ ret = pcd->driver->setup(&pcd->gadget, ctrl);
+ spin_lock(&pcd->lock);
+
+ if (ret < 0)
+ ep0_do_stall(pcd, ret);
+
+ /** This is a g_file_storage gadget driver specific
+ * workaround: a DELAYED_STATUS result from the fsg_setup
+ * routine will result in the gadget queueing a EP0 IN status
+ * phase for a two-stage control transfer.
+ *
+ * Exactly the same as a SET_CONFIGURATION/SET_INTERFACE except
+ * that this is a class specific request. Need a generic way to
+ * know when the gadget driver will queue the status phase.
+ *
+ * Can we assume when we call the gadget driver setup() function
+ * that it will always queue and require the following flag?
+ * Need to look into this.
+ */
+ if (ret == 256 + 999)
+ pcd->request_config = 1;
+ }
+}
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_in_status_phase(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ if (pcd->ep0state == EP0_STALL)
+ return;
+
+ pcd->ep0state = EP0_STATUS;
+
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 1;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_out_status_phase(struct dwc_pcd *pcd)
+{
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ if (pcd->ep0state == EP0_STALL)
+ return;
+ pcd->ep0state = EP0_STATUS;
+
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 0;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static void pcd_clear_halt(struct dwc_pcd *pcd, struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+
+ if (!ep->dwc_ep.stall_clear_flag)
+ dwc_otg_ep_clear_stall(core_if, &ep->dwc_ep);
+
+ /* Reactive the EP */
+ dwc_otg_ep_activate(core_if, &ep->dwc_ep);
+
+ if (ep->stopped) {
+ ep->stopped = 0;
+ /* If there is a request in the EP queue start it */
+
+ /*
+ * start_next_request(), outside of interrupt context at some
+ * time after the current time, after a clear-halt setup packet.
+ * Still need to implement ep mismatch in the future if a gadget
+ * ever uses more than one endpoint at once
+ */
+ if (core_if->dma_enable) {
+ ep->queue_sof = 1;
+ tasklet_schedule(pcd->start_xfer_tasklet);
+ } else {
+ /*
+ * Added-sr: 2007-07-26
+ *
+ * To re-enable this endpoint it's important to
+ * set this next_ep number. Otherwise the endpoint
+ * will not get active again after stalling.
+ */
+ if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+ start_next_request(ep);
+ }
+ }
+
+ /* Start Control Status Phase */
+ do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet is sent
+ * from the host. The Device Control register is written with the Test Mode
+ * bits set to the specified Test Mode. This is done as a tasklet so that the
+ * "Status" phase of the control transfer completes before transmitting the TEST
+ * packets.
+ *
+ */
+static void do_test_mode(unsigned long data)
+{
+ union dctl_data dctl;
+ struct dwc_pcd *pcd = (struct dwc_pcd *) data;
+ int test_mode = pcd->test_mode;
+
+ dctl.d32 = dwc_read_reg32(dev_ctl_reg(pcd));
+ switch (test_mode) {
+ case 1: /* TEST_J */
+ dctl.b.tstctl = 1;
+ break;
+ case 2: /* TEST_K */
+ dctl.b.tstctl = 2;
+ break;
+ case 3: /* TEST_SE0_NAK */
+ dctl.b.tstctl = 3;
+ break;
+ case 4: /* TEST_PACKET */
+ dctl.b.tstctl = 4;
+ break;
+ case 5: /* TEST_FORCE_ENABLE */
+ dctl.b.tstctl = 5;
+ break;
+ }
+ dwc_write_reg32(dev_ctl_reg(pcd), dctl.d32);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static void do_set_feature(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct core_global_regs *regs = core_if->core_global_regs;
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ struct pcd_ep *ep = NULL;
+ int otg_cap = core_if->core_params->otg_cap;
+ union gotgctl_data gotgctl = {.d32 = 0};
+
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (__le16_to_cpu(ctrl.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 1;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ /*
+ * Setup the Test Mode tasklet to do the Test
+ * Packet generation after the SETUP Status
+ * phase has completed.
+ */
+
+ pcd->test_mode_tasklet.next = 0;
+ pcd->test_mode_tasklet.state = 0;
+ atomic_set(&pcd->test_mode_tasklet.count, 0);
+
+ pcd->test_mode_tasklet.func = do_test_mode;
+ pcd->test_mode_tasklet.data = (unsigned long)pcd;
+ pcd->test_mode = __le16_to_cpu(ctrl.wIndex) >> 8;
+ tasklet_schedule(&pcd->test_mode_tasklet);
+
+ break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ /* dev may initiate HNP */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->b_hnp_enable = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ /*
+ * gotgctl.devhnpen cleared by a
+ * USB Reset?
+ */
+ gotgctl.b.devhnpen = 1;
+ gotgctl.b.hnpreq = 1;
+ dwc_write_reg32(®s->gotgctl, gotgctl.d32);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ case USB_DEVICE_A_HNP_SUPPORT:
+ /* RH port supports HNP */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ /* other RH port does */
+ if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_alt_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ }
+ break;
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ case USB_RECIP_INTERFACE:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ case USB_RECIP_ENDPOINT:
+ if (__le16_to_cpu(ctrl.wValue) == USB_ENDPOINT_HALT) {
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+
+ if (ep == 0) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ }
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static void do_clear_feature(struct dwc_pcd *pcd)
+{
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ struct pcd_ep *ep = NULL;
+
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (__le16_to_cpu(ctrl.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 0;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ /* Add CLEAR_FEATURE for TEST modes. */
+ break;
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+ if (ep == 0) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+
+ pcd_clear_halt(pcd, ep);
+ break;
+ }
+}
+
+/**
+ * This function processes SETUP commands. In Linux, the USB Command processing
+ * is done in two places - the first being the PCD and the second in the Gadget
+ * Driver (for example, the File-Backed Storage Gadget Driver).
+ *
+ * GET_STATUS: Command is processed as defined in chapter 9 of the USB 2.0
+ * Specification chapter 9
+ *
+ * CLEAR_FEATURE: The Device and Endpoint requests are the ENDPOINT_HALT feature
+ * is procesed, all others the interface requests are ignored.
+ *
+ * SET_FEATURE: The Device and Endpoint requests are processed by the PCD.
+ * Interface requests are passed to the Gadget Driver.
+ *
+ * SET_ADDRESS: PCD, Program the DCFG reg, with device address received
+ *
+ * GET_DESCRIPTOR: Gadget Driver, Return the requested descriptor
+ *
+ * SET_DESCRIPTOR: Gadget Driver, Optional - not implemented by any of the
+ * existing Gadget Drivers.
+ *
+ * SET_CONFIGURATION: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_CONFIGURATION: Gadget Driver, Return the current configuration
+ *
+ * SET_INTERFACE: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_INTERFACE: Gadget Driver, Return the current interface.
+ *
+ * SYNC_FRAME: Display debug message.
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static void pcd_setup(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+ struct pcd_ep *ep;
+ struct pcd_ep *ep0 = &pcd->ep0;
+ u16 *status = pcd->status_buf;
+ union deptsiz0_data doeptsize0 = {.d32 = 0};
+
+ doeptsize0.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doeptsiz);
+
+ /* handle > 1 setup packet , assert error for now */
+ if (core_if->dma_enable && (doeptsize0.b.supcnt < 2))
+ printk(KERN_ERR "\n\n CANNOT handle > 1 setup packet in "
+ "DMA mode\n\n");
+
+ /* Clean up the request queue */
+ request_nuke(ep0);
+ ep0->stopped = 0;
+
+ if (ctrl.bRequestType & USB_DIR_IN) {
+ ep0->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_IN_DATA_PHASE;
+ } else {
+ ep0->dwc_ep.is_in = 0;
+ pcd->ep0state = EP0_OUT_DATA_PHASE;
+ }
+
+ if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
+ /*
+ * Handle non-standard (class/vendor) requests in the gadget
+ * driver
+ */
+ do_gadget_setup(pcd, &ctrl);
+ return;
+ }
+
+ switch (ctrl.bRequest) {
+ case USB_REQ_GET_STATUS:
+ switch (ctrl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ *status = 0x1; /* Self powered */
+ *status |= pcd->remote_wakeup_enable << 1;
+ break;
+ case USB_RECIP_INTERFACE:
+ *status = 0;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+ if (ep == 0 || __le16_to_cpu(ctrl.wLength) > 2) {
+ ep0_do_stall(pcd, -EOPNOTSUPP);
+ return;
+ }
+ *status = ep->stopped;
+ break;
+ }
+
+ *status = __cpu_to_le16(*status);
+
+ pcd->ep0_pending = 1;
+ ep0->dwc_ep.start_xfer_buff = (u8 *) status;
+ ep0->dwc_ep.xfer_buff = (u8 *) status;
+ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+ ep0->dwc_ep.xfer_len = 2;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ do_clear_feature(pcd);
+ break;
+ case USB_REQ_SET_FEATURE:
+ do_set_feature(pcd);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ if (ctrl.bRequestType == USB_RECIP_DEVICE) {
+ union dcfg_data dcfg = {.d32 = 0};
+
+ dcfg.b.devaddr = __le16_to_cpu(ctrl.wValue);
+ dwc_modify_reg32(&dev_if->dev_global_regs->dcfg, 0,
+ dcfg.d32);
+ do_setup_in_status_phase(pcd);
+ return;
+ }
+ break;
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SET_CONFIGURATION:
+ pcd->request_config = 1; /* Configuration changed */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ default:
+ /* Call the Gadget Driver's setup functions */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ }
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int ep0_complete_request(struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(ep->pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ struct device_in_ep_regs *in_regs = dev_if->in_ep_regs[ep->dwc_ep.num];
+ union deptsiz0_data deptsiz;
+ struct pcd_request *req;
+ int is_last = 0;
+ struct dwc_pcd *pcd = ep->pcd;
+
+ if (pcd->ep0_pending && list_empty(&ep->queue)) {
+ if (ep->dwc_ep.is_in)
+ do_setup_out_status_phase(pcd);
+ else
+ do_setup_in_status_phase(pcd);
+
+ pcd->ep0_pending = 0;
+ pcd->ep0state = EP0_STATUS;
+ return 1;
+ }
+
+ if (list_empty(&ep->queue))
+ return 0;
+
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ if (pcd->ep0state == EP0_STATUS) {
+ is_last = 1;
+ } else if (ep->dwc_ep.is_in) {
+ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz);
+
+ if (deptsiz.b.xfersize == 0) {
+ req->req.actual = ep->dwc_ep.xfer_count;
+ do_setup_out_status_phase(pcd);
+ }
+ } else {
+ /* This is ep0-OUT */
+ req->req.actual = ep->dwc_ep.xfer_count;
+ do_setup_in_status_phase(pcd);
+ }
+
+ /* Complete the request */
+ if (is_last) {
+ request_done(ep, req, 0);
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * This function completes the request for the EP. If there are additional
+ * requests for the EP in the queue they will be started.
+ */
+static void complete_ep(struct pcd_ep *ep)
+{
+ struct core_if *core_if = GET_CORE_IF(ep->pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ struct device_in_ep_regs *in_ep_regs =
+ dev_if->in_ep_regs[ep->dwc_ep.num];
+ union deptsiz_data deptsiz;
+ struct pcd_request *req = NULL;
+ int is_last = 0;
+
+ /* Get any pending requests */
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+ if (ep->dwc_ep.is_in) {
+ deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz);
+
+ if (core_if->dma_enable && !deptsiz.b.xfersize)
+ ep->dwc_ep.xfer_count = ep->dwc_ep.xfer_len;
+
+ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 &&
+ ep->dwc_ep.xfer_count == ep->dwc_ep.xfer_len)
+ is_last = 1;
+ else
+ printk(KERN_WARNING "Incomplete transfer (%s-%s "
+ "[siz=%d pkt=%d])\n", ep->ep.name,
+ ep->dwc_ep.is_in ? "IN" : "OUT",
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+ } else {
+ struct device_out_ep_regs *out_ep_regs =
+ dev_if->out_ep_regs[ep->dwc_ep.num];
+ deptsiz.d32 = dwc_read_reg32(&out_ep_regs->doeptsiz);
+ is_last = 1;
+ }
+
+ /* Complete the request */
+ if (is_last) {
+ /*
+ * 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(core_if, DWC_LIMITED_XFER)) &&
+ ep->dwc_ep.bytes_pending) {
+ struct device_in_ep_regs *in_regs =
+ core_if->dev_if->in_ep_regs[ep->dwc_ep.num];
+ union gintmsk_data intr_mask = { .d32 = 0};
+
+ ep->dwc_ep.xfer_len = ep->dwc_ep.bytes_pending;
+ if (ep->dwc_ep.xfer_len > MAX_XFER_LEN) {
+ ep->dwc_ep.bytes_pending = ep->dwc_ep.xfer_len -
+ MAX_XFER_LEN;
+ ep->dwc_ep.xfer_len = MAX_XFER_LEN;
+ } else {
+ ep->dwc_ep.bytes_pending = 0;
+ }
+
+ /*
+ * Restart the current transfer with the next "chunk"
+ * of data.
+ */
+ ep->dwc_ep.xfer_count = 0;
+
+ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz));
+ deptsiz.b.xfersize = ep->dwc_ep.xfer_len;
+ deptsiz.b.pktcnt = (ep->dwc_ep.xfer_len - 1 +
+ ep->dwc_ep.maxpacket) / ep->dwc_ep.maxpacket;
+ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintsts,
+ intr_mask.d32, 0);
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+
+ /*
+ * Just return here if message was not completely
+ * transferred.
+ */
+ return;
+ }
+ if (core_if->dma_enable)
+ req->req.actual = ep->dwc_ep.xfer_len -
+ deptsiz.b.xfersize;
+ else
+ req->req.actual = ep->dwc_ep.xfer_count;
+
+ request_done(ep, req, 0);
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+
+ /* If there is a request in the queue start it. */
+ start_next_request(ep);
+ }
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ */
+static void dwc_otg_ep0_continue_transfer(struct core_if *c_if,
+ struct dwc_ep *ep)
+{
+ union depctl_data depctl;
+ union deptsiz0_data deptsiz;
+ 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 (ep->is_in) {
+ struct device_in_ep_regs *in_regs = d_if->in_ep_regs[0];
+ union gnptxsts_data tx_status = {.d32 = 0};
+
+ tx_status.d32 = dwc_read_reg32(&glbl_regs->gnptxsts);
+
+ depctl.d32 = dwc_read_reg32(&in_regs->diepctl);
+ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz);
+
+ /*
+ * Program the transfer size and packet count as follows:
+ * xfersize = N * maxpacket + short_packet
+ * pktcnt = N + (short_packet exist ? 1 : 0)
+ */
+ if (ep->total_len - ep->xfer_count > ep->maxpacket)
+ deptsiz.b.xfersize = ep->maxpacket;
+ else
+ deptsiz.b.xfersize = ep->total_len - ep->xfer_count;
+
+ deptsiz.b.pktcnt = 1;
+ ep->xfer_len += deptsiz.b.xfersize;
+ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+ /* Write the DMA register */
+ if (c_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH)
+ 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);
+
+ /*
+ * Enable the Non-Periodic Tx FIFO empty interrupt, the
+ * data will be written into the fifo by the ISR.
+ */
+ if (!c_if->dma_enable) {
+ /* First clear it from GINTSTS */
+ intr_mask.b.nptxfempty = 1;
+ dwc_write_reg32(&glbl_regs->gintsts, intr_mask.d32);
+
+ /* To avoid spurious NPTxFEmp intr */
+ dwc_modify_reg32(&glbl_regs->gintmsk, intr_mask.d32, 0);
+ }
+ }
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in ep0state
+ */
+static void handle_ep0(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct pcd_ep *ep0 = &pcd->ep0;
+
+ switch (pcd->ep0state) {
+ case EP0_DISCONNECT:
+ break;
+ case EP0_IDLE:
+ pcd->request_config = 0;
+ pcd_setup(pcd);
+ break;
+ case EP0_IN_DATA_PHASE:
+ if (core_if->dma_enable)
+ /*
+ * For EP0 we can only program 1 packet at a time so we
+ * need to do the calculations after each complete.
+ * Call write_packet to make the calculations, as in
+ * slave mode, and use those values to determine if we
+ * can complete.
+ */
+ dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 1);
+ else
+ dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 0);
+
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)
+ dwc_otg_ep0_continue_transfer(core_if, &ep0->dwc_ep);
+ else
+ ep0_complete_request(ep0);
+ break;
+ case EP0_OUT_DATA_PHASE:
+ ep0_complete_request(ep0);
+ break;
+ case EP0_STATUS:
+ ep0_complete_request(ep0);
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 1;
+ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */
+
+ /* Prepare for more SETUP Packets */
+ if (core_if->dma_enable) {
+ ep0_out_start(core_if, pcd);
+ } else {
+ int i;
+ union depctl_data diepctl;
+
+ 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);
+ }
+
+ 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 < core_if->dev_if->num_in_eps; i++) {
+ 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]);
+ }
+ }
+ }
+ break;
+ case EP0_STALL:
+ printk(KERN_ERR "EP0 STALLed, should not get here "
+ "handle_ep0()\n");
+ break;
+ }
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(struct dwc_pcd *pcd, const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ union deptsiz_data dieptsiz = {.d32 = 0};
+ struct pcd_ep *ep;
+
+ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[ep_num]->dieptsiz);
+ ep = get_in_ep(pcd, ep_num);
+
+ /*
+ * If pktcnt is not 0, and xfersize is 0, and there is a buffer,
+ * resend the last packet.
+ */
+ if (dieptsiz.b.pktcnt && !dieptsiz.b.xfersize &&
+ ep->dwc_ep.start_xfer_buff) {
+ if (ep->dwc_ep.xfer_len <= ep->dwc_ep.maxpacket) {
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+ } else {
+ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+
+ /* convert packet size to dwords. */
+ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+ }
+ ep->stopped = 0;
+
+ if (!ep_num)
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ else
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+}
+
+/**
+ * Handle the IN EP Transfer Complete interrupt.
+ *
+ * If dedicated fifos are enabled, then the Tx FIFO empty interrupt for the EP
+ * is disabled. Otherwise the NP Tx FIFO empty interrupt is disabled.
+ */
+static void handle_in_ep_xfr_complete_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ struct core_if *c_if = GET_CORE_IF(pcd);
+ struct device_if *d_if = c_if->dev_if;
+ struct dwc_ep *dwc_ep = &ep->dwc_ep;
+ union diepint_data epint = {.d32 = 0};
+
+ if (c_if->en_multiple_tx_fifo) {
+ u32 fifoemptymsk = 0x1 << dwc_ep->num;
+ dwc_modify_reg32(&d_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ fifoemptymsk, 0);
+ } else {
+ union gintmsk_data intr_mask = {.d32 = 0};
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&c_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+ }
+
+ /* Clear the interrupt, then complete the transfer */
+ epint.b.xfercompl = 1;
+ dwc_write_reg32(&d_if->in_ep_regs[num]->diepint, epint.d32);
+
+ if (!num)
+ handle_ep0(pcd);
+ else
+ complete_ep(ep);
+}
+
+/**
+ * Handle the IN EP disable interrupt.
+ */
+static void handle_in_ep_disable_intr(struct dwc_pcd *pcd,
+ const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct device_if *dev_if = core_if->dev_if;
+ union deptsiz_data dieptsiz = {.d32 = 0};
+ union dctl_data dctl = {.d32 = 0};
+ struct pcd_ep *ep;
+ struct dwc_ep *dwc_ep;
+ union diepint_data diepint = {.d32 = 0};
+
+ ep = get_in_ep(pcd, ep_num);
+ dwc_ep = &ep->dwc_ep;
+
+ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[ep_num]->dieptsiz);
+
+ if (ep->stopped) {
+ /* Flush the Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+
+ /* Clear the Global IN NP NAK */
+ dctl.d32 = 0;
+ dctl.b.cgnpinnak = 1;
+ dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, 0);
+
+ if (dieptsiz.b.pktcnt || dieptsiz.b.xfersize)
+ restart_transfer(pcd, ep_num);
+ } else {
+ if (dieptsiz.b.pktcnt || dieptsiz.b.xfersize)
+ restart_transfer(pcd, ep_num);
+ }
+ /* Clear epdisabled */
+ diepint.b.epdisabled = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, ep_num), diepint.d32);
+
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static void handle_in_ep_timeout_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ struct pcd_ep *ep;
+ union dctl_data dctl = {.d32 = 0};
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union diepint_data diepint = {.d32 = 0};
+
+ ep = get_in_ep(pcd, ep_num);
+
+ /* Disable the NP Tx Fifo Empty Interrrupt */
+ if (!core_if->dma_enable) {
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+ }
+
+ /* Non-periodic EP */
+ /* Enable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+
+ /* Set Global IN NAK */
+ dctl.b.sgnpinnak = 1;
+ dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, dctl.d32);
+ ep->stopped = 1;
+
+ /* Clear timeout */
+ diepint.b.timeout = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, ep_num), diepint.d32);
+}
+
+/**
+ * Handles the IN Token received with TxF Empty interrupt.
+ *
+ * For the 405EZ, only start the next transfer, when currently no other transfer
+ * is active on this endpoint.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void handle_in_ep_tx_fifo_empty_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ union diepint_data diepint = {.d32 = 0};
+
+ if (!ep->stopped && num) {
+ union diepint_data diepmsk = {.d32 = 0};
+ diepmsk.b.intktxfemp = 1;
+ dwc_modify_reg32(dev_diepmsk_reg(pcd), diepmsk.d32, 0);
+
+ if (dwc_has_feature(GET_CORE_IF(pcd), DWC_LIMITED_XFER)) {
+ if (!ep->dwc_ep.active)
+ start_next_request(ep);
+ } else {
+ start_next_request(ep);
+ }
+ }
+ /* Clear intktxfemp */
+ diepint.b.intktxfemp = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, num), diepint.d32);
+}
+
+static void handle_in_ep_nak_effective_intr(struct dwc_pcd *pcd,
+ struct pcd_ep *ep, u32 num)
+{
+ union depctl_data diepctl = {.d32 = 0};
+ union diepint_data diepint = {.d32 = 0};
+
+ /* Periodic EP */
+ if (ep->disabling) {
+ diepctl.d32 = 0;
+ diepctl.b.snak = 1;
+ diepctl.b.epdis = 1;
+ dwc_modify_reg32(in_ep_ctl_reg(pcd, num), diepctl.d32,
+ diepctl.d32);
+ }
+ /* Clear inepnakeff */
+ diepint.b.inepnakeff = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, num), diepint.d32);
+
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline u32 dwc_otg_read_diep_intr(struct core_if *core_if,
+ struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 v, msk, emp;
+ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk);
+ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+ return v;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_in_ep_intr(struct core_if *_if)
+{
+ u32 v;
+ v = dwc_read_reg32(&_if->dev_if->dev_global_regs->daint) &
+ dwc_read_reg32(&_if->dev_if->dev_global_regs->daintmsk);
+ return v & 0xffff;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register
+ * - Repeat the following for each IN EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DIEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Time-out Handshake" log error
+ * - If "IN Token Received when TxFIFO Empty" write packet to Tx FIFO.
+ * - If "IN Token EP Mismatch" (disable, this is handled by EP Mismatch
+ * Interrupt)
+ */
+static int dwc_otg_pcd_handle_in_ep_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ union diepint_data diepint = {.d32 = 0};
+ u32 ep_intr;
+ u32 epnum = 0;
+ struct pcd_ep *ep;
+ struct dwc_ep *dwc_ep;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+ /* Service the Device IN interrupts for each endpoint */
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ union diepint_data c_diepint;
+
+ /* Get EP pointer */
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ diepint.d32 = dwc_otg_read_diep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (diepint.b.xfercompl)
+ handle_in_ep_xfr_complete_intr(pcd, ep, epnum);
+
+ /* Endpoint disable */
+ if (diepint.b.epdisabled)
+ handle_in_ep_disable_intr(pcd, epnum);
+
+ /* AHB Error */
+ if (diepint.b.ahberr) {
+ /* Clear ahberr */
+ c_diepint.d32 = 0;
+ c_diepint.b.ahberr = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, epnum),
+ c_diepint.d32);
+ }
+
+ /* TimeOUT Handshake (non-ISOC IN EPs) */
+ if (diepint.b.timeout)
+ handle_in_ep_timeout_intr(pcd, epnum);
+
+ /* IN Token received with TxF Empty */
+ if (diepint.b.intktxfemp)
+ handle_in_ep_tx_fifo_empty_intr(pcd, ep, epnum);
+
+ /* IN Token Received with EP mismatch */
+ if (diepint.b.intknepmis) {
+ /* Clear intknepmis */
+ c_diepint.d32 = 0;
+ c_diepint.b.intknepmis = 1;
+ dwc_write_reg32(in_ep_int_reg(pcd, epnum),
+ c_diepint.d32);
+ }
+
+ /* IN Endpoint NAK Effective */
+ if (diepint.b.inepnakeff)
+ handle_in_ep_nak_effective_intr(pcd, ep, epnum);
+
+ /* IN EP Tx FIFO Empty Intr */
+ if (diepint.b.emptyintr)
+ write_empty_tx_fifo(pcd, epnum);
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_out_ep_intr(struct core_if *_if)
+{
+ u32 v;
+ v = dwc_read_reg32(&_if->dev_if->dev_global_regs->daint) &
+ dwc_read_reg32(&_if->dev_if->dev_global_regs->daintmsk);
+ return (v & 0xffff0000) >> 16;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline u32 dwc_otg_read_doep_intr(struct core_if *core_if,
+ struct dwc_ep *ep)
+{
+ struct device_if *dev_if = core_if->dev_if;
+ u32 v;
+ v = dwc_read_reg32(&dev_if->out_ep_regs[ep->num]->doepint) &
+ dwc_read_reg32(&dev_if->dev_global_regs->doepmsk);
+ return v;
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register.
+ * - Repeat the following for each OUT EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DOEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Setup Phase Done" process Setup Packet (See Standard USB Command
+ * Processing)
+ */
+static int dwc_otg_pcd_handle_out_ep_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+ u32 ep_intr;
+ union doepint_data doepint = {.d32 = 0};
+ u32 epnum = 0;
+ struct dwc_ep *dwc_ep;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ union doepint_data c_doepint;
+
+ dwc_ep = &((get_out_ep(pcd, epnum))->dwc_ep);
+ doepint.d32 = dwc_otg_read_doep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (doepint.b.xfercompl) {
+ /* Clear xfercompl */
+ c_doepint.d32 = 0;
+ c_doepint.b.xfercompl = 1;
+ dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+ c_doepint.d32);
+ if (epnum == 0)
+ handle_ep0(pcd);
+ else
+ complete_ep(get_out_ep(pcd, epnum));
+ }
+
+ /* Endpoint disable */
+ if (doepint.b.epdisabled) {
+ /* Clear epdisabled */
+ c_doepint.d32 = 0;
+ c_doepint.b.epdisabled = 1;
+ dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+ c_doepint.d32);
+ }
+
+ /* AHB Error */
+ if (doepint.b.ahberr) {
+ c_doepint.d32 = 0;
+ c_doepint.b.ahberr = 1;
+ dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+ c_doepint.d32);
+ }
+
+ /* Setup Phase Done (control EPs) */
+ if (doepint.b.setup) {
+ c_doepint.d32 = 0;
+ c_doepint.b.setup = 1;
+ dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+ c_doepint.d32);
+ handle_ep0(pcd);
+ }
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+/**
+ * Incomplete ISO IN Transfer Interrupt. This interrupt indicates one of the
+ * following conditions occurred while transmitting an ISOC transaction.
+ *
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Disable EP. When "Endpoint Disabled" interrupt is received Flush FIFO
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_in_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts = {.d32 = 0};
+
+ printk(KERN_INFO "Interrupt handler not implemented for IN ISOC "
+ "Incomplete\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.incomplisoin = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.b.incomplisoin = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt. This interrupt indicates that the
+ * core has dropped an ISO OUT packet. The following conditions can be the
+ * cause:
+ *
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Read any data from the FIFO
+ * - Disable EP. When "Endpoint Disabled" interrupt is received re-enable EP.
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_out_intr(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts = {.d32 = 0};
+
+ printk(KERN_INFO "Interrupt handler not implemented for OUT ISOC "
+ "Incomplete\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.incomplisoout = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ gintsts.b.incomplisoout = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_in_nak_effective(struct dwc_pcd *pcd)
+{
+ struct device_if *dev_if = GET_CORE_IF(pcd)->dev_if;
+ union depctl_data diepctl = {.d32 = 0};
+ union depctl_data diepctl_rd = {.d32 = 0};
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts = {.d32 = 0};
+ u32 i;
+
+ /* Disable all active IN EPs */
+ diepctl.b.epdis = 1;
+ diepctl.b.snak = 1;
+ for (i = 0; i <= dev_if->num_in_eps; i++) {
+ diepctl_rd.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, i));
+ if (diepctl_rd.b.epena)
+ dwc_write_reg32(in_ep_ctl_reg(pcd, i), diepctl.d32);
+ }
+
+ /* Disable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.b.ginnakeff = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function handles the Global OUT NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_out_nak_effective(struct dwc_pcd *pcd)
+{
+ union gintmsk_data intr_mask = {.d32 = 0};
+ union gintsts_data gintsts = {.d32 = 0};
+
+ printk(KERN_INFO "Interrupt handler not implemented for Global IN "
+ "NAK Effective\n");
+
+ /* Turn off and clear the interrupt */
+ intr_mask.b.goutnakeff = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear goutnakeff */
+ gintsts.b.goutnakeff = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts. Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd)
+{
+ struct core_if *core_if = GET_CORE_IF(pcd);
+
+ union gintsts_data gintr_status;
+ int ret = 0;
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ spin_lock(&pcd->lock);
+
+ gintr_status.d32 = dwc_otg_read_core_intr(core_if);
+ if (!gintr_status.d32) {
+ spin_unlock(&pcd->lock);
+ return 0;
+ }
+
+ if (gintr_status.b.sofintr)
+ ret |= dwc_otg_pcd_handle_sof_intr(pcd);
+ if (gintr_status.b.rxstsqlvl)
+ ret |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+ if (gintr_status.b.nptxfempty)
+ ret |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+ if (gintr_status.b.ginnakeff)
+ ret |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+ if (gintr_status.b.goutnakeff)
+ ret |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+ if (gintr_status.b.i2cintr)
+ ret |= dwc_otg_pcd_handle_i2c_intr(pcd);
+ if (gintr_status.b.erlysuspend)
+ ret |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+ if (gintr_status.b.usbreset)
+ ret |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+ if (gintr_status.b.enumdone)
+ ret |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+ if (gintr_status.b.isooutdrop)
+ ret |=
+ dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(pcd);
+ if (gintr_status.b.eopframe)
+ ret |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+ if (gintr_status.b.epmismatch)
+ ret |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+ if (gintr_status.b.inepint)
+ ret |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ if (gintr_status.b.outepintr)
+ ret |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ if (gintr_status.b.incomplisoin)
+ ret |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+ if (gintr_status.b.incomplisoout)
+ ret |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+
+ spin_unlock(&pcd->lock);
+ }
+ return ret;
+}
--
1.7.1
^ permalink raw reply related
* Re: [PATCH 1/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: David Brownell @ 2010-07-12 23:55 UTC (permalink / raw)
To: linux-usb, Fushen Chen
Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <12789766042434-git-send-email-fchen@apm.com>
Please remove all the changes not related to
this Synopsis IP ... and make the OTG functionality
key on the generic OTG symbol, not a DW-specific one.
^ permalink raw reply
* Re: section .data..init_task
From: Sean MacLennan @ 2010-07-13 0:34 UTC (permalink / raw)
To: Denys Vlasenko, linuxppc-dev, Tim Abbott, Sam Ravnborg
In-Reply-To: <20100628005900.28d4a8e0@lappy.seanm.ca>
On Mon, 28 Jun 2010 00:59:00 -0400
Sean MacLennan <smaclennan@pikatech.com> wrote:
> Anybody else seeing these messages?
>
> ppc_4xxFP-ld: .tmp_vmlinux1: section .data..init_task lma 0xc0374000
> overlaps previous sections ppc_4xxFP-ld: .tmp_vmlinux2:
> section .data..init_task lma 0xc03a2000 overlaps previous sections
> ppc_4xxFP-ld: vmlinux: section .data..init_task lma 0xc03a2000
> overlaps previous sections
>
> Or does anybody know what they mean? They started showing up in
> 2.6.35.
>
> Very easy to reproduce, so don't hesitate to ask for more info.
I had a bit of time, so I tracked this down. This patch seems to be
the culprit: http://lkml.org/lkml/2010/2/19/366
Specifically, this code:
/* The initial task and kernel stack */
- .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
- INIT_TASK_DATA(THREAD_SIZE)
- }
+ INIT_TASK_DATA_SECTION(THREAD_SIZE)
If I change it back to:
/* The initial task and kernel stack */
.data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) {
INIT_TASK_DATA(THREAD_SIZE)
}
not only do the warnings go away, but the kernel now boots again!
Cheers,
Sean
^ permalink raw reply
* [PATCH] kbuild: Enable building defconfigs from Kconfig files
From: Stephen Rothwell @ 2010-07-13 1:43 UTC (permalink / raw)
To: Linus, Michal Marek; +Cc: Catalin Marinas, Andrew Morton, ppc-dev, LKML
After this change, doing a "make xxx_defconfig" will check first for
a file called arch/<arch>/configs/Kconfig.xxx and use that to generate
the .config (effectively starting from an allnoconfig). If that file
doesn't exist, it will use arch/<ARCH>/configs/xxx_defconfig as now.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
scripts/kconfig/Makefile | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
Hi Linus,
Is this more the direction you want to take?
There are still 2 main problems with is approach:
- there are some config options that are globally and
unconditionally enabled that some platforms may not want. The only way
currently to turn them off is to reproduce the config entry with the
different default. I am not sure if we need a wa to turn them off or to
just change them to being neede to be selected by those that do want them.
- we have no way to select options that are neither bool or
tristate to suitable values. Again the only way to do that currently is
to reproduce the config entry with a different default value.
I am currently working towards using this to recreate the PowerPC
defconfigs, but it is a slow process as they have some much stuff enabled
in them and some of it is probably actually not relevant.
This process is made easier by the recent commit "kbuild: Warn on
selecting symbols with unmet direct dependencies" that is in the kbuild
tree (and linux-next).
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 7ea649d..1ab8f45 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -117,9 +117,21 @@ else
$(Q)$< -D arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig)
endif
-%_defconfig: $(obj)/conf
+configs_dir := $(srctree)/arch/$(SRCARCH)/configs
+# We check a level of sub directories because arch/powerpc
+# has its defconfig files arranged that way
+old_defconfigs := $(patsubst $(configs_dir)/%,%,\
+ $(wildcard $(configs_dir)/*_defconfig) \
+ $(wildcard $(configs_dir)/*/*_defconfig))
+defconfigs := $(patsubst $(configs_dir)/Kconfig.%,%_defconfig,\
+ $(wildcard $(configs_dir)/Kconfig.*))
+
+$(old_defconfigs): %_defconfig: $(obj)/conf
$(Q)$< -D arch/$(SRCARCH)/configs/$@ $(Kconfig)
+$(defconfigs): %_defconfig: $(obj)/conf
+ $(Q)$< -n arch/$(SRCARCH)/configs/Kconfig.$*
+
# Help text used by make help
help:
@echo ' config - Update current config utilising a line-oriented program'
--
1.7.1
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
^ permalink raw reply related
* Re: [PATCH 0/7] De-couple sysfs memory directories from memory sections
From: Nathan Fontenot @ 2010-07-13 2:28 UTC (permalink / raw)
To: 7eggert; +Cc: Bodo Eggert, linuxppc-dev, linux-kernel
In-Reply-To: <20100712194958.1A32B2D6595@mail-in-10.arcor-online.net>
On 07/12/2010 02:30 PM, Bodo Eggert wrote:
> Nathan Fontenot <nfont@austin.ibm.com> wrote:
>
>> The file 'split' allows for splitting the
>> directory in two, with each new directory covering half as many
>> memory sections as the previous directory.
>
> Just some random thoughts:
> 1) Why is it needed/helpful?
This is needed if someone needed to perform an action (add/remove) a
single memory section. The 'split' option allows users to isolate
a memory section so these operations could be performed.
> 2) If it is needed, why not take an int to split after n entries?
The idea of being able to split a directory came out of a previous
discussion on how to resolve the issue this patch set is trying to solve.
I included the split functionality in this patch set since it
was suggested. I will leave the decision of whether or not this
functionality is needed up to the community.
-Nathan
^ permalink raw reply
* Re: [PATCH 1/7] Split the memory_block structure
From: KAMEZAWA Hiroyuki @ 2010-07-13 6:18 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B37CE.50802@austin.ibm.com>
plz cc linux-mm in the next time...
And please incudes updates for Documentation/memory-hotplug.txt.
On Mon, 12 Jul 2010 10:42:06 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:
> This patch splits the memory_block struct into a memory_block
> struct to cover each sysfs directory and a new memory_block_section
> struct for each memory section covered by the sysfs directory.
>
> This also updates the routine handling memory_block creation
> and manipulation to use these updated structures.
>
Could you clarify the number of memory_block_section per memory_block ?
> Signed -off-by: Nathan Fontenot <nfont@austin.ibm.com>
> ---
> drivers/base/memory.c | 228 +++++++++++++++++++++++++++++++++++--------------
> include/linux/memory.h | 11 +-
> 2 files changed, 172 insertions(+), 67 deletions(-)
>
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c 2010-07-08 11:27:21.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c 2010-07-09 14:23:09.000000000 -0500
> @@ -28,6 +28,14 @@
> #include <asm/uaccess.h>
>
> #define MEMORY_CLASS_NAME "memory"
> +#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
> +
> +static int sections_per_block;
> +
some default value, plz. Does this can be determined only by .config ?
> +static inline int base_memory_block_id(int section_nr)
> +{
> + return (section_nr / sections_per_block) * sections_per_block;
> +}
>
> static struct sysdev_class memory_sysdev_class = {
> .name = MEMORY_CLASS_NAME,
> @@ -94,10 +102,9 @@
> }
>
> static void
> -unregister_memory(struct memory_block *memory, struct mem_section *section)
> +unregister_memory(struct memory_block *memory)
> {
> BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
> - BUG_ON(memory->sysdev.id != __section_nr(section));
>
> /* drop the ref. we got in remove_memory_block() */
> kobject_put(&memory->sysdev.kobj);
> @@ -123,13 +130,20 @@
> static ssize_t show_mem_removable(struct sys_device *dev,
> struct sysdev_attribute *attr, char *buf)
> {
> - unsigned long start_pfn;
> - int ret;
> - struct memory_block *mem =
> - container_of(dev, struct memory_block, sysdev);
> + struct list_head *pos, *tmp;
> + struct memory_block *mem;
> + int ret = 1;
> +
> + mem = container_of(dev, struct memory_block, sysdev);
> + list_for_each_safe(pos, tmp, &mem->sections) {
> + struct memory_block_section *mbs;
> + unsigned long start_pfn;
> +
> + mbs = list_entry(pos, struct memory_block_section, next);
list_for_each_entry ?
> + start_pfn = section_nr_to_pfn(mbs->phys_index);
> + ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> + }
Hmm, them, only when the whole memory block is removable, it's shown as
removable. Right ?
Does it meets ppc guy's requirements ?
>
> - start_pfn = section_nr_to_pfn(mem->phys_index);
> - ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> return sprintf(buf, "%d\n", ret);
> }
Hmm...can't you print removable information as bitmap, here ?
overkill ?
>
> @@ -182,16 +196,16 @@
> * OK to have direct references to sparsemem variables in here.
> */
> static int
> -memory_block_action(struct memory_block *mem, unsigned long action)
> +memory_block_action(struct memory_block_section *mbs, unsigned long action)
> {
> int i;
> unsigned long psection;
> unsigned long start_pfn, start_paddr;
> struct page *first_page;
> int ret;
> - int old_state = mem->state;
> ot-option-to-disable-memory-hotplug.patch
> + int old_state = mbs->state;
Where is this noise from ?
>
> - psection = mem->phys_index;
> + psection = mbs->phys_index;
> first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
>
> /*
> @@ -217,18 +231,18 @@
> ret = online_pages(start_pfn, PAGES_PER_SECTION);
> break;
> case MEM_OFFLINE:
> - mem->state = MEM_GOING_OFFLINE;
> + mbs->state = MEM_GOING_OFFLINE;
> start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
> ret = remove_memory(start_paddr,
> PAGES_PER_SECTION << PAGE_SHIFT);
> if (ret) {
> - mem->state = old_state;
> + mbs->state = old_state;
> break;
> }
> break;
> default:
> WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
> - __func__, mem, action, action);
> + __func__, mbs, action, action);
> ret = -EINVAL;
> }
>
> @@ -238,19 +252,40 @@
> static int memory_block_change_state(struct memory_block *mem,
> unsigned long to_state, unsigned long from_state_req)
> {
> + struct memory_block_section *mbs;
> + struct list_head *pos;
> int ret = 0;
> +
> mutex_lock(&mem->state_mutex);
>
> - if (mem->state != from_state_req) {
> - ret = -EINVAL;
> - goto out;
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section, next);
> +
list_for_each_entry() ?
> + if (mbs->state != from_state_req)
> + continue;
> +
> + ret = memory_block_action(mbs, to_state);
> + if (ret)
> + break;
> + }
Then, all actions will be affect all memory sections under memory block ?
(Hmm..maybe have to see following patches ?)
> +
> + if (ret) {
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section,
> + next);
> +
list_for_each_entry() ?
> + if (mbs->state == from_state_req)
> + continue;
> +
> + if (memory_block_action(mbs, to_state))
> + printk(KERN_ERR "Could not re-enable memory "
> + "section %lx\n", mbs->phys_index);
> + }
> }
>
> - ret = memory_block_action(mem, to_state);
> if (!ret)
> mem->state = to_state;
>
> -out:
> mutex_unlock(&mem->state_mutex);
> return ret;
> }
> @@ -260,20 +295,15 @@
> struct sysdev_attribute *attr, const char *buf, size_t count)
> {
Hmm, store_mem_state() ? What diff option are you using ?
> struct memory_block *mem;
> - unsigned int phys_section_nr;
> int ret = -EINVAL;
>
> mem = container_of(dev, struct memory_block, sysdev);
> - phys_section_nr = mem->phys_index;
> -
> - if (!present_section_nr(phys_section_nr))
> - goto out;
>
> if (!strncmp(buf, "online", min((int)count, 6)))
> ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
> else if(!strncmp(buf, "offline", min((int)count, 7)))
> ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
> -out:
> +
> if (ret)
> return ret;
> return count;
> @@ -435,39 +465,6 @@
> return 0;
> }
>
> -static int add_memory_block(int nid, struct mem_section *section,
> - unsigned long state, enum mem_add_context context)
> -{
> - struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> - unsigned long start_pfn;
> - int ret = 0;
> -
> - if (!mem)
> - return -ENOMEM;
> -
> - mem->phys_index = __section_nr(section);
> - mem->state = state;
> - mutex_init(&mem->state_mutex);
> - start_pfn = section_nr_to_pfn(mem->phys_index);
> - mem->phys_device = arch_get_memory_phys_device(start_pfn);
> -
> - ret = register_memory(mem, section);
> - if (!ret)
> - ret = mem_create_simple_file(mem, phys_index);
> - if (!ret)
> - ret = mem_create_simple_file(mem, state);
> - if (!ret)
> - ret = mem_create_simple_file(mem, phys_device);
> - if (!ret)
> - ret = mem_create_simple_file(mem, removable);
> - if (!ret) {
> - if (context == HOTPLUG)
> - ret = register_mem_sect_under_node(mem, nid);
> - }
> -
> - return ret;
> -}
> -
please divide clean-up and logic-change patches into their own..
> /*
> * For now, we have a linear search to go find the appropriate
> * memory_block corresponding to a particular phys_index. If
> @@ -482,12 +479,13 @@
> struct sys_device *sysdev;
> struct memory_block *mem;
> char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
> + int block_id = base_memory_block_id(__section_nr(section));
>
> /*
> * This only works because we know that section == sysdev->id
> * slightly redundant with sysdev_register()
> */
> - sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
> + sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id);
Hmm. Then, the user has to calculate block-id in addtion to section-id.
Can't we use memory block name as memory%d-%d(start-end) ?
>
> kobj = kset_find_obj(&memory_sysdev_class.kset, name);
> if (!kobj)
> @@ -498,19 +496,97 @@
>
> return mem;
> }
> +static int add_mem_block_section(struct memory_block *mem,
> + int section_nr, unsigned long state)
> +{
> + struct memory_block_section *mbs;
> +
> + mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
> + if (!mbs)
> + return -ENOMEM;
> +
> + mbs->phys_index = section_nr;
> + mbs->state = state;
> +
> + list_add(&mbs->next, &mem->sections);
> + return 0;
> +}
> +
> +static int add_memory_block(int nid, struct mem_section *section,
> + unsigned long state, enum mem_add_context context)
> +{
> + struct memory_block *mem;
> + int ret = 0;
> +
> + mem = find_memory_block(section);
I guess you need to add changes to find_memory_block. section-ID != block-ID.
> + if (!mem) {
> + unsigned long start_pfn;
> +
> + mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> + if (!mem)
> + return -ENOMEM;
> +
> + mem->state = state;
> + mutex_init(&mem->state_mutex);
> + start_pfn = section_nr_to_pfn(__section_nr(section));
> + mem->phys_device = arch_get_memory_phys_device(start_pfn);
> + INIT_LIST_HEAD(&mem->sections);
I'm not sure this phys_device is properly set in any arch...but this changes in
granule will not affect ?
> +
> + ret = register_memory(mem, section);
> + if (!ret)
> + ret = mem_create_simple_file(mem, phys_index);
> + if (!ret)
> + ret = mem_create_simple_file(mem, state);
> + if (!ret)
> + ret = mem_create_simple_file(mem, phys_device);
> + if (!ret)
> + ret = mem_create_simple_file(mem, removable);
> + if (!ret) {
> + if (context == HOTPLUG)
> + ret = register_mem_sect_under_node(mem, nid);
> + }
> + } else {
> + kobject_put(&mem->sysdev.kobj);
> + }
> +
> + if (!ret)
> + ret = add_mem_block_section(mem, __section_nr(section), state);
> +
> + return ret;
> +}
>
> int remove_memory_block(unsigned long node_id, struct mem_section *section,
> int phys_device)
> {
> struct memory_block *mem;
> + struct memory_block_section *mbs;
> + struct list_head *pos, *tmp;
> + int section_nr = __section_nr(section);
>
> mem = find_memory_block(section);
ditto.
> - unregister_mem_sect_under_nodes(mem);
> - mem_remove_simple_file(mem, phys_index);
> - mem_remove_simple_file(mem, state);
> - mem_remove_simple_file(mem, phys_device);
> - mem_remove_simple_file(mem, removable);
> - unregister_memory(mem, section);
> + mutex_lock(&mem->state_mutex);
> +
> + /* remove the specified section */
> + list_for_each_safe(pos, tmp, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section, next);
> +
list_for_each_entry_safe ?
> + if (mbs->phys_index == section_nr) {
> + list_del(&mbs->next);
> + kfree(mbs);
> + }
> + }
> +
> + mutex_unlock(&mem->state_mutex);
> +
> + if (list_empty(&mem->sections)) {
> + unregister_mem_sect_under_nodes(mem);
> + mem_remove_simple_file(mem, phys_index);
> + mem_remove_simple_file(mem, state);
> + mem_remove_simple_file(mem, phys_device);
> + mem_remove_simple_file(mem, removable);
> + unregister_memory(mem);
> + kfree(mem);
> + }
>
> return 0;
> }
> @@ -532,6 +608,24 @@
> return remove_memory_block(0, section, 0);
> }
>
> +u32 __weak memory_block_size(void)
> +{
> + return MIN_MEMORY_BLOCK_SIZE;
> +}
> +
> +static u32 get_memory_block_size(void)
> +{
> + u32 blk_sz;
> +
> + blk_sz = memory_block_size();
> +
> + /* Validate blk_sz is a power of 2 and not less than section size */
> + if ((blk_sz & (blk_sz - 1)) || (blk_sz < MIN_MEMORY_BLOCK_SIZE))
> + blk_sz = MIN_MEMORY_BLOCK_SIZE;
> +
> + return blk_sz;
> +}
> +
> /*
> * Initialize the sysfs support for memory devices...
> */
> @@ -540,12 +634,16 @@
> unsigned int i;
> int ret;
> int err;
> + int block_sz;
>
> memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
> ret = sysdev_class_register(&memory_sysdev_class);
> if (ret)
> goto out;
>
> + block_sz = get_memory_block_size();
> + sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
> +
> /*
> * Create entries for memory sections that were found
> * during boot and have been initialized
> Index: linux-2.6/include/linux/memory.h
> ===================================================================
> --- linux-2.6.orig/include/linux/memory.h 2010-07-08 11:27:21.000000000 -0500
> +++ linux-2.6/include/linux/memory.h 2010-07-09 14:22:44.000000000 -0500
> @@ -19,9 +19,15 @@
> #include <linux/node.h>
> #include <linux/compiler.h>
> #include <linux/mutex.h>
> +#include <linux/list.h>
>
> -struct memory_block {
> +struct memory_block_section {
> + unsigned long state;
> unsigned long phys_index;
> + struct list_head next;
> +};
> +
> +struct memory_block {
> unsigned long state;
> /*
> * This serializes all state change requests. It isn't
> @@ -34,6 +40,7 @@
> void *hw; /* optional pointer to fw/hw data */
> int (*phys_callback)(struct memory_block *);
> struct sys_device sysdev;
> + struct list_head sections;
> };
>
> int arch_get_memory_phys_device(unsigned long start_pfn);
> @@ -113,7 +120,7 @@
> extern int remove_memory_block(unsigned long, struct mem_section *, int);
> extern int memory_notify(unsigned long val, void *v);
> extern int memory_isolate_notify(unsigned long val, void *v);
> -extern struct memory_block *find_memory_block(unsigned long);
> +extern struct memory_block *find_memory_block(struct mem_section *);
> extern int memory_is_hidden(struct mem_section *);
> #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT)
> enum mem_add_context { BOOT, HOTPLUG };
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
^ permalink raw reply
* Re: [PATCH 3/7] Update the [register,unregister]_memory routines
From: KAMEZAWA Hiroyuki @ 2010-07-13 6:20 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B384A.4000902@austin.ibm.com>
On Mon, 12 Jul 2010 10:44:10 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:
> This patch moves the register/unregister_memory routines to
> avoid a forward declaration. It also moves the sysfs file
> creation and deletion for each directory into the register/
> unregister routines to avoid duplicating it with these updates.
>
> Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
> ---
> drivers/base/memory.c | 93 +++++++++++++++++++++++++-------------------------
> 1 file changed, 48 insertions(+), 45 deletions(-)
>
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:23:17.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c 2010-07-09 14:23:20.000000000 -0500
> @@ -87,31 +87,6 @@
> EXPORT_SYMBOL(unregister_memory_isolate_notifier);
>
> /*
> - * register_memory - Setup a sysfs device for a memory block
> - */
> -static
> -int register_memory(struct memory_block *memory, struct mem_section *section)
> -{
> - int error;
> -
> - memory->sysdev.cls = &memory_sysdev_class;
> - memory->sysdev.id = __section_nr(section);
> -
> - error = sysdev_register(&memory->sysdev);
> - return error;
> -}
> -
> -static void
> -unregister_memory(struct memory_block *memory)
> -{
> - BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
> -
> - /* drop the ref. we got in remove_memory_block() */
> - kobject_put(&memory->sysdev.kobj);
> - sysdev_unregister(&memory->sysdev);
> -}
> -
> -/*
> * use this as the physical section index that this memsection
> * uses.
> */
> @@ -346,6 +321,53 @@
> sysdev_remove_file(&mem->sysdev, &attr_##attr_name)
>
> /*
> + * register_memory - Setup a sysfs device for a memory block
> + */
> +static
> +int register_memory(struct memory_block *memory, struct mem_section *section,
> + int nid, enum mem_add_context context)
> +{
> + int ret;
> +
> + memory->sysdev.cls = &memory_sysdev_class;
> + memory->sysdev.id = __section_nr(section);
> +
Why not block-ID but section-ID ?
-Kame
^ permalink raw reply
* Re: [PATCH 4/7] Allow sysfs memory directories to be split
From: KAMEZAWA Hiroyuki @ 2010-07-13 6:28 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B3895.3040209@austin.ibm.com>
On Mon, 12 Jul 2010 10:45:25 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:
> This patch introduces the new 'split' file in each memory sysfs
> directory and the associated routines needed to handle splitting
> a directory.
>
> Signed-off-by; Nathan Fontenot <nfont@austin.ibm.com>
> ---
pleae check diff option...
> drivers/base/memory.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 98 insertions(+), 1 deletion(-)
>
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:23:20.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c 2010-07-09 14:38:09.000000000 -0500
> @@ -32,6 +32,9 @@
>
> static int sections_per_block;
>
> +static int register_memory(struct memory_block *, struct mem_section *,
> + int, enum mem_add_context);
> +
> static inline int base_memory_block_id(int section_nr)
> {
> return (section_nr / sections_per_block) * sections_per_block;
> @@ -309,11 +312,100 @@
> return sprintf(buf, "%d\n", mem->phys_device);
> }
>
> +static void update_memory_block_phys_indexes(struct memory_block *mem)
> +{
> + struct list_head *pos;
> + struct memory_block_section *mbs;
> + unsigned long min_index = 0xffffffff;
> + unsigned long max_index = 0;
> +
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section, next);
> +
> + if (mbs->phys_index < min_index)
> + min_index = mbs->phys_index;
> +
> + if (mbs->phys_index > max_index)
> + max_index = mbs->phys_index;
> + }
> +
> + mem->start_phys_index = min_index;
> + mem->end_phys_index = max_index;
> +}
> +
> +static ssize_t
> +store_mem_split_block(struct sys_device *dev, struct sysdev_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct memory_block *mem, *new_mem_blk;
> + struct memory_block_section *mbs;
> + struct list_head *pos, *tmp;
> + struct mem_section *section;
> + int min_scn_nr = 0;
> + int max_scn_nr = 0;
> + int total_scns = 0;
> + int new_blk_min, new_blk_total;
> + int ret = -EINVAL;
> +
> + mem = container_of(dev, struct memory_block, sysdev);
> +
> + if (list_is_singular(&mem->sections))
> + return -EINVAL;
What this means ?
> +
> + mutex_lock(&mem->state_mutex);
> +
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section, next);
> +
> + total_scns++;
> +
> + if (min_scn_nr > mbs->phys_index)
> + min_scn_nr = mbs->phys_index;
> +
> + if (max_scn_nr < mbs->phys_index)
> + max_scn_nr = mbs->phys_index;
> + }
> +
> + new_mem_blk = kzalloc(sizeof(*new_mem_blk), GFP_KERNEL);
> + if (!new_mem_blk)
> + return -ENOMEM;
> +
> + mutex_init(&new_mem_blk->state_mutex);
> + INIT_LIST_HEAD(&new_mem_blk->sections);
> + new_mem_blk->state = mem->state;
> +
> + mutex_lock(&new_mem_blk->state_mutex);
> +
> + new_blk_total = total_scns / 2;
> + new_blk_min = max_scn_nr - new_blk_total + 1;
> +
> + section = __nr_to_section(new_blk_min);
> + ret = register_memory(new_mem_blk, section, 0, HOTPLUG);
> +
'nid' is always 0 ?
And for what purpose this interface is ? Does this split memory block into 2 pieces
of the same size ?? sounds __very__ strange interface to me.
If this is necessary, I hope move the whole things to configfs rather than
something tricky.
Bye.
-Kame
^ permalink raw reply
* [PATCH] powerpc: reduce the size of the defconfigs
From: Stephen Rothwell @ 2010-07-13 7:09 UTC (permalink / raw)
To: Linus; +Cc: Olof Johansson, ppc-dev, LKML, "Uwe Kleine-König"
[-- Attachment #1: Type: text/plain, Size: 7899 bytes --]
This uses Uwe's script (modified by Olof Johansson to speed it up
somewhat) to reduce the size of all the powerpc defconfigs. The resulting
files have been verified to produce the same .config files by generating
them before and after this patch and comparing the results.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
arch/powerpc/configs/40x/acadia_defconfig | 1002 ----------
arch/powerpc/configs/40x/ep405_defconfig | 1211 ------------
arch/powerpc/configs/40x/hcu4_defconfig | 1064 ----------
arch/powerpc/configs/40x/kilauea_defconfig | 1197 -----------
arch/powerpc/configs/40x/makalu_defconfig | 1005 ----------
arch/powerpc/configs/40x/virtex_defconfig | 1105 -----------
arch/powerpc/configs/40x/walnut_defconfig | 1089 ----------
arch/powerpc/configs/44x/arches_defconfig | 1059 ----------
arch/powerpc/configs/44x/bamboo_defconfig | 1020 ----------
arch/powerpc/configs/44x/canyonlands_defconfig | 1263 ------------
arch/powerpc/configs/44x/ebony_defconfig | 1103 -----------
arch/powerpc/configs/44x/eiger_defconfig | 1177 -----------
arch/powerpc/configs/44x/icon_defconfig | 1335 -------------
arch/powerpc/configs/44x/iss476-smp_defconfig | 938 ---------
arch/powerpc/configs/44x/katmai_defconfig | 1088 ----------
arch/powerpc/configs/44x/rainier_defconfig | 1090 ----------
arch/powerpc/configs/44x/redwood_defconfig | 1168 -----------
arch/powerpc/configs/44x/sam440ep_defconfig | 1319 -------------
arch/powerpc/configs/44x/sequoia_defconfig | 1111 -----------
arch/powerpc/configs/44x/taishan_defconfig | 1097 -----------
arch/powerpc/configs/44x/virtex5_defconfig | 1111 -----------
arch/powerpc/configs/44x/warp_defconfig | 1389 -------------
arch/powerpc/configs/52xx/cm5200_defconfig | 1232 ------------
arch/powerpc/configs/52xx/lite5200b_defconfig | 1257 ------------
arch/powerpc/configs/52xx/motionpro_defconfig | 1265 ------------
arch/powerpc/configs/52xx/pcm030_defconfig | 1219 ------------
arch/powerpc/configs/52xx/tqm5200_defconfig | 1367 -------------
arch/powerpc/configs/83xx/asp8347_defconfig | 1432 --------------
arch/powerpc/configs/83xx/kmeter1_defconfig | 927 ---------
arch/powerpc/configs/83xx/mpc8313_rdb_defconfig | 1729 ----------------
arch/powerpc/configs/83xx/mpc8315_rdb_defconfig | 1798 -----------------
arch/powerpc/configs/83xx/mpc832x_mds_defconfig | 1328 -------------
arch/powerpc/configs/83xx/mpc832x_rdb_defconfig | 1475 --------------
arch/powerpc/configs/83xx/mpc834x_itx_defconfig | 1567 ---------------
arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig | 1453 --------------
arch/powerpc/configs/83xx/mpc834x_mds_defconfig | 1262 ------------
arch/powerpc/configs/83xx/mpc836x_mds_defconfig | 1403 -------------
arch/powerpc/configs/83xx/mpc836x_rdk_defconfig | 1304 ------------
arch/powerpc/configs/83xx/mpc837x_mds_defconfig | 1333 -------------
arch/powerpc/configs/83xx/mpc837x_rdb_defconfig | 1471 --------------
arch/powerpc/configs/83xx/sbc834x_defconfig | 1398 -------------
arch/powerpc/configs/85xx/ksi8560_defconfig | 1119 -----------
arch/powerpc/configs/85xx/mpc8540_ads_defconfig | 992 ----------
arch/powerpc/configs/85xx/mpc8560_ads_defconfig | 1139 -----------
arch/powerpc/configs/85xx/mpc85xx_cds_defconfig | 1155 -----------
arch/powerpc/configs/85xx/sbc8548_defconfig | 1001 ----------
arch/powerpc/configs/85xx/sbc8560_defconfig | 1029 ----------
arch/powerpc/configs/85xx/socrates_defconfig | 1645 ----------------
arch/powerpc/configs/85xx/stx_gp3_defconfig | 1528 --------------
arch/powerpc/configs/85xx/tqm8540_defconfig | 1318 -------------
arch/powerpc/configs/85xx/tqm8541_defconfig | 1364 -------------
arch/powerpc/configs/85xx/tqm8548_defconfig | 1355 +-------------
arch/powerpc/configs/85xx/tqm8555_defconfig | 1364 -------------
arch/powerpc/configs/85xx/tqm8560_defconfig | 1364 -------------
arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 1786 -----------------
arch/powerpc/configs/86xx/gef_ppc9a_defconfig | 1736 ----------------
arch/powerpc/configs/86xx/gef_sbc310_defconfig | 1625 ---------------
arch/powerpc/configs/86xx/gef_sbc610_defconfig | 1819 -----------------
arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig | 1634 ---------------
arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig | 1640 ----------------
arch/powerpc/configs/86xx/sbc8641d_defconfig | 1432 --------------
arch/powerpc/configs/adder875_defconfig | 913 ---------
arch/powerpc/configs/amigaone_defconfig | 1490 --------------
arch/powerpc/configs/c2k_defconfig | 1608 ---------------
arch/powerpc/configs/cell_defconfig | 1314 +------------
arch/powerpc/configs/celleb_defconfig | 1195 +-----------
arch/powerpc/configs/chrp32_defconfig | 1469 --------------
arch/powerpc/configs/ep8248e_defconfig | 1118 -----------
arch/powerpc/configs/ep88xc_defconfig | 858 --------
arch/powerpc/configs/g5_defconfig | 1549 ---------------
arch/powerpc/configs/gamecube_defconfig | 945 ---------
arch/powerpc/configs/holly_defconfig | 879 ---------
arch/powerpc/configs/iseries_defconfig | 1056 ----------
arch/powerpc/configs/linkstation_defconfig | 1782 -----------------
arch/powerpc/configs/maple_defconfig | 1371 -------------
arch/powerpc/configs/mgcoge_defconfig | 1158 -----------
arch/powerpc/configs/mgsuvd_defconfig | 937 ---------
arch/powerpc/configs/mpc512x_defconfig | 1559 ---------------
arch/powerpc/configs/mpc5200_defconfig | 1849 -----------------
arch/powerpc/configs/mpc7448_hpc2_defconfig | 1194 -----------
arch/powerpc/configs/mpc8272_ads_defconfig | 1185 -----------
arch/powerpc/configs/mpc83xx_defconfig | 1662 ----------------
arch/powerpc/configs/mpc85xx_defconfig | 1704 ----------------
arch/powerpc/configs/mpc85xx_smp_defconfig | 1708 ----------------
arch/powerpc/configs/mpc866_ads_defconfig | 950 ---------
arch/powerpc/configs/mpc86xx_defconfig | 1680 ----------------
arch/powerpc/configs/mpc885_ads_defconfig | 863 --------
arch/powerpc/configs/pasemi_defconfig | 1931 +------------------
arch/powerpc/configs/pmac32_defconfig | 1884 +------------------
arch/powerpc/configs/ppc40x_defconfig | 1332 -------------
arch/powerpc/configs/ppc44x_defconfig | 1471 --------------
arch/powerpc/configs/ppc64_defconfig | 1724 +----------------
arch/powerpc/configs/ppc64e_defconfig | 1803 -----------------
arch/powerpc/configs/ppc6xx_defconfig | 2186 +--------------------
arch/powerpc/configs/pq2fads_defconfig | 1304 ------------
arch/powerpc/configs/prpmc2800_defconfig | 1674 ----------------
arch/powerpc/configs/ps3_defconfig | 1334 -------------
arch/powerpc/configs/pseries_defconfig | 1454 +--------------
arch/powerpc/configs/storcenter_defconfig | 1305 ------------
arch/powerpc/configs/wii_defconfig | 1262 ------------
100 files changed, 51 insertions(+), 133815 deletions(-)
The patch is available at
http://ozlabs.org/~sfr/powerpc-reduce-the-size-of-the-defconfigs.patch.bz2 .
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply
* Re: [PATCH] powerpc: reduce the size of the defconfigs
From: Uwe Kleine-König @ 2010-07-13 7:31 UTC (permalink / raw)
To: Stephen Rothwell; +Cc: Olof Johansson, ppc-dev, Linus, LKML
In-Reply-To: <20100713170945.26d95641.sfr@canb.auug.org.au>
On Tue, Jul 13, 2010 at 05:09:45PM +1000, Stephen Rothwell wrote:
> This uses Uwe's script (modified by Olof Johansson to speed it up
> somewhat) to reduce the size of all the powerpc defconfigs. The resulting
IMHO we should add the script to the source, too. And if it's only for
me to see Olof's optimisation. :-)
> files have been verified to produce the same .config files by generating
> them before and after this patch and comparing the results.
>
> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
> ---
> 100 files changed, 51 insertions(+), 133815 deletions(-)
Nice.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* optimized script [Was: ARM defconfig files]
From: Uwe Kleine-König @ 2010-07-13 8:07 UTC (permalink / raw)
To: Linus Torvalds
Cc: Stephen Rothwell, Daniel Walker, Russell King - ARM Linux,
Nicolas Pitre, Kevin Hilman, linux-arm-msm,
Linux Kernel Mailing List, Eric Miao, Olof Johansson, linux-omap,
linuxppc-dev, linux-arm-kernel
In-Reply-To: <20100713070741.GB26442@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 1548 bytes --]
Hello,
On Tue, Jul 13, 2010 at 09:07:41AM +0200, Uwe Kleine-König wrote:
> Hi
>
> On Mon, Jul 12, 2010 at 01:50:47PM -0600, Grant Likely wrote:
> > On Mon, Jul 12, 2010 at 1:34 PM, Linus Torvalds
> > <torvalds@linux-foundation.org> wrote:
> > > On Mon, Jul 12, 2010 at 12:17 PM, Nicolas Pitre <nico@fluxnic.net> wrote:
> > >> I think Uwe could provide his script and add it to the kernel tree.
> > >> Then all architectures could benefit from it. Having the defconfig
> > >> files contain only those options which are different from the defaults
> > >> is certainly more readable, even on x86.
> > >
> > > Quite possible. But maintainers would need to be on the lookout of
> > > people actually using the script, and refusing to apply patches that
> > > re-introduce the whole big thing.
> >
> > I can (partially) speak for powerpc. If ARM uses this approach, then
> > I think we can do the same. After the defconfigs are trimmed, I
> > certainly won't pick up any more full defconfigs.
> I just restarted my script on the powerpc defconfigs basing on rc5, I
> assume they complete in a few days time.
So Stephen was faster than me. I don't know yet how he optimised my
script, meanwhile I put some efforts into it, too by just checking lines
that match "^(# )?CONFIG_".
Find it attached.
I will start to reduce the remaining configs (i.e. all but arm and
powerpc).
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: reduce_defconfig --]
[-- Type: text/plain, Size: 1899 bytes --]
#! /usr/bin/env python
# vim: set fileencoding=utf-8 :
# Copyright (C) 2010 by Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
import getopt
import re
import os
import subprocess
import sys
# This prevents including a timestamp in the .config which makes comparing a
# bit easier.
os.environ['KCONFIG_NOTIMESTAMP'] = 'Yes, please'
re_interesting = re.compile(r'^(# )?CONFIG_')
opts, args = getopt.getopt(sys.argv[1:], '', ['arch=', 'src='])
src = ''
arch = 'arm'
for o, a in opts:
if o == '--arch':
arch = a
elif o == '--src':
src = a
configdir = os.path.join(src, 'arch', arch, 'configs')
def all_defconfigs():
lc = len(configdir)
for root, dirs, files in os.walk(configdir):
root = root[lc + 1:]
for f in filter(lambda s: s.endswith('_defconfig'), files):
yield os.path.join(root, f)
if not args:
args = all_defconfigs()
for target in args:
defconfig_src = os.path.join(configdir, target)
subprocess.check_call(['make', '-s', 'ARCH=%s' % arch, target])
origconfig = list(open('.config'))
config = list(origconfig)
config_size = os.stat('.config').st_size
i = 0
while i < len(config):
mo = re_interesting.match(config[i])
if mo:
defconfig = open(defconfig_src, 'w')
defconfig.writelines(config[:i])
defconfig.writelines(config[i + 1:])
defconfig.close()
subprocess.check_call(['make', '-s', 'ARCH=%s' % arch, target])
if os.stat('.config').st_size == config_size and list(open('.config')) == origconfig:
print '-%s' % config[i][:-1]
del config[i]
else:
print ' %s' % config[i][:-1]
i += 1
else:
del config[i]
defconfig = open(defconfig_src, 'w')
defconfig.writelines(config)
defconfig.close()
^ permalink raw reply
* Re: section .data..init_task
From: Sam Ravnborg @ 2010-07-13 8:54 UTC (permalink / raw)
To: Sean MacLennan; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100712203435.08a3e90f@lappy.seanm.ca>
On Mon, Jul 12, 2010 at 08:34:35PM -0400, Sean MacLennan wrote:
> On Mon, 28 Jun 2010 00:59:00 -0400
> Sean MacLennan <smaclennan@pikatech.com> wrote:
>
> > Anybody else seeing these messages?
> >
> > ppc_4xxFP-ld: .tmp_vmlinux1: section .data..init_task lma 0xc0374000
> > overlaps previous sections ppc_4xxFP-ld: .tmp_vmlinux2:
> > section .data..init_task lma 0xc03a2000 overlaps previous sections
> > ppc_4xxFP-ld: vmlinux: section .data..init_task lma 0xc03a2000
> > overlaps previous sections
> >
> > Or does anybody know what they mean? They started showing up in
> > 2.6.35.
> >
> > Very easy to reproduce, so don't hesitate to ask for more info.
>
> I had a bit of time, so I tracked this down. This patch seems to be
> the culprit: http://lkml.org/lkml/2010/2/19/366
>
> Specifically, this code:
>
> /* The initial task and kernel stack */
> - .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
> - INIT_TASK_DATA(THREAD_SIZE)
> - }
> + INIT_TASK_DATA_SECTION(THREAD_SIZE)
>
> If I change it back to:
>
> /* The initial task and kernel stack */
> .data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) {
> INIT_TASK_DATA(THREAD_SIZE)
> }
>
> not only do the warnings go away, but the kernel now boots again!
It looks like a missing AT() in the output section.
The following patch should also fix it.
Please test and let us know.
Thanks,
Sam
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 48c5299..3c4bf03 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -435,7 +435,7 @@
*/
#define INIT_TASK_DATA_SECTION(align) \
. = ALIGN(align); \
- .data..init_task : { \
+ .data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) { \
INIT_TASK_DATA(align) \
}
^ permalink raw reply related
* [
From: Sam Ravnborg @ 2010-07-13 9:50 UTC (permalink / raw)
To: Sean MacLennan, Benjamin Herrenschmidt
Cc: Denys Vlasenko, linuxppc-dev, Michal Marek, Tim Abbott
In-Reply-To: <20100712203435.08a3e90f@lappy.seanm.ca>
>From 851e645a7eee68380caaf026eb6d3be118876370 Mon Sep 17 00:00:00 2001
From: Sam Ravnborg <sam@ravnborg.org>
Date: Tue, 13 Jul 2010 11:39:42 +0200
Subject: [PATCH] vmlinux.lds: fix .data..init_task output section (fix popwerpc boot)
The .data..init_task output section was missing
a load offset causing a popwerpc target to fail to boot.
Sean MacLennan tracked it down to the definition of
INIT_TASK_DATA_SECTION().
There are only two users of INIT_TASK_DATA_SECTION()
in the kernel today: cris and popwerpc.
cris do not support relocatable kernels and is thus not
impacted by this change.
Fix INIT_TASK_DATA_SECTION() to specify load offset like
all other output sections.
Reported-by: Sean MacLennan <smaclennan@pikatech.com>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
---
On the assumption that Sean reports that it fixes
the warnings/boot issue here is a real patch.
Ben - will you take it via the popwerpc tree
or shall I ask Michal to take it via kbuild?
Sam
include/asm-generic/vmlinux.lds.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 48c5299..cdfff74 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -435,7 +435,7 @@
*/
#define INIT_TASK_DATA_SECTION(align) \
. = ALIGN(align); \
- .data..init_task : { \
+ .data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) { \
INIT_TASK_DATA(align) \
}
--
1.6.0.6
^ permalink raw reply related
* Re: [PATCH] powerpc: reduce the size of the defconfigs
From: Martyn Welch @ 2010-07-13 10:35 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Olof Johansson, Stephen Rothwell, Linus, ppc-dev, LKML
In-Reply-To: <20100713073137.GC26442@pengutronix.de>
Uwe Kleine-König wrote:
> On Tue, Jul 13, 2010 at 05:09:45PM +1000, Stephen Rothwell wrote:
>
>> This uses Uwe's script (modified by Olof Johansson to speed it up
>> somewhat) to reduce the size of all the powerpc defconfigs. The resulting
>>
> IMHO we should add the script to the source, too. And if it's only for
> me to see Olof's optimisation. :-)
>
>
As the person who introduced the gef_* PPC defconfigs to the kernel (I
understood this to be best practice), I'm happy to go along with
whatever - as long as I can build a bootable kernel from the mainline
kernel. I think I understand Linus' wish not to see all the defconfig
churn (I get fed up diffing defconfigs, just to read through loads of
additions and removals of undef'ed entires from just the few configs I'm
interested in).
I assume I'm not alone among those attempting to add board support to
the mainline kernel in not being able to keep up with the LKML mailing
list and therefore have missed most of this discussion (think I've
managed to read some of it now). I would very much appreciate some
documentation / guidance on how to use this script - preferably provided
as a comment in the head of the script or/and some pointers in
"Documentation/".
Martyn
>> files have been verified to produce the same .config files by generating
>> them before and after this patch and comparing the results.
>>
>> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
>> ---
>> 100 files changed, 51 insertions(+), 133815 deletions(-)
>>
> Nice.
>
> Best regards
> Uwe
>
>
--
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms | Wales (3828642) at 100
T +44(0)127322748 | Barbirolli Square, Manchester,
E martyn.welch@ge.com | M2 3AB VAT:GB 927559189
^ permalink raw reply
* High process latencies due to MPC5200 FEC hard- soft-irq processing
From: Wolfgang Grandegger @ 2010-07-13 13:29 UTC (permalink / raw)
To: Netdev; +Cc: linuxppc-dev@ozlabs.org, LKML
Hello,
we realized, that multiple ping floods (ping -f) can cause very large
high-priority process latencies (up to a many seconds) on a MPC5200
PowerPC system with FEC NAPI support. The latencies are measured with
# cyclictest -p 80 -n
The problem is that processing of the ICMP pakets in the Hard-Irq and
Soft-IRQ context can last for a long time without returning to the
scheduler. Reducing MAX_SOFTIRQ_RESTART from 10 to 2 helps - the latency
goes down to 35 ms with 2 "ping -f" - but it's not a configurable
parameter, even if it somehow depends on the CPU power. And using the
-rt patches seems overkill to me. Any other ideas or comments on how to
get rid of such high process latencies?
Wolfgang.
^ permalink raw reply
* Re: [PATCH 1/7] Split the memory_block structure
From: Brian King @ 2010-07-13 14:00 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B37CE.50802@austin.ibm.com>
On 07/12/2010 10:42 AM, Nathan Fontenot wrote:
> @@ -123,13 +130,20 @@
> static ssize_t show_mem_removable(struct sys_device *dev,
> struct sysdev_attribute *attr, char *buf)
> {
> - unsigned long start_pfn;
> - int ret;
> - struct memory_block *mem =
> - container_of(dev, struct memory_block, sysdev);
> + struct list_head *pos, *tmp;
> + struct memory_block *mem;
> + int ret = 1;
> +
> + mem = container_of(dev, struct memory_block, sysdev);
> + list_for_each_safe(pos, tmp, &mem->sections) {
> + struct memory_block_section *mbs;
> + unsigned long start_pfn;
> +
> + mbs = list_entry(pos, struct memory_block_section, next);
> + start_pfn = section_nr_to_pfn(mbs->phys_index);
> + ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> + }
I don't see you deleting anyting from the list in this loop. Why do you need
to use list_for_each_safe? That won't protect you if someone else is messing
with the list.
>
> - start_pfn = section_nr_to_pfn(mem->phys_index);
> - ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> return sprintf(buf, "%d\n", ret);
> }
>
> @@ -238,19 +252,40 @@
> static int memory_block_change_state(struct memory_block *mem,
> unsigned long to_state, unsigned long from_state_req)
> {
> + struct memory_block_section *mbs;
> + struct list_head *pos;
> int ret = 0;
> +
> mutex_lock(&mem->state_mutex);
>
> - if (mem->state != from_state_req) {
> - ret = -EINVAL;
> - goto out;
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section, next);
> +
> + if (mbs->state != from_state_req)
> + continue;
> +
> + ret = memory_block_action(mbs, to_state);
> + if (ret)
> + break;
> + }
Would it be better here to loop through all the sections and ensure they
are in the proper state first before starting to change the state of any
of them? Then you could easily return -EINVAL if one or more is in
the incorrect state and wouldn't need to the code below.
> + if (ret) {
> + list_for_each(pos, &mem->sections) {
> + mbs = list_entry(pos, struct memory_block_section,
> + next);
> +
> + if (mbs->state == from_state_req)
> + continue;
> +
> + if (memory_block_action(mbs, to_state))
> + printk(KERN_ERR "Could not re-enable memory "
> + "section %lx\n", mbs->phys_index);
> + }
> }
>
> - ret = memory_block_action(mem, to_state);
> if (!ret)
> mem->state = to_state;
>
> -out:
> mutex_unlock(&mem->state_mutex);
> return ret;
> }
> @@ -498,19 +496,97 @@
>
> return mem;
> }
> +static int add_mem_block_section(struct memory_block *mem,
> + int section_nr, unsigned long state)
> +{
> + struct memory_block_section *mbs;
> +
> + mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
> + if (!mbs)
> + return -ENOMEM;
> +
> + mbs->phys_index = section_nr;
> + mbs->state = state;
> +
> + list_add(&mbs->next, &mem->sections);
I don't think there is sufficient protection for this list. Don't we
need to be holding a lock of some sort when adding/deleting/iterating
through this list?
> + return 0;
> +}
--
Brian King
Linux on Power Virtualization
IBM Linux Technology Center
^ permalink raw reply
* 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: jjDaNiMoTh @ 2010-07-13 14:03 UTC (permalink / raw)
To: xorg-driver-ati; +Cc: linuxppc-dev
Hello to all.
Sorry if these aren't right place, please point me to the right
direction if you can :)
When trying new 2.6.35-rc4, our kernel team (ArchLinuxPPC) has tried
to setup KMS acceleration for radeon based machine.
We have removed radeonfb, and all others framebuffer driver, and added
fbcon and KMS enabled by default for radeon driver.
With a clean start, the screen freeze, when the control pass from
yaboot to kernel.
If we start with video=fbcon (or video=radeondrmfb), we could reach
the loading modules point, but after the loading of radeon, the screen
goes black, without any log information.
Loading kernel with video=fbcon radeon.modeset=0 allow us to reach the
end of init stage, and we could load X.org. In this case, acceleration
is disabled.
If we log out and do the following command:
modprobe -r radeon drm
modprobe drm debug=1
modprobe radeon modeset=1
The screen goes black, but at next boot we have found the logs. Any hint?
System information:
Xorg server: 1.8.1
xf86-video-ati 6.13.0
ati-dri 7.8.1
mesa 7.8.1
linux 2.6.35-rc4-00131-ge467e10
There are logs (most relevant part):
[...]
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
[....]
Jul 13 15:31:28 jim kernel: [drm] Module unloaded
Jul 13 15:31:39 jim kernel: [drm] Initialized drm 1.1.0 20060810
Jul 13 15:31:39 jim kernel: [drm] radeon kernel modesetting enabled.
Jul 13 15:31:39 jim kernel: [drm] initializing kernel modesetting
(RV350 0x1002:0x4E50).
Jul 13 15:31:39 jim kernel: [drm] register mmio base: 0xB0000000
Jul 13 15:31:39 jim kernel: [drm] register mmio size: 65536
Jul 13 15:31:39 jim kernel: [drm] Using generic clock info
Jul 13 15:31:39 jim kernel: agpgart-uninorth 0000:00:0b.0: putting AGP
V2 device into 4x mode
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: putting AGP V2 device
into 4x mode
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: GTT: 256M 0x00000000
- 0x0FFFFFFF
Jul 13 15:31:39 jim kernel: [drm] Generation 2 PCI interface, using
max accessible memory
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: VRAM: 64M 0xB8000000
- 0xBBFFFFFF (64M used)
Jul 13 15:31:39 jim kernel: [drm] radeon: irq initialized.
Jul 13 15:31:39 jim kernel: [drm] Detected VRAM RAM=64M, BAR=128M
Jul 13 15:31:39 jim kernel: [drm] RAM width 128bits DDR
Jul 13 15:31:39 jim kernel: [TTM] Zone kernel: Available graphics
memory: 384990 kiB.
Jul 13 15:31:39 jim kernel: [TTM] Zone highmem: Available graphics
memory: 516062 kiB.
Jul 13 15:31:39 jim kernel: [TTM] Initializing pool allocator.
Jul 13 15:31:39 jim kernel: [drm] radeon: 64M of VRAM memory ready
Jul 13 15:31:39 jim kernel: [drm] radeon: 256M of GTT memory ready.
Jul 13 15:31:39 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initialized.
Jul 13 15:31:39 jim kernel: [drm] Loading R300 Microcode
Jul 13 15:31:39 jim kernel: [drm] radeon: ring at 0x0000000000000000
Jul 13 15:31:39 jim kernel: [drm] ring test succeeded in 3 usecs
Jul 13 15:31:39 jim kernel: [drm] radeon: ib pool ready.
Jul 13 15:31:40 jim kernel: GPU lockup (waiting for 0x00000001 last
fence id 0x00000000)
Jul 13 15:31:40 jim kernel: NIP: f260fda4 LR: f260fda4 CTR: 00000001
Jul 13 15:31:40 jim kernel: REGS: ef3a3b90 TRAP: 0700 Not tainted
(2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:40 jim kernel: MSR: 00029032 <EE,ME,CE,IR,DR> CR:
22822484 XER: 20000000
Jul 13 15:31:40 jim kernel: TASK = eedb9ac0[2066] 'modprobe' THREAD: ef3a2000
Jul 13 15:31:40 jim kernel: GPR00: f260fda4 ef3a3c40 eedb9ac0 00000040
416d5d8c ffffffff c04db984 416d5d09
Jul 13 15:31:40 jim kernel: GPR08: 416d5d8c 00000001 00000000 0000000a
22822482 100238a8 00000000 00000000
Jul 13 15:31:40 jim kernel: GPR16: 00000000 0000007d c04a0000 f26a1d54
00000001 00000000 00000000 ef3a2000
Jul 13 15:31:40 jim kernel: GPR24: c005f328 ef3a3c54 00000000 eed386cc
ef3a3c48 00000000 eed38000 ef085d40
Jul 13 15:31:40 jim kernel: NIP [f260fda4]
radeon_fence_wait+0x28c/0x2f4 [radeon]
Jul 13 15:31:40 jim kernel: LR [f260fda4] radeon_fence_wait+0x28c/0x2f4 [radeon]
Jul 13 15:31:40 jim kernel: Call Trace:
Jul 13 15:31:40 jim kernel: [ef3a3c40] [f260fda4]
radeon_fence_wait+0x28c/0x2f4 [radeon] (unreliable)
Jul 13 15:31:40 jim kernel: [ef3a3cb0] [f2638234]
r100_ib_test+0x158/0x280 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3ce0] [f26383a4]
r100_ib_init+0x28/0xc8 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3cf0] [f263f65c]
r300_startup+0xd4/0x1e4 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d00] [f263fb3c] r300_init+0x150/0x334 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d10] [f25fc514]
radeon_device_init+0x2b0/0x418 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d30] [f25fdc0c]
radeon_driver_load_kms+0xa4/0x1f4 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d60] [f2444c4c] drm_get_dev+0x284/0x43c [drm]
Jul 13 15:31:40 jim kernel: [ef3a3d90] [f268d75c]
radeon_pci_probe+0x18/0x28 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3da0] [c01f6f20] pci_device_probe+0x80/0xa4
Jul 13 15:31:40 jim kernel: [ef3a3dc0] [c025cf2c] driver_probe_device+0xc0/0x208
Jul 13 15:31:40 jim kernel: [ef3a3de0] [c025d130] __driver_attach+0xbc/0xc0
Jul 13 15:31:40 jim kernel: [ef3a3e00] [c025bd28] bus_for_each_dev+0x64/0xa0
Jul 13 15:31:40 jim kernel: [ef3a3e30] [c025cb58] driver_attach+0x24/0x34
Jul 13 15:31:40 jim kernel: [ef3a3e40] [c025c618] bus_add_driver+0xd8/0x308
Jul 13 15:31:40 jim kernel: [ef3a3e70] [c025d418] driver_register+0x88/0x154
Jul 13 15:31:40 jim kernel: [ef3a3e90] [c01f7200]
__pci_register_driver+0x4c/0xdc
Jul 13 15:31:40 jim kernel: [ef3a3eb0] [f243ee50] drm_init+0x120/0x134 [drm]
Jul 13 15:31:40 jim kernel: [ef3a3ed0] [f26c00e4]
radeon_init+0xe4/0x128 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3ef0] [c0003ff4] do_one_initcall+0x3c/0x1d8
Jul 13 15:31:40 jim kernel: [ef3a3f20] [c007b434] sys_init_module+0xdc/0x1e0
Jul 13 15:31:40 jim kernel: [ef3a3f40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:40 jim kernel: --- Exception: c01 at 0xff62b58
Jul 13 15:31:40 jim kernel: LR = 0x10002c7c
Jul 13 15:31:40 jim kernel: Instruction dump:
Jul 13 15:31:40 jim kernel: 4bffffc4 813e0a18 7fc3f378 80090014
7c0903a6 4e800421 2f830000 419eff78
Jul 13 15:31:40 jim kernel: 809f0010 7e639b78 7ec5b378 4807dc59
<0fe00000> 9a9e16a8 7fc3f378 4bfecd41
Jul 13 15:31:40 jim kernel: radeon 0000:00:10.0: GPU reset succeed
Jul 13 15:31:40 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initialized.
Jul 13 15:31:40 jim kernel: [drm] radeon: ring at 0x0000000000000000
Jul 13 15:31:41 jim kernel: Oops: Kernel access of bad area, sig: 11 [#1]
Jul 13 15:31:41 jim kernel: PREEMPT PowerMac
Jul 13 15:31:41 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:41 jim kernel: NIP: f2482248 LR: f25fcb88 CTR: 00000000
Jul 13 15:31:41 jim kernel: REGS: ef3a3b50 TRAP: 0300 Tainted: G
W (2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:41 jim kernel: MSR: 00009032 <EE,ME,IR,DR> CR: 22822484
XER: 20000000
Jul 13 15:31:41 jim kernel: DAR: 00000000, DSISR: 40000000
Jul 13 15:31:41 jim kernel: TASK = eedb9ac0[2066] 'modprobe' THREAD: ef3a2000
Jul 13 15:31:41 jim kernel: GPR00: f25fcb88 ef3a3c00 eedb9ac0 ef2c8c00
416d67bb ffffffff c04db97e 416d66f1
Jul 13 15:31:41 jim kernel: GPR08: 416d67bb 00000030 f28e002c eed39670
22822482 100238a8 00000000 00000000
Jul 13 15:31:41 jim kernel: GPR16: 00000000 0000007d c04a0000 f26a1d54
00000001 00000000 00000000 ef3a2000
Jul 13 15:31:41 jim kernel: GPR24: c005f328 ef3a3c54 f2484054 f2483878
ef2c8c00 ef2c8ea4 ef2c8e98 fffffffc
Jul 13 15:31:41 jim kernel: NIP [f2482248]
drm_helper_resume_force_mode+0x38/0x16c [drm_kms_helper]
Jul 13 15:31:41 jim kernel: LR [f25fcb88] radeon_gpu_reset+0x98/0x104 [radeon]
Jul 13 15:31:41 jim kernel: Call Trace:
Jul 13 15:31:41 jim kernel: [ef3a3c00] [ffffffea] 0xffffffea (unreliable)
Jul 13 15:31:41 jim kernel: [ef3a3c30] [f25fcb88]
radeon_gpu_reset+0x98/0x104 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3c40] [f260fdb4]
radeon_fence_wait+0x29c/0x2f4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3cb0] [f2638234]
r100_ib_test+0x158/0x280 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3ce0] [f26383a4]
r100_ib_init+0x28/0xc8 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3cf0] [f263f65c]
r300_startup+0xd4/0x1e4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d00] [f263fb3c] r300_init+0x150/0x334 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d10] [f25fc514]
radeon_device_init+0x2b0/0x418 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d30] [f25fdc0c]
radeon_driver_load_kms+0xa4/0x1f4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d60] [f2444c4c] drm_get_dev+0x284/0x43c [drm]
Jul 13 15:31:41 jim kernel: [ef3a3d90] [f268d75c]
radeon_pci_probe+0x18/0x28 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3da0] [c01f6f20] pci_device_probe+0x80/0xa4
Jul 13 15:31:41 jim kernel: [ef3a3dc0] [c025cf2c] driver_probe_device+0xc0/0x208
Jul 13 15:31:41 jim kernel: [ef3a3de0] [c025d130] __driver_attach+0xbc/0xc0
Jul 13 15:31:41 jim kernel: [ef3a3e00] [c025bd28] bus_for_each_dev+0x64/0xa0
Jul 13 15:31:41 jim kernel: [ef3a3e30] [c025cb58] driver_attach+0x24/0x34
Jul 13 15:31:41 jim kernel: [ef3a3e40] [c025c618] bus_add_driver+0xd8/0x308
Jul 13 15:31:41 jim kernel: [ef3a3e70] [c025d418] driver_register+0x88/0x154
Jul 13 15:31:41 jim kernel: [ef3a3e90] [c01f7200]
__pci_register_driver+0x4c/0xdc
Jul 13 15:31:41 jim kernel: [ef3a3eb0] [f243ee50] drm_init+0x120/0x134 [drm]
Jul 13 15:31:41 jim kernel: [ef3a3ed0] [f26c00e4]
radeon_init+0xe4/0x128 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3ef0] [c0003ff4] do_one_initcall+0x3c/0x1d8
Jul 13 15:31:41 jim kernel: [ef3a3f20] [c007b434] sys_init_module+0xdc/0x1e0
Jul 13 15:31:41 jim kernel: [ef3a3f40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:41 jim kernel: --- Exception: c01 at 0xff62b58
Jul 13 15:31:41 jim kernel: LR = 0x10002c7c
Jul 13 15:31:41 jim kernel: Instruction dump:
Jul 13 15:31:41 jim kernel: bf010010 7c7d1b78 3f60f248 90010034
3f40f248 3b7b382c 7c7c1b78 3bc30298
Jul 13 15:31:41 jim kernel: 3b5a4054 3b7b004c 87fd02a4 3bfffffc
<813f0004> 2f890000 419e0008 7c004a2c
Jul 13 15:31:41 jim kernel: ---[ end trace 9928f19443a4dfb8 ]---
Jul 13 15:31:48 jim kernel: Oops: Kernel access of bad area, sig: 11 [#2]
Jul 13 15:31:48 jim kernel: PREEMPT PowerMac
Jul 13 15:31:48 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:48 jim kernel: NIP: c0382df4 LR: c0382de0 CTR: 00000000
Jul 13 15:31:48 jim kernel: REGS: eed7dd80 TRAP: 0300 Tainted: G
D W (2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:48 jim kernel: MSR: 00001032 <ME,IR,DR> CR: 24000424
XER: 20000000
Jul 13 15:31:48 jim kernel: DAR: 00000000, DSISR: 42000000
Jul 13 15:31:48 jim kernel: TASK = efb46820[2095] 'X' THREAD: eed7c000
Jul 13 15:31:48 jim kernel: GPR00: ffffffff eed7de30 efb46820 ef2c8e3c
eed7de38 eed7c000 eed7de44 0000082f
Jul 13 15:31:48 jim kernel: GPR08: 0000e200 00000000 0000007f c0382fc8
24000422 101d3ea8 101cbed4 00000000
Jul 13 15:31:48 jim kernel: GPR16: 10478758 00000000 00000001 101d3c0c
00000000 101cb8c8 ef2c8c00 00009032
Jul 13 15:31:48 jim kernel: GPR24: ef2c8dc0 ef2c8e40 eed7de38 efb46820
c04d0000 00009032 eed7c000 ef2c8e3c
Jul 13 15:31:48 jim kernel: NIP [c0382df4] __mutex_lock_slowpath+0xa0/0x274
Jul 13 15:31:48 jim kernel: LR [c0382de0] __mutex_lock_slowpath+0x8c/0x274
Jul 13 15:31:48 jim kernel: Call Trace:
Jul 13 15:31:48 jim kernel: [eed7de30] [c0382dd0]
__mutex_lock_slowpath+0x7c/0x274 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7de70] [c0382fe0] mutex_lock+0x18/0x34
Jul 13 15:31:48 jim kernel: [eed7de80] [f244d010] drm_fb_release+0x28/0xac [drm]
Jul 13 15:31:48 jim kernel: [eed7dea0] [f243fab8] drm_release+0x674/0x770 [drm]
Jul 13 15:31:48 jim kernel: [eed7dee0] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7df00] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7df20] [c00f88ac] sys_close+0xb0/0x12c
Jul 13 15:31:48 jim kernel: [eed7df40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:48 jim kernel: --- Exception: c01 at 0xfec3730
Jul 13 15:31:48 jim kernel: LR = 0xf8b9340
Jul 13 15:31:48 jim kernel: Instruction dump:
Jul 13 15:31:48 jim kernel: 7f44d378 3b3f0004 4bcee849 80bb0004
7fe3fb78 7f44d378 4bcee9e5 813f0008
Jul 13 15:31:48 jim kernel: 3800ffff 93210008 935f0008 9121000c
<93490000> 93610010 7d20f828 7c00f92d
Jul 13 15:31:48 jim kernel: ---[ end trace 9928f19443a4dfb9 ]---
Jul 13 15:31:48 jim kernel: note: X[2095] exited with preempt_count 2
Jul 13 15:31:48 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:48 jim kernel: Call Trace:
Jul 13 15:31:48 jim kernel: [eed7da50] [c000a6e8]
show_stack+0x50/0x158 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7da90] [c00369bc] __schedule_bug+0x64/0x68
Jul 13 15:31:48 jim kernel: [eed7daa0] [c03816a0] schedule+0x360/0x428
Jul 13 15:31:48 jim kernel: [eed7dae0] [c0382068] schedule_timeout+0x1c0/0x304
Jul 13 15:31:48 jim kernel: [eed7db30] [c0381b7c] wait_for_common+0xd4/0x1c8
Jul 13 15:31:48 jim kernel: [eed7db70] [c005a0b8] flush_cpu_workqueue+0xdc/0x110
Jul 13 15:31:48 jim kernel: [eed7dbb0] [c022fc7c] tty_ldisc_release+0x2c/0x84
Jul 13 15:31:48 jim kernel: [eed7dbd0] [c0228374] tty_release+0x428/0x5b0
Jul 13 15:31:48 jim kernel: [eed7dc70] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7dc90] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7dcb0] [c0042958] put_files_struct+0x12c/0x158
Jul 13 15:31:48 jim kernel: [eed7dce0] [c0042b7c] do_exit+0x118/0x708
Jul 13 15:31:48 jim kernel: [eed7dd30] [c0015560] die+0x100/0x2c0
Jul 13 15:31:48 jim kernel: [eed7dd60] [c001f350] bad_page_fault+0x90/0xc8
Jul 13 15:31:48 jim kernel: [eed7dd70] [c001843c] handle_page_fault+0x7c/0x80
Jul 13 15:31:48 jim kernel: --- Exception: 300 at
__mutex_lock_slowpath+0xa0/0x274
Jul 13 15:31:48 jim kernel: LR = __mutex_lock_slowpath+0x8c/0x274
Jul 13 15:31:48 jim kernel: [eed7de30] [c0382dd0]
__mutex_lock_slowpath+0x7c/0x274 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7de70] [c0382fe0] mutex_lock+0x18/0x34
Jul 13 15:31:48 jim kernel: [eed7de80] [f244d010] drm_fb_release+0x28/0xac [drm]
Jul 13 15:31:48 jim kernel: [eed7dea0] [f243fab8] drm_release+0x674/0x770 [drm]
Jul 13 15:31:48 jim kernel: [eed7dee0] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7df00] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7df20] [c00f88ac] sys_close+0xb0/0x12c
Jul 13 15:31:48 jim kernel: [eed7df40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:48 jim kernel: --- Exception: c01 at 0xfec3730
Jul 13 15:31:48 jim kernel: LR = 0xf8b9340
Jul 13 15:33:09 jim kernel: Using PowerMac machine description
[Next boot..]
Many thanks
^ permalink raw reply
* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: Michel Dänzer @ 2010-07-13 14:19 UTC (permalink / raw)
To: jjDaNiMoTh; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <AANLkTikdlwl6VxdbkHP0TveQ3SPO0w4RD-onoG3QPyCO@mail.gmail.com>
On Die, 2010-07-13 at 16:03 +0200, jjDaNiMoTh wrote:=20
>=20
> When trying new 2.6.35-rc4, our kernel team (ArchLinuxPPC) has tried
> to setup KMS acceleration for radeon based machine.
> We have removed radeonfb, and all others framebuffer driver, and added
> fbcon and KMS enabled by default for radeon driver.
>=20
> With a clean start, the screen freeze, when the control pass from
> yaboot to kernel.
>=20
> If we start with video=3Dfbcon (or video=3Dradeondrmfb), we could reach
> the loading modules point, [...]
Which framebuffer device (if any) is it trying to initialize otherwise?
OFfb? The first paragraph above implies none, but then I'm not sure why
the video=3D parameters would make any difference.
> Jul 13 15:31:39 jim kernel: [drm] Initialized drm 1.1.0 20060810
> Jul 13 15:31:39 jim kernel: [drm] radeon kernel modesetting enabled.
> Jul 13 15:31:39 jim kernel: [drm] initializing kernel modesetting
> (RV350 0x1002:0x4E50).
> Jul 13 15:31:39 jim kernel: [drm] register mmio base: 0xB0000000
> Jul 13 15:31:39 jim kernel: [drm] register mmio size: 65536
> Jul 13 15:31:39 jim kernel: [drm] Using generic clock info
> Jul 13 15:31:39 jim kernel: agpgart-uninorth 0000:00:0b.0: putting AGP
> V2 device into 4x mode
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: putting AGP V2 device
> into 4x mode
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: GTT: 256M 0x00000000
> - 0x0FFFFFFF
> Jul 13 15:31:39 jim kernel: [drm] Generation 2 PCI interface, using
> max accessible memory
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: VRAM: 64M 0xB8000000
> - 0xBBFFFFFF (64M used)
> Jul 13 15:31:39 jim kernel: [drm] radeon: irq initialized.
> Jul 13 15:31:39 jim kernel: [drm] Detected VRAM RAM=3D64M, BAR=3D128M
> Jul 13 15:31:39 jim kernel: [drm] RAM width 128bits DDR
> Jul 13 15:31:39 jim kernel: [TTM] Zone kernel: Available graphics
> memory: 384990 kiB.
> Jul 13 15:31:39 jim kernel: [TTM] Zone highmem: Available graphics
> memory: 516062 kiB.
> Jul 13 15:31:39 jim kernel: [TTM] Initializing pool allocator.
> Jul 13 15:31:39 jim kernel: [drm] radeon: 64M of VRAM memory ready
> Jul 13 15:31:39 jim kernel: [drm] radeon: 256M of GTT memory ready.
> Jul 13 15:31:39 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initial=
ized.
> Jul 13 15:31:39 jim kernel: [drm] Loading R300 Microcode
> Jul 13 15:31:39 jim kernel: [drm] radeon: ring at 0x0000000000000000
> Jul 13 15:31:39 jim kernel: [drm] ring test succeeded in 3 usecs
> Jul 13 15:31:39 jim kernel: [drm] radeon: ib pool ready.
So far, so good.
> Jul 13 15:31:40 jim kernel: GPU lockup (waiting for 0x00000001 last
> fence id 0x00000000)
The GPU locks up, and things go downhill from there...
Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?
--=20
Earthling Michel D=C3=A4nzer | http://www.vmware.c=
om
Libre software enthusiast | Debian, X and DRI developer
^ permalink raw reply
* [PATCH] powerpc: Add vmcoreinfo symbols to allow makdumpfile to filter core files properly
From: Neil Horman @ 2010-07-13 13:46 UTC (permalink / raw)
To: linux-kernel, kexec, vgoyal, hbabu, benh, paulus, linuxppc-dev; +Cc: nhorman
Hey all-
About 2 years ago now, I sent this patch upstream to allow makedumpfile
to properly filter cores on ppc64:
http://www.mail-archive.com/kexec@lists.infradead.org/msg02426.html
It got acks from the kexec folks so I pulled it into RHEL, but I never checked
back here to make sure it ever made it in, which apparently it didn't. It still
needs to be included, so I'm reposting it here, making sure to copy all the ppc
folks this time. I've retested it on the latest linus kernel and it works fine,
allowing makedumpfile to find all the symbols it needs to properly strip a
vmcore on ppc64.
Neil
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
machine_kexec.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c
index bb3d893..0df7031 100644
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -45,6 +45,18 @@ void machine_kexec_cleanup(struct kimage *image)
ppc_md.machine_kexec_cleanup(image);
}
+void arch_crash_save_vmcoreinfo(void)
+{
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ VMCOREINFO_SYMBOL(node_data);
+ VMCOREINFO_LENGTH(node_data, MAX_NUMNODES);
+#endif
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+ VMCOREINFO_SYMBOL(contig_page_data);
+#endif
+}
+
/*
* Do not allocate memory (or fail in any way) in machine_kexec().
* We are past the point of no return, committed to rebooting now.
^ permalink raw reply related
* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: jjDaNiMoTh @ 2010-07-13 14:51 UTC (permalink / raw)
To: Michel Dänzer; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <1279030767.515.15.camel@thor.local>
2010/7/13 Michel D=C3=A4nzer <michel@daenzer.net>:
[cut]
> Which framebuffer device (if any) is it trying to initialize otherwise?
> OFfb? The first paragraph above implies none, but then I'm not sure why
> the video=3D parameters would make any difference.
We tried and with 2.6.35-rc4 we could boot without video=3D. First good new=
s :)
[cut]
> Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?
with radeon.agpmode=3D-1, we could start X server (no black screen),
with both radeon.modeset=3D{0,1}. In all cases, Xorg works fine, except
when we try to load an OpenGL application (like glxgears), Xorg
freeze, we could move only the mouse, we couldn't switch to a backup
console. Same situations with glxgears in both modeset=3D0 and =3D1. In
the log (Xorg.0.log) we have found:
[.. other xorg log, no EE only WW]
[ 65.238] (II) RADEON(0): Panel infos found from DDC detailed: 1280x854
[ 65.238] (II) RADEON(0): EDID vendor "APP", prod id 39968
[ 65.249] (II) RADEON(0): Output: DVI-0, Detected Monitor Type: 0
[ 65.249] Unhandled monitor type 0
[ 65.249] (II) RADEON(0): Output: S-video, Detected Monitor Type: 0
[ 137.813] [mi] EQ overflowing. The server is probably stuck in an
infinite loop.
[ 137.813]
Backtrace:
[ 137.814] 0: /usr/bin/X (xorg_backtrace+0x58) [0x100582cc]
[ 137.814] 1: /usr/bin/X (mieqEnqueue+0x1c8) [0x1004e5d8]
[ 137.814] 2: /usr/bin/X (xf86PostButtonEventP+0xf4) [0x10061be8]
[ 137.814] 3: /usr/bin/X (xf86PostButtonEvent+0xb4) [0x10061d2c]
[ 137.814] 4: /usr/lib/xorg/modules/input/evdev_drv.so
(0xf380000+0x3d88) [0xf383d88]
[ 137.814] 5: /usr/bin/X (0x10000000+0x68784) [0x10068784]
[ 137.814] 6: /usr/bin/X (0x10000000+0x11a7e4) [0x1011a7e4]
[ 137.814] 7: (vdso) (__kernel_sigtramp32+0x0) [0x100344]
[ 137.814] 8: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x48534) [0xf43d534]
[ 137.814] 9: /usr/lib/libdrm.so.2 (drmIoctl+0x40) [0xf8b8f64]
[ 137.814] 10: /usr/lib/libdrm.so.2 (drmCommandWrite+0x24) [0xf8bbe60]
[ 137.814] 11: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x46944) [0xf43b944]
[ 137.814] 12: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x64d8c) [0xf459d8c]
[ 137.814] 13: /usr/lib/xorg/modules/extensions/libglx.so
(0xf930000+0x40f78) [0xf970f78]
[ 137.814] 14: /usr/lib/xorg/modules/extensions/libglx.so
(0xf930000+0x44be4) [0xf974be4]
[ 137.814] 15: /usr/bin/X (0x10000000+0x34a24) [0x10034a24]
[ 137.815] 16: /usr/bin/X (0x10000000+0x18bc4) [0x10018bc4]
[ 137.815] 17: /lib/libc.so.6 (0xfb39000+0x1f544) [0xfb58544]
[ 137.815] 18: /lib/libc.so.6 (0xfb39000+0x1f6d0) [0xfb586d0]
Do we need to compile mesa, ati-dri, x.org and xf86-video-ati from git?
Many thanks
^ permalink raw reply
* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: Michel Dänzer @ 2010-07-13 14:59 UTC (permalink / raw)
To: jjDaNiMoTh; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <AANLkTilk4-gIiJPQb5aqZ5v4YvgzsddexnsmrFBER5HO@mail.gmail.com>
On Die, 2010-07-13 at 16:51 +0200, jjDaNiMoTh wrote:=20
> 2010/7/13 Michel D=C3=A4nzer <michel@daenzer.net>:
> > Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?
>=20
> with radeon.agpmode=3D-1, we could start X server (no black screen),
> with both radeon.modeset=3D{0,1}.
Note that radeon.agpmode is only effective with radeon.modeset=3D1,
otherwise you need to use Option "AGPMode" in xorg.conf (and vice
versa).
> In all cases, Xorg works fine, except when we try to load an OpenGL
> application (like glxgears), Xorg freeze, we could move only the
> mouse, we couldn't switch to a backup console.
Could be a GPU lockup again, possibly due to still using AGP 4x with
modeset=3D0.
> Same situations with glxgears in both modeset=3D0 and =3D1. In the log
> (Xorg.0.log) we have found:=20
>=20
> [.. other xorg log, no EE only WW]
> [ 65.238] (II) RADEON(0): Panel infos found from DDC detailed: 1280x85=
4
> [ 65.238] (II) RADEON(0): EDID vendor "APP", prod id 39968
> [ 65.249] (II) RADEON(0): Output: DVI-0, Detected Monitor Type: 0
> [ 65.249] Unhandled monitor type 0
> [ 65.249] (II) RADEON(0): Output: S-video, Detected Monitor Type: 0
> [ 137.813] [mi] EQ overflowing. The server is probably stuck in an
> infinite loop.
> [ 137.813]
> Backtrace:
> [ 137.814] 0: /usr/bin/X (xorg_backtrace+0x58) [0x100582cc]
> [ 137.814] 1: /usr/bin/X (mieqEnqueue+0x1c8) [0x1004e5d8]
> [ 137.814] 2: /usr/bin/X (xf86PostButtonEventP+0xf4) [0x10061be8]
> [ 137.814] 3: /usr/bin/X (xf86PostButtonEvent+0xb4) [0x10061d2c]
> [ 137.814] 4: /usr/lib/xorg/modules/input/evdev_drv.so
> (0xf380000+0x3d88) [0xf383d88]
> [ 137.814] 5: /usr/bin/X (0x10000000+0x68784) [0x10068784]
> [ 137.814] 6: /usr/bin/X (0x10000000+0x11a7e4) [0x1011a7e4]
> [ 137.814] 7: (vdso) (__kernel_sigtramp32+0x0) [0x100344]
> [ 137.814] 8: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x48534) [0xf43d534]
> [ 137.814] 9: /usr/lib/libdrm.so.2 (drmIoctl+0x40) [0xf8b8f64]
> [ 137.814] 10: /usr/lib/libdrm.so.2 (drmCommandWrite+0x24) [0xf8bbe60]
> [ 137.814] 11: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x46944) [0xf43b944]
> [ 137.814] 12: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x64d8c) [0xf459d8c]
> [ 137.814] 13: /usr/lib/xorg/modules/extensions/libglx.so
> (0xf930000+0x40f78) [0xf970f78]
> [ 137.814] 14: /usr/lib/xorg/modules/extensions/libglx.so
> (0xf930000+0x44be4) [0xf974be4]
> [ 137.814] 15: /usr/bin/X (0x10000000+0x34a24) [0x10034a24]
> [ 137.815] 16: /usr/bin/X (0x10000000+0x18bc4) [0x10018bc4]
> [ 137.815] 17: /lib/libc.so.6 (0xfb39000+0x1f544) [0xfb58544]
> [ 137.815] 18: /lib/libc.so.6 (0xfb39000+0x1f6d0) [0xfb586d0]
What does the log file contain with modeset=3D1?
> Do we need to compile mesa, ati-dri, x.org and xf86-video-ati from git?
Shouldn't be necessary.
--=20
Earthling Michel D=C3=A4nzer | http://www.vmware.c=
om
Libre software enthusiast | Debian, X and DRI developer
^ permalink raw reply
* Re: section .data..init_task
From: Sean MacLennan @ 2010-07-13 15:26 UTC (permalink / raw)
To: Sam Ravnborg; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100713085419.GA5826@merkur.ravnborg.org>
On Tue, 13 Jul 2010 10:54:19 +0200
Sam Ravnborg <sam@ravnborg.org> wrote:
> It looks like a missing AT() in the output section.
> The following patch should also fix it.
>
> Please test and let us know.
>
> Thanks,
> Sam
Applied the patch and it solves the problem. Thanks.
Cheers,
Sean
^ permalink raw reply
* Re: section .data..init_task
From: Sam Ravnborg @ 2010-07-13 15:33 UTC (permalink / raw)
To: Sean MacLennan; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100713112610.30b66318@lappy.seanm.ca>
On Tue, Jul 13, 2010 at 11:26:10AM -0400, Sean MacLennan wrote:
> On Tue, 13 Jul 2010 10:54:19 +0200
> Sam Ravnborg <sam@ravnborg.org> wrote:
>
> > It looks like a missing AT() in the output section.
> > The following patch should also fix it.
> >
> > Please test and let us know.
> >
> > Thanks,
> > Sam
>
> Applied the patch and it solves the problem. Thanks.
Thanks for the quick feedback!
Sam
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox