2011-05-04 08:49:47 +04:00
/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
2010-12-07 15:23:55 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only 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 Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*
*/
# include <linux/module.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/uaccess.h>
# include <linux/debugfs.h>
# include <linux/seq_file.h>
2010-12-07 15:23:58 +03:00
# include <linux/pm_runtime.h>
2010-12-07 15:23:55 +03:00
# include <linux/usb.h>
# include <linux/usb/otg.h>
# include <linux/usb/ulpi.h>
# include <linux/usb/gadget.h>
# include <linux/usb/hcd.h>
# include <linux/usb/msm_hsusb.h>
# include <linux/usb/msm_hsusb_hw.h>
2011-05-04 08:49:48 +04:00
# include <linux/regulator/consumer.h>
2010-12-07 15:23:55 +03:00
# include <mach/clk.h>
# define MSM_USB_BASE (motg->regs)
# define DRIVER_NAME "msm_otg"
# define ULPI_IO_TIMEOUT_USEC (10 * 1000)
2011-05-04 08:49:48 +04:00
# define USB_PHY_3P3_VOL_MIN 3050000 /* uV */
# define USB_PHY_3P3_VOL_MAX 3300000 /* uV */
# define USB_PHY_3P3_HPM_LOAD 50000 /* uA */
# define USB_PHY_3P3_LPM_LOAD 4000 /* uA */
# define USB_PHY_1P8_VOL_MIN 1800000 /* uV */
# define USB_PHY_1P8_VOL_MAX 1800000 /* uV */
# define USB_PHY_1P8_HPM_LOAD 50000 /* uA */
# define USB_PHY_1P8_LPM_LOAD 4000 /* uA */
# define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */
# define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
static struct regulator * hsusb_3p3 ;
static struct regulator * hsusb_1p8 ;
static struct regulator * hsusb_vddcx ;
static int msm_hsusb_init_vddcx ( struct msm_otg * motg , int init )
{
int ret = 0 ;
if ( init ) {
2012-02-13 15:24:09 +04:00
hsusb_vddcx = regulator_get ( motg - > phy . dev , " HSUSB_VDDCX " ) ;
2011-05-04 08:49:48 +04:00
if ( IS_ERR ( hsusb_vddcx ) ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to get hsusb vddcx \n " ) ;
2011-05-04 08:49:48 +04:00
return PTR_ERR ( hsusb_vddcx ) ;
}
ret = regulator_set_voltage ( hsusb_vddcx ,
USB_PHY_VDD_DIG_VOL_MIN ,
USB_PHY_VDD_DIG_VOL_MAX ) ;
if ( ret ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to set the voltage "
2011-05-04 08:49:48 +04:00
" for hsusb vddcx \n " ) ;
regulator_put ( hsusb_vddcx ) ;
return ret ;
}
ret = regulator_enable ( hsusb_vddcx ) ;
if ( ret ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to enable hsusb vddcx \n " ) ;
2011-05-04 08:49:48 +04:00
regulator_put ( hsusb_vddcx ) ;
}
} else {
ret = regulator_set_voltage ( hsusb_vddcx , 0 ,
2011-05-15 20:55:57 +04:00
USB_PHY_VDD_DIG_VOL_MAX ) ;
2011-05-15 20:55:58 +04:00
if ( ret )
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to set the voltage "
2011-05-04 08:49:48 +04:00
" for hsusb vddcx \n " ) ;
ret = regulator_disable ( hsusb_vddcx ) ;
if ( ret )
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to disable hsusb vddcx \n " ) ;
2011-05-04 08:49:48 +04:00
regulator_put ( hsusb_vddcx ) ;
}
return ret ;
}
static int msm_hsusb_ldo_init ( struct msm_otg * motg , int init )
{
int rc = 0 ;
if ( init ) {
2012-02-13 15:24:09 +04:00
hsusb_3p3 = regulator_get ( motg - > phy . dev , " HSUSB_3p3 " ) ;
2011-05-04 08:49:48 +04:00
if ( IS_ERR ( hsusb_3p3 ) ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to get hsusb 3p3 \n " ) ;
2011-05-04 08:49:48 +04:00
return PTR_ERR ( hsusb_3p3 ) ;
}
rc = regulator_set_voltage ( hsusb_3p3 , USB_PHY_3P3_VOL_MIN ,
USB_PHY_3P3_VOL_MAX ) ;
if ( rc ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to set voltage level "
2011-05-04 08:49:48 +04:00
" for hsusb 3p3 \n " ) ;
goto put_3p3 ;
}
rc = regulator_enable ( hsusb_3p3 ) ;
if ( rc ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to enable the hsusb 3p3 \n " ) ;
2011-05-04 08:49:48 +04:00
goto put_3p3 ;
}
2012-02-13 15:24:09 +04:00
hsusb_1p8 = regulator_get ( motg - > phy . dev , " HSUSB_1p8 " ) ;
2011-05-04 08:49:48 +04:00
if ( IS_ERR ( hsusb_1p8 ) ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to get hsusb 1p8 \n " ) ;
2011-05-04 08:49:48 +04:00
rc = PTR_ERR ( hsusb_1p8 ) ;
goto disable_3p3 ;
}
rc = regulator_set_voltage ( hsusb_1p8 , USB_PHY_1P8_VOL_MIN ,
USB_PHY_1P8_VOL_MAX ) ;
if ( rc ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to set voltage level "
2011-05-04 08:49:48 +04:00
" for hsusb 1p8 \n " ) ;
goto put_1p8 ;
}
rc = regulator_enable ( hsusb_1p8 ) ;
if ( rc ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " unable to enable the hsusb 1p8 \n " ) ;
2011-05-04 08:49:48 +04:00
goto put_1p8 ;
}
return 0 ;
}
regulator_disable ( hsusb_1p8 ) ;
put_1p8 :
regulator_put ( hsusb_1p8 ) ;
disable_3p3 :
regulator_disable ( hsusb_3p3 ) ;
put_3p3 :
regulator_put ( hsusb_3p3 ) ;
return rc ;
}
2011-05-04 08:49:49 +04:00
# ifdef CONFIG_PM_SLEEP
# define USB_PHY_SUSP_DIG_VOL 500000
static int msm_hsusb_config_vddcx ( int high )
{
int max_vol = USB_PHY_VDD_DIG_VOL_MAX ;
int min_vol ;
int ret ;
if ( high )
min_vol = USB_PHY_VDD_DIG_VOL_MIN ;
else
min_vol = USB_PHY_SUSP_DIG_VOL ;
ret = regulator_set_voltage ( hsusb_vddcx , min_vol , max_vol ) ;
if ( ret ) {
pr_err ( " %s: unable to set the voltage for regulator "
" HSUSB_VDDCX \n " , __func__ ) ;
return ret ;
}
pr_debug ( " %s: min_vol:%d max_vol:%d \n " , __func__ , min_vol , max_vol ) ;
return ret ;
}
# endif
2011-05-04 08:49:48 +04:00
static int msm_hsusb_ldo_set_mode ( int on )
{
int ret = 0 ;
if ( ! hsusb_1p8 | | IS_ERR ( hsusb_1p8 ) ) {
pr_err ( " %s: HSUSB_1p8 is not initialized \n " , __func__ ) ;
return - ENODEV ;
}
if ( ! hsusb_3p3 | | IS_ERR ( hsusb_3p3 ) ) {
pr_err ( " %s: HSUSB_3p3 is not initialized \n " , __func__ ) ;
return - ENODEV ;
}
if ( on ) {
ret = regulator_set_optimum_mode ( hsusb_1p8 ,
USB_PHY_1P8_HPM_LOAD ) ;
if ( ret < 0 ) {
pr_err ( " %s: Unable to set HPM of the regulator "
" HSUSB_1p8 \n " , __func__ ) ;
return ret ;
}
ret = regulator_set_optimum_mode ( hsusb_3p3 ,
USB_PHY_3P3_HPM_LOAD ) ;
if ( ret < 0 ) {
pr_err ( " %s: Unable to set HPM of the regulator "
" HSUSB_3p3 \n " , __func__ ) ;
regulator_set_optimum_mode ( hsusb_1p8 ,
USB_PHY_1P8_LPM_LOAD ) ;
return ret ;
}
} else {
ret = regulator_set_optimum_mode ( hsusb_1p8 ,
USB_PHY_1P8_LPM_LOAD ) ;
if ( ret < 0 )
pr_err ( " %s: Unable to set LPM of the regulator "
" HSUSB_1p8 \n " , __func__ ) ;
ret = regulator_set_optimum_mode ( hsusb_3p3 ,
USB_PHY_3P3_LPM_LOAD ) ;
if ( ret < 0 )
pr_err ( " %s: Unable to set LPM of the regulator "
" HSUSB_3p3 \n " , __func__ ) ;
}
pr_debug ( " reg (%s) \n " , on ? " HPM " : " LPM " ) ;
return ret < 0 ? ret : 0 ;
}
2012-02-13 15:24:09 +04:00
static int ulpi_read ( struct usb_phy * phy , u32 reg )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
int cnt = 0 ;
/* initiate read operation */
writel ( ULPI_RUN | ULPI_READ | ULPI_ADDR ( reg ) ,
USB_ULPI_VIEWPORT ) ;
/* wait for completion */
while ( cnt < ULPI_IO_TIMEOUT_USEC ) {
if ( ! ( readl ( USB_ULPI_VIEWPORT ) & ULPI_RUN ) )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = ULPI_IO_TIMEOUT_USEC ) {
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " ulpi_read: timeout %08x \n " ,
2010-12-07 15:23:55 +03:00
readl ( USB_ULPI_VIEWPORT ) ) ;
return - ETIMEDOUT ;
}
return ULPI_DATA_READ ( readl ( USB_ULPI_VIEWPORT ) ) ;
}
2012-02-13 15:24:09 +04:00
static int ulpi_write ( struct usb_phy * phy , u32 val , u32 reg )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
int cnt = 0 ;
/* initiate write operation */
writel ( ULPI_RUN | ULPI_WRITE |
ULPI_ADDR ( reg ) | ULPI_DATA ( val ) ,
USB_ULPI_VIEWPORT ) ;
/* wait for completion */
while ( cnt < ULPI_IO_TIMEOUT_USEC ) {
if ( ! ( readl ( USB_ULPI_VIEWPORT ) & ULPI_RUN ) )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = ULPI_IO_TIMEOUT_USEC ) {
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " ulpi_write: timeout \n " ) ;
2010-12-07 15:23:55 +03:00
return - ETIMEDOUT ;
}
return 0 ;
}
2012-02-13 15:24:09 +04:00
static struct usb_phy_io_ops msm_otg_io_ops = {
2010-12-07 15:23:55 +03:00
. read = ulpi_read ,
. write = ulpi_write ,
} ;
static void ulpi_init ( struct msm_otg * motg )
{
struct msm_otg_platform_data * pdata = motg - > pdata ;
int * seq = pdata - > phy_init_seq ;
if ( ! seq )
return ;
while ( seq [ 0 ] > = 0 ) {
2012-02-13 15:24:09 +04:00
dev_vdbg ( motg - > phy . dev , " ulpi: write 0x%02x to 0x%02x \n " ,
2010-12-07 15:23:55 +03:00
seq [ 0 ] , seq [ 1 ] ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( & motg - > phy , seq [ 0 ] , seq [ 1 ] ) ;
2010-12-07 15:23:55 +03:00
seq + = 2 ;
}
}
static int msm_otg_link_clk_reset ( struct msm_otg * motg , bool assert )
{
int ret ;
if ( assert ) {
ret = clk_reset ( motg - > clk , CLK_RESET_ASSERT ) ;
if ( ret )
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " usb hs_clk assert failed \n " ) ;
2010-12-07 15:23:55 +03:00
} else {
ret = clk_reset ( motg - > clk , CLK_RESET_DEASSERT ) ;
if ( ret )
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " usb hs_clk deassert failed \n " ) ;
2010-12-07 15:23:55 +03:00
}
return ret ;
}
static int msm_otg_phy_clk_reset ( struct msm_otg * motg )
{
int ret ;
ret = clk_reset ( motg - > phy_reset_clk , CLK_RESET_ASSERT ) ;
if ( ret ) {
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " usb phy clk assert failed \n " ) ;
2010-12-07 15:23:55 +03:00
return ret ;
}
usleep_range ( 10000 , 12000 ) ;
ret = clk_reset ( motg - > phy_reset_clk , CLK_RESET_DEASSERT ) ;
if ( ret )
2012-02-13 15:24:09 +04:00
dev_err ( motg - > phy . dev , " usb phy clk deassert failed \n " ) ;
2010-12-07 15:23:55 +03:00
return ret ;
}
static int msm_otg_phy_reset ( struct msm_otg * motg )
{
u32 val ;
int ret ;
int retries ;
ret = msm_otg_link_clk_reset ( motg , 1 ) ;
if ( ret )
return ret ;
ret = msm_otg_phy_clk_reset ( motg ) ;
if ( ret )
return ret ;
ret = msm_otg_link_clk_reset ( motg , 0 ) ;
if ( ret )
return ret ;
val = readl ( USB_PORTSC ) & ~ PORTSC_PTS_MASK ;
writel ( val | PORTSC_PTS_ULPI , USB_PORTSC ) ;
for ( retries = 3 ; retries > 0 ; retries - - ) {
2012-02-13 15:24:09 +04:00
ret = ulpi_write ( & motg - > phy , ULPI_FUNC_CTRL_SUSPENDM ,
2010-12-07 15:23:55 +03:00
ULPI_CLR ( ULPI_FUNC_CTRL ) ) ;
if ( ! ret )
break ;
ret = msm_otg_phy_clk_reset ( motg ) ;
if ( ret )
return ret ;
}
if ( ! retries )
return - ETIMEDOUT ;
/* This reset calibrates the phy, if the above write succeeded */
ret = msm_otg_phy_clk_reset ( motg ) ;
if ( ret )
return ret ;
for ( retries = 3 ; retries > 0 ; retries - - ) {
2012-02-13 15:24:09 +04:00
ret = ulpi_read ( & motg - > phy , ULPI_DEBUG ) ;
2010-12-07 15:23:55 +03:00
if ( ret ! = - ETIMEDOUT )
break ;
ret = msm_otg_phy_clk_reset ( motg ) ;
if ( ret )
return ret ;
}
if ( ! retries )
return - ETIMEDOUT ;
2012-02-13 15:24:09 +04:00
dev_info ( motg - > phy . dev , " phy_reset: success \n " ) ;
2010-12-07 15:23:55 +03:00
return 0 ;
}
# define LINK_RESET_TIMEOUT_USEC (250 * 1000)
2012-02-13 15:24:09 +04:00
static int msm_otg_reset ( struct usb_phy * phy )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
struct msm_otg_platform_data * pdata = motg - > pdata ;
int cnt = 0 ;
int ret ;
u32 val = 0 ;
u32 ulpi_val = 0 ;
ret = msm_otg_phy_reset ( motg ) ;
if ( ret ) {
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " phy_reset failed \n " ) ;
2010-12-07 15:23:55 +03:00
return ret ;
}
ulpi_init ( motg ) ;
writel ( USBCMD_RESET , USB_USBCMD ) ;
while ( cnt < LINK_RESET_TIMEOUT_USEC ) {
if ( ! ( readl ( USB_USBCMD ) & USBCMD_RESET ) )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = LINK_RESET_TIMEOUT_USEC )
return - ETIMEDOUT ;
/* select ULPI phy */
writel ( 0x80000000 , USB_PORTSC ) ;
msleep ( 100 ) ;
writel ( 0x0 , USB_AHBBURST ) ;
writel ( 0x00 , USB_AHBMODE ) ;
if ( pdata - > otg_control = = OTG_PHY_CONTROL ) {
val = readl ( USB_OTGSC ) ;
if ( pdata - > mode = = USB_OTG ) {
ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID ;
val | = OTGSC_IDIE | OTGSC_BSVIE ;
} else if ( pdata - > mode = = USB_PERIPHERAL ) {
ulpi_val = ULPI_INT_SESS_VALID ;
val | = OTGSC_BSVIE ;
}
writel ( val , USB_OTGSC ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , ulpi_val , ULPI_USB_INT_EN_RISE ) ;
ulpi_write ( phy , ulpi_val , ULPI_USB_INT_EN_FALL ) ;
2010-12-07 15:23:55 +03:00
}
return 0 ;
}
2010-12-07 15:23:58 +03:00
# define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
2011-02-15 07:12:34 +03:00
# define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
# ifdef CONFIG_PM_SLEEP
2010-12-07 15:23:58 +03:00
static int msm_otg_suspend ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
struct usb_bus * bus = phy - > otg - > host ;
2010-12-07 15:23:58 +03:00
struct msm_otg_platform_data * pdata = motg - > pdata ;
int cnt = 0 ;
if ( atomic_read ( & motg - > in_lpm ) )
return 0 ;
disable_irq ( motg - > irq ) ;
/*
2011-05-04 08:49:49 +04:00
* Chipidea 45 - nm PHY suspend sequence :
*
2010-12-07 15:23:58 +03:00
* Interrupt Latch Register auto - clear feature is not present
* in all PHY versions . Latch register is clear on read type .
* Clear latch register to avoid spurious wakeup from
* low power mode ( LPM ) .
2011-05-04 08:49:49 +04:00
*
2010-12-07 15:23:58 +03:00
* PHY comparators are disabled when PHY enters into low power
* mode ( LPM ) . Keep PHY comparators ON in LPM only when we expect
* VBUS / Id notifications from USB PHY . Otherwise turn off USB
* PHY comparators . This save significant amount of power .
2011-05-04 08:49:49 +04:00
*
2010-12-07 15:23:58 +03:00
* PLL is not turned off when PHY enters into low power mode ( LPM ) .
* Disable PLL for maximum power savings .
*/
2011-05-04 08:49:49 +04:00
if ( motg - > pdata - > phy_type = = CI_45NM_INTEGRATED_PHY ) {
2012-02-13 15:24:09 +04:00
ulpi_read ( phy , 0x14 ) ;
2011-05-04 08:49:49 +04:00
if ( pdata - > otg_control = = OTG_PHY_CONTROL )
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x01 , 0x30 ) ;
ulpi_write ( phy , 0x08 , 0x09 ) ;
2011-05-04 08:49:49 +04:00
}
2010-12-07 15:23:58 +03:00
/*
* PHY may take some time or even fail to enter into low power
* mode ( LPM ) . Hence poll for 500 msec and reset the PHY and link
* in failure case .
*/
writel ( readl ( USB_PORTSC ) | PORTSC_PHCD , USB_PORTSC ) ;
while ( cnt < PHY_SUSPEND_TIMEOUT_USEC ) {
if ( readl ( USB_PORTSC ) & PORTSC_PHCD )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = PHY_SUSPEND_TIMEOUT_USEC ) {
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " Unable to suspend PHY \n " ) ;
msm_otg_reset ( phy ) ;
2010-12-07 15:23:58 +03:00
enable_irq ( motg - > irq ) ;
return - ETIMEDOUT ;
}
/*
* PHY has capability to generate interrupt asynchronously in low
* power mode ( LPM ) . This interrupt is level triggered . So USB IRQ
* line must be disabled till async interrupt enable bit is cleared
* in USBCMD register . Assert STP ( ULPI interface STOP signal ) to
* block data communication from PHY .
*/
writel ( readl ( USB_USBCMD ) | ASYNC_INTR_CTRL | ULPI_STP_CTRL , USB_USBCMD ) ;
2011-05-04 08:49:49 +04:00
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL )
writel ( readl ( USB_PHY_CTRL ) | PHY_RETEN , USB_PHY_CTRL ) ;
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2010-12-07 15:23:58 +03:00
if ( motg - > core_clk )
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > core_clk ) ;
2010-12-07 15:23:58 +03:00
2011-05-04 08:49:46 +04:00
if ( ! IS_ERR ( motg - > pclk_src ) )
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk_src ) ;
2011-05-04 08:49:46 +04:00
2011-05-04 08:49:49 +04:00
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL ) {
msm_hsusb_ldo_set_mode ( 0 ) ;
msm_hsusb_config_vddcx ( 0 ) ;
}
2012-02-13 15:24:09 +04:00
if ( device_may_wakeup ( phy - > dev ) )
2010-12-07 15:23:58 +03:00
enable_irq_wake ( motg - > irq ) ;
if ( bus )
clear_bit ( HCD_FLAG_HW_ACCESSIBLE , & ( bus_to_hcd ( bus ) ) - > flags ) ;
atomic_set ( & motg - > in_lpm , 1 ) ;
enable_irq ( motg - > irq ) ;
2012-02-13 15:24:09 +04:00
dev_info ( phy - > dev , " USB in low power mode \n " ) ;
2010-12-07 15:23:58 +03:00
return 0 ;
}
static int msm_otg_resume ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
struct usb_bus * bus = phy - > otg - > host ;
2010-12-07 15:23:58 +03:00
int cnt = 0 ;
unsigned temp ;
if ( ! atomic_read ( & motg - > in_lpm ) )
return 0 ;
2011-05-04 08:49:46 +04:00
if ( ! IS_ERR ( motg - > pclk_src ) )
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > pclk_src ) ;
2011-05-04 08:49:46 +04:00
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > pclk ) ;
clk_prepare_enable ( motg - > clk ) ;
2010-12-07 15:23:58 +03:00
if ( motg - > core_clk )
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > core_clk ) ;
2010-12-07 15:23:58 +03:00
2011-05-04 08:49:49 +04:00
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL ) {
msm_hsusb_ldo_set_mode ( 1 ) ;
msm_hsusb_config_vddcx ( 1 ) ;
writel ( readl ( USB_PHY_CTRL ) & ~ PHY_RETEN , USB_PHY_CTRL ) ;
}
2010-12-07 15:23:58 +03:00
temp = readl ( USB_USBCMD ) ;
temp & = ~ ASYNC_INTR_CTRL ;
temp & = ~ ULPI_STP_CTRL ;
writel ( temp , USB_USBCMD ) ;
/*
* PHY comes out of low power mode ( LPM ) in case of wakeup
* from asynchronous interrupt .
*/
if ( ! ( readl ( USB_PORTSC ) & PORTSC_PHCD ) )
goto skip_phy_resume ;
writel ( readl ( USB_PORTSC ) & ~ PORTSC_PHCD , USB_PORTSC ) ;
while ( cnt < PHY_RESUME_TIMEOUT_USEC ) {
if ( ! ( readl ( USB_PORTSC ) & PORTSC_PHCD ) )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = PHY_RESUME_TIMEOUT_USEC ) {
/*
* This is a fatal error . Reset the link and
* PHY . USB state can not be restored . Re - insertion
* of USB cable is the only way to get USB working .
*/
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " Unable to resume USB. "
2010-12-07 15:23:58 +03:00
" Re-plugin the cable \n " ) ;
2012-02-13 15:24:09 +04:00
msm_otg_reset ( phy ) ;
2010-12-07 15:23:58 +03:00
}
skip_phy_resume :
2012-02-13 15:24:09 +04:00
if ( device_may_wakeup ( phy - > dev ) )
2010-12-07 15:23:58 +03:00
disable_irq_wake ( motg - > irq ) ;
if ( bus )
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & ( bus_to_hcd ( bus ) ) - > flags ) ;
2011-05-02 10:26:33 +04:00
atomic_set ( & motg - > in_lpm , 0 ) ;
2010-12-07 15:23:58 +03:00
if ( motg - > async_int ) {
motg - > async_int = 0 ;
2012-02-13 15:24:09 +04:00
pm_runtime_put ( phy - > dev ) ;
2010-12-07 15:23:58 +03:00
enable_irq ( motg - > irq ) ;
}
2012-02-13 15:24:09 +04:00
dev_info ( phy - > dev , " USB exited from low power mode \n " ) ;
2010-12-07 15:23:58 +03:00
return 0 ;
}
2011-02-15 07:12:34 +03:00
# endif
2010-12-07 15:23:58 +03:00
2011-05-04 08:49:47 +04:00
static void msm_otg_notify_charger ( struct msm_otg * motg , unsigned mA )
{
if ( motg - > cur_power = = mA )
return ;
/* TODO: Notify PMIC about available current */
2012-02-13 15:24:09 +04:00
dev_info ( motg - > phy . dev , " Avail curr from USB = %u \n " , mA ) ;
2011-05-04 08:49:47 +04:00
motg - > cur_power = mA ;
}
2012-02-13 15:24:09 +04:00
static int msm_otg_set_power ( struct usb_phy * phy , unsigned mA )
2011-05-04 08:49:47 +04:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2011-05-04 08:49:47 +04:00
/*
* Gadget driver uses set_power method to notify about the
* available current based on suspend / configured states .
*
* IDEV_CHG can be drawn irrespective of suspend / un - configured
* states when CDP / ACA is connected .
*/
if ( motg - > chg_type = = USB_SDP_CHARGER )
msm_otg_notify_charger ( motg , mA ) ;
return 0 ;
}
2012-02-13 15:24:09 +04:00
static void msm_otg_start_host ( struct usb_phy * phy , int on )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
struct msm_otg_platform_data * pdata = motg - > pdata ;
struct usb_hcd * hcd ;
2012-02-13 15:24:09 +04:00
if ( ! phy - > otg - > host )
2010-12-07 15:23:55 +03:00
return ;
2012-02-13 15:24:09 +04:00
hcd = bus_to_hcd ( phy - > otg - > host ) ;
2010-12-07 15:23:55 +03:00
if ( on ) {
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " host on \n " ) ;
2010-12-07 15:23:55 +03:00
if ( pdata - > vbus_power )
pdata - > vbus_power ( 1 ) ;
/*
* Some boards have a switch cotrolled by gpio
* to enable / disable internal HUB . Enable internal
* HUB before kicking the host .
*/
if ( pdata - > setup_gpio )
pdata - > setup_gpio ( OTG_STATE_A_HOST ) ;
# ifdef CONFIG_USB
usb_add_hcd ( hcd , hcd - > irq , IRQF_SHARED ) ;
# endif
} else {
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " host off \n " ) ;
2010-12-07 15:23:55 +03:00
# ifdef CONFIG_USB
usb_remove_hcd ( hcd ) ;
# endif
if ( pdata - > setup_gpio )
pdata - > setup_gpio ( OTG_STATE_UNDEFINED ) ;
if ( pdata - > vbus_power )
pdata - > vbus_power ( 0 ) ;
}
}
2012-02-13 15:24:09 +04:00
static int msm_otg_set_host ( struct usb_otg * otg , struct usb_bus * host )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( otg - > phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
struct usb_hcd * hcd ;
/*
* Fail host registration if this board can support
* only peripheral configuration .
*/
if ( motg - > pdata - > mode = = USB_PERIPHERAL ) {
2012-02-13 15:24:09 +04:00
dev_info ( otg - > phy - > dev , " Host mode is not supported \n " ) ;
2010-12-07 15:23:55 +03:00
return - ENODEV ;
}
if ( ! host ) {
2012-02-13 15:24:09 +04:00
if ( otg - > phy - > state = = OTG_STATE_A_HOST ) {
pm_runtime_get_sync ( otg - > phy - > dev ) ;
msm_otg_start_host ( otg - > phy , 0 ) ;
2010-12-07 15:23:55 +03:00
otg - > host = NULL ;
2012-02-13 15:24:09 +04:00
otg - > phy - > state = OTG_STATE_UNDEFINED ;
2010-12-07 15:23:55 +03:00
schedule_work ( & motg - > sm_work ) ;
} else {
otg - > host = NULL ;
}
return 0 ;
}
hcd = bus_to_hcd ( host ) ;
hcd - > power_budget = motg - > pdata - > power_budget ;
otg - > host = host ;
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " host driver registered w/ tranceiver \n " ) ;
2010-12-07 15:23:55 +03:00
/*
* Kick the state machine work , if peripheral is not supported
* or peripheral is already registered with us .
*/
2010-12-07 15:23:58 +03:00
if ( motg - > pdata - > mode = = USB_HOST | | otg - > gadget ) {
2012-02-13 15:24:09 +04:00
pm_runtime_get_sync ( otg - > phy - > dev ) ;
2010-12-07 15:23:55 +03:00
schedule_work ( & motg - > sm_work ) ;
2010-12-07 15:23:58 +03:00
}
2010-12-07 15:23:55 +03:00
return 0 ;
}
2012-02-13 15:24:09 +04:00
static void msm_otg_start_peripheral ( struct usb_phy * phy , int on )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
struct msm_otg_platform_data * pdata = motg - > pdata ;
2012-02-13 15:24:09 +04:00
if ( ! phy - > otg - > gadget )
2010-12-07 15:23:55 +03:00
return ;
if ( on ) {
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " gadget on \n " ) ;
2010-12-07 15:23:55 +03:00
/*
* Some boards have a switch cotrolled by gpio
* to enable / disable internal HUB . Disable internal
* HUB before kicking the gadget .
*/
if ( pdata - > setup_gpio )
pdata - > setup_gpio ( OTG_STATE_B_PERIPHERAL ) ;
2012-02-13 15:24:09 +04:00
usb_gadget_vbus_connect ( phy - > otg - > gadget ) ;
2010-12-07 15:23:55 +03:00
} else {
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " gadget off \n " ) ;
usb_gadget_vbus_disconnect ( phy - > otg - > gadget ) ;
2010-12-07 15:23:55 +03:00
if ( pdata - > setup_gpio )
pdata - > setup_gpio ( OTG_STATE_UNDEFINED ) ;
}
}
2012-02-13 15:24:09 +04:00
static int msm_otg_set_peripheral ( struct usb_otg * otg ,
struct usb_gadget * gadget )
2010-12-07 15:23:55 +03:00
{
2012-02-13 15:24:09 +04:00
struct msm_otg * motg = container_of ( otg - > phy , struct msm_otg , phy ) ;
2010-12-07 15:23:55 +03:00
/*
* Fail peripheral registration if this board can support
* only host configuration .
*/
if ( motg - > pdata - > mode = = USB_HOST ) {
2012-02-13 15:24:09 +04:00
dev_info ( otg - > phy - > dev , " Peripheral mode is not supported \n " ) ;
2010-12-07 15:23:55 +03:00
return - ENODEV ;
}
if ( ! gadget ) {
2012-02-13 15:24:09 +04:00
if ( otg - > phy - > state = = OTG_STATE_B_PERIPHERAL ) {
pm_runtime_get_sync ( otg - > phy - > dev ) ;
msm_otg_start_peripheral ( otg - > phy , 0 ) ;
2010-12-07 15:23:55 +03:00
otg - > gadget = NULL ;
2012-02-13 15:24:09 +04:00
otg - > phy - > state = OTG_STATE_UNDEFINED ;
2010-12-07 15:23:55 +03:00
schedule_work ( & motg - > sm_work ) ;
} else {
otg - > gadget = NULL ;
}
return 0 ;
}
otg - > gadget = gadget ;
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " peripheral driver registered w/ tranceiver \n " ) ;
2010-12-07 15:23:55 +03:00
/*
* Kick the state machine work , if host is not supported
* or host is already registered with us .
*/
2010-12-07 15:23:58 +03:00
if ( motg - > pdata - > mode = = USB_PERIPHERAL | | otg - > host ) {
2012-02-13 15:24:09 +04:00
pm_runtime_get_sync ( otg - > phy - > dev ) ;
2010-12-07 15:23:55 +03:00
schedule_work ( & motg - > sm_work ) ;
2010-12-07 15:23:58 +03:00
}
2010-12-07 15:23:55 +03:00
return 0 ;
}
2011-05-04 08:49:47 +04:00
static bool msm_chg_check_secondary_det ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
ret = chg_det & ( 1 < < 4 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x87 ) ;
2011-05-04 08:49:47 +04:00
ret = chg_det & 1 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_enable_secondary_det ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* Turn off charger block */
chg_det | = ~ ( 1 < < 1 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
udelay ( 20 ) ;
/* control chg block via ULPI */
chg_det & = ~ ( 1 < < 3 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* put it in host mode for enabling D- source */
chg_det & = ~ ( 1 < < 2 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* Turn on chg detect block */
chg_det & = ~ ( 1 < < 1 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
udelay ( 20 ) ;
/* enable chg detection */
chg_det & = ~ ( 1 < < 0 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
break ;
case SNPS_28NM_INTEGRATED_PHY :
/*
* Configure DM as current source , DP as current sink
* and enable battery charging comparators .
*/
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x8 , 0x85 ) ;
ulpi_write ( phy , 0x2 , 0x85 ) ;
ulpi_write ( phy , 0x1 , 0x85 ) ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
}
static bool msm_chg_check_primary_det ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
ret = chg_det & ( 1 < < 4 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x87 ) ;
2011-05-04 08:49:47 +04:00
ret = chg_det & 1 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_enable_primary_det ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* enable chg detection */
chg_det & = ~ ( 1 < < 0 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
break ;
case SNPS_28NM_INTEGRATED_PHY :
/*
* Configure DP as current source , DM as current sink
* and enable battery charging comparators .
*/
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x2 , 0x85 ) ;
ulpi_write ( phy , 0x1 , 0x85 ) ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
}
static bool msm_chg_check_dcd ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 line_state ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
line_state = ulpi_read ( phy , 0x15 ) ;
2011-05-04 08:49:47 +04:00
ret = ! ( line_state & 1 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
line_state = ulpi_read ( phy , 0x87 ) ;
2011-05-04 08:49:47 +04:00
ret = line_state & 2 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_disable_dcd ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
chg_det & = ~ ( 1 < < 5 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x10 , 0x86 ) ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
}
static void msm_chg_enable_dcd ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* Turn on D+ current source */
chg_det | = ( 1 < < 5 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Data contact detection enable */
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x10 , 0x85 ) ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
}
static void msm_chg_block_on ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 func_ctrl , chg_det ;
/* put the controller in non-driving mode */
2012-02-13 15:24:09 +04:00
func_ctrl = ulpi_read ( phy , ULPI_FUNC_CTRL ) ;
2011-05-04 08:49:47 +04:00
func_ctrl & = ~ ULPI_FUNC_CTRL_OPMODE_MASK ;
func_ctrl | = ULPI_FUNC_CTRL_OPMODE_NONDRIVING ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , func_ctrl , ULPI_FUNC_CTRL ) ;
2011-05-04 08:49:47 +04:00
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* control chg block via ULPI */
chg_det & = ~ ( 1 < < 3 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* Turn on chg detect block */
chg_det & = ~ ( 1 < < 1 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
udelay ( 20 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Clear charger detecting control bits */
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x3F , 0x86 ) ;
2011-05-04 08:49:47 +04:00
/* Clear alt interrupt latch and enable bits */
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x1F , 0x92 ) ;
ulpi_write ( phy , 0x1F , 0x95 ) ;
2011-05-04 08:49:47 +04:00
udelay ( 100 ) ;
break ;
default :
break ;
}
}
static void msm_chg_block_off ( struct msm_otg * motg )
{
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
u32 func_ctrl , chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 15:24:09 +04:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 08:49:47 +04:00
/* Turn off charger block */
chg_det | = ~ ( 1 < < 1 ) ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 08:49:47 +04:00
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Clear charger detecting control bits */
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x3F , 0x86 ) ;
2011-05-04 08:49:47 +04:00
/* Clear alt interrupt latch and enable bits */
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , 0x1F , 0x92 ) ;
ulpi_write ( phy , 0x1F , 0x95 ) ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
/* put the controller in normal mode */
2012-02-13 15:24:09 +04:00
func_ctrl = ulpi_read ( phy , ULPI_FUNC_CTRL ) ;
2011-05-04 08:49:47 +04:00
func_ctrl & = ~ ULPI_FUNC_CTRL_OPMODE_MASK ;
func_ctrl | = ULPI_FUNC_CTRL_OPMODE_NORMAL ;
2012-02-13 15:24:09 +04:00
ulpi_write ( phy , func_ctrl , ULPI_FUNC_CTRL ) ;
2011-05-04 08:49:47 +04:00
}
# define MSM_CHG_DCD_POLL_TIME (100 * HZ / 1000) /* 100 msec */
# define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */
# define MSM_CHG_PRIMARY_DET_TIME (40 * HZ / 1000) /* TVDPSRC_ON */
# define MSM_CHG_SECONDARY_DET_TIME (40 * HZ / 1000) /* TVDMSRC_ON */
static void msm_chg_detect_work ( struct work_struct * w )
{
struct msm_otg * motg = container_of ( w , struct msm_otg , chg_work . work ) ;
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 08:49:47 +04:00
bool is_dcd , tmout , vout ;
unsigned long delay ;
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " chg detection work \n " ) ;
2011-05-04 08:49:47 +04:00
switch ( motg - > chg_state ) {
case USB_CHG_STATE_UNDEFINED :
2012-02-13 15:24:09 +04:00
pm_runtime_get_sync ( phy - > dev ) ;
2011-05-04 08:49:47 +04:00
msm_chg_block_on ( motg ) ;
msm_chg_enable_dcd ( motg ) ;
motg - > chg_state = USB_CHG_STATE_WAIT_FOR_DCD ;
motg - > dcd_retries = 0 ;
delay = MSM_CHG_DCD_POLL_TIME ;
break ;
case USB_CHG_STATE_WAIT_FOR_DCD :
is_dcd = msm_chg_check_dcd ( motg ) ;
tmout = + + motg - > dcd_retries = = MSM_CHG_DCD_MAX_RETRIES ;
if ( is_dcd | | tmout ) {
msm_chg_disable_dcd ( motg ) ;
msm_chg_enable_primary_det ( motg ) ;
delay = MSM_CHG_PRIMARY_DET_TIME ;
motg - > chg_state = USB_CHG_STATE_DCD_DONE ;
} else {
delay = MSM_CHG_DCD_POLL_TIME ;
}
break ;
case USB_CHG_STATE_DCD_DONE :
vout = msm_chg_check_primary_det ( motg ) ;
if ( vout ) {
msm_chg_enable_secondary_det ( motg ) ;
delay = MSM_CHG_SECONDARY_DET_TIME ;
motg - > chg_state = USB_CHG_STATE_PRIMARY_DONE ;
} else {
motg - > chg_type = USB_SDP_CHARGER ;
motg - > chg_state = USB_CHG_STATE_DETECTED ;
delay = 0 ;
}
break ;
case USB_CHG_STATE_PRIMARY_DONE :
vout = msm_chg_check_secondary_det ( motg ) ;
if ( vout )
motg - > chg_type = USB_DCP_CHARGER ;
else
motg - > chg_type = USB_CDP_CHARGER ;
motg - > chg_state = USB_CHG_STATE_SECONDARY_DONE ;
/* fall through */
case USB_CHG_STATE_SECONDARY_DONE :
motg - > chg_state = USB_CHG_STATE_DETECTED ;
case USB_CHG_STATE_DETECTED :
msm_chg_block_off ( motg ) ;
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " charger = %d \n " , motg - > chg_type ) ;
2011-05-04 08:49:47 +04:00
schedule_work ( & motg - > sm_work ) ;
return ;
default :
return ;
}
schedule_delayed_work ( & motg - > chg_work , delay ) ;
}
2010-12-07 15:23:55 +03:00
/*
* We support OTG , Peripheral only and Host only configurations . In case
* of OTG , mode switch ( host - - > peripheral / peripheral - - > host ) can happen
* via Id pin status or user request ( debugfs ) . Id / BSV interrupts are not
* enabled when switch is controlled by user and default mode is supplied
* by board file , which can be changed by userspace later .
*/
static void msm_otg_init_sm ( struct msm_otg * motg )
{
struct msm_otg_platform_data * pdata = motg - > pdata ;
u32 otgsc = readl ( USB_OTGSC ) ;
switch ( pdata - > mode ) {
case USB_OTG :
if ( pdata - > otg_control = = OTG_PHY_CONTROL ) {
if ( otgsc & OTGSC_ID )
set_bit ( ID , & motg - > inputs ) ;
else
clear_bit ( ID , & motg - > inputs ) ;
if ( otgsc & OTGSC_BSV )
set_bit ( B_SESS_VLD , & motg - > inputs ) ;
else
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
} else if ( pdata - > otg_control = = OTG_USER_CONTROL ) {
if ( pdata - > default_mode = = USB_HOST ) {
clear_bit ( ID , & motg - > inputs ) ;
} else if ( pdata - > default_mode = = USB_PERIPHERAL ) {
set_bit ( ID , & motg - > inputs ) ;
set_bit ( B_SESS_VLD , & motg - > inputs ) ;
} else {
set_bit ( ID , & motg - > inputs ) ;
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
}
}
break ;
case USB_HOST :
clear_bit ( ID , & motg - > inputs ) ;
break ;
case USB_PERIPHERAL :
set_bit ( ID , & motg - > inputs ) ;
if ( otgsc & OTGSC_BSV )
set_bit ( B_SESS_VLD , & motg - > inputs ) ;
else
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
break ;
default :
break ;
}
}
static void msm_otg_sm_work ( struct work_struct * w )
{
struct msm_otg * motg = container_of ( w , struct msm_otg , sm_work ) ;
2012-02-13 15:24:09 +04:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 15:23:55 +03:00
2012-02-13 15:24:09 +04:00
switch ( otg - > phy - > state ) {
2010-12-07 15:23:55 +03:00
case OTG_STATE_UNDEFINED :
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " OTG_STATE_UNDEFINED state \n " ) ;
msm_otg_reset ( otg - > phy ) ;
2010-12-07 15:23:55 +03:00
msm_otg_init_sm ( motg ) ;
2012-02-13 15:24:09 +04:00
otg - > phy - > state = OTG_STATE_B_IDLE ;
2010-12-07 15:23:55 +03:00
/* FALL THROUGH */
case OTG_STATE_B_IDLE :
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " OTG_STATE_B_IDLE state \n " ) ;
2010-12-07 15:23:55 +03:00
if ( ! test_bit ( ID , & motg - > inputs ) & & otg - > host ) {
/* disable BSV bit */
writel ( readl ( USB_OTGSC ) & ~ OTGSC_BSVIE , USB_OTGSC ) ;
2012-02-13 15:24:09 +04:00
msm_otg_start_host ( otg - > phy , 1 ) ;
otg - > phy - > state = OTG_STATE_A_HOST ;
2011-05-04 08:49:47 +04:00
} else if ( test_bit ( B_SESS_VLD , & motg - > inputs ) ) {
switch ( motg - > chg_state ) {
case USB_CHG_STATE_UNDEFINED :
msm_chg_detect_work ( & motg - > chg_work . work ) ;
break ;
case USB_CHG_STATE_DETECTED :
switch ( motg - > chg_type ) {
case USB_DCP_CHARGER :
msm_otg_notify_charger ( motg ,
IDEV_CHG_MAX ) ;
break ;
case USB_CDP_CHARGER :
msm_otg_notify_charger ( motg ,
IDEV_CHG_MAX ) ;
2012-02-13 15:24:09 +04:00
msm_otg_start_peripheral ( otg - > phy , 1 ) ;
otg - > phy - > state
= OTG_STATE_B_PERIPHERAL ;
2011-05-04 08:49:47 +04:00
break ;
case USB_SDP_CHARGER :
msm_otg_notify_charger ( motg , IUNIT ) ;
2012-02-13 15:24:09 +04:00
msm_otg_start_peripheral ( otg - > phy , 1 ) ;
otg - > phy - > state
= OTG_STATE_B_PERIPHERAL ;
2011-05-04 08:49:47 +04:00
break ;
default :
break ;
}
break ;
default :
break ;
}
} else {
/*
* If charger detection work is pending , decrement
* the pm usage counter to balance with the one that
* is incremented in charger detection work .
*/
if ( cancel_delayed_work_sync ( & motg - > chg_work ) ) {
2012-02-13 15:24:09 +04:00
pm_runtime_put_sync ( otg - > phy - > dev ) ;
msm_otg_reset ( otg - > phy ) ;
2011-05-04 08:49:47 +04:00
}
msm_otg_notify_charger ( motg , 0 ) ;
motg - > chg_state = USB_CHG_STATE_UNDEFINED ;
motg - > chg_type = USB_INVALID_CHARGER ;
2010-12-07 15:23:55 +03:00
}
2012-02-13 15:24:09 +04:00
pm_runtime_put_sync ( otg - > phy - > dev ) ;
2010-12-07 15:23:55 +03:00
break ;
case OTG_STATE_B_PERIPHERAL :
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " OTG_STATE_B_PERIPHERAL state \n " ) ;
2010-12-07 15:23:55 +03:00
if ( ! test_bit ( B_SESS_VLD , & motg - > inputs ) | |
! test_bit ( ID , & motg - > inputs ) ) {
2011-05-04 08:49:47 +04:00
msm_otg_notify_charger ( motg , 0 ) ;
2012-02-13 15:24:09 +04:00
msm_otg_start_peripheral ( otg - > phy , 0 ) ;
2011-05-04 08:49:47 +04:00
motg - > chg_state = USB_CHG_STATE_UNDEFINED ;
motg - > chg_type = USB_INVALID_CHARGER ;
2012-02-13 15:24:09 +04:00
otg - > phy - > state = OTG_STATE_B_IDLE ;
msm_otg_reset ( otg - > phy ) ;
2010-12-07 15:23:55 +03:00
schedule_work ( w ) ;
}
break ;
case OTG_STATE_A_HOST :
2012-02-13 15:24:09 +04:00
dev_dbg ( otg - > phy - > dev , " OTG_STATE_A_HOST state \n " ) ;
2010-12-07 15:23:55 +03:00
if ( test_bit ( ID , & motg - > inputs ) ) {
2012-02-13 15:24:09 +04:00
msm_otg_start_host ( otg - > phy , 0 ) ;
otg - > phy - > state = OTG_STATE_B_IDLE ;
msm_otg_reset ( otg - > phy ) ;
2010-12-07 15:23:55 +03:00
schedule_work ( w ) ;
}
break ;
default :
break ;
}
}
static irqreturn_t msm_otg_irq ( int irq , void * data )
{
struct msm_otg * motg = data ;
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2010-12-07 15:23:55 +03:00
u32 otgsc = 0 ;
2010-12-07 15:23:58 +03:00
if ( atomic_read ( & motg - > in_lpm ) ) {
disable_irq_nosync ( irq ) ;
motg - > async_int = 1 ;
2012-02-13 15:24:09 +04:00
pm_runtime_get ( phy - > dev ) ;
2010-12-07 15:23:58 +03:00
return IRQ_HANDLED ;
}
2010-12-07 15:23:55 +03:00
otgsc = readl ( USB_OTGSC ) ;
if ( ! ( otgsc & ( OTGSC_IDIS | OTGSC_BSVIS ) ) )
return IRQ_NONE ;
if ( ( otgsc & OTGSC_IDIS ) & & ( otgsc & OTGSC_IDIE ) ) {
if ( otgsc & OTGSC_ID )
set_bit ( ID , & motg - > inputs ) ;
else
clear_bit ( ID , & motg - > inputs ) ;
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " ID set/clear \n " ) ;
pm_runtime_get_noresume ( phy - > dev ) ;
2010-12-07 15:23:55 +03:00
} else if ( ( otgsc & OTGSC_BSVIS ) & & ( otgsc & OTGSC_BSVIE ) ) {
if ( otgsc & OTGSC_BSV )
set_bit ( B_SESS_VLD , & motg - > inputs ) ;
else
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
2012-02-13 15:24:09 +04:00
dev_dbg ( phy - > dev , " BSV set/clear \n " ) ;
pm_runtime_get_noresume ( phy - > dev ) ;
2010-12-07 15:23:55 +03:00
}
writel ( otgsc , USB_OTGSC ) ;
schedule_work ( & motg - > sm_work ) ;
return IRQ_HANDLED ;
}
static int msm_otg_mode_show ( struct seq_file * s , void * unused )
{
struct msm_otg * motg = s - > private ;
2012-02-13 15:24:09 +04:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 15:23:55 +03:00
2012-02-13 15:24:09 +04:00
switch ( otg - > phy - > state ) {
2010-12-07 15:23:55 +03:00
case OTG_STATE_A_HOST :
seq_printf ( s , " host \n " ) ;
break ;
case OTG_STATE_B_PERIPHERAL :
seq_printf ( s , " peripheral \n " ) ;
break ;
default :
seq_printf ( s , " none \n " ) ;
break ;
}
return 0 ;
}
static int msm_otg_mode_open ( struct inode * inode , struct file * file )
{
return single_open ( file , msm_otg_mode_show , inode - > i_private ) ;
}
static ssize_t msm_otg_mode_write ( struct file * file , const char __user * ubuf ,
size_t count , loff_t * ppos )
{
2011-02-15 07:12:35 +03:00
struct seq_file * s = file - > private_data ;
struct msm_otg * motg = s - > private ;
2010-12-07 15:23:55 +03:00
char buf [ 16 ] ;
2012-02-13 15:24:09 +04:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 15:23:55 +03:00
int status = count ;
enum usb_mode_type req_mode ;
memset ( buf , 0x00 , sizeof ( buf ) ) ;
if ( copy_from_user ( & buf , ubuf , min_t ( size_t , sizeof ( buf ) - 1 , count ) ) ) {
status = - EFAULT ;
goto out ;
}
if ( ! strncmp ( buf , " host " , 4 ) ) {
req_mode = USB_HOST ;
} else if ( ! strncmp ( buf , " peripheral " , 10 ) ) {
req_mode = USB_PERIPHERAL ;
} else if ( ! strncmp ( buf , " none " , 4 ) ) {
req_mode = USB_NONE ;
} else {
status = - EINVAL ;
goto out ;
}
switch ( req_mode ) {
case USB_NONE :
2012-02-13 15:24:09 +04:00
switch ( otg - > phy - > state ) {
2010-12-07 15:23:55 +03:00
case OTG_STATE_A_HOST :
case OTG_STATE_B_PERIPHERAL :
set_bit ( ID , & motg - > inputs ) ;
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
break ;
default :
goto out ;
}
break ;
case USB_PERIPHERAL :
2012-02-13 15:24:09 +04:00
switch ( otg - > phy - > state ) {
2010-12-07 15:23:55 +03:00
case OTG_STATE_B_IDLE :
case OTG_STATE_A_HOST :
set_bit ( ID , & motg - > inputs ) ;
set_bit ( B_SESS_VLD , & motg - > inputs ) ;
break ;
default :
goto out ;
}
break ;
case USB_HOST :
2012-02-13 15:24:09 +04:00
switch ( otg - > phy - > state ) {
2010-12-07 15:23:55 +03:00
case OTG_STATE_B_IDLE :
case OTG_STATE_B_PERIPHERAL :
clear_bit ( ID , & motg - > inputs ) ;
break ;
default :
goto out ;
}
break ;
default :
goto out ;
}
2012-02-13 15:24:09 +04:00
pm_runtime_get_sync ( otg - > phy - > dev ) ;
2010-12-07 15:23:55 +03:00
schedule_work ( & motg - > sm_work ) ;
out :
return status ;
}
const struct file_operations msm_otg_mode_fops = {
. open = msm_otg_mode_open ,
. read = seq_read ,
. write = msm_otg_mode_write ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static struct dentry * msm_otg_dbg_root ;
static struct dentry * msm_otg_dbg_mode ;
static int msm_otg_debugfs_init ( struct msm_otg * motg )
{
msm_otg_dbg_root = debugfs_create_dir ( " msm_otg " , NULL ) ;
if ( ! msm_otg_dbg_root | | IS_ERR ( msm_otg_dbg_root ) )
return - ENODEV ;
msm_otg_dbg_mode = debugfs_create_file ( " mode " , S_IRUGO | S_IWUSR ,
msm_otg_dbg_root , motg , & msm_otg_mode_fops ) ;
if ( ! msm_otg_dbg_mode ) {
debugfs_remove ( msm_otg_dbg_root ) ;
msm_otg_dbg_root = NULL ;
return - ENODEV ;
}
return 0 ;
}
static void msm_otg_debugfs_cleanup ( void )
{
debugfs_remove ( msm_otg_dbg_mode ) ;
debugfs_remove ( msm_otg_dbg_root ) ;
}
static int __init msm_otg_probe ( struct platform_device * pdev )
{
int ret = 0 ;
struct resource * res ;
struct msm_otg * motg ;
2012-02-13 15:24:09 +04:00
struct usb_phy * phy ;
2010-12-07 15:23:55 +03:00
dev_info ( & pdev - > dev , " msm_otg probe \n " ) ;
2013-07-30 12:02:13 +04:00
if ( ! dev_get_platdata ( & pdev - > dev ) ) {
2010-12-07 15:23:55 +03:00
dev_err ( & pdev - > dev , " No platform data given. Bailing out \n " ) ;
return - ENODEV ;
}
motg = kzalloc ( sizeof ( struct msm_otg ) , GFP_KERNEL ) ;
if ( ! motg ) {
dev_err ( & pdev - > dev , " unable to allocate msm_otg \n " ) ;
return - ENOMEM ;
}
2012-02-13 15:24:09 +04:00
motg - > phy . otg = kzalloc ( sizeof ( struct usb_otg ) , GFP_KERNEL ) ;
if ( ! motg - > phy . otg ) {
dev_err ( & pdev - > dev , " unable to allocate msm_otg \n " ) ;
return - ENOMEM ;
}
2013-07-30 12:02:13 +04:00
motg - > pdata = dev_get_platdata ( & pdev - > dev ) ;
2012-02-13 15:24:09 +04:00
phy = & motg - > phy ;
phy - > dev = & pdev - > dev ;
2010-12-07 15:23:55 +03:00
motg - > phy_reset_clk = clk_get ( & pdev - > dev , " usb_phy_clk " ) ;
if ( IS_ERR ( motg - > phy_reset_clk ) ) {
dev_err ( & pdev - > dev , " failed to get usb_phy_clk \n " ) ;
ret = PTR_ERR ( motg - > phy_reset_clk ) ;
goto free_motg ;
}
motg - > clk = clk_get ( & pdev - > dev , " usb_hs_clk " ) ;
if ( IS_ERR ( motg - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get usb_hs_clk \n " ) ;
ret = PTR_ERR ( motg - > clk ) ;
goto put_phy_reset_clk ;
}
2011-05-04 08:49:46 +04:00
clk_set_rate ( motg - > clk , 60000000 ) ;
/*
* If USB Core is running its protocol engine based on CORE CLK ,
* CORE CLK must be running at > 55 Mhz for correct HSUSB
* operation and USB core cannot tolerate frequency changes on
* CORE CLK . For such USB cores , vote for maximum clk frequency
* on pclk source
*/
if ( motg - > pdata - > pclk_src_name ) {
motg - > pclk_src = clk_get ( & pdev - > dev ,
motg - > pdata - > pclk_src_name ) ;
if ( IS_ERR ( motg - > pclk_src ) )
goto put_clk ;
clk_set_rate ( motg - > pclk_src , INT_MAX ) ;
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > pclk_src ) ;
2011-05-04 08:49:46 +04:00
} else
motg - > pclk_src = ERR_PTR ( - ENOENT ) ;
2010-12-07 15:23:55 +03:00
motg - > pclk = clk_get ( & pdev - > dev , " usb_hs_pclk " ) ;
if ( IS_ERR ( motg - > pclk ) ) {
dev_err ( & pdev - > dev , " failed to get usb_hs_pclk \n " ) ;
ret = PTR_ERR ( motg - > pclk ) ;
2011-05-04 08:49:46 +04:00
goto put_pclk_src ;
2010-12-07 15:23:55 +03:00
}
/*
* USB core clock is not present on all MSM chips . This
* clock is introduced to remove the dependency on AXI
* bus frequency .
*/
motg - > core_clk = clk_get ( & pdev - > dev , " usb_hs_core_clk " ) ;
if ( IS_ERR ( motg - > core_clk ) )
motg - > core_clk = NULL ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to get platform resource mem \n " ) ;
ret = - ENODEV ;
goto put_core_clk ;
}
motg - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! motg - > regs ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
goto put_core_clk ;
}
dev_info ( & pdev - > dev , " OTG regs = %p \n " , motg - > regs ) ;
motg - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ! motg - > irq ) {
dev_err ( & pdev - > dev , " platform_get_irq failed \n " ) ;
ret = - ENODEV ;
goto free_regs ;
}
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > clk ) ;
clk_prepare_enable ( motg - > pclk ) ;
2011-05-04 08:49:48 +04:00
ret = msm_hsusb_init_vddcx ( motg , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vddcx configuration failed \n " ) ;
goto free_regs ;
}
ret = msm_hsusb_ldo_init ( motg , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vreg configuration failed \n " ) ;
goto vddcx_exit ;
}
ret = msm_hsusb_ldo_set_mode ( 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vreg enable failed \n " ) ;
goto ldo_exit ;
}
2010-12-07 15:23:55 +03:00
if ( motg - > core_clk )
2013-06-17 21:43:10 +04:00
clk_prepare_enable ( motg - > core_clk ) ;
2010-12-07 15:23:55 +03:00
writel ( 0 , USB_USBINTR ) ;
writel ( 0 , USB_OTGSC ) ;
INIT_WORK ( & motg - > sm_work , msm_otg_sm_work ) ;
2011-05-04 08:49:47 +04:00
INIT_DELAYED_WORK ( & motg - > chg_work , msm_chg_detect_work ) ;
2010-12-07 15:23:55 +03:00
ret = request_irq ( motg - > irq , msm_otg_irq , IRQF_SHARED ,
" msm_otg " , motg ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " request irq failed \n " ) ;
goto disable_clks ;
}
2012-02-13 15:24:09 +04:00
phy - > init = msm_otg_reset ;
phy - > set_power = msm_otg_set_power ;
phy - > io_ops = & msm_otg_io_ops ;
2010-12-07 15:23:55 +03:00
2012-02-13 15:24:09 +04:00
phy - > otg - > phy = & motg - > phy ;
phy - > otg - > set_host = msm_otg_set_host ;
phy - > otg - > set_peripheral = msm_otg_set_peripheral ;
2010-12-07 15:23:55 +03:00
2012-06-22 15:32:46 +04:00
ret = usb_add_phy ( & motg - > phy , USB_PHY_TYPE_USB2 ) ;
2010-12-07 15:23:55 +03:00
if ( ret ) {
2012-06-22 15:32:45 +04:00
dev_err ( & pdev - > dev , " usb_add_phy failed \n " ) ;
2010-12-07 15:23:55 +03:00
goto free_irq ;
}
platform_set_drvdata ( pdev , motg ) ;
device_init_wakeup ( & pdev - > dev , 1 ) ;
if ( motg - > pdata - > mode = = USB_OTG & &
motg - > pdata - > otg_control = = OTG_USER_CONTROL ) {
ret = msm_otg_debugfs_init ( motg ) ;
if ( ret )
dev_dbg ( & pdev - > dev , " mode debugfs file is "
" not available \n " ) ;
}
2010-12-07 15:23:58 +03:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2010-12-07 15:23:55 +03:00
2010-12-07 15:23:58 +03:00
return 0 ;
2010-12-07 15:23:55 +03:00
free_irq :
free_irq ( motg - > irq , motg ) ;
disable_clks :
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2011-05-04 08:49:48 +04:00
ldo_exit :
msm_hsusb_ldo_init ( motg , 0 ) ;
vddcx_exit :
msm_hsusb_init_vddcx ( motg , 0 ) ;
2010-12-07 15:23:55 +03:00
free_regs :
iounmap ( motg - > regs ) ;
put_core_clk :
if ( motg - > core_clk )
clk_put ( motg - > core_clk ) ;
clk_put ( motg - > pclk ) ;
2011-05-04 08:49:46 +04:00
put_pclk_src :
if ( ! IS_ERR ( motg - > pclk_src ) ) {
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk_src ) ;
2011-05-04 08:49:46 +04:00
clk_put ( motg - > pclk_src ) ;
}
2010-12-07 15:23:55 +03:00
put_clk :
clk_put ( motg - > clk ) ;
put_phy_reset_clk :
clk_put ( motg - > phy_reset_clk ) ;
free_motg :
2012-02-13 15:24:09 +04:00
kfree ( motg - > phy . otg ) ;
2010-12-07 15:23:55 +03:00
kfree ( motg ) ;
return ret ;
}
2012-11-19 22:26:20 +04:00
static int msm_otg_remove ( struct platform_device * pdev )
2010-12-07 15:23:55 +03:00
{
struct msm_otg * motg = platform_get_drvdata ( pdev ) ;
2012-02-13 15:24:09 +04:00
struct usb_phy * phy = & motg - > phy ;
2010-12-07 15:23:58 +03:00
int cnt = 0 ;
2010-12-07 15:23:55 +03:00
2012-02-13 15:24:09 +04:00
if ( phy - > otg - > host | | phy - > otg - > gadget )
2010-12-07 15:23:55 +03:00
return - EBUSY ;
msm_otg_debugfs_cleanup ( ) ;
2011-05-04 08:49:47 +04:00
cancel_delayed_work_sync ( & motg - > chg_work ) ;
2010-12-07 15:23:55 +03:00
cancel_work_sync ( & motg - > sm_work ) ;
2010-12-07 15:23:58 +03:00
2011-02-15 07:12:34 +03:00
pm_runtime_resume ( & pdev - > dev ) ;
2010-12-07 15:23:58 +03:00
2010-12-07 15:23:55 +03:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
2010-12-07 15:23:58 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2010-12-07 15:23:55 +03:00
2012-06-22 15:32:46 +04:00
usb_remove_phy ( phy ) ;
2010-12-07 15:23:55 +03:00
free_irq ( motg - > irq , motg ) ;
2010-12-07 15:23:58 +03:00
/*
* Put PHY in low power mode .
*/
2012-02-13 15:24:09 +04:00
ulpi_read ( phy , 0x14 ) ;
ulpi_write ( phy , 0x08 , 0x09 ) ;
2010-12-07 15:23:58 +03:00
writel ( readl ( USB_PORTSC ) | PORTSC_PHCD , USB_PORTSC ) ;
while ( cnt < PHY_SUSPEND_TIMEOUT_USEC ) {
if ( readl ( USB_PORTSC ) & PORTSC_PHCD )
break ;
udelay ( 1 ) ;
cnt + + ;
}
if ( cnt > = PHY_SUSPEND_TIMEOUT_USEC )
2012-02-13 15:24:09 +04:00
dev_err ( phy - > dev , " Unable to suspend PHY \n " ) ;
2010-12-07 15:23:58 +03:00
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2010-12-07 15:23:55 +03:00
if ( motg - > core_clk )
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > core_clk ) ;
2011-05-04 08:49:46 +04:00
if ( ! IS_ERR ( motg - > pclk_src ) ) {
2013-06-17 21:43:10 +04:00
clk_disable_unprepare ( motg - > pclk_src ) ;
2011-05-04 08:49:46 +04:00
clk_put ( motg - > pclk_src ) ;
}
2011-05-04 08:49:48 +04:00
msm_hsusb_ldo_init ( motg , 0 ) ;
2010-12-07 15:23:55 +03:00
iounmap ( motg - > regs ) ;
2010-12-07 15:23:58 +03:00
pm_runtime_set_suspended ( & pdev - > dev ) ;
2010-12-07 15:23:55 +03:00
clk_put ( motg - > phy_reset_clk ) ;
clk_put ( motg - > pclk ) ;
clk_put ( motg - > clk ) ;
if ( motg - > core_clk )
clk_put ( motg - > core_clk ) ;
2012-02-13 15:24:09 +04:00
kfree ( motg - > phy . otg ) ;
2010-12-07 15:23:55 +03:00
kfree ( motg ) ;
return 0 ;
}
2010-12-07 15:23:58 +03:00
# ifdef CONFIG_PM_RUNTIME
static int msm_otg_runtime_idle ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
2012-02-13 15:24:09 +04:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 15:23:58 +03:00
dev_dbg ( dev , " OTG runtime idle \n " ) ;
/*
* It is observed some times that a spurious interrupt
* comes when PHY is put into LPM immediately after PHY reset .
* This 1 sec delay also prevents entering into LPM immediately
* after asynchronous interrupt .
*/
2012-02-13 15:24:09 +04:00
if ( otg - > phy - > state ! = OTG_STATE_UNDEFINED )
2010-12-07 15:23:58 +03:00
pm_schedule_suspend ( dev , 1000 ) ;
return - EAGAIN ;
}
static int msm_otg_runtime_suspend ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " OTG runtime suspend \n " ) ;
return msm_otg_suspend ( motg ) ;
}
static int msm_otg_runtime_resume ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " OTG runtime resume \n " ) ;
return msm_otg_resume ( motg ) ;
}
# endif
2011-02-15 07:12:34 +03:00
# ifdef CONFIG_PM_SLEEP
2010-12-07 15:23:58 +03:00
static int msm_otg_pm_suspend ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " OTG PM suspend \n " ) ;
return msm_otg_suspend ( motg ) ;
}
static int msm_otg_pm_resume ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
int ret ;
dev_dbg ( dev , " OTG PM resume \n " ) ;
ret = msm_otg_resume ( motg ) ;
if ( ret )
return ret ;
/*
* Runtime PM Documentation recommends bringing the
* device to full powered state upon resume .
*/
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
}
# endif
2011-02-15 07:12:34 +03:00
# ifdef CONFIG_PM
2010-12-07 15:23:58 +03:00
static const struct dev_pm_ops msm_otg_dev_pm_ops = {
2011-02-15 07:12:34 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( msm_otg_pm_suspend , msm_otg_pm_resume )
SET_RUNTIME_PM_OPS ( msm_otg_runtime_suspend , msm_otg_runtime_resume ,
msm_otg_runtime_idle )
2010-12-07 15:23:58 +03:00
} ;
2011-02-15 07:12:34 +03:00
# endif
2010-12-07 15:23:58 +03:00
2010-12-07 15:23:55 +03:00
static struct platform_driver msm_otg_driver = {
2012-11-19 22:21:08 +04:00
. remove = msm_otg_remove ,
2010-12-07 15:23:55 +03:00
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
2011-02-15 07:12:34 +03:00
# ifdef CONFIG_PM
2010-12-07 15:23:58 +03:00
. pm = & msm_otg_dev_pm_ops ,
2011-02-15 07:12:34 +03:00
# endif
2010-12-07 15:23:55 +03:00
} ,
} ;
2013-01-09 15:15:28 +04:00
module_platform_driver_probe ( msm_otg_driver , msm_otg_probe ) ;
2010-12-07 15:23:55 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MSM USB transceiver driver " ) ;