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"
static LIST_HEAD ( dsa_switch_trees ) ;
static DEFINE_MUTEX ( dsa2_mutex ) ;
2017-03-29 00:45:07 +03:00
static const struct devlink_ops dsa_devlink_ops = {
} ;
2016-06-04 22:17:07 +03:00
static struct dsa_switch_tree * dsa_get_dst ( u32 tree )
{
struct dsa_switch_tree * dst ;
list_for_each_entry ( dst , & dsa_switch_trees , list )
2016-11-28 09:48:48 +03:00
if ( dst - > tree = = tree ) {
kref_get ( & dst - > refcount ) ;
2016-06-04 22:17:07 +03:00
return dst ;
2016-11-28 09:48:48 +03:00
}
2016-06-04 22:17:07 +03:00
return NULL ;
}
static void dsa_free_dst ( struct kref * ref )
{
struct dsa_switch_tree * dst = container_of ( ref , struct dsa_switch_tree ,
refcount ) ;
list_del ( & dst - > list ) ;
kfree ( dst ) ;
}
static void dsa_put_dst ( struct dsa_switch_tree * dst )
{
kref_put ( & dst - > refcount , dsa_free_dst ) ;
}
static struct dsa_switch_tree * dsa_add_dst ( u32 tree )
{
struct dsa_switch_tree * dst ;
dst = kzalloc ( sizeof ( * dst ) , GFP_KERNEL ) ;
if ( ! dst )
return NULL ;
dst - > tree = tree ;
INIT_LIST_HEAD ( & dst - > list ) ;
list_add_tail ( & dsa_switch_trees , & dst - > list ) ;
kref_init ( & dst - > refcount ) ;
return dst ;
}
static void dsa_dst_add_ds ( struct dsa_switch_tree * dst ,
struct dsa_switch * ds , u32 index )
{
kref_get ( & dst - > refcount ) ;
dst - > ds [ index ] = ds ;
}
static void dsa_dst_del_ds ( struct dsa_switch_tree * dst ,
struct dsa_switch * ds , u32 index )
{
dst - > ds [ index ] = NULL ;
kref_put ( & dst - > refcount , dsa_free_dst ) ;
}
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-02-05 00:02:43 +03:00
return ! ! ( port - > dn | | port - > name ) ;
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-02-05 00:02:43 +03:00
if ( port - > name & & ! strcmp ( port - > name , " dsa " ) )
return true ;
else
return ! ! of_parse_phandle ( port - > dn , " link " , 0 ) ;
2017-01-26 21:45:52 +03:00
}
static bool dsa_port_is_cpu ( struct dsa_port * port )
{
2017-02-05 00:02:43 +03:00
if ( port - > name & & ! strcmp ( port - > name , " cpu " ) )
return true ;
else
return ! ! of_parse_phandle ( port - > dn , " ethernet " , 0 ) ;
2016-06-04 22:17:07 +03:00
}
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 ;
ds - > dsa_port_mask | = BIT ( index ) ;
}
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-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-08-05 23:20:18 +03:00
err = dsa_cpu_dsa_setup ( 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 ) ;
2016-06-04 22:17:07 +03:00
dsa_cpu_dsa_destroy ( port ) ;
}
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-08-05 23:20:18 +03:00
err = dsa_cpu_dsa_setup ( 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 ) ;
2016-06-04 22:17:07 +03:00
dsa_cpu_dsa_destroy ( port ) ;
2017-06-02 22:31:22 +03:00
port - > ds - > cpu_port_mask & = ~ BIT ( port - > index ) ;
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 ;
2017-02-05 00:02:43 +03:00
const char * name = port - > name ;
2016-06-04 22:17:07 +03:00
int err ;
2017-02-05 00:02:43 +03:00
if ( port - > dn )
name = of_get_property ( port - > dn , " label " , NULL ) ;
2017-01-10 02:13:51 +03:00
if ( ! name )
name = " eth%d " ;
2016-06-04 22:17:07 +03:00
2017-08-05 23:20:19 +03:00
err = dsa_slave_create ( port , name ) ;
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 ) ;
port - > netdev = 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-06-02 22:31:22 +03:00
devlink_port_type_eth_set ( & port - > devlink_port , port - > netdev ) ;
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 ) ;
if ( port - > netdev ) {
dsa_slave_destroy ( port - > netdev ) ;
port - > netdev = NULL ;
port - > ds - > enabled_port_mask & = ~ ( 1 < < port - > index ) ;
2016-06-04 22:17:07 +03:00
}
}
static int dsa_ds_apply ( 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 ;
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
*/
ds - > phys_mii_mask = ds - > enabled_port_mask ;
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-09-19 16:28:01 +03:00
if ( ds - > ops - > set_addr ) {
2017-06-13 23:27:19 +03:00
err = ds - > ops - > set_addr ( ds , dst - > cpu_dp - > netdev - > dev_addr ) ;
2016-09-19 16:28:01 +03:00
if ( err < 0 )
return err ;
}
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
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 ;
}
static void dsa_ds_unapply ( 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 ;
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
}
static int dsa_dst_apply ( 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_apply ( dst , ds ) ;
if ( err )
return err ;
}
2017-09-19 18:56:58 +03:00
err = dsa_cpu_port_ethtool_setup ( dst - > cpu_dp ) ;
if ( err )
return err ;
2016-06-08 02:32:42 +03:00
2016-06-04 22:17:07 +03:00
/* If we use a tagging format that doesn't have an ethertype
* field , make sure that all packets from this point on get
* sent to the tag format ' s receive function .
*/
wmb ( ) ;
2017-06-13 23:27:19 +03:00
dst - > cpu_dp - > netdev - > dsa_ptr = dst ;
2016-06-04 22:17:07 +03:00
dst - > applied = true ;
return 0 ;
}
static void dsa_dst_unapply ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
u32 index ;
if ( ! dst - > applied )
return ;
2017-06-13 23:27:19 +03:00
dst - > cpu_dp - > netdev - > dsa_ptr = NULL ;
2016-06-04 22:17:07 +03:00
/* If we used a tagging format that doesn't have an ethertype
* field , make sure that all packets from this point get sent
* without the tag and go through the regular receive path .
*/
wmb ( ) ;
for ( index = 0 ; index < DSA_MAX_SWITCHES ; index + + ) {
ds = dst - > ds [ index ] ;
if ( ! ds )
continue ;
dsa_ds_unapply ( dst , ds ) ;
}
2017-09-19 18:56:58 +03:00
dsa_cpu_port_ethtool_restore ( dst - > cpu_dp ) ;
dst - > cpu_dp = NULL ;
2016-06-08 02:32:42 +03:00
2016-06-04 22:17:07 +03:00
pr_info ( " DSA: tree %d unapplied \n " , dst - > tree ) ;
dst - > applied = false ;
}
2017-01-26 21:45:52 +03:00
static int dsa_cpu_parse ( struct dsa_port * port , u32 index ,
2016-06-04 22:17:07 +03:00
struct dsa_switch_tree * dst ,
struct dsa_switch * ds )
{
2016-08-22 17:01:01 +03:00
enum dsa_tag_protocol tag_protocol ;
2016-06-04 22:17:07 +03:00
struct net_device * ethernet_dev ;
struct device_node * ethernet ;
2017-02-05 00:02:43 +03:00
if ( port - > dn ) {
ethernet = of_parse_phandle ( port - > dn , " ethernet " , 0 ) ;
if ( ! ethernet )
return - EINVAL ;
ethernet_dev = of_find_net_device_by_node ( ethernet ) ;
} else {
ethernet_dev = dsa_dev_to_net_device ( ds - > cd - > netdev [ index ] ) ;
dev_put ( ethernet_dev ) ;
}
2016-06-04 22:17:07 +03:00
if ( ! ethernet_dev )
return - EPROBE_DEFER ;
2017-06-13 23:27:19 +03:00
if ( ! dst - > cpu_dp ) {
2017-05-16 21:10:33 +03:00
dst - > cpu_dp = port ;
2017-06-13 23:27:19 +03:00
dst - > cpu_dp - > netdev = ethernet_dev ;
}
2016-06-04 22:17:07 +03:00
2017-07-24 20:49:23 +03:00
/* Initialize cpu_port_mask now for drv->setup()
* to have access to a correct value , just like what
* net / dsa / dsa . c : : dsa_switch_setup_one does .
*/
ds - > cpu_port_mask | = BIT ( index ) ;
2016-08-23 19:38:56 +03:00
tag_protocol = ds - > ops - > get_tag_protocol ( ds ) ;
2016-08-22 17:01:01 +03:00
dst - > tag_ops = dsa_resolve_tag_protocol ( tag_protocol ) ;
2016-06-04 22:17:07 +03:00
if ( IS_ERR ( dst - > tag_ops ) ) {
dev_warn ( ds - > dev , " No tagger for this switch \n " ) ;
2017-07-24 20:49:23 +03:00
ds - > cpu_port_mask & = ~ BIT ( index ) ;
2016-06-04 22:17:07 +03:00
return PTR_ERR ( dst - > tag_ops ) ;
}
dst - > rcv = dst - > tag_ops - > rcv ;
return 0 ;
}
static int dsa_ds_parse ( 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 ] ;
2017-06-02 22:31:23 +03:00
if ( ! dsa_port_is_valid ( port ) | |
dsa_port_is_dsa ( port ) )
2016-06-04 22:17:07 +03:00
continue ;
if ( dsa_port_is_cpu ( port ) ) {
err = dsa_cpu_parse ( port , index , dst , ds ) ;
if ( err )
return err ;
2017-06-02 22:31:23 +03:00
} else {
/* Initialize enabled_port_mask now for drv->setup()
* to have access to a correct value , just like what
* net / dsa / dsa . c : : dsa_switch_setup_one does .
*/
ds - > enabled_port_mask | = BIT ( index ) ;
2016-06-04 22:17:07 +03:00
}
2017-06-02 22:31:23 +03:00
2016-06-04 22:17:07 +03:00
}
pr_info ( " DSA: switch %d %d parsed \n " , dst - > tree , ds - > index ) ;
return 0 ;
}
static int dsa_dst_parse ( struct dsa_switch_tree * dst )
{
struct dsa_switch * ds ;
2017-06-15 22:06:54 +03:00
struct dsa_port * dp ;
2016-06-04 22:17:07 +03:00
u32 index ;
2017-06-15 22:06:54 +03:00
int port ;
2016-06-04 22:17:07 +03:00
int err ;
for ( index = 0 ; index < DSA_MAX_SWITCHES ; index + + ) {
ds = dst - > ds [ index ] ;
if ( ! ds )
continue ;
err = dsa_ds_parse ( dst , ds ) ;
if ( err )
return err ;
}
2017-08-29 03:10:51 +03:00
if ( ! dst - > cpu_dp ) {
2016-06-04 22:17:07 +03:00
pr_warn ( " Tree has no master device \n " ) ;
return - EINVAL ;
}
2017-06-15 22:06:54 +03:00
/* Assign the default CPU port to all ports of the fabric */
for ( index = 0 ; index < DSA_MAX_SWITCHES ; index + + ) {
ds = dst - > ds [ index ] ;
if ( ! ds )
continue ;
for ( port = 0 ; port < ds - > num_ports ; port + + ) {
dp = & ds - > ports [ port ] ;
if ( ! dsa_port_is_valid ( dp ) | |
dsa_port_is_dsa ( dp ) | |
dsa_port_is_cpu ( dp ) )
continue ;
dp - > cpu_dp = dst - > cpu_dp ;
}
}
2016-06-04 22:17:07 +03:00
pr_info ( " DSA: tree %d parsed \n " , dst - > tree ) ;
return 0 ;
}
static int dsa_parse_ports_dn ( struct device_node * ports , struct dsa_switch * ds )
{
struct device_node * port ;
int err ;
u32 reg ;
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 ;
ds - > ports [ reg ] . dn = port ;
}
return 0 ;
}
2017-02-05 00:02:43 +03:00
static int dsa_parse_ports ( struct dsa_chip_data * cd , struct dsa_switch * ds )
{
bool valid_name_found = false ;
unsigned int i ;
for ( i = 0 ; i < DSA_MAX_PORTS ; i + + ) {
if ( ! cd - > port_names [ i ] )
continue ;
ds - > ports [ i ] . name = cd - > port_names [ i ] ;
valid_name_found = true ;
}
if ( ! valid_name_found & & i = = DSA_MAX_PORTS )
return - EINVAL ;
return 0 ;
}
2017-01-26 21:45:53 +03:00
static int dsa_parse_member_dn ( struct device_node * np , u32 * tree , u32 * index )
2016-06-04 22:17:07 +03:00
{
int err ;
* tree = * index = 0 ;
err = of_property_read_u32_index ( np , " dsa,member " , 0 , tree ) ;
if ( err ) {
/* Does not exist, but it is optional */
if ( err = = - EINVAL )
return 0 ;
return err ;
}
err = of_property_read_u32_index ( np , " dsa,member " , 1 , index ) ;
if ( err )
return err ;
if ( * index > = DSA_MAX_SWITCHES )
return - EINVAL ;
return 0 ;
}
2017-02-05 00:02:43 +03:00
static int dsa_parse_member ( struct dsa_chip_data * pd , u32 * tree , u32 * index )
{
if ( ! pd )
return - ENODEV ;
/* We do not support complex trees with dsa_chip_data */
* tree = 0 ;
* index = 0 ;
return 0 ;
}
2016-06-04 22:17:07 +03:00
static struct device_node * dsa_get_ports ( struct dsa_switch * ds ,
struct device_node * np )
{
struct device_node * ports ;
ports = of_get_child_by_name ( np , " ports " ) ;
if ( ! ports ) {
dev_err ( ds - > dev , " no ports child node found \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
return ports ;
}
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-01-26 21:45:54 +03:00
struct device_node * ports ;
2016-06-04 22:17:07 +03:00
u32 tree , index ;
2016-07-07 03:03:54 +03:00
int i , err ;
2016-06-04 22:17:07 +03:00
2017-02-05 00:02:43 +03:00
if ( np ) {
err = dsa_parse_member_dn ( np , & tree , & index ) ;
if ( err )
return err ;
2016-06-04 22:17:07 +03:00
2017-02-05 00:02:43 +03:00
ports = dsa_get_ports ( ds , np ) ;
if ( IS_ERR ( ports ) )
return PTR_ERR ( ports ) ;
2016-06-04 22:17:07 +03:00
2017-02-05 00:02:43 +03:00
err = dsa_parse_ports_dn ( ports , ds ) ;
if ( err )
return err ;
} else {
err = dsa_parse_member ( pdata , & tree , & index ) ;
if ( err )
return err ;
err = dsa_parse_ports ( pdata , ds ) ;
if ( err )
return err ;
}
2016-06-04 22:17:07 +03:00
dst = dsa_get_dst ( tree ) ;
if ( ! dst ) {
dst = dsa_add_dst ( tree ) ;
if ( ! dst )
return - ENOMEM ;
}
if ( dst - > ds [ index ] ) {
err = - EBUSY ;
goto out ;
}
ds - > dst = dst ;
ds - > index = index ;
2017-02-05 00:02:43 +03:00
ds - > cd = pdata ;
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 ;
2016-06-04 22:17:07 +03:00
dsa_dst_add_ds ( dst , ds , index ) ;
err = dsa_dst_complete ( dst ) ;
if ( err < 0 )
goto out_del_dst ;
if ( err = = 1 ) {
/* Not all switches registered yet */
err = 0 ;
goto out ;
}
if ( dst - > applied ) {
pr_info ( " DSA: Disjoint trees? \n " ) ;
return - EINVAL ;
}
err = dsa_dst_parse ( dst ) ;
2017-01-05 13:10:13 +03:00
if ( err ) {
if ( err = = - EPROBE_DEFER ) {
dsa_dst_del_ds ( dst , ds , ds - > index ) ;
return err ;
}
2016-06-04 22:17:07 +03:00
goto out_del_dst ;
2017-01-05 13:10:13 +03:00
}
2016-06-04 22:17:07 +03:00
err = dsa_dst_apply ( dst ) ;
if ( err ) {
dsa_dst_unapply ( dst ) ;
goto out_del_dst ;
}
dsa_put_dst ( dst ) ;
return 0 ;
out_del_dst :
dsa_dst_del_ds ( dst , ds , ds - > index ) ;
out :
dsa_put_dst ( dst ) ;
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 ;
dsa_dst_unapply ( dst ) ;
dsa_dst_del_ds ( dst , ds , ds - > index ) ;
}
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 ) ;