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/interrupt.h>
2008-11-14 00:24:34 +00:00
# include <linux/platform_device.h>
# include <linux/gpio.h>
2015-10-20 10:08:59 -04:00
# include <linux/platform_data/mdio-gpio.h>
2008-11-14 00:24:34 +00:00
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 ;
2017-01-11 12:59:50 -08:00
struct gpio_desc * mdc , * mdio , * mdo ;
2008-05-26 11:53:21 +02:00
} ;
2018-04-19 01:02:55 +02:00
static void * mdio_gpio_of_get_data ( struct device * dev )
2012-08-24 01:59:17 +00:00
{
struct mdio_gpio_platform_data * pdata ;
2018-04-19 01:02:55 +02:00
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
2012-08-24 01:59:17 +00:00
if ( ! pdata )
return NULL ;
2018-04-19 01:02:55 +02:00
pdata - > mdc = devm_gpiod_get_index ( dev , NULL , 0 , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( pdata - > mdc ) )
return ERR_CAST ( pdata - > mdc ) ;
2012-08-24 01:59:17 +00:00
2018-04-19 01:02:55 +02:00
pdata - > mdio = devm_gpiod_get_index ( dev , NULL , 1 , GPIOD_IN ) ;
if ( IS_ERR ( pdata - > mdio ) )
return ERR_CAST ( pdata - > mdio ) ;
2012-08-24 01:59:17 +00:00
2018-04-19 01:02:55 +02:00
pdata - > mdo = devm_gpiod_get_index_optional ( dev , NULL , 2 , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( pdata - > mdo ) )
return ERR_CAST ( pdata - > mdo ) ;
2014-04-15 19:16:42 -07:00
2012-08-24 01:59:17 +00:00
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 ) ;
2014-04-15 19:16:42 -07:00
if ( bitbang - > mdo ) {
/* Separate output pin. Always set its value to high
* when changing direction . If direction is input ,
* assume the pin serves as pull - up . If direction is
* output , the default value is high .
*/
2017-01-11 12:59:51 -08:00
gpiod_set_value ( bitbang - > mdo , 1 ) ;
2014-04-15 19:16:42 -07:00
return ;
}
2008-05-26 11:53:21 +02:00
if ( dir )
2017-01-11 12:59:51 -08:00
gpiod_direction_output ( bitbang - > mdio , 1 ) ;
2008-05-26 11:53:21 +02:00
else
2017-01-11 12:59:50 -08:00
gpiod_direction_input ( bitbang - > mdio ) ;
2008-05-26 11:53:21 +02:00
}
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 ) ;
2017-01-11 12:59:51 -08:00
return gpiod_get_value ( bitbang - > mdio ) ;
2008-05-26 11:53:21 +02:00
}
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 ) ;
2014-04-15 19:16:42 -07:00
if ( bitbang - > mdo )
2017-01-11 12:59:51 -08:00
gpiod_set_value ( bitbang - > mdo , what ) ;
2014-04-15 19:16:42 -07:00
else
2017-01-11 12:59:51 -08:00
gpiod_set_value ( bitbang - > mdio , what ) ;
2008-05-26 11:53:21 +02:00
}
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 ) ;
2017-01-11 12:59:51 -08:00
gpiod_set_value ( bitbang - > mdc , what ) ;
2008-05-26 11:53:21 +02:00
}
2017-08-22 13:43:29 +05:30
static const struct mdiobb_ops mdio_gpio_ops = {
2008-05-26 11:53:21 +02:00
. 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 ,
2018-04-19 01:02:56 +02:00
struct mdio_gpio_info * bitbang ,
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 ;
bitbang - > ctrl . ops = & mdio_gpio_ops ;
2018-04-19 01:02:55 +02:00
bitbang - > mdc = pdata - > mdc ;
bitbang - > mdio = pdata - > mdio ;
bitbang - > mdo = pdata - > mdo ;
2008-05-26 11:53:21 +02:00
new_bus = alloc_mdio_bitbang ( & bitbang - > ctrl ) ;
if ( ! new_bus )
2018-04-19 01:02:55 +02:00
return NULL ;
2008-05-26 11:53:21 +02:00
2018-04-19 01:02:49 +02:00
new_bus - > name = " GPIO Bitbanged MDIO " ;
2008-11-14 00:24:34 +00:00
new_bus - > parent = dev ;
2015-05-08 16:18:49 +02:00
if ( bus_id ! = - 1 )
snprintf ( new_bus - > id , MII_BUS_ID_SIZE , " gpio-%x " , bus_id ) ;
else
strncpy ( new_bus - > id , " gpio " , MII_BUS_ID_SIZE ) ;
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
}
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
2009-07-23 10:56:48 -07:00
free_mdio_bitbang ( bus ) ;
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 ;
2018-04-19 01:02:56 +02:00
struct mdio_gpio_info * bitbang ;
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
2018-04-19 01:02:56 +02:00
bitbang = devm_kzalloc ( & pdev - > dev , sizeof ( * bitbang ) , GFP_KERNEL ) ;
if ( ! bitbang )
return - ENOMEM ;
2012-11-16 00:33:59 +00:00
if ( pdev - > dev . of_node ) {
2018-04-19 01:02:55 +02:00
pdata = mdio_gpio_of_get_data ( & pdev - > dev ) ;
2012-11-16 00:33:59 +00:00
bus_id = of_alias_get_id ( pdev - > dev . of_node , " mdio-gpio " ) ;
2014-05-08 10:09:21 +02:00
if ( bus_id < 0 ) {
dev_warn ( & pdev - > dev , " failed to get alias id \n " ) ;
bus_id = 0 ;
}
2012-11-16 00:33:59 +00:00
} else {
2013-08-30 14:08:55 +09:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
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 ;
2018-04-19 01:02:56 +02:00
new_bus = mdio_gpio_bus_init ( & pdev - > dev , bitbang , 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 ;
}
2015-03-17 19:40:23 +01:00
static const struct of_device_id mdio_gpio_of_match [ ] = {
2012-08-24 01:59:17 +00:00
{ . compatible = " virtual,mdio-gpio " , } ,
{ /* sentinel */ }
2008-05-26 11:53:21 +02:00
} ;
2015-09-18 18:16:29 +02:00
MODULE_DEVICE_TABLE ( of , mdio_gpio_of_match ) ;
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 " ,
2012-08-24 01:59:17 +00:00
. of_match_table = mdio_gpio_of_match ,
2008-11-14 00:24:34 +00:00
} ,
} ;
2013-03-20 01:41:31 +00:00
module_platform_driver ( mdio_gpio_driver ) ;
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 " ) ;