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/interrupt.h>
2008-11-14 03:24:34 +03:00
# include <linux/platform_device.h>
# include <linux/gpio.h>
2015-10-20 17:08:59 +03:00
# include <linux/platform_data/mdio-gpio.h>
2008-11-14 03:24:34 +03:00
2008-05-26 13:53:21 +04:00
# include <linux/of_gpio.h>
2009-07-23 21:56:48 +04:00
# include <linux/of_mdio.h>
2008-05-26 13:53:21 +04:00
struct mdio_gpio_info {
struct mdiobb_ctrl ctrl ;
2014-04-16 06:16:42 +04:00
int mdc , mdio , mdo ;
int mdc_active_low , mdio_active_low , mdo_active_low ;
2008-05-26 13:53:21 +04:00
} ;
2012-08-24 05:59:17 +04: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 ;
2014-04-16 06:16:41 +04:00
enum of_gpio_flags flags ;
2012-08-24 05:59:17 +04:00
int ret ;
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ;
2014-04-16 06:16:41 +04:00
ret = of_get_gpio_flags ( np , 0 , & flags ) ;
2012-08-24 05:59:17 +04:00
if ( ret < 0 )
return NULL ;
pdata - > mdc = ret ;
2014-04-16 06:16:41 +04:00
pdata - > mdc_active_low = flags & OF_GPIO_ACTIVE_LOW ;
2012-08-24 05:59:17 +04:00
2014-04-16 06:16:41 +04:00
ret = of_get_gpio_flags ( np , 1 , & flags ) ;
2012-08-24 05:59:17 +04:00
if ( ret < 0 )
return NULL ;
pdata - > mdio = ret ;
2014-04-16 06:16:41 +04:00
pdata - > mdio_active_low = flags & OF_GPIO_ACTIVE_LOW ;
2012-08-24 05:59:17 +04:00
2014-04-16 06:16:42 +04:00
ret = of_get_gpio_flags ( np , 2 , & flags ) ;
if ( ret > 0 ) {
pdata - > mdo = ret ;
pdata - > mdo_active_low = flags & OF_GPIO_ACTIVE_LOW ;
}
2012-08-24 05:59:17 +04:00
return pdata ;
}
2008-05-26 13:53:21 +04: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-16 06:16:42 +04: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 .
*/
2015-04-22 20:06:54 +03:00
gpio_set_value_cansleep ( bitbang - > mdo ,
1 ^ bitbang - > mdo_active_low ) ;
2014-04-16 06:16:42 +04:00
return ;
}
2008-05-26 13:53:21 +04:00
if ( dir )
2014-04-16 06:16:41 +04:00
gpio_direction_output ( bitbang - > mdio ,
1 ^ bitbang - > mdio_active_low ) ;
2008-05-26 13:53:21 +04:00
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 ) ;
2015-04-22 20:06:54 +03:00
return gpio_get_value_cansleep ( bitbang - > mdio ) ^
bitbang - > mdio_active_low ;
2008-05-26 13:53:21 +04:00
}
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 ) ;
2014-04-16 06:16:42 +04:00
if ( bitbang - > mdo )
2015-04-22 20:06:54 +03:00
gpio_set_value_cansleep ( bitbang - > mdo ,
what ^ bitbang - > mdo_active_low ) ;
2014-04-16 06:16:42 +04:00
else
2015-04-22 20:06:54 +03:00
gpio_set_value_cansleep ( bitbang - > mdio ,
what ^ bitbang - > mdio_active_low ) ;
2008-05-26 13:53:21 +04:00
}
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 ) ;
2015-04-22 20:06:54 +03:00
gpio_set_value_cansleep ( bitbang - > mdc , what ^ bitbang - > mdc_active_low ) ;
2008-05-26 13:53:21 +04:00
}
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
} ;
2012-12-03 18:24:14 +04:00
static struct mii_bus * mdio_gpio_bus_init ( struct device * dev ,
2012-12-06 18:30:56 +04:00
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 i ;
2017-01-11 23:59:49 +03:00
unsigned long mdc_flags = GPIOF_OUT_INIT_LOW ;
unsigned long mdio_flags = GPIOF_DIR_IN ;
unsigned long mdo_flags = GPIOF_OUT_INIT_HIGH ;
2008-05-26 13:53:21 +04:00
2014-04-16 06:16:40 +04:00
bitbang = devm_kzalloc ( dev , sizeof ( * bitbang ) , GFP_KERNEL ) ;
2008-05-26 13:53:21 +04:00
if ( ! bitbang )
goto out ;
bitbang - > ctrl . ops = & mdio_gpio_ops ;
2011-11-15 15:54:15 +04:00
bitbang - > ctrl . reset = pdata - > reset ;
2008-11-14 03:24:34 +03:00
bitbang - > mdc = pdata - > mdc ;
2014-04-16 06:16:41 +04:00
bitbang - > mdc_active_low = pdata - > mdc_active_low ;
2008-11-14 03:24:34 +03:00
bitbang - > mdio = pdata - > mdio ;
2014-04-16 06:16:41 +04:00
bitbang - > mdio_active_low = pdata - > mdio_active_low ;
2014-04-16 06:16:42 +04:00
bitbang - > mdo = pdata - > mdo ;
bitbang - > mdo_active_low = pdata - > mdo_active_low ;
2008-05-26 13:53:21 +04:00
new_bus = alloc_mdio_bitbang ( & bitbang - > ctrl ) ;
if ( ! new_bus )
2014-04-16 06:16:40 +04:00
goto out ;
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
new_bus - > phy_mask = pdata - > phy_mask ;
2015-05-13 14:35:39 +03:00
new_bus - > phy_ignore_ta_mask = pdata - > phy_ignore_ta_mask ;
2016-01-06 22:11:15 +03:00
memcpy ( new_bus - > irq , pdata - > irqs , sizeof ( new_bus - > irq ) ) ;
2008-11-14 03:24:34 +03:00
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
2015-05-08 17:18:49 +03: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 03:24:34 +03:00
2017-01-11 23:59:49 +03:00
if ( devm_gpio_request_one ( dev , bitbang - > mdc , mdc_flags , " mdc " ) )
2008-11-14 03:24:34 +03:00
goto out_free_bus ;
2008-05-26 13:53:21 +04:00
2017-01-11 23:59:49 +03:00
if ( devm_gpio_request_one ( dev , bitbang - > mdio , mdio_flags , " mdio " ) )
2014-04-16 06:16:40 +04:00
goto out_free_bus ;
2008-11-14 03:24:34 +03:00
2014-04-16 06:16:42 +04:00
if ( bitbang - > mdo ) {
2017-01-11 23:59:49 +03:00
if ( devm_gpio_request_one ( dev , bitbang - > mdo , mdo_flags , " mdo " ) )
2014-04-16 06:16:42 +04:00
goto out_free_bus ;
}
2008-11-14 03:24:34 +03:00
dev_set_drvdata ( dev , new_bus ) ;
2008-05-26 13:53:21 +04:00
2009-07-23 21:56:48 +04:00
return new_bus ;
2008-05-26 13:53:21 +04:00
out_free_bus :
free_mdio_bitbang ( new_bus ) ;
out :
2009-07-23 21:56:48 +04:00
return NULL ;
2008-05-26 13:53:21 +04:00
}
2009-11-17 01:47:33 +03:00
static void mdio_gpio_bus_deinit ( 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
2009-07-23 21:56:48 +04:00
free_mdio_bitbang ( bus ) ;
2008-11-14 03:24:34 +03:00
}
2012-12-03 18:24:14 +04:00
static void mdio_gpio_bus_destroy ( struct device * dev )
2009-07-23 21:56:48 +04:00
{
struct mii_bus * bus = dev_get_drvdata ( dev ) ;
mdiobus_unregister ( bus ) ;
mdio_gpio_bus_deinit ( dev ) ;
}
2012-12-03 18:24:14 +04:00
static int mdio_gpio_probe ( struct platform_device * pdev )
2008-11-14 03:24:34 +03:00
{
2012-08-24 05:59:17 +04:00
struct mdio_gpio_platform_data * pdata ;
2009-07-23 21:56:48 +04:00
struct mii_bus * new_bus ;
2012-11-16 04:33:59 +04:00
int ret , bus_id ;
2008-11-14 03:24:34 +03:00
2012-11-16 04:33:59 +04:00
if ( pdev - > dev . of_node ) {
2012-08-24 05:59:17 +04:00
pdata = mdio_gpio_of_get_data ( pdev ) ;
2012-11-16 04:33:59 +04:00
bus_id = of_alias_get_id ( pdev - > dev . of_node , " mdio-gpio " ) ;
2014-05-08 12:09:21 +04:00
if ( bus_id < 0 ) {
dev_warn ( & pdev - > dev , " failed to get alias id \n " ) ;
bus_id = 0 ;
}
2012-11-16 04:33:59 +04:00
} else {
2013-08-30 09:08:55 +04:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
2012-11-16 04:33:59 +04:00
bus_id = pdev - > id ;
}
2012-08-24 05:59:17 +04:00
2008-11-14 03:24:34 +03:00
if ( ! pdata )
return - ENODEV ;
2012-11-16 04:33:59 +04:00
new_bus = mdio_gpio_bus_init ( & pdev - > dev , pdata , bus_id ) ;
2009-07-23 21:56:48 +04:00
if ( ! new_bus )
return - ENODEV ;
2012-08-24 05:59:17 +04: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 21:56:48 +04:00
if ( ret )
mdio_gpio_bus_deinit ( & pdev - > dev ) ;
return ret ;
2008-11-14 03:24:34 +03:00
}
2012-12-03 18:24:14 +04:00
static int mdio_gpio_remove ( struct platform_device * pdev )
2008-11-14 03:24:34 +03:00
{
mdio_gpio_bus_destroy ( & pdev - > dev ) ;
return 0 ;
}
2015-03-17 21:40:23 +03:00
static const struct of_device_id mdio_gpio_of_match [ ] = {
2012-08-24 05:59:17 +04:00
{ . compatible = " virtual,mdio-gpio " , } ,
{ /* sentinel */ }
2008-05-26 13:53:21 +04:00
} ;
2015-09-18 19:16:29 +03:00
MODULE_DEVICE_TABLE ( of , mdio_gpio_of_match ) ;
2008-05-26 13:53:21 +04:00
2008-11-14 03:24:34 +03:00
static struct platform_driver mdio_gpio_driver = {
. probe = mdio_gpio_probe ,
2012-12-03 18:24:14 +04:00
. remove = mdio_gpio_remove ,
2008-11-14 03:24:34 +03:00
. driver = {
. name = " mdio-gpio " ,
2012-08-24 05:59:17 +04:00
. of_match_table = mdio_gpio_of_match ,
2008-11-14 03:24:34 +03:00
} ,
} ;
2013-03-20 05:41:31 +04:00
module_platform_driver ( mdio_gpio_driver ) ;
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 " ) ;