LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V6 08/10] USB ppc4xx: Add Synopsys DWC OTG PCD interrupt function
From: tmarri @ 2010-12-09  0:32 UTC (permalink / raw)
  To: linux-usb; +Cc: tmarri, Mark Miesfeld, linuxppc-dev, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Implements the DWC OTG PCD Interrupt Service routine.

Signed-off-by: Tirumala R Marri<tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_pcd_intr.c | 2278 ++++++++++++++++++++++++++++++++
 1 files changed, 2278 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
new file mode 100644
index 0000000..9e8a2df
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
@@ -0,0 +1,2278 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. 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.
+ *
+ */
+
+#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)
+{
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		u32 i;
+		int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+
+		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)
+{
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		u32 i;
+		int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+		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)
+{
+	if (!list_empty(&ep->queue)) {
+		struct pcd_request *req;
+
+		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);
+	u32 gintsts;
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTMSK_STRT_OF_FRM;
+	dwc_write32((u32)(core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+	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);
+	u32 global_regs = (u32)core_if->core_global_regs;
+	u32 gintmask = 0;
+	u32 grxsts;
+	struct pcd_ep *ep;
+	u32 gintsts;
+
+	/* Disable the Rx Status Queue Level interrupt */
+	gintmask |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+	dwc_modify32(global_regs + DWC_GINTMSK, gintmask, 0);
+
+	/* Get the Status from the top of the FIFO */
+	grxsts = dwc_read32(global_regs + DWC_GRXSTSP);
+
+	/* Get pointer to EP structure */
+	ep = get_out_ep(pcd, DWC_DM_RXSTS_CHAN_NUM_RD(grxsts));
+
+	switch (DWC_DM_RXSTS_PKT_STS_RD(grxsts)) {
+	case DWC_DSTS_GOUT_NAK:
+		break;
+	case DWC_STS_DATA_UPDT:
+		if ((grxsts & DWC_DM_RXSTS_BYTE_CNT) && ep->dwc_ep.xfer_buff) {
+			dwc_otg_read_packet(core_if, ep->dwc_ep.xfer_buff,
+						DWC_DM_RXSTS_BYTE_CNT_RD(grxsts));
+			ep->dwc_ep.xfer_count += DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+			ep->dwc_ep.xfer_buff += DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+		}
+		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 += DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+		break;
+	default:
+		pr_err("RX_STS_Q Interrupt: Unknown status %d\n",
+					DWC_HM_RXSTS_PKT_STS_RD(grxsts));
+		break;
+	}
+
+	/* Enable the Rx Status Queue Level interrupt */
+	dwc_modify32(global_regs + DWC_GINTMSK, 0, gintmask);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_RXFIFO_NOT_EMPT;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+
+	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)
+{
+	u32 regs = (u32)core_if->dev_if->dev_global_regs;
+	const u32 TOKEN_Q_DEPTH = DWC_HWCFG2_DEV_TKN_Q_DEPTH_RD(core_if->hwcfg2);
+	/* Number of Token Queue Registers */
+	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+	u32 dtknqr1 = 0;
+	u32 in_tkn_epnums[4];
+	int ndx;
+	u32 i;
+	u32 addr = regs + DWC_DTKNQR1;
+	int epnum = 0;
+
+	/* Read the DTKNQ Registers */
+	for (i = 0; i <= DTKNQ_REG_CNT; i++) {
+		in_tkn_epnums[i] = dwc_read32(addr);
+
+		if (addr == (regs + DWC_DVBUSDIS))
+			addr = regs + DWC_DTKNQR3_DTHRCTL;
+		else
+			++addr;
+	}
+
+	/* Copy the DTKNQR1 data to the bit field. */
+	dtknqr1 = in_tkn_epnums[0];
+
+	/* Get the EP numbers */
+	in_tkn_epnums[0] = DWC_DTKNQR1_EP_TKN_NO_RD(dtknqr1);
+	ndx = DWC_DTKNQR1_INT_TKN_Q_WR_PTR_RD(dtknqr1) - 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);
+	u32 global_regs = (u32)core_if->core_global_regs;
+	u32 txstatus = 0;
+	u32 gintsts = 0;
+	int epnum;
+	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 = dwc_read32(global_regs + DWC_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 ((DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(txstatus) > 0 )&&
+			(DWC_GNPTXSTS_NPTXFSPCAVAIL_RD(txstatus) > 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 = dwc_read32(global_regs + DWC_GNPTXSTS);
+	}
+
+	/* Clear nptxfempty interrupt */
+	gintsts |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+
+	/* Re-enable tx-fifo empty interrupt, if packets are stil pending */
+	if (len)
+		dwc_modify32(global_regs + DWC_GINTSTS, 0, gintsts);
+	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);
+	u32 regs;
+	u32 txstatus = 0;
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, epnum);
+	regs = (u32)core_if->dev_if->in_ep_regs[epnum];
+	txstatus = dwc_read32(regs + DWC_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 (DWC_DTXFSTS_TXFSSPC_AVAI_RD(txstatus) > 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 = dwc_read32(regs + DWC_DTXFSTS);
+	}
+	/* Clear emptyintr */
+	diepint = DWC_DIEPINT_TXFIFO_EMPTY_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, epnum), diepint);
+	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;
+	u32 intr_mask = 0;
+	u32 global_regs = (u32)GET_CORE_IF(pcd)->core_global_regs;
+
+	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 |= DWC_INTMSK_NP_TXFIFO_EMPT;
+	dwc_modify32(global_regs + DWC_GINTMSK,	intr_mask, 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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for i2cintr\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask |= DWC_INTMSK_I2C_INTR;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_I2C_INTR;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+				gintsts);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_early_suspend_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Early Suspend Detected\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask |= DWC_INTMSK_EARLY_SUSP;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_EARLY_SUSP;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+				gintsts);
+
+	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;
+	u32 doeptsize0 = 0;
+
+	doeptsize0 = DWC_DEPTSIZ0_SUPCNT_RW(doeptsize0, 3);
+	doeptsize0 = DWC_DEPTSIZ0_PKT_CNT_RW(doeptsize0, 1);
+	doeptsize0 = DWC_DEPTSIZ0_XFER_SIZ_RW(doeptsize0, 8 * 3);
+	dwc_write32((u32)dev_if->out_ep_regs[0] + DWC_DOEPTSIZ, doeptsize0);
+
+	if (core_if->dma_enable) {
+		u32 doepctl = 0;
+
+		dwc_write32((u32)dev_if->out_ep_regs[0] + DWC_DOEPDMA,
+				pcd->setup_pkt_dma_handle);
+		doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+		doepctl = DWC_DEPCTL_ACT_EP_RW(doepctl, 1);
+		dwc_write32(out_ep_ctl_reg(pcd, 0), doepctl);
+	}
+}
+
+/**
+ * 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;
+	u32 doepctl = 0;
+	u32 daintmsk = 0;
+	u32 doepmsk = 0;
+	u32 diepmsk = 0;
+	u32 dcfg = 0;
+	u32 resetctl = 0;
+	u32 dctl = 0;
+	u32 i;
+	u32 gintsts = 0;
+
+	pr_info("USB RESET\n");
+
+	/* reset the HNP settings */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Clear the Remote Wakeup Signalling */
+	dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+	dwc_modify32(dev_ctl_reg(pcd), dctl, 0);
+
+	/* Set NAK for all OUT EPs */
+	doepctl = DWC_DEPCTL_SET_NAK_RW(doepctl, 1);
+	for (i = 0; i <= dev_if->num_out_eps; i++)
+		dwc_write32(out_ep_ctl_reg(pcd, i), doepctl);
+
+	/* Flush the NP Tx FIFO */
+	dwc_otg_flush_tx_fifo(core_if, 0);
+
+	/* Flush the Learning Queue */
+	resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
+	dwc_write32((u32)core_if->core_global_regs + DWC_GRSTCTL, resetctl);
+
+	daintmsk |= DWC_DAINT_INEP00;
+	daintmsk |= DWC_DAINT_OUTEP00;
+	dwc_write32((u32)dev_if->dev_global_regs + DWC_DAINTMSK, daintmsk);
+
+	doepmsk = DWC_DOEPMSK_SETUP_DONE_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_AHB_ERROR_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_EP_DISA_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_TX_COMPL_RW(doepmsk, 1);
+	dwc_write32((u32)dev_if->dev_global_regs + DWC_DOEPMSK, doepmsk);
+
+	diepmsk = DWC_DIEPMSK_TX_CMPL_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_TOUT_COND_RW(diepmsk,1);
+	diepmsk = DWC_DIEPMSK_EP_DISA_RW(diepmsk,1);
+	diepmsk = DWC_DIEPMSK_AHB_ERROR_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+	dwc_write32((u32)dev_if->dev_global_regs + DWC_DIEPMSK, diepmsk);
+
+	/* Reset Device Address */
+	dcfg = dwc_read32((u32)dev_if->dev_global_regs + DWC_DCFG);
+	dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg, 0);
+	dwc_write32((u32)dev_if->dev_global_regs + DWC_DCFG, dcfg);
+
+	/* setup EP0 to receive SETUP packets */
+	ep0_out_start(core_if, pcd);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_USB_RST;
+	dwc_write32((u32)(core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+
+	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)
+{
+	u32 dsts = 0;
+	enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+	dsts = dwc_read32(dev_sts_reg(pcd));
+
+	switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+	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;
+	u32 dsts;
+	u32 diepctl = 0;
+	u32 doepctl = 0;
+	u32 dctl = 0;
+
+	/* Read the Device Status and Endpoint 0 Control registers */
+	dsts = dwc_read32((u32)dev_if->dev_global_regs + DWC_DSTS);
+	diepctl = dwc_read32((u32)dev_if->in_ep_regs[0] + DWC_DIEPCTL);
+	doepctl = dwc_read32((u32)dev_if->out_ep_regs[0] + DWC_DOEPCTL);
+
+	/* Set the MPS of the IN EP based on the enumeration speed */
+	switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+	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 = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_64);
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		diepctl = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_8);
+		break;
+	}
+	dwc_write32((u32)dev_if->in_ep_regs[0] + DWC_DIEPCTL, diepctl);
+
+	/* Enable OUT EP for receive */
+	doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+	dwc_write32((u32)dev_if->out_ep_regs[0] + DWC_DOEPCTL, doepctl);
+
+	dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+	dwc_modify32((u32)dev_if->dev_global_regs + DWC_DCTL, dctl, dctl);
+}
+
+/**
+ * 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;
+	u32 gintsts;
+	u32 gusbcfg;
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 global_regs = (u32)core_if->core_global_regs;
+	u32 gsnpsid = global_regs + DWC_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 = dwc_read32(global_regs + DWC_GUSBCFG);
+
+	/* Set USB turnaround time based on device speed and PHY interface. */
+	if (pcd->gadget.speed == USB_SPEED_HIGH) {
+		switch (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2)) {
+		case DWC_HWCFG2_HS_PHY_TYPE_ULPI:
+			gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+				DWC_USBCFG_TRN_TIME(9);
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI:
+			if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(core_if->hwcfg4) == 0)
+				gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+					DWC_USBCFG_TRN_TIME(utmi8b);
+			else if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(core_if->hwcfg4) == 1)
+				gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+					DWC_USBCFG_TRN_TIME(utmi16b);
+			else if (core_if->core_params->phy_utmi_width == 8)
+				gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+					DWC_USBCFG_TRN_TIME(utmi8b);
+			else
+				gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+					DWC_USBCFG_TRN_TIME(utmi16b);
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI:
+			if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
+				gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+					DWC_USBCFG_TRN_TIME(9);
+			} else {
+				if (core_if->core_params->phy_utmi_width == 16)
+					gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+						DWC_USBCFG_TRN_TIME(utmi16b);
+				else
+					gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+						DWC_USBCFG_TRN_TIME(utmi8b);
+			}
+			break;
+		}
+	} else {
+		/* Full or low speed */
+		gusbcfg = (gusbcfg & (~((u32)DWC_USBCFG_TRN_TIME(0xf)))) |
+			DWC_USBCFG_TRN_TIME(9);
+	}
+	dwc_write32(global_regs + DWC_GUSBCFG, gusbcfg);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ENUM_DONE;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+			 gintsts);
+
+	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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt Handler not implemented for ISOC Out "
+			"Dropped\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ISYNC_OUTPKT_DRP;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+			 gintsts);
+
+	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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for End of "
+		"Periodic Portion of Micro-Frame Interrupt");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_END_OF_PFRM;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_END_OF_PFRM;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,							gintsts);
+
+	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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for End Point "
+				"Mismatch\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
+	dwc_modify32((u32)(core_if->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts= 0;
+	gintsts |= DWC_INTSTS_ENDP_MIS_MTCH;
+	dwc_write32((u32)(core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+	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;
+
+	pr_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)
+{
+	if (pcd->driver && pcd->driver->setup) {
+		int ret;
+
+		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)
+{
+	u32 dctl = 0;
+	struct dwc_pcd *pcd = (struct dwc_pcd *) data;
+	int test_mode = pcd->test_mode;
+
+	dctl = dwc_read32(dev_ctl_reg(pcd));
+	switch (test_mode) {
+	case 1:		/* TEST_J */
+		dctl = DWC_DCTL_TST_CTL(dctl, 1);
+		break;
+	case 2:		/* TEST_K */
+		dctl = DWC_DCTL_TST_CTL(dctl, 2);
+		break;
+	case 3:		/* TEST_SE0_NAK */
+		dctl = DWC_DCTL_TST_CTL(dctl, 3);
+		break;
+	case 4:		/* TEST_PACKET */
+		dctl = DWC_DCTL_TST_CTL(dctl, 4);
+		break;
+	case 5:		/* TEST_FORCE_ENABLE */
+		dctl = DWC_DCTL_TST_CTL(dctl, 5);
+		break;
+	}
+	dwc_write32(dev_ctl_reg(pcd), dctl);
+}
+
+/**
+ * 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);
+	u32 regs = (u32)core_if->core_global_regs;
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	int otg_cap = core_if->core_params->otg_cap;
+	u32 gotgctl = 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 |= DWC_GCTL_DEV_HNP_ENA;
+				gotgctl |= DWC_GCTL_HNP_REQ;
+				dwc_write32(regs + DWC_GOTGCTL, gotgctl);
+			} 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) {
+			struct pcd_ep *ep;
+
+			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;
+
+	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 == NULL) {
+			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;
+	u32 doeptsize0 = 0;
+
+	doeptsize0 = dwc_read32((u32)dev_if->out_ep_regs[0] + DWC_DOEPTSIZ);
+
+	/* handle > 1 setup packet , assert error for now */
+	if (core_if->dma_enable && (DWC_DEPTSIZ0_SUPCNT_RD(doeptsize0) < 2))
+		pr_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) {
+			u32 dcfg = 0;
+
+			dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg,
+					__le16_to_cpu(ctrl.wValue));
+			dwc_modify32((u32)dev_if->dev_global_regs + DWC_DCFG, 0,
+						dcfg);
+			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;
+	u32 in_regs = (u32)dev_if->in_ep_regs[ep->dwc_ep.num];
+	u32 deptsiz = 0;
+	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 = dwc_read32((u32)in_regs + DWC_DIEPTSIZ);
+
+		if (DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz) == 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;
+	u32 in_ep_regs = (u32)dev_if->in_ep_regs[ep->dwc_ep.num];
+	u32 deptsiz = 0;
+	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 = dwc_read32((u32)in_ep_regs + DWC_DIEPTSIZ);
+
+		if (core_if->dma_enable  && !DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz))
+			ep->dwc_ep.xfer_count = ep->dwc_ep.xfer_len;
+
+		if (DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz) == 0 &&
+				DWC_DEPTSIZ_PKT_CNT_RD(deptsiz) == 0 &&
+				ep->dwc_ep.xfer_count == ep->dwc_ep.xfer_len)
+			is_last = 1;
+		else
+			pr_warning("Incomplete transfer (%s-%s "
+				"[siz=%d pkt=%d])\n", ep->ep.name,
+				ep->dwc_ep.is_in ? "IN" : "OUT",
+				DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz),
+				DWC_DEPTSIZ_PKT_CNT_RD(deptsiz));
+	} else {
+		u32 out_ep_regs = (u32)dev_if->out_ep_regs[ep->dwc_ep.num];
+
+		deptsiz = dwc_read32((u32)out_ep_regs + DWC_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) {
+			u32 in_regs =
+				(u32)core_if->dev_if->in_ep_regs[ep->dwc_ep.num];
+			u32 intr_mask = 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 = dwc_read32((u32)in_regs + DWC_DIEPTSIZ);
+			deptsiz = DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, ep->dwc_ep.xfer_len);
+			deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+					((ep->dwc_ep.xfer_len - 1 +
+				ep->dwc_ep.maxpacket) / ep->dwc_ep.maxpacket));
+			dwc_write32((u32)in_regs + DWC_DIEPTSIZ, deptsiz);
+
+			intr_mask |= DWC_INTSTS_NP_TXFIFO_EMPT;
+			dwc_modify32((u32)(core_if->core_global_regs) + DWC_GINTSTS,
+						intr_mask, 0);
+			dwc_modify32((u32)(core_if->core_global_regs) + DWC_GINTMSK,
+						intr_mask, intr_mask);
+
+			/*
+			 * Just return here if message was not completely
+			 * transferred.
+			 */
+			return;
+		}
+		if (core_if->dma_enable)
+			req->req.actual = ep->dwc_ep.xfer_len -
+				DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz);
+		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)
+{
+	if (ep->is_in) {
+		u32 depctl = 0;
+		u32 deptsiz = 0;
+		struct device_if *d_if = c_if->dev_if;
+		u32 in_regs = (u32)d_if->in_ep_regs[0];
+		u32 tx_status = 0;
+		u32 glbl_regs = (u32)c_if->core_global_regs;
+
+		tx_status = dwc_read32(glbl_regs + DWC_GNPTXSTS);
+
+		depctl = dwc_read32((u32)in_regs + DWC_DIEPCTL);
+		deptsiz = dwc_read32((u32)in_regs + DWC_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 = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+					ep->maxpacket);
+		else
+			deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+					(ep->total_len - ep->xfer_count));
+
+		deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+		ep->xfer_len += DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz);
+		dwc_write32((u32)in_regs + DWC_DIEPTSIZ, deptsiz);
+
+		/* Write the DMA register */
+		if (DWC_HWCFG2_ARCH_RD(c_if->hwcfg2) == DWC_INT_DMA_ARCH)
+			dwc_write32((u32)in_regs + DWC_DIEPDMA, ep->dma_addr);
+
+		/* EP enable, IN data in FIFO */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32((u32)in_regs + DWC_DIEPCTL, depctl);
+
+		/*
+		 * Enable the Non-Periodic Tx FIFO empty interrupt, the
+		 * data will be written into the fifo by the ISR.
+		 */
+		if (!c_if->dma_enable) {
+			u32 intr_mask = 0;
+
+			/* First clear it from GINTSTS */
+			intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+			dwc_write32(glbl_regs + DWC_GINTSTS, intr_mask);
+
+			/* To avoid spurious NPTxFEmp intr */
+			dwc_modify32(glbl_regs + DWC_GINTMSK, intr_mask,
+				intr_mask);
+		}
+	}
+}
+
+/**
+ * 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;
+			u32 diepctl = 0;
+
+			diepctl = dwc_read32(in_ep_ctl_reg(pcd, 0));
+			if (pcd->ep0.queue_sof) {
+				pcd->ep0.queue_sof = 0;
+				start_next_request(&pcd->ep0);
+			}
+
+			diepctl = dwc_read32(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 =
+					dwc_read32(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:
+		pr_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;
+	u32 dieptsiz = 0;
+	struct pcd_ep *ep;
+
+	dieptsiz = dwc_read32((u32)dev_if->in_ep_regs[ep_num] + DWC_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 (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) &&
+			!DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz) &&
+			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;
+	u32 diepint = 0;
+
+	if (c_if->en_multiple_tx_fifo) {
+		u32 fifoemptymsk = 0x1 << dwc_ep->num;
+		dwc_modify32((u32)d_if->dev_global_regs + DWC_DTKNQR4FIFOEMPTYMSK,
+					fifoemptymsk, 0);
+	} else {
+		u32 intr_mask = 0;
+
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32((u32)(c_if->core_global_regs) + DWC_GINTMSK,
+					intr_mask, 0);
+	}
+
+	/* Clear the interrupt, then complete the transfer */
+	diepint = DWC_DIEPINT_TX_CMPL_RW(diepint, 1);
+	dwc_write32((u32)d_if->in_ep_regs[num] + DWC_DIEPINT, diepint);
+
+	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;
+	u32 dieptsiz = 0;
+	u32 dctl = 0;
+	struct pcd_ep *ep;
+	struct dwc_ep *dwc_ep;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, ep_num);
+	dwc_ep = &ep->dwc_ep;
+
+	dieptsiz = dwc_read32((u32)dev_if->in_ep_regs[ep_num] + DWC_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 = 0;
+		dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+		dwc_modify32(dev_ctl_reg(pcd), dctl, 0);
+
+		if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+				DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+			restart_transfer(pcd, ep_num);
+	} else {
+		if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+				DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+			restart_transfer(pcd, ep_num);
+	}
+	/* Clear epdisabled */
+	diepint = DWC_DIEPINT_EP_DISA_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, ep_num), diepint);
+
+}
+
+/**
+ * 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;
+	u32 dctl = 0;
+	u32 intr_mask = 0;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, ep_num);
+
+	/* Disable the NP Tx Fifo Empty Interrrupt */
+	if (!core_if->dma_enable) {
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32((u32)(core_if->core_global_regs) + DWC_GINTMSK,
+					intr_mask, 0);
+	}
+
+	/* Non-periodic EP */
+	/* Enable the Global IN NAK Effective Interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+	dwc_modify32((u32)(core_if->core_global_regs) + DWC_GINTMSK, 0, intr_mask);
+
+	/* Set Global IN NAK */
+	dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+	dwc_modify32(dev_ctl_reg(pcd), dctl, dctl);
+	ep->stopped = 1;
+
+	/* Clear timeout */
+	diepint = DWC_DIEPINT_TOUT_COND_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, ep_num), diepint);
+}
+
+/**
+ * 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)
+{
+	u32 diepint = 0;
+
+	if (!ep->stopped && num) {
+		u32 diepmsk = 0;
+
+		diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk,1);
+		dwc_modify32(dev_diepmsk_reg(pcd), diepmsk, 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 = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, num), diepint);
+}
+
+static void handle_in_ep_nak_effective_intr(struct dwc_pcd *pcd,
+			struct pcd_ep *ep, u32 num)
+{
+	u32 diepctl = 0;
+	u32 diepint = 0;
+
+	/* Periodic EP */
+	if (ep->disabling) {
+		diepctl = 0;
+		diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+		diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+		dwc_modify32(in_ep_ctl_reg(pcd, num), diepctl,
+					diepctl);
+	}
+	/* Clear inepnakeff */
+	diepint = DWC_DIEPINT_IN_EP_NAK_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, num), diepint);
+
+}
+
+/**
+ * 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_read32((u32)dev_if->dev_global_regs + DWC_DIEPMSK);
+	emp = dwc_read32((u32)dev_if->dev_global_regs + DWC_DTKNQR4FIFOEMPTYMSK);
+	msk |= ((emp >> ep->num) & 0x1) << 7;
+	v = dwc_read32((u32)dev_if->in_ep_regs[ep->num] + DWC_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_read32((u32)_if->dev_if->dev_global_regs + DWC_DAINT) &
+		dwc_read32((u32)_if->dev_if->dev_global_regs + DWC_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);
+	u32 diepint = 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) {
+			u32 c_diepint;
+
+			/* Get EP pointer */
+			ep = get_in_ep(pcd, epnum);
+			dwc_ep = &ep->dwc_ep;
+
+			diepint = dwc_otg_read_diep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (DWC_DIEPINT_TX_CMPL_RD(diepint))
+				handle_in_ep_xfr_complete_intr(pcd, ep, epnum);
+
+			/* Endpoint disable */
+			if (DWC_DIEPINT_EP_DISA_RD(diepint))
+				handle_in_ep_disable_intr(pcd, epnum);
+
+			/* AHB Error */
+			if (DWC_DIEPINT_AHB_ERROR_RD(diepint)) {
+				/* Clear ahberr */
+				c_diepint = 0;
+				c_diepint = DWC_DIEPINT_AHB_ERROR_RW(c_diepint, 1);
+				dwc_write32(in_ep_int_reg(pcd, epnum),
+					c_diepint);
+			}
+
+			/* TimeOUT Handshake (non-ISOC IN EPs) */
+			if (DWC_DIEPINT_TOUT_COND_RD(diepint))
+				handle_in_ep_timeout_intr(pcd, epnum);
+
+			/* IN Token received with TxF Empty */
+			if (DWC_DIEPINT_IN_TKN_TX_EMPTY_RD(diepint))
+				handle_in_ep_tx_fifo_empty_intr(pcd, ep, epnum);
+
+			/* IN Token Received with EP mismatch */
+			if (DWC_DIEPINT_IN_TKN_EP_MISS_RD(diepint)) {
+				/* Clear intknepmis */
+				c_diepint = 0;
+				c_diepint = DWC_DIEPINT_IN_TKN_EP_MISS_RW(c_diepint, 1);
+				dwc_write32(in_ep_int_reg(pcd, epnum),
+					c_diepint);
+			}
+
+			/* IN Endpoint NAK Effective */
+			if (DWC_DIEPINT_IN_EP_NAK_RD(diepint))
+				handle_in_ep_nak_effective_intr(pcd, ep, epnum);
+
+			/* IN EP Tx FIFO Empty Intr */
+			if (DWC_DIEPINT_TXFIFO_EMPTY_RD(diepint))
+				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_read32((u32)_if->dev_if->dev_global_regs + DWC_DAINT) &
+		dwc_read32((u32)_if->dev_if->dev_global_regs + DWC_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_read32((u32)dev_if->out_ep_regs[ep->num] + DWC_DOEPINT) &
+			dwc_read32((u32)dev_if->dev_global_regs + DWC_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;
+	u32 doepint = 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) {
+			u32 c_doepint = 0;
+
+			dwc_ep = &((get_out_ep(pcd, epnum))->dwc_ep);
+			doepint = dwc_otg_read_doep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (DWC_DOEPINT_TX_COMPL_RD(doepint)) {
+				/* Clear xfercompl */
+				c_doepint = 0;
+				c_doepint = DWC_DOEPMSK_TX_COMPL_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+						c_doepint);
+				if (epnum == 0)
+					handle_ep0(pcd);
+				else
+					complete_ep(get_out_ep(pcd, epnum));
+			}
+
+			/* Endpoint disable */
+			if (DWC_DOEPINT_EP_DISA_RD(doepint)) {
+				/* Clear epdisabled */
+				c_doepint = 0;
+				c_doepint = DWC_DOEPMSK_EP_DISA_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+						c_doepint);
+			}
+
+			/* AHB Error */
+			if (DWC_DOEPINT_AHB_ERROR_RD(doepint)) {
+				c_doepint = 0;
+				c_doepint = DWC_DOEPMSK_AHB_ERROR_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+						c_doepint);
+			}
+
+		    /* Setup Phase Done (control EPs) */
+		    if (DWC_DOEPINT_SETUP_DONE_RD(doepint)) {
+				c_doepint = 0;
+				c_doepint = DWC_DOEPMSK_SETUP_DONE_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+						c_doepint);
+				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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for IN ISOC "
+				"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_IN_ATX;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+				gintsts);
+	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)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for OUT ISOC "
+				"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_OUT_PTX;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+				gintsts);
+	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;
+	u32 diepctl = 0;
+	u32 diepctl_rd = 0;
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+	u32 i;
+
+	/* Disable all active IN EPs */
+	diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+	diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+	for (i = 0; i <= dev_if->num_in_eps; i++) {
+		diepctl_rd = dwc_read32(in_ep_ctl_reg(pcd, i));
+		if (DWC_DEPCTL_EPENA_RD(diepctl_rd))
+			dwc_write32(in_ep_ctl_reg(pcd, i), diepctl);
+	}
+
+	/* Disable the Global IN NAK Effective Interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_GLBL_IN_NAK;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+			gintsts);
+	return 1;
+}
+
+/**
+ * This function handles the Global OUT NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_out_nak_effective(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for Global IN "
+			"NAK Effective\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_OUT_NAK;
+	dwc_modify32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+				intr_mask, 0);
+
+	/* Clear goutnakeff */
+	gintsts |= DWC_INTSTS_GLBL_OUT_NAK;
+	dwc_write32((u32)(GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+				gintsts);
+	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);
+	u32 gintr_status;
+	int ret = 0;
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		spin_lock(&pcd->lock);
+
+		gintr_status = dwc_otg_read_core_intr(core_if);
+		if (!gintr_status) {
+			spin_unlock(&pcd->lock);
+			return 0;
+		}
+
+		if (gintr_status & DWC_INTSTS_STRT_OF_FRM)
+			ret |= dwc_otg_pcd_handle_sof_intr(pcd);
+		if (gintr_status & DWC_INTSTS_RXFIFO_NOT_EMPT)
+			ret |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+		if (gintr_status & DWC_INTSTS_NP_TXFIFO_EMPT)
+			ret |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+		if (gintr_status & DWC_INTSTS_GLBL_IN_NAK)
+			ret |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+		if (gintr_status & DWC_INTSTS_GLBL_OUT_NAK)
+			ret |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+		if (gintr_status & DWC_INTSTS_I2C_INTR)
+			ret |= dwc_otg_pcd_handle_i2c_intr(pcd);
+		if (gintr_status & DWC_INTSTS_EARLY_SUSP)
+			ret |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+		if (gintr_status & DWC_INTSTS_USB_RST)
+			ret |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ENUM_DONE)
+			ret |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ISYNC_OUTPKT_DRP)
+			ret |=
+			dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(pcd);
+		if (gintr_status & DWC_INTSTS_END_OF_PFRM)
+			ret |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ENDP_MIS_MTCH)
+			ret |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+		if (gintr_status & DWC_INTSTS_IN_ENDP)
+			ret |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+		if (gintr_status & DWC_INTSTS_OUT_ENDP)
+			ret |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+		if (gintr_status & DWC_INTSTS_INCMP_IN_ATX)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+		if (gintr_status & DWC_INTSTS_INCMP_OUT_PTX)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+
+		spin_unlock(&pcd->lock);
+	}
+	return ret;
+}
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V6 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
From: tmarri @ 2010-12-09  0:32 UTC (permalink / raw)
  To: linux-usb; +Cc: tmarri, Mark Miesfeld, linuxppc-dev, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Enable gadget support

