2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0+
2011-04-19 00:01:59 +04:00
/*
* Copyright ( C ) 2007 , 2008 Freescale semiconductor , Inc .
*
* Author : Li Yang < LeoLi @ freescale . com >
* Jerry Huang < Chang - Ming . Huang @ freescale . com >
*
* Initialization based on code from Shlomi Gridish .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/proc_fs.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/timer.h>
# include <linux/usb.h>
# include <linux/device.h>
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
# include <linux/workqueue.h>
# include <linux/time.h>
# include <linux/fsl_devices.h>
# include <linux/platform_device.h>
# include <linux/uaccess.h>
# include <asm/unaligned.h>
2013-03-07 19:37:59 +04:00
# include "phy-fsl-usb.h"
2011-04-19 00:01:59 +04:00
2017-03-30 09:46:41 +03:00
# ifdef VERBOSE
# define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
__func__ , # # args )
# else
# define VDBG(stuff...) do {} while (0)
# endif
2011-04-19 00:01:59 +04:00
# define DRIVER_VERSION "Rev. 1.55"
# define DRIVER_AUTHOR "Jerry Huang / Li Yang"
# define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
# define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION
static const char driver_name [ ] = " fsl-usb2-otg " ;
const pm_message_t otg_suspend_state = {
. event = 1 ,
} ;
# define HA_DATA_PULSE
static struct usb_dr_mmap * usb_dr_regs ;
static struct fsl_otg * fsl_otg_dev ;
static int srp_wait_done ;
/* FSM timers */
struct fsl_otg_timer * a_wait_vrise_tmr , * a_wait_bcon_tmr , * a_aidl_bdis_tmr ,
* b_ase0_brst_tmr , * b_se0_srp_tmr ;
/* Driver specific timers */
struct fsl_otg_timer * b_data_pulse_tmr , * b_vbus_pulse_tmr , * b_srp_fail_tmr ,
* b_srp_wait_tmr , * a_wait_enum_tmr ;
static struct list_head active_timers ;
2019-08-19 10:42:34 +03:00
static const struct fsl_otg_config fsl_otg_initdata = {
2011-04-19 00:01:59 +04:00
. otg_port = 1 ,
} ;
# ifdef CONFIG_PPC32
static u32 _fsl_readl_be ( const unsigned __iomem * p )
{
return in_be32 ( p ) ;
}
static u32 _fsl_readl_le ( const unsigned __iomem * p )
{
return in_le32 ( p ) ;
}
static void _fsl_writel_be ( u32 v , unsigned __iomem * p )
{
out_be32 ( p , v ) ;
}
static void _fsl_writel_le ( u32 v , unsigned __iomem * p )
{
out_le32 ( p , v ) ;
}
static u32 ( * _fsl_readl ) ( const unsigned __iomem * p ) ;
static void ( * _fsl_writel ) ( u32 v , unsigned __iomem * p ) ;
# define fsl_readl(p) (*_fsl_readl)((p))
# define fsl_writel(v, p) (*_fsl_writel)((v), (p))
# else
# define fsl_readl(addr) readl(addr)
# define fsl_writel(val, addr) writel(val, addr)
# endif /* CONFIG_PPC32 */
int write_ulpi ( u8 addr , u8 data )
{
u32 temp ;
temp = 0x60000000 | ( addr < < 16 ) | data ;
fsl_writel ( temp , & usb_dr_regs - > ulpiview ) ;
return 0 ;
}
/* -------------------------------------------------------------*/
/* Operations that will be called from OTG Finite State Machine */
/* Charge vbus for vbus pulsing in SRP */
2013-10-03 07:42:04 +04:00
void fsl_otg_chrg_vbus ( struct otg_fsm * fsm , int on )
2011-04-19 00:01:59 +04:00
{
u32 tmp ;
tmp = fsl_readl ( & usb_dr_regs - > otgsc ) & ~ OTGSC_INTSTS_MASK ;
if ( on )
/* stop discharging, start charging */
tmp = ( tmp & ~ OTGSC_CTRL_VBUS_DISCHARGE ) |
OTGSC_CTRL_VBUS_CHARGE ;
else
/* stop charging */
tmp & = ~ OTGSC_CTRL_VBUS_CHARGE ;
fsl_writel ( tmp , & usb_dr_regs - > otgsc ) ;
}
/* Discharge vbus through a resistor to ground */
void fsl_otg_dischrg_vbus ( int on )
{
u32 tmp ;
tmp = fsl_readl ( & usb_dr_regs - > otgsc ) & ~ OTGSC_INTSTS_MASK ;
if ( on )
/* stop charging, start discharging */
tmp = ( tmp & ~ OTGSC_CTRL_VBUS_CHARGE ) |
OTGSC_CTRL_VBUS_DISCHARGE ;
else
/* stop discharging */
tmp & = ~ OTGSC_CTRL_VBUS_DISCHARGE ;
fsl_writel ( tmp , & usb_dr_regs - > otgsc ) ;
}
/* A-device driver vbus, controlled through PP bit in PORTSC */
2013-10-03 07:42:04 +04:00
void fsl_otg_drv_vbus ( struct otg_fsm * fsm , int on )
2011-04-19 00:01:59 +04:00
{
u32 tmp ;
if ( on ) {
tmp = fsl_readl ( & usb_dr_regs - > portsc ) & ~ PORTSC_W1C_BITS ;
fsl_writel ( tmp | PORTSC_PORT_POWER , & usb_dr_regs - > portsc ) ;
} else {
tmp = fsl_readl ( & usb_dr_regs - > portsc ) &
~ PORTSC_W1C_BITS & ~ PORTSC_PORT_POWER ;
fsl_writel ( tmp , & usb_dr_regs - > portsc ) ;
}
}
/*
* Pull - up D + , signalling connect by periperal . Also used in
* data - line pulsing in SRP
*/
2013-10-03 07:42:04 +04:00
void fsl_otg_loc_conn ( struct otg_fsm * fsm , int on )
2011-04-19 00:01:59 +04:00
{
u32 tmp ;
tmp = fsl_readl ( & usb_dr_regs - > otgsc ) & ~ OTGSC_INTSTS_MASK ;
if ( on )
tmp | = OTGSC_CTRL_DATA_PULSING ;
else
tmp & = ~ OTGSC_CTRL_DATA_PULSING ;
fsl_writel ( tmp , & usb_dr_regs - > otgsc ) ;
}
/*
* Generate SOF by host . This is controlled through suspend / resume the
* port . In host mode , controller will automatically send SOF .
* Suspend will block the data on the port .
*/
2013-10-03 07:42:04 +04:00
void fsl_otg_loc_sof ( struct otg_fsm * fsm , int on )
2011-04-19 00:01:59 +04:00
{
u32 tmp ;
tmp = fsl_readl ( & fsl_otg_dev - > dr_mem_map - > portsc ) & ~ PORTSC_W1C_BITS ;
if ( on )
tmp | = PORTSC_PORT_FORCE_RESUME ;
else
tmp | = PORTSC_PORT_SUSPEND ;
fsl_writel ( tmp , & fsl_otg_dev - > dr_mem_map - > portsc ) ;
}
/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
2013-10-03 07:42:04 +04:00
void fsl_otg_start_pulse ( struct otg_fsm * fsm )
2011-04-19 00:01:59 +04:00
{
u32 tmp ;
srp_wait_done = 0 ;
# ifdef HA_DATA_PULSE
tmp = fsl_readl ( & usb_dr_regs - > otgsc ) & ~ OTGSC_INTSTS_MASK ;
tmp | = OTGSC_HA_DATA_PULSE ;
fsl_writel ( tmp , & usb_dr_regs - > otgsc ) ;
# else
fsl_otg_loc_conn ( 1 ) ;
# endif
2013-10-03 07:42:04 +04:00
fsl_otg_add_timer ( fsm , b_data_pulse_tmr ) ;
2011-04-19 00:01:59 +04:00
}
void b_data_pulse_end ( unsigned long foo )
{
# ifdef HA_DATA_PULSE
# else
fsl_otg_loc_conn ( 0 ) ;
# endif
/* Do VBUS pulse after data pulse */
fsl_otg_pulse_vbus ( ) ;
}
void fsl_otg_pulse_vbus ( void )
{
srp_wait_done = 0 ;
2013-10-03 07:42:04 +04:00
fsl_otg_chrg_vbus ( & fsl_otg_dev - > fsm , 1 ) ;
2011-04-19 00:01:59 +04:00
/* start the timer to end vbus charge */
2013-10-03 07:42:04 +04:00
fsl_otg_add_timer ( & fsl_otg_dev - > fsm , b_vbus_pulse_tmr ) ;
2011-04-19 00:01:59 +04:00
}
void b_vbus_pulse_end ( unsigned long foo )
{
2013-10-03 07:42:04 +04:00
fsl_otg_chrg_vbus ( & fsl_otg_dev - > fsm , 0 ) ;
2011-04-19 00:01:59 +04:00
/*
* As USB3300 using the same a_sess_vld and b_sess_vld voltage
* we need to discharge the bus for a while to distinguish
* residual voltage of vbus pulsing and A device pull up
*/
fsl_otg_dischrg_vbus ( 1 ) ;
2013-10-03 07:42:04 +04:00
fsl_otg_add_timer ( & fsl_otg_dev - > fsm , b_srp_wait_tmr ) ;
2011-04-19 00:01:59 +04:00
}
void b_srp_end ( unsigned long foo )
{
fsl_otg_dischrg_vbus ( 0 ) ;
srp_wait_done = 1 ;
2014-11-12 17:31:40 +03:00
if ( ( fsl_otg_dev - > phy . otg - > state = = OTG_STATE_B_SRP_INIT ) & &
2011-04-19 00:01:59 +04:00
fsl_otg_dev - > fsm . b_sess_vld )
fsl_otg_dev - > fsm . b_srp_done = 1 ;
}
/*
* Workaround for a_host suspending too fast . When a_bus_req = 0 ,
* a_host will start by SRP . It needs to set b_hnp_enable before
* actually suspending to start HNP
*/
void a_wait_enum ( unsigned long foo )
{
VDBG ( " a_wait_enum timeout \n " ) ;
2012-02-13 15:24:06 +04:00
if ( ! fsl_otg_dev - > phy . otg - > host - > b_hnp_enable )
2013-10-03 07:42:04 +04:00
fsl_otg_add_timer ( & fsl_otg_dev - > fsm , a_wait_enum_tmr ) ;
2011-04-19 00:01:59 +04:00
else
otg_statemachine ( & fsl_otg_dev - > fsm ) ;
}
/* The timeout callback function to set time out bit */
void set_tmout ( unsigned long indicator )
{
* ( int * ) indicator = 1 ;
}
/* Initialize timers */
int fsl_otg_init_timers ( struct otg_fsm * fsm )
{
/* FSM used timers */
a_wait_vrise_tmr = otg_timer_initializer ( & set_tmout , TA_WAIT_VRISE ,
( unsigned long ) & fsm - > a_wait_vrise_tmout ) ;
if ( ! a_wait_vrise_tmr )
return - ENOMEM ;
a_wait_bcon_tmr = otg_timer_initializer ( & set_tmout , TA_WAIT_BCON ,
( unsigned long ) & fsm - > a_wait_bcon_tmout ) ;
if ( ! a_wait_bcon_tmr )
return - ENOMEM ;
a_aidl_bdis_tmr = otg_timer_initializer ( & set_tmout , TA_AIDL_BDIS ,
( unsigned long ) & fsm - > a_aidl_bdis_tmout ) ;
if ( ! a_aidl_bdis_tmr )
return - ENOMEM ;
b_ase0_brst_tmr = otg_timer_initializer ( & set_tmout , TB_ASE0_BRST ,
( unsigned long ) & fsm - > b_ase0_brst_tmout ) ;
if ( ! b_ase0_brst_tmr )
return - ENOMEM ;
b_se0_srp_tmr = otg_timer_initializer ( & set_tmout , TB_SE0_SRP ,
( unsigned long ) & fsm - > b_se0_srp ) ;
if ( ! b_se0_srp_tmr )
return - ENOMEM ;
b_srp_fail_tmr = otg_timer_initializer ( & set_tmout , TB_SRP_FAIL ,
( unsigned long ) & fsm - > b_srp_done ) ;
if ( ! b_srp_fail_tmr )
return - ENOMEM ;
a_wait_enum_tmr = otg_timer_initializer ( & a_wait_enum , 10 ,
( unsigned long ) & fsm ) ;
if ( ! a_wait_enum_tmr )
return - ENOMEM ;
/* device driver used timers */
b_srp_wait_tmr = otg_timer_initializer ( & b_srp_end , TB_SRP_WAIT , 0 ) ;
if ( ! b_srp_wait_tmr )
return - ENOMEM ;
b_data_pulse_tmr = otg_timer_initializer ( & b_data_pulse_end ,
TB_DATA_PLS , 0 ) ;
if ( ! b_data_pulse_tmr )
return - ENOMEM ;
b_vbus_pulse_tmr = otg_timer_initializer ( & b_vbus_pulse_end ,
TB_VBUS_PLS , 0 ) ;
if ( ! b_vbus_pulse_tmr )
return - ENOMEM ;
return 0 ;
}
/* Uninitialize timers */
void fsl_otg_uninit_timers ( void )
{
/* FSM used timers */
2013-03-05 23:34:50 +04:00
kfree ( a_wait_vrise_tmr ) ;
kfree ( a_wait_bcon_tmr ) ;
kfree ( a_aidl_bdis_tmr ) ;
kfree ( b_ase0_brst_tmr ) ;
kfree ( b_se0_srp_tmr ) ;
kfree ( b_srp_fail_tmr ) ;
kfree ( a_wait_enum_tmr ) ;
2011-04-19 00:01:59 +04:00
/* device driver used timers */
2013-03-05 23:34:50 +04:00
kfree ( b_srp_wait_tmr ) ;
kfree ( b_data_pulse_tmr ) ;
kfree ( b_vbus_pulse_tmr ) ;
2011-04-19 00:01:59 +04:00
}
2013-10-03 07:42:04 +04:00
static struct fsl_otg_timer * fsl_otg_get_timer ( enum otg_fsm_timer t )
{
struct fsl_otg_timer * timer ;
/* REVISIT: use array of pointers to timers instead */
switch ( t ) {
case A_WAIT_VRISE :
timer = a_wait_vrise_tmr ;
break ;
case A_WAIT_BCON :
timer = a_wait_vrise_tmr ;
break ;
case A_AIDL_BDIS :
timer = a_wait_vrise_tmr ;
break ;
case B_ASE0_BRST :
timer = a_wait_vrise_tmr ;
break ;
case B_SE0_SRP :
timer = a_wait_vrise_tmr ;
break ;
case B_SRP_FAIL :
timer = a_wait_vrise_tmr ;
break ;
case A_WAIT_ENUM :
timer = a_wait_vrise_tmr ;
break ;
default :
timer = NULL ;
}
return timer ;
}
2011-04-19 00:01:59 +04:00
/* Add timer to timer list */
2013-10-03 07:42:04 +04:00
void fsl_otg_add_timer ( struct otg_fsm * fsm , void * gtimer )
2011-04-19 00:01:59 +04:00
{
struct fsl_otg_timer * timer = gtimer ;
struct fsl_otg_timer * tmp_timer ;
/*
* Check if the timer is already in the active list ,
* if so update timer count
*/
list_for_each_entry ( tmp_timer , & active_timers , list )
if ( tmp_timer = = timer ) {
timer - > count = timer - > expires ;
return ;
}
timer - > count = timer - > expires ;
list_add_tail ( & timer - > list , & active_timers ) ;
}
2013-10-03 07:42:04 +04:00
static void fsl_otg_fsm_add_timer ( struct otg_fsm * fsm , enum otg_fsm_timer t )
{
struct fsl_otg_timer * timer ;
timer = fsl_otg_get_timer ( t ) ;
if ( ! timer )
return ;
fsl_otg_add_timer ( fsm , timer ) ;
}
2011-04-19 00:01:59 +04:00
/* Remove timer from the timer list; clear timeout status */
2013-10-03 07:42:04 +04:00
void fsl_otg_del_timer ( struct otg_fsm * fsm , void * gtimer )
2011-04-19 00:01:59 +04:00
{
struct fsl_otg_timer * timer = gtimer ;
struct fsl_otg_timer * tmp_timer , * del_tmp ;
list_for_each_entry_safe ( tmp_timer , del_tmp , & active_timers , list )
if ( tmp_timer = = timer )
list_del ( & timer - > list ) ;
}
2013-10-03 07:42:04 +04:00
static void fsl_otg_fsm_del_timer ( struct otg_fsm * fsm , enum otg_fsm_timer t )
{
struct fsl_otg_timer * timer ;
timer = fsl_otg_get_timer ( t ) ;
if ( ! timer )
return ;
fsl_otg_del_timer ( fsm , timer ) ;
}
2011-04-19 00:01:59 +04:00
/* Reset controller, not reset the bus */
void otg_reset_controller ( void )
{
u32 command ;
command = fsl_readl ( & usb_dr_regs - > usbcmd ) ;
command | = ( 1 < < 1 ) ;
fsl_writel ( command , & usb_dr_regs - > usbcmd ) ;
while ( fsl_readl ( & usb_dr_regs - > usbcmd ) & ( 1 < < 1 ) )
;
}
/* Call suspend/resume routines in host driver */
int fsl_otg_start_host ( struct otg_fsm * fsm , int on )
{
2012-02-13 15:24:06 +04:00
struct usb_otg * otg = fsm - > otg ;
2011-04-19 00:01:59 +04:00
struct device * dev ;
2014-10-30 20:41:14 +03:00
struct fsl_otg * otg_dev =
container_of ( otg - > usb_phy , struct fsl_otg , phy ) ;
2011-04-19 00:01:59 +04:00
u32 retval = 0 ;
2012-02-13 15:24:06 +04:00
if ( ! otg - > host )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
2012-02-13 15:24:06 +04:00
dev = otg - > host - > controller ;
2011-04-19 00:01:59 +04:00
/*
* Update a_vbus_vld state as a_vbus_vld int is disabled
* in device mode
*/
fsm - > a_vbus_vld =
! ! ( fsl_readl ( & usb_dr_regs - > otgsc ) & OTGSC_STS_A_VBUS_VALID ) ;
if ( on ) {
/* start fsl usb host controller */
if ( otg_dev - > host_working )
goto end ;
else {
otg_reset_controller ( ) ;
VDBG ( " host on...... \n " ) ;
if ( dev - > driver - > pm & & dev - > driver - > pm - > resume ) {
retval = dev - > driver - > pm - > resume ( dev ) ;
if ( fsm - > id ) {
/* default-b */
2013-10-03 07:42:04 +04:00
fsl_otg_drv_vbus ( fsm , 1 ) ;
2011-04-19 00:01:59 +04:00
/*
* Workaround : b_host can ' t driver
* vbus , but PP in PORTSC needs to
* be 1 for host to work .
* So we set drv_vbus bit in
* transceiver to 0 thru ULPI .
*/
write_ulpi ( 0x0c , 0x20 ) ;
}
}
otg_dev - > host_working = 1 ;
}
} else {
/* stop fsl usb host controller */
if ( ! otg_dev - > host_working )
goto end ;
else {
VDBG ( " host off...... \n " ) ;
if ( dev & & dev - > driver ) {
if ( dev - > driver - > pm & & dev - > driver - > pm - > suspend )
retval = dev - > driver - > pm - > suspend ( dev ) ;
if ( fsm - > id )
/* default-b */
2013-10-03 07:42:04 +04:00
fsl_otg_drv_vbus ( fsm , 0 ) ;
2011-04-19 00:01:59 +04:00
}
otg_dev - > host_working = 0 ;
}
}
end :
return retval ;
}
/*
* Call suspend and resume function in udc driver
* to stop and start udc driver .
*/
int fsl_otg_start_gadget ( struct otg_fsm * fsm , int on )
{
2012-02-13 15:24:06 +04:00
struct usb_otg * otg = fsm - > otg ;
2011-04-19 00:01:59 +04:00
struct device * dev ;
2012-02-13 15:24:06 +04:00
if ( ! otg - > gadget | | ! otg - > gadget - > dev . parent )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
VDBG ( " gadget %s \n " , on ? " on " : " off " ) ;
2012-02-13 15:24:06 +04:00
dev = otg - > gadget - > dev . parent ;
2011-04-19 00:01:59 +04:00
if ( on ) {
if ( dev - > driver - > resume )
dev - > driver - > resume ( dev ) ;
} else {
if ( dev - > driver - > suspend )
dev - > driver - > suspend ( dev , otg_suspend_state ) ;
}
return 0 ;
}
/*
* Called by initialization code of host driver . Register host controller
* to the OTG . Suspend host for OTG role detection .
*/
2012-02-13 15:24:06 +04:00
static int fsl_otg_set_host ( struct usb_otg * otg , struct usb_bus * host )
2011-04-19 00:01:59 +04:00
{
2012-09-10 08:42:31 +04:00
struct fsl_otg * otg_dev ;
if ( ! otg )
return - ENODEV ;
2011-04-19 00:01:59 +04:00
2014-10-30 20:41:14 +03:00
otg_dev = container_of ( otg - > usb_phy , struct fsl_otg , phy ) ;
2012-09-10 08:42:31 +04:00
if ( otg_dev ! = fsl_otg_dev )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
2012-02-13 15:24:06 +04:00
otg - > host = host ;
2011-04-19 00:01:59 +04:00
otg_dev - > fsm . a_bus_drop = 0 ;
otg_dev - > fsm . a_bus_req = 1 ;
if ( host ) {
VDBG ( " host off...... \n " ) ;
2012-02-13 15:24:06 +04:00
otg - > host - > otg_port = fsl_otg_initdata . otg_port ;
otg - > host - > is_b_host = otg_dev - > fsm . id ;
2011-04-19 00:01:59 +04:00
/*
2014-09-19 19:32:23 +04:00
* must leave time for hub_wq to finish its thing
2011-04-19 00:01:59 +04:00
* before yanking the host driver out from under it ,
* so suspend the host after a short delay .
*/
otg_dev - > host_working = 1 ;
schedule_delayed_work ( & otg_dev - > otg_event , 100 ) ;
return 0 ;
} else {
/* host driver going away */
if ( ! ( fsl_readl ( & otg_dev - > dr_mem_map - > otgsc ) &
OTGSC_STS_USB_ID ) ) {
/* Mini-A cable connected */
struct otg_fsm * fsm = & otg_dev - > fsm ;
2014-11-12 17:31:40 +03:00
otg - > state = OTG_STATE_UNDEFINED ;
2011-04-19 00:01:59 +04:00
fsm - > protocol = PROTO_UNDEF ;
}
}
otg_dev - > host_working = 0 ;
otg_statemachine ( & otg_dev - > fsm ) ;
return 0 ;
}
/* Called by initialization code of udc. Register udc to OTG. */
2012-02-13 15:24:06 +04:00
static int fsl_otg_set_peripheral ( struct usb_otg * otg ,
struct usb_gadget * gadget )
2011-04-19 00:01:59 +04:00
{
2012-09-10 08:42:31 +04:00
struct fsl_otg * otg_dev ;
2011-04-19 00:01:59 +04:00
2012-09-10 08:42:31 +04:00
if ( ! otg )
return - ENODEV ;
2014-10-30 20:41:14 +03:00
otg_dev = container_of ( otg - > usb_phy , struct fsl_otg , phy ) ;
2011-04-19 00:01:59 +04:00
VDBG ( " otg_dev 0x%x \n " , ( int ) otg_dev ) ;
VDBG ( " fsl_otg_dev 0x%x \n " , ( int ) fsl_otg_dev ) ;
2012-09-10 08:42:31 +04:00
if ( otg_dev ! = fsl_otg_dev )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
if ( ! gadget ) {
2012-02-13 15:24:06 +04:00
if ( ! otg - > default_a )
otg - > gadget - > ops - > vbus_draw ( otg - > gadget , 0 ) ;
usb_gadget_vbus_disconnect ( otg - > gadget ) ;
otg - > gadget = 0 ;
2011-04-19 00:01:59 +04:00
otg_dev - > fsm . b_bus_req = 0 ;
otg_statemachine ( & otg_dev - > fsm ) ;
return 0 ;
}
2012-02-13 15:24:06 +04:00
otg - > gadget = gadget ;
otg - > gadget - > is_a_peripheral = ! otg_dev - > fsm . id ;
2011-04-19 00:01:59 +04:00
otg_dev - > fsm . b_bus_req = 1 ;
/* start the gadget right away if the ID pin says Mini-B */
2013-06-28 22:32:55 +04:00
pr_debug ( " ID pin=%d \n " , otg_dev - > fsm . id ) ;
2011-04-19 00:01:59 +04:00
if ( otg_dev - > fsm . id = = 1 ) {
fsl_otg_start_host ( & otg_dev - > fsm , 0 ) ;
otg_drv_vbus ( & otg_dev - > fsm , 0 ) ;
fsl_otg_start_gadget ( & otg_dev - > fsm , 1 ) ;
}
return 0 ;
}
/*
* Delayed pin detect interrupt processing .
*
* When the Mini - A cable is disconnected from the board ,
2011-11-29 08:31:00 +04:00
* the pin - detect interrupt happens before the disconnect
2011-04-19 00:01:59 +04:00
* interrupts for the connected device ( s ) . In order to
* process the disconnect interrupt ( s ) prior to switching
* roles , the pin - detect interrupts are delayed , and handled
* by this routine .
*/
static void fsl_otg_event ( struct work_struct * work )
{
struct fsl_otg * og = container_of ( work , struct fsl_otg , otg_event . work ) ;
struct otg_fsm * fsm = & og - > fsm ;
if ( fsm - > id ) { /* switch to gadget */
fsl_otg_start_host ( fsm , 0 ) ;
otg_drv_vbus ( fsm , 0 ) ;
fsl_otg_start_gadget ( fsm , 1 ) ;
}
}
/* B-device start SRP */
2012-02-13 15:24:06 +04:00
static int fsl_otg_start_srp ( struct usb_otg * otg )
2011-04-19 00:01:59 +04:00
{
2012-09-10 08:42:31 +04:00
struct fsl_otg * otg_dev ;
2014-11-12 17:31:40 +03:00
if ( ! otg | | otg - > state ! = OTG_STATE_B_IDLE )
2012-09-10 08:42:31 +04:00
return - ENODEV ;
2011-04-19 00:01:59 +04:00
2014-10-30 20:41:14 +03:00
otg_dev = container_of ( otg - > usb_phy , struct fsl_otg , phy ) ;
2012-09-10 08:42:31 +04:00
if ( otg_dev ! = fsl_otg_dev )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
otg_dev - > fsm . b_bus_req = 1 ;
otg_statemachine ( & otg_dev - > fsm ) ;
return 0 ;
}
/* A_host suspend will call this function to start hnp */
2012-02-13 15:24:06 +04:00
static int fsl_otg_start_hnp ( struct usb_otg * otg )
2011-04-19 00:01:59 +04:00
{
2012-09-10 08:42:31 +04:00
struct fsl_otg * otg_dev ;
if ( ! otg )
return - ENODEV ;
2011-04-19 00:01:59 +04:00
2014-10-30 20:41:14 +03:00
otg_dev = container_of ( otg - > usb_phy , struct fsl_otg , phy ) ;
2012-09-10 08:42:31 +04:00
if ( otg_dev ! = fsl_otg_dev )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
2013-06-28 22:32:55 +04:00
pr_debug ( " start_hnp... \n " ) ;
2011-04-19 00:01:59 +04:00
/* clear a_bus_req to enter a_suspend state */
otg_dev - > fsm . a_bus_req = 0 ;
otg_statemachine ( & otg_dev - > fsm ) ;
return 0 ;
}
/*
* Interrupt handler . OTG / host / peripheral share the same int line .
* OTG driver clears OTGSC interrupts and leaves USB interrupts
* intact . It needs to have knowledge of some USB interrupts
* such as port change .
*/
irqreturn_t fsl_otg_isr ( int irq , void * dev_id )
{
struct otg_fsm * fsm = & ( ( struct fsl_otg * ) dev_id ) - > fsm ;
2012-02-13 15:24:06 +04:00
struct usb_otg * otg = ( ( struct fsl_otg * ) dev_id ) - > phy . otg ;
2011-04-19 00:01:59 +04:00
u32 otg_int_src , otg_sc ;
otg_sc = fsl_readl ( & usb_dr_regs - > otgsc ) ;
otg_int_src = otg_sc & OTGSC_INTSTS_MASK & ( otg_sc > > 8 ) ;
/* Only clear otg interrupts */
fsl_writel ( otg_sc , & usb_dr_regs - > otgsc ) ;
/*FIXME: ID change not generate when init to 0 */
fsm - > id = ( otg_sc & OTGSC_STS_USB_ID ) ? 1 : 0 ;
otg - > default_a = ( fsm - > id = = 0 ) ;
/* process OTG interrupts */
if ( otg_int_src ) {
if ( otg_int_src & OTGSC_INTSTS_USB_ID ) {
fsm - > id = ( otg_sc & OTGSC_STS_USB_ID ) ? 1 : 0 ;
otg - > default_a = ( fsm - > id = = 0 ) ;
/* clear conn information */
if ( fsm - > id )
fsm - > b_conn = 0 ;
else
fsm - > a_conn = 0 ;
if ( otg - > host )
otg - > host - > is_b_host = fsm - > id ;
if ( otg - > gadget )
otg - > gadget - > is_a_peripheral = ! fsm - > id ;
VDBG ( " ID int (ID is %d) \n " , fsm - > id ) ;
if ( fsm - > id ) { /* switch to gadget */
schedule_delayed_work (
& ( ( struct fsl_otg * ) dev_id ) - > otg_event ,
100 ) ;
} else { /* switch to host */
cancel_delayed_work ( &
( ( struct fsl_otg * ) dev_id ) - >
otg_event ) ;
fsl_otg_start_gadget ( fsm , 0 ) ;
otg_drv_vbus ( fsm , 1 ) ;
fsl_otg_start_host ( fsm , 1 ) ;
}
return IRQ_HANDLED ;
}
}
return IRQ_NONE ;
}
static struct otg_fsm_ops fsl_otg_ops = {
. chrg_vbus = fsl_otg_chrg_vbus ,
. drv_vbus = fsl_otg_drv_vbus ,
. loc_conn = fsl_otg_loc_conn ,
. loc_sof = fsl_otg_loc_sof ,
. start_pulse = fsl_otg_start_pulse ,
2013-10-03 07:42:04 +04:00
. add_timer = fsl_otg_fsm_add_timer ,
. del_timer = fsl_otg_fsm_del_timer ,
2011-04-19 00:01:59 +04:00
. start_host = fsl_otg_start_host ,
. start_gadget = fsl_otg_start_gadget ,
} ;
/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
static int fsl_otg_conf ( struct platform_device * pdev )
{
struct fsl_otg * fsl_otg_tc ;
int status ;
if ( fsl_otg_dev )
return 0 ;
/* allocate space to fsl otg device */
fsl_otg_tc = kzalloc ( sizeof ( struct fsl_otg ) , GFP_KERNEL ) ;
if ( ! fsl_otg_tc )
return - ENOMEM ;
2012-02-13 15:24:06 +04:00
fsl_otg_tc - > phy . otg = kzalloc ( sizeof ( struct usb_otg ) , GFP_KERNEL ) ;
if ( ! fsl_otg_tc - > phy . otg ) {
kfree ( fsl_otg_tc ) ;
return - ENOMEM ;
}
2011-04-19 00:01:59 +04:00
INIT_DELAYED_WORK ( & fsl_otg_tc - > otg_event , fsl_otg_event ) ;
INIT_LIST_HEAD ( & active_timers ) ;
status = fsl_otg_init_timers ( & fsl_otg_tc - > fsm ) ;
if ( status ) {
pr_info ( " Couldn't init OTG timers \n " ) ;
goto err ;
}
2013-11-26 06:46:05 +04:00
mutex_init ( & fsl_otg_tc - > fsm . lock ) ;
2011-04-19 00:01:59 +04:00
/* Set OTG state machine operations */
fsl_otg_tc - > fsm . ops = & fsl_otg_ops ;
/* initialize the otg structure */
2012-02-13 15:24:06 +04:00
fsl_otg_tc - > phy . label = DRIVER_DESC ;
2013-05-10 13:45:08 +04:00
fsl_otg_tc - > phy . dev = & pdev - > dev ;
2012-02-13 15:24:06 +04:00
2014-10-30 20:41:14 +03:00
fsl_otg_tc - > phy . otg - > usb_phy = & fsl_otg_tc - > phy ;
2012-02-13 15:24:06 +04:00
fsl_otg_tc - > phy . otg - > set_host = fsl_otg_set_host ;
fsl_otg_tc - > phy . otg - > set_peripheral = fsl_otg_set_peripheral ;
fsl_otg_tc - > phy . otg - > start_hnp = fsl_otg_start_hnp ;
fsl_otg_tc - > phy . otg - > start_srp = fsl_otg_start_srp ;
2011-04-19 00:01:59 +04:00
fsl_otg_dev = fsl_otg_tc ;
/* Store the otg transceiver */
2012-06-22 15:32:46 +04:00
status = usb_add_phy ( & fsl_otg_tc - > phy , USB_PHY_TYPE_USB2 ) ;
2011-04-19 00:01:59 +04:00
if ( status ) {
pr_warn ( FSL_OTG_NAME " : unable to register OTG transceiver. \n " ) ;
goto err ;
}
return 0 ;
err :
fsl_otg_uninit_timers ( ) ;
2012-02-13 15:24:06 +04:00
kfree ( fsl_otg_tc - > phy . otg ) ;
2011-04-19 00:01:59 +04:00
kfree ( fsl_otg_tc ) ;
return status ;
}
/* OTG Initialization */
int usb_otg_start ( struct platform_device * pdev )
{
struct fsl_otg * p_otg ;
2012-06-22 15:32:46 +04:00
struct usb_phy * otg_trans = usb_get_phy ( USB_PHY_TYPE_USB2 ) ;
2011-04-19 00:01:59 +04:00
struct otg_fsm * fsm ;
int status ;
struct resource * res ;
u32 temp ;
2013-07-30 12:02:13 +04:00
struct fsl_usb2_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2011-04-19 00:01:59 +04:00
2012-02-13 15:24:06 +04:00
p_otg = container_of ( otg_trans , struct fsl_otg , phy ) ;
2011-04-19 00:01:59 +04:00
fsm = & p_otg - > fsm ;
/* Initialize the state machine structure with default values */
SET_OTG_STATE ( otg_trans , OTG_STATE_UNDEFINED ) ;
2012-02-13 15:24:06 +04:00
fsm - > otg = p_otg - > phy . otg ;
2011-04-19 00:01:59 +04:00
/* We don't require predefined MEM/IRQ resource index */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENXIO ;
/* We don't request_mem_region here to enable resource sharing
* with host / device */
usb_dr_regs = ioremap ( res - > start , sizeof ( struct usb_dr_mmap ) ) ;
p_otg - > dr_mem_map = ( struct usb_dr_mmap * ) usb_dr_regs ;
pdata - > regs = ( void * ) usb_dr_regs ;
if ( pdata - > init & & pdata - > init ( pdev ) ! = 0 )
return - EINVAL ;
usb/phy: fix PPC64 build errors in phy-fsl-usb.c
Fix build errors when built for PPC64:
These variables are only used on PPC32 so they don't need to be
initialized for PPC64.
../drivers/usb/phy/phy-fsl-usb.c: In function 'usb_otg_start':
../drivers/usb/phy/phy-fsl-usb.c:865:3: error: '_fsl_readl' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_be;
../drivers/usb/phy/phy-fsl-usb.c:865:16: error: '_fsl_readl_be' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_be;
../drivers/usb/phy/phy-fsl-usb.c:866:3: error: '_fsl_writel' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_be;
../drivers/usb/phy/phy-fsl-usb.c:866:17: error: '_fsl_writel_be' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_be;
../drivers/usb/phy/phy-fsl-usb.c:868:16: error: '_fsl_readl_le' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_le;
../drivers/usb/phy/phy-fsl-usb.c:869:17: error: '_fsl_writel_le' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_le;
and the sysfs "show" function return type should be ssize_t, not int:
../drivers/usb/phy/phy-fsl-usb.c:1042:49: error: initialization of 'ssize_t (*)(struct device *, struct device_attribute *, char *)' {aka 'long int (*)(struct device *, struct device_attribute *, char *)'} from incompatible pointer type 'int (*)(struct device *, struct device_attribute *, char *)' [-Werror=incompatible-pointer-types]
static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
Cc: Felipe Balbi <balbi@kernel.org>
Cc: linux-usb@vger.kernel.org
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-07-15 20:37:37 +03:00
# ifdef CONFIG_PPC32
2011-04-19 00:01:59 +04:00
if ( pdata - > big_endian_mmio ) {
_fsl_readl = _fsl_readl_be ;
_fsl_writel = _fsl_writel_be ;
} else {
_fsl_readl = _fsl_readl_le ;
_fsl_writel = _fsl_writel_le ;
}
usb/phy: fix PPC64 build errors in phy-fsl-usb.c
Fix build errors when built for PPC64:
These variables are only used on PPC32 so they don't need to be
initialized for PPC64.
../drivers/usb/phy/phy-fsl-usb.c: In function 'usb_otg_start':
../drivers/usb/phy/phy-fsl-usb.c:865:3: error: '_fsl_readl' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_be;
../drivers/usb/phy/phy-fsl-usb.c:865:16: error: '_fsl_readl_be' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_be;
../drivers/usb/phy/phy-fsl-usb.c:866:3: error: '_fsl_writel' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_be;
../drivers/usb/phy/phy-fsl-usb.c:866:17: error: '_fsl_writel_be' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_be;
../drivers/usb/phy/phy-fsl-usb.c:868:16: error: '_fsl_readl_le' undeclared (first use in this function); did you mean 'fsl_readl'?
_fsl_readl = _fsl_readl_le;
../drivers/usb/phy/phy-fsl-usb.c:869:17: error: '_fsl_writel_le' undeclared (first use in this function); did you mean 'fsl_writel'?
_fsl_writel = _fsl_writel_le;
and the sysfs "show" function return type should be ssize_t, not int:
../drivers/usb/phy/phy-fsl-usb.c:1042:49: error: initialization of 'ssize_t (*)(struct device *, struct device_attribute *, char *)' {aka 'long int (*)(struct device *, struct device_attribute *, char *)'} from incompatible pointer type 'int (*)(struct device *, struct device_attribute *, char *)' [-Werror=incompatible-pointer-types]
static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
Cc: Felipe Balbi <balbi@kernel.org>
Cc: linux-usb@vger.kernel.org
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-07-15 20:37:37 +03:00
# endif
2011-04-19 00:01:59 +04:00
/* request irq */
p_otg - > irq = platform_get_irq ( pdev , 0 ) ;
2021-08-09 23:50:18 +03:00
if ( p_otg - > irq < 0 )
return p_otg - > irq ;
2011-04-19 00:01:59 +04:00
status = request_irq ( p_otg - > irq , fsl_otg_isr ,
IRQF_SHARED , driver_name , p_otg ) ;
if ( status ) {
2012-02-13 15:24:06 +04:00
dev_dbg ( p_otg - > phy . dev , " can't get IRQ %d, error %d \n " ,
2011-04-19 00:01:59 +04:00
p_otg - > irq , status ) ;
iounmap ( p_otg - > dr_mem_map ) ;
2012-02-13 15:24:06 +04:00
kfree ( p_otg - > phy . otg ) ;
2011-04-19 00:01:59 +04:00
kfree ( p_otg ) ;
return status ;
}
/* stop the controller */
temp = fsl_readl ( & p_otg - > dr_mem_map - > usbcmd ) ;
temp & = ~ USB_CMD_RUN_STOP ;
fsl_writel ( temp , & p_otg - > dr_mem_map - > usbcmd ) ;
/* reset the controller */
temp = fsl_readl ( & p_otg - > dr_mem_map - > usbcmd ) ;
temp | = USB_CMD_CTRL_RESET ;
fsl_writel ( temp , & p_otg - > dr_mem_map - > usbcmd ) ;
/* wait reset completed */
while ( fsl_readl ( & p_otg - > dr_mem_map - > usbcmd ) & USB_CMD_CTRL_RESET )
;
/* configure the VBUSHS as IDLE(both host and device) */
temp = USB_MODE_STREAM_DISABLE | ( pdata - > es ? USB_MODE_ES : 0 ) ;
fsl_writel ( temp , & p_otg - > dr_mem_map - > usbmode ) ;
/* configure PHY interface */
temp = fsl_readl ( & p_otg - > dr_mem_map - > portsc ) ;
temp & = ~ ( PORTSC_PHY_TYPE_SEL | PORTSC_PTW ) ;
switch ( pdata - > phy_mode ) {
case FSL_USB2_PHY_ULPI :
temp | = PORTSC_PTS_ULPI ;
break ;
case FSL_USB2_PHY_UTMI_WIDE :
temp | = PORTSC_PTW_16BIT ;
2020-07-07 23:00:40 +03:00
fallthrough ;
2011-04-19 00:01:59 +04:00
case FSL_USB2_PHY_UTMI :
temp | = PORTSC_PTS_UTMI ;
2020-07-07 23:00:40 +03:00
fallthrough ;
2011-04-19 00:01:59 +04:00
default :
break ;
}
fsl_writel ( temp , & p_otg - > dr_mem_map - > portsc ) ;
if ( pdata - > have_sysif_regs ) {
/* configure control enable IO output, big endian register */
temp = __raw_readl ( & p_otg - > dr_mem_map - > control ) ;
temp | = USB_CTRL_IOENB ;
__raw_writel ( temp , & p_otg - > dr_mem_map - > control ) ;
}
/* disable all interrupt and clear all OTGSC status */
temp = fsl_readl ( & p_otg - > dr_mem_map - > otgsc ) ;
temp & = ~ OTGSC_INTERRUPT_ENABLE_BITS_MASK ;
temp | = OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE ;
fsl_writel ( temp , & p_otg - > dr_mem_map - > otgsc ) ;
/*
* The identification ( id ) input is FALSE when a Mini - A plug is inserted
* in the devices Mini - AB receptacle . Otherwise , this input is TRUE .
* Also : record initial state of ID pin
*/
if ( fsl_readl ( & p_otg - > dr_mem_map - > otgsc ) & OTGSC_STS_USB_ID ) {
2014-11-12 17:31:40 +03:00
p_otg - > phy . otg - > state = OTG_STATE_UNDEFINED ;
2011-04-19 00:01:59 +04:00
p_otg - > fsm . id = 1 ;
} else {
2014-11-12 17:31:40 +03:00
p_otg - > phy . otg - > state = OTG_STATE_A_IDLE ;
2011-04-19 00:01:59 +04:00
p_otg - > fsm . id = 0 ;
}
2013-06-28 22:32:55 +04:00
pr_debug ( " initial ID pin=%d \n " , p_otg - > fsm . id ) ;
2011-04-19 00:01:59 +04:00
/* enable OTG ID pin interrupt */
temp = fsl_readl ( & p_otg - > dr_mem_map - > otgsc ) ;
temp | = OTGSC_INTR_USB_ID_EN ;
temp & = ~ ( OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN ) ;
fsl_writel ( temp , & p_otg - > dr_mem_map - > otgsc ) ;
return 0 ;
}
2012-11-19 22:21:48 +04:00
static int fsl_otg_probe ( struct platform_device * pdev )
2011-04-19 00:01:59 +04:00
{
int ret ;
2013-07-30 12:02:13 +04:00
if ( ! dev_get_platdata ( & pdev - > dev ) )
2011-04-19 00:01:59 +04:00
return - ENODEV ;
/* configure the OTG */
ret = fsl_otg_conf ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Couldn't configure OTG module \n " ) ;
return ret ;
}
/* start OTG */
ret = usb_otg_start ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Can't init FSL OTG device \n " ) ;
return ret ;
}
return ret ;
}
2012-11-19 22:26:20 +04:00
static int fsl_otg_remove ( struct platform_device * pdev )
2011-04-19 00:01:59 +04:00
{
2013-07-30 12:02:13 +04:00
struct fsl_usb2_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2011-04-19 00:01:59 +04:00
2012-06-22 15:32:46 +04:00
usb_remove_phy ( & fsl_otg_dev - > phy ) ;
2011-04-19 00:01:59 +04:00
free_irq ( fsl_otg_dev - > irq , fsl_otg_dev ) ;
iounmap ( ( void * ) usb_dr_regs ) ;
fsl_otg_uninit_timers ( ) ;
2012-02-13 15:24:06 +04:00
kfree ( fsl_otg_dev - > phy . otg ) ;
2011-04-19 00:01:59 +04:00
kfree ( fsl_otg_dev ) ;
if ( pdata - > exit )
pdata - > exit ( pdev ) ;
return 0 ;
}
struct platform_driver fsl_otg_driver = {
. probe = fsl_otg_probe ,
2012-11-19 22:21:08 +04:00
. remove = fsl_otg_remove ,
2011-04-19 00:01:59 +04:00
. driver = {
. name = driver_name ,
. owner = THIS_MODULE ,
} ,
} ;
2011-11-27 16:16:27 +04:00
module_platform_driver ( fsl_otg_driver ) ;
2011-04-19 00:01:59 +04:00
MODULE_DESCRIPTION ( DRIVER_INFO ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_LICENSE ( " GPL " ) ;