2011-04-04 08:44:59 +04:00
/*
* Renesas USB driver
*
* Copyright ( C ) 2011 Renesas Solutions Corp .
* Kuninori Morimoto < kuninori . morimoto . gx @ renesas . com >
*
* 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 St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
*/
# include <linux/interrupt.h>
2012-06-03 20:39:13 +04:00
# include "common.h"
# include "mod.h"
2011-04-04 08:44:59 +04:00
# define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
2011-04-28 11:41:20 +04:00
# define usbhs_mod_info_call(priv, func, param...) \
( { \
struct usbhs_mod_info * info ; \
info = usbhs_priv_to_modinfo ( priv ) ; \
! info - > func ? 0 : \
info - > func ( param ) ; \
} )
/*
* autonomy
*
* these functions are used if platform doesn ' t have external phy .
* - > there is no " notify_hotplug " callback from platform
* - > call " notify_hotplug " by itself
* - > use own interrupt to connect / disconnect
* - > it mean module clock is always ON
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
static int usbhsm_autonomy_get_vbus ( struct platform_device * pdev )
{
struct usbhs_priv * priv = usbhs_pdev_to_priv ( pdev ) ;
return VBSTS & usbhs_read ( priv , INTSTS0 ) ;
}
static int usbhsm_autonomy_irq_vbus ( struct usbhs_priv * priv ,
struct usbhs_irq_state * irq_state )
{
struct platform_device * pdev = usbhs_priv_to_pdev ( priv ) ;
2011-10-24 13:25:48 +04:00
renesas_usbhs_call_notify_hotplug ( pdev ) ;
return 0 ;
2011-04-28 11:41:20 +04:00
}
void usbhs_mod_autonomy_mode ( struct usbhs_priv * priv )
{
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
info - > irq_vbus = usbhsm_autonomy_irq_vbus ;
2011-10-13 08:02:22 +04:00
priv - > pfunc . get_vbus = usbhsm_autonomy_get_vbus ;
2011-04-28 11:41:20 +04:00
usbhs_irq_callback_update ( priv , NULL ) ;
}
2011-04-04 08:44:59 +04:00
/*
* host / gadget functions
*
* renesas_usbhs host / gadget can register itself by below functions .
* these functions are called when probe
*
*/
void usbhs_mod_register ( struct usbhs_priv * priv , struct usbhs_mod * mod , int id )
{
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
info - > mod [ id ] = mod ;
mod - > priv = priv ;
}
struct usbhs_mod * usbhs_mod_get ( struct usbhs_priv * priv , int id )
{
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
struct usbhs_mod * ret = NULL ;
switch ( id ) {
case USBHS_HOST :
case USBHS_GADGET :
ret = info - > mod [ id ] ;
break ;
}
return ret ;
}
2011-10-11 09:02:45 +04:00
int usbhs_mod_is_host ( struct usbhs_priv * priv )
2011-04-04 08:44:59 +04:00
{
2011-10-11 09:02:45 +04:00
struct usbhs_mod * mod = usbhs_mod_get_current ( priv ) ;
2011-04-04 08:44:59 +04:00
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
if ( ! mod )
return - EINVAL ;
return info - > mod [ USBHS_HOST ] = = mod ;
}
struct usbhs_mod * usbhs_mod_get_current ( struct usbhs_priv * priv )
{
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
return info - > curt ;
}
int usbhs_mod_change ( struct usbhs_priv * priv , int id )
{
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
struct usbhs_mod * mod = NULL ;
int ret = 0 ;
/* id < 0 mean no current */
switch ( id ) {
case USBHS_HOST :
case USBHS_GADGET :
mod = info - > mod [ id ] ;
break ;
default :
ret = - EINVAL ;
}
info - > curt = mod ;
return ret ;
}
static irqreturn_t usbhs_interrupt ( int irq , void * data ) ;
int usbhs_mod_probe ( struct usbhs_priv * priv )
{
struct device * dev = usbhs_priv_to_dev ( priv ) ;
int ret ;
2011-04-05 06:40:54 +04:00
/*
* install host / gadget driver
*/
2011-10-11 09:07:40 +04:00
ret = usbhs_mod_host_probe ( priv ) ;
2011-04-05 06:40:54 +04:00
if ( ret < 0 )
return ret ;
2011-10-11 09:07:40 +04:00
ret = usbhs_mod_gadget_probe ( priv ) ;
if ( ret < 0 )
goto mod_init_host_err ;
2011-04-04 08:44:59 +04:00
/* irq settings */
2012-10-29 11:44:41 +04:00
ret = devm_request_irq ( dev , priv - > irq , usbhs_interrupt ,
2012-01-05 10:37:18 +04:00
priv - > irqflags , dev_name ( dev ) , priv ) ;
2011-04-05 06:40:54 +04:00
if ( ret ) {
2011-04-04 08:44:59 +04:00
dev_err ( dev , " irq request err \n " ) ;
2011-04-05 06:40:54 +04:00
goto mod_init_gadget_err ;
}
return ret ;
mod_init_gadget_err :
usbhs_mod_gadget_remove ( priv ) ;
2011-10-11 09:07:40 +04:00
mod_init_host_err :
usbhs_mod_host_remove ( priv ) ;
2011-04-04 08:44:59 +04:00
return ret ;
}
void usbhs_mod_remove ( struct usbhs_priv * priv )
{
2011-10-11 09:07:40 +04:00
usbhs_mod_host_remove ( priv ) ;
2011-04-05 06:40:54 +04:00
usbhs_mod_gadget_remove ( priv ) ;
2011-04-04 08:44:59 +04:00
}
/*
* status functions
*/
int usbhs_status_get_device_state ( struct usbhs_irq_state * irq_state )
{
int state = irq_state - > intsts0 & DVSQ_MASK ;
switch ( state ) {
case POWER_STATE :
case DEFAULT_STATE :
case ADDRESS_STATE :
case CONFIGURATION_STATE :
return state ;
}
return - EIO ;
}
int usbhs_status_get_ctrl_stage ( struct usbhs_irq_state * irq_state )
{
/*
* return value
*
* IDLE_SETUP_STAGE
* READ_DATA_STAGE
* READ_STATUS_STAGE
* WRITE_DATA_STAGE
* WRITE_STATUS_STAGE
* NODATA_STATUS_STAGE
* SEQUENCE_ERROR
*/
return ( int ) irq_state - > intsts0 & CTSQ_MASK ;
}
2012-08-20 13:39:23 +04:00
static int usbhs_status_get_each_irq ( struct usbhs_priv * priv ,
struct usbhs_irq_state * state )
2011-04-04 08:44:59 +04:00
{
struct usbhs_mod * mod = usbhs_mod_get_current ( priv ) ;
2012-08-20 13:39:23 +04:00
u16 intenb0 , intenb1 ;
2014-08-22 15:14:00 +04:00
unsigned long flags ;
2011-04-04 08:44:59 +04:00
2014-08-22 15:14:00 +04:00
/******************** spin lock ********************/
usbhs_lock ( priv , flags ) ;
2011-04-04 08:44:59 +04:00
state - > intsts0 = usbhs_read ( priv , INTSTS0 ) ;
2012-08-20 13:39:23 +04:00
intenb0 = usbhs_read ( priv , INTENB0 ) ;
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) ) {
state - > intsts1 = usbhs_read ( priv , INTSTS1 ) ;
intenb1 = usbhs_read ( priv , INTENB1 ) ;
2015-05-22 14:06:35 +03:00
} else {
state - > intsts1 = intenb1 = 0 ;
2015-01-09 03:28:41 +03:00
}
2012-08-20 13:39:23 +04:00
2011-04-04 08:44:59 +04:00
/* mask */
2011-04-28 11:41:07 +04:00
if ( mod ) {
state - > brdysts = usbhs_read ( priv , BRDYSTS ) ;
state - > nrdysts = usbhs_read ( priv , NRDYSTS ) ;
state - > bempsts = usbhs_read ( priv , BEMPSTS ) ;
state - > bempsts & = mod - > irq_bempsts ;
state - > brdysts & = mod - > irq_brdysts ;
}
2014-08-22 15:14:00 +04:00
usbhs_unlock ( priv , flags ) ;
/******************** spin unlock ******************/
2012-08-20 13:39:23 +04:00
/*
* Check whether the irq enable registers and the irq status are set
* when IRQF_SHARED is set .
*/
if ( priv - > irqflags & IRQF_SHARED ) {
if ( ! ( intenb0 & state - > intsts0 ) & &
! ( intenb1 & state - > intsts1 ) & &
! ( state - > bempsts ) & &
! ( state - > brdysts ) )
return - EIO ;
}
return 0 ;
2011-04-04 08:44:59 +04:00
}
/*
* interrupt
*/
# define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
# define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
static irqreturn_t usbhs_interrupt ( int irq , void * data )
{
struct usbhs_priv * priv = data ;
struct usbhs_irq_state irq_state ;
2012-08-20 13:39:23 +04:00
if ( usbhs_status_get_each_irq ( priv , & irq_state ) < 0 )
return IRQ_NONE ;
2011-04-04 08:44:59 +04:00
/*
* clear interrupt
*
* The hardware is _very_ picky to clear interrupt bit .
* Especially INTSTS0_MAGIC , INTSTS1_MAGIC value .
*
* see
* " Operation "
* - " Control Transfer (DCP) "
* - Function : : VALID bit should 0
*/
usbhs_write ( priv , INTSTS0 , ~ irq_state . intsts0 & INTSTS0_MAGIC ) ;
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) )
usbhs_write ( priv , INTSTS1 , ~ irq_state . intsts1 & INTSTS1_MAGIC ) ;
2011-04-04 08:44:59 +04:00
2016-08-29 12:00:38 +03:00
/*
* The driver should not clear the xxxSTS after the line of
* " call irq callback functions " because each " if " statement is
* possible to call the callback function for avoiding any side effects .
*/
if ( irq_state . intsts0 & BRDY )
usbhs_write ( priv , BRDYSTS , ~ irq_state . brdysts ) ;
2012-10-12 08:49:38 +04:00
usbhs_write ( priv , NRDYSTS , ~ irq_state . nrdysts ) ;
2016-08-29 12:00:38 +03:00
if ( irq_state . intsts0 & BEMP )
usbhs_write ( priv , BEMPSTS , ~ irq_state . bempsts ) ;
2011-04-04 08:44:59 +04:00
/*
* call irq callback functions
* see also
* usbhs_irq_setting_update
*/
2011-10-11 09:06:57 +04:00
/* INTSTS0 */
2011-04-28 11:41:20 +04:00
if ( irq_state . intsts0 & VBINT )
usbhs_mod_info_call ( priv , irq_vbus , priv , & irq_state ) ;
2011-04-04 08:44:59 +04:00
if ( irq_state . intsts0 & DVST )
usbhs_mod_call ( priv , irq_dev_state , priv , & irq_state ) ;
if ( irq_state . intsts0 & CTRT )
usbhs_mod_call ( priv , irq_ctrl_stage , priv , & irq_state ) ;
if ( irq_state . intsts0 & BEMP )
usbhs_mod_call ( priv , irq_empty , priv , & irq_state ) ;
if ( irq_state . intsts0 & BRDY )
usbhs_mod_call ( priv , irq_ready , priv , & irq_state ) ;
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) ) {
/* INTSTS1 */
if ( irq_state . intsts1 & ATTCH )
usbhs_mod_call ( priv , irq_attch , priv , & irq_state ) ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( irq_state . intsts1 & DTCH )
usbhs_mod_call ( priv , irq_dtch , priv , & irq_state ) ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( irq_state . intsts1 & SIGN )
usbhs_mod_call ( priv , irq_sign , priv , & irq_state ) ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( irq_state . intsts1 & SACK )
usbhs_mod_call ( priv , irq_sack , priv , & irq_state ) ;
}
2011-04-04 08:44:59 +04:00
return IRQ_HANDLED ;
}
void usbhs_irq_callback_update ( struct usbhs_priv * priv , struct usbhs_mod * mod )
{
u16 intenb0 = 0 ;
2011-10-11 09:06:57 +04:00
u16 intenb1 = 0 ;
2011-04-28 11:41:20 +04:00
struct usbhs_mod_info * info = usbhs_priv_to_modinfo ( priv ) ;
2011-04-04 08:44:59 +04:00
2011-10-11 09:01:15 +04:00
/*
* BEMPENB / BRDYENB are picky .
* below method is required
*
* - clear INTSTS0
* - update BEMPENB / BRDYENB
* - update INTSTS0
*/
2011-04-04 08:44:59 +04:00
usbhs_write ( priv , INTENB0 , 0 ) ;
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) )
usbhs_write ( priv , INTENB1 , 0 ) ;
2011-04-04 08:44:59 +04:00
usbhs_write ( priv , BEMPENB , 0 ) ;
usbhs_write ( priv , BRDYENB , 0 ) ;
/*
* see also
* usbhs_interrupt
*/
/*
* it don ' t enable DVSE ( intenb0 ) here
* but " mod->irq_dev_state " will be called .
*/
2011-04-28 11:41:20 +04:00
if ( info - > irq_vbus )
intenb0 | = VBSE ;
2011-04-04 08:44:59 +04:00
2011-04-28 11:41:07 +04:00
if ( mod ) {
2011-10-11 09:06:57 +04:00
/*
* INTSTS0
*/
2011-04-28 11:41:07 +04:00
if ( mod - > irq_ctrl_stage )
intenb0 | = CTRE ;
2011-04-04 08:44:59 +04:00
2011-04-28 11:41:07 +04:00
if ( mod - > irq_empty & & mod - > irq_bempsts ) {
usbhs_write ( priv , BEMPENB , mod - > irq_bempsts ) ;
intenb0 | = BEMPE ;
}
2011-04-04 08:44:59 +04:00
2011-04-28 11:41:07 +04:00
if ( mod - > irq_ready & & mod - > irq_brdysts ) {
usbhs_write ( priv , BRDYENB , mod - > irq_brdysts ) ;
intenb0 | = BRDYE ;
}
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) ) {
/*
* INTSTS1
*/
if ( mod - > irq_attch )
intenb1 | = ATTCHE ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( mod - > irq_dtch )
intenb1 | = DTCHE ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( mod - > irq_sign )
intenb1 | = SIGNE ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( mod - > irq_sack )
intenb1 | = SACKE ;
}
2011-04-04 08:44:59 +04:00
}
2011-10-11 09:01:15 +04:00
if ( intenb0 )
usbhs_write ( priv , INTENB0 , intenb0 ) ;
2011-10-11 09:06:57 +04:00
2015-01-09 03:28:41 +03:00
if ( usbhs_mod_is_host ( priv ) & & intenb1 )
2011-10-11 09:06:57 +04:00
usbhs_write ( priv , INTENB1 , intenb1 ) ;
2011-04-04 08:44:59 +04:00
}