Signed-off-by: Tirumala R Marri<tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/gadget/Kconfig        |   22 ++++++++++++++++++++++
 drivers/usb/gadget/gadget_chips.h |    8 ++++++++
 2 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 747b0d3..b2bcc4e 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
 	  This OTG-capable silicon IP is used in dual designs including
 	  the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
 
+# dwc_otg builds in ../dwc_otg along with host support
+config USB_GADGET_DWC_HDRC
+	boolean "DesignWare USB Peripheral"
+	depends on DWC_OTG_MODE || DWC_DEVICE_ONLY
+	select USB_GADGET_DUALSPEED
+	select USB_GADGET_SELECTED
+	select USB_OTG
+	help
+	This OTG-capable Designware USB IP
+
+config USB_OTG
+	boolean "OTG Support"
+	depends on USB_GADGET_DWC_HDRC
+	help
+	The most notable feature of USB OTG is support for a
+	"Dual-Role" device, which can act as either a device
+	or a host.  The initial role choice can be changed
+	later, when two dual-role devices talk to each other.
+	Select this only if your board has a Mini-AB connector.
+
+
+
 config USB_GADGET_M66592
 	boolean "Renesas M66592 USB Peripheral Controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index d7b3bbe..e008e07 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -142,6 +142,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#if defined(CONFIG_DWC_OTG_MODE) || defined(CONFIG_DWC_DEVICE_ONLY)
