12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a40c175bSVivien Didelot /* 3a40c175bSVivien Didelot * Handling of a single switch port 4a40c175bSVivien Didelot * 5a40c175bSVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 6a40c175bSVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7a40c175bSVivien Didelot */ 8a40c175bSVivien Didelot 9a40c175bSVivien Didelot #include <linux/if_bridge.h> 10cfbed329SVivien Didelot #include <linux/notifier.h> 1157ab1ca2SVivien Didelot #include <linux/of_mdio.h> 1257ab1ca2SVivien Didelot #include <linux/of_net.h> 13a40c175bSVivien Didelot 14a40c175bSVivien Didelot #include "dsa_priv.h" 15a40c175bSVivien Didelot 16886f8e26SVladimir Oltean /** 17886f8e26SVladimir Oltean * dsa_port_notify - Notify the switching fabric of changes to a port 18886f8e26SVladimir Oltean * @dp: port on which change occurred 19886f8e26SVladimir Oltean * @e: event, must be of type DSA_NOTIFIER_* 20886f8e26SVladimir Oltean * @v: event-specific value. 21886f8e26SVladimir Oltean * 22886f8e26SVladimir Oltean * Notify all switches in the DSA tree that this port's switch belongs to, 23886f8e26SVladimir Oltean * including this switch itself, of an event. Allows the other switches to 24886f8e26SVladimir Oltean * reconfigure themselves for cross-chip operations. Can also be used to 25886f8e26SVladimir Oltean * reconfigure ports without net_devices (CPU ports, DSA links) whenever 26886f8e26SVladimir Oltean * a user port's state changes. 27886f8e26SVladimir Oltean */ 28bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) 29cfbed329SVivien Didelot { 30886f8e26SVladimir Oltean return dsa_tree_notify(dp->ds->dst, e, v); 31cfbed329SVivien Didelot } 32cfbed329SVivien Didelot 33bae33f2bSVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state) 34a40c175bSVivien Didelot { 35a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 36a40c175bSVivien Didelot int port = dp->index; 37a40c175bSVivien Didelot 38bae33f2bSVladimir Oltean if (!ds->ops->port_stp_state_set) 39bae33f2bSVladimir Oltean return -EOPNOTSUPP; 40a40c175bSVivien Didelot 41a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 42a40c175bSVivien Didelot 43a40c175bSVivien Didelot if (ds->ops->port_fast_age) { 44a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 45a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 46a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 47a40c175bSVivien Didelot */ 48a40c175bSVivien Didelot 49a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 50a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 51a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 52a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 53a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 54a40c175bSVivien Didelot ds->ops->port_fast_age(ds, port); 55a40c175bSVivien Didelot } 56a40c175bSVivien Didelot 57a40c175bSVivien Didelot dp->stp_state = state; 58a40c175bSVivien Didelot 59a40c175bSVivien Didelot return 0; 60a40c175bSVivien Didelot } 61a40c175bSVivien Didelot 62fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) 63a40c175bSVivien Didelot { 64a40c175bSVivien Didelot int err; 65a40c175bSVivien Didelot 66bae33f2bSVladimir Oltean err = dsa_port_set_state(dp, state); 67a40c175bSVivien Didelot if (err) 68a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 69a40c175bSVivien Didelot } 70cfbed329SVivien Didelot 718640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 72fb8a6a2bSVivien Didelot { 73fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 74fb8a6a2bSVivien Didelot int port = dp->index; 75fb8a6a2bSVivien Didelot int err; 76fb8a6a2bSVivien Didelot 77fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 78fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 79fb8a6a2bSVivien Didelot if (err) 80fb8a6a2bSVivien Didelot return err; 81fb8a6a2bSVivien Didelot } 82fb8a6a2bSVivien Didelot 839c2054a5SRussell King if (!dp->bridge_dev) 849c2054a5SRussell King dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 85fb8a6a2bSVivien Didelot 868640f8dcSRussell King if (dp->pl) 878640f8dcSRussell King phylink_start(dp->pl); 888640f8dcSRussell King 89fb8a6a2bSVivien Didelot return 0; 90fb8a6a2bSVivien Didelot } 91fb8a6a2bSVivien Didelot 928640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 938640f8dcSRussell King { 948640f8dcSRussell King int err; 958640f8dcSRussell King 968640f8dcSRussell King rtnl_lock(); 978640f8dcSRussell King err = dsa_port_enable_rt(dp, phy); 988640f8dcSRussell King rtnl_unlock(); 998640f8dcSRussell King 1008640f8dcSRussell King return err; 1018640f8dcSRussell King } 1028640f8dcSRussell King 1038640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp) 104fb8a6a2bSVivien Didelot { 105fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 106fb8a6a2bSVivien Didelot int port = dp->index; 107fb8a6a2bSVivien Didelot 1088640f8dcSRussell King if (dp->pl) 1098640f8dcSRussell King phylink_stop(dp->pl); 1108640f8dcSRussell King 1119c2054a5SRussell King if (!dp->bridge_dev) 112fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 113fb8a6a2bSVivien Didelot 114fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 11575104db0SAndrew Lunn ds->ops->port_disable(ds, port); 116fb8a6a2bSVivien Didelot } 117fb8a6a2bSVivien Didelot 1188640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp) 1198640f8dcSRussell King { 1208640f8dcSRussell King rtnl_lock(); 1218640f8dcSRussell King dsa_port_disable_rt(dp); 1228640f8dcSRussell King rtnl_unlock(); 1238640f8dcSRussell King } 1248640f8dcSRussell King 125cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 126cfbed329SVivien Didelot { 127cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 128f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 129cfbed329SVivien Didelot .sw_index = dp->ds->index, 130cfbed329SVivien Didelot .port = dp->index, 131cfbed329SVivien Didelot .br = br, 132cfbed329SVivien Didelot }; 133cfbed329SVivien Didelot int err; 134cfbed329SVivien Didelot 135c1388063SRussell King /* Set the flooding mode before joining the port in the switch */ 136bae33f2bSVladimir Oltean err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD); 137c1388063SRussell King if (err) 138c1388063SRussell King return err; 139c1388063SRussell King 140c1388063SRussell King /* Here the interface is already bridged. Reflect the current 141c1388063SRussell King * configuration so that drivers can program their chips accordingly. 142cfbed329SVivien Didelot */ 143cfbed329SVivien Didelot dp->bridge_dev = br; 144cfbed329SVivien Didelot 145f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); 146cfbed329SVivien Didelot 147cfbed329SVivien Didelot /* The bridging is rolled back on error */ 148c1388063SRussell King if (err) { 149bae33f2bSVladimir Oltean dsa_port_bridge_flags(dp, 0); 150cfbed329SVivien Didelot dp->bridge_dev = NULL; 151c1388063SRussell King } 152cfbed329SVivien Didelot 153cfbed329SVivien Didelot return err; 154cfbed329SVivien Didelot } 155cfbed329SVivien Didelot 156cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 157cfbed329SVivien Didelot { 158cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 159f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 160cfbed329SVivien Didelot .sw_index = dp->ds->index, 161cfbed329SVivien Didelot .port = dp->index, 162cfbed329SVivien Didelot .br = br, 163cfbed329SVivien Didelot }; 164cfbed329SVivien Didelot int err; 165cfbed329SVivien Didelot 166cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 167cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 168cfbed329SVivien Didelot */ 169cfbed329SVivien Didelot dp->bridge_dev = NULL; 170cfbed329SVivien Didelot 171f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 172cfbed329SVivien Didelot if (err) 173cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 174cfbed329SVivien Didelot 175c1388063SRussell King /* Port is leaving the bridge, disable flooding */ 176bae33f2bSVladimir Oltean dsa_port_bridge_flags(dp, 0); 177c1388063SRussell King 178cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 179cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 180cfbed329SVivien Didelot */ 181cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 182cfbed329SVivien Didelot } 1834d61d304SVivien Didelot 184058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 185058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 186058102a6STobias Waldekranz { 187058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 188058102a6STobias Waldekranz .sw_index = dp->ds->index, 189058102a6STobias Waldekranz .port = dp->index, 190058102a6STobias Waldekranz }; 191058102a6STobias Waldekranz bool tx_enabled; 192058102a6STobias Waldekranz 193058102a6STobias Waldekranz if (!dp->lag_dev) 194058102a6STobias Waldekranz return 0; 195058102a6STobias Waldekranz 196058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 197058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 198058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 199058102a6STobias Waldekranz * in order to include it in the tx set. 200058102a6STobias Waldekranz */ 201058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 202058102a6STobias Waldekranz 203058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 204058102a6STobias Waldekranz return 0; 205058102a6STobias Waldekranz 206058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 207058102a6STobias Waldekranz 208058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 209058102a6STobias Waldekranz } 210058102a6STobias Waldekranz 211058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, 212058102a6STobias Waldekranz struct netdev_lag_upper_info *uinfo) 213058102a6STobias Waldekranz { 214058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 215058102a6STobias Waldekranz .sw_index = dp->ds->index, 216058102a6STobias Waldekranz .port = dp->index, 217058102a6STobias Waldekranz .lag = lag, 218058102a6STobias Waldekranz .info = uinfo, 219058102a6STobias Waldekranz }; 220058102a6STobias Waldekranz int err; 221058102a6STobias Waldekranz 222058102a6STobias Waldekranz dsa_lag_map(dp->ds->dst, lag); 223058102a6STobias Waldekranz dp->lag_dev = lag; 224058102a6STobias Waldekranz 225058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 226058102a6STobias Waldekranz if (err) { 227058102a6STobias Waldekranz dp->lag_dev = NULL; 228058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 229058102a6STobias Waldekranz } 230058102a6STobias Waldekranz 231058102a6STobias Waldekranz return err; 232058102a6STobias Waldekranz } 233058102a6STobias Waldekranz 234058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) 235058102a6STobias Waldekranz { 236058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 237058102a6STobias Waldekranz .sw_index = dp->ds->index, 238058102a6STobias Waldekranz .port = dp->index, 239058102a6STobias Waldekranz .lag = lag, 240058102a6STobias Waldekranz }; 241058102a6STobias Waldekranz int err; 242058102a6STobias Waldekranz 243058102a6STobias Waldekranz if (!dp->lag_dev) 244058102a6STobias Waldekranz return; 245058102a6STobias Waldekranz 246058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 247058102a6STobias Waldekranz * attached to a bridge. 248058102a6STobias Waldekranz */ 249058102a6STobias Waldekranz if (dp->bridge_dev) 250058102a6STobias Waldekranz dsa_port_bridge_leave(dp, dp->bridge_dev); 251058102a6STobias Waldekranz 252058102a6STobias Waldekranz dp->lag_tx_enabled = false; 253058102a6STobias Waldekranz dp->lag_dev = NULL; 254058102a6STobias Waldekranz 255058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 256058102a6STobias Waldekranz if (err) 257058102a6STobias Waldekranz pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", 258058102a6STobias Waldekranz err); 259058102a6STobias Waldekranz 260058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 261058102a6STobias Waldekranz } 262058102a6STobias Waldekranz 263adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 2648f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 2658f5d16f6SVladimir Oltean bool vlan_filtering) 2668f5d16f6SVladimir Oltean { 2678f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 268adb256ebSVladimir Oltean int err, i; 269adb256ebSVladimir Oltean 270adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 271adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 272adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 273adb256ebSVladimir Oltean * as long as we have 8021q uppers. 274adb256ebSVladimir Oltean */ 275adb256ebSVladimir Oltean if (vlan_filtering && dsa_is_user_port(ds, dp->index)) { 276adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 277adb256ebSVladimir Oltean struct net_device *br = dp->bridge_dev; 278adb256ebSVladimir Oltean struct list_head *iter; 279adb256ebSVladimir Oltean 280adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 281adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 282adb256ebSVladimir Oltean u16 vid; 283adb256ebSVladimir Oltean 284adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 285adb256ebSVladimir Oltean continue; 286adb256ebSVladimir Oltean 287adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 288adb256ebSVladimir Oltean 289adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 290adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 291adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 292adb256ebSVladimir Oltean */ 293adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 294adb256ebSVladimir Oltean if (err == 0) { 295adb256ebSVladimir Oltean dev_err(ds->dev, "Must remove upper %s first\n", 296adb256ebSVladimir Oltean upper_dev->name); 297adb256ebSVladimir Oltean return false; 298adb256ebSVladimir Oltean } 299adb256ebSVladimir Oltean } 300adb256ebSVladimir Oltean } 3018f5d16f6SVladimir Oltean 3028f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 3038f5d16f6SVladimir Oltean return true; 3048f5d16f6SVladimir Oltean 3058f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 3068f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 3078f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 3088f5d16f6SVladimir Oltean * different setting than what is being requested. 3098f5d16f6SVladimir Oltean */ 3108f5d16f6SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 3118f5d16f6SVladimir Oltean struct net_device *other_bridge; 3128f5d16f6SVladimir Oltean 3138f5d16f6SVladimir Oltean other_bridge = dsa_to_port(ds, i)->bridge_dev; 3148f5d16f6SVladimir Oltean if (!other_bridge) 3158f5d16f6SVladimir Oltean continue; 3168f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 3178f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 3188f5d16f6SVladimir Oltean */ 3198f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 3208f5d16f6SVladimir Oltean continue; 3218f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 3228f5d16f6SVladimir Oltean dev_err(ds->dev, "VLAN filtering is a global setting\n"); 3238f5d16f6SVladimir Oltean return false; 3248f5d16f6SVladimir Oltean } 3258f5d16f6SVladimir Oltean } 3268f5d16f6SVladimir Oltean return true; 3278f5d16f6SVladimir Oltean } 3288f5d16f6SVladimir Oltean 329bae33f2bSVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering) 3304d61d304SVivien Didelot { 3314d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 332adb256ebSVladimir Oltean bool apply; 333bae33f2bSVladimir Oltean int err; 334adb256ebSVladimir Oltean 3358f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 336707ec383SVladimir Oltean return -EOPNOTSUPP; 3378f5d16f6SVladimir Oltean 338adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 339adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 340adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 341adb256ebSVladimir Oltean */ 342adb256ebSVladimir Oltean rcu_read_lock(); 343adb256ebSVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering); 344adb256ebSVladimir Oltean rcu_read_unlock(); 345adb256ebSVladimir Oltean if (!apply) 3468f5d16f6SVladimir Oltean return -EINVAL; 347707ec383SVladimir Oltean 348ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 349ec9121e7SVladimir Oltean return 0; 350ec9121e7SVladimir Oltean 351bae33f2bSVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering); 35233162e9aSVladimir Oltean if (err) 35333162e9aSVladimir Oltean return err; 3548f5d16f6SVladimir Oltean 35514574676SVladimir Oltean if (ds->vlan_filtering_is_global) 35614574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 35714574676SVladimir Oltean else 35833162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 3592e554a7aSVladimir Oltean 3604d61d304SVivien Didelot return 0; 3614d61d304SVivien Didelot } 362d87bd94eSVivien Didelot 36354a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 36454a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 36554a0ed0dSRussell King */ 36654a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 36754a0ed0dSRussell King { 36854a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 36954a0ed0dSRussell King 37054a0ed0dSRussell King if (!dp->bridge_dev) 37154a0ed0dSRussell King return false; 37254a0ed0dSRussell King 37354a0ed0dSRussell King return (!ds->configure_vlan_while_not_filtering && 37454a0ed0dSRussell King !br_vlan_enabled(dp->bridge_dev)); 37554a0ed0dSRussell King } 37654a0ed0dSRussell King 377bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 378d87bd94eSVivien Didelot { 379d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 380d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 381bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 382bae33f2bSVladimir Oltean int err; 383d87bd94eSVivien Didelot 384bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 385bae33f2bSVladimir Oltean 386bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 387bae33f2bSVladimir Oltean if (err) 388bae33f2bSVladimir Oltean return err; 389d87bd94eSVivien Didelot 390d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 391d87bd94eSVivien Didelot 39277b61365SVladimir Oltean return 0; 393d87bd94eSVivien Didelot } 394d1cffff0SVivien Didelot 395bae33f2bSVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags) 396ea87005aSFlorian Fainelli { 397ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 398ea87005aSFlorian Fainelli 399ea87005aSFlorian Fainelli if (!ds->ops->port_egress_floods || 400ea87005aSFlorian Fainelli (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) 401ea87005aSFlorian Fainelli return -EINVAL; 402ea87005aSFlorian Fainelli 403ea87005aSFlorian Fainelli return 0; 404ea87005aSFlorian Fainelli } 405ea87005aSFlorian Fainelli 406bae33f2bSVladimir Oltean int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags) 40757652796SRussell King { 40857652796SRussell King struct dsa_switch *ds = dp->ds; 40957652796SRussell King int port = dp->index; 41057652796SRussell King int err = 0; 41157652796SRussell King 41257652796SRussell King if (ds->ops->port_egress_floods) 41357652796SRussell King err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, 41457652796SRussell King flags & BR_MCAST_FLOOD); 41557652796SRussell King 41657652796SRussell King return err; 41757652796SRussell King } 41857652796SRussell King 419bae33f2bSVladimir Oltean int dsa_port_mrouter(struct dsa_port *dp, bool mrouter) 42008cc83ccSVivien Didelot { 42108cc83ccSVivien Didelot struct dsa_switch *ds = dp->ds; 42208cc83ccSVivien Didelot int port = dp->index; 42308cc83ccSVivien Didelot 424bae33f2bSVladimir Oltean if (!ds->ops->port_egress_floods) 425bae33f2bSVladimir Oltean return -EOPNOTSUPP; 42608cc83ccSVivien Didelot 42708cc83ccSVivien Didelot return ds->ops->port_egress_floods(ds, port, true, mrouter); 42808cc83ccSVivien Didelot } 42908cc83ccSVivien Didelot 430bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 431bfcb8132SVladimir Oltean bool propagate_upstream) 432bfcb8132SVladimir Oltean { 433bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 434bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 435bfcb8132SVladimir Oltean .propagate_upstream = propagate_upstream, 436bfcb8132SVladimir Oltean .port = dp->index, 437bfcb8132SVladimir Oltean .mtu = new_mtu, 438bfcb8132SVladimir Oltean }; 439bfcb8132SVladimir Oltean 440bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 441bfcb8132SVladimir Oltean } 442bfcb8132SVladimir Oltean 4432acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 4442acf4e6aSArkadi Sharshevsky u16 vid) 445d1cffff0SVivien Didelot { 446685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 447685fb6a4SVivien Didelot .sw_index = dp->ds->index, 448685fb6a4SVivien Didelot .port = dp->index, 4492acf4e6aSArkadi Sharshevsky .addr = addr, 4502acf4e6aSArkadi Sharshevsky .vid = vid, 451685fb6a4SVivien Didelot }; 452d1cffff0SVivien Didelot 453685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 454d1cffff0SVivien Didelot } 455d1cffff0SVivien Didelot 4562acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 4572acf4e6aSArkadi Sharshevsky u16 vid) 458d1cffff0SVivien Didelot { 459685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 460685fb6a4SVivien Didelot .sw_index = dp->ds->index, 461685fb6a4SVivien Didelot .port = dp->index, 4622acf4e6aSArkadi Sharshevsky .addr = addr, 4632acf4e6aSArkadi Sharshevsky .vid = vid, 4642acf4e6aSArkadi Sharshevsky 465685fb6a4SVivien Didelot }; 466d1cffff0SVivien Didelot 467685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 468d1cffff0SVivien Didelot } 469d1cffff0SVivien Didelot 470de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 471de40fc5dSVivien Didelot { 472de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 473de40fc5dSVivien Didelot int port = dp->index; 474de40fc5dSVivien Didelot 475de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 476de40fc5dSVivien Didelot return -EOPNOTSUPP; 477de40fc5dSVivien Didelot 478de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 479de40fc5dSVivien Didelot } 480de40fc5dSVivien Didelot 481bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 482ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 4833a9afea3SVivien Didelot { 4848ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 4858ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 4868ae5bcdcSVivien Didelot .port = dp->index, 4878ae5bcdcSVivien Didelot .mdb = mdb, 4888ae5bcdcSVivien Didelot }; 4893a9afea3SVivien Didelot 4908ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 4913a9afea3SVivien Didelot } 4923a9afea3SVivien Didelot 493bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 4943a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 4953a9afea3SVivien Didelot { 4968ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 4978ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 4988ae5bcdcSVivien Didelot .port = dp->index, 4998ae5bcdcSVivien Didelot .mdb = mdb, 5008ae5bcdcSVivien Didelot }; 5013a9afea3SVivien Didelot 5028ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 5033a9afea3SVivien Didelot } 5043a9afea3SVivien Didelot 505076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 506ffb68fc5SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 507076e7133SVivien Didelot { 508d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 509d0c627b8SVivien Didelot .sw_index = dp->ds->index, 510d0c627b8SVivien Didelot .port = dp->index, 511d0c627b8SVivien Didelot .vlan = vlan, 512d0c627b8SVivien Didelot }; 513076e7133SVivien Didelot 514d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 515076e7133SVivien Didelot } 516076e7133SVivien Didelot 517076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 518076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 519076e7133SVivien Didelot { 520d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 521d0c627b8SVivien Didelot .sw_index = dp->ds->index, 522d0c627b8SVivien Didelot .port = dp->index, 523d0c627b8SVivien Didelot .vlan = vlan, 524d0c627b8SVivien Didelot }; 525076e7133SVivien Didelot 526d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 527076e7133SVivien Didelot } 52857ab1ca2SVivien Didelot 52953da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 53053da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 53153da0ebaSVladimir Oltean { 53253da0ebaSVladimir Oltean cpu_dp->filter = tag_ops->filter; 53353da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 53453da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 53553da0ebaSVladimir Oltean } 53653da0ebaSVladimir Oltean 5376207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 5386207a78cSFlorian Fainelli { 5396207a78cSFlorian Fainelli struct device_node *phy_dn; 5406207a78cSFlorian Fainelli struct phy_device *phydev; 5416207a78cSFlorian Fainelli 5426207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 5436207a78cSFlorian Fainelli if (!phy_dn) 5446207a78cSFlorian Fainelli return NULL; 5456207a78cSFlorian Fainelli 5466207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 5476207a78cSFlorian Fainelli if (!phydev) { 5486207a78cSFlorian Fainelli of_node_put(phy_dn); 5496207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 5506207a78cSFlorian Fainelli } 5516207a78cSFlorian Fainelli 5529919a363SWen Yang of_node_put(phy_dn); 5536207a78cSFlorian Fainelli return phydev; 5546207a78cSFlorian Fainelli } 5556207a78cSFlorian Fainelli 5568ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 55777373d49SIoana Ciornei unsigned long *supported, 55877373d49SIoana Ciornei struct phylink_link_state *state) 55977373d49SIoana Ciornei { 56077373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 56177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 56277373d49SIoana Ciornei 56377373d49SIoana Ciornei if (!ds->ops->phylink_validate) 56477373d49SIoana Ciornei return; 56577373d49SIoana Ciornei 56677373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 56777373d49SIoana Ciornei } 56877373d49SIoana Ciornei 5698ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 57077373d49SIoana Ciornei struct phylink_link_state *state) 57177373d49SIoana Ciornei { 57277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 57377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 57487615c96SRussell King int err; 57577373d49SIoana Ciornei 576d46b7e4fSRussell King /* Only called for inband modes */ 577d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 578d46b7e4fSRussell King state->link = 0; 579d46b7e4fSRussell King return; 58077373d49SIoana Ciornei } 581d46b7e4fSRussell King 58287615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 58387615c96SRussell King if (err < 0) { 58487615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 58587615c96SRussell King dp->index, err); 586d46b7e4fSRussell King state->link = 0; 587d46b7e4fSRussell King } 58887615c96SRussell King } 58977373d49SIoana Ciornei 5908ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 59177373d49SIoana Ciornei unsigned int mode, 59277373d49SIoana Ciornei const struct phylink_link_state *state) 59377373d49SIoana Ciornei { 59477373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 59577373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 59677373d49SIoana Ciornei 59777373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 59877373d49SIoana Ciornei return; 59977373d49SIoana Ciornei 60077373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 60177373d49SIoana Ciornei } 60277373d49SIoana Ciornei 6038ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 60477373d49SIoana Ciornei { 60577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 60677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 60777373d49SIoana Ciornei 60877373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 60977373d49SIoana Ciornei return; 61077373d49SIoana Ciornei 61177373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 61277373d49SIoana Ciornei } 61377373d49SIoana Ciornei 6148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 61577373d49SIoana Ciornei unsigned int mode, 61677373d49SIoana Ciornei phy_interface_t interface) 61777373d49SIoana Ciornei { 61877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 6190e279218SIoana Ciornei struct phy_device *phydev = NULL; 62077373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 62177373d49SIoana Ciornei 6220e279218SIoana Ciornei if (dsa_is_user_port(ds, dp->index)) 6230e279218SIoana Ciornei phydev = dp->slave->phydev; 6240e279218SIoana Ciornei 62577373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 6260e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 6270e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 62877373d49SIoana Ciornei return; 62977373d49SIoana Ciornei } 63077373d49SIoana Ciornei 63177373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 63277373d49SIoana Ciornei } 63377373d49SIoana Ciornei 6348ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 63591a208f2SRussell King struct phy_device *phydev, 63677373d49SIoana Ciornei unsigned int mode, 63777373d49SIoana Ciornei phy_interface_t interface, 63891a208f2SRussell King int speed, int duplex, 63991a208f2SRussell King bool tx_pause, bool rx_pause) 64077373d49SIoana Ciornei { 64177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 64277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 64377373d49SIoana Ciornei 64477373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 6450e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 6460e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 64777373d49SIoana Ciornei return; 64877373d49SIoana Ciornei } 64977373d49SIoana Ciornei 6505b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 6515b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 65277373d49SIoana Ciornei } 65377373d49SIoana Ciornei 65477373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 65577373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 656d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 65777373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 65877373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 65977373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 66077373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 66177373d49SIoana Ciornei }; 66277373d49SIoana Ciornei 66333615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 66433615367SSebastian Reichel { 66533615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 66633615367SSebastian Reichel struct phy_device *phydev; 66733615367SSebastian Reichel int port = dp->index; 66833615367SSebastian Reichel int err = 0; 66933615367SSebastian Reichel 6706207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 6716207a78cSFlorian Fainelli if (!phydev) 67233615367SSebastian Reichel return 0; 67333615367SSebastian Reichel 6746207a78cSFlorian Fainelli if (IS_ERR(phydev)) 6756207a78cSFlorian Fainelli return PTR_ERR(phydev); 67633615367SSebastian Reichel 67733615367SSebastian Reichel if (enable) { 67833615367SSebastian Reichel err = genphy_resume(phydev); 67933615367SSebastian Reichel if (err < 0) 68033615367SSebastian Reichel goto err_put_dev; 68133615367SSebastian Reichel 68233615367SSebastian Reichel err = genphy_read_status(phydev); 68333615367SSebastian Reichel if (err < 0) 68433615367SSebastian Reichel goto err_put_dev; 68533615367SSebastian Reichel } else { 68633615367SSebastian Reichel err = genphy_suspend(phydev); 68733615367SSebastian Reichel if (err < 0) 68833615367SSebastian Reichel goto err_put_dev; 68933615367SSebastian Reichel } 69033615367SSebastian Reichel 69133615367SSebastian Reichel if (ds->ops->adjust_link) 69233615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 69333615367SSebastian Reichel 69433615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 69533615367SSebastian Reichel 69633615367SSebastian Reichel err_put_dev: 69733615367SSebastian Reichel put_device(&phydev->mdio.dev); 69833615367SSebastian Reichel return err; 69933615367SSebastian Reichel } 70033615367SSebastian Reichel 70133615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 70257ab1ca2SVivien Didelot { 70357ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 70457ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 70557ab1ca2SVivien Didelot struct phy_device *phydev; 70657ab1ca2SVivien Didelot int port = dp->index; 7070c65b2b9SAndrew Lunn phy_interface_t mode; 70857ab1ca2SVivien Didelot int err; 70957ab1ca2SVivien Didelot 71057ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 71157ab1ca2SVivien Didelot if (err) { 71257ab1ca2SVivien Didelot dev_err(ds->dev, 71357ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 71457ab1ca2SVivien Didelot port); 71557ab1ca2SVivien Didelot return err; 71657ab1ca2SVivien Didelot } 71757ab1ca2SVivien Didelot 71857ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 71957ab1ca2SVivien Didelot 7200c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 7210c65b2b9SAndrew Lunn if (err) 72257ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 72357ab1ca2SVivien Didelot phydev->interface = mode; 72457ab1ca2SVivien Didelot 72557ab1ca2SVivien Didelot genphy_read_status(phydev); 72657ab1ca2SVivien Didelot 72757ab1ca2SVivien Didelot if (ds->ops->adjust_link) 72857ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 72957ab1ca2SVivien Didelot 73057ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 73157ab1ca2SVivien Didelot 73257ab1ca2SVivien Didelot return 0; 73357ab1ca2SVivien Didelot } 73457ab1ca2SVivien Didelot 7350e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 7360e279218SIoana Ciornei { 7370e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 7380e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 7390c65b2b9SAndrew Lunn phy_interface_t mode; 7400c65b2b9SAndrew Lunn int err; 7410e279218SIoana Ciornei 7420c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 7430c65b2b9SAndrew Lunn if (err) 7440e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 7450e279218SIoana Ciornei 7460e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 7470e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 748787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 7490e279218SIoana Ciornei 7500e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 7510e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 7520e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 7530e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 7540e279218SIoana Ciornei return PTR_ERR(dp->pl); 7550e279218SIoana Ciornei } 7560e279218SIoana Ciornei 7570e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 7582131fba5SFlorian Fainelli if (err && err != -ENODEV) { 7590e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 7600e279218SIoana Ciornei goto err_phy_connect; 7610e279218SIoana Ciornei } 7620e279218SIoana Ciornei 7630e279218SIoana Ciornei return 0; 7640e279218SIoana Ciornei 7650e279218SIoana Ciornei err_phy_connect: 7660e279218SIoana Ciornei phylink_destroy(dp->pl); 7670e279218SIoana Ciornei return err; 7680e279218SIoana Ciornei } 7690e279218SIoana Ciornei 77033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 77157ab1ca2SVivien Didelot { 7720e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 773a20f9970SAndrew Lunn struct device_node *phy_np; 7743be98b2dSAndrew Lunn int port = dp->index; 7750e279218SIoana Ciornei 776a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 777a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 7783be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 7793be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 7803be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 7813be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 7820e279218SIoana Ciornei return dsa_port_phylink_register(dp); 7833be98b2dSAndrew Lunn } 784a20f9970SAndrew Lunn return 0; 785a20f9970SAndrew Lunn } 7860e279218SIoana Ciornei 7870e279218SIoana Ciornei dev_warn(ds->dev, 7880e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 7890e279218SIoana Ciornei 79033615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 79133615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 79233615367SSebastian Reichel else 79333615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 79433615367SSebastian Reichel } 79557ab1ca2SVivien Didelot 79633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 79733615367SSebastian Reichel { 7980e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 7990e279218SIoana Ciornei 800a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 8010e279218SIoana Ciornei rtnl_lock(); 8020e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 8030e279218SIoana Ciornei rtnl_unlock(); 8040e279218SIoana Ciornei phylink_destroy(dp->pl); 805a20f9970SAndrew Lunn dp->pl = NULL; 8060e279218SIoana Ciornei return; 8070e279218SIoana Ciornei } 8080e279218SIoana Ciornei 80933615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 81033615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 81133615367SSebastian Reichel else 81233615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 81357ab1ca2SVivien Didelot } 814cf963573SFlorian Fainelli 815cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 816cf963573SFlorian Fainelli { 817cf963573SFlorian Fainelli struct phy_device *phydev; 818cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 819cf963573SFlorian Fainelli 820cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 821cf963573SFlorian Fainelli return ret; 822cf963573SFlorian Fainelli 823cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 824cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 825cf963573SFlorian Fainelli return ret; 826cf963573SFlorian Fainelli 827cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 828cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 829cf963573SFlorian Fainelli 830cf963573SFlorian Fainelli return ret; 831cf963573SFlorian Fainelli } 832cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 833cf963573SFlorian Fainelli 834cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 835cf963573SFlorian Fainelli { 836cf963573SFlorian Fainelli struct phy_device *phydev; 837cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 838cf963573SFlorian Fainelli 839cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 840cf963573SFlorian Fainelli return ret; 841cf963573SFlorian Fainelli 842cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 843cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 844cf963573SFlorian Fainelli return ret; 845cf963573SFlorian Fainelli 846cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 847cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 848cf963573SFlorian Fainelli 849cf963573SFlorian Fainelli return ret; 850cf963573SFlorian Fainelli } 851cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 852cf963573SFlorian Fainelli 853cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 854cf963573SFlorian Fainelli { 855cf963573SFlorian Fainelli struct phy_device *phydev; 856cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 857cf963573SFlorian Fainelli 858cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 859cf963573SFlorian Fainelli return ret; 860cf963573SFlorian Fainelli 861cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 862cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 863cf963573SFlorian Fainelli return ret; 864cf963573SFlorian Fainelli 865cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 866cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 867cf963573SFlorian Fainelli 868cf963573SFlorian Fainelli return ret; 869cf963573SFlorian Fainelli } 870cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 871*18596f50SGeorge McCollister 872*18596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 873*18596f50SGeorge McCollister { 874*18596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 875*18596f50SGeorge McCollister .sw_index = dp->ds->index, 876*18596f50SGeorge McCollister .port = dp->index, 877*18596f50SGeorge McCollister .hsr = hsr, 878*18596f50SGeorge McCollister }; 879*18596f50SGeorge McCollister int err; 880*18596f50SGeorge McCollister 881*18596f50SGeorge McCollister dp->hsr_dev = hsr; 882*18596f50SGeorge McCollister 883*18596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info); 884*18596f50SGeorge McCollister if (err) 885*18596f50SGeorge McCollister dp->hsr_dev = NULL; 886*18596f50SGeorge McCollister 887*18596f50SGeorge McCollister return err; 888*18596f50SGeorge McCollister } 889*18596f50SGeorge McCollister 890*18596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 891*18596f50SGeorge McCollister { 892*18596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 893*18596f50SGeorge McCollister .sw_index = dp->ds->index, 894*18596f50SGeorge McCollister .port = dp->index, 895*18596f50SGeorge McCollister .hsr = hsr, 896*18596f50SGeorge McCollister }; 897*18596f50SGeorge McCollister int err; 898*18596f50SGeorge McCollister 899*18596f50SGeorge McCollister dp->hsr_dev = NULL; 900*18596f50SGeorge McCollister 901*18596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info); 902*18596f50SGeorge McCollister if (err) 903*18596f50SGeorge McCollister pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n"); 904*18596f50SGeorge McCollister } 905