summaryrefslogtreecommitdiff
path: root/drivers/usb/musb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r--drivers/usb/musb/Kconfig12
-rw-r--r--drivers/usb/musb/Makefile5
-rw-r--r--drivers/usb/musb/musb_core.c94
-rw-r--r--drivers/usb/musb/musb_core.h28
-rw-r--r--drivers/usb/musb/musb_dma.h12
-rw-r--r--drivers/usb/musb/musb_gadget.c262
-rw-r--r--drivers/usb/musb/musb_gadget.h7
-rw-r--r--drivers/usb/musb/musb_host.c125
-rw-r--r--drivers/usb/musb/musb_virthub.c5
-rw-r--r--drivers/usb/musb/ste_config.h52
-rw-r--r--drivers/usb/musb/stm_musb.c638
-rw-r--r--drivers/usb/musb/stm_musb_dma.c722
-rw-r--r--drivers/usb/musb/stm_musb_dma.h59
13 files changed, 1913 insertions, 108 deletions
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index cfd38edfcf9..8d5caddc03e 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -41,6 +41,7 @@ config USB_MUSB_SOC
default y if ARCH_OMAP4
default y if (BF54x && !BF544)
default y if (BF52x && !BF522 && !BF523)
+ default y if ARCH_U8500
comment "DaVinci 35x and 644x USB support"
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx
@@ -57,6 +58,17 @@ comment "OMAP 44xx high speed USB support"
comment "Blackfin high speed USB Support"
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
+comment "U8500 USB support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+
+config U8500_USB_HS_OTG
+ boolean "USB HS-OTG support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+ default y
+ help
+ Say Y here if you want to enable support for the U8500 USB
+ high speed on-the-go USB interface with external ULPI transceiver.
+
config USB_TUSB6010
boolean "TUSB 6010 support"
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 9705f716386..bddb323c3f2 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -26,6 +26,10 @@ ifeq ($(CONFIG_ARCH_OMAP4),y)
musb_hdrc-objs += omap2430.o
endif
+ifeq ($(CONFIG_ARCH_U8500),y)
+ musb_hdrc-objs += stm_musb.o
+endif
+
ifeq ($(CONFIG_BF54x),y)
musb_hdrc-objs += blackfin.o
endif
@@ -67,6 +71,7 @@ ifneq ($(CONFIG_MUSB_PIO_ONLY),y)
endif
endif
endif
+ musb_hdrc-objs += stm_musb_dma.o
endif
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3b795c56221..ee10bf1f031 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -4,6 +4,7 @@
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -106,12 +107,16 @@
#endif
#include "musb_core.h"
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
#ifdef CONFIG_ARCH_DAVINCI
#include "davinci.h"
#endif
+#include <mach/stm_musb.h>
+
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
@@ -459,6 +464,29 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
int_usb);
+ /*
+ * XXX The following code has been inserted here as a temporary hack
+ * to get some USB events directly from the USB hardware. This code
+ * and callbacks should eventually be integrated into the generic
+ * USB gadget stack.
+ */
+ if (!(devctl & MUSB_DEVCTL_HM)) {
+ if (int_usb & MUSB_INTR_RESET) {
+ if (power & MUSB_POWER_HSMODE)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_HS);
+ else
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_FS);
+ }
+ if (int_usb & MUSB_INTR_RESUME)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESUME);
+ else if (int_usb & MUSB_INTR_SUSPEND)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_SUSPEND);
+ }
+
/* in host mode, the peripheral may issue remote wakeup.
* in peripheral mode, the host may resume the link.
* spurious RESUME irqs happen too, paired with SUSPEND.
@@ -492,7 +520,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ + msecs_to_jiffies(30);
musb->xceiv->state = OTG_STATE_A_HOST;
musb->is_active = 1;
@@ -987,6 +1015,10 @@ void musb_start(struct musb *musb)
}
musb_platform_enable(musb);
musb_writeb(regs, MUSB_DEVCTL, devctl);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ musb_save_context(musb);
+ clk_disable(musb->clock);
+#endif
}
@@ -995,6 +1027,9 @@ static void musb_generic_disable(struct musb *musb)
void __iomem *mbase = musb->mregs;
u16 temp;
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_enable(musb->clock);
+#endif
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
musb_writew(mbase, MUSB_INTRTXE, 0);
@@ -1008,6 +1043,9 @@ static void musb_generic_disable(struct musb *musb)
temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_disable(musb->clock);
+#endif
}
/*
@@ -1067,7 +1105,11 @@ static void musb_shutdown(struct platform_device *pdev)
|| defined(CONFIG_ARCH_OMAP4)
static ushort __initdata fifo_mode = 4;
#else
+#ifndef CONFIG_ARCH_U8500
static ushort __initdata fifo_mode = 2;
+#else
+static ushort __initdata fifo_mode = 5;
+#endif
#endif
/* "modprobe ... fifo_mode=1" etc */
@@ -1150,8 +1192,8 @@ static struct musb_fifo_cfg __initdata mode_4_cfg[] = {
/* mode 5 - fits in 8KB */
static struct musb_fifo_cfg __initdata mode_5_cfg[] = {
-{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
@@ -1536,7 +1578,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
static irqreturn_t generic_interrupt(int irq, void *__hci)
{
@@ -1591,6 +1633,14 @@ irqreturn_t musb_interrupt(struct musb *musb)
}
#endif
+ /**
+ * HACK for detecting the AX8817X series Ethernet over USB
+ * Adapters for U8500 platform
+ */
+#if (defined(CONFIG_ARCH_U8500) && defined(CONFIG_USB_NET_AX8817X))
+ mdelay(10);
+#endif
+
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
@@ -2417,26 +2467,42 @@ static int musb_suspend(struct device *dev)
spin_lock_irqsave(&musb->lock, flags);
if (is_peripheral_active(musb)) {
- /* FIXME force disconnect unless we know USB will wake
+ /*
+ * FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
} else if (is_host_active(musb)) {
- /* we know all the children are suspended; sometimes
+ /*
+ * we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
}
+#ifndef CONFIG_ARCH_U8500
musb_save_context(musb);
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
+#endif
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
-static int musb_resume_noirq(struct device *dev)
+static int musb_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct musb *musb = dev_to_musb(&pdev->dev);
@@ -2444,12 +2510,14 @@ static int musb_resume_noirq(struct device *dev)
if (!musb->clock)
return 0;
+#ifndef CONFIG_ARCH_U8500
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
musb_restore_context(musb);
+#endif
/* for static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
@@ -2460,7 +2528,7 @@ static int musb_resume_noirq(struct device *dev)
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
- .resume_noirq = musb_resume_noirq,
+ .resume = musb_resume,
};
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
@@ -2513,10 +2581,18 @@ static int __init musb_init(void)
return platform_driver_probe(&musb_driver, musb_probe);
}
+#ifndef CONFIG_ARCH_U8500
/* make us init after usbcore and i2c (transceivers, regulators, etc)
* and before usb gadget and host-side drivers start to register
*/
fs_initcall(musb_init);
+#else
+/* with fs_initcall the dma controller driver was loaded after mentor IP
+ * driver so when DMA is enabled, it will break as DMA controller driver is
+ * not loaded. This has been done to correct the order
+ */
+module_init(musb_init);
+#endif
static void __exit musb_cleanup(void)
{
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 91d67794e35..c037939a8a2 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -72,8 +72,6 @@ struct musb_ep;
#include <linux/usb/hcd.h>
#include "musb_host.h"
-
-
#ifdef CONFIG_USB_MUSB_OTG
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
@@ -204,6 +202,7 @@ enum musb_g_ep0_state {
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
+#define USB_SUSP_DET_DURATION 5 /* suspend time 5ms */
/*************************** REGISTER ACCESS ********************************/
@@ -492,8 +491,10 @@ extern void musb_platform_save_context(struct musb *musb,
extern void musb_platform_restore_context(struct musb *musb,
struct musb_context_registers *musb_context);
#else
-#define musb_platform_save_context(m, x) do {} while (0)
-#define musb_platform_restore_context(m, x) do {} while (0)
+static inline void musb_platform_save_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
+static inline void musb_platform_restore_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
#endif
#endif
@@ -598,18 +599,31 @@ extern void musb_hnp_stop(struct musb *musb);
extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode);
+#ifdef CONFIG_PM
+void musb_save_context(struct musb *);
+extern void musb_restore_context(struct musb *musb);
+#endif
+
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
#else
-#define musb_platform_try_idle(x, y) do {} while (0)
+static inline void musb_platform_try_idle(struct musb *musb, unsigned long timeout) { }
#endif
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN)
extern int musb_platform_get_vbus_status(struct musb *musb);
#else
-#define musb_platform_get_vbus_status(x) 0
+static inline int musb_platform_get_vbus_status(struct musb *musb) { return 0; }
+#endif
+
+#if defined(CONFIG_ARCH_U8500)
+extern void musb_platform_device_en(int enable);
+extern void musb_platform_session_req(void);
+#else
+static inline void musb_platform_device_en(int enable) { }
+static inline void musb_platform_session_req(void) { }
#endif
extern int __init musb_platform_init(struct musb *musb, void *board_data);
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 916065ba9e7..a114af6c330 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -36,7 +36,7 @@
#define __MUSB_DMA_H__
struct musb_hw_ep;
-
+struct musb_request;
/*
* DMA Controller Abstraction
*
@@ -91,6 +91,10 @@ struct musb_hw_ep;
# endif
#endif
+#ifdef CONFIG_USB_U8500_DMA
+#undef USE_MODE1
+#endif
+
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
@@ -155,6 +159,10 @@ dma_channel_status(struct dma_channel *c)
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
+ * @is_compatible:allow dma code to indicate incompatibility
+ * with usb request. Gadget musb driver call this api, if
+ * available, before dma mappings to avoid any unnecessary
+ * mapping operations.
*
* Controllers manage dma channels.
*/
@@ -169,6 +177,8 @@ struct dma_controller {
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
+ int (*is_compatible)(struct dma_channel *channel,
+ struct musb_request *);
};
/* called after channel_program(), may indicate a fault */
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6fca870e957..9ac40c4bab9 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -5,6 +5,7 @@
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,14 @@
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
#include <linux/slab.h>
#include "musb_core.h"
-
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
+#define USB_ENABLE_PHY 1
/* MUSB PERIPHERAL status 3-mar-2006:
*
* - EP0 seems solid. It passes both USBCV and usbtest control cases.
@@ -91,6 +95,8 @@
*/
/* ----------------------------------------------------------------------- */
+#define is_buffer_mapped(req) (is_dma_capable() && \
+ (req->map_state != UN_MAPPED))
/*
* Immediately complete a request.
@@ -109,6 +115,7 @@ __acquires(ep->musb->lock)
struct musb_request *req;
struct musb *musb;
int busy = ep->busy;
+ int dma_len;
req = to_musb_request(request);
@@ -119,20 +126,34 @@ __acquires(ep->musb->lock)
ep->busy = 1;
spin_unlock(&musb->lock);
+#ifdef CONFIG_ARM
+ /* If DMA is enabled we need to flush all data we read. */
+ if (ep->hw_ep->musb->controller->dma_mask != 0)
+ dmac_flush_range((const u8 *)req->request.buf,
+ (const u8 *)req->request.buf + req->request.length);
+#endif
+#ifdef CONFIG_USB_U8500_DMA
+ if (!(req->request.actual < ep->packet_sz)
+ && is_buffer_mapped(req)
+ && (ep->packet_sz >= DMA_PACKET_THRESHOLD)) {
+#else
if (is_dma_capable()) {
- if (req->mapped) {
+#endif
+ dma_len = req->request.actual - (req->request.actual % 512) ;
+
+ if (req->map_state == MUSB_MAPPED) {
dma_unmap_single(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
req->request.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else if (req->request.dma != DMA_ADDR_INVALID)
+ req->map_state = UN_MAPPED;
+ } else if (req->map_state == PRE_MAPPED)
dma_sync_single_for_cpu(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
@@ -209,7 +230,20 @@ static void nuke(struct musb_ep *ep, const int status)
static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep)
{
+#ifdef CONFIG_USB_U8500_DMA
+ /*
+ * In case of full speed mode with double buffering, bulk split
+ * does not work as defined. Hence in case of FS, return the EP
+ * size instead of EP FIFO size.
+ */
+ u8 power;
+
+ power = musb_readb(musb->mregs, MUSB_POWER);
+
+ if (can_bulk_split(musb, ep->type) && (power & MUSB_POWER_HSMODE))
+#else
if (can_bulk_split(musb, ep->type))
+#endif
return ep->hw_ep->max_packet_sz_tx;
else
return ep->packet_sz;
@@ -267,12 +301,17 @@ static void txstate(struct musb *musb, struct musb_request *req)
int use_dma = 0;
musb_ep = req->ep;
-
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma) {
+#endif
/* we shouldn't get here while DMA is active ... but we do ... */
if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
DBG(4, "dma pending...\n");
return;
}
+#ifdef CONFIG_USB_U8500_DMA
+ }
+#endif
/* read TXCSR before */
csr = musb_readw(epio, MUSB_TXCSR);
@@ -298,10 +337,25 @@ static void txstate(struct musb *musb, struct musb_request *req)
csr);
#ifndef CONFIG_MUSB_PIO_ONLY
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma && is_buffer_mapped(req)) {
+#else
if (is_dma_capable() && musb_ep->dma) {
+#endif
struct dma_controller *c = musb->dma_controller;
use_dma = (request->dma != DMA_ADDR_INVALID);
+#ifdef CONFIG_USB_U8500_DMA
+ if (request->length >= DMA_PACKET_THRESHOLD) {
+ csr |= (MUSB_TXCSR_AUTOSET|
+ MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_MODE);
+ csr &= ~MUSB_TXCSR_P_UNDERRUN;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ }
+ use_dma = use_dma && c->channel_program(musb_ep->dma, musb_ep->packet_sz, DMA_MODE_1, request->dma, request->length);
+#endif
/* MUSB_TXCSR_P_ISO is still set correctly */
@@ -422,6 +476,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
request = next_request(musb_ep);
@@ -463,13 +518,41 @@ void musb_g_tx(struct musb *musb, u8 epnum)
u8 is_dma = 0;
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
+ int count = 0;
is_dma = 1;
+
+ /* ensure writebuffer is empty */
+ csr = musb_readw(epio, MUSB_TXCSR);
+ bytes_left = request->length
+ -musb_ep->dma->actual_len;
+ for (count = 0; count < MAX_COUNT; count++) {
+ if (!(csr&MUSB_TXCSR_FIFONOTEMPTY))
+ break;
+ csr = musb_readw(epio, MUSB_TXCSR);
+ }
+
csr |= MUSB_TXCSR_P_WZC_BITS;
+#ifdef CONFIG_ARCH_U8500
+ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_MODE
+ | MUSB_TXCSR_AUTOSET
+ | MUSB_TXCSR_P_UNDERRUN
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_TXPKTRDY);
+#else
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
- MUSB_TXCSR_TXPKTRDY);
+ MUSB_TXCSR_DMAMODE | MUSB_TXCSR_TXPKTRDY);
+#endif
musb_writew(epio, MUSB_TXCSR, csr);
- /* Ensure writebuffer is empty. */
- csr = musb_readw(epio, MUSB_TXCSR);
+ if (bytes_left) {
+ musb_write_fifo(musb_ep->hw_ep,
+ bytes_left,
+ (u8 *) (request->buf +
+ musb_ep->dma->actual_len));
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_TXPKTRDY);
+ request->actual += bytes_left;
+ }
+
request->actual += musb_ep->dma->actual_len;
DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
epnum, csr, musb_ep->dma->actual_len, request);
@@ -502,25 +585,33 @@ void musb_g_tx(struct musb *musb, u8 epnum)
}
/* ... or if not, then complete it. */
- musb_g_giveback(musb_ep, request, 0);
+ if (request->actual == request->length) {
+ musb_g_giveback(musb_ep, request, 0);
- /*
- * Kickstart next transfer if appropriate;
- * the packet that just completed might not
- * be transmitted for hours or days.
- * REVISIT for double buffering...
- * FIXME revisit for stalls too...
- */
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- return;
-
- request = musb_ep->desc ? next_request(musb_ep) : NULL;
- if (!request) {
- DBG(4, "%s idle now\n",
- musb_ep->end_point.name);
- return;
+ /*
+ * Kickstart next transfer if appropriate;
+ * the packet that just completed might not
+ * be transmitted for hours or days.
+ * REVISIT for double buffering...
+ * FIXME revisit for stalls too...
+ */
+ musb_ep_select(mbase, epnum);
+ csr = musb_readw(epio, MUSB_TXCSR);
+#ifdef CONFIG_ARCH_U8500
+ if ((csr & MUSB_TXCSR_FIFONOTEMPTY) &&
+ !(musb_ep->hw_ep->tx_double_buffered))
+#else
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY)
+#endif
+ return;
+
+ request =
+ musb_ep->desc ? next_request(musb_ep) : NULL;
+ if (!request) {
+ DBG(4, "%s idle now\n",
+ musb_ep->end_point.name);
+ return;
+ }
}
}
@@ -616,8 +707,14 @@ static void rxstate(struct musb *musb, struct musb_request *req)
if (csr & MUSB_RXCSR_RXPKTRDY) {
len = musb_readw(epio, MUSB_RXCOUNT);
if (request->actual < request->length) {
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
+
#ifdef CONFIG_USB_INVENTRA_DMA
if (is_dma_capable() && musb_ep->dma) {
+#else
+ if (is_buffer_mapped(req) &&
+ (len >= DMA_PACKET_THRESHOLD)) {
+#endif
struct dma_controller *c;
struct dma_channel *channel;
int use_dma = 0;
@@ -646,44 +743,54 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* then becomes usable as a runtime "use mode 1" hint...
*/
- csr |= MUSB_RXCSR_DMAENAB;
-#ifdef USE_MODE1
- csr |= MUSB_RXCSR_AUTOCLEAR;
- /* csr |= MUSB_RXCSR_DMAMODE; */
-
- /* this special sequence (enabling and then
- * disabling MUSB_RXCSR_DMAMODE) is required
- * to get DMAReq to activate
- */
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
-#endif
- musb_writew(epio, MUSB_RXCSR, csr);
-
if (request->actual < request->length) {
int transfer_size = 0;
-#ifdef USE_MODE1
- transfer_size = min(request->length,
- channel->max_len);
-#else
- transfer_size = len;
-#endif
- if (transfer_size <= musb_ep->packet_sz)
- musb_ep->dma->desired_mode = 0;
- else
+
+ /* In case first packet is short */
+ if (len < musb_ep->packet_sz)
+ transfer_size = len;
+ else if (request->short_not_ok) {
+
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_AUTOCLEAR;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ channel->max_len);
+
+ csr |= MUSB_RXCSR_DMAMODE;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
musb_ep->dma->desired_mode = 1;
+ } else {
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ (unsigned)len);
+
+ musb_ep->dma->desired_mode = 0;
+ }
use_dma = c->channel_program(
- channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- request->dma
- + request->actual,
- transfer_size);
- }
+ channel,
+ musb_ep->packet_sz,
+ channel->desired_mode,
+ request->dma
+ + request->actual,
+ transfer_size);
- if (use_dma)
- return;
+ if (use_dma)
+ return;
+ }
}
#endif /* Mentor's DMA */
@@ -743,6 +850,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
@@ -791,7 +899,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_DMAMODE);
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_P_WZC_BITS | csr);
-
+ csr = musb_readw(epio, MUSB_RXCSR);
request->actual += musb_ep->dma->actual_len;
DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n",
@@ -799,7 +907,9 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_readw(epio, MUSB_RXCSR),
musb_ep->dma->actual_len, request);
-#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA)
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) \
+ || defined(CONFIG_USB_U8500_DMA)
+
/* Autoclear doesn't clear RxPktRdy for short packets */
if ((dma->desired_mode == 0)
|| (dma->actual_len
@@ -963,6 +1073,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
* for some reason you run out of channels here.
*/
if (is_dma_capable() && musb->dma_controller) {
+
struct dma_controller *c = musb->dma_controller;
musb_ep->dma = c->channel_alloc(c, hw_ep,
@@ -1102,6 +1213,8 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
struct musb *musb;
int status = 0;
unsigned long lockflags;
+ int compatible = 0;
+ struct dma_controller *dma;
if (!ep || !req)
return -EINVAL;
@@ -1110,6 +1223,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
musb_ep = to_musb_ep(ep);
musb = musb_ep->musb;
+ dma = musb->dma_controller;
request = to_musb_request(req);
request->musb = musb;
@@ -1125,7 +1239,15 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->epnum = musb_ep->current_epnum;
request->tx = musb_ep->is_in;
- if (is_dma_capable() && musb_ep->dma) {
+ request->map_state = UN_MAPPED;
+
+ if (musb_ep->dma && dma) {
+ if (dma->is_compatible)
+ compatible = dma->is_compatible(musb_ep->dma, request);
+
+ }
+
+ if (is_dma_capable() && musb_ep->dma && compatible) {
if (request->request.dma == DMA_ADDR_INVALID) {
request->request.dma = dma_map_single(
musb->controller,
@@ -1134,7 +1256,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 1;
+ request->map_state = MUSB_MAPPED;
} else {
dma_sync_single_for_device(musb->controller,
request->request.dma,
@@ -1142,12 +1264,12 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 0;
+ request->map_state = PRE_MAPPED;
}
} else if (!req->buf) {
return -ENODATA;
} else
- request->mapped = 0;
+ request->map_state = UN_MAPPED;
spin_lock_irqsave(&musb->lock, lockflags);
@@ -1417,6 +1539,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
u8 power, devctl;
int retries;
+
+ musb_platform_device_en(USB_ENABLE_PHY);
+ musb_platform_session_req();
+
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
@@ -1894,6 +2020,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
+ stm_prcmu_qos_handler(SET_OPP);
switch (musb->xceiv->state) {
case OTG_STATE_B_IDLE:
break;
@@ -1940,6 +2067,7 @@ void musb_g_suspend(struct musb *musb)
WARNING("unhandled SUSPEND transition (%s)\n",
otg_state_string(musb));
}
+ stm_prcmu_qos_handler(!SET_OPP);
}
/* Called during SRP */
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index c8b140325d8..20d6b064542 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -34,6 +34,11 @@
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
+enum buffer_map_state {
+ UN_MAPPED = 0,
+ PRE_MAPPED,
+ MUSB_MAPPED
+};
struct musb_request {
struct usb_request request;
@@ -41,7 +46,7 @@ struct musb_request {
struct musb *musb;
u8 tx; /* endpoint direction */
u8 epnum;
- u8 mapped;
+ enum buffer_map_state map_state;
};
static inline struct musb_request *to_musb_request(struct usb_request *req)
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 877d20b1dff..25cbeadb8b0 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -45,6 +45,9 @@
#include "musb_core.h"
#include "musb_host.h"
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
/* MUSB HOST status 22-mar-2006
*
@@ -106,24 +109,41 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
{
void __iomem *epio = ep->regs;
+ void __iomem *regs = ep->musb->mregs;
u16 csr;
- u16 lastcsr = 0;
- int retries = 1000;
+ u8 addr;
+ int retries = 3000; /* 3ms */
+ /*
+ * NOTE: We are using a hack here because the FIFO-FLUSH
+ * bit is broken in hardware! The hack consists of changing
+ * the TXFUNCADDR to an unused device address and waiting
+ * for any pending USB packets to hit the 3-strikes and your
+ * gone rule.
+ */
+ addr = musb_readb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR));
csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- if (csr != lastcsr)
- DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
- lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
- musb_writew(epio, MUSB_TXCSR, csr);
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum,
+ MUSB_TXFUNCADDR), 127);
csr = musb_readw(epio, MUSB_TXCSR);
- if (WARN(retries-- < 1,
- "Could not flush host TX%d fifo: csr: %04x\n",
- ep->epnum, csr))
- return;
- mdelay(1);
+ retries--;
+ if (retries == 0) {
+ /* can happen if the USB clocks are OFF */
+ DBG(3, "Could not flush host TX%d "
+ "fifo: csr=0x%04x\n", ep->epnum, csr);
+ break;
+ }
+ udelay(1);
}
+ /* clear any errors */
+ csr &= ~(MUSB_TXCSR_H_ERROR
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_NAKTIMEOUT);
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ /* restore endpoint address */
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR), addr);
}
static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
@@ -175,9 +195,16 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
+
+#ifdef CONFIG_USB_U8500_DMA
+ txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_H_WZC_BITS;
+ txcsr |= MUSB_TXCSR_DMAMODE;
+#else
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
if (is_cppi_enabled())
txcsr |= MUSB_TXCSR_DMAMODE;
+#endif
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
@@ -290,8 +317,13 @@ start:
if (!hw_ep->tx_channel)
musb_h_tx_start(hw_ep);
+#ifdef CONFIG_USB_U8500_DMA
+ else
+ musb_h_tx_dma_start(hw_ep);
+#else
else if (is_cppi_enabled() || tusb_dma_omap())
musb_h_tx_dma_start(hw_ep);
+#endif
}
}
@@ -323,8 +355,15 @@ __acquires(musb->lock)
urb->actual_length, urb->transfer_buffer_length
);
+
usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
spin_unlock(&musb->lock);
+#ifdef CONFIG_USB_U8500_DMA
+ /* Make it safe to call this routine more than once */
+ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
+ URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
+ URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
+#endif
usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
spin_lock(&musb->lock);
}
@@ -627,7 +666,35 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u16 csr;
u8 mode;
-#ifdef CONFIG_USB_INVENTRA_DMA
+#ifdef CONFIG_USB_U8500_DMA
+ if (length > channel->max_len)
+ length = channel->max_len;
+
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (length > pkt_size) {
+ mode = 1;
+ csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
+ /* autoset shouldn't be set in high bandwidth */
+ if (qh->hb_mult == 1)
+ csr |= MUSB_TXCSR_AUTOSET;
+ } else {
+ mode = 0;
+ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
+ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
+ }
+ channel->desired_mode = mode;
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ channel->actual_len = 0;
+
+ /*
+ * TX uses "RNDIS" mode automatically but needs help
+ * to identify the zero-length-final-packet case.
+ */
+ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
+
+#else
+#ifdef CONFIG_USB_INVENTRA_DMA
if (length > channel->max_len)
length = channel->max_len;
@@ -657,6 +724,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
*/
mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
#endif
+#endif
qh->segsize = length;
@@ -842,7 +910,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
}
/* kick things off */
-
if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
/* candidate for DMA */
if (dma_channel) {
@@ -872,7 +939,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
csr |= MUSB_RXCSR_DMAENAB;
}
}
-
csr |= MUSB_RXCSR_H_REQPKT;
DBG(7, "RXCSR%d := %04x\n", epnum, csr);
musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
@@ -1141,7 +1207,9 @@ void musb_host_tx(struct musb *musb, u8 epnum)
DBG(3, "TX 3strikes on ep=%d\n", epnum);
status = -ETIMEDOUT;
-
+ } else if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
+ /* BUSY - can happen during USB transfer cancel */
+ return;
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
DBG(6, "TX end=%d device not responding\n", epnum);
@@ -1437,6 +1505,9 @@ void musb_host_rx(struct musb *musb, u8 epnum)
u32 status;
struct dma_channel *dma;
+#if defined(CONFIG_USB_U8500_DMA)
+ u16 tmp_val;
+#endif
musb_ep_select(mbase, epnum);
urb = next_urb(qh);
@@ -1527,7 +1598,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
done = true;
goto finish;
}
-
if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) {
/* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */
ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr);
@@ -1541,7 +1611,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
-#ifndef CONFIG_USB_INVENTRA_DMA
+#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_U8500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
@@ -1564,6 +1634,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
MUSB_RXCSR_H_WZC_BITS | rx_csr);
}
#endif
+
if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) {
xfer_len = dma->actual_len;
@@ -1573,7 +1644,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
@@ -1629,7 +1700,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
@@ -1713,6 +1784,14 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
val = musb_readw(epio, MUSB_RXCSR);
+
+#if defined(CONFIG_USB_U8500_DMA)
+ /* retain the original value,
+ *which will be used to reset CSR
+ */
+ tmp_val = val;
+#endif
+
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
@@ -1741,9 +1820,13 @@ void musb_host_rx(struct musb *musb, u8 epnum)
hw_ep->rx_channel = NULL;
dma = NULL;
/* REVISIT reset CSR */
+
+#if defined(CONFIG_USB_U8500_DMA)
+ musb_writew(epio, MUSB_RXCSR, tmp_val);
+#endif
}
}
-#endif /* Mentor DMA */
+#endif /* Mentor DMA || U8500 DMA */
if (!dma) {
done = musb_host_packet_rx(musb, urb,
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 92e85e027cf..663dda191dd 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -84,8 +84,9 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
&& musb->xceiv->host->b_hnp_enable;
if (musb->is_active)
mod_timer(&musb->otg_timer, jiffies
- + msecs_to_jiffies(
- OTG_TIME_A_AIDL_BDIS));
+ + msecs_to_jiffies((
+ OTG_TIME_A_AIDL_BDIS +
+ USB_SUSP_DET_DURATION)));
musb_platform_try_idle(musb, 0);
break;
#ifdef CONFIG_USB_MUSB_OTG
diff --git a/drivers/usb/musb/ste_config.h b/drivers/usb/musb/ste_config.h
new file mode 100644
index 00000000000..daa336369fa
--- /dev/null
+++ b/drivers/usb/musb/ste_config.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STE_CONFIG_H__
+#define __STE_CONFIG_H__
+
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+#define CONFIG_USB_U8500_DMA
+#endif
+#define U8500_DMA_END_POINTS 7
+#define DMA_MODE_0 0
+#define DMA_MODE_1 1
+#define DMA_PACKET_THRESHOLD 512
+#define RX_END_POINT_OFFSET 6
+#define DELAY_IN_MICROSECONDS 10
+#define MAX_COUNT 35000
+void stm_prcmu_qos_handler(int);
+#define SET_OPP 1
+
+enum nmdk_dma_tx_rx_channel {
+ TX_CHANNEL_1 = 0,
+ TX_CHANNEL_2,
+ TX_CHANNEL_3,
+ TX_CHANNEL_4,
+ TX_CHANNEL_5,
+ TX_CHANNEL_6,
+ TX_CHANNEL_7,
+ RX_CHANNEL_1,
+ RX_CHANNEL_2,
+ RX_CHANNEL_3,
+ RX_CHANNEL_4,
+ RX_CHANNEL_5,
+ RX_CHANNEL_6,
+ RX_CHANNEL_7
+};
+#endif
+
diff --git a/drivers/usb/musb/stm_musb.c b/drivers/usb/musb/stm_musb.c
new file mode 100644
index 00000000000..5ce3c4a75dc
--- /dev/null
+++ b/drivers/usb/musb/stm_musb.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2009 STMicroelectronics
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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.
+ */
+
+/** @file stm_musb.c
+ * @brief This file contains the USB controller and Phy initialization
+ * with default as interrupt mode was implemented
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <mach/stm_musb.h>
+#include <mach/musb_db8500.h>
+#include <mach/prcmu-fw-api.h>
+#include "musb_core.h"
+
+static u8 ulpi_read_register(struct musb *musb, u8 address);
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data);
+/* callback argument for AB8500 callback functions */
+static struct musb *musb_status;
+static spinlock_t musb_ulpi_spinlock;
+static unsigned musb_power;
+#ifdef CONFIG_USB_OTG_20
+static int userrequest;
+#endif
+static struct workqueue_struct *stm_usb_power_wq;
+static struct work_struct stm_prcmu_qos;
+static int musb_qos_req;
+
+#define PERI5_CLK_ENABLE 1
+#define PERI5_CLK_DISABLE 0
+
+/**
+ * musb_set_session() - Start the USB session
+ *
+ * This function is used to start the USB sessios in USB host mode
+ * once the A cable is plugged in
+ */
+void musb_set_session(void)
+{
+ u8 val;
+ void __iomem *regs;
+
+ if (musb_status == NULL) {
+ printk(KERN_ERR "Error: devctl session cannot be set\n");
+ return;
+ }
+ regs = musb_status->mregs;
+ val = musb_readb(regs, MUSB_DEVCTL);
+ musb_writeb(regs, MUSB_DEVCTL, val | MUSB_DEVCTL_SESSION);
+}
+EXPORT_SYMBOL(musb_set_session);
+
+
+void stm_prcmu_qos_handler(int value)
+{
+ if (value)
+ musb_qos_req = 100;
+ else
+ musb_qos_req = 50;
+ queue_work(stm_usb_power_wq, &stm_prcmu_qos);
+}
+EXPORT_SYMBOL(stm_prcmu_qos_handler);
+
+static void stm_prcmu_qos_work(struct work_struct *work)
+{
+ if (musb_qos_req == 100) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 100);
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 50);
+ prcmu_release_usb_wakeup_state();
+ }
+}
+
+void stm_set_peripheral_clock(int enable)
+{
+ if (enable) {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 1);
+ else
+ clk_enable(musb_status->clock);
+ } else {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 0);
+ else
+ clk_disable(musb_status->clock);
+ }
+}
+
+#ifdef CONFIG_PM
+void stm_musb_context(int enable)
+{
+ void __iomem *regs;
+
+ if (enable) {
+ stm_set_peripheral_clock(PERI5_CLK_ENABLE);
+ musb_restore_context(musb_status);
+ regs = musb_status->mregs;
+ } else
+ stm_set_peripheral_clock(PERI5_CLK_DISABLE);
+}
+EXPORT_SYMBOL(stm_musb_context);
+#endif
+
+void
+ab8500_bm_usb_state_changed_wrapper(u8 bm_usb_state)
+{
+ if ((bm_usb_state == AB8500_BM_USB_STATE_RESET_HS) ||
+ (bm_usb_state == AB8500_BM_USB_STATE_RESET_FS)) {
+ musb_power = 0;
+ }
+
+ /*
+ * TODO: Instead of using callbacks, we should be using notify
+ * to tell the battery manager when there is s state change
+ */
+ ab8500_charger_usb_state_changed(bm_usb_state, musb_power);
+}
+
+#ifdef CONFIG_USB_OTG_20
+int musb_adp(void)
+{
+ if (userrequest == 0)
+ return 0;
+ else
+ return 1;
+}
+EXPORT_SYMBOL(musb_adp);
+#endif
+
+/* Sys interfaces */
+static struct kobject *usbstatus_kobj;
+static ssize_t usb_cable_status
+ (struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ u8 is_active = 0;
+
+ if (strcmp(attr->name, "cable_connect") == 0) {
+ is_active = musb_status->is_active;
+ sprintf(buf, "%d\n", is_active);
+ }
+ return strlen(buf);
+}
+
+static struct attribute usb_cable_connect_attribute = \
+ {.name = "cable_connect", .mode = S_IRUGO};
+static struct attribute *usb_status[] = {
+ &usb_cable_connect_attribute,
+ NULL
+};
+
+struct sysfs_ops usb_sysfs_ops = {
+ .show = usb_cable_status,
+};
+
+static struct kobj_type ktype_usbstatus = {
+ .sysfs_ops = &usb_sysfs_ops,
+ .default_attrs = usb_status,
+};
+
+/*
+ * A structure was declared as global for timer in USB host mode
+ */
+static struct timer_list notify_timer;
+
+/* TODO: Remove or use!! */
+#ifdef UNUSED_USB_STUFF
+
+/**
+ * ulpi_read_register() - Read the usb register from address writing into ULPI
+ * @musb: struct musb pointer.
+ * @address: address for reading from ULPI register of USB
+ *
+ * This function read the value from the specific address in USB host mode.
+ */
+static u8 ulpi_read_register(struct musb *musb, u8 address)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a read access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI read timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* get data */
+ val = musb_readb(mbase, OTG_UREGDATA);
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return val;
+}
+#endif
+
+/**
+ * ulpi_write_register() - Write to a usb phy's ULPI register
+ * using the Mentor ULPI wrapper functionality
+ * @musb: struct musb pointer.
+ * @address: address of ULPI register
+ * @data: data for ULPI register
+ * This function writes the value given by data to the specific address
+ */
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* First write to ULPI wrapper registers */
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a write access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val &= ~OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* Write data to ULPI wrapper data register */
+ musb_writeb(mbase, OTG_UREGDATA, data);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI write timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return 0;
+
+}
+
+
+/**
+ * musb_stm_hs_otg_init() - Initialize the USB for paltform specific.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB with the given musb structure information.
+ */
+int __init musb_stm_hs_otg_init(struct musb *musb)
+{
+ u8 val;
+
+ if (musb->clock)
+ clk_enable(musb->clock);
+
+ /* enable ULPI interface */
+ val = musb_readb(musb->mregs, OTG_TOPCTRL);
+ val |= OTG_TOPCTRL_MODE_ULPI;
+ musb_writeb(musb->mregs, OTG_TOPCTRL, val);
+
+ /* do soft reset */
+ val = musb_readb(musb->mregs, 0x7F);
+ val |= 0x2;
+ musb_writeb(musb->mregs, 0x7F, val);
+
+ return 0;
+}
+/**
+ * musb_stm_fs_init() - Initialize the file system for the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the file system of USB.
+ */
+int __init musb_stm_fs_init(struct musb *musb)
+{
+ return 0;
+}
+/**
+ * musb_platform_enable() - Enable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function enables the USB.
+ */
+void musb_platform_enable(struct musb *musb)
+{
+}
+/**
+ * musb_platform_disable() - Disable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function disables the USB.
+ */
+void musb_platform_disable(struct musb *musb)
+{
+}
+
+/**
+ * musb_platform_try_idle() - Check the USB state active or not.
+ * @musb: struct musb pointer.
+ * @timeout: set the timeout to keep the host in idle mode.
+ *
+ * This function keeps the USB host in idle state based on the musb inforamtion.
+ */
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+{
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ unsigned long default_timeout =
+ jiffies + msecs_to_jiffies(10);
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = default_timeout;
+
+ /* Never idle if active, or when VBUS
+ timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n",
+ otg_state_string(musb));
+ del_timer(&notify_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout)) {
+ if (!timer_pending(&notify_timer))
+ last_timer = timeout;
+ else {
+ DBG(4,
+ "Longer idle timer already pending,ignoring\n");
+ return;
+ }
+ }
+ last_timer = timeout;
+
+ DBG(4, "%s inactive, for idle timer for %lu ms\n",
+ otg_state_string(musb),
+ (unsigned long)jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&notify_timer, timeout);
+ }
+}
+
+/**
+ * set_vbus() - Set the Vbus for the USB.
+ * @musb: struct musb pointer.
+ * @is_on: set Vbus for USB or not.
+ *
+ * This function set the Vbus for USB.
+ */
+static void set_vbus(struct musb *musb, int is_on)
+{
+ u8 devctl;
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ if (is_on) {
+ musb->is_active = 1;
+ musb->xceiv->default_a = 1;
+ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ MUSB_HST_MODE(musb);
+ } else {
+ musb->is_active = 0;
+
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+
+ musb->xceiv->default_a = 0;
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ MUSB_DEV_MODE(musb);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ DBG(1, "VBUS %s, devctl %02x "
+ /* otg %3x conf %08x prcm %08x */ "\n",
+ otg_state_string(musb),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+ if (!is_on) {
+ /* Discahrge the VBUS */
+ if (musb_status == NULL)
+ return;
+ ulpi_write_register(musb_status, ULPI_OCTRL, 0x08);
+ }
+}
+/**
+ * set_power() - Set the power for the USB transceiver.
+ * @x: struct usb_transceiver pointer.
+ * @mA: set mA power for USB.
+ *
+ * This function set the power for the USB.
+ */
+static int set_power(struct otg_transceiver *x, unsigned mA)
+{
+ if (mA > 100) {
+ /* AB V2 has eye diagram issues when drawing more
+ * than 100mA from VBUS.So setting charging current
+ * to 100mA in case of standard host
+ */
+ if (musb_get_abx500_rev() < 0x30)
+ mA = 100;
+ else
+ mA = 300;
+ }
+ musb_power = mA;
+ DBG(1, "Set VBUS Power = %d mA\n", mA);
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_CONFIGURED);
+ return 0;
+}
+/**
+ * musb_platform_set_mode() - Set the mode for the USB driver.
+ * @musb: struct musb pointer.
+ * @musb_mode: usb mode.
+ *
+ * This function set the mode for the USB.
+ */
+int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+ u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ switch (musb_mode) {
+ case MUSB_HOST:
+ otg_set_host(musb->xceiv, musb->xceiv->host);
+ break;
+ case MUSB_PERIPHERAL:
+ otg_set_peripheral(musb->xceiv, musb->xceiv->gadget);
+ break;
+ case MUSB_OTG:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+/**
+ * funct_host_notify_timer() - Initialize the timer for USB host driver.
+ * @data: usb host data.
+ *
+ * This function runs the timer for the USB host mode.
+ */
+static void funct_host_notify_timer(unsigned long data)
+{
+ struct musb *musb = (void *)data;
+ unsigned long flags;
+ u8 devctl;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+#ifdef CONFIG_PM
+ stm_musb_context(USB_DISABLE);
+#endif
+
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+ DBG(1, "otg_state %s devctl %d\n", otg_state_string(musb), devctl);
+}
+
+/**
+ * musb_platform_init() - Initialize the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB controller and Phy.
+ */
+int __init musb_platform_init(struct musb *musb, void *board_data)
+{
+ int ret;
+
+ usb_nop_xceiv_register();
+
+ musb->xceiv = otg_get_transceiver();
+ if (!musb->xceiv) {
+ pr_err("U8500 USB : no transceiver configured\n");
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = musb_stm_hs_otg_init(musb);
+ if (ret < 0)
+ goto done;
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = set_vbus;
+ if (is_peripheral_enabled(musb))
+ musb->xceiv->set_power = set_power;
+
+ ret = musb_phy_en(musb->board_mode);
+ if (ret < 0)
+ goto done;
+
+ if (musb_status == NULL) {
+ musb_status = musb;
+ spin_lock_init(&musb_ulpi_spinlock);
+ }
+
+ /* Registering usb device for sysfs */
+ usbstatus_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
+
+ if (usbstatus_kobj == NULL)
+ ret = -ENOMEM;
+ usbstatus_kobj->ktype = &ktype_usbstatus;
+ kobject_init(usbstatus_kobj, usbstatus_kobj->ktype);
+
+ ret = kobject_set_name(usbstatus_kobj, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ ret = kobject_add(usbstatus_kobj, NULL, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ init_timer(&notify_timer);
+ notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ notify_timer.function = funct_host_notify_timer;
+ notify_timer.data = (unsigned long)musb;
+ add_timer(&notify_timer);
+ }
+
+ stm_usb_power_wq = create_singlethread_workqueue(
+ "stm_usb_power_wq");
+ if (stm_usb_power_wq == NULL)
+ return -ENOMEM;
+
+ INIT_WORK(&stm_prcmu_qos, stm_prcmu_qos_work);
+
+ ret = musb_force_detect(musb->board_mode);
+ if (ret < 0)
+ goto done;
+ return 0;
+
+done:
+ usb_nop_xceiv_unregister();
+ return ret;
+}
+/**
+ * musb_platform_exit() - unregister the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function unregisters the USB controller.
+ */
+int musb_platform_exit(struct musb *musb)
+{
+ musb->clock = 0;
+
+ if (musb->board_mode != MUSB_PERIPHERAL)
+ del_timer_sync(&notify_timer);
+
+ usb_nop_xceiv_unregister();
+
+ musb_status = NULL;
+
+ return 0;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.c b/drivers/usb/musb/stm_musb_dma.c
new file mode 100644
index 00000000000..468e96bdcea
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.c
@@ -0,0 +1,722 @@
+/* Copyright (C) 2009 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/pfn.h>
+#include "musb_core.h"
+#include "ste_config.h"
+#include "stm_musb_dma.h"
+#include <plat/ste_dma40.h>
+#include <mach/ste-dma40-db8500.h>
+
+/*
+ * U8500 system DMA used for USB can't transfer less
+ * than max packet size of Buldk EP which is 512
+ */
+#define U8500_USB_DMA_MIN_TRANSFER_SIZE 512
+
+/**
+ * dma_controller_start() - creates the logical channels pool and registers callbacks
+ * @c: pointer to DMA Controller
+ *
+ * This function requests the logical channels from the DMA driver and creates
+ * logical channels based on event lines and also registers the callbacks which
+ * are invoked after data transfer in the transmit or receive direction.
+*/
+
+static int dma_controller_start(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct stedma40_chan_cfg *info;
+ u8 bit;
+ struct dma_channel *channel = NULL;
+ /*bit 0 for receive and bit 1 for transmit*/
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ musb_channel = &(controller->channel[bit]);
+ info = kzalloc(sizeof(struct stedma40_chan_cfg), GFP_KERNEL);
+ if (!info) {
+ ERR("could not allocate dma info structure\n");
+ return -1;
+ }
+ musb_channel->info = info;
+ musb_channel->controller = controller;
+#ifdef CONFIG_USB_U8500_DMA
+ info->high_priority = true;
+#else
+ info->mode = STEDMA40_MODE_PHYSICAL;
+ info->high_priority = true;
+#endif
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ int dst_dev_type;
+
+ info->dir = STEDMA40_MEM_TO_PERIPH;
+ info->src_dev_type = STEDMA40_DEV_SRC_MEMORY;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+
+ case TX_CHANNEL_1:
+ dst_dev_type = DB8500_DMA_DEV38_USB_OTG_OEP_1_9;
+ break;
+ case TX_CHANNEL_2:
+ dst_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_OEP_2_10;
+ break;
+ case TX_CHANNEL_3:
+ dst_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_OEP_3_11;
+ break;
+ case TX_CHANNEL_4:
+ dst_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_OEP_4_12;
+ break;
+ case TX_CHANNEL_5:
+ dst_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_OEP_5_13;
+ break;
+ case TX_CHANNEL_6:
+ dst_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_OEP_6_14;
+ break;
+ case TX_CHANNEL_7:
+ dst_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_OEP_7_15;
+ break;
+
+ }
+
+ info->dst_dev_type = dst_dev_type;
+#endif
+
+ } else {
+ int src_dev_type;
+
+ info->dir = STEDMA40_PERIPH_TO_MEM;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+ case RX_CHANNEL_1:
+ src_dev_type = DB8500_DMA_DEV38_USB_OTG_IEP_1_9;
+ break;
+ case RX_CHANNEL_2:
+ src_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_IEP_2_10;
+ break;
+ case RX_CHANNEL_3:
+ src_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_IEP_3_11;
+ break;
+ case RX_CHANNEL_4:
+ src_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_IEP_4_12;
+ break;
+ case RX_CHANNEL_5:
+ src_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_IEP_5_13;
+ break;
+ case RX_CHANNEL_6:
+ src_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_IEP_6_14;
+ break;
+ case RX_CHANNEL_7:
+ src_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_IEP_7_15;
+ break;
+ }
+
+ info->src_dev_type = src_dev_type;
+#endif
+ info->dst_dev_type = STEDMA40_DEV_DST_MEMORY;
+ }
+ info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ info->src_info.psize = STEDMA40_PSIZE_LOG_16;
+
+ info->dst_info.data_width = STEDMA40_WORD_WIDTH ;
+ info->dst_info.psize = STEDMA40_PSIZE_LOG_16;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+ channel->private_data = musb_channel;
+ channel->status = MUSB_DMA_STATUS_FREE;
+ channel->max_len = 0x10000;
+ /* Tx => mode 1; Rx => mode 0 */
+ channel->desired_mode = bit;
+ channel->actual_len = 0;
+
+ musb_channel->dma_chan = dma_request_channel(mask,
+ stedma40_filter,
+ info);
+ if (!musb_channel->dma_chan)
+ ERR("dma pipe can't be allocated\n");
+#ifndef CONFIG_USB_U8500_DMA
+ /* Tx => mode 1; Rx => mode 0 */
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ INIT_WORK(&musb_channel->channel_data_tx,
+ musb_channel_work_tx);
+ DBG(2, "channel allocated for TX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ } else {
+ INIT_WORK(&musb_channel->channel_data_rx,
+ musb_channel_work_rx);
+ DBG(2, "channel allocated for RX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ }
+
+ }
+ return 0;
+}
+
+static void dma_channel_release(struct dma_channel *channel);
+
+/**
+ * dma_controller_stop() - releases all the channels and frees the DMA pipes
+ * @c: pointer to DMA controller
+ *
+ * This function frees all of the logical channels and frees the DMA pipes
+*/
+
+static int dma_controller_stop(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel;
+ struct dma_channel *channel;
+ u8 bit;
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ channel = &controller->channel[bit].channel;
+ musb_channel = channel->private_data;
+ dma_channel_release(channel);
+ if (musb_channel->info) {
+ dma_release_channel(musb_channel->dma_chan);
+ kfree(musb_channel->info);
+ musb_channel->info = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dma_controller_allocate() - allocates the DMA channels
+ * @c: pointer to DMA controller
+ * @hw_ep: pointer to endpoint
+ * @transmit: transmit or receive direction
+ *
+ * This function allocates the DMA channel and initializes
+ * the channel
+*/
+
+static struct dma_channel *dma_channel_allocate(struct dma_controller *c,
+ struct musb_hw_ep *hw_ep, u8 transmit)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct dma_channel *channel = NULL;
+ u8 bit;
+
+#ifndef CONFIG_USB_U8500_DMA
+
+ /*bit 0 for receive and bit 1 for transmit*/
+ for (bit = 0; bit < 2; bit++) {
+ if (!(controller->used_channels & (1 << bit))) {
+
+ if ((transmit && !bit))
+ continue;
+ if ((!transmit && bit))
+ break;
+#else
+ if (hw_ep->epnum > 0
+ && hw_ep->epnum <= U8500_DMA_END_POINTS) {
+ if (transmit)
+ bit = hw_ep->epnum - 1;
+ else
+ bit =
+ hw_ep->epnum + RX_END_POINT_OFFSET;
+ } else
+ return NULL;
+#endif
+ controller->used_channels |= (1 << bit);
+ musb_channel = &(controller->channel[bit]);
+ musb_channel->idx = bit;
+ musb_channel->epnum = hw_ep->epnum;
+ musb_channel->hw_ep = hw_ep;
+ musb_channel->transmit = transmit;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+#ifndef CONFIG_USB_U8500_DMA
+ break;
+ }
+ }
+#endif
+ return channel;
+}
+
+/**
+ * dma_channel_release() - releases the DMA channel
+ * @channel: channel to be released
+ *
+ * This function releases the DMA channel
+ *
+*/
+
+static void dma_channel_release(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ channel->actual_len = 0;
+ musb_channel->start_addr = 0;
+ musb_channel->len = 0;
+
+ DBG(2, "enter\n");
+ musb_channel->controller->used_channels &=
+ ~(1 << musb_channel->idx);
+
+ channel->status = MUSB_DMA_STATUS_FREE;
+ if (musb_channel->is_pipe_allocated)
+ musb_channel->is_pipe_allocated = 0;
+ DBG(2, "exit\n");
+}
+
+/**
+ * configure_channel() - configures the source, destination addresses and
+ * starts the transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: Dma mode
+ * @dma_addr: DMA source address for transmit direction
+ * or DMA destination address for receive direction
+ * @len: length
+ * This function configures the source and destination addresses for DMA
+ * operation and initiates the DMA transfer
+*/
+
+static bool configure_channel(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ u32 dma_count;
+ struct dma_chan *dma_chan = musb_channel->dma_chan;
+ struct dma_async_tx_descriptor *dma_desc;
+ enum dma_data_direction direction;
+ struct scatterlist sg;
+
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_qh *qh;
+ struct urb *urb;
+#endif
+ unsigned int usb_fifo_addr =
+ (unsigned int)(MUSB_FIFO_OFFSET(hw_ep->epnum) + mbase);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (musb_channel->transmit)
+ qh = hw_ep->out_qh;
+ else
+ qh = hw_ep->in_qh;
+ urb = next_urb(qh);
+#endif
+
+ dma_count = len - (len % packet_sz);
+ musb_channel->cur_len = dma_count;
+ usb_fifo_addr =
+ U8500_USBOTG_BASE + ((unsigned int)usb_fifo_addr & 0xFFFF);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (musb_channel->transmit)
+ dma_addr = musb->tx_dma_phy;
+ else
+ dma_addr = musb->rx_dma_phy;
+ }
+#endif
+
+ stedma40_set_dev_addr(dma_chan, usb_fifo_addr, usb_fifo_addr);
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_addr)), dma_count,
+ offset_in_page(dma_addr));
+ sg_dma_address(&sg) = dma_addr;
+ sg_dma_len(&sg) = dma_count;
+
+ direction = musb_channel->transmit ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ dma_desc = dma_chan->device->
+ device_prep_slave_sg(dma_chan, &sg, 1, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma_desc)
+ return false;
+
+ dma_desc->callback = musb_channel->transmit ?
+ musb_tx_dma_controller_handler :
+ musb_rx_dma_controller_handler;
+ dma_desc->callback_param = channel;
+ dma_desc->tx_submit(dma_desc);
+ dma_async_issue_pending(dma_chan);
+
+ return true;
+}
+
+/**
+ * dma_channel_program() - Configures the channel and initiates transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: mode
+ * @dma_addr: physical address of memory
+ * @len: length
+ *
+ * This function configures the channel and initiates the DMA transfer
+*/
+
+static int dma_channel_program(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ bool ret;
+
+ BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
+ channel->status == MUSB_DMA_STATUS_BUSY);
+ if (len < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return false;
+ if (!musb_channel->transmit && len < packet_sz)
+ return false;
+ channel->actual_len = 0;
+ musb_channel->start_addr = dma_addr;
+ musb_channel->len = len;
+ musb_channel->max_packet_sz = packet_sz;
+ channel->status = MUSB_DMA_STATUS_BUSY;
+
+
+ if ((mode == 1) && (len >= packet_sz))
+ ret = configure_channel(channel, packet_sz, 1, dma_addr, len);
+ else
+ ret = configure_channel(channel, packet_sz, 0, dma_addr, len);
+ return ret;
+}
+
+/**
+ * dma_channel_abort() - aborts the DMA transfer
+ * @channel: pointer to DMA channel.
+ *
+ * This function aborts the DMA transfer.
+*/
+
+static int dma_channel_abort(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ void __iomem *mbase = musb_channel->controller->base;
+ u16 csr;
+ if (channel->status == MUSB_DMA_STATUS_BUSY) {
+ if (musb_channel->transmit) {
+
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_TXCSR));
+ csr &= ~(MUSB_TXCSR_AUTOSET |
+ MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),
+ csr);
+ } else {
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_RXCSR));
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR |
+ MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),
+ csr);
+ }
+
+ if (musb_channel->is_pipe_allocated) {
+ musb_channel->dma_chan->device->
+ device_control(musb_channel->dma_chan, DMA_TERMINATE_ALL, 0);
+ channel->status = MUSB_DMA_STATUS_FREE;
+ }
+ }
+ return 0;
+}
+
+#include <linux/usb/composite.h>
+
+static int dma_is_compatible(struct dma_channel *channel,
+ struct musb_request *req)
+{
+
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ struct usb_descriptor_header **descriptors;
+ struct usb_function *f;
+
+ struct usb_gadget *gadget = &musb->g;
+
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+
+ if (!strcmp(f->name, "cdc_ethernet") ||
+ !strcmp(f->name, "rndis") ||
+ !strcmp(f->name, "adb")) {
+
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+
+ if ((*descriptors)->bDescriptorType !=
+ USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)
+ *descriptors;
+
+ if (ep->bEndpointAddress == req->epnum)
+ return 0;
+ }
+
+ }
+ }
+
+ if (req->request.length < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * musb_rx_dma_controller_handler() - callback invoked when the data is received in the receive direction
+ * @private_data: DMA channel
+ *
+ * This callback is invoked when the DMA transfer is completed
+ * in the receive direction.
+*/
+void musb_rx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int rxcsr;
+ struct musb_qh *qh = hw_ep->in_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ urb = next_urb(qh);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ musb_memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ }
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_rx);
+#endif
+}
+
+/**
+ * musb_tx_dma_controller_handler() - callback invoked on the transmit direction DMA data transfer
+ * @private_data: pointer to DMA channel.
+ *
+ * This callback is invoked when the DMA tranfer is completed
+ * in the transmit direction
+*/
+
+void musb_tx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int txcsr;
+ struct musb_qh *qh = hw_ep->out_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ if (pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ urb = next_urb(qh);
+ qh->offset += channel->actual_len;
+ buf = urb->transfer_buffer + qh->offset;
+ musb_write_fifo(hw_ep, pio, buf);
+ qh->segsize = pio;
+ musb_writew(hw_ep->regs, MUSB_TXCSR, MUSB_TXCSR_TXPKTRDY);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_tx);
+#endif
+}
+
+/**
+ * dma_controller_destroy() - deallocates the DMA controller
+ * @c: pointer to dma controller.
+ *
+ * This function deallocates the DMA controller.
+*/
+
+void dma_controller_destroy(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ u8 bit;
+ if (!controller)
+ return;
+ for (bit = 0; bit < MUSB_HSDMA_CHANNELS; bit++)
+ musb_channel = &(controller->channel[bit]);
+ if (controller->irq)
+ free_irq(controller->irq, c);
+
+ kfree(controller);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the DMA transfer
+ * is completed in the transmit direction.
+*/
+
+static void musb_channel_work_tx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_tx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the
+ * DMA transfer is completed in the receive direction.
+*/
+
+static void musb_channel_work_rx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_rx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(mbase, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * dma_controller_create() - creates the dma controller and initializes callbacks
+ *
+ * @musb: pointer to mentor core driver data instance|
+ * @base: base address of musb registers.
+ *
+ * This function creates the DMA controller and initializes the callbacks
+ * that are invoked from the Mentor IP core.
+*/
+
+struct dma_controller *__init
+dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ struct musb_dma_controller *controller;
+
+ controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return NULL;
+
+ controller->channel_count = MUSB_HSDMA_CHANNELS;
+ controller->private_data = musb;
+ controller->base = base;
+
+ controller->controller.start = dma_controller_start;
+ controller->controller.stop = dma_controller_stop;
+ controller->controller.channel_alloc = dma_channel_allocate;
+ controller->controller.channel_release = dma_channel_release;
+ controller->controller.channel_program = dma_channel_program;
+ controller->controller.channel_abort = dma_channel_abort;
+ controller->controller.is_compatible = dma_is_compatible;
+
+ return &controller->controller;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.h b/drivers/usb/musb/stm_musb_dma.h
new file mode 100644
index 00000000000..753cf08a882
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM_MUSB_DMA_H__
+#define __STM_MUSB_DMA_H__
+
+#define MUSB_HSDMA_CHANNELS 16
+struct musb_dma_controller;
+struct dma_chan;
+struct stedma40_chan_cfg;
+struct musb_dma_channel {
+ struct dma_channel channel;
+ struct musb_dma_controller *controller;
+ struct stedma40_chan_cfg *info;
+ struct musb_hw_ep *hw_ep;
+ struct work_struct channel_data_tx;
+ struct work_struct channel_data_rx;
+ u32 start_addr;
+ u32 len;
+ u32 is_pipe_allocated;
+ u16 max_packet_sz;
+ u8 idx;
+ struct dma_chan *dma_chan;
+ unsigned int cur_len;
+ u8 epnum;
+ u8 last_xfer;
+ u8 transmit;
+};
+
+struct musb_dma_controller {
+ struct dma_controller controller;
+ struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
+ void *private_data;
+ void __iomem *base;
+ u8 channel_count;
+ u8 used_channels;
+ u8 irq;
+};
+void musb_rx_dma_controller_handler(void *private_data);
+void musb_tx_dma_controller_handler(void *private_data);
+static void musb_channel_work_tx(struct work_struct *data);
+static void musb_channel_work_rx(struct work_struct *data);
+#endif
+