2008-07-24 13:27:36 +04:00
/*
* MUSB OTG driver core code
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright ( C ) 2005 - 2006 by Texas Instruments
* Copyright ( C ) 2006 - 2007 Nokia Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
* THIS SOFTWARE IS PROVIDED " AS IS " AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
*/
/*
* Inventra ( Multipoint ) Dual - Role Controller Driver for Linux .
*
* This consists of a Host Controller Driver ( HCD ) and a peripheral
* controller driver implementing the " Gadget " API ; OTG support is
* in the works . These are normal Linux - USB controller drivers which
* use IRQs and have no dedicated thread .
*
* This version of the driver has only been used with products from
* Texas Instruments . Those products integrate the Inventra logic
* with other DMA , IRQ , and bus modules , as well as other logic that
* needs to be reflected in this driver .
*
*
* NOTE : the original Mentor code here was pretty much a collection
* of mechanisms that don ' t seem to have been fully integrated / working
* for * any * Linux kernel version . This version aims at Linux 2.6 . now ,
* Key open issues include :
*
* - Lack of host - side transaction scheduling , for all transfer types .
* The hardware doesn ' t do it ; instead , software must .
*
* This is not an issue for OTG devices that don ' t support external
* hubs , but for more " normal " USB hosts it ' s a user issue that the
* " multipoint " support doesn ' t scale in the expected ways . That
* includes DaVinci EVM in a common non - OTG mode .
*
* * Control and bulk use dedicated endpoints , and there ' s as
* yet no mechanism to either ( a ) reclaim the hardware when
* peripherals are NAKing , which gets complicated with bulk
* endpoints , or ( b ) use more than a single bulk endpoint in
* each direction .
*
* RESULT : one device may be perceived as blocking another one .
*
* * Interrupt and isochronous will dynamically allocate endpoint
* hardware , but ( a ) there ' s no record keeping for bandwidth ;
* ( b ) in the common case that few endpoints are available , there
* is no mechanism to reuse endpoints to talk to multiple devices .
*
* RESULT : At one extreme , bandwidth can be overcommitted in
* some hardware configurations , no faults will be reported .
* At the other extreme , the bandwidth capabilities which do
* exist tend to be severely undercommitted . You can ' t yet hook
* up both a keyboard and a mouse to an external USB hub .
*/
/*
* This gets many kinds of configuration information :
* - Kconfig for everything user - configurable
* - platform_device for addressing , irq , and platform_data
* - platform_data is mostly for board - specific informarion
2008-09-11 12:53:23 +04:00
* ( plus recentrly , SOC or family details )
2008-07-24 13:27:36 +04:00
*
* Most of the conditional compilation will ( someday ) vanish .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/kobject.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# ifdef CONFIG_ARM
2008-08-30 20:42:02 +04:00
# include <mach/hardware.h>
# include <mach/memory.h>
2008-07-24 13:27:36 +04:00
# include <asm/mach-types.h>
# endif
# include "musb_core.h"
# ifdef CONFIG_ARCH_DAVINCI
# include "davinci.h"
# endif
2009-03-31 23:32:12 +04:00
# define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
2008-07-24 13:27:36 +04:00
2008-10-29 16:10:39 +03:00
unsigned musb_debug ;
2009-02-21 00:45:17 +03:00
module_param_named ( debug , musb_debug , uint , S_IRUGO | S_IWUSR ) ;
2008-08-10 22:22:35 +04:00
MODULE_PARM_DESC ( debug , " Debug message level. Default = 0 " ) ;
2008-07-24 13:27:36 +04:00
# define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
# define DRIVER_DESC "Inventra Dual-Role USB Controller Driver"
2008-08-10 22:22:35 +04:00
# define MUSB_VERSION "6.0"
2008-07-24 13:27:36 +04:00
# define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION
# define MUSB_DRIVER_NAME "musb_hdrc"
const char musb_driver_name [ ] = MUSB_DRIVER_NAME ;
MODULE_DESCRIPTION ( DRIVER_INFO ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " MUSB_DRIVER_NAME ) ;
/*-------------------------------------------------------------------------*/
static inline struct musb * dev_to_musb ( struct device * dev )
{
# ifdef CONFIG_USB_MUSB_HDRC_HCD
/* usbcore insists dev->driver_data is a "struct hcd *" */
return hcd_to_musb ( dev_get_drvdata ( dev ) ) ;
# else
return dev_get_drvdata ( dev ) ;
# endif
}
/*-------------------------------------------------------------------------*/
2010-03-25 14:25:28 +03:00
# ifndef CONFIG_BLACKFIN
static int musb_ulpi_read ( struct otg_transceiver * otg , u32 offset )
{
void __iomem * addr = otg - > io_priv ;
int i = 0 ;
u8 r ;
u8 power ;
/* Make sure the transceiver is not in low power mode */
power = musb_readb ( addr , MUSB_POWER ) ;
power & = ~ MUSB_POWER_SUSPENDM ;
musb_writeb ( addr , MUSB_POWER , power ) ;
/* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the
* ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM .
*/
musb_writeb ( addr , MUSB_ULPI_REG_ADDR , ( u8 ) offset ) ;
musb_writeb ( addr , MUSB_ULPI_REG_CONTROL ,
MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR ) ;
while ( ! ( musb_readb ( addr , MUSB_ULPI_REG_CONTROL )
& MUSB_ULPI_REG_CMPLT ) ) {
i + + ;
if ( i = = 10000 ) {
DBG ( 3 , " ULPI read timed out \n " ) ;
return - ETIMEDOUT ;
}
}
r = musb_readb ( addr , MUSB_ULPI_REG_CONTROL ) ;
r & = ~ MUSB_ULPI_REG_CMPLT ;
musb_writeb ( addr , MUSB_ULPI_REG_CONTROL , r ) ;
return musb_readb ( addr , MUSB_ULPI_REG_DATA ) ;
}
static int musb_ulpi_write ( struct otg_transceiver * otg ,
u32 offset , u32 data )
{
void __iomem * addr = otg - > io_priv ;
int i = 0 ;
u8 r = 0 ;
u8 power ;
/* Make sure the transceiver is not in low power mode */
power = musb_readb ( addr , MUSB_POWER ) ;
power & = ~ MUSB_POWER_SUSPENDM ;
musb_writeb ( addr , MUSB_POWER , power ) ;
musb_writeb ( addr , MUSB_ULPI_REG_ADDR , ( u8 ) offset ) ;
musb_writeb ( addr , MUSB_ULPI_REG_DATA , ( u8 ) data ) ;
musb_writeb ( addr , MUSB_ULPI_REG_CONTROL , MUSB_ULPI_REG_REQ ) ;
while ( ! ( musb_readb ( addr , MUSB_ULPI_REG_CONTROL )
& MUSB_ULPI_REG_CMPLT ) ) {
i + + ;
if ( i = = 10000 ) {
DBG ( 3 , " ULPI write timed out \n " ) ;
return - ETIMEDOUT ;
}
}
r = musb_readb ( addr , MUSB_ULPI_REG_CONTROL ) ;
r & = ~ MUSB_ULPI_REG_CMPLT ;
musb_writeb ( addr , MUSB_ULPI_REG_CONTROL , r ) ;
return 0 ;
}
# else
2010-06-24 21:37:08 +04:00
# define musb_ulpi_read NULL
# define musb_ulpi_write NULL
2010-03-25 14:25:28 +03:00
# endif
static struct otg_io_access_ops musb_ulpi_access = {
. read = musb_ulpi_read ,
. write = musb_ulpi_write ,
} ;
/*-------------------------------------------------------------------------*/
2008-12-02 22:33:48 +03:00
# if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN)
2008-07-24 13:27:36 +04:00
/*
* Load an endpoint ' s FIFO
*/
void musb_write_fifo ( struct musb_hw_ep * hw_ep , u16 len , const u8 * src )
{
void __iomem * fifo = hw_ep - > fifo ;
prefetch ( ( u8 * ) src ) ;
DBG ( 4 , " %cX ep%d fifo %p count %d buf %p \n " ,
' T ' , hw_ep - > epnum , fifo , len , src ) ;
/* we can't assume unaligned reads work */
if ( likely ( ( 0x01 & ( unsigned long ) src ) = = 0 ) ) {
u16 index = 0 ;
/* best case is 32bit-aligned source address */
if ( ( 0x02 & ( unsigned long ) src ) = = 0 ) {
if ( len > = 4 ) {
writesl ( fifo , src + index , len > > 2 ) ;
index + = len & ~ 0x03 ;
}
if ( len & 0x02 ) {
musb_writew ( fifo , 0 , * ( u16 * ) & src [ index ] ) ;
index + = 2 ;
}
} else {
if ( len > = 2 ) {
writesw ( fifo , src + index , len > > 1 ) ;
index + = len & ~ 0x01 ;
}
}
if ( len & 0x01 )
musb_writeb ( fifo , 0 , src [ index ] ) ;
} else {
/* byte aligned */
writesb ( fifo , src , len ) ;
}
}
/*
* Unload an endpoint ' s FIFO
*/
void musb_read_fifo ( struct musb_hw_ep * hw_ep , u16 len , u8 * dst )
{
void __iomem * fifo = hw_ep - > fifo ;
DBG ( 4 , " %cX ep%d fifo %p count %d buf %p \n " ,
' R ' , hw_ep - > epnum , fifo , len , dst ) ;
/* we can't assume unaligned writes work */
if ( likely ( ( 0x01 & ( unsigned long ) dst ) = = 0 ) ) {
u16 index = 0 ;
/* best case is 32bit-aligned destination address */
if ( ( 0x02 & ( unsigned long ) dst ) = = 0 ) {
if ( len > = 4 ) {
readsl ( fifo , dst , len > > 2 ) ;
index = len & ~ 0x03 ;
}
if ( len & 0x02 ) {
* ( u16 * ) & dst [ index ] = musb_readw ( fifo , 0 ) ;
index + = 2 ;
}
} else {
if ( len > = 2 ) {
readsw ( fifo , dst , len > > 1 ) ;
index = len & ~ 0x01 ;
}
}
if ( len & 0x01 )
dst [ index ] = musb_readb ( fifo , 0 ) ;
} else {
/* byte aligned */
readsb ( fifo , dst , len ) ;
}
}
# endif /* normal PIO */
/*-------------------------------------------------------------------------*/
/* for high speed test mode; see USB 2.0 spec 7.1.20 */
static const u8 musb_test_packet [ 53 ] = {
/* implicit SYNC then DATA0 to start */
/* JKJKJKJK x9 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
/* JJKKJJKK x8 */
0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa ,
/* JJJJKKKK x8 */
0xee , 0xee , 0xee , 0xee , 0xee , 0xee , 0xee , 0xee ,
/* JJJJJJJKKKKKKK x8 */
0xfe , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
/* JJJJJJJK x8 */
0x7f , 0xbf , 0xdf , 0xef , 0xf7 , 0xfb , 0xfd ,
/* JKKKKKKK x10, JK */
0xfc , 0x7e , 0xbf , 0xdf , 0xef , 0xf7 , 0xfb , 0xfd , 0x7e
/* implicit CRC16 then EOP to end */
} ;
void musb_load_testpacket ( struct musb * musb )
{
void __iomem * regs = musb - > endpoints [ 0 ] . regs ;
musb_ep_select ( musb - > mregs , 0 ) ;
musb_write_fifo ( musb - > control_ep ,
sizeof ( musb_test_packet ) , musb_test_packet ) ;
musb_writew ( regs , MUSB_CSR0 , MUSB_CSR0_TXPKTRDY ) ;
}
/*-------------------------------------------------------------------------*/
const char * otg_state_string ( struct musb * musb )
{
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_A_IDLE : return " a_idle " ;
case OTG_STATE_A_WAIT_VRISE : return " a_wait_vrise " ;
case OTG_STATE_A_WAIT_BCON : return " a_wait_bcon " ;
case OTG_STATE_A_HOST : return " a_host " ;
case OTG_STATE_A_SUSPEND : return " a_suspend " ;
case OTG_STATE_A_PERIPHERAL : return " a_peripheral " ;
case OTG_STATE_A_WAIT_VFALL : return " a_wait_vfall " ;
case OTG_STATE_A_VBUS_ERR : return " a_vbus_err " ;
case OTG_STATE_B_IDLE : return " b_idle " ;
case OTG_STATE_B_SRP_INIT : return " b_srp_init " ;
case OTG_STATE_B_PERIPHERAL : return " b_peripheral " ;
case OTG_STATE_B_WAIT_ACON : return " b_wait_acon " ;
case OTG_STATE_B_HOST : return " b_host " ;
default : return " UNDEFINED " ;
}
}
# ifdef CONFIG_USB_MUSB_OTG
/*
* Handles OTG hnp timeouts , such as b_ase0_brst
*/
void musb_otg_timer_func ( unsigned long data )
{
struct musb * musb = ( struct musb * ) data ;
unsigned long flags ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_B_WAIT_ACON :
DBG ( 1 , " HNP: b_wait_acon timeout; back to b_peripheral \n " ) ;
musb_g_disconnect ( musb ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
2008-07-24 13:27:36 +04:00
musb - > is_active = 0 ;
break ;
2009-03-31 23:35:09 +04:00
case OTG_STATE_A_SUSPEND :
2008-07-24 13:27:36 +04:00
case OTG_STATE_A_WAIT_BCON :
2009-03-31 23:35:09 +04:00
DBG ( 1 , " HNP: %s timeout \n " , otg_state_string ( musb ) ) ;
musb_set_vbus ( musb , 0 ) ;
musb - > xceiv - > state = OTG_STATE_A_WAIT_VFALL ;
2008-07-24 13:27:36 +04:00
break ;
default :
DBG ( 1 , " HNP: Unhandled mode %s \n " , otg_state_string ( musb ) ) ;
}
musb - > ignore_disconnect = 0 ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
}
/*
2009-03-31 23:32:12 +04:00
* Stops the HNP transition . Caller must take care of locking .
2008-07-24 13:27:36 +04:00
*/
void musb_hnp_stop ( struct musb * musb )
{
struct usb_hcd * hcd = musb_to_hcd ( musb ) ;
void __iomem * mbase = musb - > mregs ;
u8 reg ;
2009-03-31 23:35:09 +04:00
DBG ( 1 , " HNP: stop from %s \n " , otg_state_string ( musb ) ) ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_A_PERIPHERAL :
musb_g_disconnect ( musb ) ;
2009-03-31 23:35:09 +04:00
DBG ( 1 , " HNP: back to %s \n " , otg_state_string ( musb ) ) ;
2008-07-24 13:27:36 +04:00
break ;
case OTG_STATE_B_HOST :
DBG ( 1 , " HNP: Disabling HR \n " ) ;
hcd - > self . is_b_host = 0 ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
2008-07-24 13:27:36 +04:00
MUSB_DEV_MODE ( musb ) ;
reg = musb_readb ( mbase , MUSB_POWER ) ;
reg | = MUSB_POWER_SUSPENDM ;
musb_writeb ( mbase , MUSB_POWER , reg ) ;
/* REVISIT: Start SESSION_REQUEST here? */
break ;
default :
DBG ( 1 , " HNP: Stopping in unknown state %s \n " ,
otg_state_string ( musb ) ) ;
}
/*
* When returning to A state after HNP , avoid hub_port_rebounce ( ) ,
* which cause occasional OPT A " Did not receive reset after connect "
* errors .
*/
2010-03-05 01:05:08 +03:00
musb - > port1_status & = ~ ( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
2008-07-24 13:27:36 +04:00
}
# endif
/*
* Interrupt Service Routine to record USB " global " interrupts .
* Since these do not happen often and signify things of
* paramount importance , it seems OK to check them individually ;
* the order of the tests is specified in the manual
*
* @ param musb instance pointer
* @ param int_usb register contents
* @ param devctl
* @ param power
*/
static irqreturn_t musb_stage0_irq ( struct musb * musb , u8 int_usb ,
u8 devctl , u8 power )
{
irqreturn_t handled = IRQ_NONE ;
DBG ( 3 , " <== Power=%02x, DevCtl=%02x, int_usb=0x%x \n " , power , devctl ,
int_usb ) ;
/* 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 .
*/
if ( int_usb & MUSB_INTR_RESUME ) {
handled = IRQ_HANDLED ;
DBG ( 3 , " RESUME (%s) \n " , otg_state_string ( musb ) ) ;
if ( devctl & MUSB_DEVCTL_HM ) {
# ifdef CONFIG_USB_MUSB_HDRC_HCD
2010-03-12 11:27:24 +03:00
void __iomem * mbase = musb - > mregs ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_A_SUSPEND :
/* remote wakeup? later, GetPortStatus
* will stop RESUME signaling
*/
if ( power & MUSB_POWER_SUSPENDM ) {
/* spurious */
musb - > int_usb & = ~ MUSB_INTR_SUSPEND ;
DBG ( 2 , " Spurious SUSPENDM \n " ) ;
break ;
}
power & = ~ MUSB_POWER_SUSPENDM ;
musb_writeb ( mbase , MUSB_POWER ,
power | MUSB_POWER_RESUME ) ;
musb - > port1_status | =
( USB_PORT_STAT_C_SUSPEND < < 16 )
| MUSB_PORT_STAT_RESUME ;
musb - > rh_timer = jiffies
+ msecs_to_jiffies ( 20 ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_HOST ;
2008-07-24 13:27:36 +04:00
musb - > is_active = 1 ;
usb_hcd_resume_root_hub ( musb_to_hcd ( musb ) ) ;
break ;
case OTG_STATE_B_WAIT_ACON :
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
2008-07-24 13:27:36 +04:00
musb - > is_active = 1 ;
MUSB_DEV_MODE ( musb ) ;
break ;
default :
WARNING ( " bogus %s RESUME (%s) \n " ,
" host " ,
otg_state_string ( musb ) ) ;
}
# endif
} else {
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
# ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_SUSPEND :
/* possibly DISCONNECT is upcoming */
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_HOST ;
2008-07-24 13:27:36 +04:00
usb_hcd_resume_root_hub ( musb_to_hcd ( musb ) ) ;
break ;
# endif
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_WAIT_ACON :
case OTG_STATE_B_PERIPHERAL :
/* disconnect while suspended? we may
* not get a disconnect irq . . .
*/
if ( ( devctl & MUSB_DEVCTL_VBUS )
! = ( 3 < < MUSB_DEVCTL_VBUS_SHIFT )
) {
musb - > int_usb | = MUSB_INTR_DISCONNECT ;
musb - > int_usb & = ~ MUSB_INTR_SUSPEND ;
break ;
}
musb_g_resume ( musb ) ;
break ;
case OTG_STATE_B_IDLE :
musb - > int_usb & = ~ MUSB_INTR_SUSPEND ;
break ;
# endif
default :
WARNING ( " bogus %s RESUME (%s) \n " ,
" peripheral " ,
otg_state_string ( musb ) ) ;
}
}
}
# ifdef CONFIG_USB_MUSB_HDRC_HCD
/* see manual for the order of the tests */
if ( int_usb & MUSB_INTR_SESSREQ ) {
2010-03-12 11:27:24 +03:00
void __iomem * mbase = musb - > mregs ;
2008-07-24 13:27:36 +04:00
DBG ( 1 , " SESSION_REQUEST (%s) \n " , otg_state_string ( musb ) ) ;
/* IRQ arrives from ID pin sense or (later, if VBUS power
* is removed ) SRP . responses are time critical :
* - turn on VBUS ( with silicon - specific mechanism )
* - go through A_WAIT_VRISE
* - . . . to A_WAIT_BCON .
* a_wait_vrise_tmout triggers VBUS_ERROR transitions
*/
musb_writeb ( mbase , MUSB_DEVCTL , MUSB_DEVCTL_SESSION ) ;
musb - > ep0_stage = MUSB_EP0_START ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_IDLE ;
2008-07-24 13:27:36 +04:00
MUSB_HST_MODE ( musb ) ;
musb_set_vbus ( musb , 1 ) ;
handled = IRQ_HANDLED ;
}
if ( int_usb & MUSB_INTR_VBUSERROR ) {
int ignore = 0 ;
/* During connection as an A-Device, we may see a short
* current spikes causing voltage drop , because of cable
* and peripheral capacitance combined with vbus draw .
* ( So : less common with truly self - powered devices , where
* vbus doesn ' t act like a power supply . )
*
* Such spikes are short ; usually less than ~ 500 usec , max
* of ~ 2 msec . That is , they ' re not sustained overcurrent
* errors , though they ' re reported using VBUSERROR irqs .
*
* Workarounds : ( a ) hardware : use self powered devices .
* ( b ) software : ignore non - repeated VBUS errors .
*
* REVISIT : do delays from lots of DEBUG_KERNEL checks
* make trouble here , keeping VBUS < 4.4 V ?
*/
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_A_HOST :
/* recovery is dicey once we've gotten past the
* initial stages of enumeration , but if VBUS
* stayed ok at the other end of the link , and
* another reset is due ( at least for high speed ,
* to redo the chirp etc ) , it might work OK . . .
*/
case OTG_STATE_A_WAIT_BCON :
case OTG_STATE_A_WAIT_VRISE :
if ( musb - > vbuserr_retry ) {
2010-03-12 11:27:24 +03:00
void __iomem * mbase = musb - > mregs ;
2008-07-24 13:27:36 +04:00
musb - > vbuserr_retry - - ;
ignore = 1 ;
devctl | = MUSB_DEVCTL_SESSION ;
musb_writeb ( mbase , MUSB_DEVCTL , devctl ) ;
} else {
musb - > port1_status | =
2010-03-05 01:05:08 +03:00
USB_PORT_STAT_OVERCURRENT
| ( USB_PORT_STAT_C_OVERCURRENT < < 16 ) ;
2008-07-24 13:27:36 +04:00
}
break ;
default :
break ;
}
DBG ( 1 , " VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x \n " ,
otg_state_string ( musb ) ,
devctl ,
( { char * s ;
switch ( devctl & MUSB_DEVCTL_VBUS ) {
case 0 < < MUSB_DEVCTL_VBUS_SHIFT :
s = " <SessEnd " ; break ;
case 1 < < MUSB_DEVCTL_VBUS_SHIFT :
s = " <AValid " ; break ;
case 2 < < MUSB_DEVCTL_VBUS_SHIFT :
s = " <VBusValid " ; break ;
/* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
default :
s = " VALID " ; break ;
} ; s ; } ) ,
VBUSERR_RETRY_COUNT - musb - > vbuserr_retry ,
musb - > port1_status ) ;
/* go through A_WAIT_VFALL then start a new session */
if ( ! ignore )
musb_set_vbus ( musb , 0 ) ;
handled = IRQ_HANDLED ;
}
2010-06-15 13:10:27 +04:00
# endif
2009-12-28 14:40:40 +03:00
if ( int_usb & MUSB_INTR_SUSPEND ) {
DBG ( 1 , " SUSPEND (%s) devctl %02x power %02x \n " ,
otg_state_string ( musb ) , devctl , power ) ;
handled = IRQ_HANDLED ;
switch ( musb - > xceiv - > state ) {
# ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_A_PERIPHERAL :
/* We also come here if the cable is removed, since
* this silicon doesn ' t report ID - no - longer - grounded .
*
* We depend on T ( a_wait_bcon ) to shut us down , and
* hope users don ' t do anything dicey during this
* undesired detour through A_WAIT_BCON .
*/
musb_hnp_stop ( musb ) ;
usb_hcd_resume_root_hub ( musb_to_hcd ( musb ) ) ;
musb_root_disconnect ( musb ) ;
musb_platform_try_idle ( musb , jiffies
+ msecs_to_jiffies ( musb - > a_wait_bcon
? : OTG_TIME_A_WAIT_BCON ) ) ;
break ;
# endif
case OTG_STATE_B_IDLE :
if ( ! musb - > is_active )
break ;
case OTG_STATE_B_PERIPHERAL :
musb_g_suspend ( musb ) ;
musb - > is_active = is_otg_enabled ( musb )
& & musb - > xceiv - > gadget - > b_hnp_enable ;
if ( musb - > is_active ) {
# ifdef CONFIG_USB_MUSB_OTG
musb - > xceiv - > state = OTG_STATE_B_WAIT_ACON ;
DBG ( 1 , " HNP: Setting timer for b_ase0_brst \n " ) ;
mod_timer ( & musb - > otg_timer , jiffies
+ msecs_to_jiffies (
OTG_TIME_B_ASE0_BRST ) ) ;
# endif
}
break ;
case OTG_STATE_A_WAIT_BCON :
if ( musb - > a_wait_bcon ! = 0 )
musb_platform_try_idle ( musb , jiffies
+ msecs_to_jiffies ( musb - > a_wait_bcon ) ) ;
break ;
case OTG_STATE_A_HOST :
musb - > xceiv - > state = OTG_STATE_A_SUSPEND ;
musb - > is_active = is_otg_enabled ( musb )
& & musb - > xceiv - > host - > b_hnp_enable ;
break ;
case OTG_STATE_B_HOST :
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
DBG ( 1 , " REVISIT: SUSPEND as B_HOST \n " ) ;
break ;
default :
/* "should not happen" */
musb - > is_active = 0 ;
break ;
}
}
2010-06-15 13:10:27 +04:00
# ifdef CONFIG_USB_MUSB_HDRC_HCD
2008-07-24 13:27:36 +04:00
if ( int_usb & MUSB_INTR_CONNECT ) {
struct usb_hcd * hcd = musb_to_hcd ( musb ) ;
handled = IRQ_HANDLED ;
musb - > is_active = 1 ;
set_bit ( HCD_FLAG_SAW_IRQ , & hcd - > flags ) ;
musb - > ep0_stage = MUSB_EP0_START ;
# ifdef CONFIG_USB_MUSB_OTG
/* flush endpoints when transitioning from Device Mode */
if ( is_peripheral_active ( musb ) ) {
/* REVISIT HNP; just force disconnect */
}
2010-07-08 12:33:00 +04:00
musb_writew ( musb - > mregs , MUSB_INTRTXE , musb - > epmask ) ;
musb_writew ( musb - > mregs , MUSB_INTRRXE , musb - > epmask & 0xfffe ) ;
musb_writeb ( musb - > mregs , MUSB_INTRUSBE , 0xf7 ) ;
2008-07-24 13:27:36 +04:00
# endif
musb - > port1_status & = ~ ( USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED
| USB_PORT_STAT_ENABLE
) ;
musb - > port1_status | = USB_PORT_STAT_CONNECTION
| ( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
/* high vs full speed is just a guess until after reset */
if ( devctl & MUSB_DEVCTL_LSDEV )
musb - > port1_status | = USB_PORT_STAT_LOW_SPEED ;
/* indicate new connection to OTG machine */
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
case OTG_STATE_B_PERIPHERAL :
if ( int_usb & MUSB_INTR_SUSPEND ) {
DBG ( 1 , " HNP: SUSPEND+CONNECT, now b_host \n " ) ;
int_usb & = ~ MUSB_INTR_SUSPEND ;
2009-04-02 21:16:11 +04:00
goto b_host ;
2008-07-24 13:27:36 +04:00
} else
DBG ( 1 , " CONNECT as b_peripheral??? \n " ) ;
break ;
case OTG_STATE_B_WAIT_ACON :
2009-04-02 21:16:11 +04:00
DBG ( 1 , " HNP: CONNECT, now b_host \n " ) ;
b_host :
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_HOST ;
2008-07-24 13:27:36 +04:00
hcd - > self . is_b_host = 1 ;
2009-04-02 21:16:11 +04:00
musb - > ignore_disconnect = 0 ;
del_timer ( & musb - > otg_timer ) ;
2008-07-24 13:27:36 +04:00
break ;
default :
if ( ( devctl & MUSB_DEVCTL_VBUS )
= = ( 3 < < MUSB_DEVCTL_VBUS_SHIFT ) ) {
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_HOST ;
2008-07-24 13:27:36 +04:00
hcd - > self . is_b_host = 0 ;
}
break ;
}
2009-04-02 21:16:11 +04:00
/* poke the root hub */
MUSB_HST_MODE ( musb ) ;
if ( hcd - > status_urb )
usb_hcd_poll_rh_status ( hcd ) ;
else
usb_hcd_resume_root_hub ( hcd ) ;
2008-07-24 13:27:36 +04:00
DBG ( 1 , " CONNECT (%s) devctl %02x \n " ,
otg_state_string ( musb ) , devctl ) ;
}
# endif /* CONFIG_USB_MUSB_HDRC_HCD */
2009-12-28 14:40:40 +03:00
if ( ( int_usb & MUSB_INTR_DISCONNECT ) & & ! musb - > ignore_disconnect ) {
DBG ( 1 , " DISCONNECT (%s) as %s, devctl %02x \n " ,
otg_state_string ( musb ) ,
MUSB_MODE ( musb ) , devctl ) ;
handled = IRQ_HANDLED ;
switch ( musb - > xceiv - > state ) {
# ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST :
case OTG_STATE_A_SUSPEND :
usb_hcd_resume_root_hub ( musb_to_hcd ( musb ) ) ;
musb_root_disconnect ( musb ) ;
if ( musb - > a_wait_bcon ! = 0 & & is_otg_enabled ( musb ) )
musb_platform_try_idle ( musb , jiffies
+ msecs_to_jiffies ( musb - > a_wait_bcon ) ) ;
break ;
# endif /* HOST */
# ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_B_HOST :
/* REVISIT this behaves for "real disconnect"
* cases ; make sure the other transitions from
* from B_HOST act right too . The B_HOST code
* in hnp_stop ( ) is currently not used . . .
*/
musb_root_disconnect ( musb ) ;
musb_to_hcd ( musb ) - > self . is_b_host = 0 ;
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
MUSB_DEV_MODE ( musb ) ;
musb_g_disconnect ( musb ) ;
break ;
case OTG_STATE_A_PERIPHERAL :
musb_hnp_stop ( musb ) ;
musb_root_disconnect ( musb ) ;
/* FALLTHROUGH */
case OTG_STATE_B_WAIT_ACON :
/* FALLTHROUGH */
# endif /* OTG */
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_PERIPHERAL :
case OTG_STATE_B_IDLE :
musb_g_disconnect ( musb ) ;
break ;
# endif /* GADGET */
default :
WARNING ( " unhandled DISCONNECT transition (%s) \n " ,
otg_state_string ( musb ) ) ;
break ;
}
}
2008-07-24 13:27:36 +04:00
/* mentor saves a bit: bus reset and babble share the same irq.
* only host sees babble ; only peripheral sees bus reset .
*/
if ( int_usb & MUSB_INTR_RESET ) {
2009-12-28 14:40:40 +03:00
handled = IRQ_HANDLED ;
2008-07-24 13:27:36 +04:00
if ( is_host_capable ( ) & & ( devctl & MUSB_DEVCTL_HM ) ! = 0 ) {
/*
* Looks like non - HS BABBLE can be ignored , but
* HS BABBLE is an error condition . For HS the solution
* is to avoid babble in the first place and fix what
* caused BABBLE . When HS BABBLE happens we can only
* stop the session .
*/
if ( devctl & ( MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV ) )
DBG ( 1 , " BABBLE devctl: %02x \n " , devctl ) ;
else {
ERR ( " Stopping host session -- babble \n " ) ;
2009-12-28 14:40:40 +03:00
musb_writeb ( musb - > mregs , MUSB_DEVCTL , 0 ) ;
2008-07-24 13:27:36 +04:00
}
} else if ( is_peripheral_capable ( ) ) {
DBG ( 1 , " BUS RESET as %s \n " , otg_state_string ( musb ) ) ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
# ifdef CONFIG_USB_OTG
case OTG_STATE_A_SUSPEND :
/* We need to ignore disconnect on suspend
* otherwise tusb 2.0 won ' t reconnect after a
* power cycle , which breaks otg compliance .
*/
musb - > ignore_disconnect = 1 ;
musb_g_reset ( musb ) ;
/* FALLTHROUGH */
case OTG_STATE_A_WAIT_BCON : /* OPT TD.4.7-900ms */
2009-03-31 23:32:12 +04:00
/* never use invalid T(a_wait_bcon) */
DBG ( 1 , " HNP: in %s, %d msec timeout \n " ,
otg_state_string ( musb ) ,
TA_WAIT_BCON ( musb ) ) ;
mod_timer ( & musb - > otg_timer , jiffies
+ msecs_to_jiffies ( TA_WAIT_BCON ( musb ) ) ) ;
2008-07-24 13:27:36 +04:00
break ;
case OTG_STATE_A_PERIPHERAL :
2009-04-02 21:16:11 +04:00
musb - > ignore_disconnect = 0 ;
del_timer ( & musb - > otg_timer ) ;
musb_g_reset ( musb ) ;
2008-07-24 13:27:36 +04:00
break ;
case OTG_STATE_B_WAIT_ACON :
DBG ( 1 , " HNP: RESET (%s), to b_peripheral \n " ,
otg_state_string ( musb ) ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
2008-07-24 13:27:36 +04:00
musb_g_reset ( musb ) ;
break ;
# endif
case OTG_STATE_B_IDLE :
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_PERIPHERAL ;
2008-07-24 13:27:36 +04:00
/* FALLTHROUGH */
case OTG_STATE_B_PERIPHERAL :
musb_g_reset ( musb ) ;
break ;
default :
DBG ( 1 , " Unhandled BUS RESET as %s \n " ,
otg_state_string ( musb ) ) ;
}
}
}
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
* supporting transfer phasing to prevent exceeding ISO bandwidth
* limits of a given frame or microframe .
*
* It ' s not needed for peripheral side , which dedicates endpoints ;
* though it _might_ use SOF irqs for other purposes .
*
* And it ' s not currently needed for host side , which also dedicates
* endpoints , relies on TX / RX interval registers , and isn ' t claimed
* to support ISO transfers yet .
*/
if ( int_usb & MUSB_INTR_SOF ) {
void __iomem * mbase = musb - > mregs ;
struct musb_hw_ep * ep ;
u8 epnum ;
u16 frame ;
DBG ( 6 , " START_OF_FRAME \n " ) ;
handled = IRQ_HANDLED ;
/* start any periodic Tx transfers waiting for current frame */
frame = musb_readw ( mbase , MUSB_FRAME ) ;
ep = musb - > endpoints ;
for ( epnum = 1 ; ( epnum < musb - > nr_endpoints )
& & ( musb - > epmask > = ( 1 < < epnum ) ) ;
epnum + + , ep + + ) {
/*
* FIXME handle framecounter wraps ( 12 bits )
* eliminate duplicated StartUrb logic
*/
if ( ep - > dwWaitFrame > = frame ) {
ep - > dwWaitFrame = 0 ;
pr_debug ( " SOF --> periodic TX%s on %d \n " ,
ep - > tx_channel ? " DMA " : " " ,
epnum ) ;
if ( ! ep - > tx_channel )
musb_h_tx_start ( musb , epnum ) ;
else
cppi_hostdma_start ( musb , epnum ) ;
}
} /* end of for loop */
}
# endif
2009-12-28 14:40:40 +03:00
schedule_work ( & musb - > irq_work ) ;
2008-07-24 13:27:36 +04:00
return handled ;
}
/*-------------------------------------------------------------------------*/
/*
* Program the HDRC to start ( enable interrupts , dma , etc . ) .
*/
void musb_start ( struct musb * musb )
{
void __iomem * regs = musb - > mregs ;
u8 devctl = musb_readb ( regs , MUSB_DEVCTL ) ;
DBG ( 2 , " <== devctl %02x \n " , devctl ) ;
/* Set INT enable registers, enable interrupts */
musb_writew ( regs , MUSB_INTRTXE , musb - > epmask ) ;
musb_writew ( regs , MUSB_INTRRXE , musb - > epmask & 0xfffe ) ;
musb_writeb ( regs , MUSB_INTRUSBE , 0xf7 ) ;
musb_writeb ( regs , MUSB_TESTMODE , 0 ) ;
/* put into basic highspeed mode and start session */
musb_writeb ( regs , MUSB_POWER , MUSB_POWER_ISOUPDATE
| MUSB_POWER_SOFTCONN
| MUSB_POWER_HSENAB
/* ENSUSPEND wedges tusb */
/* | MUSB_POWER_ENSUSPEND */
) ;
musb - > is_active = 0 ;
devctl = musb_readb ( regs , MUSB_DEVCTL ) ;
devctl & = ~ MUSB_DEVCTL_SESSION ;
if ( is_otg_enabled ( musb ) ) {
/* session started after:
* ( a ) ID - grounded irq , host mode ;
* ( b ) vbus present / connect IRQ , peripheral mode ;
* ( c ) peripheral initiates , using SRP
*/
if ( ( devctl & MUSB_DEVCTL_VBUS ) = = MUSB_DEVCTL_VBUS )
musb - > is_active = 1 ;
else
devctl | = MUSB_DEVCTL_SESSION ;
} else if ( is_host_enabled ( musb ) ) {
/* assume ID pin is hard-wired to ground */
devctl | = MUSB_DEVCTL_SESSION ;
} else /* peripheral is enabled */ {
if ( ( devctl & MUSB_DEVCTL_VBUS ) = = MUSB_DEVCTL_VBUS )
musb - > is_active = 1 ;
}
musb_platform_enable ( musb ) ;
musb_writeb ( regs , MUSB_DEVCTL , devctl ) ;
}
static void musb_generic_disable ( struct musb * musb )
{
void __iomem * mbase = musb - > mregs ;
u16 temp ;
/* disable interrupts */
musb_writeb ( mbase , MUSB_INTRUSBE , 0 ) ;
musb_writew ( mbase , MUSB_INTRTXE , 0 ) ;
musb_writew ( mbase , MUSB_INTRRXE , 0 ) ;
/* off */
musb_writeb ( mbase , MUSB_DEVCTL , 0 ) ;
/* flush pending interrupts */
temp = musb_readb ( mbase , MUSB_INTRUSB ) ;
temp = musb_readw ( mbase , MUSB_INTRTX ) ;
temp = musb_readw ( mbase , MUSB_INTRRX ) ;
}
/*
* Make the HDRC stop ( disable interrupts , etc . ) ;
* reversible by musb_start
* called on gadget driver unregister
* with controller locked , irqs blocked
* acts as a NOP unless some role activated the hardware
*/
void musb_stop ( struct musb * musb )
{
/* stop IRQs, timers, ... */
musb_platform_disable ( musb ) ;
musb_generic_disable ( musb ) ;
DBG ( 3 , " HDRC disabled \n " ) ;
/* FIXME
* - mark host and / or peripheral drivers unusable / inactive
* - disable DMA ( and enable it in HdrcStart )
* - make sure we can musb_start ( ) after musb_stop ( ) ; with
* OTG mode , gadget driver module rmmod / modprobe cycles that
* - . . .
*/
musb_platform_try_idle ( musb , 0 ) ;
}
static void musb_shutdown ( struct platform_device * pdev )
{
struct musb * musb = dev_to_musb ( & pdev - > dev ) ;
unsigned long flags ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
musb_platform_disable ( musb ) ;
musb_generic_disable ( musb ) ;
2010-03-25 14:14:29 +03:00
if ( musb - > clock )
2008-07-24 13:27:36 +04:00
clk_put ( musb - > clock ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
/* FIXME power down */
}
/*-------------------------------------------------------------------------*/
/*
* The silicon either has hard - wired endpoint configurations , or else
* " dynamic fifo " sizing . The driver has support for both , though at this
2008-09-11 12:53:23 +04:00
* writing only the dynamic sizing is very well tested . Since we switched
* away from compile - time hardware parameters , we can no longer rely on
* dead code elimination to leave only the relevant one in the object file .
2008-07-24 13:27:36 +04:00
*
* We don ' t currently use dynamic fifo setup capability to do anything
* more than selecting one of a bunch of predefined configurations .
*/
# if defined(CONFIG_USB_TUSB6010) || \
2010-03-12 11:29:09 +03:00
defined ( CONFIG_ARCH_OMAP2430 ) | | defined ( CONFIG_ARCH_OMAP3 ) \
| | defined ( CONFIG_ARCH_OMAP4 )
2008-07-24 13:27:36 +04:00
static ushort __initdata fifo_mode = 4 ;
# else
static ushort __initdata fifo_mode = 2 ;
# endif
/* "modprobe ... fifo_mode=1" etc */
module_param ( fifo_mode , ushort , 0 ) ;
MODULE_PARM_DESC ( fifo_mode , " initial endpoint configuration " ) ;
/*
* tables defining fifo_mode values . define more if you like .
* for host side , make sure both halves of ep1 are set up .
*/
/* mode 0 - fits in 2KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_0_cfg [ ] = {
2008-07-24 13:27:36 +04:00
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_RXTX , . maxpacket = 512 , } ,
{ . hw_ep_num = 3 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
} ;
/* mode 1 - fits in 4KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_1_cfg [ ] = {
2008-07-24 13:27:36 +04:00
{ . 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_RXTX , . maxpacket = 512 , . mode = BUF_DOUBLE , } ,
{ . hw_ep_num = 3 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
} ;
/* mode 2 - fits in 4KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_2_cfg [ ] = {
2008-07-24 13:27:36 +04:00
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 3 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
} ;
/* mode 3 - fits in 4KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_3_cfg [ ] = {
2008-07-24 13:27:36 +04:00
{ . 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_RXTX , . maxpacket = 256 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RXTX , . maxpacket = 256 , } ,
} ;
/* mode 4 - fits in 16KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_4_cfg [ ] = {
2008-07-24 13:27:36 +04:00
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . 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 , } ,
{ . hw_ep_num = 3 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 6 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 6 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 7 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 7 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 8 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 8 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 9 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 9 , . style = FIFO_RX , . maxpacket = 512 , } ,
2009-04-04 03:16:17 +04:00
{ . hw_ep_num = 10 , . style = FIFO_TX , . maxpacket = 256 , } ,
{ . hw_ep_num = 10 , . style = FIFO_RX , . maxpacket = 64 , } ,
{ . hw_ep_num = 11 , . style = FIFO_TX , . maxpacket = 256 , } ,
{ . hw_ep_num = 11 , . style = FIFO_RX , . maxpacket = 64 , } ,
{ . hw_ep_num = 12 , . style = FIFO_TX , . maxpacket = 256 , } ,
{ . hw_ep_num = 12 , . style = FIFO_RX , . maxpacket = 64 , } ,
{ . hw_ep_num = 13 , . style = FIFO_RXTX , . maxpacket = 4096 , } ,
2008-07-24 13:27:36 +04:00
{ . hw_ep_num = 14 , . style = FIFO_RXTX , . maxpacket = 1024 , } ,
{ . hw_ep_num = 15 , . style = FIFO_RXTX , . maxpacket = 1024 , } ,
} ;
2009-12-28 14:40:34 +03:00
/* mode 5 - fits in 8KB */
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata mode_5_cfg [ ] = {
2009-12-28 14:40:34 +03:00
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . 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 , } ,
{ . hw_ep_num = 3 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 6 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 6 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 7 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 7 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 8 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 8 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 9 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 9 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 10 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 10 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 11 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 11 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 12 , . style = FIFO_TX , . maxpacket = 32 , } ,
{ . hw_ep_num = 12 , . style = FIFO_RX , . maxpacket = 32 , } ,
{ . hw_ep_num = 13 , . style = FIFO_RXTX , . maxpacket = 512 , } ,
{ . hw_ep_num = 14 , . style = FIFO_RXTX , . maxpacket = 1024 , } ,
{ . hw_ep_num = 15 , . style = FIFO_RXTX , . maxpacket = 1024 , } ,
} ;
2008-07-24 13:27:36 +04:00
/*
* configure a fifo ; for non - shared endpoints , this may be called
* once for a tx fifo and once for an rx fifo .
*
* returns negative errno or offset for next fifo .
*/
static int __init
fifo_setup ( struct musb * musb , struct musb_hw_ep * hw_ep ,
2010-03-12 11:29:06 +03:00
const struct musb_fifo_cfg * cfg , u16 offset )
2008-07-24 13:27:36 +04:00
{
void __iomem * mbase = musb - > mregs ;
int size = 0 ;
u16 maxpacket = cfg - > maxpacket ;
u16 c_off = offset > > 3 ;
u8 c_size ;
/* expect hw_ep has already been zero-initialized */
size = ffs ( max ( maxpacket , ( u16 ) 8 ) ) - 1 ;
maxpacket = 1 < < size ;
c_size = size - 3 ;
if ( cfg - > mode = = BUF_DOUBLE ) {
2008-08-08 13:40:54 +04:00
if ( ( offset + ( maxpacket < < 1 ) ) >
( 1 < < ( musb - > config - > ram_bits + 2 ) ) )
2008-07-24 13:27:36 +04:00
return - EMSGSIZE ;
c_size | = MUSB_FIFOSZ_DPB ;
} else {
2008-08-08 13:40:54 +04:00
if ( ( offset + maxpacket ) > ( 1 < < ( musb - > config - > ram_bits + 2 ) ) )
2008-07-24 13:27:36 +04:00
return - EMSGSIZE ;
}
/* configure the FIFO */
musb_writeb ( mbase , MUSB_INDEX , hw_ep - > epnum ) ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
/* EP0 reserved endpoint for control, bidirectional;
* EP1 reserved for bulk , two unidirection halves .
*/
if ( hw_ep - > epnum = = 1 )
musb - > bulk_ep = hw_ep ;
/* REVISIT error check: be sure ep0 can both rx and tx ... */
# endif
switch ( cfg - > style ) {
case FIFO_TX :
2008-12-02 22:33:48 +03:00
musb_write_txfifosz ( mbase , c_size ) ;
musb_write_txfifoadd ( mbase , c_off ) ;
2008-07-24 13:27:36 +04:00
hw_ep - > tx_double_buffered = ! ! ( c_size & MUSB_FIFOSZ_DPB ) ;
hw_ep - > max_packet_sz_tx = maxpacket ;
break ;
case FIFO_RX :
2008-12-02 22:33:48 +03:00
musb_write_rxfifosz ( mbase , c_size ) ;
musb_write_rxfifoadd ( mbase , c_off ) ;
2008-07-24 13:27:36 +04:00
hw_ep - > rx_double_buffered = ! ! ( c_size & MUSB_FIFOSZ_DPB ) ;
hw_ep - > max_packet_sz_rx = maxpacket ;
break ;
case FIFO_RXTX :
2008-12-02 22:33:48 +03:00
musb_write_txfifosz ( mbase , c_size ) ;
musb_write_txfifoadd ( mbase , c_off ) ;
2008-07-24 13:27:36 +04:00
hw_ep - > rx_double_buffered = ! ! ( c_size & MUSB_FIFOSZ_DPB ) ;
hw_ep - > max_packet_sz_rx = maxpacket ;
2008-12-02 22:33:48 +03:00
musb_write_rxfifosz ( mbase , c_size ) ;
musb_write_rxfifoadd ( mbase , c_off ) ;
2008-07-24 13:27:36 +04:00
hw_ep - > tx_double_buffered = hw_ep - > rx_double_buffered ;
hw_ep - > max_packet_sz_tx = maxpacket ;
hw_ep - > is_shared_fifo = true ;
break ;
}
/* NOTE rx and tx endpoint irqs aren't managed separately,
* which happens to be ok
*/
musb - > epmask | = ( 1 < < hw_ep - > epnum ) ;
return offset + ( maxpacket < < ( ( c_size & MUSB_FIFOSZ_DPB ) ? 1 : 0 ) ) ;
}
2010-03-12 11:29:06 +03:00
static struct musb_fifo_cfg __initdata ep0_cfg = {
2008-07-24 13:27:36 +04:00
. style = FIFO_RXTX , . maxpacket = 64 ,
} ;
static int __init ep_config_from_table ( struct musb * musb )
{
2010-03-12 11:29:06 +03:00
const struct musb_fifo_cfg * cfg ;
2008-07-24 13:27:36 +04:00
unsigned i , n ;
int offset ;
struct musb_hw_ep * hw_ep = musb - > endpoints ;
2010-03-12 11:29:06 +03:00
if ( musb - > config - > fifo_cfg ) {
cfg = musb - > config - > fifo_cfg ;
n = musb - > config - > fifo_cfg_size ;
goto done ;
}
2008-07-24 13:27:36 +04:00
switch ( fifo_mode ) {
default :
fifo_mode = 0 ;
/* FALLTHROUGH */
case 0 :
cfg = mode_0_cfg ;
n = ARRAY_SIZE ( mode_0_cfg ) ;
break ;
case 1 :
cfg = mode_1_cfg ;
n = ARRAY_SIZE ( mode_1_cfg ) ;
break ;
case 2 :
cfg = mode_2_cfg ;
n = ARRAY_SIZE ( mode_2_cfg ) ;
break ;
case 3 :
cfg = mode_3_cfg ;
n = ARRAY_SIZE ( mode_3_cfg ) ;
break ;
case 4 :
cfg = mode_4_cfg ;
n = ARRAY_SIZE ( mode_4_cfg ) ;
break ;
2009-12-28 14:40:34 +03:00
case 5 :
cfg = mode_5_cfg ;
n = ARRAY_SIZE ( mode_5_cfg ) ;
break ;
2008-07-24 13:27:36 +04:00
}
printk ( KERN_DEBUG " %s: setup fifo_mode %d \n " ,
musb_driver_name , fifo_mode ) ;
2010-03-12 11:29:06 +03:00
done :
2008-07-24 13:27:36 +04:00
offset = fifo_setup ( musb , hw_ep , & ep0_cfg , 0 ) ;
/* assert(offset > 0) */
/* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would
2008-08-08 13:40:54 +04:00
* be better than static musb - > config - > num_eps and DYN_FIFO_SIZE . . .
2008-07-24 13:27:36 +04:00
*/
for ( i = 0 ; i < n ; i + + ) {
u8 epn = cfg - > hw_ep_num ;
2008-08-08 13:40:54 +04:00
if ( epn > = musb - > config - > num_eps ) {
2008-07-24 13:27:36 +04:00
pr_debug ( " %s: invalid ep %d \n " ,
musb_driver_name , epn ) ;
2008-11-24 14:06:50 +03:00
return - EINVAL ;
2008-07-24 13:27:36 +04:00
}
offset = fifo_setup ( musb , hw_ep + epn , cfg + + , offset ) ;
if ( offset < 0 ) {
pr_debug ( " %s: mem overrun, ep %d \n " ,
musb_driver_name , epn ) ;
return - EINVAL ;
}
epn + + ;
musb - > nr_endpoints = max ( epn , musb - > nr_endpoints ) ;
}
printk ( KERN_DEBUG " %s: %d/%d max ep, %d/%d memory \n " ,
musb_driver_name ,
2008-08-08 13:40:54 +04:00
n + 1 , musb - > config - > num_eps * 2 - 1 ,
offset , ( 1 < < ( musb - > config - > ram_bits + 2 ) ) ) ;
2008-07-24 13:27:36 +04:00
# ifdef CONFIG_USB_MUSB_HDRC_HCD
if ( ! musb - > bulk_ep ) {
pr_debug ( " %s: missing bulk \n " , musb_driver_name ) ;
return - EINVAL ;
}
# endif
return 0 ;
}
/*
* ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false
* @ param musb the controller
*/
static int __init ep_config_from_hw ( struct musb * musb )
{
2008-12-02 22:33:48 +03:00
u8 epnum = 0 ;
2008-07-24 13:27:36 +04:00
struct musb_hw_ep * hw_ep ;
void * mbase = musb - > mregs ;
2008-12-02 22:33:48 +03:00
int ret = 0 ;
2008-07-24 13:27:36 +04:00
DBG ( 2 , " <== static silicon ep config \n " ) ;
/* FIXME pick up ep0 maxpacket size */
2008-08-08 13:40:54 +04:00
for ( epnum = 1 ; epnum < musb - > config - > num_eps ; epnum + + ) {
2008-07-24 13:27:36 +04:00
musb_ep_select ( mbase , epnum ) ;
hw_ep = musb - > endpoints + epnum ;
2008-12-02 22:33:48 +03:00
ret = musb_read_fifosize ( musb , hw_ep , epnum ) ;
if ( ret < 0 )
2008-07-24 13:27:36 +04:00
break ;
/* FIXME set up hw_ep->{rx,tx}_double_buffered */
# ifdef CONFIG_USB_MUSB_HDRC_HCD
/* pick an RX/TX endpoint for bulk */
if ( hw_ep - > max_packet_sz_tx < 512
| | hw_ep - > max_packet_sz_rx < 512 )
continue ;
/* REVISIT: this algorithm is lazy, we should at least
* try to pick a double buffered endpoint .
*/
if ( musb - > bulk_ep )
continue ;
musb - > bulk_ep = hw_ep ;
# endif
}
# ifdef CONFIG_USB_MUSB_HDRC_HCD
if ( ! musb - > bulk_ep ) {
pr_debug ( " %s: missing bulk \n " , musb_driver_name ) ;
return - EINVAL ;
}
# endif
return 0 ;
}
enum { MUSB_CONTROLLER_MHDRC , MUSB_CONTROLLER_HDRC , } ;
/* Initialize MUSB (M)HDRC part of the USB hardware subsystem;
* configure endpoints , or take their config from silicon
*/
static int __init musb_core_init ( u16 musb_type , struct musb * musb )
{
u8 reg ;
char * type ;
2009-12-22 13:49:53 +03:00
char aInfo [ 90 ] , aRevision [ 32 ] , aDate [ 12 ] ;
2008-07-24 13:27:36 +04:00
void __iomem * mbase = musb - > mregs ;
int status = 0 ;
int i ;
/* log core options (read using indexed model) */
2008-12-02 22:33:48 +03:00
reg = musb_read_configdata ( mbase ) ;
2008-07-24 13:27:36 +04:00
strcpy ( aInfo , ( reg & MUSB_CONFIGDATA_UTMIDW ) ? " UTMI-16 " : " UTMI-8 " ) ;
2009-12-28 14:40:41 +03:00
if ( reg & MUSB_CONFIGDATA_DYNFIFO ) {
2008-07-24 13:27:36 +04:00
strcat ( aInfo , " , dyn FIFOs " ) ;
2009-12-28 14:40:41 +03:00
musb - > dyn_fifo = true ;
}
2008-07-24 13:27:36 +04:00
if ( reg & MUSB_CONFIGDATA_MPRXE ) {
strcat ( aInfo , " , bulk combine " ) ;
musb - > bulk_combine = true ;
}
if ( reg & MUSB_CONFIGDATA_MPTXE ) {
strcat ( aInfo , " , bulk split " ) ;
musb - > bulk_split = true ;
}
if ( reg & MUSB_CONFIGDATA_HBRXE ) {
strcat ( aInfo , " , HB-ISO Rx " ) ;
2009-04-04 03:16:17 +04:00
musb - > hb_iso_rx = true ;
2008-07-24 13:27:36 +04:00
}
if ( reg & MUSB_CONFIGDATA_HBTXE ) {
strcat ( aInfo , " , HB-ISO Tx " ) ;
2009-04-04 03:16:17 +04:00
musb - > hb_iso_tx = true ;
2008-07-24 13:27:36 +04:00
}
if ( reg & MUSB_CONFIGDATA_SOFTCONE )
strcat ( aInfo , " , SoftConn " ) ;
printk ( KERN_DEBUG " %s: ConfigData=0x%02x (%s) \n " ,
musb_driver_name , reg , aInfo ) ;
aDate [ 0 ] = 0 ;
if ( MUSB_CONTROLLER_MHDRC = = musb_type ) {
musb - > is_multipoint = 1 ;
type = " M " ;
} else {
musb - > is_multipoint = 0 ;
type = " " ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
# ifndef CONFIG_USB_OTG_BLACKLIST_HUB
printk ( KERN_ERR
" %s: kernel must blacklist external hubs \n " ,
musb_driver_name ) ;
# endif
# endif
}
/* log release info */
2009-11-16 18:39:21 +03:00
musb - > hwvers = musb_read_hwvers ( mbase ) ;
snprintf ( aRevision , 32 , " %d.%d%s " , MUSB_HWVERS_MAJOR ( musb - > hwvers ) ,
MUSB_HWVERS_MINOR ( musb - > hwvers ) ,
( musb - > hwvers & MUSB_HWVERS_RC ) ? " RC " : " " ) ;
2008-07-24 13:27:36 +04:00
printk ( KERN_DEBUG " %s: %sHDRC RTL version %s %s \n " ,
musb_driver_name , type , aRevision , aDate ) ;
/* configure ep0 */
2008-12-02 22:33:48 +03:00
musb_configure_ep0 ( musb ) ;
2008-07-24 13:27:36 +04:00
/* discover endpoint configuration */
musb - > nr_endpoints = 1 ;
musb - > epmask = 1 ;
2010-01-21 16:33:54 +03:00
if ( musb - > dyn_fifo )
status = ep_config_from_table ( musb ) ;
else
status = ep_config_from_hw ( musb ) ;
2008-07-24 13:27:36 +04:00
if ( status < 0 )
return status ;
/* finish init, and print endpoint config */
for ( i = 0 ; i < musb - > nr_endpoints ; i + + ) {
struct musb_hw_ep * hw_ep = musb - > endpoints + i ;
hw_ep - > fifo = MUSB_FIFO_OFFSET ( i ) + mbase ;
# ifdef CONFIG_USB_TUSB6010
hw_ep - > fifo_async = musb - > async + 0x400 + MUSB_FIFO_OFFSET ( i ) ;
hw_ep - > fifo_sync = musb - > sync + 0x400 + MUSB_FIFO_OFFSET ( i ) ;
hw_ep - > fifo_sync_va =
musb - > sync_va + 0x400 + MUSB_FIFO_OFFSET ( i ) ;
if ( i = = 0 )
hw_ep - > conf = mbase - 0x400 + TUSB_EP0_CONF ;
else
hw_ep - > conf = mbase + 0x400 + ( ( ( i - 1 ) & 0xf ) < < 2 ) ;
# endif
hw_ep - > regs = MUSB_EP_OFFSET ( i , 0 ) + mbase ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
2008-12-02 22:33:48 +03:00
hw_ep - > target_regs = musb_read_target_reg_base ( i , mbase ) ;
2008-07-24 13:27:36 +04:00
hw_ep - > rx_reinit = 1 ;
hw_ep - > tx_reinit = 1 ;
# endif
if ( hw_ep - > max_packet_sz_tx ) {
2009-11-17 12:52:54 +03:00
DBG ( 1 ,
2008-07-24 13:27:36 +04:00
" %s: hw_ep %d%s, %smax %d \n " ,
musb_driver_name , i ,
hw_ep - > is_shared_fifo ? " shared " : " tx " ,
hw_ep - > tx_double_buffered
? " doublebuffer, " : " " ,
hw_ep - > max_packet_sz_tx ) ;
}
if ( hw_ep - > max_packet_sz_rx & & ! hw_ep - > is_shared_fifo ) {
2009-11-17 12:52:54 +03:00
DBG ( 1 ,
2008-07-24 13:27:36 +04:00
" %s: hw_ep %d%s, %smax %d \n " ,
musb_driver_name , i ,
" rx " ,
hw_ep - > rx_double_buffered
? " doublebuffer, " : " " ,
hw_ep - > max_packet_sz_rx ) ;
}
if ( ! ( hw_ep - > max_packet_sz_tx | | hw_ep - > max_packet_sz_rx ) )
DBG ( 1 , " hw_ep %d not configured \n " , i ) ;
}
return 0 ;
}
/*-------------------------------------------------------------------------*/
2010-03-12 11:29:09 +03:00
# if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \
defined ( CONFIG_ARCH_OMAP4 )
2008-07-24 13:27:36 +04:00
static irqreturn_t generic_interrupt ( int irq , void * __hci )
{
unsigned long flags ;
irqreturn_t retval = IRQ_NONE ;
struct musb * musb = __hci ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
musb - > int_usb = musb_readb ( musb - > mregs , MUSB_INTRUSB ) ;
musb - > int_tx = musb_readw ( musb - > mregs , MUSB_INTRTX ) ;
musb - > int_rx = musb_readw ( musb - > mregs , MUSB_INTRRX ) ;
if ( musb - > int_usb | | musb - > int_tx | | musb - > int_rx )
retval = musb_interrupt ( musb ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
2009-03-27 22:52:43 +03:00
return retval ;
2008-07-24 13:27:36 +04:00
}
# else
# define generic_interrupt NULL
# endif
/*
* handle all the irqs defined by the HDRC core . for now we expect : other
* irq sources ( phy , dma , etc ) will be handled first , musb - > int_ * values
* will be assigned , and the irq will already have been acked .
*
* called in irq context with spinlock held , irqs blocked
*/
irqreturn_t musb_interrupt ( struct musb * musb )
{
irqreturn_t retval = IRQ_NONE ;
u8 devctl , power ;
int ep_num ;
u32 reg ;
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
power = musb_readb ( musb - > mregs , MUSB_POWER ) ;
DBG ( 4 , " ** IRQ %s usb%04x tx%04x rx%04x \n " ,
( devctl & MUSB_DEVCTL_HM ) ? " host " : " peripheral " ,
musb - > int_usb , musb - > int_tx , musb - > int_rx ) ;
2009-12-15 14:47:30 +03:00
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
if ( is_otg_enabled ( musb ) | | is_peripheral_enabled ( musb ) )
if ( ! musb - > gadget_driver ) {
DBG ( 5 , " No gadget driver loaded \n " ) ;
return IRQ_HANDLED ;
}
# endif
2008-07-24 13:27:36 +04:00
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
2010-06-24 21:37:06 +04:00
if ( musb - > int_usb )
2008-07-24 13:27:36 +04:00
retval | = musb_stage0_irq ( musb , musb - > int_usb ,
devctl , power ) ;
/* "stage 1" is handling endpoint irqs */
/* handle endpoint 0 first */
if ( musb - > int_tx & 1 ) {
if ( devctl & MUSB_DEVCTL_HM )
retval | = musb_h_ep0_irq ( musb ) ;
else
retval | = musb_g_ep0_irq ( musb ) ;
}
/* RX on endpoints 1-15 */
reg = musb - > int_rx > > 1 ;
ep_num = 1 ;
while ( reg ) {
if ( reg & 1 ) {
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED ;
if ( devctl & MUSB_DEVCTL_HM ) {
if ( is_host_capable ( ) )
musb_host_rx ( musb , ep_num ) ;
} else {
if ( is_peripheral_capable ( ) )
musb_g_rx ( musb , ep_num ) ;
}
}
reg > > = 1 ;
ep_num + + ;
}
/* TX on endpoints 1-15 */
reg = musb - > int_tx > > 1 ;
ep_num = 1 ;
while ( reg ) {
if ( reg & 1 ) {
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED ;
if ( devctl & MUSB_DEVCTL_HM ) {
if ( is_host_capable ( ) )
musb_host_tx ( musb , ep_num ) ;
} else {
if ( is_peripheral_capable ( ) )
musb_g_tx ( musb , ep_num ) ;
}
}
reg > > = 1 ;
ep_num + + ;
}
return retval ;
}
# ifndef CONFIG_MUSB_PIO_ONLY
static int __initdata use_dma = 1 ;
/* "modprobe ... use_dma=0" etc */
module_param ( use_dma , bool , 0 ) ;
MODULE_PARM_DESC ( use_dma , " enable/disable use of DMA " ) ;
void musb_dma_completion ( struct musb * musb , u8 epnum , u8 transmit )
{
u8 devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
/* called with controller lock already held */
if ( ! epnum ) {
# ifndef CONFIG_USB_TUSB_OMAP_DMA
if ( ! is_cppi_enabled ( ) ) {
/* endpoint 0 */
if ( devctl & MUSB_DEVCTL_HM )
musb_h_ep0_irq ( musb ) ;
else
musb_g_ep0_irq ( musb ) ;
}
# endif
} else {
/* endpoints 1..15 */
if ( transmit ) {
if ( devctl & MUSB_DEVCTL_HM ) {
if ( is_host_capable ( ) )
musb_host_tx ( musb , epnum ) ;
} else {
if ( is_peripheral_capable ( ) )
musb_g_tx ( musb , epnum ) ;
}
} else {
/* receive */
if ( devctl & MUSB_DEVCTL_HM ) {
if ( is_host_capable ( ) )
musb_host_rx ( musb , epnum ) ;
} else {
if ( is_peripheral_capable ( ) )
musb_g_rx ( musb , epnum ) ;
}
}
}
}
# else
# define use_dma 0
# endif
/*-------------------------------------------------------------------------*/
# ifdef CONFIG_SYSFS
static ssize_t
musb_mode_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct musb * musb = dev_to_musb ( dev ) ;
unsigned long flags ;
int ret = - EINVAL ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
ret = sprintf ( buf , " %s \n " , otg_state_string ( musb ) ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
return ret ;
}
static ssize_t
musb_mode_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t n )
{
struct musb * musb = dev_to_musb ( dev ) ;
unsigned long flags ;
2008-11-24 14:06:47 +03:00
int status ;
2008-07-24 13:27:36 +04:00
spin_lock_irqsave ( & musb - > lock , flags ) ;
2008-11-24 14:06:47 +03:00
if ( sysfs_streq ( buf , " host " ) )
status = musb_platform_set_mode ( musb , MUSB_HOST ) ;
else if ( sysfs_streq ( buf , " peripheral " ) )
status = musb_platform_set_mode ( musb , MUSB_PERIPHERAL ) ;
else if ( sysfs_streq ( buf , " otg " ) )
status = musb_platform_set_mode ( musb , MUSB_OTG ) ;
else
status = - EINVAL ;
2008-07-24 13:27:36 +04:00
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
2008-11-24 14:06:47 +03:00
return ( status = = 0 ) ? n : status ;
2008-07-24 13:27:36 +04:00
}
static DEVICE_ATTR ( mode , 0644 , musb_mode_show , musb_mode_store ) ;
static ssize_t
musb_vbus_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t n )
{
struct musb * musb = dev_to_musb ( dev ) ;
unsigned long flags ;
unsigned long val ;
if ( sscanf ( buf , " %lu " , & val ) < 1 ) {
2009-12-15 12:08:43 +03:00
dev_err ( dev , " Invalid VBUS timeout ms value \n " ) ;
2008-07-24 13:27:36 +04:00
return - EINVAL ;
}
spin_lock_irqsave ( & musb - > lock , flags ) ;
2009-03-31 23:32:12 +04:00
/* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
musb - > a_wait_bcon = val ? max_t ( int , val , OTG_TIME_A_WAIT_BCON ) : 0 ;
2009-03-31 23:30:04 +04:00
if ( musb - > xceiv - > state = = OTG_STATE_A_WAIT_BCON )
2008-07-24 13:27:36 +04:00
musb - > is_active = 0 ;
musb_platform_try_idle ( musb , jiffies + msecs_to_jiffies ( val ) ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
return n ;
}
static ssize_t
musb_vbus_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct musb * musb = dev_to_musb ( dev ) ;
unsigned long flags ;
unsigned long val ;
int vbus ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
val = musb - > a_wait_bcon ;
2009-03-31 23:32:12 +04:00
/* FIXME get_vbus_status() is normally #defined as false...
* and is effectively TUSB - specific .
*/
2008-07-24 13:27:36 +04:00
vbus = musb_platform_get_vbus_status ( musb ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
2009-03-31 23:32:12 +04:00
return sprintf ( buf , " Vbus %s, timeout %lu msec \n " ,
2008-07-24 13:27:36 +04:00
vbus ? " on " : " off " , val ) ;
}
static DEVICE_ATTR ( vbus , 0644 , musb_vbus_show , musb_vbus_store ) ;
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* Gadget drivers can't know that a host is connected so they might want
* to start SRP , but users can . This allows userspace to trigger SRP .
*/
static ssize_t
musb_srp_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t n )
{
struct musb * musb = dev_to_musb ( dev ) ;
unsigned short srp ;
if ( sscanf ( buf , " %hu " , & srp ) ! = 1
| | ( srp ! = 1 ) ) {
2009-12-15 12:08:43 +03:00
dev_err ( dev , " SRP: Value must be 1 \n " ) ;
2008-07-24 13:27:36 +04:00
return - EINVAL ;
}
if ( srp = = 1 )
musb_g_wakeup ( musb ) ;
return n ;
}
static DEVICE_ATTR ( srp , 0644 , NULL , musb_srp_store ) ;
# endif /* CONFIG_USB_GADGET_MUSB_HDRC */
2009-12-15 12:08:38 +03:00
static struct attribute * musb_attributes [ ] = {
& dev_attr_mode . attr ,
& dev_attr_vbus . attr ,
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
& dev_attr_srp . attr ,
# endif
NULL
} ;
static const struct attribute_group musb_attr_group = {
. attrs = musb_attributes ,
} ;
2008-07-24 13:27:36 +04:00
# endif /* sysfs */
/* Only used to provide driver mode change events */
static void musb_irq_work ( struct work_struct * data )
{
struct musb * musb = container_of ( data , struct musb , irq_work ) ;
static int old_state ;
2009-03-31 23:30:04 +04:00
if ( musb - > xceiv - > state ! = old_state ) {
old_state = musb - > xceiv - > state ;
2008-07-24 13:27:36 +04:00
sysfs_notify ( & musb - > controller - > kobj , NULL , " mode " ) ;
}
}
/* --------------------------------------------------------------------------
* Init support
*/
static struct musb * __init
2008-08-08 13:40:54 +04:00
allocate_instance ( struct device * dev ,
struct musb_hdrc_config * config , void __iomem * mbase )
2008-07-24 13:27:36 +04:00
{
struct musb * musb ;
struct musb_hw_ep * ep ;
int epnum ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
struct usb_hcd * hcd ;
2008-11-07 03:52:53 +03:00
hcd = usb_create_hcd ( & musb_hc_driver , dev , dev_name ( dev ) ) ;
2008-07-24 13:27:36 +04:00
if ( ! hcd )
return NULL ;
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
musb = hcd_to_musb ( hcd ) ;
INIT_LIST_HEAD ( & musb - > control ) ;
INIT_LIST_HEAD ( & musb - > in_bulk ) ;
INIT_LIST_HEAD ( & musb - > out_bulk ) ;
hcd - > uses_new_polling = 1 ;
musb - > vbuserr_retry = VBUSERR_RETRY_COUNT ;
2009-03-31 23:32:12 +04:00
musb - > a_wait_bcon = OTG_TIME_A_WAIT_BCON ;
2008-07-24 13:27:36 +04:00
# else
musb = kzalloc ( sizeof * musb , GFP_KERNEL ) ;
if ( ! musb )
return NULL ;
dev_set_drvdata ( dev , musb ) ;
# endif
musb - > mregs = mbase ;
musb - > ctrl_base = mbase ;
musb - > nIrq = - ENODEV ;
2008-08-08 13:40:54 +04:00
musb - > config = config ;
2008-09-15 14:09:31 +04:00
BUG_ON ( musb - > config - > num_eps > MUSB_C_NUM_EPS ) ;
2008-07-24 13:27:36 +04:00
for ( epnum = 0 , ep = musb - > endpoints ;
2008-08-08 13:40:54 +04:00
epnum < musb - > config - > num_eps ;
2008-07-24 13:27:36 +04:00
epnum + + , ep + + ) {
ep - > musb = musb ;
ep - > epnum = epnum ;
}
musb - > controller = dev ;
return musb ;
}
static void musb_free ( struct musb * musb )
{
/* this has multiple entry modes. it handles fault cleanup after
* probe ( ) , where things may be partially set up , as well as rmmod
* cleanup after everything ' s been de - activated .
*/
# ifdef CONFIG_SYSFS
2009-12-15 12:08:38 +03:00
sysfs_remove_group ( & musb - > controller - > kobj , & musb_attr_group ) ;
2008-07-24 13:27:36 +04:00
# endif
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
musb_gadget_cleanup ( musb ) ;
# endif
2009-01-25 04:56:39 +03:00
if ( musb - > nIrq > = 0 ) {
if ( musb - > irq_wake )
disable_irq_wake ( musb - > nIrq ) ;
2008-07-24 13:27:36 +04:00
free_irq ( musb - > nIrq , musb ) ;
}
if ( is_dma_capable ( ) & & musb - > dma_controller ) {
struct dma_controller * c = musb - > dma_controller ;
( void ) c - > stop ( c ) ;
dma_controller_destroy ( c ) ;
}
2009-08-03 10:13:40 +04:00
# ifdef CONFIG_USB_MUSB_OTG
put_device ( musb - > xceiv - > dev ) ;
# endif
2008-07-24 13:27:36 +04:00
# ifdef CONFIG_USB_MUSB_HDRC_HCD
usb_put_hcd ( musb_to_hcd ( musb ) ) ;
# else
kfree ( musb ) ;
# endif
}
/*
* Perform generic per - controller initialization .
*
* @ pDevice : the controller ( already clocked , etc )
* @ nIrq : irq
* @ mregs : virtual address of controller registers ,
* not yet corrected for platform - specific offsets
*/
static int __init
musb_init_controller ( struct device * dev , int nIrq , void __iomem * ctrl )
{
int status ;
struct musb * musb ;
struct musb_hdrc_platform_data * plat = dev - > platform_data ;
/* The driver might handle more features than the board; OK.
* Fail when the board needs a feature that ' s not enabled .
*/
if ( ! plat ) {
dev_dbg ( dev , " no platform_data? \n " ) ;
2010-03-25 14:14:33 +03:00
status = - ENODEV ;
goto fail0 ;
2008-07-24 13:27:36 +04:00
}
2010-03-25 14:14:33 +03:00
2008-07-24 13:27:36 +04:00
switch ( plat - > mode ) {
case MUSB_HOST :
# ifdef CONFIG_USB_MUSB_HDRC_HCD
break ;
# else
goto bad_config ;
# endif
case MUSB_PERIPHERAL :
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
break ;
# else
goto bad_config ;
# endif
case MUSB_OTG :
# ifdef CONFIG_USB_MUSB_OTG
break ;
# else
bad_config :
# endif
default :
dev_err ( dev , " incompatible Kconfig role setting \n " ) ;
2010-03-25 14:14:33 +03:00
status = - EINVAL ;
goto fail0 ;
2008-07-24 13:27:36 +04:00
}
/* allocate */
2008-08-08 13:40:54 +04:00
musb = allocate_instance ( dev , plat - > config , ctrl ) ;
2010-03-25 14:14:33 +03:00
if ( ! musb ) {
status = - ENOMEM ;
goto fail0 ;
}
2008-07-24 13:27:36 +04:00
spin_lock_init ( & musb - > lock ) ;
musb - > board_mode = plat - > mode ;
musb - > board_set_power = plat - > set_power ;
musb - > set_clock = plat - > set_clock ;
musb - > min_power = plat - > min_power ;
/* Clock usage is chip-specific ... functional clock (DaVinci,
* OMAP2430 ) , or PHY ref ( some TUSB6010 boards ) . All this core
* code does is make sure a clock handle is available ; platform
* code manages it during start / stop and suspend / resume .
*/
if ( plat - > clock ) {
musb - > clock = clk_get ( dev , plat - > clock ) ;
if ( IS_ERR ( musb - > clock ) ) {
status = PTR_ERR ( musb - > clock ) ;
musb - > clock = NULL ;
2010-03-25 14:14:33 +03:00
goto fail1 ;
2008-07-24 13:27:36 +04:00
}
}
2009-03-31 23:30:04 +04:00
/* The musb_platform_init() call:
* - adjusts musb - > mregs and musb - > isr if needed ,
* - may initialize an integrated tranceiver
* - initializes musb - > xceiv , usually by otg_get_transceiver ( )
* - activates clocks .
* - stops powering VBUS
* - assigns musb - > board_set_vbus if host mode is enabled
*
* There are various transciever configurations . Blackfin ,
* DaVinci , TUSB60x0 , and others integrate them . OMAP3 uses
* external / discrete ones in various flavors ( twl4030 family ,
* isp1504 , non - OTG , etc ) mostly hooking up through ULPI .
2008-07-24 13:27:36 +04:00
*/
musb - > isr = generic_interrupt ;
2010-03-12 11:29:07 +03:00
status = musb_platform_init ( musb , plat - > board_data ) ;
2008-07-24 13:27:36 +04:00
if ( status < 0 )
2010-03-25 14:14:33 +03:00
goto fail2 ;
2008-07-24 13:27:36 +04:00
if ( ! musb - > isr ) {
status = - ENODEV ;
2010-03-25 14:14:33 +03:00
goto fail3 ;
2008-07-24 13:27:36 +04:00
}
2010-03-25 14:25:28 +03:00
if ( ! musb - > xceiv - > io_ops ) {
musb - > xceiv - > io_priv = musb - > mregs ;
musb - > xceiv - > io_ops = & musb_ulpi_access ;
}
2008-07-24 13:27:36 +04:00
# ifndef CONFIG_MUSB_PIO_ONLY
if ( use_dma & & dev - > dma_mask ) {
struct dma_controller * c ;
c = dma_controller_create ( musb , musb - > mregs ) ;
musb - > dma_controller = c ;
if ( c )
( void ) c - > start ( c ) ;
}
# endif
/* ideally this would be abstracted in platform setup */
if ( ! is_dma_capable ( ) | | ! musb - > dma_controller )
dev - > dma_mask = NULL ;
/* be sure interrupts are disabled before connecting ISR */
musb_platform_disable ( musb ) ;
musb_generic_disable ( musb ) ;
/* setup musb parts of the core (especially endpoints) */
2008-08-08 13:40:54 +04:00
status = musb_core_init ( plat - > config - > multipoint
2008-07-24 13:27:36 +04:00
? MUSB_CONTROLLER_MHDRC
: MUSB_CONTROLLER_HDRC , musb ) ;
if ( status < 0 )
2010-03-25 14:14:33 +03:00
goto fail3 ;
2008-07-24 13:27:36 +04:00
2009-07-27 13:03:19 +04:00
# ifdef CONFIG_USB_MUSB_OTG
2009-03-31 23:32:12 +04:00
setup_timer ( & musb - > otg_timer , musb_otg_timer_func , ( unsigned long ) musb ) ;
# endif
2008-07-24 13:27:36 +04:00
/* Init IRQ workqueue before request_irq */
INIT_WORK ( & musb - > irq_work , musb_irq_work ) ;
/* attach to the IRQ */
2008-11-07 03:52:53 +03:00
if ( request_irq ( nIrq , musb - > isr , 0 , dev_name ( dev ) , musb ) ) {
2008-07-24 13:27:36 +04:00
dev_err ( dev , " request_irq %d failed! \n " , nIrq ) ;
status = - ENODEV ;
2010-03-25 14:14:33 +03:00
goto fail3 ;
2008-07-24 13:27:36 +04:00
}
musb - > nIrq = nIrq ;
/* FIXME this handles wakeup irqs wrong */
2008-11-24 14:06:53 +03:00
if ( enable_irq_wake ( nIrq ) = = 0 ) {
musb - > irq_wake = 1 ;
2008-07-24 13:27:36 +04:00
device_init_wakeup ( dev , 1 ) ;
2008-11-24 14:06:53 +03:00
} else {
musb - > irq_wake = 0 ;
}
2008-07-24 13:27:36 +04:00
2009-03-31 23:30:04 +04:00
/* host side needs more setup */
if ( is_host_enabled ( musb ) ) {
2008-07-24 13:27:36 +04:00
struct usb_hcd * hcd = musb_to_hcd ( musb ) ;
2009-03-31 23:30:04 +04:00
otg_set_host ( musb - > xceiv , & hcd - > self ) ;
if ( is_otg_enabled ( musb ) )
2008-07-24 13:27:36 +04:00
hcd - > self . otg_port = 1 ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > host = & hcd - > self ;
2008-07-24 13:27:36 +04:00
hcd - > power_budget = 2 * ( plat - > power ? : 250 ) ;
2009-12-28 14:40:42 +03:00
/* program PHY to use external vBus if required */
if ( plat - > extvbus ) {
2010-03-12 11:27:21 +03:00
u8 busctl = musb_read_ulpi_buscontrol ( musb - > mregs ) ;
2009-12-28 14:40:42 +03:00
busctl | = MUSB_ULPI_USE_EXTVBUS ;
2010-03-12 11:27:21 +03:00
musb_write_ulpi_buscontrol ( musb - > mregs , busctl ) ;
2009-12-28 14:40:42 +03:00
}
2008-07-24 13:27:36 +04:00
}
/* For the host-only role, we can activate right away.
* ( We expect the ID pin to be forcibly grounded ! ! )
* Otherwise , wait till the gadget driver hooks up .
*/
if ( ! is_otg_enabled ( musb ) & & is_host_enabled ( musb ) ) {
MUSB_HST_MODE ( musb ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > default_a = 1 ;
musb - > xceiv - > state = OTG_STATE_A_IDLE ;
2008-07-24 13:27:36 +04:00
status = usb_add_hcd ( musb_to_hcd ( musb ) , - 1 , 0 ) ;
DBG ( 1 , " %s mode, status %d, devctl %02x %c \n " ,
" HOST " , status ,
musb_readb ( musb - > mregs , MUSB_DEVCTL ) ,
( musb_readb ( musb - > mregs , MUSB_DEVCTL )
& MUSB_DEVCTL_BDEVICE
? ' B ' : ' A ' ) ) ;
} else /* peripheral is enabled */ {
MUSB_DEV_MODE ( musb ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > default_a = 0 ;
musb - > xceiv - > state = OTG_STATE_B_IDLE ;
2008-07-24 13:27:36 +04:00
status = musb_gadget_setup ( musb ) ;
DBG ( 1 , " %s mode, status %d, dev%02x \n " ,
is_otg_enabled ( musb ) ? " OTG " : " PERIPHERAL " ,
status ,
musb_readb ( musb - > mregs , MUSB_DEVCTL ) ) ;
}
2010-03-25 14:14:32 +03:00
if ( status < 0 )
2010-03-25 14:14:33 +03:00
goto fail3 ;
2008-07-24 13:27:36 +04:00
2010-03-12 11:29:11 +03:00
status = musb_init_debugfs ( musb ) ;
if ( status < 0 )
2010-03-25 14:25:18 +03:00
goto fail4 ;
2010-03-12 11:29:11 +03:00
2008-07-24 13:27:36 +04:00
# ifdef CONFIG_SYSFS
2009-12-15 12:08:38 +03:00
status = sysfs_create_group ( & musb - > controller - > kobj , & musb_attr_group ) ;
2008-09-11 12:53:25 +04:00
if ( status )
2010-03-25 14:25:18 +03:00
goto fail5 ;
2010-03-25 14:14:32 +03:00
# endif
2008-07-24 13:27:36 +04:00
2010-01-21 16:33:58 +03:00
dev_info ( dev , " USB %s mode controller at %p using %s, IRQ %d \n " ,
( { char * s ;
switch ( musb - > board_mode ) {
case MUSB_HOST : s = " Host " ; break ;
case MUSB_PERIPHERAL : s = " Peripheral " ; break ;
default : s = " OTG " ; break ;
} ; s ; } ) ,
ctrl ,
( is_dma_capable ( ) & & musb - > dma_controller )
? " DMA " : " PIO " ,
musb - > nIrq ) ;
2008-09-11 12:53:25 +04:00
return 0 ;
2008-07-24 13:27:36 +04:00
2010-03-25 14:25:18 +03:00
fail5 :
musb_exit_debugfs ( musb ) ;
2010-03-25 14:14:33 +03:00
fail4 :
if ( ! is_otg_enabled ( musb ) & & is_host_enabled ( musb ) )
usb_remove_hcd ( musb_to_hcd ( musb ) ) ;
else
musb_gadget_cleanup ( musb ) ;
fail3 :
if ( musb - > irq_wake )
device_init_wakeup ( dev , 0 ) ;
2008-07-24 13:27:36 +04:00
musb_platform_exit ( musb ) ;
2008-09-11 12:53:25 +04:00
2010-03-25 14:14:33 +03:00
fail2 :
2008-09-11 12:53:25 +04:00
if ( musb - > clock )
clk_put ( musb - > clock ) ;
2010-03-25 14:14:33 +03:00
fail1 :
dev_err ( musb - > controller ,
" musb_init_controller failed with status %d \n " , status ) ;
2008-09-11 12:53:25 +04:00
musb_free ( musb ) ;
2010-03-25 14:14:33 +03:00
fail0 :
2008-09-11 12:53:25 +04:00
return status ;
2008-07-24 13:27:36 +04:00
}
/*-------------------------------------------------------------------------*/
/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just
* bridge to a platform device ; this driver then suffices .
*/
# ifndef CONFIG_MUSB_PIO_ONLY
static u64 * orig_dma_mask ;
# endif
static int __init musb_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
int irq = platform_get_irq ( pdev , 0 ) ;
2010-01-21 16:33:57 +03:00
int status ;
2008-07-24 13:27:36 +04:00
struct resource * iomem ;
void __iomem * base ;
iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! iomem | | irq = = 0 )
return - ENODEV ;
2009-12-15 12:08:42 +03:00
base = ioremap ( iomem - > start , resource_size ( iomem ) ) ;
2008-07-24 13:27:36 +04:00
if ( ! base ) {
dev_err ( dev , " ioremap failed \n " ) ;
return - ENOMEM ;
}
# ifndef CONFIG_MUSB_PIO_ONLY
/* clobbered by use_dma=n */
orig_dma_mask = dev - > dma_mask ;
# endif
2010-01-21 16:33:57 +03:00
status = musb_init_controller ( dev , irq , base ) ;
if ( status < 0 )
iounmap ( base ) ;
return status ;
2008-07-24 13:27:36 +04:00
}
2009-12-15 12:08:41 +03:00
static int __exit musb_remove ( struct platform_device * pdev )
2008-07-24 13:27:36 +04:00
{
struct musb * musb = dev_to_musb ( & pdev - > dev ) ;
void __iomem * ctrl_base = musb - > ctrl_base ;
/* this gets called on rmmod.
* - Host mode : host may still be active
* - Peripheral mode : peripheral is deactivated ( or never - activated )
* - OTG mode : both roles are deactivated ( or never - activated )
*/
2010-03-12 11:29:11 +03:00
musb_exit_debugfs ( musb ) ;
2008-07-24 13:27:36 +04:00
musb_shutdown ( pdev ) ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
if ( musb - > board_mode = = MUSB_HOST )
usb_remove_hcd ( musb_to_hcd ( musb ) ) ;
# endif
2010-03-25 14:14:32 +03:00
musb_writeb ( musb - > mregs , MUSB_DEVCTL , 0 ) ;
musb_platform_exit ( musb ) ;
musb_writeb ( musb - > mregs , MUSB_DEVCTL , 0 ) ;
2008-07-24 13:27:36 +04:00
musb_free ( musb ) ;
iounmap ( ctrl_base ) ;
device_init_wakeup ( & pdev - > dev , 0 ) ;
# ifndef CONFIG_MUSB_PIO_ONLY
pdev - > dev . dma_mask = orig_dma_mask ;
# endif
return 0 ;
}
# ifdef CONFIG_PM
2010-01-21 16:33:52 +03:00
static struct musb_context_registers musb_context ;
void musb_save_context ( struct musb * musb )
{
int i ;
void __iomem * musb_base = musb - > mregs ;
if ( is_host_enabled ( musb ) ) {
musb_context . frame = musb_readw ( musb_base , MUSB_FRAME ) ;
musb_context . testmode = musb_readb ( musb_base , MUSB_TESTMODE ) ;
2010-03-25 14:14:26 +03:00
musb_context . busctl = musb_read_ulpi_buscontrol ( musb - > mregs ) ;
2010-01-21 16:33:52 +03:00
}
musb_context . power = musb_readb ( musb_base , MUSB_POWER ) ;
musb_context . intrtxe = musb_readw ( musb_base , MUSB_INTRTXE ) ;
musb_context . intrrxe = musb_readw ( musb_base , MUSB_INTRRXE ) ;
musb_context . intrusbe = musb_readb ( musb_base , MUSB_INTRUSBE ) ;
musb_context . index = musb_readb ( musb_base , MUSB_INDEX ) ;
musb_context . devctl = musb_readb ( musb_base , MUSB_DEVCTL ) ;
for ( i = 0 ; i < MUSB_C_NUM_EPS ; + + i ) {
musb_writeb ( musb_base , MUSB_INDEX , i ) ;
musb_context . index_regs [ i ] . txmaxp =
musb_readw ( musb_base , 0x10 + MUSB_TXMAXP ) ;
musb_context . index_regs [ i ] . txcsr =
musb_readw ( musb_base , 0x10 + MUSB_TXCSR ) ;
musb_context . index_regs [ i ] . rxmaxp =
musb_readw ( musb_base , 0x10 + MUSB_RXMAXP ) ;
musb_context . index_regs [ i ] . rxcsr =
musb_readw ( musb_base , 0x10 + MUSB_RXCSR ) ;
if ( musb - > dyn_fifo ) {
musb_context . index_regs [ i ] . txfifoadd =
musb_read_txfifoadd ( musb_base ) ;
musb_context . index_regs [ i ] . rxfifoadd =
musb_read_rxfifoadd ( musb_base ) ;
musb_context . index_regs [ i ] . txfifosz =
musb_read_txfifosz ( musb_base ) ;
musb_context . index_regs [ i ] . rxfifosz =
musb_read_rxfifosz ( musb_base ) ;
}
if ( is_host_enabled ( musb ) ) {
musb_context . index_regs [ i ] . txtype =
musb_readb ( musb_base , 0x10 + MUSB_TXTYPE ) ;
musb_context . index_regs [ i ] . txinterval =
musb_readb ( musb_base , 0x10 + MUSB_TXINTERVAL ) ;
musb_context . index_regs [ i ] . rxtype =
musb_readb ( musb_base , 0x10 + MUSB_RXTYPE ) ;
musb_context . index_regs [ i ] . rxinterval =
musb_readb ( musb_base , 0x10 + MUSB_RXINTERVAL ) ;
musb_context . index_regs [ i ] . txfunaddr =
musb_read_txfunaddr ( musb_base , i ) ;
musb_context . index_regs [ i ] . txhubaddr =
musb_read_txhubaddr ( musb_base , i ) ;
musb_context . index_regs [ i ] . txhubport =
musb_read_txhubport ( musb_base , i ) ;
musb_context . index_regs [ i ] . rxfunaddr =
musb_read_rxfunaddr ( musb_base , i ) ;
musb_context . index_regs [ i ] . rxhubaddr =
musb_read_rxhubaddr ( musb_base , i ) ;
musb_context . index_regs [ i ] . rxhubport =
musb_read_rxhubport ( musb_base , i ) ;
}
}
musb_writeb ( musb_base , MUSB_INDEX , musb_context . index ) ;
2010-01-21 16:33:53 +03:00
musb_platform_save_context ( musb , & musb_context ) ;
2010-01-21 16:33:52 +03:00
}
void musb_restore_context ( struct musb * musb )
{
int i ;
void __iomem * musb_base = musb - > mregs ;
void __iomem * ep_target_regs ;
2010-01-21 16:33:53 +03:00
musb_platform_restore_context ( musb , & musb_context ) ;
2010-01-21 16:33:52 +03:00
if ( is_host_enabled ( musb ) ) {
musb_writew ( musb_base , MUSB_FRAME , musb_context . frame ) ;
musb_writeb ( musb_base , MUSB_TESTMODE , musb_context . testmode ) ;
2010-03-25 14:14:26 +03:00
musb_write_ulpi_buscontrol ( musb - > mregs , musb_context . busctl ) ;
2010-01-21 16:33:52 +03:00
}
musb_writeb ( musb_base , MUSB_POWER , musb_context . power ) ;
musb_writew ( musb_base , MUSB_INTRTXE , musb_context . intrtxe ) ;
musb_writew ( musb_base , MUSB_INTRRXE , musb_context . intrrxe ) ;
musb_writeb ( musb_base , MUSB_INTRUSBE , musb_context . intrusbe ) ;
musb_writeb ( musb_base , MUSB_DEVCTL , musb_context . devctl ) ;
for ( i = 0 ; i < MUSB_C_NUM_EPS ; + + i ) {
musb_writeb ( musb_base , MUSB_INDEX , i ) ;
musb_writew ( musb_base , 0x10 + MUSB_TXMAXP ,
musb_context . index_regs [ i ] . txmaxp ) ;
musb_writew ( musb_base , 0x10 + MUSB_TXCSR ,
musb_context . index_regs [ i ] . txcsr ) ;
musb_writew ( musb_base , 0x10 + MUSB_RXMAXP ,
musb_context . index_regs [ i ] . rxmaxp ) ;
musb_writew ( musb_base , 0x10 + MUSB_RXCSR ,
musb_context . index_regs [ i ] . rxcsr ) ;
if ( musb - > dyn_fifo ) {
musb_write_txfifosz ( musb_base ,
musb_context . index_regs [ i ] . txfifosz ) ;
musb_write_rxfifosz ( musb_base ,
musb_context . index_regs [ i ] . rxfifosz ) ;
musb_write_txfifoadd ( musb_base ,
musb_context . index_regs [ i ] . txfifoadd ) ;
musb_write_rxfifoadd ( musb_base ,
musb_context . index_regs [ i ] . rxfifoadd ) ;
}
if ( is_host_enabled ( musb ) ) {
musb_writeb ( musb_base , 0x10 + MUSB_TXTYPE ,
musb_context . index_regs [ i ] . txtype ) ;
musb_writeb ( musb_base , 0x10 + MUSB_TXINTERVAL ,
musb_context . index_regs [ i ] . txinterval ) ;
musb_writeb ( musb_base , 0x10 + MUSB_RXTYPE ,
musb_context . index_regs [ i ] . rxtype ) ;
musb_writeb ( musb_base , 0x10 + MUSB_RXINTERVAL ,
musb_context . index_regs [ i ] . rxinterval ) ;
musb_write_txfunaddr ( musb_base , i ,
musb_context . index_regs [ i ] . txfunaddr ) ;
musb_write_txhubaddr ( musb_base , i ,
musb_context . index_regs [ i ] . txhubaddr ) ;
musb_write_txhubport ( musb_base , i ,
musb_context . index_regs [ i ] . txhubport ) ;
ep_target_regs =
musb_read_target_reg_base ( i , musb_base ) ;
musb_write_rxfunaddr ( ep_target_regs ,
musb_context . index_regs [ i ] . rxfunaddr ) ;
musb_write_rxhubaddr ( ep_target_regs ,
musb_context . index_regs [ i ] . rxhubaddr ) ;
musb_write_rxhubport ( ep_target_regs ,
musb_context . index_regs [ i ] . rxhubport ) ;
}
}
musb_writeb ( musb_base , MUSB_INDEX , musb_context . index ) ;
}
2009-07-08 15:22:56 +04:00
static int musb_suspend ( struct device * dev )
2008-07-24 13:27:36 +04:00
{
2009-07-08 15:22:56 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2008-07-24 13:27:36 +04:00
unsigned long flags ;
struct musb * musb = dev_to_musb ( & pdev - > dev ) ;
if ( ! musb - > clock )
return 0 ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
if ( is_peripheral_active ( musb ) ) {
/* FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond . . .
*/
} else if ( is_host_active ( musb ) ) {
/* we know all the children are suspended; sometimes
* they will even be wakeup - enabled .
*/
}
2010-01-21 16:33:52 +03:00
musb_save_context ( musb ) ;
2008-07-24 13:27:36 +04:00
if ( musb - > set_clock )
musb - > set_clock ( musb - > clock , 0 ) ;
else
clk_disable ( musb - > clock ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
return 0 ;
}
2009-07-08 15:22:56 +04:00
static int musb_resume_noirq ( struct device * dev )
2008-07-24 13:27:36 +04:00
{
2009-07-08 15:22:56 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2008-07-24 13:27:36 +04:00
struct musb * musb = dev_to_musb ( & pdev - > dev ) ;
if ( ! musb - > clock )
return 0 ;
if ( musb - > set_clock )
musb - > set_clock ( musb - > clock , 1 ) ;
else
clk_enable ( musb - > clock ) ;
2010-01-21 16:33:52 +03:00
musb_restore_context ( musb ) ;
2008-07-24 13:27:36 +04:00
/* for static cmos like DaVinci, register values were preserved
2009-03-27 04:56:51 +03:00
* unless for some reason the whole soc powered down or the USB
* module got reset through the PSC ( vs just being disabled ) .
2008-07-24 13:27:36 +04:00
*/
return 0 ;
}
2009-12-15 05:00:08 +03:00
static const struct dev_pm_ops musb_dev_pm_ops = {
2009-07-08 15:22:56 +04:00
. suspend = musb_suspend ,
. resume_noirq = musb_resume_noirq ,
} ;
# define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
2008-07-24 13:27:36 +04:00
# else
2009-07-08 15:22:56 +04:00
# define MUSB_DEV_PM_OPS NULL
2008-07-24 13:27:36 +04:00
# endif
static struct platform_driver musb_driver = {
. driver = {
. name = ( char * ) musb_driver_name ,
. bus = & platform_bus_type ,
. owner = THIS_MODULE ,
2009-07-08 15:22:56 +04:00
. pm = MUSB_DEV_PM_OPS ,
2008-07-24 13:27:36 +04:00
} ,
2009-12-15 12:08:41 +03:00
. remove = __exit_p ( musb_remove ) ,
2008-07-24 13:27:36 +04:00
. shutdown = musb_shutdown ,
} ;
/*-------------------------------------------------------------------------*/
static int __init musb_init ( void )
{
# ifdef CONFIG_USB_MUSB_HDRC_HCD
if ( usb_disabled ( ) )
return 0 ;
# endif
pr_info ( " %s: version " MUSB_VERSION " , "
# ifdef CONFIG_MUSB_PIO_ONLY
" pio "
# elif defined(CONFIG_USB_TI_CPPI_DMA)
" cppi-dma "
# elif defined(CONFIG_USB_INVENTRA_DMA)
" musb-dma "
# elif defined(CONFIG_USB_TUSB_OMAP_DMA)
" tusb-omap-dma "
# else
" ?dma? "
# endif
" , "
# ifdef CONFIG_USB_MUSB_OTG
" otg (peripheral+host) "
# elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
" peripheral "
# elif defined(CONFIG_USB_MUSB_HDRC_HCD)
" host "
# endif
" , debug=%d \n " ,
2008-10-29 16:10:39 +03:00
musb_driver_name , musb_debug ) ;
2008-07-24 13:27:36 +04:00
return platform_driver_probe ( & musb_driver , musb_probe ) ;
}
2009-02-21 00:45:17 +03:00
/* make us init after usbcore and i2c (transceivers, regulators, etc)
* and before usb gadget and host - side drivers start to register
2008-07-24 13:27:36 +04:00
*/
2009-02-21 00:45:17 +03:00
fs_initcall ( musb_init ) ;
2008-07-24 13:27:36 +04:00
static void __exit musb_cleanup ( void )
{
platform_driver_unregister ( & musb_driver ) ;
}
module_exit ( musb_cleanup ) ;