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
* Jarkko Nikula < jarkko . nikula @ nokia . com >
* 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/slab.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <asm/mach-types.h>
2008-08-30 20:42:02 +04:00
# include <mach/hardware.h>
# include <mach/mux.h>
2008-07-24 13:27:36 +04:00
# include "musb_core.h"
# include "omap2430.h"
# ifdef CONFIG_ARCH_OMAP3430
# define get_cpu_rev() 2
# endif
# define MUSB_TIMEOUT_A_WAIT_BCON 1100
static struct timer_list musb_idle_timer ;
static void musb_do_idle ( unsigned long _musb )
{
struct musb * musb = ( void * ) _musb ;
unsigned long flags ;
u8 power ;
u8 devctl ;
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
switch ( musb - > xceiv . state ) {
case OTG_STATE_A_WAIT_BCON :
devctl & = ~ MUSB_DEVCTL_SESSION ;
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
if ( devctl & MUSB_DEVCTL_BDEVICE ) {
musb - > xceiv . state = OTG_STATE_B_IDLE ;
MUSB_DEV_MODE ( musb ) ;
} else {
musb - > xceiv . state = OTG_STATE_A_IDLE ;
MUSB_HST_MODE ( musb ) ;
}
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 ... */
musb - > xceiv . state = OTG_STATE_A_HOST ;
}
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 )
musb - > xceiv . state = OTG_STATE_B_IDLE ;
else
musb - > xceiv . state = OTG_STATE_A_WAIT_BCON ;
# 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 )
& & ( musb - > xceiv . state = = OTG_STATE_A_WAIT_BCON ) ) ) {
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_vbus_power ( struct musb * musb , int is_on , int sleeping )
{
}
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 ;
musb - > xceiv . default_a = 1 ;
musb - > xceiv . state = OTG_STATE_A_WAIT_VRISE ;
devctl | = MUSB_DEVCTL_SESSION ;
MUSB_HST_MODE ( musb ) ;
} else {
musb - > is_active = 0 ;
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
* jumping right to B_IDLE . . .
*/
musb - > xceiv . default_a = 0 ;
musb - > xceiv . state = OTG_STATE_B_IDLE ;
devctl & = ~ MUSB_DEVCTL_SESSION ;
MUSB_DEV_MODE ( musb ) ;
}
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
DBG ( 1 , " VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ " \n " ,
otg_state_string ( musb ) ,
musb_readb ( musb - > mregs , MUSB_DEVCTL ) ) ;
}
static int omap_set_power ( struct otg_transceiver * x , unsigned mA )
{
return 0 ;
}
static int musb_platform_resume ( struct musb * musb ) ;
void musb_platform_set_mode ( struct musb * musb , u8 musb_mode )
{
u8 devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
devctl | = MUSB_DEVCTL_SESSION ;
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
switch ( musb_mode ) {
case MUSB_HOST :
otg_set_host ( & musb - > xceiv , musb - > xceiv . host ) ;
break ;
case MUSB_PERIPHERAL :
otg_set_peripheral ( & musb - > xceiv , musb - > xceiv . gadget ) ;
break ;
case MUSB_OTG :
break ;
}
}
int __init musb_platform_init ( struct musb * musb )
{
u32 l ;
# if defined(CONFIG_ARCH_OMAP2430)
omap_cfg_reg ( AE5_2430_USB0HS_STP ) ;
# endif
musb_platform_resume ( musb ) ;
l = omap_readl ( OTG_SYSCONFIG ) ;
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 */
l | = AUTOIDLE ; /* enable auto idle */
omap_writel ( l , OTG_SYSCONFIG ) ;
l = omap_readl ( OTG_INTERFSEL ) ;
l | = ULPI_12PIN ;
omap_writel ( l , OTG_INTERFSEL ) ;
pr_debug ( " HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
" sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x \n " ,
omap_readl ( OTG_REVISION ) , omap_readl ( OTG_SYSCONFIG ) ,
omap_readl ( OTG_SYSSTATUS ) , omap_readl ( OTG_INTERFSEL ) ,
omap_readl ( OTG_SIMENABLE ) ) ;
omap_vbus_power ( musb , musb - > board_mode = = MUSB_HOST , 1 ) ;
if ( is_host_enabled ( musb ) )
musb - > board_set_vbus = omap_set_vbus ;
if ( is_peripheral_enabled ( musb ) )
musb - > xceiv . set_power = omap_set_power ;
musb - > a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON ;
setup_timer ( & musb_idle_timer , musb_do_idle , ( unsigned long ) musb ) ;
return 0 ;
}
int musb_platform_suspend ( struct musb * musb )
{
u32 l ;
if ( ! musb - > clock )
return 0 ;
/* in any role */
l = omap_readl ( OTG_FORCESTDBY ) ;
l | = ENABLEFORCE ; /* enable MSTANDBY */
omap_writel ( l , OTG_FORCESTDBY ) ;
l = omap_readl ( OTG_SYSCONFIG ) ;
l | = ENABLEWAKEUP ; /* enable wakeup */
omap_writel ( l , OTG_SYSCONFIG ) ;
if ( musb - > xceiv . set_suspend )
musb - > xceiv . set_suspend ( & musb - > xceiv , 1 ) ;
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 ;
if ( musb - > xceiv . set_suspend )
musb - > xceiv . set_suspend ( & musb - > xceiv , 0 ) ;
if ( musb - > set_clock )
musb - > set_clock ( musb - > clock , 1 ) ;
else
clk_enable ( musb - > clock ) ;
l = omap_readl ( OTG_SYSCONFIG ) ;
l & = ~ ENABLEWAKEUP ; /* disable wakeup */
omap_writel ( l , OTG_SYSCONFIG ) ;
l = omap_readl ( OTG_FORCESTDBY ) ;
l & = ~ ENABLEFORCE ; /* disable MSTANDBY */
omap_writel ( l , OTG_FORCESTDBY ) ;
return 0 ;
}
int musb_platform_exit ( struct musb * musb )
{
omap_vbus_power ( musb , 0 /*off*/ , 1 ) ;
musb_platform_suspend ( musb ) ;
clk_put ( musb - > clock ) ;
musb - > clock = 0 ;
return 0 ;
}