2016-06-04 22:17:07 +03:00
/*
* net / dsa / dsa2 . c - Hardware switch handling , binding version 2
* Copyright ( c ) 2008 - 2009 Marvell Semiconductor
* Copyright ( c ) 2013 Florian Fainelli < florian @ openwrt . org >
* Copyright ( c ) 2016 Andrew Lunn < andrew @ lunn . ch >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/list.h>
2017-03-29 00:45:06 +03:00
# include <linux/netdevice.h>
2016-06-04 22:17:07 +03:00
# include <linux/slab.h>
# include <linux/rtnetlink.h>
# include <linux/of.h>
# include <linux/of_net.h>
2019-03-24 13:14:26 +03:00
# include <net/devlink.h>
2017-05-17 22:46:03 +03:00
2016-06-04 22:17:07 +03:00
# include "dsa_priv.h"
2017-11-04 02:05:24 +03:00
static LIST_HEAD ( dsa_tree_list ) ;
2016-06-04 22:17:07 +03:00
static DEFINE_MUTEX ( dsa2_mutex ) ;
2017-03-29 00:45:07 +03:00
static const struct devlink_ops dsa_devlink_ops = {
} ;
2017-11-04 02:05:24 +03:00
static struct dsa_switch_tree * dsa_tree_find ( int index )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch_tree * dst ;
2017-11-04 02:05:24 +03:00
list_for_each_entry ( dst , & dsa_tree_list , list )
2017-11-04 02:05:22 +03:00
if ( dst - > index = = index )
2016-06-04 22:17:07 +03:00
return dst ;
2017-11-04 02:05:22 +03:00
2016-06-04 22:17:07 +03:00
return NULL ;
}
2017-11-04 02:05:24 +03:00
static struct dsa_switch_tree * dsa_tree_alloc ( int index )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch_tree * dst ;
dst = kzalloc ( sizeof ( * dst ) , GFP_KERNEL ) ;
if ( ! dst )
return NULL ;
2017-11-04 02:05:24 +03:00
2017-11-04 02:05:21 +03:00
dst - > index = index ;
2017-11-04 02:05:24 +03:00
2016-06-04 22:17:07 +03:00
INIT_LIST_HEAD ( & dst - > list ) ;
2017-11-04 02:05:24 +03:00
list_add_tail ( & dsa_tree_list , & dst - > list ) ;
2017-11-04 02:05:22 +03:00
2016-06-04 22:17:07 +03:00
kref_init ( & dst - > refcount ) ;
return dst ;
}
2017-11-04 02:05:23 +03:00
static void dsa_tree_free ( struct dsa_switch_tree * dst )
{
list_del ( & dst - > list ) ;
kfree ( dst ) ;
}
2017-11-24 19:36:06 +03:00
static struct dsa_switch_tree * dsa_tree_get ( struct dsa_switch_tree * dst )
2017-11-04 02:05:24 +03:00
{
2017-11-24 19:36:06 +03:00
if ( dst )
kref_get ( & dst - > refcount ) ;
2017-11-04 02:05:24 +03:00
return dst ;
}
2017-11-24 19:36:06 +03:00
static struct dsa_switch_tree * dsa_tree_touch ( int index )
2017-11-04 02:05:23 +03:00
{
2017-11-24 19:36:06 +03:00
struct dsa_switch_tree * dst ;
dst = dsa_tree_find ( index ) ;
if ( dst )
return dsa_tree_get ( dst ) ;
else
return dsa_tree_alloc ( index ) ;
2017-11-04 02:05:23 +03:00
}
static void dsa_tree_release ( struct kref * ref )
{
struct dsa_switch_tree * dst ;
dst = container_of ( ref , struct dsa_switch_tree , refcount ) ;
dsa_tree_free ( dst ) ;
}
static void dsa_tree_put ( struct dsa_switch_tree * dst )
{
2017-11-24 19:36:06 +03:00
if ( dst )
kref_put ( & dst - > refcount , dsa_tree_release ) ;
2017-11-04 02:05:23 +03:00
}
2017-01-26 21:45:52 +03:00
static bool dsa_port_is_dsa ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-10-27 22:55:15 +03:00
return port - > type = = DSA_PORT_TYPE_DSA ;
2017-01-26 21:45:52 +03:00
}
static bool dsa_port_is_cpu ( struct dsa_port * port )
{
2017-10-27 22:55:15 +03:00
return port - > type = = DSA_PORT_TYPE_CPU ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:44 +03:00
static bool dsa_port_is_user ( struct dsa_port * dp )
{
return dp - > type = = DSA_PORT_TYPE_USER ;
}
2017-11-07 00:11:49 +03:00
static struct dsa_port * dsa_tree_find_port_by_node ( struct dsa_switch_tree * dst ,
struct device_node * dn )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch * ds ;
2017-11-07 00:11:49 +03:00
struct dsa_port * dp ;
int device , port ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:49 +03:00
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
2016-06-04 22:17:07 +03:00
if ( ! ds )
continue ;
2017-11-07 00:11:49 +03:00
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
if ( dp - > dn = = dn )
return dp ;
}
2016-06-04 22:17:07 +03:00
}
return NULL ;
}
2017-11-07 00:11:51 +03:00
static bool dsa_port_setup_routing_table ( struct dsa_port * dp )
2016-06-04 22:17:07 +03:00
{
2017-11-07 00:11:51 +03:00
struct dsa_switch * ds = dp - > ds ;
struct dsa_switch_tree * dst = ds - > dst ;
struct device_node * dn = dp - > dn ;
2017-11-07 00:11:50 +03:00
struct of_phandle_iterator it ;
2017-11-07 00:11:49 +03:00
struct dsa_port * link_dp ;
2017-11-07 00:11:50 +03:00
int err ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:50 +03:00
of_for_each_phandle ( & it , err , dn , " link " , NULL , 0 ) {
link_dp = dsa_tree_find_port_by_node ( dst , it . node ) ;
if ( ! link_dp ) {
of_node_put ( it . node ) ;
2017-11-07 00:11:51 +03:00
return false ;
2017-11-07 00:11:50 +03:00
}
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:51 +03:00
ds - > rtable [ link_dp - > ds - > index ] = dp - > index ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:51 +03:00
return true ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:51 +03:00
static bool dsa_switch_setup_routing_table ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
2017-11-07 00:11:51 +03:00
bool complete = true ;
struct dsa_port * dp ;
int i ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:51 +03:00
for ( i = 0 ; i < DSA_MAX_SWITCHES ; i + + )
ds - > rtable [ i ] = DSA_RTABLE_NONE ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:51 +03:00
for ( i = 0 ; i < ds - > num_ports ; i + + ) {
dp = & ds - > ports [ i ] ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:51 +03:00
if ( dsa_port_is_dsa ( dp ) ) {
complete = dsa_port_setup_routing_table ( dp ) ;
if ( ! complete )
break ;
}
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:51 +03:00
return complete ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:51 +03:00
static bool dsa_tree_setup_routing_table ( struct dsa_switch_tree * dst )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch * ds ;
2017-11-07 00:11:51 +03:00
bool complete = true ;
int device ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:51 +03:00
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
2016-06-04 22:17:07 +03:00
if ( ! ds )
continue ;
2017-11-07 00:11:51 +03:00
complete = dsa_switch_setup_routing_table ( ds ) ;
if ( ! complete )
break ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:51 +03:00
return complete ;
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:44 +03:00
static struct dsa_port * dsa_tree_find_first_cpu ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
struct dsa_port * dp ;
int device , port ;
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
if ( ! ds )
continue ;
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
if ( dsa_port_is_cpu ( dp ) )
return dp ;
}
}
return NULL ;
}
static int dsa_tree_setup_default_cpu ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
struct dsa_port * dp ;
int device , port ;
/* DSA currently only supports a single CPU port */
dst - > cpu_dp = dsa_tree_find_first_cpu ( dst ) ;
if ( ! dst - > cpu_dp ) {
pr_warn ( " Tree has no master device \n " ) ;
return - EINVAL ;
}
/* Assign the default CPU port to all ports of the fabric */
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
if ( ! ds )
continue ;
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
2017-12-05 23:34:12 +03:00
if ( dsa_port_is_user ( dp ) | | dsa_port_is_dsa ( dp ) )
2017-11-07 00:11:44 +03:00
dp - > cpu_dp = dst - > cpu_dp ;
}
}
return 0 ;
}
static void dsa_tree_teardown_default_cpu ( struct dsa_switch_tree * dst )
{
/* DSA currently only supports a single CPU port */
dst - > cpu_dp = NULL ;
}
2017-11-07 00:11:48 +03:00
static int dsa_port_setup ( struct dsa_port * dp )
2016-06-04 22:17:07 +03:00
{
2017-11-07 00:11:48 +03:00
struct dsa_switch * ds = dp - > ds ;
2018-05-18 02:55:39 +03:00
int err = 0 ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:48 +03:00
memset ( & dp - > devlink_port , 0 , sizeof ( dp - > devlink_port ) ) ;
2016-06-04 22:17:07 +03:00
2018-05-18 02:55:39 +03:00
if ( dp - > type ! = DSA_PORT_TYPE_UNUSED )
err = devlink_port_register ( ds - > devlink , & dp - > devlink_port ,
dp - > index ) ;
2017-11-07 00:11:48 +03:00
if ( err )
2016-06-04 22:17:07 +03:00
return err ;
2017-11-07 00:11:48 +03:00
switch ( dp - > type ) {
case DSA_PORT_TYPE_UNUSED :
break ;
case DSA_PORT_TYPE_CPU :
2018-05-18 10:29:03 +03:00
/* dp->index is used now as port_number. However
* CPU ports should have separate numbering
* independent from front panel port numbers .
*/
devlink_port_attrs_set ( & dp - > devlink_port ,
DEVLINK_PORT_FLAVOUR_CPU ,
dp - > index , false , 0 ) ;
err = dsa_port_link_register_of ( dp ) ;
if ( err ) {
dev_err ( ds - > dev , " failed to setup link for port %d.%d \n " ,
ds - > index , dp - > index ) ;
return err ;
}
break ;
2017-11-07 00:11:48 +03:00
case DSA_PORT_TYPE_DSA :
2018-05-18 10:29:03 +03:00
/* dp->index is used now as port_number. However
* DSA ports should have separate numbering
* independent from front panel port numbers .
*/
devlink_port_attrs_set ( & dp - > devlink_port ,
DEVLINK_PORT_FLAVOUR_DSA ,
dp - > index , false , 0 ) ;
2018-01-23 18:03:46 +03:00
err = dsa_port_link_register_of ( dp ) ;
2017-11-07 00:11:48 +03:00
if ( err ) {
2018-01-23 18:03:46 +03:00
dev_err ( ds - > dev , " failed to setup link for port %d.%d \n " ,
2017-11-07 00:11:48 +03:00
ds - > index , dp - > index ) ;
return err ;
}
break ;
case DSA_PORT_TYPE_USER :
2018-05-18 10:29:03 +03:00
devlink_port_attrs_set ( & dp - > devlink_port ,
DEVLINK_PORT_FLAVOUR_PHYSICAL ,
dp - > index , false , 0 ) ;
2017-11-07 00:11:48 +03:00
err = dsa_slave_create ( dp ) ;
if ( err )
dev_err ( ds - > dev , " failed to create slave for port %d.%d \n " ,
ds - > index , dp - > index ) ;
else
devlink_port_type_eth_set ( & dp - > devlink_port , dp - > slave ) ;
break ;
2016-06-04 22:17:07 +03:00
}
return 0 ;
}
2017-11-07 00:11:48 +03:00
static void dsa_port_teardown ( struct dsa_port * dp )
2016-06-04 22:17:07 +03:00
{
2018-05-18 02:55:39 +03:00
if ( dp - > type ! = DSA_PORT_TYPE_UNUSED )
devlink_port_unregister ( & dp - > devlink_port ) ;
2017-11-07 00:11:48 +03:00
switch ( dp - > type ) {
case DSA_PORT_TYPE_UNUSED :
break ;
case DSA_PORT_TYPE_CPU :
case DSA_PORT_TYPE_DSA :
2018-01-23 18:03:46 +03:00
dsa_port_link_unregister_of ( dp ) ;
2017-11-07 00:11:48 +03:00
break ;
case DSA_PORT_TYPE_USER :
if ( dp - > slave ) {
dsa_slave_destroy ( dp - > slave ) ;
dp - > slave = NULL ;
}
break ;
2016-06-04 22:17:07 +03:00
}
}
2017-11-07 00:11:47 +03:00
static int dsa_switch_setup ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
int err ;
2016-06-08 02:32:39 +03:00
/* Initialize ds->phys_mii_mask before registering the slave MDIO bus
2016-08-23 19:38:56 +03:00
* driver and before ops - > setup ( ) has run , since the switch drivers and
2016-06-08 02:32:39 +03:00
* the slave MDIO bus driver rely on these values for probing PHY
* devices or not
*/
2017-10-26 18:22:56 +03:00
ds - > phys_mii_mask | = dsa_user_ports ( ds ) ;
2016-06-08 02:32:39 +03:00
2017-03-29 00:45:07 +03:00
/* Add the switch to devlink before calling setup, so that setup can
* add dpipe tables
*/
ds - > devlink = devlink_alloc ( & dsa_devlink_ops , 0 ) ;
if ( ! ds - > devlink )
return - ENOMEM ;
err = devlink_register ( ds - > devlink , ds - > dev ) ;
if ( err )
return err ;
2016-08-23 19:38:56 +03:00
err = ds - > ops - > setup ( ds ) ;
2016-06-04 22:17:07 +03:00
if ( err < 0 )
return err ;
2017-02-03 21:20:20 +03:00
err = dsa_switch_register_notifier ( ds ) ;
if ( err )
return err ;
2016-08-23 19:38:56 +03:00
if ( ! ds - > slave_mii_bus & & ds - > ops - > phy_read ) {
2016-06-08 02:32:40 +03:00
ds - > slave_mii_bus = devm_mdiobus_alloc ( ds - > dev ) ;
if ( ! ds - > slave_mii_bus )
return - ENOMEM ;
dsa_slave_mii_bus_init ( ds ) ;
err = mdiobus_register ( ds - > slave_mii_bus ) ;
if ( err < 0 )
return err ;
}
2016-06-04 22:17:07 +03:00
return 0 ;
}
2017-11-07 00:11:47 +03:00
static void dsa_switch_teardown ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
2016-08-23 19:38:56 +03:00
if ( ds - > slave_mii_bus & & ds - > ops - > phy_read )
2016-06-08 02:32:40 +03:00
mdiobus_unregister ( ds - > slave_mii_bus ) ;
2017-02-03 21:20:20 +03:00
dsa_switch_unregister_notifier ( ds ) ;
2017-03-29 00:45:07 +03:00
if ( ds - > devlink ) {
devlink_unregister ( ds - > devlink ) ;
devlink_free ( ds - > devlink ) ;
ds - > devlink = NULL ;
}
2016-06-04 22:17:07 +03:00
}
2017-11-07 00:11:47 +03:00
static int dsa_tree_setup_switches ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
2017-11-07 00:11:48 +03:00
struct dsa_port * dp ;
int device , port ;
2017-11-07 00:11:47 +03:00
int err ;
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
if ( ! ds )
continue ;
err = dsa_switch_setup ( ds ) ;
if ( err )
return err ;
2017-11-07 00:11:48 +03:00
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
err = dsa_port_setup ( dp ) ;
if ( err )
return err ;
}
2017-11-07 00:11:47 +03:00
}
return 0 ;
}
static void dsa_tree_teardown_switches ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
2017-11-07 00:11:48 +03:00
struct dsa_port * dp ;
int device , port ;
2017-11-07 00:11:47 +03:00
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
if ( ! ds )
continue ;
2017-11-07 00:11:48 +03:00
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
dsa_port_teardown ( dp ) ;
}
2017-11-07 00:11:47 +03:00
dsa_switch_teardown ( ds ) ;
}
}
2017-11-07 00:11:45 +03:00
static int dsa_tree_setup_master ( struct dsa_switch_tree * dst )
{
struct dsa_port * cpu_dp = dst - > cpu_dp ;
struct net_device * master = cpu_dp - > master ;
/* DSA currently supports a single pair of CPU port and master device */
return dsa_master_setup ( master , cpu_dp ) ;
}
static void dsa_tree_teardown_master ( struct dsa_switch_tree * dst )
{
struct dsa_port * cpu_dp = dst - > cpu_dp ;
struct net_device * master = cpu_dp - > master ;
return dsa_master_teardown ( master ) ;
}
2017-11-07 00:11:46 +03:00
static int dsa_tree_setup ( struct dsa_switch_tree * dst )
2016-06-04 22:17:07 +03:00
{
2017-11-07 00:11:51 +03:00
bool complete ;
2016-06-04 22:17:07 +03:00
int err ;
2017-11-07 00:11:46 +03:00
if ( dst - > setup ) {
pr_err ( " DSA: tree %d already setup! Disjoint trees? \n " ,
dst - > index ) ;
return - EEXIST ;
}
2017-11-07 00:11:51 +03:00
complete = dsa_tree_setup_routing_table ( dst ) ;
if ( ! complete )
return 0 ;
2017-11-07 00:11:44 +03:00
err = dsa_tree_setup_default_cpu ( dst ) ;
if ( err )
return err ;
2017-11-07 00:11:47 +03:00
err = dsa_tree_setup_switches ( dst ) ;
if ( err )
return err ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:45 +03:00
err = dsa_tree_setup_master ( dst ) ;
2017-09-19 18:56:59 +03:00
if ( err )
return err ;
2017-11-07 00:11:46 +03:00
dst - > setup = true ;
pr_info ( " DSA: tree %d setup \n " , dst - > index ) ;
2016-06-04 22:17:07 +03:00
return 0 ;
}
2017-11-07 00:11:46 +03:00
static void dsa_tree_teardown ( struct dsa_switch_tree * dst )
2016-06-04 22:17:07 +03:00
{
2017-11-07 00:11:46 +03:00
if ( ! dst - > setup )
2016-06-04 22:17:07 +03:00
return ;
2017-11-07 00:11:45 +03:00
dsa_tree_teardown_master ( dst ) ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:47 +03:00
dsa_tree_teardown_switches ( dst ) ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:44 +03:00
dsa_tree_teardown_default_cpu ( dst ) ;
2016-06-08 02:32:42 +03:00
2017-11-07 00:11:46 +03:00
pr_info ( " DSA: tree %d torn down \n " , dst - > index ) ;
dst - > setup = false ;
2016-06-04 22:17:07 +03:00
}
2017-11-04 02:05:25 +03:00
static void dsa_tree_remove_switch ( struct dsa_switch_tree * dst ,
unsigned int index )
{
2017-11-07 00:11:52 +03:00
dsa_tree_teardown ( dst ) ;
2017-11-04 02:05:25 +03:00
dst - > ds [ index ] = NULL ;
dsa_tree_put ( dst ) ;
}
static int dsa_tree_add_switch ( struct dsa_switch_tree * dst ,
struct dsa_switch * ds )
{
unsigned int index = ds - > index ;
2017-11-07 00:11:52 +03:00
int err ;
2017-11-04 02:05:25 +03:00
if ( dst - > ds [ index ] )
return - EBUSY ;
dsa_tree_get ( dst ) ;
dst - > ds [ index ] = ds ;
2017-11-07 00:11:52 +03:00
err = dsa_tree_setup ( dst ) ;
if ( err )
dsa_tree_remove_switch ( dst , index ) ;
return err ;
2017-11-04 02:05:25 +03:00
}
2017-11-04 02:05:29 +03:00
static int dsa_port_parse_user ( struct dsa_port * dp , const char * name )
{
if ( ! name )
name = " eth%d " ;
dp - > type = DSA_PORT_TYPE_USER ;
dp - > name = name ;
return 0 ;
}
static int dsa_port_parse_dsa ( struct dsa_port * dp )
{
dp - > type = DSA_PORT_TYPE_DSA ;
return 0 ;
}
static int dsa_port_parse_cpu ( struct dsa_port * dp , struct net_device * master )
{
2017-11-04 02:05:30 +03:00
struct dsa_switch * ds = dp - > ds ;
struct dsa_switch_tree * dst = ds - > dst ;
const struct dsa_device_ops * tag_ops ;
enum dsa_tag_protocol tag_protocol ;
2017-11-11 02:22:52 +03:00
tag_protocol = ds - > ops - > get_tag_protocol ( ds , dp - > index ) ;
2017-11-04 02:05:30 +03:00
tag_ops = dsa_resolve_tag_protocol ( tag_protocol ) ;
if ( IS_ERR ( tag_ops ) ) {
dev_warn ( ds - > dev , " No tagger for this switch \n " ) ;
return PTR_ERR ( tag_ops ) ;
}
2017-11-04 02:05:29 +03:00
dp - > type = DSA_PORT_TYPE_CPU ;
2017-11-04 02:05:30 +03:00
dp - > rcv = tag_ops - > rcv ;
dp - > tag_ops = tag_ops ;
2017-11-04 02:05:29 +03:00
dp - > master = master ;
2017-11-04 02:05:30 +03:00
dp - > dst = dst ;
2017-11-04 02:05:29 +03:00
return 0 ;
}
2017-10-27 22:55:14 +03:00
static int dsa_port_parse_of ( struct dsa_port * dp , struct device_node * dn )
{
2017-10-27 22:55:15 +03:00
struct device_node * ethernet = of_parse_phandle ( dn , " ethernet " , 0 ) ;
2017-10-27 22:55:18 +03:00
const char * name = of_get_property ( dn , " label " , NULL ) ;
2017-11-04 02:05:28 +03:00
bool link = of_property_read_bool ( dn , " link " ) ;
2017-10-27 22:55:15 +03:00
2017-11-04 02:05:29 +03:00
dp - > dn = dn ;
2017-10-27 22:55:15 +03:00
if ( ethernet ) {
2017-10-27 22:55:17 +03:00
struct net_device * master ;
master = of_find_net_device_by_node ( ethernet ) ;
if ( ! master )
return - EPROBE_DEFER ;
2017-11-04 02:05:29 +03:00
return dsa_port_parse_cpu ( dp , master ) ;
2017-10-27 22:55:15 +03:00
}
2017-11-04 02:05:29 +03:00
if ( link )
return dsa_port_parse_dsa ( dp ) ;
2017-10-27 22:55:14 +03:00
2017-11-04 02:05:29 +03:00
return dsa_port_parse_user ( dp , name ) ;
2017-10-27 22:55:14 +03:00
}
2017-11-04 02:05:27 +03:00
static int dsa_switch_parse_ports_of ( struct dsa_switch * ds ,
struct device_node * dn )
2016-06-04 22:17:07 +03:00
{
2017-10-27 22:55:13 +03:00
struct device_node * ports , * port ;
2017-10-27 22:55:14 +03:00
struct dsa_port * dp ;
2019-02-25 10:22:19 +03:00
int err = 0 ;
2016-06-04 22:17:07 +03:00
u32 reg ;
2017-10-27 22:55:13 +03:00
ports = of_get_child_by_name ( dn , " ports " ) ;
if ( ! ports ) {
dev_err ( ds - > dev , " no ports child node found \n " ) ;
return - EINVAL ;
}
2016-06-04 22:17:07 +03:00
for_each_available_child_of_node ( ports , port ) {
err = of_property_read_u32 ( port , " reg " , & reg ) ;
if ( err )
2019-02-25 10:22:19 +03:00
goto out_put_node ;
2016-06-04 22:17:07 +03:00
2019-02-25 10:22:19 +03:00
if ( reg > = ds - > num_ports ) {
err = - EINVAL ;
goto out_put_node ;
}
2016-06-04 22:17:07 +03:00
2017-10-27 22:55:14 +03:00
dp = & ds - > ports [ reg ] ;
err = dsa_port_parse_of ( dp , port ) ;
if ( err )
2019-02-25 10:22:19 +03:00
goto out_put_node ;
2016-06-04 22:17:07 +03:00
}
2019-02-25 10:22:19 +03:00
out_put_node :
of_node_put ( ports ) ;
return err ;
2016-06-04 22:17:07 +03:00
}
2017-11-04 02:05:27 +03:00
static int dsa_switch_parse_member_of ( struct dsa_switch * ds ,
struct device_node * dn )
{
u32 m [ 2 ] = { 0 , 0 } ;
int sz ;
/* Don't error out if this optional property isn't found */
sz = of_property_read_variable_u32_array ( dn , " dsa,member " , m , 2 , 2 ) ;
if ( sz < 0 & & sz ! = - EINVAL )
return sz ;
ds - > index = m [ 1 ] ;
if ( ds - > index > = DSA_MAX_SWITCHES )
return - EINVAL ;
ds - > dst = dsa_tree_touch ( m [ 0 ] ) ;
if ( ! ds - > dst )
return - ENOMEM ;
return 0 ;
}
static int dsa_switch_parse_of ( struct dsa_switch * ds , struct device_node * dn )
{
int err ;
err = dsa_switch_parse_member_of ( ds , dn ) ;
if ( err )
return err ;
return dsa_switch_parse_ports_of ( ds , dn ) ;
}
2017-10-27 22:55:14 +03:00
static int dsa_port_parse ( struct dsa_port * dp , const char * name ,
struct device * dev )
{
2017-10-27 22:55:15 +03:00
if ( ! strcmp ( name , " cpu " ) ) {
2017-10-27 22:55:17 +03:00
struct net_device * master ;
master = dsa_dev_to_net_device ( dev ) ;
if ( ! master )
return - EPROBE_DEFER ;
dev_put ( master ) ;
2017-11-04 02:05:29 +03:00
return dsa_port_parse_cpu ( dp , master ) ;
2017-10-27 22:55:15 +03:00
}
2017-11-04 02:05:29 +03:00
if ( ! strcmp ( name , " dsa " ) )
return dsa_port_parse_dsa ( dp ) ;
2017-10-27 22:55:14 +03:00
2017-11-04 02:05:29 +03:00
return dsa_port_parse_user ( dp , name ) ;
2017-10-27 22:55:14 +03:00
}
2017-11-04 02:05:27 +03:00
static int dsa_switch_parse_ports ( struct dsa_switch * ds ,
struct dsa_chip_data * cd )
2017-02-05 00:02:43 +03:00
{
bool valid_name_found = false ;
2017-10-27 22:55:14 +03:00
struct dsa_port * dp ;
struct device * dev ;
const char * name ;
2017-02-05 00:02:43 +03:00
unsigned int i ;
2017-10-27 22:55:14 +03:00
int err ;
2017-02-05 00:02:43 +03:00
for ( i = 0 ; i < DSA_MAX_PORTS ; i + + ) {
2017-10-27 22:55:14 +03:00
name = cd - > port_names [ i ] ;
dev = cd - > netdev [ i ] ;
dp = & ds - > ports [ i ] ;
if ( ! name )
2017-02-05 00:02:43 +03:00
continue ;
2017-10-27 22:55:14 +03:00
err = dsa_port_parse ( dp , name , dev ) ;
if ( err )
return err ;
2017-02-05 00:02:43 +03:00
valid_name_found = true ;
}
if ( ! valid_name_found & & i = = DSA_MAX_PORTS )
return - EINVAL ;
return 0 ;
}
2017-11-04 02:05:27 +03:00
static int dsa_switch_parse ( struct dsa_switch * ds , struct dsa_chip_data * cd )
2017-02-05 00:02:43 +03:00
{
2017-11-04 02:05:27 +03:00
ds - > cd = cd ;
2017-02-05 00:02:43 +03:00
2017-11-04 02:05:27 +03:00
/* We don't support interconnected switches nor multiple trees via
* platform data , so this is the unique switch of the tree .
*/
ds - > index = 0 ;
ds - > dst = dsa_tree_touch ( 0 ) ;
if ( ! ds - > dst )
return - ENOMEM ;
2017-02-05 00:02:43 +03:00
2017-11-04 02:05:27 +03:00
return dsa_switch_parse_ports ( ds , cd ) ;
2017-02-05 00:02:43 +03:00
}
2017-11-07 00:11:52 +03:00
static int dsa_switch_add ( struct dsa_switch * ds )
{
struct dsa_switch_tree * dst = ds - > dst ;
return dsa_tree_add_switch ( dst , ds ) ;
}
2017-11-07 00:11:53 +03:00
static int dsa_switch_probe ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
2017-05-27 01:12:51 +03:00
struct dsa_chip_data * pdata = ds - > dev - > platform_data ;
struct device_node * np = ds - > dev - > of_node ;
2017-11-07 00:11:51 +03:00
int err ;
2016-06-04 22:17:07 +03:00
2017-11-04 02:05:27 +03:00
if ( np )
err = dsa_switch_parse_of ( ds , np ) ;
else if ( pdata )
err = dsa_switch_parse ( ds , pdata ) ;
else
err = - ENODEV ;
2016-06-04 22:17:07 +03:00
2017-11-04 02:05:27 +03:00
if ( err )
return err ;
2016-07-07 03:03:54 +03:00
2017-11-07 00:11:52 +03:00
return dsa_switch_add ( ds ) ;
2016-06-04 22:17:07 +03:00
}
2017-01-27 23:29:36 +03:00
struct dsa_switch * dsa_switch_alloc ( struct device * dev , size_t n )
{
struct dsa_switch * ds ;
2017-01-27 23:29:38 +03:00
int i ;
2017-01-27 23:29:36 +03:00
2019-02-08 04:16:03 +03:00
ds = devm_kzalloc ( dev , struct_size ( ds , ports , n ) , GFP_KERNEL ) ;
2017-01-27 23:29:36 +03:00
if ( ! ds )
return NULL ;
2018-07-17 07:10:34 +03:00
/* We avoid allocating memory outside dsa_switch
* if it is not needed .
*/
if ( n < = sizeof ( ds - > _bitmap ) * 8 ) {
ds - > bitmap = & ds - > _bitmap ;
} else {
ds - > bitmap = devm_kcalloc ( dev ,
BITS_TO_LONGS ( n ) ,
sizeof ( unsigned long ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! ds - > bitmap ) )
return NULL ;
}
2017-01-27 23:29:36 +03:00
ds - > dev = dev ;
ds - > num_ports = n ;
2017-01-27 23:29:38 +03:00
for ( i = 0 ; i < ds - > num_ports ; + + i ) {
ds - > ports [ i ] . index = i ;
ds - > ports [ i ] . ds = ds ;
}
2017-01-27 23:29:36 +03:00
return ds ;
}
EXPORT_SYMBOL_GPL ( dsa_switch_alloc ) ;
2017-05-27 01:12:51 +03:00
int dsa_register_switch ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
int err ;
mutex_lock ( & dsa2_mutex ) ;
2017-11-07 00:11:53 +03:00
err = dsa_switch_probe ( ds ) ;
2017-11-24 19:36:06 +03:00
dsa_tree_put ( ds - > dst ) ;
2016-06-04 22:17:07 +03:00
mutex_unlock ( & dsa2_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( dsa_register_switch ) ;
2017-11-07 00:11:53 +03:00
static void dsa_switch_remove ( struct dsa_switch * ds )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch_tree * dst = ds - > dst ;
2017-11-04 02:05:25 +03:00
unsigned int index = ds - > index ;
2016-06-04 22:17:07 +03:00
2017-11-04 02:05:25 +03:00
dsa_tree_remove_switch ( dst , index ) ;
2016-06-04 22:17:07 +03:00
}
void dsa_unregister_switch ( struct dsa_switch * ds )
{
mutex_lock ( & dsa2_mutex ) ;
2017-11-07 00:11:53 +03:00
dsa_switch_remove ( ds ) ;
2016-06-04 22:17:07 +03:00
mutex_unlock ( & dsa2_mutex ) ;
}
EXPORT_SYMBOL_GPL ( dsa_unregister_switch ) ;