2012-05-02 19:16:39 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2011 , 2012 Cavium , Inc .
*/
# include <linux/platform_device.h>
# include <linux/device.h>
# include <linux/of_mdio.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/phy.h>
# include <linux/mdio-mux.h>
# include <linux/of_gpio.h>
# define DRV_VERSION "1.0"
# define DRV_DESCRIPTION "GPIO controlled MDIO bus multiplexer driver"
# define MDIO_MUX_GPIO_MAX_BITS 8
struct mdio_mux_gpio_state {
int gpio [ MDIO_MUX_GPIO_MAX_BITS ] ;
unsigned int num_gpios ;
void * mux_handle ;
} ;
static int mdio_mux_gpio_switch_fn ( int current_child , int desired_child ,
void * data )
{
int change ;
unsigned int n ;
struct mdio_mux_gpio_state * s = data ;
if ( current_child = = desired_child )
return 0 ;
change = current_child = = - 1 ? - 1 : current_child ^ desired_child ;
for ( n = 0 ; n < s - > num_gpios ; n + + ) {
if ( change & 1 )
gpio_set_value_cansleep ( s - > gpio [ n ] ,
( desired_child & 1 ) ! = 0 ) ;
change > > = 1 ;
desired_child > > = 1 ;
}
return 0 ;
}
2012-12-03 18:24:14 +04:00
static int mdio_mux_gpio_probe ( struct platform_device * pdev )
2012-05-02 19:16:39 +04:00
{
enum of_gpio_flags f ;
struct mdio_mux_gpio_state * s ;
2013-02-12 21:48:37 +04:00
int num_gpios ;
2012-05-02 19:16:39 +04:00
unsigned int n ;
int r ;
if ( ! pdev - > dev . of_node )
return - ENODEV ;
num_gpios = of_gpio_count ( pdev - > dev . of_node ) ;
2013-02-12 21:48:37 +04:00
if ( num_gpios < = 0 | | num_gpios > MDIO_MUX_GPIO_MAX_BITS )
2012-05-02 19:16:39 +04:00
return - ENODEV ;
s = devm_kzalloc ( & pdev - > dev , sizeof ( * s ) , GFP_KERNEL ) ;
if ( ! s )
return - ENOMEM ;
s - > num_gpios = num_gpios ;
for ( n = 0 ; n < num_gpios ; ) {
int gpio = of_get_gpio_flags ( pdev - > dev . of_node , n , & f ) ;
if ( gpio < 0 ) {
r = ( gpio = = - ENODEV ) ? - EPROBE_DEFER : gpio ;
goto err ;
}
s - > gpio [ n ] = gpio ;
n + + ;
r = gpio_request ( gpio , " mdio_mux_gpio " ) ;
if ( r )
goto err ;
r = gpio_direction_output ( gpio , 0 ) ;
if ( r )
goto err ;
}
r = mdio_mux_init ( & pdev - > dev ,
mdio_mux_gpio_switch_fn , & s - > mux_handle , s ) ;
if ( r = = 0 ) {
pdev - > dev . platform_data = s ;
return 0 ;
}
err :
while ( n ) {
n - - ;
gpio_free ( s - > gpio [ n ] ) ;
}
return r ;
}
2012-12-03 18:24:14 +04:00
static int mdio_mux_gpio_remove ( struct platform_device * pdev )
2012-05-02 19:16:39 +04:00
{
struct mdio_mux_gpio_state * s = pdev - > dev . platform_data ;
mdio_mux_uninit ( s - > mux_handle ) ;
return 0 ;
}
static struct of_device_id mdio_mux_gpio_match [ ] = {
{
. compatible = " mdio-mux-gpio " ,
} ,
{
/* Legacy compatible property. */
. compatible = " cavium,mdio-mux-sn74cbtlv3253 " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mdio_mux_gpio_match ) ;
static struct platform_driver mdio_mux_gpio_driver = {
. driver = {
. name = " mdio-mux-gpio " ,
. owner = THIS_MODULE ,
. of_match_table = mdio_mux_gpio_match ,
} ,
. probe = mdio_mux_gpio_probe ,
2012-12-03 18:24:14 +04:00
. remove = mdio_mux_gpio_remove ,
2012-05-02 19:16:39 +04:00
} ;
module_platform_driver ( mdio_mux_gpio_driver ) ;
MODULE_DESCRIPTION ( DRV_DESCRIPTION ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( " David Daney " ) ;
MODULE_LICENSE ( " GPL " ) ;