2008-11-24 12:02:21 -08:00
/*
* otg . c - - USB OTG utility code
*
* Copyright ( C ) 2004 Texas Instruments
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/kernel.h>
2011-05-27 09:56:31 -04:00
# include <linux/export.h>
2012-06-22 17:02:46 +05:30
# include <linux/err.h>
2008-11-24 12:02:21 -08:00
# include <linux/device.h>
2012-06-22 17:02:47 +05:30
# include <linux/slab.h>
2008-11-24 12:02:21 -08:00
# include <linux/usb/otg.h>
2012-06-22 17:02:46 +05:30
static LIST_HEAD ( phy_list ) ;
static DEFINE_SPINLOCK ( phy_lock ) ;
static struct usb_phy * __usb_find_phy ( struct list_head * list ,
enum usb_phy_type type )
{
struct usb_phy * phy = NULL ;
list_for_each_entry ( phy , list , head ) {
if ( phy - > type ! = type )
continue ;
return phy ;
}
return ERR_PTR ( - ENODEV ) ;
}
2008-11-24 12:02:21 -08:00
2012-06-22 17:02:47 +05:30
static void devm_usb_phy_release ( struct device * dev , void * res )
{
struct usb_phy * phy = * ( struct usb_phy * * ) res ;
usb_put_phy ( phy ) ;
}
static int devm_usb_phy_match ( struct device * dev , void * res , void * match_data )
{
return res = = match_data ;
}
/**
* devm_usb_get_phy - find the USB PHY
* @ dev - device that requests this phy
* @ type - the type of the phy the controller requires
*
* Gets the phy using usb_get_phy ( ) , and associates a device with it using
* devres . On driver detach , release function is invoked on the devres data ,
* then , devres data is freed .
*
* For use by USB host and peripheral drivers .
*/
struct usb_phy * devm_usb_get_phy ( struct device * dev , enum usb_phy_type type )
{
struct usb_phy * * ptr , * phy ;
ptr = devres_alloc ( devm_usb_phy_release , sizeof ( * ptr ) , GFP_KERNEL ) ;
if ( ! ptr )
return NULL ;
phy = usb_get_phy ( type ) ;
2012-06-26 17:40:32 +05:30
if ( ! IS_ERR ( phy ) ) {
2012-06-22 17:02:47 +05:30
* ptr = phy ;
devres_add ( dev , ptr ) ;
} else
devres_free ( ptr ) ;
return phy ;
}
EXPORT_SYMBOL ( devm_usb_get_phy ) ;
2008-11-24 12:02:21 -08:00
/**
2012-06-22 17:02:46 +05:30
* usb_get_phy - find the USB PHY
* @ type - the type of the phy the controller requires
2008-11-24 12:02:21 -08:00
*
2012-06-22 17:02:45 +05:30
* Returns the phy driver , after getting a refcount to it ; or
2012-06-26 17:40:32 +05:30
* - ENODEV if there is no such phy . The caller is responsible for
2012-06-22 17:02:45 +05:30
* calling usb_put_phy ( ) to release that count .
2008-11-24 12:02:21 -08:00
*
* For use by USB host and peripheral drivers .
*/
2012-06-22 17:02:46 +05:30
struct usb_phy * usb_get_phy ( enum usb_phy_type type )
2008-11-24 12:02:21 -08:00
{
2012-06-22 17:02:46 +05:30
struct usb_phy * phy = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & phy_lock , flags ) ;
phy = __usb_find_phy ( & phy_list , type ) ;
if ( IS_ERR ( phy ) ) {
pr_err ( " unable to find transceiver of type %s \n " ,
usb_phy_type_string ( type ) ) ;
2012-07-02 12:20:24 +05:30
goto err0 ;
2012-06-22 17:02:46 +05:30
}
get_device ( phy - > dev ) ;
2012-07-02 12:20:24 +05:30
err0 :
2012-06-22 17:02:46 +05:30
spin_unlock_irqrestore ( & phy_lock , flags ) ;
2012-02-13 13:24:04 +02:00
return phy ;
2008-11-24 12:02:21 -08:00
}
2012-06-22 17:02:45 +05:30
EXPORT_SYMBOL ( usb_get_phy ) ;
2008-11-24 12:02:21 -08:00
2012-06-22 17:02:47 +05:30
/**
* devm_usb_put_phy - release the USB PHY
* @ dev - device that wants to release this phy
* @ phy - the phy returned by devm_usb_get_phy ( )
*
* destroys the devres associated with this phy and invokes usb_put_phy
* to release the phy .
*
* For use by USB host and peripheral drivers .
*/
void devm_usb_put_phy ( struct device * dev , struct usb_phy * phy )
{
int r ;
r = devres_destroy ( dev , devm_usb_phy_release , devm_usb_phy_match , phy ) ;
dev_WARN_ONCE ( dev , r , " couldn't find PHY resource \n " ) ;
}
EXPORT_SYMBOL ( devm_usb_put_phy ) ;
2008-11-24 12:02:21 -08:00
/**
2012-06-22 17:02:46 +05:30
* usb_put_phy - release the USB PHY
2012-06-22 17:02:45 +05:30
* @ x : the phy returned by usb_get_phy ( )
2008-11-24 12:02:21 -08:00
*
2012-06-22 17:02:45 +05:30
* Releases a refcount the caller received from usb_get_phy ( ) .
2008-11-24 12:02:21 -08:00
*
* For use by USB host and peripheral drivers .
*/
2012-06-22 17:02:45 +05:30
void usb_put_phy ( struct usb_phy * x )
2008-11-24 12:02:21 -08:00
{
2009-04-21 20:33:10 -07:00
if ( x )
put_device ( x - > dev ) ;
2008-11-24 12:02:21 -08:00
}
2012-06-22 17:02:45 +05:30
EXPORT_SYMBOL ( usb_put_phy ) ;
2008-11-24 12:02:21 -08:00
/**
2012-06-22 17:02:46 +05:30
* usb_add_phy - declare the USB PHY
2012-06-22 17:02:45 +05:30
* @ x : the USB phy to be used ; or NULL
2012-06-22 17:02:46 +05:30
* @ type - the type of this PHY
2008-11-24 12:02:21 -08:00
*
2012-06-22 17:02:45 +05:30
* This call is exclusively for use by phy drivers , which
2008-11-24 12:02:21 -08:00
* coordinate the activities of drivers for host and peripheral
* controllers , and in some cases for VBUS current regulation .
*/
2012-06-22 17:02:46 +05:30
int usb_add_phy ( struct usb_phy * x , enum usb_phy_type type )
2008-11-24 12:02:21 -08:00
{
2012-06-22 17:02:46 +05:30
int ret = 0 ;
unsigned long flags ;
struct usb_phy * phy ;
2012-08-07 19:56:30 +05:30
if ( x - > type ! = USB_PHY_TYPE_UNDEFINED ) {
2012-06-22 17:02:46 +05:30
dev_err ( x - > dev , " not accepting initialized PHY %s \n " , x - > label ) ;
return - EINVAL ;
}
spin_lock_irqsave ( & phy_lock , flags ) ;
list_for_each_entry ( phy , & phy_list , head ) {
if ( phy - > type = = type ) {
ret = - EBUSY ;
dev_err ( x - > dev , " transceiver type %s already exists \n " ,
usb_phy_type_string ( type ) ) ;
goto out ;
}
}
x - > type = type ;
list_add_tail ( & x - > head , & phy_list ) ;
out :
spin_unlock_irqrestore ( & phy_lock , flags ) ;
return ret ;
2008-11-24 12:02:21 -08:00
}
2012-06-22 17:02:45 +05:30
EXPORT_SYMBOL ( usb_add_phy ) ;
2011-04-15 16:18:38 +02:00
2012-06-22 17:02:46 +05:30
/**
* usb_remove_phy - remove the OTG PHY
* @ x : the USB OTG PHY to be removed ;
*
* This reverts the effects of usb_add_phy
*/
void usb_remove_phy ( struct usb_phy * x )
{
unsigned long flags ;
spin_lock_irqsave ( & phy_lock , flags ) ;
if ( x )
list_del ( & x - > head ) ;
spin_unlock_irqrestore ( & phy_lock , flags ) ;
}
EXPORT_SYMBOL ( usb_remove_phy ) ;
2011-04-15 16:18:38 +02:00
const char * otg_state_string ( enum usb_otg_state state )
{
switch ( state ) {
case OTG_STATE_A_IDLE :
return " a_idle " ;
case OTG_STATE_A_WAIT_VRISE :
return " a_wait_vrise " ;
case OTG_STATE_A_WAIT_BCON :
return " a_wait_bcon " ;
case OTG_STATE_A_HOST :
return " a_host " ;
case OTG_STATE_A_SUSPEND :
return " a_suspend " ;
case OTG_STATE_A_PERIPHERAL :
return " a_peripheral " ;
case OTG_STATE_A_WAIT_VFALL :
return " a_wait_vfall " ;
case OTG_STATE_A_VBUS_ERR :
return " a_vbus_err " ;
case OTG_STATE_B_IDLE :
return " b_idle " ;
case OTG_STATE_B_SRP_INIT :
return " b_srp_init " ;
case OTG_STATE_B_PERIPHERAL :
return " b_peripheral " ;
case OTG_STATE_B_WAIT_ACON :
return " b_wait_acon " ;
case OTG_STATE_B_HOST :
return " b_host " ;
default :
return " UNDEFINED " ;
}
}
EXPORT_SYMBOL ( otg_state_string ) ;