2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2014-04-23 15:56:44 +08:00
/*
* otg_fsm . c - ChipIdea USB IP core OTG FSM driver
*
* Copyright ( C ) 2014 Freescale Semiconductor , Inc .
*
* Author : Jun Li
*/
/*
* This file mainly handles OTG fsm , it includes OTG fsm operations
* for HNP and SRP .
2014-04-23 15:56:50 +08:00
*
* TODO List
* - ADP
* - OTG test device
2014-04-23 15:56:44 +08:00
*/
# include <linux/usb/otg.h>
# include <linux/usb/gadget.h>
# include <linux/usb/hcd.h>
# include <linux/usb/chipidea.h>
2014-04-23 15:56:48 +08:00
# include <linux/regulator/consumer.h>
2014-04-23 15:56:44 +08:00
# include "ci.h"
# include "bits.h"
# include "otg.h"
# include "otg_fsm.h"
2014-04-23 15:56:51 +08:00
/* Add for otg: interact with user space app */
static ssize_t
2018-01-23 11:24:05 +01:00
a_bus_req_show ( struct device * dev , struct device_attribute * attr , char * buf )
2014-04-23 15:56:51 +08:00
{
char * next ;
unsigned size , t ;
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
next = buf ;
size = PAGE_SIZE ;
t = scnprintf ( next , size , " %d \n " , ci - > fsm . a_bus_req ) ;
size - = t ;
next + = t ;
return PAGE_SIZE - size ;
}
static ssize_t
2018-01-23 11:24:05 +01:00
a_bus_req_store ( struct device * dev , struct device_attribute * attr ,
2014-04-23 15:56:51 +08:00
const char * buf , size_t count )
{
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
if ( count > 2 )
return - 1 ;
mutex_lock ( & ci - > fsm . lock ) ;
if ( buf [ 0 ] = = ' 0 ' ) {
ci - > fsm . a_bus_req = 0 ;
} else if ( buf [ 0 ] = = ' 1 ' ) {
/* If a_bus_drop is TRUE, a_bus_req can't be set */
if ( ci - > fsm . a_bus_drop ) {
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
ci - > fsm . a_bus_req = 1 ;
2016-02-19 10:04:45 +08:00
if ( ci - > fsm . otg - > state = = OTG_STATE_A_PERIPHERAL ) {
ci - > gadget . host_request_flag = 1 ;
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
2014-04-23 15:56:51 +08:00
}
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:51 +08:00
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
2018-01-23 11:24:05 +01:00
static DEVICE_ATTR_RW ( a_bus_req ) ;
2014-04-23 15:56:51 +08:00
static ssize_t
2018-01-23 11:24:05 +01:00
a_bus_drop_show ( struct device * dev , struct device_attribute * attr , char * buf )
2014-04-23 15:56:51 +08:00
{
char * next ;
unsigned size , t ;
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
next = buf ;
size = PAGE_SIZE ;
t = scnprintf ( next , size , " %d \n " , ci - > fsm . a_bus_drop ) ;
size - = t ;
next + = t ;
return PAGE_SIZE - size ;
}
static ssize_t
2018-01-23 11:24:05 +01:00
a_bus_drop_store ( struct device * dev , struct device_attribute * attr ,
2014-04-23 15:56:51 +08:00
const char * buf , size_t count )
{
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
if ( count > 2 )
return - 1 ;
mutex_lock ( & ci - > fsm . lock ) ;
if ( buf [ 0 ] = = ' 0 ' ) {
ci - > fsm . a_bus_drop = 0 ;
} else if ( buf [ 0 ] = = ' 1 ' ) {
ci - > fsm . a_bus_drop = 1 ;
ci - > fsm . a_bus_req = 0 ;
}
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:51 +08:00
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
2018-01-23 11:24:05 +01:00
static DEVICE_ATTR_RW ( a_bus_drop ) ;
2014-04-23 15:56:51 +08:00
static ssize_t
2018-01-23 11:24:05 +01:00
b_bus_req_show ( struct device * dev , struct device_attribute * attr , char * buf )
2014-04-23 15:56:51 +08:00
{
char * next ;
unsigned size , t ;
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
next = buf ;
size = PAGE_SIZE ;
t = scnprintf ( next , size , " %d \n " , ci - > fsm . b_bus_req ) ;
size - = t ;
next + = t ;
return PAGE_SIZE - size ;
}
static ssize_t
2018-01-23 11:24:05 +01:00
b_bus_req_store ( struct device * dev , struct device_attribute * attr ,
2014-04-23 15:56:51 +08:00
const char * buf , size_t count )
{
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
if ( count > 2 )
return - 1 ;
mutex_lock ( & ci - > fsm . lock ) ;
if ( buf [ 0 ] = = ' 0 ' )
ci - > fsm . b_bus_req = 0 ;
2016-02-19 10:04:45 +08:00
else if ( buf [ 0 ] = = ' 1 ' ) {
2014-04-23 15:56:51 +08:00
ci - > fsm . b_bus_req = 1 ;
2016-02-19 10:04:45 +08:00
if ( ci - > fsm . otg - > state = = OTG_STATE_B_PERIPHERAL ) {
ci - > gadget . host_request_flag = 1 ;
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
}
2014-04-23 15:56:51 +08:00
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:51 +08:00
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
2018-01-23 11:24:05 +01:00
static DEVICE_ATTR_RW ( b_bus_req ) ;
2014-04-23 15:56:51 +08:00
static ssize_t
2018-01-23 11:24:07 +01:00
a_clr_err_store ( struct device * dev , struct device_attribute * attr ,
2014-04-23 15:56:51 +08:00
const char * buf , size_t count )
{
struct ci_hdrc * ci = dev_get_drvdata ( dev ) ;
if ( count > 2 )
return - 1 ;
mutex_lock ( & ci - > fsm . lock ) ;
if ( buf [ 0 ] = = ' 1 ' )
ci - > fsm . a_clr_err = 1 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:51 +08:00
mutex_unlock ( & ci - > fsm . lock ) ;
return count ;
}
2018-01-23 11:24:07 +01:00
static DEVICE_ATTR_WO ( a_clr_err ) ;
2014-04-23 15:56:51 +08:00
static struct attribute * inputs_attrs [ ] = {
& dev_attr_a_bus_req . attr ,
& dev_attr_a_bus_drop . attr ,
& dev_attr_b_bus_req . attr ,
& dev_attr_a_clr_err . attr ,
NULL ,
} ;
2017-08-04 17:36:39 +05:30
static const struct attribute_group inputs_attr_group = {
2014-04-23 15:56:51 +08:00
. name = " inputs " ,
. attrs = inputs_attrs ,
} ;
2015-03-20 16:28:06 +08:00
/*
* Keep this list in the same order as timers indexed
* by enum otg_fsm_timer in include / linux / usb / otg - fsm . h
*/
static unsigned otg_timer_ms [ ] = {
TA_WAIT_VRISE ,
TA_WAIT_VFALL ,
TA_WAIT_BCON ,
TA_AIDL_BDIS ,
TB_ASE0_BRST ,
TA_BIDL_ADIS ,
2016-02-19 10:04:49 +08:00
TB_AIDL_BDIS ,
2015-03-20 16:28:06 +08:00
TB_SE0_SRP ,
TB_SRP_FAIL ,
0 ,
TB_DATA_PLS ,
TB_SSEND_SRP ,
} ;
2014-04-23 15:56:48 +08:00
/*
* Add timer to active timer list
*/
2015-03-20 16:28:05 +08:00
static void ci_otg_add_timer ( struct ci_hdrc * ci , enum otg_fsm_timer t )
2014-04-23 15:56:48 +08:00
{
2015-03-20 16:28:06 +08:00
unsigned long flags , timer_sec , timer_nsec ;
2014-04-23 15:56:48 +08:00
2015-03-20 16:28:05 +08:00
if ( t > = NUM_OTG_FSM_TIMERS )
2014-04-23 15:56:48 +08:00
return ;
2015-03-20 16:28:06 +08:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
timer_sec = otg_timer_ms [ t ] / MSEC_PER_SEC ;
timer_nsec = ( otg_timer_ms [ t ] % MSEC_PER_SEC ) * NSEC_PER_MSEC ;
ci - > hr_timeouts [ t ] = ktime_add ( ktime_get ( ) ,
ktime_set ( timer_sec , timer_nsec ) ) ;
ci - > enabled_otg_timer_bits | = ( 1 < < t ) ;
if ( ( ci - > next_otg_timer = = NUM_OTG_FSM_TIMERS ) | |
2017-05-26 12:15:59 +02:00
ktime_after ( ci - > hr_timeouts [ ci - > next_otg_timer ] ,
2016-12-25 11:38:40 +01:00
ci - > hr_timeouts [ t ] ) ) {
2015-03-20 16:28:06 +08:00
ci - > next_otg_timer = t ;
hrtimer_start_range_ns ( & ci - > otg_fsm_hrtimer ,
ci - > hr_timeouts [ t ] , NSEC_PER_MSEC ,
HRTIMER_MODE_ABS ) ;
}
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2014-04-23 15:56:48 +08:00
}
/*
* Remove timer from active timer list
*/
2015-03-20 16:28:05 +08:00
static void ci_otg_del_timer ( struct ci_hdrc * ci , enum otg_fsm_timer t )
2014-04-23 15:56:48 +08:00
{
2015-03-20 16:28:06 +08:00
unsigned long flags , enabled_timer_bits ;
enum otg_fsm_timer cur_timer , next_timer = NUM_OTG_FSM_TIMERS ;
2014-04-23 15:56:48 +08:00
2015-03-20 16:28:06 +08:00
if ( ( t > = NUM_OTG_FSM_TIMERS ) | |
! ( ci - > enabled_otg_timer_bits & ( 1 < < t ) ) )
2014-04-23 15:56:48 +08:00
return ;
2015-03-20 16:28:06 +08:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
ci - > enabled_otg_timer_bits & = ~ ( 1 < < t ) ;
if ( ci - > next_otg_timer = = t ) {
if ( ci - > enabled_otg_timer_bits = = 0 ) {
/* No enabled timers after delete it */
hrtimer_cancel ( & ci - > otg_fsm_hrtimer ) ;
ci - > next_otg_timer = NUM_OTG_FSM_TIMERS ;
} else {
/* Find the next timer */
enabled_timer_bits = ci - > enabled_otg_timer_bits ;
for_each_set_bit ( cur_timer , & enabled_timer_bits ,
NUM_OTG_FSM_TIMERS ) {
if ( ( next_timer = = NUM_OTG_FSM_TIMERS ) | |
2017-05-26 12:15:59 +02:00
ktime_before ( ci - > hr_timeouts [ next_timer ] ,
2016-12-25 11:38:40 +01:00
ci - > hr_timeouts [ cur_timer ] ) )
2015-03-20 16:28:06 +08:00
next_timer = cur_timer ;
}
2014-04-23 15:56:50 +08:00
}
}
2015-03-20 16:28:06 +08:00
if ( next_timer ! = NUM_OTG_FSM_TIMERS ) {
ci - > next_otg_timer = next_timer ;
hrtimer_start_range_ns ( & ci - > otg_fsm_hrtimer ,
ci - > hr_timeouts [ next_timer ] , NSEC_PER_MSEC ,
HRTIMER_MODE_ABS ) ;
2015-02-11 12:45:03 +08:00
}
2015-03-20 16:28:06 +08:00
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2014-04-23 15:56:50 +08:00
}
2015-03-20 16:28:06 +08:00
/* OTG FSM timer handlers */
static int a_wait_vrise_tmout ( struct ci_hdrc * ci )
2014-04-23 15:56:49 +08:00
{
2015-03-20 16:28:06 +08:00
ci - > fsm . a_wait_vrise_tmout = 1 ;
return 0 ;
2014-04-23 15:56:49 +08:00
}
2015-03-20 16:28:06 +08:00
static int a_wait_vfall_tmout ( struct ci_hdrc * ci )
2014-04-23 15:56:49 +08:00
{
2015-03-20 16:28:06 +08:00
ci - > fsm . a_wait_vfall_tmout = 1 ;
return 0 ;
2014-04-23 15:56:49 +08:00
}
2015-03-20 16:28:06 +08:00
static int a_wait_bcon_tmout ( struct ci_hdrc * ci )
2014-04-23 15:56:49 +08:00
{
2015-03-20 16:28:06 +08:00
ci - > fsm . a_wait_bcon_tmout = 1 ;
return 0 ;
}
2014-04-23 15:56:49 +08:00
2015-03-20 16:28:06 +08:00
static int a_aidl_bdis_tmout ( struct ci_hdrc * ci )
{
ci - > fsm . a_aidl_bdis_tmout = 1 ;
return 0 ;
2014-04-23 15:56:49 +08:00
}
2015-03-20 16:28:06 +08:00
static int b_ase0_brst_tmout ( struct ci_hdrc * ci )
2014-04-23 15:56:49 +08:00
{
2015-03-20 16:28:06 +08:00
ci - > fsm . b_ase0_brst_tmout = 1 ;
return 0 ;
}
2014-04-23 15:56:49 +08:00
2015-03-20 16:28:06 +08:00
static int a_bidl_adis_tmout ( struct ci_hdrc * ci )
{
ci - > fsm . a_bidl_adis_tmout = 1 ;
return 0 ;
}
2014-04-23 15:56:49 +08:00
2016-02-19 10:04:49 +08:00
static int b_aidl_bdis_tmout ( struct ci_hdrc * ci )
{
ci - > fsm . a_bus_suspend = 1 ;
return 0 ;
}
2015-03-20 16:28:06 +08:00
static int b_se0_srp_tmout ( struct ci_hdrc * ci )
{
ci - > fsm . b_se0_srp = 1 ;
return 0 ;
2014-04-23 15:56:49 +08:00
}
2015-03-20 16:28:06 +08:00
static int b_srp_fail_tmout ( struct ci_hdrc * ci )
2014-04-23 15:56:49 +08:00
{
2015-03-20 16:28:06 +08:00
ci - > fsm . b_srp_done = 1 ;
return 1 ;
}
2014-04-23 15:56:49 +08:00
2015-03-20 16:28:06 +08:00
static int b_data_pls_tmout ( struct ci_hdrc * ci )
{
2014-04-23 15:56:49 +08:00
ci - > fsm . b_srp_done = 1 ;
ci - > fsm . b_bus_req = 0 ;
if ( ci - > fsm . power_up )
ci - > fsm . power_up = 0 ;
hw_write_otgsc ( ci , OTGSC_HABA , 0 ) ;
2015-03-20 16:28:06 +08:00
pm_runtime_put ( ci - > dev ) ;
return 0 ;
}
2014-04-23 15:56:49 +08:00
2015-03-20 16:28:06 +08:00
static int b_ssend_srp_tmout ( struct ci_hdrc * ci )
{
ci - > fsm . b_ssend_srp = 1 ;
/* only vbus fall below B_sess_vld in b_idle state */
if ( ci - > fsm . otg - > state = = OTG_STATE_B_IDLE )
return 0 ;
else
return 1 ;
}
/*
* Keep this list in the same order as timers indexed
* by enum otg_fsm_timer in include / linux / usb / otg - fsm . h
*/
static int ( * otg_timer_handlers [ ] ) ( struct ci_hdrc * ) = {
a_wait_vrise_tmout , /* A_WAIT_VRISE */
a_wait_vfall_tmout , /* A_WAIT_VFALL */
a_wait_bcon_tmout , /* A_WAIT_BCON */
a_aidl_bdis_tmout , /* A_AIDL_BDIS */
b_ase0_brst_tmout , /* B_ASE0_BRST */
a_bidl_adis_tmout , /* A_BIDL_ADIS */
2016-02-19 10:04:49 +08:00
b_aidl_bdis_tmout , /* B_AIDL_BDIS */
2015-03-20 16:28:06 +08:00
b_se0_srp_tmout , /* B_SE0_SRP */
b_srp_fail_tmout , /* B_SRP_FAIL */
NULL , /* A_WAIT_ENUM */
b_data_pls_tmout , /* B_DATA_PLS */
b_ssend_srp_tmout , /* B_SSEND_SRP */
} ;
/*
* Enable the next nearest enabled timer if have
*/
static enum hrtimer_restart ci_otg_hrtimer_func ( struct hrtimer * t )
{
struct ci_hdrc * ci = container_of ( t , struct ci_hdrc , otg_fsm_hrtimer ) ;
ktime_t now , * timeout ;
unsigned long enabled_timer_bits ;
unsigned long flags ;
enum otg_fsm_timer cur_timer , next_timer = NUM_OTG_FSM_TIMERS ;
int ret = - EINVAL ;
spin_lock_irqsave ( & ci - > lock , flags ) ;
enabled_timer_bits = ci - > enabled_otg_timer_bits ;
ci - > next_otg_timer = NUM_OTG_FSM_TIMERS ;
now = ktime_get ( ) ;
for_each_set_bit ( cur_timer , & enabled_timer_bits , NUM_OTG_FSM_TIMERS ) {
2017-05-26 12:15:59 +02:00
if ( ktime_compare ( now , ci - > hr_timeouts [ cur_timer ] ) > = 0 ) {
2015-03-20 16:28:06 +08:00
ci - > enabled_otg_timer_bits & = ~ ( 1 < < cur_timer ) ;
if ( otg_timer_handlers [ cur_timer ] )
ret = otg_timer_handlers [ cur_timer ] ( ci ) ;
} else {
if ( ( next_timer = = NUM_OTG_FSM_TIMERS ) | |
2017-05-26 12:15:59 +02:00
ktime_before ( ci - > hr_timeouts [ cur_timer ] ,
2016-12-25 11:38:40 +01:00
ci - > hr_timeouts [ next_timer ] ) )
2015-03-20 16:28:06 +08:00
next_timer = cur_timer ;
}
}
/* Enable the next nearest timer */
if ( next_timer < NUM_OTG_FSM_TIMERS ) {
timeout = & ci - > hr_timeouts [ next_timer ] ;
hrtimer_start_range_ns ( & ci - > otg_fsm_hrtimer , * timeout ,
NSEC_PER_MSEC , HRTIMER_MODE_ABS ) ;
ci - > next_otg_timer = next_timer ;
}
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
if ( ! ret )
ci_otg_queue_work ( ci ) ;
return HRTIMER_NORESTART ;
2014-04-23 15:56:49 +08:00
}
/* Initialize timers */
static int ci_otg_init_timers ( struct ci_hdrc * ci )
{
2015-03-20 16:28:06 +08:00
hrtimer_init ( & ci - > otg_fsm_hrtimer , CLOCK_MONOTONIC , HRTIMER_MODE_ABS ) ;
ci - > otg_fsm_hrtimer . function = ci_otg_hrtimer_func ;
2014-04-23 15:56:49 +08:00
return 0 ;
}
2014-04-23 15:56:48 +08:00
/* -------------------------------------------------------------*/
/* Operations that will be called from OTG Finite State Machine */
/* -------------------------------------------------------------*/
static void ci_otg_fsm_add_timer ( struct otg_fsm * fsm , enum otg_fsm_timer t )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( t < NUM_OTG_FSM_TIMERS )
ci_otg_add_timer ( ci , t ) ;
return ;
}
static void ci_otg_fsm_del_timer ( struct otg_fsm * fsm , enum otg_fsm_timer t )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( t < NUM_OTG_FSM_TIMERS )
ci_otg_del_timer ( ci , t ) ;
return ;
}
/*
* A - device drive vbus : turn on vbus regulator and enable port power
* Data pulse irq should be disabled while vbus is on .
*/
static void ci_otg_drv_vbus ( struct otg_fsm * fsm , int on )
{
int ret ;
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( on ) {
/* Enable power power */
hw_write ( ci , OP_PORTSC , PORTSC_W1C_BITS | PORTSC_PP ,
PORTSC_PP ) ;
if ( ci - > platdata - > reg_vbus ) {
ret = regulator_enable ( ci - > platdata - > reg_vbus ) ;
if ( ret ) {
dev_err ( ci - > dev ,
" Failed to enable vbus regulator, ret=%d \n " ,
ret ) ;
return ;
}
}
/* Disable data pulse irq */
hw_write_otgsc ( ci , OTGSC_DPIE , 0 ) ;
fsm - > a_srp_det = 0 ;
fsm - > power_up = 0 ;
} else {
if ( ci - > platdata - > reg_vbus )
regulator_disable ( ci - > platdata - > reg_vbus ) ;
fsm - > a_bus_drop = 1 ;
fsm - > a_bus_req = 0 ;
}
}
/*
* Control data line by Run Stop bit .
*/
static void ci_otg_loc_conn ( struct otg_fsm * fsm , int on )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( on )
hw_write ( ci , OP_USBCMD , USBCMD_RS , USBCMD_RS ) ;
else
hw_write ( ci , OP_USBCMD , USBCMD_RS , 0 ) ;
}
/*
* Generate SOF by host .
* In host mode , controller will automatically send SOF .
* Suspend will block the data on the port .
2015-12-15 17:51:29 +08:00
*
* This is controlled through usbcore by usb autosuspend ,
* so the usb device class driver need support autosuspend ,
* otherwise the bus suspend will not happen .
2014-04-23 15:56:48 +08:00
*/
static void ci_otg_loc_sof ( struct otg_fsm * fsm , int on )
{
2015-12-15 17:51:29 +08:00
struct usb_device * udev ;
2014-04-23 15:56:48 +08:00
2015-12-15 17:51:29 +08:00
if ( ! fsm - > otg - > host )
return ;
udev = usb_hub_find_child ( fsm - > otg - > host - > root_hub , 1 ) ;
if ( ! udev )
return ;
if ( on ) {
usb_disable_autosuspend ( udev ) ;
} else {
pm_runtime_set_autosuspend_delay ( & udev - > dev , 0 ) ;
usb_enable_autosuspend ( udev ) ;
}
2014-04-23 15:56:48 +08:00
}
/*
* Start SRP pulsing by data - line pulsing ,
* no v - bus pulsing followed
*/
static void ci_otg_start_pulse ( struct otg_fsm * fsm )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
/* Hardware Assistant Data pulse */
hw_write_otgsc ( ci , OTGSC_HADP , OTGSC_HADP ) ;
2015-03-20 16:28:06 +08:00
pm_runtime_get ( ci - > dev ) ;
2014-04-23 15:56:48 +08:00
ci_otg_add_timer ( ci , B_DATA_PLS ) ;
}
static int ci_otg_start_host ( struct otg_fsm * fsm , int on )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( on ) {
ci_role_stop ( ci ) ;
ci_role_start ( ci , CI_ROLE_HOST ) ;
} else {
ci_role_stop ( ci ) ;
ci_role_start ( ci , CI_ROLE_GADGET ) ;
}
return 0 ;
}
static int ci_otg_start_gadget ( struct otg_fsm * fsm , int on )
{
struct ci_hdrc * ci = container_of ( fsm , struct ci_hdrc , fsm ) ;
if ( on )
usb_gadget_vbus_connect ( & ci - > gadget ) ;
else
usb_gadget_vbus_disconnect ( & ci - > gadget ) ;
return 0 ;
}
static struct otg_fsm_ops ci_otg_ops = {
. drv_vbus = ci_otg_drv_vbus ,
. loc_conn = ci_otg_loc_conn ,
. loc_sof = ci_otg_loc_sof ,
. start_pulse = ci_otg_start_pulse ,
. add_timer = ci_otg_fsm_add_timer ,
. del_timer = ci_otg_fsm_del_timer ,
. start_host = ci_otg_start_host ,
. start_gadget = ci_otg_start_gadget ,
} ;
2014-04-23 15:56:50 +08:00
int ci_otg_fsm_work ( struct ci_hdrc * ci )
{
/*
* Don ' t do fsm transition for B device
* when there is no gadget class driver
*/
if ( ci - > fsm . id & & ! ( ci - > driver ) & &
2014-10-30 18:41:13 +01:00
ci - > fsm . otg - > state < OTG_STATE_A_IDLE )
2014-04-23 15:56:50 +08:00
return 0 ;
2015-02-11 12:45:03 +08:00
pm_runtime_get_sync ( ci - > dev ) ;
2014-04-23 15:56:50 +08:00
if ( otg_statemachine ( & ci - > fsm ) ) {
2014-10-30 18:41:13 +01:00
if ( ci - > fsm . otg - > state = = OTG_STATE_A_IDLE ) {
2014-04-23 15:56:50 +08:00
/*
* Further state change for cases :
* a_idle to b_idle ; or
* a_idle to a_wait_vrise due to ID change ( 1 - > 0 ) , so
* B - dev becomes A - dev can try to start new session
* consequently ; or
* a_idle to a_wait_vrise when power up
*/
if ( ( ci - > fsm . id ) | | ( ci - > id_event ) | |
2015-03-20 16:28:06 +08:00
( ci - > fsm . power_up ) ) {
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2015-03-20 16:28:06 +08:00
} else {
/* Enable data pulse irq */
hw_write ( ci , OP_PORTSC , PORTSC_W1C_BITS |
PORTSC_PP , 0 ) ;
hw_write_otgsc ( ci , OTGSC_DPIS , OTGSC_DPIS ) ;
hw_write_otgsc ( ci , OTGSC_DPIE , OTGSC_DPIE ) ;
}
2014-04-23 15:56:50 +08:00
if ( ci - > id_event )
ci - > id_event = false ;
2014-10-30 18:41:13 +01:00
} else if ( ci - > fsm . otg - > state = = OTG_STATE_B_IDLE ) {
2014-04-23 15:56:50 +08:00
if ( ci - > fsm . b_sess_vld ) {
ci - > fsm . power_up = 0 ;
/*
* Further transite to b_periphearl state
* when register gadget driver with vbus on
*/
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
2015-02-11 12:45:03 +08:00
} else if ( ci - > fsm . otg - > state = = OTG_STATE_A_HOST ) {
pm_runtime_mark_last_busy ( ci - > dev ) ;
pm_runtime_put_autosuspend ( ci - > dev ) ;
return 0 ;
2014-04-23 15:56:50 +08:00
}
}
2015-02-11 12:45:03 +08:00
pm_runtime_put_sync ( ci - > dev ) ;
2014-04-23 15:56:50 +08:00
return 0 ;
}
/*
* Update fsm variables in each state if catching expected interrupts ,
* called by otg fsm isr .
*/
static void ci_otg_fsm_event ( struct ci_hdrc * ci )
{
u32 intr_sts , otg_bsess_vld , port_conn ;
struct otg_fsm * fsm = & ci - > fsm ;
intr_sts = hw_read_intr_status ( ci ) ;
otg_bsess_vld = hw_read_otgsc ( ci , OTGSC_BSV ) ;
port_conn = hw_read ( ci , OP_PORTSC , PORTSC_CCS ) ;
2014-10-30 18:41:13 +01:00
switch ( ci - > fsm . otg - > state ) {
2014-04-23 15:56:50 +08:00
case OTG_STATE_A_WAIT_BCON :
if ( port_conn ) {
fsm - > b_conn = 1 ;
fsm - > a_bus_req = 1 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
case OTG_STATE_B_IDLE :
if ( otg_bsess_vld & & ( intr_sts & USBi_PCI ) & & port_conn ) {
fsm - > b_sess_vld = 1 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
case OTG_STATE_B_PERIPHERAL :
if ( ( intr_sts & USBi_SLI ) & & port_conn & & otg_bsess_vld ) {
2016-02-19 10:04:49 +08:00
ci_otg_add_timer ( ci , B_AIDL_BDIS ) ;
2014-04-23 15:56:50 +08:00
} else if ( intr_sts & USBi_PCI ) {
2016-02-19 10:04:49 +08:00
ci_otg_del_timer ( ci , B_AIDL_BDIS ) ;
2014-04-23 15:56:50 +08:00
if ( fsm - > a_bus_suspend = = 1 )
fsm - > a_bus_suspend = 0 ;
}
break ;
case OTG_STATE_B_HOST :
if ( ( intr_sts & USBi_PCI ) & & ! port_conn ) {
fsm - > a_conn = 0 ;
fsm - > b_bus_req = 0 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
case OTG_STATE_A_PERIPHERAL :
if ( intr_sts & USBi_SLI ) {
fsm - > b_bus_suspend = 1 ;
/*
* Init a timer to know how long this suspend
2014-11-26 13:44:38 +08:00
* will continue , if time out , indicates B no longer
2014-04-23 15:56:50 +08:00
* wants to be host role
*/
ci_otg_add_timer ( ci , A_BIDL_ADIS ) ;
}
if ( intr_sts & USBi_URI )
ci_otg_del_timer ( ci , A_BIDL_ADIS ) ;
if ( intr_sts & USBi_PCI ) {
if ( fsm - > b_bus_suspend = = 1 ) {
ci_otg_del_timer ( ci , A_BIDL_ADIS ) ;
fsm - > b_bus_suspend = 0 ;
}
}
break ;
case OTG_STATE_A_SUSPEND :
if ( ( intr_sts & USBi_PCI ) & & ! port_conn ) {
fsm - > b_conn = 0 ;
/* if gadget driver is binded */
if ( ci - > driver ) {
/* A device to be peripheral mode */
ci - > gadget . is_a_peripheral = 1 ;
}
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
case OTG_STATE_A_HOST :
if ( ( intr_sts & USBi_PCI ) & & ! port_conn ) {
fsm - > b_conn = 0 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
case OTG_STATE_B_WAIT_ACON :
if ( ( intr_sts & USBi_PCI ) & & port_conn ) {
fsm - > a_conn = 1 ;
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
break ;
default :
break ;
}
}
/*
* ci_otg_irq - otg fsm related irq handling
* and also update otg fsm variable by monitoring usb host and udc
* state change interrupts .
* @ ci : ci_hdrc
*/
irqreturn_t ci_otg_fsm_irq ( struct ci_hdrc * ci )
{
irqreturn_t retval = IRQ_NONE ;
u32 otgsc , otg_int_src = 0 ;
struct otg_fsm * fsm = & ci - > fsm ;
otgsc = hw_read_otgsc ( ci , ~ 0 ) ;
otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & ( otgsc > > 8 ) ;
fsm - > id = ( otgsc & OTGSC_ID ) ? 1 : 0 ;
if ( otg_int_src ) {
2015-03-20 16:28:06 +08:00
if ( otg_int_src & OTGSC_DPIS ) {
2014-04-23 15:56:50 +08:00
hw_write_otgsc ( ci , OTGSC_DPIS , OTGSC_DPIS ) ;
fsm - > a_srp_det = 1 ;
fsm - > a_bus_drop = 0 ;
} else if ( otg_int_src & OTGSC_IDIS ) {
hw_write_otgsc ( ci , OTGSC_IDIS , OTGSC_IDIS ) ;
if ( fsm - > id = = 0 ) {
fsm - > a_bus_drop = 0 ;
fsm - > a_bus_req = 1 ;
ci - > id_event = true ;
}
} else if ( otg_int_src & OTGSC_BSVIS ) {
hw_write_otgsc ( ci , OTGSC_BSVIS , OTGSC_BSVIS ) ;
if ( otgsc & OTGSC_BSV ) {
fsm - > b_sess_vld = 1 ;
ci_otg_del_timer ( ci , B_SSEND_SRP ) ;
ci_otg_del_timer ( ci , B_SRP_FAIL ) ;
fsm - > b_ssend_srp = 0 ;
} else {
fsm - > b_sess_vld = 0 ;
if ( fsm - > id )
ci_otg_add_timer ( ci , B_SSEND_SRP ) ;
}
} else if ( otg_int_src & OTGSC_AVVIS ) {
hw_write_otgsc ( ci , OTGSC_AVVIS , OTGSC_AVVIS ) ;
if ( otgsc & OTGSC_AVV ) {
fsm - > a_vbus_vld = 1 ;
} else {
fsm - > a_vbus_vld = 0 ;
fsm - > b_conn = 0 ;
}
}
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
return IRQ_HANDLED ;
}
ci_otg_fsm_event ( ci ) ;
return retval ;
}
void ci_hdrc_otg_fsm_start ( struct ci_hdrc * ci )
{
2014-05-23 08:12:49 +08:00
ci_otg_queue_work ( ci ) ;
2014-04-23 15:56:50 +08:00
}
2014-04-23 15:56:44 +08:00
int ci_hdrc_otg_fsm_init ( struct ci_hdrc * ci )
{
2014-04-23 15:56:49 +08:00
int retval = 0 ;
2014-04-23 15:56:44 +08:00
2014-10-30 18:41:19 +01:00
if ( ci - > phy )
ci - > otg . phy = ci - > phy ;
else
ci - > otg . usb_phy = ci - > usb_phy ;
2014-04-23 15:56:44 +08:00
2014-10-30 18:41:16 +01:00
ci - > otg . gadget = & ci - > gadget ;
ci - > fsm . otg = & ci - > otg ;
2014-04-23 15:56:44 +08:00
ci - > fsm . power_up = 1 ;
ci - > fsm . id = hw_read_otgsc ( ci , OTGSC_ID ) ? 1 : 0 ;
2014-10-30 18:41:13 +01:00
ci - > fsm . otg - > state = OTG_STATE_UNDEFINED ;
2014-04-23 15:56:48 +08:00
ci - > fsm . ops = & ci_otg_ops ;
2016-02-19 10:04:46 +08:00
ci - > gadget . hnp_polling_support = 1 ;
ci - > fsm . host_req_flag = devm_kzalloc ( ci - > dev , 1 , GFP_KERNEL ) ;
if ( ! ci - > fsm . host_req_flag )
return - ENOMEM ;
2014-04-23 15:56:44 +08:00
mutex_init ( & ci - > fsm . lock ) ;
2014-04-23 15:56:49 +08:00
retval = ci_otg_init_timers ( ci ) ;
if ( retval ) {
dev_err ( ci - > dev , " Couldn't init OTG timers \n " ) ;
return retval ;
}
2015-03-20 16:28:06 +08:00
ci - > enabled_otg_timer_bits = 0 ;
ci - > next_otg_timer = NUM_OTG_FSM_TIMERS ;
2014-04-23 15:56:49 +08:00
2014-04-23 15:56:51 +08:00
retval = sysfs_create_group ( & ci - > dev - > kobj , & inputs_attr_group ) ;
if ( retval < 0 ) {
dev_dbg ( ci - > dev ,
" Can't register sysfs attr group: %d \n " , retval ) ;
return retval ;
}
2014-04-23 15:56:44 +08:00
/* Enable A vbus valid irq */
hw_write_otgsc ( ci , OTGSC_AVVIE , OTGSC_AVVIE ) ;
if ( ci - > fsm . id ) {
ci - > fsm . b_ssend_srp =
hw_read_otgsc ( ci , OTGSC_BSV ) ? 0 : 1 ;
ci - > fsm . b_sess_vld =
hw_read_otgsc ( ci , OTGSC_BSV ) ? 1 : 0 ;
/* Enable BSV irq */
hw_write_otgsc ( ci , OTGSC_BSVIE , OTGSC_BSVIE ) ;
}
return 0 ;
}
2014-04-23 15:56:51 +08:00
void ci_hdrc_otg_fsm_remove ( struct ci_hdrc * ci )
{
sysfs_remove_group ( & ci - > dev - > kobj , & inputs_attr_group ) ;
}