+#define gadget_is_dwc_otg_pcd(g)	(!strcmp("dwc_otg_pcd", (g)->name))
+#else
+#define gadget_is_dwc_otg_pcd(g)	0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -207,6 +213,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x26;
 	else if (gadget_is_pch(gadget))
 		return 0x27;
+	else if (gadget_is_dwc_otg_pcd(gadget))
+		return 0x28;
 	return -ENOENT;
 }
 
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V6 10/10] USB ppc4xx: Add Synopsys DWC OTG driver kernel configuration and Makefile
From: tmarri @ 2010-12-09  0:32 UTC (permalink / raw)
  To: linux-usb; +Cc: tmarri, Mark Miesfeld, linuxppc-dev, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Add Synopsys DesignWare HS USB OTG driver kernel configuration.
Synopsys OTG driver may operate in  host only, device only, or OTG mode.
The driver also allows user configure the core to use its internal DMA
or Slave (PIO) mode.

Signed-off-by: Tirumala R Marri<tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/Makefile             |    2 +
 drivers/usb/Kconfig          |    3 +-
 drivers/usb/dwc_otg/Kconfig  |   96 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/Makefile |   19 ++++++++
 4 files changed, 119 insertions(+), 1 deletions(-)

diff --git a/drivers/Makefile b/drivers/Makefile
index 14152fc..0ecbf42 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_UWB)		+= uwb/
 obj-$(CONFIG_USB_OTG_UTILS)	+= usb/otg/
 obj-$(CONFIG_USB)		+= usb/
 obj-$(CONFIG_USB_MUSB_HDRC)	+= usb/musb/
