2011-05-04 10:19:47 +05:30
/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
2010-12-07 17:53:55 +05:30
*
* 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 17:53:58 +05:30
# include <linux/pm_runtime.h>
2014-04-28 16:34:15 +03:00
# include <linux/of.h>
# include <linux/of_device.h>
2014-04-28 16:34:16 +03:00
# include <linux/reset.h>
2010-12-07 17:53:55 +05:30
# include <linux/usb.h>
# include <linux/usb/otg.h>
2014-04-28 16:34:15 +03:00
# include <linux/usb/of.h>
2010-12-07 17:53:55 +05:30
# 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 10:19:48 +05:30
# include <linux/regulator/consumer.h>
2010-12-07 17:53:55 +05:30
# define MSM_USB_BASE (motg->regs)
# define DRIVER_NAME "msm_otg"
# define ULPI_IO_TIMEOUT_USEC (10 * 1000)
2014-04-28 16:34:18 +03:00
# define LINK_RESET_TIMEOUT_USEC (250 * 1000)
2011-05-04 10:19:48 +05:30
# 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 */
2014-04-28 16:34:22 +03:00
# define USB_PHY_SUSP_DIG_VOL 500000 /* uV */
enum vdd_levels {
VDD_LEVEL_NONE = 0 ,
VDD_LEVEL_MIN ,
VDD_LEVEL_MAX ,
} ;
2011-05-04 10:19:48 +05:30
static int msm_hsusb_init_vddcx ( struct msm_otg * motg , int init )
{
int ret = 0 ;
if ( init ) {
2014-04-28 16:34:06 +03:00
ret = regulator_set_voltage ( motg - > vddcx ,
2014-04-28 16:34:22 +03:00
motg - > vdd_levels [ VDD_LEVEL_MIN ] ,
motg - > vdd_levels [ VDD_LEVEL_MAX ] ) ;
2011-05-04 10:19:48 +05:30
if ( ret ) {
2014-04-28 16:34:10 +03:00
dev_err ( motg - > phy . dev , " Cannot set vddcx voltage \n " ) ;
2011-05-04 10:19:48 +05:30
return ret ;
}
2014-04-28 16:34:06 +03:00
ret = regulator_enable ( motg - > vddcx ) ;
2014-04-28 16:34:08 +03:00
if ( ret )
2012-02-13 13:24:09 +02:00
dev_err ( motg - > phy . dev , " unable to enable hsusb vddcx \n " ) ;
2011-05-04 10:19:48 +05:30
} else {
2014-04-28 16:34:06 +03:00
ret = regulator_set_voltage ( motg - > vddcx , 0 ,
2014-04-28 16:34:22 +03:00
motg - > vdd_levels [ VDD_LEVEL_MAX ] ) ;
2011-05-15 09:55:58 -07:00
if ( ret )
2014-04-28 16:34:10 +03:00
dev_err ( motg - > phy . dev , " Cannot set vddcx voltage \n " ) ;
2014-04-28 16:34:06 +03:00
ret = regulator_disable ( motg - > vddcx ) ;
2011-05-04 10:19:48 +05:30
if ( ret )
2012-02-13 13:24:09 +02:00
dev_err ( motg - > phy . dev , " unable to disable hsusb vddcx \n " ) ;
2011-05-04 10:19:48 +05:30
}
return ret ;
}
static int msm_hsusb_ldo_init ( struct msm_otg * motg , int init )
{
int rc = 0 ;
if ( init ) {
2014-04-28 16:34:06 +03:00
rc = regulator_set_voltage ( motg - > v3p3 , USB_PHY_3P3_VOL_MIN ,
2011-05-04 10:19:48 +05:30
USB_PHY_3P3_VOL_MAX ) ;
if ( rc ) {
2014-04-28 16:34:10 +03:00
dev_err ( motg - > phy . dev , " Cannot set v3p3 voltage \n " ) ;
2014-04-28 16:34:08 +03:00
goto exit ;
2011-05-04 10:19:48 +05:30
}
2014-04-28 16:34:06 +03:00
rc = regulator_enable ( motg - > v3p3 ) ;
2011-05-04 10:19:48 +05:30
if ( rc ) {
2012-02-13 13:24:09 +02:00
dev_err ( motg - > phy . dev , " unable to enable the hsusb 3p3 \n " ) ;
2014-04-28 16:34:08 +03:00
goto exit ;
2011-05-04 10:19:48 +05:30
}
2014-04-28 16:34:06 +03:00
rc = regulator_set_voltage ( motg - > v1p8 , USB_PHY_1P8_VOL_MIN ,
2011-05-04 10:19:48 +05:30
USB_PHY_1P8_VOL_MAX ) ;
if ( rc ) {
2014-04-28 16:34:10 +03:00
dev_err ( motg - > phy . dev , " Cannot set v1p8 voltage \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_3p3 ;
2011-05-04 10:19:48 +05:30
}
2014-04-28 16:34:06 +03:00
rc = regulator_enable ( motg - > v1p8 ) ;
2011-05-04 10:19:48 +05:30
if ( rc ) {
2012-02-13 13:24:09 +02:00
dev_err ( motg - > phy . dev , " unable to enable the hsusb 1p8 \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_3p3 ;
2011-05-04 10:19:48 +05:30
}
return 0 ;
}
2014-04-28 16:34:06 +03:00
regulator_disable ( motg - > v1p8 ) ;
2011-05-04 10:19:48 +05:30
disable_3p3 :
2014-04-28 16:34:06 +03:00
regulator_disable ( motg - > v3p3 ) ;
2014-04-28 16:34:08 +03:00
exit :
2011-05-04 10:19:48 +05:30
return rc ;
}
2014-04-28 16:34:06 +03:00
static int msm_hsusb_ldo_set_mode ( struct msm_otg * motg , int on )
2011-05-04 10:19:48 +05:30
{
int ret = 0 ;
if ( on ) {
2015-02-11 19:35:30 -08:00
ret = regulator_set_load ( motg - > v1p8 , USB_PHY_1P8_HPM_LOAD ) ;
2011-05-04 10:19:48 +05:30
if ( ret < 0 ) {
2014-04-28 16:34:10 +03:00
pr_err ( " Could not set HPM for v1p8 \n " ) ;
2011-05-04 10:19:48 +05:30
return ret ;
}
2015-02-11 19:35:30 -08:00
ret = regulator_set_load ( motg - > v3p3 , USB_PHY_3P3_HPM_LOAD ) ;
2011-05-04 10:19:48 +05:30
if ( ret < 0 ) {
2014-04-28 16:34:10 +03:00
pr_err ( " Could not set HPM for v3p3 \n " ) ;
2015-02-11 19:35:30 -08:00
regulator_set_load ( motg - > v1p8 , USB_PHY_1P8_LPM_LOAD ) ;
2011-05-04 10:19:48 +05:30
return ret ;
}
} else {
2015-02-11 19:35:30 -08:00
ret = regulator_set_load ( motg - > v1p8 , USB_PHY_1P8_LPM_LOAD ) ;
2011-05-04 10:19:48 +05:30
if ( ret < 0 )
2014-04-28 16:34:10 +03:00
pr_err ( " Could not set LPM for v1p8 \n " ) ;
2015-02-11 19:35:30 -08:00
ret = regulator_set_load ( motg - > v3p3 , USB_PHY_3P3_LPM_LOAD ) ;
2011-05-04 10:19:48 +05:30
if ( ret < 0 )
2014-04-28 16:34:10 +03:00
pr_err ( " Could not set LPM for v3p3 \n " ) ;
2011-05-04 10:19:48 +05:30
}
pr_debug ( " reg (%s) \n " , on ? " HPM " : " LPM " ) ;
return ret < 0 ? ret : 0 ;
}
2012-02-13 13:24:09 +02:00
static int ulpi_read ( struct usb_phy * phy , u32 reg )
2010-12-07 17:53:55 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
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 13:24:09 +02:00
dev_err ( phy - > dev , " ulpi_read: timeout %08x \n " ,
2010-12-07 17:53:55 +05:30
readl ( USB_ULPI_VIEWPORT ) ) ;
return - ETIMEDOUT ;
}
return ULPI_DATA_READ ( readl ( USB_ULPI_VIEWPORT ) ) ;
}
2012-02-13 13:24:09 +02:00
static int ulpi_write ( struct usb_phy * phy , u32 val , u32 reg )
2010-12-07 17:53:55 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
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 13:24:09 +02:00
dev_err ( phy - > dev , " ulpi_write: timeout \n " ) ;
2010-12-07 17:53:55 +05:30
return - ETIMEDOUT ;
}
return 0 ;
}
2012-02-13 13:24:09 +02:00
static struct usb_phy_io_ops msm_otg_io_ops = {
2010-12-07 17:53:55 +05:30
. read = ulpi_read ,
. write = ulpi_write ,
} ;
static void ulpi_init ( struct msm_otg * motg )
{
struct msm_otg_platform_data * pdata = motg - > pdata ;
2014-04-28 16:34:15 +03:00
int * seq = pdata - > phy_init_seq , idx ;
u32 addr = ULPI_EXT_VENDOR_SPECIFIC ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:15 +03:00
for ( idx = 0 ; idx < pdata - > phy_init_sz ; idx + + ) {
if ( seq [ idx ] = = - 1 )
continue ;
2010-12-07 17:53:55 +05:30
2012-02-13 13:24:09 +02:00
dev_vdbg ( motg - > phy . dev , " ulpi: write 0x%02x to 0x%02x \n " ,
2014-04-28 16:34:15 +03:00
seq [ idx ] , addr + idx ) ;
ulpi_write ( & motg - > phy , seq [ idx ] , addr + idx ) ;
2010-12-07 17:53:55 +05:30
}
}
2014-04-28 16:34:21 +03:00
static int msm_phy_notify_disconnect ( struct usb_phy * phy ,
enum usb_device_speed speed )
{
int val ;
/*
* Put the transceiver in non - driving mode . Otherwise host
* may not detect soft - disconnection .
*/
val = ulpi_read ( phy , ULPI_FUNC_CTRL ) ;
val & = ~ ULPI_FUNC_CTRL_OPMODE_MASK ;
val | = ULPI_FUNC_CTRL_OPMODE_NONDRIVING ;
ulpi_write ( phy , val , ULPI_FUNC_CTRL ) ;
return 0 ;
}
2010-12-07 17:53:55 +05:30
static int msm_otg_link_clk_reset ( struct msm_otg * motg , bool assert )
{
2014-04-28 16:34:16 +03:00
int ret ;
2013-12-30 13:15:27 -08:00
2015-03-13 11:09:42 -07:00
if ( assert )
2014-04-28 16:34:16 +03:00
ret = reset_control_assert ( motg - > link_rst ) ;
else
ret = reset_control_deassert ( motg - > link_rst ) ;
2013-12-30 13:15:27 -08:00
if ( ret )
dev_err ( motg - > phy . dev , " usb link clk reset %s failed \n " ,
assert ? " assert " : " deassert " ) ;
2010-12-07 17:53:55 +05:30
return ret ;
}
static int msm_otg_phy_clk_reset ( struct msm_otg * motg )
{
2014-06-30 18:29:49 +01:00
int ret = 0 ;
2010-12-07 17:53:55 +05:30
2015-03-13 11:09:42 -07:00
if ( motg - > phy_rst )
2014-04-28 16:34:16 +03:00
ret = reset_control_reset ( motg - > phy_rst ) ;
2013-12-30 13:15:27 -08:00
2010-12-07 17:53:55 +05:30
if ( ret )
2013-12-30 13:15:27 -08:00
dev_err ( motg - > phy . dev , " usb phy clk reset failed \n " ) ;
2010-12-07 17:53:55 +05:30
return ret ;
}
2014-04-28 16:34:18 +03:00
static int msm_link_reset ( struct msm_otg * motg )
2010-12-07 17:53:55 +05:30
{
u32 val ;
int ret ;
ret = msm_otg_link_clk_reset ( motg , 1 ) ;
if ( ret )
return ret ;
2014-04-28 16:34:18 +03:00
/* wait for 1ms delay as suggested in HPG. */
usleep_range ( 1000 , 1200 ) ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:18 +03:00
ret = msm_otg_link_clk_reset ( motg , 0 ) ;
2010-12-07 17:53:55 +05:30
if ( ret )
return ret ;
2014-04-28 16:34:17 +03:00
if ( motg - > phy_number )
writel ( readl ( USB_PHY_CTRL2 ) | BIT ( 16 ) , USB_PHY_CTRL2 ) ;
2014-04-28 16:34:19 +03:00
/* put transceiver in serial mode as part of reset */
2014-04-28 16:34:18 +03:00
val = readl ( USB_PORTSC ) & ~ PORTSC_PTS_MASK ;
2014-04-28 16:34:19 +03:00
writel ( val | PORTSC_PTS_SERIAL , USB_PORTSC ) ;
2014-04-28 16:34:18 +03:00
2010-12-07 17:53:55 +05:30
return 0 ;
}
2012-02-13 13:24:09 +02:00
static int msm_otg_reset ( struct usb_phy * phy )
2010-12-07 17:53:55 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
int cnt = 0 ;
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 ;
2014-04-28 16:34:19 +03:00
/* select ULPI phy and clear other status/control bits in PORTSC */
writel ( PORTSC_PTS_ULPI , USB_PORTSC ) ;
2014-04-28 16:34:18 +03:00
writel ( 0x0 , USB_AHBBURST ) ;
writel ( 0x08 , USB_AHBMODE ) ;
if ( motg - > phy_number )
writel ( readl ( USB_PHY_CTRL2 ) | BIT ( 16 ) , USB_PHY_CTRL2 ) ;
return 0 ;
}
static void msm_phy_reset ( struct msm_otg * motg )
{
void __iomem * addr ;
if ( motg - > pdata - > phy_type ! = SNPS_28NM_INTEGRATED_PHY ) {
msm_otg_phy_clk_reset ( motg ) ;
return ;
}
addr = USB_PHY_CTRL ;
if ( motg - > phy_number )
addr = USB_PHY_CTRL2 ;
/* Assert USB PHY_POR */
writel ( readl ( addr ) | PHY_POR_ASSERT , addr ) ;
/*
* wait for minimum 10 microseconds as suggested in HPG .
* Use a slightly larger value since the exact value didn ' t
* work 100 % of the time .
*/
udelay ( 12 ) ;
/* Deassert USB PHY_POR */
writel ( readl ( addr ) & ~ PHY_POR_ASSERT , addr ) ;
}
static int msm_usb_reset ( struct usb_phy * phy )
{
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
int ret ;
if ( ! IS_ERR ( motg - > core_clk ) )
clk_prepare_enable ( motg - > core_clk ) ;
ret = msm_link_reset ( motg ) ;
if ( ret ) {
dev_err ( phy - > dev , " phy_reset failed \n " ) ;
return ret ;
}
ret = msm_otg_reset ( & motg - > phy ) ;
if ( ret ) {
dev_err ( phy - > dev , " link reset failed \n " ) ;
return ret ;
}
2010-12-07 17:53:55 +05:30
msleep ( 100 ) ;
2014-04-28 16:34:18 +03:00
/* Reset USB PHY after performing USB Link RESET */
msm_phy_reset ( motg ) ;
if ( ! IS_ERR ( motg - > core_clk ) )
clk_disable_unprepare ( motg - > core_clk ) ;
return 0 ;
}
static int msm_phy_init ( struct usb_phy * phy )
{
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
struct msm_otg_platform_data * pdata = motg - > pdata ;
u32 val , ulpi_val = 0 ;
/* Program USB PHY Override registers. */
ulpi_init ( motg ) ;
/*
* It is recommended in HPG to reset USB PHY after programming
* USB PHY Override registers .
*/
msm_phy_reset ( motg ) ;
2010-12-07 17:53:55 +05:30
if ( pdata - > otg_control = = OTG_PHY_CONTROL ) {
val = readl ( USB_OTGSC ) ;
2014-04-28 16:34:11 +03:00
if ( pdata - > mode = = USB_DR_MODE_OTG ) {
2010-12-07 17:53:55 +05:30
ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID ;
val | = OTGSC_IDIE | OTGSC_BSVIE ;
2014-04-28 16:34:11 +03:00
} else if ( pdata - > mode = = USB_DR_MODE_PERIPHERAL ) {
2010-12-07 17:53:55 +05:30
ulpi_val = ULPI_INT_SESS_VALID ;
val | = OTGSC_BSVIE ;
}
writel ( val , USB_OTGSC ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , ulpi_val , ULPI_USB_INT_EN_RISE ) ;
ulpi_write ( phy , ulpi_val , ULPI_USB_INT_EN_FALL ) ;
2010-12-07 17:53:55 +05:30
}
2014-04-28 16:34:17 +03:00
if ( motg - > phy_number )
writel ( readl ( USB_PHY_CTRL2 ) | BIT ( 16 ) , USB_PHY_CTRL2 ) ;
2010-12-07 17:53:55 +05:30
return 0 ;
}
2010-12-07 17:53:58 +05:30
# define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
2011-02-15 09:42:34 +05:30
# define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
2014-02-18 10:36:29 -06:00
# ifdef CONFIG_PM
2014-04-28 16:34:06 +03:00
static int msm_hsusb_config_vddcx ( struct msm_otg * motg , int high )
2014-02-18 10:36:29 -06:00
{
2014-04-28 16:34:22 +03:00
int max_vol = motg - > vdd_levels [ VDD_LEVEL_MAX ] ;
2014-02-18 10:36:29 -06:00
int min_vol ;
int ret ;
if ( high )
2014-04-28 16:34:22 +03:00
min_vol = motg - > vdd_levels [ VDD_LEVEL_MIN ] ;
2014-02-18 10:36:29 -06:00
else
2014-04-28 16:34:22 +03:00
min_vol = motg - > vdd_levels [ VDD_LEVEL_NONE ] ;
2014-02-18 10:36:29 -06:00
2014-04-28 16:34:06 +03:00
ret = regulator_set_voltage ( motg - > vddcx , min_vol , max_vol ) ;
2014-02-18 10:36:29 -06:00
if ( ret ) {
2014-04-28 16:34:10 +03:00
pr_err ( " Cannot set vddcx voltage \n " ) ;
2014-02-18 10:36:29 -06:00
return ret ;
}
pr_debug ( " %s: min_vol:%d max_vol:%d \n " , __func__ , min_vol , max_vol ) ;
return ret ;
}
2010-12-07 17:53:58 +05:30
static int msm_otg_suspend ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
struct usb_bus * bus = phy - > otg - > host ;
2010-12-07 17:53:58 +05:30
struct msm_otg_platform_data * pdata = motg - > pdata ;
2014-04-28 16:34:17 +03:00
void __iomem * addr ;
2010-12-07 17:53:58 +05:30
int cnt = 0 ;
if ( atomic_read ( & motg - > in_lpm ) )
return 0 ;
disable_irq ( motg - > irq ) ;
/*
2011-05-04 10:19:49 +05:30
* Chipidea 45 - nm PHY suspend sequence :
*
2010-12-07 17:53:58 +05:30
* 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 10:19:49 +05:30
*
2010-12-07 17:53:58 +05:30
* 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 10:19:49 +05:30
*
2010-12-07 17:53:58 +05:30
* PLL is not turned off when PHY enters into low power mode ( LPM ) .
* Disable PLL for maximum power savings .
*/
2011-05-04 10:19:49 +05:30
if ( motg - > pdata - > phy_type = = CI_45NM_INTEGRATED_PHY ) {
2012-02-13 13:24:09 +02:00
ulpi_read ( phy , 0x14 ) ;
2011-05-04 10:19:49 +05:30
if ( pdata - > otg_control = = OTG_PHY_CONTROL )
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x01 , 0x30 ) ;
ulpi_write ( phy , 0x08 , 0x09 ) ;
2011-05-04 10:19:49 +05:30
}
2010-12-07 17:53:58 +05:30
/*
* 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 13:24:09 +02:00
dev_err ( phy - > dev , " Unable to suspend PHY \n " ) ;
msm_otg_reset ( phy ) ;
2010-12-07 17:53:58 +05:30
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 ) ;
2014-04-28 16:34:17 +03:00
addr = USB_PHY_CTRL ;
if ( motg - > phy_number )
addr = USB_PHY_CTRL2 ;
2011-05-04 10:19:49 +05:30
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL )
2014-04-28 16:34:17 +03:00
writel ( readl ( addr ) | PHY_RETEN , addr ) ;
2011-05-04 10:19:49 +05:30
2013-06-17 10:43:10 -07:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2014-04-28 16:34:08 +03:00
if ( ! IS_ERR ( motg - > core_clk ) )
2013-06-17 10:43:10 -07:00
clk_disable_unprepare ( motg - > core_clk ) ;
2010-12-07 17:53:58 +05:30
2011-05-04 10:19:49 +05:30
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL ) {
2014-04-28 16:34:06 +03:00
msm_hsusb_ldo_set_mode ( motg , 0 ) ;
msm_hsusb_config_vddcx ( motg , 0 ) ;
2011-05-04 10:19:49 +05:30
}
2012-02-13 13:24:09 +02:00
if ( device_may_wakeup ( phy - > dev ) )
2010-12-07 17:53:58 +05:30
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 13:24:09 +02:00
dev_info ( phy - > dev , " USB in low power mode \n " ) ;
2010-12-07 17:53:58 +05:30
return 0 ;
}
static int msm_otg_resume ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
struct usb_bus * bus = phy - > otg - > host ;
2014-04-28 16:34:17 +03:00
void __iomem * addr ;
2010-12-07 17:53:58 +05:30
int cnt = 0 ;
unsigned temp ;
if ( ! atomic_read ( & motg - > in_lpm ) )
return 0 ;
2013-06-17 10:43:10 -07:00
clk_prepare_enable ( motg - > pclk ) ;
clk_prepare_enable ( motg - > clk ) ;
2014-04-28 16:34:08 +03:00
if ( ! IS_ERR ( motg - > core_clk ) )
2013-06-17 10:43:10 -07:00
clk_prepare_enable ( motg - > core_clk ) ;
2010-12-07 17:53:58 +05:30
2011-05-04 10:19:49 +05:30
if ( motg - > pdata - > phy_type = = SNPS_28NM_INTEGRATED_PHY & &
motg - > pdata - > otg_control = = OTG_PMIC_CONTROL ) {
2014-04-28 16:34:17 +03:00
addr = USB_PHY_CTRL ;
if ( motg - > phy_number )
addr = USB_PHY_CTRL2 ;
2014-04-28 16:34:06 +03:00
msm_hsusb_ldo_set_mode ( motg , 1 ) ;
msm_hsusb_config_vddcx ( motg , 1 ) ;
2014-04-28 16:34:17 +03:00
writel ( readl ( addr ) & ~ PHY_RETEN , addr ) ;
2011-05-04 10:19:49 +05:30
}
2010-12-07 17:53:58 +05:30
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 .
*/
2014-04-28 16:34:10 +03:00
dev_err ( phy - > dev , " Unable to resume USB. Re-plugin the cable \n " ) ;
2012-02-13 13:24:09 +02:00
msm_otg_reset ( phy ) ;
2010-12-07 17:53:58 +05:30
}
skip_phy_resume :
2012-02-13 13:24:09 +02:00
if ( device_may_wakeup ( phy - > dev ) )
2010-12-07 17:53:58 +05:30
disable_irq_wake ( motg - > irq ) ;
if ( bus )
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & ( bus_to_hcd ( bus ) ) - > flags ) ;
2011-05-02 11:56:33 +05:30
atomic_set ( & motg - > in_lpm , 0 ) ;
2010-12-07 17:53:58 +05:30
if ( motg - > async_int ) {
motg - > async_int = 0 ;
2012-02-13 13:24:09 +02:00
pm_runtime_put ( phy - > dev ) ;
2010-12-07 17:53:58 +05:30
enable_irq ( motg - > irq ) ;
}
2012-02-13 13:24:09 +02:00
dev_info ( phy - > dev , " USB exited from low power mode \n " ) ;
2010-12-07 17:53:58 +05:30
return 0 ;
}
2011-02-15 09:42:34 +05:30
# endif
2010-12-07 17:53:58 +05:30
2011-05-04 10:19:47 +05:30
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 13:24:09 +02:00
dev_info ( motg - > phy . dev , " Avail curr from USB = %u \n " , mA ) ;
2011-05-04 10:19:47 +05:30
motg - > cur_power = mA ;
}
2012-02-13 13:24:09 +02:00
static int msm_otg_set_power ( struct usb_phy * phy , unsigned mA )
2011-05-04 10:19:47 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2011-05-04 10:19:47 +05:30
/*
* 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 13:24:09 +02:00
static void msm_otg_start_host ( struct usb_phy * phy , int on )
2010-12-07 17:53:55 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
struct msm_otg_platform_data * pdata = motg - > pdata ;
struct usb_hcd * hcd ;
2012-02-13 13:24:09 +02:00
if ( ! phy - > otg - > host )
2010-12-07 17:53:55 +05:30
return ;
2012-02-13 13:24:09 +02:00
hcd = bus_to_hcd ( phy - > otg - > host ) ;
2010-12-07 17:53:55 +05:30
if ( on ) {
2012-02-13 13:24:09 +02:00
dev_dbg ( phy - > dev , " host on \n " ) ;
2010-12-07 17:53:55 +05:30
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 ) ;
2013-11-05 10:46:02 +08:00
device_wakeup_enable ( hcd - > self . controller ) ;
2010-12-07 17:53:55 +05:30
# endif
} else {
2012-02-13 13:24:09 +02:00
dev_dbg ( phy - > dev , " host off \n " ) ;
2010-12-07 17:53:55 +05:30
# 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 13:24:09 +02:00
static int msm_otg_set_host ( struct usb_otg * otg , struct usb_bus * host )
2010-12-07 17:53:55 +05:30
{
2014-10-30 18:41:14 +01:00
struct msm_otg * motg = container_of ( otg - > usb_phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
struct usb_hcd * hcd ;
/*
* Fail host registration if this board can support
* only peripheral configuration .
*/
2014-04-28 16:34:11 +03:00
if ( motg - > pdata - > mode = = USB_DR_MODE_PERIPHERAL ) {
2014-10-30 18:41:14 +01:00
dev_info ( otg - > usb_phy - > dev , " Host mode is not supported \n " ) ;
2010-12-07 17:53:55 +05:30
return - ENODEV ;
}
if ( ! host ) {
2014-10-30 18:41:13 +01:00
if ( otg - > state = = OTG_STATE_A_HOST ) {
2014-10-30 18:41:14 +01:00
pm_runtime_get_sync ( otg - > usb_phy - > dev ) ;
msm_otg_start_host ( otg - > usb_phy , 0 ) ;
2010-12-07 17:53:55 +05:30
otg - > host = NULL ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_UNDEFINED ;
2010-12-07 17:53:55 +05:30
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 ;
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev , " host driver registered w/ tranceiver \n " ) ;
2010-12-07 17:53:55 +05:30
/*
* Kick the state machine work , if peripheral is not supported
* or peripheral is already registered with us .
*/
2014-04-28 16:34:11 +03:00
if ( motg - > pdata - > mode = = USB_DR_MODE_HOST | | otg - > gadget ) {
2014-10-30 18:41:14 +01:00
pm_runtime_get_sync ( otg - > usb_phy - > dev ) ;
2010-12-07 17:53:55 +05:30
schedule_work ( & motg - > sm_work ) ;
2010-12-07 17:53:58 +05:30
}
2010-12-07 17:53:55 +05:30
return 0 ;
}
2012-02-13 13:24:09 +02:00
static void msm_otg_start_peripheral ( struct usb_phy * phy , int on )
2010-12-07 17:53:55 +05:30
{
2012-02-13 13:24:09 +02:00
struct msm_otg * motg = container_of ( phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
struct msm_otg_platform_data * pdata = motg - > pdata ;
2012-02-13 13:24:09 +02:00
if ( ! phy - > otg - > gadget )
2010-12-07 17:53:55 +05:30
return ;
if ( on ) {
2012-02-13 13:24:09 +02:00
dev_dbg ( phy - > dev , " gadget on \n " ) ;
2010-12-07 17:53:55 +05:30
/*
* 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 13:24:09 +02:00
usb_gadget_vbus_connect ( phy - > otg - > gadget ) ;
2010-12-07 17:53:55 +05:30
} else {
2012-02-13 13:24:09 +02:00
dev_dbg ( phy - > dev , " gadget off \n " ) ;
usb_gadget_vbus_disconnect ( phy - > otg - > gadget ) ;
2010-12-07 17:53:55 +05:30
if ( pdata - > setup_gpio )
pdata - > setup_gpio ( OTG_STATE_UNDEFINED ) ;
}
}
2012-02-13 13:24:09 +02:00
static int msm_otg_set_peripheral ( struct usb_otg * otg ,
struct usb_gadget * gadget )
2010-12-07 17:53:55 +05:30
{
2014-10-30 18:41:14 +01:00
struct msm_otg * motg = container_of ( otg - > usb_phy , struct msm_otg , phy ) ;
2010-12-07 17:53:55 +05:30
/*
* Fail peripheral registration if this board can support
* only host configuration .
*/
2014-04-28 16:34:11 +03:00
if ( motg - > pdata - > mode = = USB_DR_MODE_HOST ) {
2014-10-30 18:41:14 +01:00
dev_info ( otg - > usb_phy - > dev , " Peripheral mode is not supported \n " ) ;
2010-12-07 17:53:55 +05:30
return - ENODEV ;
}
if ( ! gadget ) {
2014-10-30 18:41:13 +01:00
if ( otg - > state = = OTG_STATE_B_PERIPHERAL ) {
2014-10-30 18:41:14 +01:00
pm_runtime_get_sync ( otg - > usb_phy - > dev ) ;
msm_otg_start_peripheral ( otg - > usb_phy , 0 ) ;
2010-12-07 17:53:55 +05:30
otg - > gadget = NULL ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_UNDEFINED ;
2010-12-07 17:53:55 +05:30
schedule_work ( & motg - > sm_work ) ;
} else {
otg - > gadget = NULL ;
}
return 0 ;
}
otg - > gadget = gadget ;
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev ,
" peripheral driver registered w/ tranceiver \n " ) ;
2010-12-07 17:53:55 +05:30
/*
* Kick the state machine work , if host is not supported
* or host is already registered with us .
*/
2014-04-28 16:34:11 +03:00
if ( motg - > pdata - > mode = = USB_DR_MODE_PERIPHERAL | | otg - > host ) {
2014-10-30 18:41:14 +01:00
pm_runtime_get_sync ( otg - > usb_phy - > dev ) ;
2010-12-07 17:53:55 +05:30
schedule_work ( & motg - > sm_work ) ;
2010-12-07 17:53:58 +05:30
}
2010-12-07 17:53:55 +05:30
return 0 ;
}
2011-05-04 10:19:47 +05:30
static bool msm_chg_check_secondary_det ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
ret = chg_det & ( 1 < < 4 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x87 ) ;
2011-05-04 10:19:47 +05:30
ret = chg_det & 1 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_enable_secondary_det ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* Turn off charger block */
chg_det | = ~ ( 1 < < 1 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
udelay ( 20 ) ;
/* control chg block via ULPI */
chg_det & = ~ ( 1 < < 3 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* put it in host mode for enabling D- source */
chg_det & = ~ ( 1 < < 2 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* Turn on chg detect block */
chg_det & = ~ ( 1 < < 1 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
udelay ( 20 ) ;
/* enable chg detection */
chg_det & = ~ ( 1 < < 0 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
break ;
case SNPS_28NM_INTEGRATED_PHY :
/*
* Configure DM as current source , DP as current sink
* and enable battery charging comparators .
*/
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x8 , 0x85 ) ;
ulpi_write ( phy , 0x2 , 0x85 ) ;
ulpi_write ( phy , 0x1 , 0x85 ) ;
2011-05-04 10:19:47 +05:30
break ;
default :
break ;
}
}
static bool msm_chg_check_primary_det ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
ret = chg_det & ( 1 < < 4 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x87 ) ;
2011-05-04 10:19:47 +05:30
ret = chg_det & 1 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_enable_primary_det ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* enable chg detection */
chg_det & = ~ ( 1 < < 0 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
break ;
case SNPS_28NM_INTEGRATED_PHY :
/*
* Configure DP as current source , DM as current sink
* and enable battery charging comparators .
*/
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x2 , 0x85 ) ;
ulpi_write ( phy , 0x1 , 0x85 ) ;
2011-05-04 10:19:47 +05:30
break ;
default :
break ;
}
}
static bool msm_chg_check_dcd ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 line_state ;
bool ret = false ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
line_state = ulpi_read ( phy , 0x15 ) ;
2011-05-04 10:19:47 +05:30
ret = ! ( line_state & 1 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
line_state = ulpi_read ( phy , 0x87 ) ;
2011-05-04 10:19:47 +05:30
ret = line_state & 2 ;
break ;
default :
break ;
}
return ret ;
}
static void msm_chg_disable_dcd ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
chg_det & = ~ ( 1 < < 5 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
break ;
case SNPS_28NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x10 , 0x86 ) ;
2011-05-04 10:19:47 +05:30
break ;
default :
break ;
}
}
static void msm_chg_enable_dcd ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* Turn on D+ current source */
chg_det | = ( 1 < < 5 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Data contact detection enable */
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x10 , 0x85 ) ;
2011-05-04 10:19:47 +05:30
break ;
default :
break ;
}
}
static void msm_chg_block_on ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 func_ctrl , chg_det ;
/* put the controller in non-driving mode */
2012-02-13 13:24:09 +02:00
func_ctrl = ulpi_read ( phy , ULPI_FUNC_CTRL ) ;
2011-05-04 10:19:47 +05:30
func_ctrl & = ~ ULPI_FUNC_CTRL_OPMODE_MASK ;
func_ctrl | = ULPI_FUNC_CTRL_OPMODE_NONDRIVING ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , func_ctrl , ULPI_FUNC_CTRL ) ;
2011-05-04 10:19:47 +05:30
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* control chg block via ULPI */
chg_det & = ~ ( 1 < < 3 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* Turn on chg detect block */
chg_det & = ~ ( 1 < < 1 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
udelay ( 20 ) ;
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Clear charger detecting control bits */
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x3F , 0x86 ) ;
2011-05-04 10:19:47 +05:30
/* Clear alt interrupt latch and enable bits */
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x1F , 0x92 ) ;
ulpi_write ( phy , 0x1F , 0x95 ) ;
2011-05-04 10:19:47 +05:30
udelay ( 100 ) ;
break ;
default :
break ;
}
}
static void msm_chg_block_off ( struct msm_otg * motg )
{
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
u32 func_ctrl , chg_det ;
switch ( motg - > pdata - > phy_type ) {
case CI_45NM_INTEGRATED_PHY :
2012-02-13 13:24:09 +02:00
chg_det = ulpi_read ( phy , 0x34 ) ;
2011-05-04 10:19:47 +05:30
/* Turn off charger block */
chg_det | = ~ ( 1 < < 1 ) ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , chg_det , 0x34 ) ;
2011-05-04 10:19:47 +05:30
break ;
case SNPS_28NM_INTEGRATED_PHY :
/* Clear charger detecting control bits */
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x3F , 0x86 ) ;
2011-05-04 10:19:47 +05:30
/* Clear alt interrupt latch and enable bits */
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , 0x1F , 0x92 ) ;
ulpi_write ( phy , 0x1F , 0x95 ) ;
2011-05-04 10:19:47 +05:30
break ;
default :
break ;
}
/* put the controller in normal mode */
2012-02-13 13:24:09 +02:00
func_ctrl = ulpi_read ( phy , ULPI_FUNC_CTRL ) ;
2011-05-04 10:19:47 +05:30
func_ctrl & = ~ ULPI_FUNC_CTRL_OPMODE_MASK ;
func_ctrl | = ULPI_FUNC_CTRL_OPMODE_NORMAL ;
2012-02-13 13:24:09 +02:00
ulpi_write ( phy , func_ctrl , ULPI_FUNC_CTRL ) ;
2011-05-04 10:19:47 +05:30
}
# 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 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2011-05-04 10:19:47 +05:30
bool is_dcd , tmout , vout ;
unsigned long delay ;
2012-02-13 13:24:09 +02:00
dev_dbg ( phy - > dev , " chg detection work \n " ) ;
2011-05-04 10:19:47 +05:30
switch ( motg - > chg_state ) {
case USB_CHG_STATE_UNDEFINED :
2012-02-13 13:24:09 +02:00
pm_runtime_get_sync ( phy - > dev ) ;
2011-05-04 10:19:47 +05:30
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 13:24:09 +02:00
dev_dbg ( phy - > dev , " charger = %d \n " , motg - > chg_type ) ;
2011-05-04 10:19:47 +05:30
schedule_work ( & motg - > sm_work ) ;
return ;
default :
return ;
}
schedule_delayed_work ( & motg - > chg_work , delay ) ;
}
2010-12-07 17:53:55 +05:30
/*
* 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 ) {
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_OTG :
2010-12-07 17:53:55 +05:30
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 ) {
set_bit ( ID , & motg - > inputs ) ;
clear_bit ( B_SESS_VLD , & motg - > inputs ) ;
}
break ;
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_HOST :
2010-12-07 17:53:55 +05:30
clear_bit ( ID , & motg - > inputs ) ;
break ;
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_PERIPHERAL :
2010-12-07 17:53:55 +05:30
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 13:24:09 +02:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 17:53:55 +05:30
2014-10-30 18:41:13 +01:00
switch ( otg - > state ) {
2010-12-07 17:53:55 +05:30
case OTG_STATE_UNDEFINED :
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev , " OTG_STATE_UNDEFINED state \n " ) ;
msm_otg_reset ( otg - > usb_phy ) ;
2010-12-07 17:53:55 +05:30
msm_otg_init_sm ( motg ) ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_B_IDLE ;
2010-12-07 17:53:55 +05:30
/* FALL THROUGH */
case OTG_STATE_B_IDLE :
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev , " OTG_STATE_B_IDLE state \n " ) ;
2010-12-07 17:53:55 +05:30
if ( ! test_bit ( ID , & motg - > inputs ) & & otg - > host ) {
/* disable BSV bit */
writel ( readl ( USB_OTGSC ) & ~ OTGSC_BSVIE , USB_OTGSC ) ;
2014-10-30 18:41:14 +01:00
msm_otg_start_host ( otg - > usb_phy , 1 ) ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_A_HOST ;
2011-05-04 10:19:47 +05:30
} 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 ) ;
2014-10-30 18:41:14 +01:00
msm_otg_start_peripheral ( otg - > usb_phy ,
1 ) ;
2014-10-30 18:41:13 +01:00
otg - > state
2012-02-13 13:24:09 +02:00
= OTG_STATE_B_PERIPHERAL ;
2011-05-04 10:19:47 +05:30
break ;
case USB_SDP_CHARGER :
msm_otg_notify_charger ( motg , IUNIT ) ;
2014-10-30 18:41:14 +01:00
msm_otg_start_peripheral ( otg - > usb_phy ,
1 ) ;
2014-10-30 18:41:13 +01:00
otg - > state
2012-02-13 13:24:09 +02:00
= OTG_STATE_B_PERIPHERAL ;
2011-05-04 10:19:47 +05:30
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 ) ) {
2014-10-30 18:41:14 +01:00
pm_runtime_put_sync ( otg - > usb_phy - > dev ) ;
msm_otg_reset ( otg - > usb_phy ) ;
2011-05-04 10:19:47 +05:30
}
msm_otg_notify_charger ( motg , 0 ) ;
motg - > chg_state = USB_CHG_STATE_UNDEFINED ;
motg - > chg_type = USB_INVALID_CHARGER ;
2010-12-07 17:53:55 +05:30
}
2014-06-30 18:29:57 +01:00
2014-10-30 18:41:13 +01:00
if ( otg - > state = = OTG_STATE_B_IDLE )
2014-10-30 18:41:14 +01:00
pm_runtime_put_sync ( otg - > usb_phy - > dev ) ;
2010-12-07 17:53:55 +05:30
break ;
case OTG_STATE_B_PERIPHERAL :
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev , " OTG_STATE_B_PERIPHERAL state \n " ) ;
2010-12-07 17:53:55 +05:30
if ( ! test_bit ( B_SESS_VLD , & motg - > inputs ) | |
! test_bit ( ID , & motg - > inputs ) ) {
2011-05-04 10:19:47 +05:30
msm_otg_notify_charger ( motg , 0 ) ;
2014-10-30 18:41:14 +01:00
msm_otg_start_peripheral ( otg - > usb_phy , 0 ) ;
2011-05-04 10:19:47 +05:30
motg - > chg_state = USB_CHG_STATE_UNDEFINED ;
motg - > chg_type = USB_INVALID_CHARGER ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_B_IDLE ;
2014-10-30 18:41:14 +01:00
msm_otg_reset ( otg - > usb_phy ) ;
2010-12-07 17:53:55 +05:30
schedule_work ( w ) ;
}
break ;
case OTG_STATE_A_HOST :
2014-10-30 18:41:14 +01:00
dev_dbg ( otg - > usb_phy - > dev , " OTG_STATE_A_HOST state \n " ) ;
2010-12-07 17:53:55 +05:30
if ( test_bit ( ID , & motg - > inputs ) ) {
2014-10-30 18:41:14 +01:00
msm_otg_start_host ( otg - > usb_phy , 0 ) ;
2014-10-30 18:41:13 +01:00
otg - > state = OTG_STATE_B_IDLE ;
2014-10-30 18:41:14 +01:00
msm_otg_reset ( otg - > usb_phy ) ;
2010-12-07 17:53:55 +05:30
schedule_work ( w ) ;
}
break ;
default :
break ;
}
}
static irqreturn_t msm_otg_irq ( int irq , void * data )
{
struct msm_otg * motg = data ;
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2010-12-07 17:53:55 +05:30
u32 otgsc = 0 ;
2010-12-07 17:53:58 +05:30
if ( atomic_read ( & motg - > in_lpm ) ) {
disable_irq_nosync ( irq ) ;
motg - > async_int = 1 ;
2012-02-13 13:24:09 +02:00
pm_runtime_get ( phy - > dev ) ;
2010-12-07 17:53:58 +05:30
return IRQ_HANDLED ;
}
2010-12-07 17:53:55 +05:30
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 13:24:09 +02:00
dev_dbg ( phy - > dev , " ID set/clear \n " ) ;
pm_runtime_get_noresume ( phy - > dev ) ;
2010-12-07 17:53:55 +05:30
} 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 13:24:09 +02:00
dev_dbg ( phy - > dev , " BSV set/clear \n " ) ;
pm_runtime_get_noresume ( phy - > dev ) ;
2010-12-07 17:53:55 +05:30
}
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 13:24:09 +02:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 17:53:55 +05:30
2014-10-30 18:41:13 +01:00
switch ( otg - > state ) {
2010-12-07 17:53:55 +05:30
case OTG_STATE_A_HOST :
2014-04-28 16:34:10 +03:00
seq_puts ( s , " host \n " ) ;
2010-12-07 17:53:55 +05:30
break ;
case OTG_STATE_B_PERIPHERAL :
2014-04-28 16:34:10 +03:00
seq_puts ( s , " peripheral \n " ) ;
2010-12-07 17:53:55 +05:30
break ;
default :
2014-04-28 16:34:10 +03:00
seq_puts ( s , " none \n " ) ;
2010-12-07 17:53:55 +05:30
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 09:42:35 +05:30
struct seq_file * s = file - > private_data ;
struct msm_otg * motg = s - > private ;
2010-12-07 17:53:55 +05:30
char buf [ 16 ] ;
2012-02-13 13:24:09 +02:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 17:53:55 +05:30
int status = count ;
2014-04-28 16:34:11 +03:00
enum usb_dr_mode req_mode ;
2010-12-07 17:53:55 +05:30
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 ) ) {
2014-04-28 16:34:11 +03:00
req_mode = USB_DR_MODE_HOST ;
2010-12-07 17:53:55 +05:30
} else if ( ! strncmp ( buf , " peripheral " , 10 ) ) {
2014-04-28 16:34:11 +03:00
req_mode = USB_DR_MODE_PERIPHERAL ;
2010-12-07 17:53:55 +05:30
} else if ( ! strncmp ( buf , " none " , 4 ) ) {
2014-04-28 16:34:11 +03:00
req_mode = USB_DR_MODE_UNKNOWN ;
2010-12-07 17:53:55 +05:30
} else {
status = - EINVAL ;
goto out ;
}
switch ( req_mode ) {
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_UNKNOWN :
2014-10-30 18:41:13 +01:00
switch ( otg - > state ) {
2010-12-07 17:53:55 +05:30
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 ;
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_PERIPHERAL :
2014-10-30 18:41:13 +01:00
switch ( otg - > state ) {
2010-12-07 17:53:55 +05:30
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 ;
2014-04-28 16:34:11 +03:00
case USB_DR_MODE_HOST :
2014-10-30 18:41:13 +01:00
switch ( otg - > state ) {
2010-12-07 17:53:55 +05:30
case OTG_STATE_B_IDLE :
case OTG_STATE_B_PERIPHERAL :
clear_bit ( ID , & motg - > inputs ) ;
break ;
default :
goto out ;
}
break ;
default :
goto out ;
}
2014-10-30 18:41:14 +01:00
pm_runtime_get_sync ( otg - > usb_phy - > dev ) ;
2010-12-07 17:53:55 +05:30
schedule_work ( & motg - > sm_work ) ;
out :
return status ;
}
2014-08-20 13:38:18 -05:00
static const struct file_operations msm_otg_mode_fops = {
2010-12-07 17:53:55 +05:30
. 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 ) ;
}
2014-06-18 13:42:44 +09:00
static const struct of_device_id msm_otg_dt_match [ ] = {
2014-04-28 16:34:15 +03:00
{
. compatible = " qcom,usb-otg-ci " ,
. data = ( void * ) CI_45NM_INTEGRATED_PHY
} ,
{
. compatible = " qcom,usb-otg-snps " ,
. data = ( void * ) SNPS_28NM_INTEGRATED_PHY
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , msm_otg_dt_match ) ;
static int msm_otg_read_dt ( struct platform_device * pdev , struct msm_otg * motg )
{
struct msm_otg_platform_data * pdata ;
const struct of_device_id * id ;
struct device_node * node = pdev - > dev . of_node ;
struct property * prop ;
int len , ret , words ;
2014-04-28 16:34:22 +03:00
u32 val , tmp [ 3 ] ;
2014-04-28 16:34:15 +03:00
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
motg - > pdata = pdata ;
id = of_match_device ( msm_otg_dt_match , & pdev - > dev ) ;
2014-04-30 11:33:04 -05:00
pdata - > phy_type = ( enum msm_usb_phy_type ) id - > data ;
2014-04-28 16:34:15 +03:00
2014-04-28 16:34:16 +03:00
motg - > link_rst = devm_reset_control_get ( & pdev - > dev , " link " ) ;
if ( IS_ERR ( motg - > link_rst ) )
return PTR_ERR ( motg - > link_rst ) ;
motg - > phy_rst = devm_reset_control_get ( & pdev - > dev , " phy " ) ;
if ( IS_ERR ( motg - > phy_rst ) )
2014-06-30 18:29:49 +01:00
motg - > phy_rst = NULL ;
2014-04-28 16:34:16 +03:00
2014-04-28 16:34:15 +03:00
pdata - > mode = of_usb_get_dr_mode ( node ) ;
if ( pdata - > mode = = USB_DR_MODE_UNKNOWN )
pdata - > mode = USB_DR_MODE_OTG ;
pdata - > otg_control = OTG_PHY_CONTROL ;
if ( ! of_property_read_u32 ( node , " qcom,otg-control " , & val ) )
if ( val = = OTG_PMIC_CONTROL )
pdata - > otg_control = val ;
2014-04-28 16:34:17 +03:00
if ( ! of_property_read_u32 ( node , " qcom,phy-num " , & val ) & & val < 2 )
motg - > phy_number = val ;
2014-04-28 16:34:22 +03:00
motg - > vdd_levels [ VDD_LEVEL_NONE ] = USB_PHY_SUSP_DIG_VOL ;
motg - > vdd_levels [ VDD_LEVEL_MIN ] = USB_PHY_VDD_DIG_VOL_MIN ;
motg - > vdd_levels [ VDD_LEVEL_MAX ] = USB_PHY_VDD_DIG_VOL_MAX ;
if ( of_get_property ( node , " qcom,vdd-levels " , & len ) & &
len = = sizeof ( tmp ) ) {
of_property_read_u32_array ( node , " qcom,vdd-levels " ,
tmp , len / sizeof ( * tmp ) ) ;
motg - > vdd_levels [ VDD_LEVEL_NONE ] = tmp [ VDD_LEVEL_NONE ] ;
motg - > vdd_levels [ VDD_LEVEL_MIN ] = tmp [ VDD_LEVEL_MIN ] ;
motg - > vdd_levels [ VDD_LEVEL_MAX ] = tmp [ VDD_LEVEL_MAX ] ;
}
2014-04-28 16:34:15 +03:00
prop = of_find_property ( node , " qcom,phy-init-sequence " , & len ) ;
if ( ! prop | | ! len )
return 0 ;
words = len / sizeof ( u32 ) ;
if ( words > = ULPI_EXT_VENDOR_SPECIFIC ) {
dev_warn ( & pdev - > dev , " Too big PHY init sequence %d \n " , words ) ;
return 0 ;
}
pdata - > phy_init_seq = devm_kzalloc ( & pdev - > dev , len , GFP_KERNEL ) ;
2014-10-14 15:56:17 +08:00
if ( ! pdata - > phy_init_seq )
2014-04-28 16:34:15 +03:00
return 0 ;
ret = of_property_read_u32_array ( node , " qcom,phy-init-sequence " ,
pdata - > phy_init_seq , words ) ;
if ( ! ret )
pdata - > phy_init_sz = words ;
return 0 ;
}
2014-04-28 16:34:07 +03:00
static int msm_otg_probe ( struct platform_device * pdev )
2010-12-07 17:53:55 +05:30
{
2014-04-28 16:34:08 +03:00
struct regulator_bulk_data regs [ 3 ] ;
2010-12-07 17:53:55 +05:30
int ret = 0 ;
2014-04-28 16:34:15 +03:00
struct device_node * np = pdev - > dev . of_node ;
struct msm_otg_platform_data * pdata ;
2010-12-07 17:53:55 +05:30
struct resource * res ;
struct msm_otg * motg ;
2012-02-13 13:24:09 +02:00
struct usb_phy * phy ;
2014-04-28 16:34:20 +03:00
void __iomem * phy_select ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:08 +03:00
motg = devm_kzalloc ( & pdev - > dev , sizeof ( struct msm_otg ) , GFP_KERNEL ) ;
2014-10-14 15:56:17 +08:00
if ( ! motg )
2010-12-07 17:53:55 +05:30
return - ENOMEM ;
2014-04-28 16:34:15 +03:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( ! pdata ) {
if ( ! np )
return - ENXIO ;
ret = msm_otg_read_dt ( pdev , motg ) ;
if ( ret )
return ret ;
}
2014-04-28 16:34:08 +03:00
motg - > phy . otg = devm_kzalloc ( & pdev - > dev , sizeof ( struct usb_otg ) ,
GFP_KERNEL ) ;
2014-10-14 15:56:17 +08:00
if ( ! motg - > phy . otg )
2014-04-28 16:34:08 +03:00
return - ENOMEM ;
2012-02-13 13:24:09 +02:00
phy = & motg - > phy ;
phy - > dev = & pdev - > dev ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:15 +03:00
motg - > clk = devm_clk_get ( & pdev - > dev , np ? " core " : " usb_hs_clk " ) ;
2010-12-07 17:53:55 +05:30
if ( IS_ERR ( motg - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get usb_hs_clk \n " ) ;
2014-04-28 16:34:08 +03:00
return PTR_ERR ( motg - > clk ) ;
2010-12-07 17:53:55 +05:30
}
2011-05-04 10:19:46 +05:30
/*
* 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
2014-04-28 16:34:12 +03:00
* CORE CLK .
2011-05-04 10:19:46 +05:30
*/
2014-04-28 16:34:15 +03:00
motg - > pclk = devm_clk_get ( & pdev - > dev , np ? " iface " : " usb_hs_pclk " ) ;
2010-12-07 17:53:55 +05:30
if ( IS_ERR ( motg - > pclk ) ) {
dev_err ( & pdev - > dev , " failed to get usb_hs_pclk \n " ) ;
2014-04-28 16:34:08 +03:00
return PTR_ERR ( motg - > pclk ) ;
2010-12-07 17:53:55 +05:30
}
/*
* USB core clock is not present on all MSM chips . This
* clock is introduced to remove the dependency on AXI
* bus frequency .
*/
2014-04-28 16:34:15 +03:00
motg - > core_clk = devm_clk_get ( & pdev - > dev ,
np ? " alt_core " : " usb_hs_core_clk " ) ;
2010-12-07 17:53:55 +05:30
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-05-19 23:35:19 +03:00
if ( ! res )
return - EINVAL ;
motg - > regs = devm_ioremap ( & pdev - > dev , res - > start , resource_size ( res ) ) ;
if ( ! motg - > regs )
return - ENOMEM ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:20 +03:00
/*
* NOTE : The PHYs can be multiplexed between the chipidea controller
* and the dwc3 controller , using a single bit . It is important that
* the dwc3 driver does not set this bit in an incompatible way .
*/
if ( motg - > phy_number ) {
phy_select = devm_ioremap_nocache ( & pdev - > dev , USB2_PHY_SEL , 4 ) ;
2014-07-20 11:40:37 +08:00
if ( ! phy_select )
return - ENOMEM ;
2014-04-28 16:34:20 +03:00
/* Enable second PHY with the OTG port */
2014-04-30 11:35:22 -05:00
writel ( 0x1 , phy_select ) ;
2014-04-28 16:34:20 +03:00
}
2010-12-07 17:53:55 +05:30
dev_info ( & pdev - > dev , " OTG regs = %p \n " , motg - > regs ) ;
motg - > irq = platform_get_irq ( pdev , 0 ) ;
2014-04-28 16:34:14 +03:00
if ( motg - > irq < 0 ) {
2010-12-07 17:53:55 +05:30
dev_err ( & pdev - > dev , " platform_get_irq failed \n " ) ;
2014-04-28 16:34:08 +03:00
return motg - > irq ;
}
2014-04-28 16:34:13 +03:00
regs [ 0 ] . supply = " vddcx " ;
regs [ 1 ] . supply = " v3p3 " ;
regs [ 2 ] . supply = " v1p8 " ;
2014-04-28 16:34:08 +03:00
ret = devm_regulator_bulk_get ( motg - > phy . dev , ARRAY_SIZE ( regs ) , regs ) ;
if ( ret )
return ret ;
motg - > vddcx = regs [ 0 ] . consumer ;
motg - > v3p3 = regs [ 1 ] . consumer ;
motg - > v1p8 = regs [ 2 ] . consumer ;
clk_set_rate ( motg - > clk , 60000000 ) ;
2010-12-07 17:53:55 +05:30
2013-06-17 10:43:10 -07:00
clk_prepare_enable ( motg - > clk ) ;
clk_prepare_enable ( motg - > pclk ) ;
2011-05-04 10:19:48 +05:30
2014-04-28 16:34:08 +03:00
if ( ! IS_ERR ( motg - > core_clk ) )
clk_prepare_enable ( motg - > core_clk ) ;
2011-05-04 10:19:48 +05:30
ret = msm_hsusb_init_vddcx ( motg , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vddcx configuration failed \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_clks ;
2011-05-04 10:19:48 +05:30
}
ret = msm_hsusb_ldo_init ( motg , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vreg configuration failed \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_vddcx ;
2011-05-04 10:19:48 +05:30
}
2014-04-28 16:34:06 +03:00
ret = msm_hsusb_ldo_set_mode ( motg , 1 ) ;
2011-05-04 10:19:48 +05:30
if ( ret ) {
dev_err ( & pdev - > dev , " hsusb vreg enable failed \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_ldo ;
2011-05-04 10:19:48 +05:30
}
2010-12-07 17:53:55 +05:30
writel ( 0 , USB_USBINTR ) ;
writel ( 0 , USB_OTGSC ) ;
INIT_WORK ( & motg - > sm_work , msm_otg_sm_work ) ;
2011-05-04 10:19:47 +05:30
INIT_DELAYED_WORK ( & motg - > chg_work , msm_chg_detect_work ) ;
2014-04-28 16:34:08 +03:00
ret = devm_request_irq ( & pdev - > dev , motg - > irq , msm_otg_irq , IRQF_SHARED ,
2010-12-07 17:53:55 +05:30
" msm_otg " , motg ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " request irq failed \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_ldo ;
2010-12-07 17:53:55 +05:30
}
2014-04-28 16:34:18 +03:00
phy - > init = msm_phy_init ;
2012-02-13 13:24:09 +02:00
phy - > set_power = msm_otg_set_power ;
2014-04-28 16:34:21 +03:00
phy - > notify_disconnect = msm_phy_notify_disconnect ;
2014-04-28 16:34:23 +03:00
phy - > type = USB_PHY_TYPE_USB2 ;
2012-02-13 13:24:09 +02:00
phy - > io_ops = & msm_otg_io_ops ;
2010-12-07 17:53:55 +05:30
2014-10-30 18:41:14 +01:00
phy - > otg - > usb_phy = & motg - > phy ;
2012-02-13 13:24:09 +02:00
phy - > otg - > set_host = msm_otg_set_host ;
phy - > otg - > set_peripheral = msm_otg_set_peripheral ;
2010-12-07 17:53:55 +05:30
2014-04-28 16:34:18 +03:00
msm_usb_reset ( phy ) ;
2014-04-28 16:34:23 +03:00
ret = usb_add_phy_dev ( & motg - > phy ) ;
2010-12-07 17:53:55 +05:30
if ( ret ) {
2012-06-22 17:02:45 +05:30
dev_err ( & pdev - > dev , " usb_add_phy failed \n " ) ;
2014-04-28 16:34:08 +03:00
goto disable_ldo ;
2010-12-07 17:53:55 +05:30
}
platform_set_drvdata ( pdev , motg ) ;
device_init_wakeup ( & pdev - > dev , 1 ) ;
2014-04-28 16:34:11 +03:00
if ( motg - > pdata - > mode = = USB_DR_MODE_OTG & &
2014-04-28 16:34:15 +03:00
motg - > pdata - > otg_control = = OTG_USER_CONTROL ) {
2010-12-07 17:53:55 +05:30
ret = msm_otg_debugfs_init ( motg ) ;
if ( ret )
2014-04-28 16:34:10 +03:00
dev_dbg ( & pdev - > dev , " Can not create mode change file \n " ) ;
2010-12-07 17:53:55 +05:30
}
2010-12-07 17:53:58 +05:30
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2010-12-07 17:53:55 +05:30
2010-12-07 17:53:58 +05:30
return 0 ;
2014-04-28 16:34:08 +03:00
disable_ldo :
msm_hsusb_ldo_init ( motg , 0 ) ;
disable_vddcx :
msm_hsusb_init_vddcx ( motg , 0 ) ;
2010-12-07 17:53:55 +05:30
disable_clks :
2013-06-17 10:43:10 -07:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2014-04-28 16:34:08 +03:00
if ( ! IS_ERR ( motg - > core_clk ) )
clk_disable_unprepare ( motg - > core_clk ) ;
2010-12-07 17:53:55 +05:30
return ret ;
}
2012-11-19 13:26:20 -05:00
static int msm_otg_remove ( struct platform_device * pdev )
2010-12-07 17:53:55 +05:30
{
struct msm_otg * motg = platform_get_drvdata ( pdev ) ;
2012-02-13 13:24:09 +02:00
struct usb_phy * phy = & motg - > phy ;
2010-12-07 17:53:58 +05:30
int cnt = 0 ;
2010-12-07 17:53:55 +05:30
2012-02-13 13:24:09 +02:00
if ( phy - > otg - > host | | phy - > otg - > gadget )
2010-12-07 17:53:55 +05:30
return - EBUSY ;
msm_otg_debugfs_cleanup ( ) ;
2011-05-04 10:19:47 +05:30
cancel_delayed_work_sync ( & motg - > chg_work ) ;
2010-12-07 17:53:55 +05:30
cancel_work_sync ( & motg - > sm_work ) ;
2010-12-07 17:53:58 +05:30
2011-02-15 09:42:34 +05:30
pm_runtime_resume ( & pdev - > dev ) ;
2010-12-07 17:53:58 +05:30
2010-12-07 17:53:55 +05:30
device_init_wakeup ( & pdev - > dev , 0 ) ;
2010-12-07 17:53:58 +05:30
pm_runtime_disable ( & pdev - > dev ) ;
2010-12-07 17:53:55 +05:30
2012-06-22 17:02:46 +05:30
usb_remove_phy ( phy ) ;
2014-04-28 16:34:08 +03:00
disable_irq ( motg - > irq ) ;
2010-12-07 17:53:55 +05:30
2010-12-07 17:53:58 +05:30
/*
* Put PHY in low power mode .
*/
2012-02-13 13:24:09 +02:00
ulpi_read ( phy , 0x14 ) ;
ulpi_write ( phy , 0x08 , 0x09 ) ;
2010-12-07 17:53:58 +05:30
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 13:24:09 +02:00
dev_err ( phy - > dev , " Unable to suspend PHY \n " ) ;
2010-12-07 17:53:58 +05:30
2013-06-17 10:43:10 -07:00
clk_disable_unprepare ( motg - > pclk ) ;
clk_disable_unprepare ( motg - > clk ) ;
2014-04-28 16:34:08 +03:00
if ( ! IS_ERR ( motg - > core_clk ) )
2013-06-17 10:43:10 -07:00
clk_disable_unprepare ( motg - > core_clk ) ;
2011-05-04 10:19:48 +05:30
msm_hsusb_ldo_init ( motg , 0 ) ;
2010-12-07 17:53:55 +05:30
2010-12-07 17:53:58 +05:30
pm_runtime_set_suspended ( & pdev - > dev ) ;
2010-12-07 17:53:55 +05:30
return 0 ;
}
2014-11-29 23:47:05 +01:00
# ifdef CONFIG_PM
2010-12-07 17:53:58 +05:30
static int msm_otg_runtime_idle ( struct device * dev )
{
struct msm_otg * motg = dev_get_drvdata ( dev ) ;
2012-02-13 13:24:09 +02:00
struct usb_otg * otg = motg - > phy . otg ;
2010-12-07 17:53:58 +05:30
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 .
*/
2014-10-30 18:41:13 +01:00
if ( otg - > state ! = OTG_STATE_UNDEFINED )
2010-12-07 17:53:58 +05:30
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 09:42:34 +05:30
# ifdef CONFIG_PM_SLEEP
2010-12-07 17:53:58 +05:30
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
static const struct dev_pm_ops msm_otg_dev_pm_ops = {
2011-02-15 09:42:34 +05:30
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 17:53:58 +05:30
} ;
2010-12-07 17:53:55 +05:30
static struct platform_driver msm_otg_driver = {
2014-04-28 16:34:07 +03:00
. probe = msm_otg_probe ,
2012-11-19 13:21:08 -05:00
. remove = msm_otg_remove ,
2010-12-07 17:53:55 +05:30
. driver = {
. name = DRIVER_NAME ,
2010-12-07 17:53:58 +05:30
. pm = & msm_otg_dev_pm_ops ,
2014-04-28 16:34:15 +03:00
. of_match_table = msm_otg_dt_match ,
2010-12-07 17:53:55 +05:30
} ,
} ;
2014-04-28 16:34:07 +03:00
module_platform_driver ( msm_otg_driver ) ;
2010-12-07 17:53:55 +05:30
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MSM USB transceiver driver " ) ;