2008-07-24 13:27:36 +04:00
/*
* Copyright ( C ) 2005 - 2007 by Texas Instruments
* Some code has been taken from tusb6010 . c
* Copyrights for that are attributable to :
* Copyright ( C ) 2006 Nokia Corporation
* Tony Lindgren < tony @ atomide . com >
*
* This file is part of the Inventra Controller Driver for Linux .
*
* The Inventra Controller Driver for Linux 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 .
*
* The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not ,
* write to the Free Software Foundation , Inc . , 59 Temple Place ,
* Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# 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>
2009-10-20 20:40:47 +04:00
# include <plat/mux.h>
2008-07-24 13:27:36 +04:00
# include "musb_core.h"
# include "omap2430.h"
static struct timer_list musb_idle_timer ;
static void musb_do_idle ( unsigned long _musb )
{
struct musb * musb = ( void * ) _musb ;
unsigned long flags ;
2008-10-29 16:10:38 +03:00
# ifdef CONFIG_USB_MUSB_HDRC_HCD
2008-07-24 13:27:36 +04:00
u8 power ;
2008-10-29 16:10:38 +03:00
# endif
2008-07-24 13:27:36 +04:00
u8 devctl ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
2008-11-24 14:06:49 +03:00
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-07-24 13:27:36 +04:00
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 ) {
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_IDLE ;
2008-07-24 13:27:36 +04:00
MUSB_DEV_MODE ( musb ) ;
} else {
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 ) ;
}
break ;
# ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_SUSPEND :
/* finish RESUME signaling? */
if ( musb - > port1_status & MUSB_PORT_STAT_RESUME ) {
power = musb_readb ( musb - > mregs , MUSB_POWER ) ;
power & = ~ MUSB_POWER_RESUME ;
DBG ( 1 , " root port resume stopped, power %02x \n " , power ) ;
musb_writeb ( musb - > mregs , MUSB_POWER , power ) ;
musb - > is_active = 1 ;
musb - > port1_status & = ~ ( USB_PORT_STAT_SUSPEND
| MUSB_PORT_STAT_RESUME ) ;
musb - > port1_status | = USB_PORT_STAT_C_SUSPEND < < 16 ;
usb_hcd_poll_rh_status ( musb_to_hcd ( musb ) ) ;
/* NOTE: it might really be A_WAIT_BCON ... */
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_HOST ;
2008-07-24 13:27:36 +04:00
}
break ;
# endif
# ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST :
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
if ( devctl & MUSB_DEVCTL_BDEVICE )
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_B_IDLE ;
2008-07-24 13:27:36 +04:00
else
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_WAIT_BCON ;
2008-07-24 13:27:36 +04:00
# endif
default :
break ;
}
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
}
void musb_platform_try_idle ( struct musb * musb , unsigned long timeout )
{
unsigned long default_timeout = jiffies + msecs_to_jiffies ( 3 ) ;
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 )
2009-03-31 23:30:04 +04:00
& & ( musb - > xceiv - > state = = OTG_STATE_A_WAIT_BCON ) ) ) {
2008-07-24 13:27:36 +04:00
DBG ( 4 , " %s active, deleting timer \n " , otg_state_string ( musb ) ) ;
del_timer ( & musb_idle_timer ) ;
last_timer = jiffies ;
return ;
}
if ( time_after ( last_timer , timeout ) ) {
if ( ! timer_pending ( & musb_idle_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 ( & musb_idle_timer , timeout ) ;
}
void musb_platform_enable ( struct musb * musb )
{
}
void musb_platform_disable ( struct musb * musb )
{
}
static void omap_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 ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > default_a = 1 ;
musb - > xceiv - > state = OTG_STATE_A_WAIT_VRISE ;
2008-07-24 13:27:36 +04:00
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 . . .
*/
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
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 ) ) ;
}
static int musb_platform_resume ( struct musb * musb ) ;
2008-11-24 14:06:47 +03:00
int musb_platform_set_mode ( struct musb * musb , u8 musb_mode )
2008-07-24 13:27:36 +04:00
{
u8 devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
devctl | = MUSB_DEVCTL_SESSION ;
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
2008-11-24 14:06:47 +03:00
return 0 ;
2008-07-24 13:27:36 +04:00
}
2010-03-12 11:29:07 +03:00
int __init musb_platform_init ( struct musb * musb , void * board_data )
2008-07-24 13:27:36 +04:00
{
u32 l ;
2010-03-12 11:29:07 +03:00
struct omap_musb_board_data * data = board_data ;
2008-07-24 13:27:36 +04:00
# if defined(CONFIG_ARCH_OMAP2430)
omap_cfg_reg ( AE5_2430_USB0HS_STP ) ;
# endif
2009-03-31 23:30:04 +04:00
/* We require some kind of external transceiver, hooked
* up through ULPI . TWL4030 - family PMICs include one ,
* which needs a driver , drivers aren ' t always needed .
*/
musb - > xceiv = otg_get_transceiver ( ) ;
if ( ! musb - > xceiv ) {
pr_err ( " HS USB OTG: no transceiver configured \n " ) ;
return - ENODEV ;
}
2008-07-24 13:27:36 +04:00
musb_platform_resume ( musb ) ;
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_SYSCONFIG ) ;
2008-07-24 13:27:36 +04:00
l & = ~ ENABLEWAKEUP ; /* disable wakeup */
l & = ~ NOSTDBY ; /* remove possible nostdby */
l | = SMARTSTDBY ; /* enable smart standby */
l & = ~ AUTOIDLE ; /* disable auto idle */
l & = ~ NOIDLE ; /* remove possible noidle */
l | = SMARTIDLE ; /* enable smart idle */
2009-05-18 18:54:16 +04:00
/*
* MUSB AUTOIDLE don ' t work in 3430.
* Workaround by Richard Woodruff / TI
*/
if ( ! cpu_is_omap3430 ( ) )
l | = AUTOIDLE ; /* enable auto idle */
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_SYSCONFIG , l ) ;
2008-07-24 13:27:36 +04:00
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_INTERFSEL ) ;
2010-03-12 11:29:07 +03:00
if ( data - > interface_type = = MUSB_INTERFACE_UTMI ) {
/* OMAP4 uses Internal PHY GS70 which uses UTMI interface */
l & = ~ ULPI_12PIN ; /* Disable ULPI */
l | = UTMI_8BIT ; /* Enable UTMI */
} else {
l | = ULPI_12PIN ;
}
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_INTERFSEL , l ) ;
2008-07-24 13:27:36 +04:00
pr_debug ( " HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
" sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x \n " ,
2010-01-21 16:33:53 +03:00
musb_readl ( musb - > mregs , OTG_REVISION ) ,
musb_readl ( musb - > mregs , OTG_SYSCONFIG ) ,
musb_readl ( musb - > mregs , OTG_SYSSTATUS ) ,
musb_readl ( musb - > mregs , OTG_INTERFSEL ) ,
musb_readl ( musb - > mregs , OTG_SIMENABLE ) ) ;
2008-07-24 13:27:36 +04:00
if ( is_host_enabled ( musb ) )
musb - > board_set_vbus = omap_set_vbus ;
setup_timer ( & musb_idle_timer , musb_do_idle , ( unsigned long ) musb ) ;
return 0 ;
}
2010-01-21 16:33:52 +03:00
# ifdef CONFIG_PM
2010-01-21 16:33:53 +03:00
void musb_platform_save_context ( struct musb * musb ,
struct musb_context_registers * musb_context )
2010-01-21 16:33:52 +03:00
{
2010-01-21 16:33:53 +03:00
musb_context - > otg_sysconfig = musb_readl ( musb - > mregs , OTG_SYSCONFIG ) ;
musb_context - > otg_forcestandby = musb_readl ( musb - > mregs , OTG_FORCESTDBY ) ;
2010-01-21 16:33:52 +03:00
}
2010-01-21 16:33:53 +03:00
void musb_platform_restore_context ( struct musb * musb ,
struct musb_context_registers * musb_context )
2010-01-21 16:33:52 +03:00
{
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_SYSCONFIG , musb_context - > otg_sysconfig ) ;
musb_writel ( musb - > mregs , OTG_FORCESTDBY , musb_context - > otg_forcestandby ) ;
2010-01-21 16:33:52 +03:00
}
# endif
2010-03-25 14:25:22 +03:00
static int musb_platform_suspend ( struct musb * musb )
2008-07-24 13:27:36 +04:00
{
u32 l ;
if ( ! musb - > clock )
return 0 ;
/* in any role */
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_FORCESTDBY ) ;
2008-07-24 13:27:36 +04:00
l | = ENABLEFORCE ; /* enable MSTANDBY */
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_FORCESTDBY , l ) ;
2008-07-24 13:27:36 +04:00
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_SYSCONFIG ) ;
2008-07-24 13:27:36 +04:00
l | = ENABLEWAKEUP ; /* enable wakeup */
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_SYSCONFIG , l ) ;
2008-07-24 13:27:36 +04:00
2009-03-31 23:30:04 +04:00
otg_set_suspend ( musb - > xceiv , 1 ) ;
2008-07-24 13:27:36 +04:00
if ( musb - > set_clock )
musb - > set_clock ( musb - > clock , 0 ) ;
else
clk_disable ( musb - > clock ) ;
return 0 ;
}
static int musb_platform_resume ( struct musb * musb )
{
u32 l ;
if ( ! musb - > clock )
return 0 ;
2009-03-31 23:30:04 +04:00
otg_set_suspend ( musb - > xceiv , 0 ) ;
2008-07-24 13:27:36 +04:00
if ( musb - > set_clock )
musb - > set_clock ( musb - > clock , 1 ) ;
else
clk_enable ( musb - > clock ) ;
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_SYSCONFIG ) ;
2008-07-24 13:27:36 +04:00
l & = ~ ENABLEWAKEUP ; /* disable wakeup */
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_SYSCONFIG , l ) ;
2008-07-24 13:27:36 +04:00
2010-01-21 16:33:53 +03:00
l = musb_readl ( musb - > mregs , OTG_FORCESTDBY ) ;
2008-07-24 13:27:36 +04:00
l & = ~ ENABLEFORCE ; /* disable MSTANDBY */
2010-01-21 16:33:53 +03:00
musb_writel ( musb - > mregs , OTG_FORCESTDBY , l ) ;
2008-07-24 13:27:36 +04:00
return 0 ;
}
int musb_platform_exit ( struct musb * musb )
{
musb_platform_suspend ( musb ) ;
return 0 ;
}