2008-05-26 13:53:21 +04:00
/*
2008-11-14 03:24:34 +03:00
* GPIO based MDIO bitbang driver .
* Supports OpenFirmware .
2008-05-26 13:53:21 +04:00
*
* Copyright ( c ) 2008 CSE Semaphore Belgium .
* by Laurent Pinchart < laurentp @ cse - semaphore . com >
*
2008-11-14 03:24:34 +03:00
* Copyright ( C ) 2008 , Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*
2008-05-26 13:53:21 +04:00
* Based on earlier work by
*
* Copyright ( c ) 2003 Intracom S . A .
* by Pantelis Antoniou < panto @ intracom . gr >
*
* 2005 ( c ) MontaVista Software , Inc .
* Vitaly Bordug < vbordug @ ru . mvista . com >
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
* kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/interrupt.h>
2008-11-14 03:24:34 +03:00
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <linux/mdio-gpio.h>
# ifdef CONFIG_OF_GPIO
2008-05-26 13:53:21 +04:00
# include <linux/of_gpio.h>
# include <linux/of_platform.h>
2008-11-14 03:24:34 +03:00
# endif
2008-05-26 13:53:21 +04:00
struct mdio_gpio_info {
struct mdiobb_ctrl ctrl ;
int mdc , mdio ;
} ;
static void mdio_dir ( struct mdiobb_ctrl * ctrl , int dir )
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
if ( dir )
gpio_direction_output ( bitbang - > mdio , 1 ) ;
else
gpio_direction_input ( bitbang - > mdio ) ;
}
2008-11-14 03:24:34 +03:00
static int mdio_get ( struct mdiobb_ctrl * ctrl )
2008-05-26 13:53:21 +04:00
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
return gpio_get_value ( bitbang - > mdio ) ;
}
2008-11-14 03:24:34 +03:00
static void mdio_set ( struct mdiobb_ctrl * ctrl , int what )
2008-05-26 13:53:21 +04:00
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
gpio_set_value ( bitbang - > mdio , what ) ;
}
2008-11-14 03:24:34 +03:00
static void mdc_set ( struct mdiobb_ctrl * ctrl , int what )
2008-05-26 13:53:21 +04:00
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
gpio_set_value ( bitbang - > mdc , what ) ;
}
static struct mdiobb_ops mdio_gpio_ops = {
. owner = THIS_MODULE ,
2008-11-14 03:24:34 +03:00
. set_mdc = mdc_set ,
2008-05-26 13:53:21 +04:00
. set_mdio_dir = mdio_dir ,
2008-11-14 03:24:34 +03:00
. set_mdio_data = mdio_set ,
. get_mdio_data = mdio_get ,
2008-05-26 13:53:21 +04:00
} ;
2008-11-14 03:24:34 +03:00
static int __devinit mdio_gpio_bus_init ( struct device * dev ,
struct mdio_gpio_platform_data * pdata ,
int bus_id )
2008-05-26 13:53:21 +04:00
{
struct mii_bus * new_bus ;
struct mdio_gpio_info * bitbang ;
int ret = - ENOMEM ;
int i ;
2008-11-14 03:24:34 +03:00
bitbang = kzalloc ( sizeof ( * bitbang ) , GFP_KERNEL ) ;
2008-05-26 13:53:21 +04:00
if ( ! bitbang )
goto out ;
bitbang - > ctrl . ops = & mdio_gpio_ops ;
2008-11-14 03:24:34 +03:00
bitbang - > mdc = pdata - > mdc ;
bitbang - > mdio = pdata - > mdio ;
2008-05-26 13:53:21 +04:00
new_bus = alloc_mdio_bitbang ( & bitbang - > ctrl ) ;
if ( ! new_bus )
2008-10-09 03:29:57 +04:00
goto out_free_bitbang ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
new_bus - > name = " GPIO Bitbanged MDIO " ,
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
ret = - ENODEV ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
new_bus - > phy_mask = pdata - > phy_mask ;
new_bus - > irq = pdata - > irqs ;
new_bus - > parent = dev ;
if ( new_bus - > phy_mask = = ~ 0 )
2008-05-26 13:53:21 +04:00
goto out_free_bus ;
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
2008-11-14 03:24:34 +03:00
if ( ! new_bus - > irq [ i ] )
new_bus - > irq [ i ] = PHY_POLL ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
snprintf ( new_bus - > id , MII_BUS_ID_SIZE , " %x " , bus_id ) ;
if ( gpio_request ( bitbang - > mdc , " mdc " ) )
goto out_free_bus ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
if ( gpio_request ( bitbang - > mdio , " mdio " ) )
goto out_free_mdc ;
2009-02-09 02:46:01 +03:00
gpio_direction_output ( bitbang - > mdc , 0 ) ;
2008-11-14 03:24:34 +03:00
dev_set_drvdata ( dev , new_bus ) ;
2008-05-26 13:53:21 +04:00
ret = mdiobus_register ( new_bus ) ;
if ( ret )
2008-11-14 03:24:34 +03:00
goto out_free_all ;
2008-05-26 13:53:21 +04:00
return 0 ;
2008-11-14 03:24:34 +03:00
out_free_all :
dev_set_drvdata ( dev , NULL ) ;
gpio_free ( bitbang - > mdio ) ;
out_free_mdc :
gpio_free ( bitbang - > mdc ) ;
2008-05-26 13:53:21 +04:00
out_free_bus :
free_mdio_bitbang ( new_bus ) ;
2008-10-09 03:29:57 +04:00
out_free_bitbang :
kfree ( bitbang ) ;
2008-05-26 13:53:21 +04:00
out :
return ret ;
}
2008-11-14 03:24:34 +03:00
static void __devexit mdio_gpio_bus_destroy ( struct device * dev )
2008-05-26 13:53:21 +04:00
{
2008-11-14 03:24:34 +03:00
struct mii_bus * bus = dev_get_drvdata ( dev ) ;
2008-05-26 13:53:21 +04:00
struct mdio_gpio_info * bitbang = bus - > priv ;
mdiobus_unregister ( bus ) ;
free_mdio_bitbang ( bus ) ;
2008-11-14 03:24:34 +03:00
dev_set_drvdata ( dev , NULL ) ;
gpio_free ( bitbang - > mdc ) ;
gpio_free ( bitbang - > mdio ) ;
2008-05-26 13:53:21 +04:00
kfree ( bitbang ) ;
2008-11-14 03:24:34 +03:00
}
static int __devinit mdio_gpio_probe ( struct platform_device * pdev )
{
struct mdio_gpio_platform_data * pdata = pdev - > dev . platform_data ;
if ( ! pdata )
return - ENODEV ;
return mdio_gpio_bus_init ( & pdev - > dev , pdata , pdev - > id ) ;
}
static int __devexit mdio_gpio_remove ( struct platform_device * pdev )
{
mdio_gpio_bus_destroy ( & pdev - > dev ) ;
return 0 ;
}
# ifdef CONFIG_OF_GPIO
static void __devinit add_phy ( struct mdio_gpio_platform_data * pdata ,
struct device_node * np )
{
const u32 * data ;
int len , id , irq ;
data = of_get_property ( np , " reg " , & len ) ;
if ( ! data | | len ! = 4 )
return ;
id = * data ;
pdata - > phy_mask & = ~ ( 1 < < id ) ;
irq = of_irq_to_resource ( np , 0 , NULL ) ;
if ( irq )
pdata - > irqs [ id ] = irq ;
}
static int __devinit mdio_ofgpio_probe ( struct of_device * ofdev ,
const struct of_device_id * match )
{
struct device_node * np = NULL ;
struct mdio_gpio_platform_data * pdata ;
2009-01-20 04:14:21 +03:00
int ret ;
2008-11-14 03:24:34 +03:00
pdata = kzalloc ( sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
2009-01-20 04:14:21 +03:00
ret = of_get_gpio ( ofdev - > node , 0 ) ;
if ( ret < 0 )
2008-11-14 03:24:34 +03:00
goto out_free ;
2009-01-20 04:14:21 +03:00
pdata - > mdc = ret ;
ret = of_get_gpio ( ofdev - > node , 1 ) ;
if ( ret < 0 )
goto out_free ;
pdata - > mdio = ret ;
2008-11-14 03:24:34 +03:00
while ( ( np = of_get_next_child ( ofdev - > node , np ) ) )
if ( ! strcmp ( np - > type , " ethernet-phy " ) )
add_phy ( pdata , np ) ;
return mdio_gpio_bus_init ( & ofdev - > dev , pdata , pdata - > mdc ) ;
out_free :
kfree ( pdata ) ;
return - ENODEV ;
}
static int __devexit mdio_ofgpio_remove ( struct of_device * ofdev )
{
mdio_gpio_bus_destroy ( & ofdev - > dev ) ;
kfree ( ofdev - > dev . platform_data ) ;
2008-05-26 13:53:21 +04:00
return 0 ;
}
static struct of_device_id mdio_ofgpio_match [ ] = {
{
. compatible = " virtual,mdio-gpio " ,
} ,
{ } ,
} ;
static struct of_platform_driver mdio_ofgpio_driver = {
. name = " mdio-gpio " ,
. match_table = mdio_ofgpio_match ,
. probe = mdio_ofgpio_probe ,
2008-11-14 03:24:34 +03:00
. remove = __devexit_p ( mdio_ofgpio_remove ) ,
2008-05-26 13:53:21 +04:00
} ;
2008-11-14 03:24:34 +03:00
static inline int __init mdio_ofgpio_init ( void )
2008-05-26 13:53:21 +04:00
{
return of_register_platform_driver ( & mdio_ofgpio_driver ) ;
}
2008-11-14 03:24:34 +03:00
static inline void __exit mdio_ofgpio_exit ( void )
2008-05-26 13:53:21 +04:00
{
of_unregister_platform_driver ( & mdio_ofgpio_driver ) ;
}
2008-11-14 03:24:34 +03:00
# else
static inline int __init mdio_ofgpio_init ( void ) { return 0 ; }
static inline void __exit mdio_ofgpio_exit ( void ) { }
# endif /* CONFIG_OF_GPIO */
static struct platform_driver mdio_gpio_driver = {
. probe = mdio_gpio_probe ,
. remove = __devexit_p ( mdio_gpio_remove ) ,
. driver = {
. name = " mdio-gpio " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init mdio_gpio_init ( void )
{
int ret ;
ret = mdio_ofgpio_init ( ) ;
if ( ret )
return ret ;
ret = platform_driver_register ( & mdio_gpio_driver ) ;
if ( ret )
mdio_ofgpio_exit ( ) ;
return ret ;
}
module_init ( mdio_gpio_init ) ;
static void __exit mdio_gpio_exit ( void )
{
platform_driver_unregister ( & mdio_gpio_driver ) ;
mdio_ofgpio_exit ( ) ;
}
module_exit ( mdio_gpio_exit ) ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
MODULE_ALIAS ( " platform:mdio-gpio " ) ;
MODULE_AUTHOR ( " Laurent Pinchart, Paulius Zaleckas " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Generic driver for MDIO bus emulation using GPIO " ) ;