2019-04-26 20:12:23 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-09-21 00:50:39 +05:30
/* Texas Instruments Ethernet Switch Driver
*
* Copyright ( C ) 2013 Texas Instruments
*
2015-10-07 17:27:46 -04:00
* Module Author : Mugunthan V N < mugunthanvnm @ ti . com >
*
2013-09-21 00:50:39 +05:30
*/
# include <linux/platform_device.h>
2015-10-07 17:27:46 -04:00
# include <linux/init.h>
2013-09-21 00:50:39 +05:30
# include <linux/netdevice.h>
# include <linux/phy.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include "cpsw.h"
/* AM33xx SoC specific definitions for the CONTROL port */
# define AM33XX_GMII_SEL_MODE_MII 0
# define AM33XX_GMII_SEL_MODE_RMII 1
# define AM33XX_GMII_SEL_MODE_RGMII 2
# define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
# define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
2016-10-04 19:07:29 +05:30
# define AM33XX_GMII_SEL_RGMII2_IDMODE BIT(5)
# define AM33XX_GMII_SEL_RGMII1_IDMODE BIT(4)
2013-09-21 00:50:39 +05:30
2014-05-09 19:07:34 +05:30
# define GMII_SEL_MODE_MASK 0x3
2013-09-21 00:50:39 +05:30
struct cpsw_phy_sel_priv {
struct device * dev ;
u32 __iomem * gmii_sel ;
bool rmii_clock_external ;
void ( * cpsw_phy_sel ) ( struct cpsw_phy_sel_priv * priv ,
phy_interface_t phy_mode , int slave ) ;
} ;
static void cpsw_gmii_sel_am3352 ( struct cpsw_phy_sel_priv * priv ,
phy_interface_t phy_mode , int slave )
{
u32 reg ;
u32 mask ;
u32 mode = 0 ;
2016-10-04 19:07:29 +05:30
bool rgmii_id = false ;
2013-09-21 00:50:39 +05:30
reg = readl ( priv - > gmii_sel ) ;
switch ( phy_mode ) {
case PHY_INTERFACE_MODE_RMII :
mode = AM33XX_GMII_SEL_MODE_RMII ;
break ;
case PHY_INTERFACE_MODE_RGMII :
2016-10-04 19:07:29 +05:30
mode = AM33XX_GMII_SEL_MODE_RGMII ;
break ;
2013-09-21 00:50:39 +05:30
case PHY_INTERFACE_MODE_RGMII_ID :
case PHY_INTERFACE_MODE_RGMII_RXID :
case PHY_INTERFACE_MODE_RGMII_TXID :
mode = AM33XX_GMII_SEL_MODE_RGMII ;
2016-10-04 19:07:29 +05:30
rgmii_id = true ;
2013-09-21 00:50:39 +05:30
break ;
default :
2016-02-12 19:45:36 -05:00
dev_warn ( priv - > dev ,
" Unsupported PHY mode: \" %s \" . Defaulting to MII. \n " ,
phy_modes ( phy_mode ) ) ;
/* fallthrough */
case PHY_INTERFACE_MODE_MII :
2013-09-21 00:50:39 +05:30
mode = AM33XX_GMII_SEL_MODE_MII ;
break ;
2019-01-17 20:59:19 +08:00
}
2013-09-21 00:50:39 +05:30
2014-05-09 19:07:34 +05:30
mask = GMII_SEL_MODE_MASK < < ( slave * 2 ) | BIT ( slave + 6 ) ;
2016-12-06 10:56:51 -08:00
mask | = BIT ( slave + 4 ) ;
2013-09-21 00:50:39 +05:30
mode < < = slave * 2 ;
if ( priv - > rmii_clock_external ) {
if ( slave = = 0 )
mode | = AM33XX_GMII_SEL_RMII1_IO_CLK_EN ;
else
mode | = AM33XX_GMII_SEL_RMII2_IO_CLK_EN ;
}
2016-10-04 19:07:29 +05:30
if ( rgmii_id ) {
if ( slave = = 0 )
mode | = AM33XX_GMII_SEL_RGMII1_IDMODE ;
else
mode | = AM33XX_GMII_SEL_RGMII2_IDMODE ;
}
2013-09-21 00:50:39 +05:30
reg & = ~ mask ;
reg | = mode ;
writel ( reg , priv - > gmii_sel ) ;
}
2014-05-09 19:07:34 +05:30
static void cpsw_gmii_sel_dra7xx ( struct cpsw_phy_sel_priv * priv ,
phy_interface_t phy_mode , int slave )
{
u32 reg ;
u32 mask ;
u32 mode = 0 ;
reg = readl ( priv - > gmii_sel ) ;
switch ( phy_mode ) {
case PHY_INTERFACE_MODE_RMII :
mode = AM33XX_GMII_SEL_MODE_RMII ;
break ;
case PHY_INTERFACE_MODE_RGMII :
case PHY_INTERFACE_MODE_RGMII_ID :
case PHY_INTERFACE_MODE_RGMII_RXID :
case PHY_INTERFACE_MODE_RGMII_TXID :
mode = AM33XX_GMII_SEL_MODE_RGMII ;
break ;
default :
2016-02-12 19:45:36 -05:00
dev_warn ( priv - > dev ,
" Unsupported PHY mode: \" %s \" . Defaulting to MII. \n " ,
phy_modes ( phy_mode ) ) ;
/* fallthrough */
case PHY_INTERFACE_MODE_MII :
2014-05-09 19:07:34 +05:30
mode = AM33XX_GMII_SEL_MODE_MII ;
break ;
2019-01-17 20:59:19 +08:00
}
2014-05-09 19:07:34 +05:30
switch ( slave ) {
case 0 :
mask = GMII_SEL_MODE_MASK ;
break ;
case 1 :
mask = GMII_SEL_MODE_MASK < < 4 ;
mode < < = 4 ;
break ;
default :
dev_err ( priv - > dev , " invalid slave number... \n " ) ;
return ;
}
if ( priv - > rmii_clock_external )
dev_err ( priv - > dev , " RMII External clock is not supported \n " ) ;
reg & = ~ mask ;
reg | = mode ;
writel ( reg , priv - > gmii_sel ) ;
}
2013-09-21 00:50:39 +05:30
static struct platform_driver cpsw_phy_sel_driver ;
2019-06-14 18:53:59 +01:00
static int match ( struct device * dev , const void * data )
2013-09-21 00:50:39 +05:30
{
2019-06-14 18:53:59 +01:00
const struct device_node * node = ( const struct device_node * ) data ;
2013-09-21 00:50:39 +05:30
return dev - > of_node = = node & &
dev - > driver = = & cpsw_phy_sel_driver . driver ;
}
void cpsw_phy_sel ( struct device * dev , phy_interface_t phy_mode , int slave )
{
struct device_node * node ;
struct cpsw_phy_sel_priv * priv ;
2018-08-29 08:00:24 -07:00
node = of_parse_phandle ( dev - > of_node , " cpsw-phy-sel " , 0 ) ;
2013-09-21 00:50:39 +05:30
if ( ! node ) {
2018-08-29 08:00:24 -07:00
node = of_get_child_by_name ( dev - > of_node , " cpsw-phy-sel " ) ;
if ( ! node ) {
dev_err ( dev , " Phy mode driver DT not found \n " ) ;
return ;
}
2013-09-21 00:50:39 +05:30
}
dev = bus_find_device ( & platform_bus_type , NULL , node , match ) ;
2018-05-15 18:37:25 -05:00
if ( ! dev ) {
dev_err ( dev , " unable to find platform device for %pOF \n " , node ) ;
goto out ;
}
2013-09-21 00:50:39 +05:30
priv = dev_get_drvdata ( dev ) ;
priv - > cpsw_phy_sel ( priv , phy_mode , slave ) ;
2016-11-03 18:40:20 +01:00
put_device ( dev ) ;
2018-05-15 18:37:25 -05:00
out :
of_node_put ( node ) ;
2013-09-21 00:50:39 +05:30
}
EXPORT_SYMBOL_GPL ( cpsw_phy_sel ) ;
static const struct of_device_id cpsw_phy_sel_id_table [ ] = {
{
. compatible = " ti,am3352-cpsw-phy-sel " ,
. data = & cpsw_gmii_sel_am3352 ,
} ,
2014-05-09 19:07:34 +05:30
{
. compatible = " ti,dra7xx-cpsw-phy-sel " ,
. data = & cpsw_gmii_sel_dra7xx ,
} ,
2014-05-09 19:07:35 +05:30
{
. compatible = " ti,am43xx-cpsw-phy-sel " ,
. data = & cpsw_gmii_sel_am3352 ,
} ,
2013-09-21 00:50:39 +05:30
{ }
} ;
static int cpsw_phy_sel_probe ( struct platform_device * pdev )
{
struct resource * res ;
const struct of_device_id * of_id ;
struct cpsw_phy_sel_priv * priv ;
of_id = of_match_node ( cpsw_phy_sel_id_table , pdev - > dev . of_node ) ;
if ( ! of_id )
return - EINVAL ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( & pdev - > dev , " unable to alloc memory for cpsw phy sel \n " ) ;
return - ENOMEM ;
}
2014-05-09 19:07:33 +05:30
priv - > dev = & pdev - > dev ;
2013-09-21 00:50:39 +05:30
priv - > cpsw_phy_sel = of_id - > data ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " gmii-sel " ) ;
priv - > gmii_sel = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > gmii_sel ) )
return PTR_ERR ( priv - > gmii_sel ) ;
if ( of_find_property ( pdev - > dev . of_node , " rmii-clock-ext " , NULL ) )
priv - > rmii_clock_external = true ;
dev_set_drvdata ( & pdev - > dev , priv ) ;
return 0 ;
}
static struct platform_driver cpsw_phy_sel_driver = {
. probe = cpsw_phy_sel_probe ,
. driver = {
. name = " cpsw-phy-sel " ,
2013-09-30 09:55:13 +05:30
. of_match_table = cpsw_phy_sel_id_table ,
2013-09-21 00:50:39 +05:30
} ,
} ;
2015-10-07 17:27:46 -04:00
builtin_platform_driver ( cpsw_phy_sel_driver ) ;