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>
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
/* Initialize the reference counter to the number of switches, not 1 */
2016-06-04 22:17:07 +03:00
kref_init ( & dst - > refcount ) ;
2017-11-04 02:05:22 +03:00
refcount_set ( & dst - > refcount . refcount , 0 ) ;
2016-06-04 22:17:07 +03:00
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-04 02:05:24 +03:00
static struct dsa_switch_tree * dsa_tree_touch ( int index )
{
struct dsa_switch_tree * dst ;
dst = dsa_tree_find ( index ) ;
if ( ! dst )
dst = dsa_tree_alloc ( index ) ;
return dst ;
}
2017-11-04 02:05:23 +03:00
static void dsa_tree_get ( struct dsa_switch_tree * dst )
{
kref_get ( & dst - > refcount ) ;
}
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 )
{
kref_put ( & dst - > refcount , dsa_tree_release ) ;
}
2017-02-05 00:02:43 +03:00
/* For platform data configurations, we need to have a valid name argument to
* differentiate a disabled port from an enabled one
*/
2017-01-26 21:45:52 +03:00
static bool dsa_port_is_valid ( 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_UNUSED ;
2016-06-04 22:17:07 +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-01-26 21:45:53 +03:00
static bool dsa_ds_find_port_dn ( struct dsa_switch * ds ,
struct device_node * port )
2016-06-04 22:17:07 +03:00
{
u32 index ;
2017-01-27 23:29:37 +03:00
for ( index = 0 ; index < ds - > num_ports ; index + + )
2016-06-04 22:17:07 +03:00
if ( ds - > ports [ index ] . dn = = port )
return true ;
return false ;
}
2017-01-26 21:45:53 +03:00
static struct dsa_switch * dsa_dst_find_port_dn ( struct dsa_switch_tree * dst ,
struct device_node * port )
2016-06-04 22:17:07 +03:00
{
struct dsa_switch * ds ;
u32 index ;
for ( index = 0 ; index < DSA_MAX_SWITCHES ; index + + ) {
ds = dst - > ds [ index ] ;
if ( ! ds )
continue ;
2017-01-26 21:45:53 +03:00
if ( dsa_ds_find_port_dn ( ds , port ) )
2016-06-04 22:17:07 +03:00
return ds ;
}
return NULL ;
}
static int dsa_port_complete ( struct dsa_switch_tree * dst ,
struct dsa_switch * src_ds ,
2017-01-26 21:45:52 +03:00
struct dsa_port * port ,
2016-06-04 22:17:07 +03:00
u32 src_port )
{
struct device_node * link ;
int index ;
struct dsa_switch * dst_ds ;
for ( index = 0 ; ; index + + ) {
2017-01-26 21:45:52 +03:00
link = of_parse_phandle ( port - > dn , " link " , index ) ;
2016-06-04 22:17:07 +03:00
if ( ! link )
break ;
2017-01-26 21:45:53 +03:00
dst_ds = dsa_dst_find_port_dn ( dst , link ) ;
2016-06-04 22:17:07 +03:00
of_node_put ( link ) ;
if ( ! dst_ds )
return 1 ;
src_ds - > rtable [ dst_ds - > index ] = src_port ;
}
return 0 ;
}
/* A switch is complete if all the DSA ports phandles point to ports
* known in the tree . A return value of 1 means the tree is not
* complete . This is not an error condition . A value of 0 is
* success .
*/
static int dsa_ds_complete ( struct dsa_switch_tree * dst , struct dsa_switch * ds )
{
2017-01-26 21:45:52 +03:00
struct dsa_port * port ;
2016-06-04 22:17:07 +03:00
u32 index ;
int err ;
2017-01-27 23:29:37 +03:00
for ( index = 0 ; index < ds - > num_ports ; index + + ) {
2017-01-26 21:45:52 +03:00
port = & ds - > ports [ index ] ;
if ( ! dsa_port_is_valid ( port ) )
2016-06-04 22:17:07 +03:00
continue ;
if ( ! dsa_port_is_dsa ( port ) )
continue ;
err = dsa_port_complete ( dst , ds , port , index ) ;
if ( err ! = 0 )
return err ;
}
return 0 ;
}
/* A tree is complete if all the DSA ports phandles point to ports
* known in the tree . A return value of 1 means the tree is not
* complete . This is not an error condition . A value of 0 is
* success .
*/
static int dsa_dst_complete ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
u32 index ;
int err ;
for ( index = 0 ; index < DSA_MAX_SWITCHES ; index + + ) {
ds = dst - > ds [ index ] ;
if ( ! ds )
continue ;
err = dsa_ds_complete ( dst , ds ) ;
if ( err ! = 0 )
return err ;
}
return 0 ;
}
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 ] ;
if ( dsa_port_is_user ( dp ) )
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-06-02 22:31:22 +03:00
static int dsa_dsa_port_apply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
struct dsa_switch * ds = port - > ds ;
2016-06-04 22:17:07 +03:00
int err ;
2017-10-26 17:50:07 +03:00
err = dsa_port_fixed_link_register_of ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err ) {
dev_warn ( ds - > dev , " Failed to setup dsa port %d: %d \n " ,
2017-06-02 22:31:22 +03:00
port - > index , err ) ;
2016-06-04 22:17:07 +03:00
return err ;
}
2017-06-02 22:31:22 +03:00
memset ( & port - > devlink_port , 0 , sizeof ( port - > devlink_port ) ) ;
2017-03-29 00:45:07 +03:00
2017-06-02 22:31:22 +03:00
return devlink_port_register ( ds - > devlink , & port - > devlink_port ,
port - > index ) ;
2016-06-04 22:17:07 +03:00
}
2017-06-02 22:31:22 +03:00
static void dsa_dsa_port_unapply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
devlink_port_unregister ( & port - > devlink_port ) ;
2017-10-26 17:50:07 +03:00
dsa_port_fixed_link_unregister_of ( port ) ;
2016-06-04 22:17:07 +03:00
}
2017-06-02 22:31:22 +03:00
static int dsa_cpu_port_apply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
struct dsa_switch * ds = port - > ds ;
2016-06-04 22:17:07 +03:00
int err ;
2017-10-26 17:50:07 +03:00
err = dsa_port_fixed_link_register_of ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err ) {
dev_warn ( ds - > dev , " Failed to setup cpu port %d: %d \n " ,
2017-06-02 22:31:22 +03:00
port - > index , err ) ;
2016-06-04 22:17:07 +03:00
return err ;
}
2017-06-02 22:31:22 +03:00
memset ( & port - > devlink_port , 0 , sizeof ( port - > devlink_port ) ) ;
err = devlink_port_register ( ds - > devlink , & port - > devlink_port ,
port - > index ) ;
2017-03-29 00:45:07 +03:00
return err ;
2016-06-04 22:17:07 +03:00
}
2017-06-02 22:31:22 +03:00
static void dsa_cpu_port_unapply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
devlink_port_unregister ( & port - > devlink_port ) ;
2017-10-26 17:50:07 +03:00
dsa_port_fixed_link_unregister_of ( port ) ;
2016-06-04 22:17:07 +03:00
}
2017-06-02 22:31:22 +03:00
static int dsa_user_port_apply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
struct dsa_switch * ds = port - > ds ;
2016-06-04 22:17:07 +03:00
int err ;
2017-10-27 22:55:19 +03:00
err = dsa_slave_create ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err ) {
dev_warn ( ds - > dev , " Failed to create slave %d: %d \n " ,
2017-06-02 22:31:22 +03:00
port - > index , err ) ;
2017-10-16 18:12:18 +03:00
port - > slave = NULL ;
2016-06-04 22:17:07 +03:00
return err ;
}
2017-06-02 22:31:22 +03:00
memset ( & port - > devlink_port , 0 , sizeof ( port - > devlink_port ) ) ;
err = devlink_port_register ( ds - > devlink , & port - > devlink_port ,
port - > index ) ;
2017-03-29 00:45:07 +03:00
if ( err )
return err ;
2017-10-16 18:12:18 +03:00
devlink_port_type_eth_set ( & port - > devlink_port , port - > slave ) ;
2017-03-29 00:45:07 +03:00
2016-06-04 22:17:07 +03:00
return 0 ;
}
2017-06-02 22:31:22 +03:00
static void dsa_user_port_unapply ( struct dsa_port * port )
2016-06-04 22:17:07 +03:00
{
2017-06-02 22:31:22 +03:00
devlink_port_unregister ( & port - > devlink_port ) ;
2017-10-16 18:12:18 +03:00
if ( port - > slave ) {
dsa_slave_destroy ( port - > slave ) ;
port - > slave = NULL ;
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
{
2017-01-26 21:45:52 +03:00
struct dsa_port * port ;
2016-06-04 22:17:07 +03:00
u32 index ;
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 ;
}
2017-01-27 23:29:37 +03:00
for ( index = 0 ; index < ds - > num_ports ; index + + ) {
2017-01-26 21:45:52 +03:00
port = & ds - > ports [ index ] ;
if ( ! dsa_port_is_valid ( port ) )
2016-06-04 22:17:07 +03:00
continue ;
if ( dsa_port_is_dsa ( port ) ) {
2017-06-02 22:31:22 +03:00
err = dsa_dsa_port_apply ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err )
return err ;
continue ;
}
if ( dsa_port_is_cpu ( port ) ) {
2017-06-02 22:31:22 +03:00
err = dsa_cpu_port_apply ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err )
return err ;
continue ;
}
2017-06-02 22:31:22 +03:00
err = dsa_user_port_apply ( port ) ;
2016-06-04 22:17:07 +03:00
if ( err )
continue ;
}
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
{
2017-01-26 21:45:52 +03:00
struct dsa_port * port ;
2016-06-04 22:17:07 +03:00
u32 index ;
2017-01-27 23:29:37 +03:00
for ( index = 0 ; index < ds - > num_ports ; index + + ) {
2017-01-26 21:45:52 +03:00
port = & ds - > ports [ index ] ;
if ( ! dsa_port_is_valid ( port ) )
2016-06-04 22:17:07 +03:00
continue ;
if ( dsa_port_is_dsa ( port ) ) {
2017-06-02 22:31:22 +03:00
dsa_dsa_port_unapply ( port ) ;
2016-06-04 22:17:07 +03:00
continue ;
}
if ( dsa_port_is_cpu ( port ) ) {
2017-06-02 22:31:22 +03:00
dsa_cpu_port_unapply ( port ) ;
2016-06-04 22:17:07 +03:00
continue ;
}
2017-06-02 22:31:22 +03:00
dsa_user_port_unapply ( port ) ;
2016-06-04 22:17:07 +03:00
}
2016-06-08 02:32:40 +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 ;
int device ;
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 ;
}
return 0 ;
}
static void dsa_tree_teardown_switches ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
int device ;
for ( device = 0 ; device < DSA_MAX_SWITCHES ; device + + ) {
ds = dst - > ds [ device ] ;
if ( ! ds )
continue ;
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
{
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: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 )
{
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 ;
if ( dst - > ds [ index ] )
return - EBUSY ;
dsa_tree_get ( dst ) ;
dst - > ds [ index ] = ds ;
return 0 ;
}
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 ;
tag_protocol = ds - > ops - > get_tag_protocol ( ds ) ;
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 ;
2016-06-04 22:17:07 +03:00
u32 reg ;
2017-10-27 22:55:13 +03:00
int err ;
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 )
return err ;
2017-01-27 23:29:37 +03:00
if ( reg > = ds - > num_ports )
2016-06-04 22:17:07 +03:00
return - EINVAL ;
2017-10-27 22:55:14 +03:00
dp = & ds - > ports [ reg ] ;
err = dsa_port_parse_of ( dp , port ) ;
if ( err )
return err ;
2016-06-04 22:17:07 +03:00
}
return 0 ;
}
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-05-27 01:12:51 +03:00
static int _dsa_register_switch ( 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 ;
2016-06-04 22:17:07 +03:00
struct dsa_switch_tree * dst ;
2017-11-04 02:05:27 +03:00
unsigned int index ;
2016-07-07 03:03:54 +03:00
int i , 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-04 02:05:27 +03:00
index = ds - > index ;
dst = ds - > dst ;
2017-11-04 02:05:26 +03:00
2016-07-07 03:03:54 +03:00
/* Initialize the routing table */
for ( i = 0 ; i < DSA_MAX_SWITCHES ; + + i )
ds - > rtable [ i ] = DSA_RTABLE_NONE ;
2017-11-04 02:05:25 +03:00
err = dsa_tree_add_switch ( dst , ds ) ;
if ( err )
return err ;
2016-06-04 22:17:07 +03:00
err = dsa_dst_complete ( dst ) ;
if ( err < 0 )
goto out_del_dst ;
2017-11-04 02:05:22 +03:00
/* Not all switches registered yet */
if ( err = = 1 )
return 0 ;
2016-06-04 22:17:07 +03:00
2017-11-07 00:11:46 +03:00
err = dsa_tree_setup ( dst ) ;
2016-06-04 22:17:07 +03:00
if ( err ) {
2017-11-07 00:11:46 +03:00
dsa_tree_teardown ( dst ) ;
2016-06-04 22:17:07 +03:00
goto out_del_dst ;
}
return 0 ;
out_del_dst :
2017-11-04 02:05:25 +03:00
dsa_tree_remove_switch ( dst , index ) ;
2016-06-04 22:17:07 +03:00
return err ;
}
2017-01-27 23:29:36 +03:00
struct dsa_switch * dsa_switch_alloc ( struct device * dev , size_t n )
{
size_t size = sizeof ( struct dsa_switch ) + n * sizeof ( struct dsa_port ) ;
struct dsa_switch * ds ;
2017-01-27 23:29:38 +03:00
int i ;
2017-01-27 23:29:36 +03:00
ds = devm_kzalloc ( dev , size , GFP_KERNEL ) ;
if ( ! ds )
return NULL ;
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-05-27 01:12:51 +03:00
err = _dsa_register_switch ( ds ) ;
2016-06-04 22:17:07 +03:00
mutex_unlock ( & dsa2_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( dsa_register_switch ) ;
2016-07-12 18:24:10 +03:00
static void _dsa_unregister_switch ( 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-07 00:11:46 +03:00
dsa_tree_teardown ( dst ) ;
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 ) ;
_dsa_unregister_switch ( ds ) ;
mutex_unlock ( & dsa2_mutex ) ;
}
EXPORT_SYMBOL_GPL ( dsa_unregister_switch ) ;