+obj-$(CONFIG_USB_DWC_OTG)      += usb/dwc_otg/
 obj-$(CONFIG_PCI)		+= usb/
 obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
 obj-$(CONFIG_SERIO)		+= input/serio/
@@ -105,6 +106,7 @@ obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
 ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
 obj-y				+= clocksource/
 endif
+obj-$(CONFIG_DMA_ENGINE)       += dma/
 obj-$(CONFIG_DCA)		+= dca/
 obj-$(CONFIG_HID)		+= hid/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 6585f0b..8b3623d 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -55,7 +55,6 @@ config USB_ARCH_HAS_OHCI
 config USB_ARCH_HAS_EHCI
 	boolean
 	default y if PPC_83xx
-	default y if PPC_MPC512x
 	default y if SOC_AU1200
 	default y if ARCH_IXP4XX
 	default y if ARCH_W90X900
@@ -113,6 +112,8 @@ source "drivers/usb/host/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
 
+source "drivers/usb/dwc_otg/Kconfig"
+
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/dwc_otg/Kconfig b/drivers/usb/dwc_otg/Kconfig
new file mode 100644
index 0000000..4d33d72
--- /dev/null
+++ b/drivers/usb/dwc_otg/Kconfig
@@ -0,0 +1,96 @@
+#
+# USB Dual Role (OTG-ready) Controller Drivers
+# for silicon based on Synopsys DesignWare IP
+#
+
+comment "Enable Host or Gadget support for DesignWare OTG controller"
+	depends on !USB && USB_GADGET=n
+
+config USB_DWC_OTG
+	depends on (USB || USB_GADGET)
+	select NOP_USB_XCEIV
+	select USB_OTG_UTILS
+	tristate "Synopsys DWC OTG Controller"
+	default USB_GADGET
+	help
+	  This driver provides USB Device Controller support for the
+	  Synopsys DesignWare USB OTG Core used on the AppliedMicro PowerPC SoC.
+
+config DWC_DEBUG
+	bool "Enable DWC Debugging"
+	depends on USB_DWC_OTG
+	default n
+	help
+	  Enable DWC driver debugging
+
+choice
+	prompt "DWC Mode Selection"
+	depends on USB_DWC_OTG
+	default DWC_HOST_ONLY
+	help
+	  Select the DWC Core in OTG, Host only, or Device only mode.
+
+config DWC_HOST_ONLY
+	bool "DWC Host Only Mode"
+
+config DWC_OTG_MODE
+	bool "DWC OTG Mode"
+	select USB_GADGET_SELECTED
+
+config DWC_DEVICE_ONLY
+	bool "DWC Device Only Mode"
+	select USB_GADGET_SELECTED
+
+endchoice
+
+# enable peripheral support (including with OTG)
+config USB_GADGET_DWC_HDRC
+	bool
+	depends on USB_DWC_OTG && (DWC_DEVICE_ONLY || USB_DWC_OTG)
+
+choice
+	prompt "DWC DMA/SlaveMode Selection"
+	depends on USB_DWC_OTG
+	default DWC_DMA_MODE
+	help
+	  Select the DWC DMA or Slave Mode.
+	  DMA mode uses the DWC core internal DMA engines.
+	  Slave mode uses the processor PIO to tranfer data.
+	  In Slave mode, processor's DMA channels can be used if available.
+
+config DWC_SLAVE
+	bool "DWC Slave Mode"
+
+config DWC_DMA_MODE
+	bool "DWC DMA Mode"
+
+endchoice
+
+config USB_OTG_WHITELIST
+	bool "Rely on OTG Targeted Peripherals List"
+	depends on !USB_SUSPEND && USB_DWC_OTG
+	default n
+	help
+	  This is the same flag as in ../core/Kconfig.
+	  It is here for easy deselect.
+
+config DWC_OTG_REG_LE
+	depends on USB_DWC_OTG
+	bool "DWC Little Endian Register"
+	default y
+	help
+	  OTG core register access is Little-Endian.
+
+config DWC_OTG_FIFO_LE
+	depends on USB_DWC_OTG
+	bool "DWC FIFO Little Endian"
+	default n
+	help
+	  OTG core FIFO access is Little-Endian.
+
+config DWC_LIMITED_XFER_SIZE
+	depends on USB_GADGET_DWC_HDRC
+	bool "DWC Endpoint Limited Xfer Size"
+	default n
+	help
+	  Bit fields in the Device EP Transfer Size Register is 11 bits.
diff --git a/drivers/usb/dwc_otg/Makefile b/drivers/usb/dwc_otg/Makefile
new file mode 100644
index 0000000..31dd5e8
--- /dev/null
+++ b/drivers/usb/dwc_otg/Makefile
@@ -0,0 +1,19 @@
+#
+# OTG infrastructure and transceiver drivers
+#
+obj-$(CONFIG_USB_DWC_OTG)	+= dwc_otg.o
+
+dwc_otg-objs := dwc_otg_cil.o dwc_otg_cil_intr.o dwc_otg_param.o
+
+ifeq ($(CONFIG_4xx_SOC),y)
+dwc_otg-objs += dwc_otg_apmppc.o
+endif
+
+ifneq ($(CONFIG_DWC_DEVICE_ONLY),y)
+dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_intr.o \
+		dwc_otg_hcd_queue.o
+endif
+
+ifneq ($(CONFIG_DWC_HOST_ONLY),y)
+dwc_otg-objs += dwc_otg_pcd.o dwc_otg_pcd_intr.o
+endif
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH] ppc4xx: Add USB DWC DTS entry to Canyonlands board
From: tmarri @ 2010-12-09  0:49 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: tmarri

From: Tirumala Marri <tmarri@apm.com>

Add Synopsys Designware DTS entry for 460EX based Canyonlands board.

Signed-off-by: Tirumala R Marri<tmarri@apm.com>
---
 arch/powerpc/boot/dts/canyonlands.dts |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 5b27a4b..54caec6 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -172,6 +172,19 @@
 			interrupts = <0x1e 4>;
 		};
 
+		USBOTG0: usbotg@bff80000 {
+			compatible = "amcc,dwc-otg";
+			reg = <0x4 0xbff80000 0x10000>;
+			interrupt-parent = <&USBOTG0>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x1 0x2>;
+			interrupt-map = </* USB-OTG */ 0x0 &UIC2 0x1c 0x4
+					 /* HIGH-POWER */ 0x1 &UIC1 0x1a 0x8
+					 /* DMA */ 0x2 &UIC0 0xc 0x4>;
+		};
+
 		SATA0: sata@bffd1000 {
 			compatible = "amcc,sata-460ex";
 			reg = <4 0xbffd1000 0x800 4 0xbffd0800 0x400>;
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH] ppc4xx: Add USB DWC DTS entry to Canyonlands board
From: tmarri @ 2010-12-09  0:50 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: tmarri

From: Tirumala Marri <tmarri@apm.com>

Add Synopsys Designware DTS entry for 460EX based Canyonlands board.

Signed-off-by: Tirumala R Marri<tmarri@apm.com>
---
 arch/powerpc/boot/dts/canyonlands.dts |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 5b27a4b..54caec6 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -172,6 +172,19 @@
 			interrupts = <0x1e 4>;
 		};
 
