diff options
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/musb/Makefile | 5 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 94 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 28 | ||||
-rw-r--r-- | drivers/usb/musb/musb_dma.h | 12 | ||||
-rw-r--r-- | drivers/usb/musb/musb_gadget.c | 262 | ||||
-rw-r--r-- | drivers/usb/musb/musb_gadget.h | 7 | ||||
-rw-r--r-- | drivers/usb/musb/musb_host.c | 125 | ||||
-rw-r--r-- | drivers/usb/musb/musb_virthub.c | 5 | ||||
-rw-r--r-- | drivers/usb/musb/ste_config.h | 52 | ||||
-rw-r--r-- | drivers/usb/musb/stm_musb.c | 638 | ||||
-rw-r--r-- | drivers/usb/musb/stm_musb_dma.c | 722 | ||||
-rw-r--r-- | drivers/usb/musb/stm_musb_dma.h | 59 |
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(¬ify_timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout)) { + if (!timer_pending(¬ify_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(¬ify_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(¬ify_timer); + notify_timer.expires = jiffies + msecs_to_jiffies(1000); + notify_timer.function = funct_host_notify_timer; + notify_timer.data = (unsigned long)musb; + add_timer(¬ify_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(¬ify_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 + |