2008-05-26 11:53:21 +02:00
/*
2008-11-14 00:24:34 +00:00
* GPIO based MDIO bitbang driver .
* Supports OpenFirmware .
2008-05-26 11:53:21 +02:00
*
* Copyright ( c ) 2008 CSE Semaphore Belgium .
* by Laurent Pinchart < laurentp @ cse - semaphore . com >
*
2008-11-14 00:24:34 +00:00
* Copyright ( C ) 2008 , Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*
2008-05-26 11:53:21 +02: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 00:24:34 +00:00
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <linux/mdio-gpio.h>
2008-05-26 11:53:21 +02:00
# include <linux/of_gpio.h>
2009-07-23 10:56:48 -07:00
# include <linux/of_mdio.h>
2008-05-26 11:53:21 +02:00
struct mdio_gpio_info {
struct mdiobb_ctrl ctrl ;
int mdc , mdio ;
} ;
2012-08-24 01:59:17 +00:00
static void * mdio_gpio_of_get_data ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct mdio_gpio_platform_data * pdata ;
int ret ;
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ;
ret = of_get_gpio ( np , 0 ) ;
if ( ret < 0 )
return NULL ;
pdata - > mdc = ret ;
ret = of_get_gpio ( np , 1 ) ;
if ( ret < 0 )
return NULL ;
pdata - > mdio = ret ;
return pdata ;
}
2008-05-26 11:53:21 +02:00
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 00:24:34 +00:00
static int mdio_get ( struct mdiobb_ctrl * ctrl )
2008-05-26 11:53:21 +02:00
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
return gpio_get_value ( bitbang - > mdio ) ;
}
2008-11-14 00:24:34 +00:00
static void mdio_set ( struct mdiobb_ctrl * ctrl , int what )
2008-05-26 11:53:21 +02:00
{
struct mdio_gpio_info * bitbang =
container_of ( ctrl , struct mdio_gpio_info , ctrl ) ;
gpio_set_value ( bitbang - > mdio , what ) ;
}
2008-11-14 00:24:34 +00:00
static void mdc_set ( struct mdiobb_ctrl * ctrl , int what )
2008-05-26 11:53:21 +02: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 00:24:34 +00:00
. set_mdc = mdc_set ,
2008-05-26 11:53:21 +02:00
. set_mdio_dir = mdio_dir ,
2008-11-14 00:24:34 +00:00
. set_mdio_data = mdio_set ,
. get_mdio_data = mdio_get ,
2008-05-26 11:53:21 +02:00
} ;
2012-12-03 09:24:14 -05:00
static struct mii_bus * mdio_gpio_bus_init ( struct device * dev ,
2012-12-06 14:30:56 +00:00
struct mdio_gpio_platform_data * pdata ,
int bus_id )
2008-05-26 11:53:21 +02:00
{
struct mii_bus * new_bus ;
struct mdio_gpio_info * bitbang ;
int i ;
2008-11-14 00:24:34 +00:00
bitbang = kzalloc ( sizeof ( * bitbang ) , GFP_KERNEL ) ;
2008-05-26 11:53:21 +02:00
if ( ! bitbang )
goto out ;
bitbang - > ctrl . ops = & mdio_gpio_ops ;
2011-11-15 11:54:15 +00:00
bitbang - > ctrl . reset = pdata - > reset ;
2008-11-14 00:24:34 +00:00
bitbang - > mdc = pdata - > mdc ;
bitbang - > mdio = pdata - > mdio ;
2008-05-26 11:53:21 +02:00
new_bus = alloc_mdio_bitbang ( & bitbang - > ctrl ) ;
if ( ! new_bus )
2008-10-08 16:29:57 -07:00
goto out_free_bitbang ;
2008-05-26 11:53:21 +02:00
2008-11-14 00:24:34 +00:00
new_bus - > name = " GPIO Bitbanged MDIO " ,
2008-05-26 11:53:21 +02:00
2008-11-14 00:24:34 +00: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 11:53:21 +02:00
goto out_free_bus ;
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
2008-11-14 00:24:34 +00:00
if ( ! new_bus - > irq [ i ] )
new_bus - > irq [ i ] = PHY_POLL ;
2008-05-26 11:53:21 +02:00
2012-01-09 23:59:26 +00:00
snprintf ( new_bus - > id , MII_BUS_ID_SIZE , " gpio-%x " , bus_id ) ;
2008-11-14 00:24:34 +00:00
if ( gpio_request ( bitbang - > mdc , " mdc " ) )
goto out_free_bus ;
2008-05-26 11:53:21 +02:00
2008-11-14 00:24:34 +00:00
if ( gpio_request ( bitbang - > mdio , " mdio " ) )
goto out_free_mdc ;
2009-02-08 23:46:01 +00:00
gpio_direction_output ( bitbang - > mdc , 0 ) ;
2008-11-14 00:24:34 +00:00
dev_set_drvdata ( dev , new_bus ) ;
2008-05-26 11:53:21 +02:00
2009-07-23 10:56:48 -07:00
return new_bus ;
2008-05-26 11:53:21 +02:00
2008-11-14 00:24:34 +00:00
out_free_mdc :
gpio_free ( bitbang - > mdc ) ;
2008-05-26 11:53:21 +02:00
out_free_bus :
free_mdio_bitbang ( new_bus ) ;
2008-10-08 16:29:57 -07:00
out_free_bitbang :
kfree ( bitbang ) ;
2008-05-26 11:53:21 +02:00
out :
2009-07-23 10:56:48 -07:00
return NULL ;
2008-05-26 11:53:21 +02:00
}
2009-11-16 22:47:33 +00:00
static void mdio_gpio_bus_deinit ( struct device * dev )
2008-05-26 11:53:21 +02:00
{
2008-11-14 00:24:34 +00:00
struct mii_bus * bus = dev_get_drvdata ( dev ) ;
2008-05-26 11:53:21 +02:00
struct mdio_gpio_info * bitbang = bus - > priv ;
2008-11-14 00:24:34 +00:00
dev_set_drvdata ( dev , NULL ) ;
gpio_free ( bitbang - > mdio ) ;
2009-07-23 10:56:48 -07:00
gpio_free ( bitbang - > mdc ) ;
free_mdio_bitbang ( bus ) ;
2008-05-26 11:53:21 +02:00
kfree ( bitbang ) ;
2008-11-14 00:24:34 +00:00
}
2012-12-03 09:24:14 -05:00
static void mdio_gpio_bus_destroy ( struct device * dev )
2009-07-23 10:56:48 -07:00
{
struct mii_bus * bus = dev_get_drvdata ( dev ) ;
mdiobus_unregister ( bus ) ;
mdio_gpio_bus_deinit ( dev ) ;
}
2012-12-03 09:24:14 -05:00
static int mdio_gpio_probe ( struct platform_device * pdev )
2008-11-14 00:24:34 +00:00
{
2012-08-24 01:59:17 +00:00
struct mdio_gpio_platform_data * pdata ;
2009-07-23 10:56:48 -07:00
struct mii_bus * new_bus ;
2012-11-16 00:33:59 +00:00
int ret , bus_id ;
2008-11-14 00:24:34 +00:00
2012-11-16 00:33:59 +00:00
if ( pdev - > dev . of_node ) {
2012-08-24 01:59:17 +00:00
pdata = mdio_gpio_of_get_data ( pdev ) ;
2012-11-16 00:33:59 +00:00
bus_id = of_alias_get_id ( pdev - > dev . of_node , " mdio-gpio " ) ;
} else {
2012-08-24 01:59:17 +00:00
pdata = pdev - > dev . platform_data ;
2012-11-16 00:33:59 +00:00
bus_id = pdev - > id ;
}
2012-08-24 01:59:17 +00:00
2008-11-14 00:24:34 +00:00
if ( ! pdata )
return - ENODEV ;
2012-11-16 00:33:59 +00:00
new_bus = mdio_gpio_bus_init ( & pdev - > dev , pdata , bus_id ) ;
2009-07-23 10:56:48 -07:00
if ( ! new_bus )
return - ENODEV ;
2012-08-24 01:59:17 +00:00
if ( pdev - > dev . of_node )
ret = of_mdiobus_register ( new_bus , pdev - > dev . of_node ) ;
else
ret = mdiobus_register ( new_bus ) ;
2009-07-23 10:56:48 -07:00
if ( ret )
mdio_gpio_bus_deinit ( & pdev - > dev ) ;
return ret ;
2008-11-14 00:24:34 +00:00
}
2012-12-03 09:24:14 -05:00
static int mdio_gpio_remove ( struct platform_device * pdev )
2008-11-14 00:24:34 +00:00
{
mdio_gpio_bus_destroy ( & pdev - > dev ) ;
return 0 ;
}
2012-08-24 01:59:17 +00:00
static struct of_device_id mdio_gpio_of_match [ ] = {
{ . compatible = " virtual,mdio-gpio " , } ,
{ /* sentinel */ }
2008-05-26 11:53:21 +02:00
} ;
2008-11-14 00:24:34 +00:00
static struct platform_driver mdio_gpio_driver = {
. probe = mdio_gpio_probe ,
2012-12-03 09:24:14 -05:00
. remove = mdio_gpio_remove ,
2008-11-14 00:24:34 +00:00
. driver = {
. name = " mdio-gpio " ,
. owner = THIS_MODULE ,
2012-08-24 01:59:17 +00:00
. of_match_table = mdio_gpio_of_match ,
2008-11-14 00:24:34 +00:00
} ,
} ;
static int __init mdio_gpio_init ( void )
{
2012-08-24 01:59:17 +00:00
return platform_driver_register ( & mdio_gpio_driver ) ;
2008-11-14 00:24:34 +00:00
}
module_init ( mdio_gpio_init ) ;
static void __exit mdio_gpio_exit ( void )
{
platform_driver_unregister ( & mdio_gpio_driver ) ;
}
module_exit ( mdio_gpio_exit ) ;
2008-05-26 11:53:21 +02:00
2008-11-14 00:24:34 +00: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 " ) ;