+		USBOTG0: usbotg@bff80000 {
+			compatible = "amcc,dwc-otg";
+			reg = <0x4 0xbff80000 0x10000>;
+			interrupt-parent = <&USBOTG0>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x1 0x2>;
+			interrupt-map = </* USB-OTG */ 0x0 &UIC2 0x1c 0x4
+					 /* HIGH-POWER */ 0x1 &UIC1 0x1a 0x8
+					 /* DMA */ 0x2 &UIC0 0xc 0x4>;
+		};
+
 		SATA0: sata@bffd1000 {
 			compatible = "amcc,sata-460ex";
 			reg = <4 0xbffd1000 0x800 4 0xbffd0800 0x400>;
-- 
1.6.1.rc3

^ permalink raw reply related

* Re: [PATCH 3/5] of/device: Make of_get_next_child() check status properties
From: Michael Ellerman @ 2010-12-09  1:33 UTC (permalink / raw)
  To: Scott Wood; +Cc: devicetree-discuss, linuxppc-dev, Deepak Saxena
In-Reply-To: <20101208150102.69b8062b@udp111988uds.am.freescale.net>

[-- Attachment #1: Type: text/plain, Size: 2048 bytes --]

On Wed, 2010-12-08 at 15:01 -0600, Scott Wood wrote:
> On Wed, 8 Dec 2010 11:29:44 -0800
> Deepak Saxena <deepak_saxena@mentor.com> wrote:
> 
> > We only return the next child if the device is available.
> > 
> > Signed-off-by: Hollis Blanchard <hollis_blanchard@mentor.com>
> > Signed-off-by: Deepak Saxena <deepak_saxena@mentor.com>
> > ---
> >  drivers/of/base.c |    4 +++-
> >  1 files changed, 3 insertions(+), 1 deletions(-)
> > 
> > diff --git a/drivers/of/base.c b/drivers/of/base.c
> > index 5d269a4..81b2601 100644
> > --- a/drivers/of/base.c
> > +++ b/drivers/of/base.c
> > @@ -321,6 +321,8 @@ struct device_node *of_get_next_parent(struct device_node *node)
> >   *
> >   *	Returns a node pointer with refcount incremented, use
> >   *	of_node_put() on it when done.
> > + *
> > + *	Does not return nodes marked unavailable by a status property.
> >   */
> >  struct device_node *of_get_next_child(const struct device_node *node,
> >  	struct device_node *prev)
> > @@ -330,7 +332,7 @@ struct device_node *of_get_next_child(const struct device_node *node,
> >  	read_lock(&devtree_lock);
> >  	next = prev ? prev->sibling : node->child;
> >  	for (; next; next = next->sibling)
> > -		if (of_node_get(next))
> > +		if (of_device_is_available(next) && of_node_get(next))
> >  			break;
> >  	of_node_put(prev);
> >  	read_unlock(&devtree_lock);
> 
> This seems like too low-level a place to put this.  Some code may know
> how to un-disable a device in certain situations, or it may be part of
> debug code trying to dump the whole device tree, etc.  Looking
> further[1], I see a raw version of this function, but not other things
> like of_find_compatible_node.

Yeah I agree. I think we'll eventually end up with __ versions of all or
lots of them. Not to mention there might be cases you've missed where
code expects to see unavailable nodes. The right approach is to add
_new_ routines that don't return unavailable nodes, and convert code
that you know wants to use them.

cheers


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH 3/5] of/device: Make of_get_next_child() check status properties
From: David Gibson @ 2010-12-09  3:09 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: Scott Wood, devicetree-discuss, linuxppc-dev
In-Reply-To: <1291858402.2962.2.camel@concordia>

[-- Attachment #1: Type: text/plain, Size: 2729 bytes --]

On Thu, Dec 09, 2010 at 12:33:22PM +1100, Michael Ellerman wrote:
> On Wed, 2010-12-08 at 15:01 -0600, Scott Wood wrote:
> > On Wed, 8 Dec 2010 11:29:44 -0800
> > Deepak Saxena <deepak_saxena@mentor.com> wrote:
> > 
> > > We only return the next child if the device is available.
> > > 
> > > Signed-off-by: Hollis Blanchard <hollis_blanchard@mentor.com>
> > > Signed-off-by: Deepak Saxena <deepak_saxena@mentor.com>
> > > ---
> > >  drivers/of/base.c |    4 +++-
> > >  1 files changed, 3 insertions(+), 1 deletions(-)
> > > 
> > > diff --git a/drivers/of/base.c b/drivers/of/base.c
> > > index 5d269a4..81b2601 100644
> > > --- a/drivers/of/base.c
> > > +++ b/drivers/of/base.c
> > > @@ -321,6 +321,8 @@ struct device_node *of_get_next_parent(struct device_node *node)
> > >   *
> > >   *	Returns a node pointer with refcount incremented, use
> > >   *	of_node_put() on it when done.
> > > + *
> > > + *	Does not return nodes marked unavailable by a status property.
> > >   */
> > >  struct device_node *of_get_next_child(const struct device_node *node,
> > >  	struct device_node *prev)
> > > @@ -330,7 +332,7 @@ struct device_node *of_get_next_child(const struct device_node *node,
> > >  	read_lock(&devtree_lock);
> > >  	next = prev ? prev->sibling : node->child;
> > >  	for (; next; next = next->sibling)
> > > -		if (of_node_get(next))
> > > +		if (of_device_is_available(next) && of_node_get(next))
> > >  			break;
> > >  	of_node_put(prev);
> > >  	read_unlock(&devtree_lock);
> > 
> > This seems like too low-level a place to put this.  Some code may know
> > how to un-disable a device in certain situations, or it may be part of
> > debug code trying to dump the whole device tree, etc.  Looking
> > further[1], I see a raw version of this function, but not other things
> > like of_find_compatible_node.
> 
> Yeah I agree. I think we'll eventually end up with __ versions of all or
> lots of them. Not to mention there might be cases you've missed where
> code expects to see unavailable nodes. The right approach is to add
> _new_ routines that don't return unavailable nodes, and convert code
> that you know wants to use them.

Actually, I don't think we really want these status-skipping
iterators at all.  The device tree iterators should give us the device
tree, as it is.  Those old-style drivers which seach for a node rather
than using the bus probing logic can keep individual checks of the
status property until they're converted to the new scheme.


-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: Run 'usermode-agent' cause kernel panic on Powerpc
From: Benjamin Herrenschmidt @ 2010-12-09  3:26 UTC (permalink / raw)
  To: xufeng zhang; +Cc: Linuxppc-dev, Dave Kleikamp
In-Reply-To: <4CFEDD4D.5020205@windriver.com>

On Wed, 2010-12-08 at 09:20 +0800, xufeng zhang wrote:

> > I believe it would have such an impact.  I don't see that user-mode
> > debugging would be enabled at all.
> >
> > Maybe something like this untested patch:
> >
> > diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> > index 84906d3..0e7d1cf 100644
> > --- a/arch/powerpc/kernel/process.c
> > +++ b/arch/powerpc/kernel/process.c
> > @@ -323,6 +323,13 @@ static void set_debug_reg_defaults(struct thread_struct *thread)
> >
> >   static void prime_debug_regs(struct thread_struct *thread)
> >   {
> > +	/*
> > +	 * If we're setting up debug events for user space, make sure they
> > +	 * don't fire in kernel space before we get to user space
> > +	 */
> > +	if (thread->dbcr0&  DBCR0_IDM)
> > +		mtmsr(mfmsr()&  ~MSR_DE);
> > +
> >   	mtspr(SPRN_IAC1, thread->iac1);
> >   	mtspr(SPRN_IAC2, thread->iac2);
> >   #if CONFIG_PPC_ADV_DEBUG_IACS>  2
> >
> >    
> Thanks for your reply, Dave, I know where the problem is.

So is there a kernel bug we need to fix ?

Cheers,
Ben.

^ permalink raw reply

* Re: [RFC PATCH 7/7 v2] ppc: add dynamic dma window support
From: Benjamin Herrenschmidt @ 2010-12-09  4:17 UTC (permalink / raw)
  To: Nishanth Aravamudan
  Cc: sonnyrao, miltonm, Paul Mackerras, Anton Blanchard, linuxppc-dev
In-Reply-To: <1288150518-4026-8-git-send-email-nacc@us.ibm.com>

On Tue, 2010-10-26 at 20:35 -0700, Nishanth Aravamudan wrote:

No much comments... I'm amazed how complex he firmware folks managed to
make this ... 

>  static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
>  {
>  	int err = NOTIFY_OK;
>  	struct device_node *np = node;
>  	struct pci_dn *pci = PCI_DN(np);
> +	struct direct_window *window;
>  
>  	switch (action) {
>  	case PSERIES_RECONFIG_REMOVE:
>  		if (pci && pci->iommu_table)
>  			iommu_free_table(pci->iommu_table, np->full_name);
> +
> +		spin_lock(&direct_window_list_lock);
> +		list_for_each_entry(window, &direct_window_list, list) {
> +			if (window->device == np) {
> +				list_del(&window->list);
> +				break;
> +			}
> +		}
> +		spin_unlock(&direct_window_list_lock);

Should you also kfree the window ?


Cheers,
Ben.

^ permalink raw reply

* [PATCH v5] ppc44x:PHY fixup for USB on canyonlands board
From: Rupjyoti Sarmah @ 2010-12-09  4:24 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: rsarmah

This fix is a reset for USB PHY that requires some amount of time for power to be stable on Canyonlands.

Signed-off-by: Rupjyoti Sarmah <rsarmah@apm.com>
---
changes from previous version:
-- dts node correction, removed the address and size parameters
-- Kconfig file updated removing dependency on PPC44x_SIMPLE
-- 44x prefixes in the function names changed
-- Error paths updated in canyonlands.c

 arch/powerpc/boot/dts/canyonlands.dts      |   11 +++
 arch/powerpc/platforms/44x/44x.h           |    4 +
 arch/powerpc/platforms/44x/Kconfig         |    1 -
 arch/powerpc/platforms/44x/Makefile        |    1 +
 arch/powerpc/platforms/44x/canyonlands.c   |  125 ++++++++++++++++++++++++++++
 arch/powerpc/platforms/44x/ppc44x_simple.c |    1 -
 6 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100644 arch/powerpc/platforms/44x/canyonlands.c

diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index a303703..8ff1f3f 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -224,6 +224,11 @@
 					};
 				};
 
+				cpld@2,0 {
+					compatible = "amcc,ppc460ex-bcsr";
+					reg = <2 0x0 0x9>;
+				};
+
 				ndfc@3,0 {
 					compatible = "ibm,ndfc";
 					reg = <0x00000003 0x00000000 0x00002000>;
@@ -320,6 +325,12 @@
 				interrupts = <0x3 0x4>;
 			};
 
+			GPIO0: gpio@ef600b00 {
+				compatible = "ibm,ppc4xx-gpio";
+				reg = <0xef600b00 0x00000048>;
+				gpio-controller;
+			};
+
 			ZMII0: emac-zmii@ef600d00 {
 				compatible = "ibm,zmii-460ex", "ibm,zmii";
 				reg = <0xef600d00 0x0000000c>;
diff --git a/arch/powerpc/platforms/44x/44x.h b/arch/powerpc/platforms/44x/44x.h
index dbc4d2b..63f703e 100644
--- a/arch/powerpc/platforms/44x/44x.h
+++ b/arch/powerpc/platforms/44x/44x.h
@@ -4,4 +4,8 @@
 extern u8 as1_readb(volatile u8 __iomem  *addr);
 extern void as1_writeb(u8 data, volatile u8 __iomem *addr);
 
+#define GPIO0_OSRH	0xC
+#define GPIO0_TSRH	0x14
+#define GPIO0_ISR1H	0x34
+
 #endif /* __POWERPC_PLATFORMS_44X_44X_H */
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 0f979c5..f485fc5 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -115,7 +115,6 @@ config CANYONLANDS
 	bool "Canyonlands"
 	depends on 44x
 	default n
-	select PPC44x_SIMPLE
 	select 460EX
 	select PCI
 	select PPC4xx_PCI_EXPRESS
diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile
index 82ff326..6854e73 100644
--- a/arch/powerpc/platforms/44x/Makefile
+++ b/arch/powerpc/platforms/44x/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_WARP)	+= warp.o
 obj-$(CONFIG_XILINX_VIRTEX_5_FXT) += virtex.o
 obj-$(CONFIG_XILINX_ML510) += virtex_ml510.o
 obj-$(CONFIG_ISS4xx)	+= iss4xx.o
+obj-$(CONFIG_CANYONLANDS)+= canyonlands.o
diff --git a/arch/powerpc/platforms/44x/canyonlands.c b/arch/powerpc/platforms/44x/canyonlands.c
new file mode 100644
index 0000000..9639709
--- /dev/null
+++ b/arch/powerpc/platforms/44x/canyonlands.c
@@ -0,0 +1,125 @@
+/*
+ * This contain platform specific code for APM PPC460EX based Canyonlands
+ * board.
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Author: Rupjyoti Sarmah <rsarmah@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc4xx.h>
+#include <asm/udbg.h>
+#include <asm/uic.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include "44x.h"
+
+#define BCSR_USB_EN	0x11
+
+static __initdata struct of_device_id ppc460ex_of_bus[] = {
+	{ .compatible = "ibm,plb4", },
+	{ .compatible = "ibm,opb", },
+	{ .compatible = "ibm,ebc", },
+	{ .compatible = "simple-bus", },
+	{},
+};
+
+static int __init ppc460ex_device_probe(void)
+{
+	of_platform_bus_probe(NULL, ppc460ex_of_bus, NULL);
+
+	return 0;
+}
+machine_device_initcall(canyonlands, ppc460ex_device_probe);
+
+/* Using this code only for the Canyonlands board.  */
+
+static int __init ppc460ex_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+	if (of_flat_dt_is_compatible(root, "amcc,canyonlands")) {
+		ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+		return 1;
+		}
+	return 0;
+}
+
+/* USB PHY fixup code on Canyonlands kit. */
+
+static int __init ppc460ex_canyonlands_fixup(void)
+{
+	u8 __iomem *bcsr ;
+	void __iomem *vaddr;
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_find_compatible_node(NULL, NULL, "amcc,ppc460ex-bcsr");
+	if (!np) {
+		printk(KERN_ERR "failed did not find amcc, ppc460ex bcsr node\n");
+		return -ENODEV;
+	}
+
+	bcsr = of_iomap(np, 0);
+	of_node_put(np);
+
+	if (!bcsr) {
+		printk(KERN_CRIT "Could not remap bcsr\n");
+		ret = -ENODEV;
+	}
+
+	np = of_find_compatible_node(NULL, NULL, "ibm,ppc4xx-gpio");
+	vaddr = of_iomap(np, 0);
+	if (!vaddr) {
+		printk(KERN_CRIT "Could not get gpio node address\n");
+		ret = -ENODEV;
+		goto err_bcsr;
+	}
+	/* Disable USB, through the BCSR7 bits */
+	setbits8(&bcsr[7], BCSR_USB_EN);
+
+	/* Wait for a while after reset */
+	msleep(100);
+
+	/* Enable USB here */
+	clrbits8(&bcsr[7], BCSR_USB_EN);
+
+	/*
+	 * Configure multiplexed gpio16 and gpio19 as alternate1 output
+	 * source after USB reset. In this configuration gpio16 will be
+	 * USB2HStop and gpio19 will be USB2DStop. For more details refer to
+	 * table 34-7 of PPC460EX user manual.
+	 */
+	setbits32((vaddr + GPIO0_OSRH), 0x42000000);
+	setbits32((vaddr + GPIO0_TSRH), 0x42000000);
+	of_node_put(np);
+err_bcsr:
+	iounmap(bcsr);
+	return ret;
+}
+machine_device_initcall(canyonlands, ppc460ex_canyonlands_fixup);
+define_machine(canyonlands) {
+	.name = "Canyonlands",
+	.probe = ppc460ex_probe,
+	.progress = udbg_progress,
+	.init_IRQ = uic_init_tree,
+	.get_irq = uic_get_irq,
+	.restart = ppc4xx_reset_system,
+	.calibrate_decr = generic_calibrate_decr,
+};
diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c
index 7ddcba3..c81c19c 100644
--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
@@ -53,7 +53,6 @@ static char *board[] __initdata = {
 	"amcc,arches",
 	"amcc,bamboo",
 	"amcc,bluestone",
-	"amcc,canyonlands",
 	"amcc,glacier",
 	"ibm,ebony",
 	"amcc,eiger",
-- 
1.5.6.3

^ permalink raw reply related

* Re: [RFC PATCH 6/7 v2] ppc/iommu: pass phb only to iommu_table_setparms_lpar
From: Benjamin Herrenschmidt @ 2010-12-09  4:24 UTC (permalink / raw)
  To: Nishanth Aravamudan
  Cc: sonnyrao, miltonm, Paul Mackerras, Anton Blanchard, linuxppc-dev
In-Reply-To: <1288150518-4026-7-git-send-email-nacc@us.ibm.com>

On Tue, 2010-10-26 at 20:35 -0700, Nishanth Aravamudan wrote:
> iommu_table_setparms_lpar needs either the phb or the subbusnumber
> (not both), pass the phb to make it similar to iommu_table_setparms.
> 
> Note: In cases where a caller was passing bus->number previously to
> iommu_table_setparms_lpar() rather than phb->bus->number, this can lead
> to a different value in tbl->it_busno. The only example of this was the
> removed pci_dma_dev_setup_pSeriesLP(), removed in "ppc/iommu: remove
> unneeded pci_dma_dev_setup_pSeriesLP".
> 
> Signed-off-by: Milton Miller <miltonm@bga.com>
> Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
> ---
>  arch/powerpc/platforms/pseries/iommu.c |    8 +++-----
>  1 files changed, 3 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
> index 9d564b9..45c6865 100644
> --- a/arch/powerpc/platforms/pseries/iommu.c
> +++ b/arch/powerpc/platforms/pseries/iommu.c
> @@ -323,14 +323,13 @@ static void iommu_table_setparms(struct pci_controller *phb,
>  static void iommu_table_setparms_lpar(struct pci_controller *phb,
>  				      struct device_node *dn,
>  				      struct iommu_table *tbl,
> -				      const void *dma_window,
> -				      int bussubno)
> +				      const void *dma_window)
>  {
>  	unsigned long offset, size;
>  
> -	tbl->it_busno  = bussubno;
>  	of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size);
>  
> +	tbl->it_busno = phb->bus->number;
>  	tbl->it_base   = 0;
>  	tbl->it_blocksize  = 16;
>  	tbl->it_type = TCE_PCI;
> @@ -534,8 +533,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
>  	if (!pci->iommu_table) {
>  		tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
>  				   pci->phb->node);
> -		iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window,
> -			pci->phb->bus->number);
> +		iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
>  		pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
>  		pr_debug("  created table: %p\n", pci->iommu_table);
>  	} else {

There's another caller :-) I've fixed that up locally and will push with
the fix.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH v2] powerpc, 5200: add support for charon board
From: Heiko Schocher @ 2010-12-09  6:43 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: linuxppc-dev
In-Reply-To: <20101208154911.GE4641@pengutronix.de>

Hello Wolfram,

Wolfram Sang wrote:
> On Tue, Dec 07, 2010 at 07:58:55AM +0100, Heiko Schocher wrote:
>> Signed-off-by: Heiko Schocher <hs@denx.de>
> 
> Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>

Thanks!

One more defconfig question:

Is it possible to add to the mpc5200_defconfig, what is needed for
this board to compile a kernel which contains all features it needs?

I need:

CONFIG_HWMON
CONFIG_SENSORS_LM80
CONFIG_RTC_LIB
CONFIG_RTC_CLASS
CONFIG_RTC_HCTOSYS
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
CONFIG_RTC_INTF_SYSFS
CONFIG_RTC_INTF_PROC
CONFIG_RTC_INTF_DEV
CONFIG_RTC_DRV_DS1374

I think, to have a DTS in kernel for a board, and no defconfig, which
builds a kernel with all features for it, is suboptimal, or?

bye,
Heiko
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply

* Re: [PATCH V6 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: Greg KH @ 2010-12-09  6:47 UTC (permalink / raw)
  To: tmarri; +Cc: linux-usb, linuxppc-dev
In-Reply-To: <1291854539-23879-1-git-send-email-tmarri@apm.com>

On Wed, Dec 08, 2010 at 04:28:59PM -0800, tmarri@apm.com wrote:
> From: Tirumala Marri <tmarri@apm.com>
> 
> v6:
>  1. Replaced register definitions and bit fields with macros.
>  2. Replace printks with dev_dbg or dev_err functions.
>  3. Cleanup some assignments.
>  4. Remove chip specific selections in Kconfig file.

Much nicer, thanks.

Do you wish for me to apply this to the tree if it passes review?

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH 1/2] video, sm501: add OF binding to support SM501
From: Heiko Schocher @ 2010-12-09  6:49 UTC (permalink / raw)
  To: Paul Mundt
  Cc: linux-fbdev, devicetree-discuss, linuxppc-dev, Samuel Ortiz,
	Ben Dooks
In-Reply-To: <20101208053635.GB31508@linux-sh.org>

Hello Paul,

Paul Mundt wrote:
> On Sat, Dec 04, 2010 at 09:23:47AM +0100, Heiko Schocher wrote:
>> - add binding to OF, compatible name "smi,sm501"
>>
[...]
>>  Documentation/kernel-parameters.txt          |    7 +
>>  Documentation/powerpc/dts-bindings/sm501.txt |   30 +++
>>  drivers/mfd/sm501.c                          |  141 ++++++++------
>>  drivers/video/sm501fb.c                      |  264 +++++++++++++++++---------
>>  include/linux/sm501.h                        |    8 +
>>  5 files changed, 299 insertions(+), 151 deletions(-)
>>  create mode 100644 Documentation/powerpc/dts-bindings/sm501.txt
>>
> Given that this is all SM501 dependent, is there some particular reason
> why you neglected to Cc the author or the MFD folks?

Hups, sorry! No, there is no reason, thanks for detecting this.

Hmm.. couldn;t find a MFD maillinglist?

bye,
Heiko
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply

* Re: [PATCH] powerpc: iommu: Add device name to iommu error printks
From: Olof Johansson @ 2010-12-09  7:07 UTC (permalink / raw)
  To: Anton Blanchard; +Cc: linuxppc-dev
In-Reply-To: <20101208113605.38ac68a9@kryten>

On Wed, Dec 08, 2010 at 11:36:05AM +1100, Anton Blanchard wrote:
> 
> Right now its difficult to see which device is running out of iommu space:
> 
> iommu_alloc failed, tbl c00000076e096660 vaddr c000000768806600 npages 1
> 
> Use dev_info() so we get the device name and location:
> 
> ipr 0000:00:01.0: iommu_alloc failed, tbl c00000076e096660 vaddr c000000768806600 npages 1
> 
> Signed-off-by: Anton Blanchard <anton@samba.org> 

Acked-by: Olof Johansson <olof@lixom.net>

^ permalink raw reply

* [PATCH] fsldma: fix issue of slow dma
From: Li Yang @ 2010-12-09  8:14 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linuxppc-dev, linux-kernel

From: Forrest Shi <b29237@freescale.com>

Fixed fsl dma slow issue by initializing dma mode register with
bandwidth control. It boosts dma performance and should works
with 85xx board.

Signed-off-by: Forrest Shi <b29237@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
 drivers/dma/fsldma.c |    6 ++++--
 drivers/dma/fsldma.h |    9 ++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 286c3ac..e5e172d 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -50,9 +50,11 @@ static void dma_init(struct fsldma_chan *chan)
 		 * EIE - Error interrupt enable
 		 * EOSIE - End of segments interrupt enable (basic mode)
 		 * EOLNIE - End of links interrupt enable
+		 * BWC - Bandwidth sharing among channels
 		 */
-		DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EIE
-				| FSL_DMA_MR_EOLNIE | FSL_DMA_MR_EOSIE, 32);
+		DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC
+				| FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE
+				| FSL_DMA_MR_EOSIE, 32);
 		break;
 	case FSL_DMA_IP_83XX:
 		/* Set the channel to below modes:
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index cb4d6ff..ba9f403 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved.
  *
  * Author:
  *   Zhang Wei <wei.zhang@freescale.com>, Jul 2007
@@ -36,6 +36,13 @@
 #define FSL_DMA_MR_DAHE		0x00002000
 #define FSL_DMA_MR_SAHE		0x00001000
 
+/*
+ * Bandwidth/pause control determines how many bytes a given
+ * channel is allowed to transfer before the DMA engine pauses
+ * the current channel and switches to the next channel
+ */
+#define FSL_DMA_MR_BWC         0x08000000
+
 /* Special MR definition for MPC8349 */
 #define FSL_DMA_MR_EOTIE	0x00000080
 #define FSL_DMA_MR_PRC_RM	0x00000800
-- 
1.6.6-rc1.GIT

^ permalink raw reply related

* Re: [PATCH v2] powerpc, 5200: add support for charon board
From: Wolfram Sang @ 2010-12-09 10:39 UTC (permalink / raw)
  To: Heiko Schocher; +Cc: linuxppc-dev
In-Reply-To: <4D007AA8.9050101@denx.de>

[-- Attachment #1: Type: text/plain, Size: 354 bytes --]

> Is it possible to add to the mpc5200_defconfig, what is needed for
> this board to compile a kernel which contains all features it needs?

Just add it as modules.

Regards,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH V6 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
From: Sergei Shtylyov @ 2010-12-09 13:24 UTC (permalink / raw)
  To: tmarri; +Cc: Mark Miesfeld, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1291854755-25103-1-git-send-email-tmarri@apm.com>

Hello.

On 09-12-2010 3:32, tmarri@apm.com wrote:

> From: Tirumala Marri<tmarri@apm.com>

> Enable gadget support

> Signed-off-by: Tirumala R Marri<tmarri@apm.com>
> Signed-off-by: Fushen Chen<fchen@apm.com>
> Signed-off-by: Mark Miesfeld<mmiesfeld@apm.com>
[...]

> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 747b0d3..b2bcc4e 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
[...]
> +config USB_OTG
> +	boolean "OTG Support"

    This symbol is already defined in drivers/usb/core/Kconfig.

WBR, Sergei

^ permalink raw reply

* Re: [PATCH 1/2] video, sm501: add OF binding to support SM501
From: Samuel Ortiz @ 2010-12-09 15:03 UTC (permalink / raw)
  To: Heiko Schocher
  Cc: linux-fbdev, Paul Mundt, linuxppc-dev, Ben Dooks,
	devicetree-discuss
In-Reply-To: <4D007C09.3040308@denx.de>

Hi Heiko,

On Thu, Dec 09, 2010 at 07:49:45AM +0100, Heiko Schocher wrote:
> Hello Paul,
> 
> Paul Mundt wrote:
> > On Sat, Dec 04, 2010 at 09:23:47AM +0100, Heiko Schocher wrote:
> >> - add binding to OF, compatible name "smi,sm501"
> >>
> [...]
> >>  Documentation/kernel-parameters.txt          |    7 +
> >>  Documentation/powerpc/dts-bindings/sm501.txt |   30 +++
> >>  drivers/mfd/sm501.c                          |  141 ++++++++------
> >>  drivers/video/sm501fb.c                      |  264 +++++++++++++++++---------
> >>  include/linux/sm501.h                        |    8 +
> >>  5 files changed, 299 insertions(+), 151 deletions(-)
> >>  create mode 100644 Documentation/powerpc/dts-bindings/sm501.txt
> >>
> > Given that this is all SM501 dependent, is there some particular reason
> > why you neglected to Cc the author or the MFD folks?
> 
> Hups, sorry! No, there is no reason, thanks for detecting this.
> 
> Hmm.. couldn;t find a MFD maillinglist?
We use lkml. Could you please re-send the patch to me ?

Cheers,
Samuel.

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply

* Re: [RFC PATCH 6/7 v2] ppc/iommu: pass phb only to iommu_table_setparms_lpar
From: Nishanth Aravamudan @ 2010-12-09 16:16 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: linuxppc-dev, sonnyrao, Paul Mackerras, Anton Blanchard, miltonm
In-Reply-To: <1291868679.16694.221.camel@pasglop>

On 09.12.2010 [15:24:39 +1100], Benjamin Herrenschmidt wrote:
> On Tue, 2010-10-26 at 20:35 -0700, Nishanth Aravamudan wrote:
> > iommu_table_setparms_lpar needs either the phb or the subbusnumber
> > (not both), pass the phb to make it similar to iommu_table_setparms.
> > 
> > Note: In cases where a caller was passing bus->number previously to
> > iommu_table_setparms_lpar() rather than phb->bus->number, this can lead
> > to a different value in tbl->it_busno. The only example of this was the
> > removed pci_dma_dev_setup_pSeriesLP(), removed in "ppc/iommu: remove
> > unneeded pci_dma_dev_setup_pSeriesLP".
> > 
> > Signed-off-by: Milton Miller <miltonm@bga.com>
> > Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
> > ---
> >  arch/powerpc/platforms/pseries/iommu.c |    8 +++-----
> >  1 files changed, 3 insertions(+), 5 deletions(-)
> > 
> > diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
> > index 9d564b9..45c6865 100644
> > --- a/arch/powerpc/platforms/pseries/iommu.c
> > +++ b/arch/powerpc/platforms/pseries/iommu.c
> > @@ -323,14 +323,13 @@ static void iommu_table_setparms(struct pci_controller *phb,
> >  static void iommu_table_setparms_lpar(struct pci_controller *phb,
> >  				      struct device_node *dn,
> >  				      struct iommu_table *tbl,
> > -				      const void *dma_window,
> > -				      int bussubno)
> > +				      const void *dma_window)
> >  {
> >  	unsigned long offset, size;
> >  
> > -	tbl->it_busno  = bussubno;
> >  	of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size);
> >  
> > +	tbl->it_busno = phb->bus->number;
> >  	tbl->it_base   = 0;
> >  	tbl->it_blocksize  = 16;
> >  	tbl->it_type = TCE_PCI;
> > @@ -534,8 +533,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
> >  	if (!pci->iommu_table) {
> >  		tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
> >  				   pci->phb->node);
> > -		iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window,
> > -			pci->phb->bus->number);
> > +		iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
> >  		pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
> >  		pr_debug("  created table: %p\n", pci->iommu_table);
> >  	} else {
> 
> There's another caller :-) I've fixed that up locally and will push with
> the fix.

Shoot! Thanks for catching that.

-Nish

-- 
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center

^ permalink raw reply

* Re: [PATCH V6 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: Tirumala Marri @ 2010-12-09 16:45 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linuxppc-dev
In-Reply-To: <20101209064737.GA10820@kroah.com>

[-- Attachment #1: Type: text/plain, Size: 551 bytes --]

Yes please.
Regards,
Marri

On Wed, Dec 8, 2010 at 10:47 PM, Greg KH <greg@kroah.com> wrote:

> On Wed, Dec 08, 2010 at 04:28:59PM -0800, tmarri@apm.com wrote:
> > From: Tirumala Marri <tmarri@apm.com>
> >
> > v6:
> >  1. Replaced register definitions and bit fields with macros.
> >  2. Replace printks with dev_dbg or dev_err functions.
> >  3. Cleanup some assignments.
> >  4. Remove chip specific selections in Kconfig file.
>
> Much nicer, thanks.
>
> Do you wish for me to apply this to the tree if it passes review?
>
> thanks,
>
> greg k-h
>

[-- Attachment #2: Type: text/html, Size: 970 bytes --]

^ permalink raw reply

* Re: [PATCH V6 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: Tirumala Marri @ 2010-12-09 16:47 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linuxppc-dev
In-Reply-To: <20101209064737.GA10820@kroah.com>

> Much nicer, thanks.
>
> Do you wish for me to apply this to the tree if it passes review?
>
> thanks,
>
> greg k-h

Yes, please.
Thanks,
marri

^ permalink raw reply

* Re: [PATCH V6 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
From: Tirumala Marri @ 2010-12-09 16:52 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: Mark Miesfeld, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <4D00D89B.6020208@ru.mvista.com>

On Thu, Dec 9, 2010 at 5:24 AM, Sergei Shtylyov <sshtylyov@mvista.com> wrot=
e:
> Hello.
>
> On 09-12-2010 3:32, tmarri@apm.com wrote:
>
>> From: Tirumala Marri<tmarri@apm.com>
>
>> Enable gadget support
>
>> Signed-off-by: Tirumala R Marri<tmarri@apm.com>
>> Signed-off-by: Fushen Chen<fchen@apm.com>
>> Signed-off-by: Mark Miesfeld<mmiesfeld@apm.com>
>
> [...]
>
>> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
>> index 747b0d3..b2bcc4e 100644
>> --- a/drivers/usb/gadget/Kconfig
>> +++ b/drivers/usb/gadget/Kconfig
>> @@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
>
> [...]
>>
>> +config USB_OTG
>> + =A0 =A0 =A0 boolean "OTG Support"
>
> =A0 This symbol is already defined in drivers/usb/core/Kconfig.
>
> WBR, Sergei
>
Sure I will fix that.
Thx,
Marri

^ permalink raw reply

* Re: [RFC PATCH 7/7 v2] ppc: add dynamic dma window support
From: Nishanth Aravamudan @ 2010-12-09 19:00 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: sonnyrao, miltonm, Paul Mackerras, Anton Blanchard, linuxppc-dev
In-Reply-To: <1291868226.16694.220.camel@pasglop>

On 09.12.2010 [15:17:06 +1100], Benjamin Herrenschmidt wrote:
> On Tue, 2010-10-26 at 20:35 -0700, Nishanth Aravamudan wrote:
> 
> No much comments... I'm amazed how complex he firmware folks managed to
> make this ... 
> 
> >  static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
> >  {
> >  	int err = NOTIFY_OK;
> >  	struct device_node *np = node;
> >  	struct pci_dn *pci = PCI_DN(np);
> > +	struct direct_window *window;
> >  
> >  	switch (action) {
> >  	case PSERIES_RECONFIG_REMOVE:
> >  		if (pci && pci->iommu_table)
> >  			iommu_free_table(pci->iommu_table, np->full_name);
> > +
> > +		spin_lock(&direct_window_list_lock);
> > +		list_for_each_entry(window, &direct_window_list, list) {
> > +			if (window->device == np) {
> > +				list_del(&window->list);
> > +				break;
> > +			}
> > +		}
> > +		spin_unlock(&direct_window_list_lock);
> 
> Should you also kfree the window ?

Yeah, looks like I should. I have a few other questions due to testing,
but I'll reply to my original e-mail with those.

Thanks for the review!
Nish

-- 
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center

^ permalink raw reply

* Re: [RFC PATCH 7/7 v2] ppc: add dynamic dma window support
From: Nishanth Aravamudan @ 2010-12-09 19:09 UTC (permalink / raw)
  To: sonnyrao, miltonm, Benjamin Herrenschmidt, Paul Mackerras,
	Grant Likely, Anton Blanchard, linuxppc-dev
In-Reply-To: <1288150518-4026-8-git-send-email-nacc@us.ibm.com>

On 26.10.2010 [20:35:17 -0700], Nishanth Aravamudan wrote:
> If firmware allows us to map all of a partition's memory for DMA on a
> particular bridge, create a 1:1 mapping of that memory. Add hooks for
> dealing with hotplug events. Dyanmic DMA windows can use larger than the
> default page size, and we use the largest one possible.
> 
> Not-yet-signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
> 
> ---
> 
> I've tested this briefly on a machine with suitable firmware/hardware.
> Things seem to work well, but I want to do more exhaustive I/O testing
> before asking for upstream merging. I would really appreciate any
> feedback on the updated approach.
> 
> Specific questions:
> 
> Ben, did I hook into the dma_set_mask() platform callback as you
> expected? Anything I can do better or which perhaps might lead to
> gotchas later?
> 
> I've added a disable_ddw option, but perhaps it would be better to
> just disable the feature if iommu=force?

So for the final version, I probably should document this option in
kernel-parameters.txt w/ the patch, right?

<snip>

> +static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
> +					unsigned long num_pfn, const void *arg)
> +{
> +	const struct dynamic_dma_window_prop *maprange = arg;
> +	int rc;
> +	u64 tce_size, num_tce, dma_offset, next;
> +	u32 tce_shift;
> +	long limit;
> +
> +	tce_shift = be32_to_cpu(maprange->tce_shift);
> +	tce_size = 1ULL << tce_shift;
> +	next = start_pfn << PAGE_SHIFT;
> +	num_tce = num_pfn << PAGE_SHIFT;
> +
> +	/* round back to the beginning of the tce page size */
> +	num_tce += next & (tce_size - 1);
> +	next &= ~(tce_size - 1);
> +
> +	/* covert to number of tces */
> +	num_tce |= tce_size - 1;
> +	num_tce >>= tce_shift;
> +
> +	do {
> +		/*
> +		 * Set up the page with TCE data, looping through and setting
> +		 * the values.
> +		 */
> +		limit = min_t(long, num_tce, 512);
> +		dma_offset = next + be64_to_cpu(maprange->dma_base);
> +
> +		rc = plpar_tce_stuff(be64_to_cpu(maprange->liobn),
> +					    (u64)dma_offset,
> +					     0, limit);
> +		num_tce -= limit;
> +	} while (num_tce > 0 && !rc);
> +
> +	return rc;
> +}

There is a bit of a typo here, the liobn is a 32-bit value. I've fixed
this is up locally and will update it when I send out the final version
of this patch.

I'm finding that on dlpar remove of adapters running in slots supporting
64-bit DMA, that the plpar_tce_stuff is failing. Can you think of a
reason why? It looks basically the same as the put_indirect below...

> +static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
> +					unsigned long num_pfn, const void *arg)
> +{
> +	const struct dynamic_dma_window_prop *maprange = arg;
> +	u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce, liobn;
> +	u32 tce_shift;
> +	u64 rc = 0;
> +	long l, limit;
> +
> +	local_irq_disable();	/* to protect tcep and the page behind it */
> +	tcep = __get_cpu_var(tce_page);
> +
> +	if (!tcep) {
> +		tcep = (u64 *)__get_free_page(GFP_ATOMIC);
> +		if (!tcep) {
> +			local_irq_enable();
> +			return -ENOMEM;
> +		}
> +		__get_cpu_var(tce_page) = tcep;
> +	}
> +
> +	proto_tce = TCE_PCI_READ | TCE_PCI_WRITE;
> +
> +	liobn = (u64)be32_to_cpu(maprange->liobn);
> +	tce_shift = be32_to_cpu(maprange->tce_shift);
> +	tce_size = 1ULL << tce_shift;
> +	next = start_pfn << PAGE_SHIFT;
> +	num_tce = num_pfn << PAGE_SHIFT;
> +
> +	/* round back to the beginning of the tce page size */
> +	num_tce += next & (tce_size - 1);
> +	next &= ~(tce_size - 1);
> +
> +	/* covert to number of tces */
> +	num_tce |= tce_size - 1;
> +	num_tce >>= tce_shift;
> +
> +	/* We can map max one pageful of TCEs at a time */
> +	do {
> +		/*
> +		 * Set up the page with TCE data, looping through and setting
> +		 * the values.
> +		 */
> +		limit = min_t(long, num_tce, 4096/TCE_ENTRY_SIZE);
> +		dma_offset = next + be64_to_cpu(maprange->dma_base);
> +
> +		for (l = 0; l < limit; l++) {
> +			tcep[l] = proto_tce | next;
> +			next += tce_size;
> +		}
> +
> +		rc = plpar_tce_put_indirect(liobn,
> +					    (u64)dma_offset,
> +					    (u64)virt_to_abs(tcep),
> +					    limit);
> +
> +		num_tce -= limit;
> +	} while (num_tce > 0 && !rc);
> +                printk("plpar_tce_put_indirect for offset 0x%llx and tcep[0] 0x%llx returned %llu\n",
> +                                (u64)dma_offset, tcep[0], rc);
> +

I'll cleanup the debugging on the final version too.

<snip>

> +static void remove_ddw(struct device_node *np)
> +{
> +	struct dynamic_dma_window_prop *dwp;
> +	struct property *win64;
> +	const u32 *ddr_avail;
> +        u64 liobn;
> +	int len, ret;
> +
> +	ddr_avail = of_get_property(np, "ibm,ddw-applicable", &len);
> +	win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
> +	if (!win64 || !ddr_avail || len < 3 * sizeof(u32))
> +		return;
> +
> +	dwp = win64->value;
> +        liobn = (u64)be32_to_cpu(dwp->liobn);
> +
> +	/* clear the whole window, note the arg is in kernel pages */
> +	ret = tce_clearrange_multi_pSeriesLP(0,
> +		1ULL << (be32_to_cpu(dwp->window_shift) - PAGE_SHIFT), dwp);
> +	if (ret)
> +		pr_warning("%s failed to clear tces in window.\n",
> +			 np->full_name);
> +        else
> +		pr_warning("%s successfully cleared tces in window.\n",
> +			 np->full_name);
> +
> +	ret = rtas_call(ddr_avail[2], 1, 1, NULL, liobn);
> +	if (ret)
> +		pr_warning("%s: failed to remove direct window: rtas returned "
> +			"%d to ibm,remove-pe-dma-window(%x) %llx\n",
> +			np->full_name, ret, ddr_avail[2], liobn);
> +	else
> +		pr_warning("%s: successfully removed direct window: rtas returned "
> +			"%d to ibm,remove-pe-dma-window(%x) %llx\n",
> +			np->full_name, ret, ddr_avail[2], liobn);
> +
> +	ret = prom_remove_property(np, win64);
> +	if (ret)
> +		pr_warning("%s: failed to remove direct window property (%i)\n",
> +			np->full_name, ret);
> +	else
> +		pr_warning("%s: successfully removed direct window property (%i)\n",
> +			np->full_name, ret);
> +}

When this function gets called on dlpar remove of an adapter, it throws
a proc warning because the property has already been removed from
/proc/device-tree (but not the kernel representation) before the
notifiers get called:

static int pSeries_reconfig_remove_node(struct device_node *np)
{
        struct device_node *parent, *child;

        parent = of_get_parent(np);
        if (!parent)
                return -EINVAL;

        if ((child = of_get_next_child(np, NULL))) {
                of_node_put(child);
                of_node_put(parent);
                return -EBUSY;
        }

        remove_node_proc_entries(np);

        blocking_notifier_call_chain(&pSeries_reconfig_chain,
                            PSERIES_RECONFIG_REMOVE, np);
        of_detach_node(np);

        of_node_put(parent);
        of_node_put(np); /* Must decrement the refcount */
        return 0;
}

Am I reading that correctly? Should I add a paramter to remove_ddw that
specifies if it is being called from the reconfig notifier (or perhaps
just whether it needs to remove the property)?

Also, just so I understand, it doesn't seem like dlpar provides an
option for the notifier chain to indicate failure (e.g., the tce stuff
failing above) and prevent the dlpar operation. AFAICT after discussing
with the firmware folks, it's actually non-fatal for the TCEs not to be
cleared during the dlpar remove, but seems like it might indicate an
issue if it happens in the field?

Otherwise, I've hit no problems testing this series under load. Once I
get some feedback on these questions, I'll roll out a new version,
hopefully tomorrow, that can be accepted.

Thanks,
Nish

-- 
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox