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 339264e4adSVladimir Oltean static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp) 349264e4adSVladimir Oltean { 359264e4adSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 369264e4adSVladimir Oltean struct switchdev_notifier_fdb_info info = { 379264e4adSVladimir Oltean /* flush all VLANs */ 389264e4adSVladimir Oltean .vid = 0, 399264e4adSVladimir Oltean }; 409264e4adSVladimir Oltean 419264e4adSVladimir Oltean /* When the port becomes standalone it has already left the bridge. 429264e4adSVladimir Oltean * Don't notify the bridge in that case. 439264e4adSVladimir Oltean */ 449264e4adSVladimir Oltean if (!brport_dev) 459264e4adSVladimir Oltean return; 469264e4adSVladimir Oltean 479264e4adSVladimir Oltean call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE, 489264e4adSVladimir Oltean brport_dev, &info.info, NULL); 499264e4adSVladimir Oltean } 509264e4adSVladimir Oltean 51045c45d1SVladimir Oltean static void dsa_port_fast_age(const struct dsa_port *dp) 52045c45d1SVladimir Oltean { 53045c45d1SVladimir Oltean struct dsa_switch *ds = dp->ds; 54045c45d1SVladimir Oltean 55045c45d1SVladimir Oltean if (!ds->ops->port_fast_age) 56045c45d1SVladimir Oltean return; 57045c45d1SVladimir Oltean 58045c45d1SVladimir Oltean ds->ops->port_fast_age(ds, dp->index); 599264e4adSVladimir Oltean 609264e4adSVladimir Oltean dsa_port_notify_bridge_fdb_flush(dp); 61045c45d1SVladimir Oltean } 62045c45d1SVladimir Oltean 63a4ffe09fSVladimir Oltean static bool dsa_port_can_configure_learning(struct dsa_port *dp) 64a4ffe09fSVladimir Oltean { 65a4ffe09fSVladimir Oltean struct switchdev_brport_flags flags = { 66a4ffe09fSVladimir Oltean .mask = BR_LEARNING, 67a4ffe09fSVladimir Oltean }; 68a4ffe09fSVladimir Oltean struct dsa_switch *ds = dp->ds; 69a4ffe09fSVladimir Oltean int err; 70a4ffe09fSVladimir Oltean 71a4ffe09fSVladimir Oltean if (!ds->ops->port_bridge_flags || !ds->ops->port_pre_bridge_flags) 72a4ffe09fSVladimir Oltean return false; 73a4ffe09fSVladimir Oltean 74a4ffe09fSVladimir Oltean err = ds->ops->port_pre_bridge_flags(ds, dp->index, flags, NULL); 75a4ffe09fSVladimir Oltean return !err; 76a4ffe09fSVladimir Oltean } 77a4ffe09fSVladimir Oltean 7839f32101SVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age) 79a40c175bSVivien Didelot { 80a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 81a40c175bSVivien Didelot int port = dp->index; 82a40c175bSVivien Didelot 83bae33f2bSVladimir Oltean if (!ds->ops->port_stp_state_set) 84bae33f2bSVladimir Oltean return -EOPNOTSUPP; 85a40c175bSVivien Didelot 86a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 87a40c175bSVivien Didelot 88a4ffe09fSVladimir Oltean if (!dsa_port_can_configure_learning(dp) || 89a4ffe09fSVladimir Oltean (do_fast_age && dp->learning)) { 90a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 91a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 92a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 9339f32101SVladimir Oltean * Ports that were standalone before the STP state change don't 9439f32101SVladimir Oltean * need to fast age the FDB, since address learning is off in 9539f32101SVladimir Oltean * standalone mode. 96a40c175bSVivien Didelot */ 97a40c175bSVivien Didelot 98a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 99a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 100a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 101a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 102a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 103045c45d1SVladimir Oltean dsa_port_fast_age(dp); 104a40c175bSVivien Didelot } 105a40c175bSVivien Didelot 106a40c175bSVivien Didelot dp->stp_state = state; 107a40c175bSVivien Didelot 108a40c175bSVivien Didelot return 0; 109a40c175bSVivien Didelot } 110a40c175bSVivien Didelot 11139f32101SVladimir Oltean static void dsa_port_set_state_now(struct dsa_port *dp, u8 state, 11239f32101SVladimir Oltean bool do_fast_age) 113a40c175bSVivien Didelot { 114a40c175bSVivien Didelot int err; 115a40c175bSVivien Didelot 11639f32101SVladimir Oltean err = dsa_port_set_state(dp, state, do_fast_age); 117a40c175bSVivien Didelot if (err) 118a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 119a40c175bSVivien Didelot } 120cfbed329SVivien Didelot 1218640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 122fb8a6a2bSVivien Didelot { 123fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 124fb8a6a2bSVivien Didelot int port = dp->index; 125fb8a6a2bSVivien Didelot int err; 126fb8a6a2bSVivien Didelot 127fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 128fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 129fb8a6a2bSVivien Didelot if (err) 130fb8a6a2bSVivien Didelot return err; 131fb8a6a2bSVivien Didelot } 132fb8a6a2bSVivien Didelot 1339c2054a5SRussell King if (!dp->bridge_dev) 13439f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_FORWARDING, false); 135fb8a6a2bSVivien Didelot 1368640f8dcSRussell King if (dp->pl) 1378640f8dcSRussell King phylink_start(dp->pl); 1388640f8dcSRussell King 139fb8a6a2bSVivien Didelot return 0; 140fb8a6a2bSVivien Didelot } 141fb8a6a2bSVivien Didelot 1428640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 1438640f8dcSRussell King { 1448640f8dcSRussell King int err; 1458640f8dcSRussell King 1468640f8dcSRussell King rtnl_lock(); 1478640f8dcSRussell King err = dsa_port_enable_rt(dp, phy); 1488640f8dcSRussell King rtnl_unlock(); 1498640f8dcSRussell King 1508640f8dcSRussell King return err; 1518640f8dcSRussell King } 1528640f8dcSRussell King 1538640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp) 154fb8a6a2bSVivien Didelot { 155fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 156fb8a6a2bSVivien Didelot int port = dp->index; 157fb8a6a2bSVivien Didelot 1588640f8dcSRussell King if (dp->pl) 1598640f8dcSRussell King phylink_stop(dp->pl); 1608640f8dcSRussell King 1619c2054a5SRussell King if (!dp->bridge_dev) 16239f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_DISABLED, false); 163fb8a6a2bSVivien Didelot 164fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 16575104db0SAndrew Lunn ds->ops->port_disable(ds, port); 166fb8a6a2bSVivien Didelot } 167fb8a6a2bSVivien Didelot 1688640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp) 1698640f8dcSRussell King { 1708640f8dcSRussell King rtnl_lock(); 1718640f8dcSRussell King dsa_port_disable_rt(dp); 1728640f8dcSRussell King rtnl_unlock(); 1738640f8dcSRussell King } 1748640f8dcSRussell King 1755961d6a1SVladimir Oltean static int dsa_port_inherit_brport_flags(struct dsa_port *dp, 1765961d6a1SVladimir Oltean struct netlink_ext_ack *extack) 1775e38c158SVladimir Oltean { 1785961d6a1SVladimir Oltean const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 1795961d6a1SVladimir Oltean BR_BCAST_FLOOD; 1805961d6a1SVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 1815961d6a1SVladimir Oltean int flag, err; 1825e38c158SVladimir Oltean 1835961d6a1SVladimir Oltean for_each_set_bit(flag, &mask, 32) { 1845961d6a1SVladimir Oltean struct switchdev_brport_flags flags = {0}; 1855e38c158SVladimir Oltean 1865961d6a1SVladimir Oltean flags.mask = BIT(flag); 1875e38c158SVladimir Oltean 1885961d6a1SVladimir Oltean if (br_port_flag_is_set(brport_dev, BIT(flag))) 1895961d6a1SVladimir Oltean flags.val = BIT(flag); 190e18f4c18SVladimir Oltean 1915961d6a1SVladimir Oltean err = dsa_port_bridge_flags(dp, flags, extack); 1925961d6a1SVladimir Oltean if (err && err != -EOPNOTSUPP) 1935961d6a1SVladimir Oltean return err; 1945e38c158SVladimir Oltean } 1955961d6a1SVladimir Oltean 1965961d6a1SVladimir Oltean return 0; 1975961d6a1SVladimir Oltean } 1985961d6a1SVladimir Oltean 1995961d6a1SVladimir Oltean static void dsa_port_clear_brport_flags(struct dsa_port *dp) 2005961d6a1SVladimir Oltean { 2015961d6a1SVladimir Oltean const unsigned long val = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; 2025961d6a1SVladimir Oltean const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 2035961d6a1SVladimir Oltean BR_BCAST_FLOOD; 2045961d6a1SVladimir Oltean int flag, err; 2055961d6a1SVladimir Oltean 2065961d6a1SVladimir Oltean for_each_set_bit(flag, &mask, 32) { 2075961d6a1SVladimir Oltean struct switchdev_brport_flags flags = {0}; 2085961d6a1SVladimir Oltean 2095961d6a1SVladimir Oltean flags.mask = BIT(flag); 2105961d6a1SVladimir Oltean flags.val = val & BIT(flag); 2115961d6a1SVladimir Oltean 2125961d6a1SVladimir Oltean err = dsa_port_bridge_flags(dp, flags, NULL); 2135961d6a1SVladimir Oltean if (err && err != -EOPNOTSUPP) 2145961d6a1SVladimir Oltean dev_err(dp->ds->dev, 2155961d6a1SVladimir Oltean "failed to clear bridge port flag %lu: %pe\n", 2165961d6a1SVladimir Oltean flags.val, ERR_PTR(err)); 2175961d6a1SVladimir Oltean } 2185961d6a1SVladimir Oltean } 2195961d6a1SVladimir Oltean 2204e51bf44SVladimir Oltean static int dsa_port_switchdev_sync_attrs(struct dsa_port *dp, 2215961d6a1SVladimir Oltean struct netlink_ext_ack *extack) 2225961d6a1SVladimir Oltean { 223010e269fSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 224010e269fSVladimir Oltean struct net_device *br = dp->bridge_dev; 2255961d6a1SVladimir Oltean int err; 2265961d6a1SVladimir Oltean 2275961d6a1SVladimir Oltean err = dsa_port_inherit_brport_flags(dp, extack); 2285961d6a1SVladimir Oltean if (err) 2295961d6a1SVladimir Oltean return err; 2305961d6a1SVladimir Oltean 23139f32101SVladimir Oltean err = dsa_port_set_state(dp, br_port_get_stp_state(brport_dev), false); 232010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 233010e269fSVladimir Oltean return err; 234010e269fSVladimir Oltean 235010e269fSVladimir Oltean err = dsa_port_vlan_filtering(dp, br_vlan_enabled(br), extack); 236010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 237010e269fSVladimir Oltean return err; 238010e269fSVladimir Oltean 239010e269fSVladimir Oltean err = dsa_port_ageing_time(dp, br_get_ageing_time(br)); 240010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 241010e269fSVladimir Oltean return err; 242010e269fSVladimir Oltean 24374918945SVladimir Oltean return 0; 24474918945SVladimir Oltean } 24574918945SVladimir Oltean 24674918945SVladimir Oltean static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp) 2475961d6a1SVladimir Oltean { 2485961d6a1SVladimir Oltean /* Configure the port for standalone mode (no address learning, 2495961d6a1SVladimir Oltean * flood everything). 2505961d6a1SVladimir Oltean * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events 2515961d6a1SVladimir Oltean * when the user requests it through netlink or sysfs, but not 2525961d6a1SVladimir Oltean * automatically at port join or leave, so we need to handle resetting 2535961d6a1SVladimir Oltean * the brport flags ourselves. But we even prefer it that way, because 2545961d6a1SVladimir Oltean * otherwise, some setups might never get the notification they need, 2555961d6a1SVladimir Oltean * for example, when a port leaves a LAG that offloads the bridge, 2565961d6a1SVladimir Oltean * it becomes standalone, but as far as the bridge is concerned, no 2575961d6a1SVladimir Oltean * port ever left. 2585961d6a1SVladimir Oltean */ 2595961d6a1SVladimir Oltean dsa_port_clear_brport_flags(dp); 2605961d6a1SVladimir Oltean 2615961d6a1SVladimir Oltean /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 2625961d6a1SVladimir Oltean * so allow it to be in BR_STATE_FORWARDING to be kept functional 2635961d6a1SVladimir Oltean */ 26439f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_FORWARDING, true); 265010e269fSVladimir Oltean 266010e269fSVladimir Oltean /* VLAN filtering is handled by dsa_switch_bridge_leave */ 267010e269fSVladimir Oltean 268010e269fSVladimir Oltean /* Ageing time may be global to the switch chip, so don't change it 269010e269fSVladimir Oltean * here because we have no good reason (or value) to change it to. 270010e269fSVladimir Oltean */ 2715e38c158SVladimir Oltean } 2725e38c158SVladimir Oltean 273123abc06SVladimir Oltean static void dsa_port_bridge_tx_fwd_unoffload(struct dsa_port *dp, 274123abc06SVladimir Oltean struct net_device *bridge_dev) 275123abc06SVladimir Oltean { 276123abc06SVladimir Oltean int bridge_num = dp->bridge_num; 277123abc06SVladimir Oltean struct dsa_switch *ds = dp->ds; 278123abc06SVladimir Oltean 279123abc06SVladimir Oltean /* No bridge TX forwarding offload => do nothing */ 280123abc06SVladimir Oltean if (!ds->ops->port_bridge_tx_fwd_unoffload || dp->bridge_num == -1) 281123abc06SVladimir Oltean return; 282123abc06SVladimir Oltean 283123abc06SVladimir Oltean dp->bridge_num = -1; 284123abc06SVladimir Oltean 285*f5e165e7SVladimir Oltean dsa_bridge_num_put(bridge_dev, bridge_num); 286123abc06SVladimir Oltean 287123abc06SVladimir Oltean /* Notify the chips only once the offload has been deactivated, so 288123abc06SVladimir Oltean * that they can update their configuration accordingly. 289123abc06SVladimir Oltean */ 290123abc06SVladimir Oltean ds->ops->port_bridge_tx_fwd_unoffload(ds, dp->index, bridge_dev, 291123abc06SVladimir Oltean bridge_num); 292123abc06SVladimir Oltean } 293123abc06SVladimir Oltean 294123abc06SVladimir Oltean static bool dsa_port_bridge_tx_fwd_offload(struct dsa_port *dp, 295123abc06SVladimir Oltean struct net_device *bridge_dev) 296123abc06SVladimir Oltean { 297123abc06SVladimir Oltean struct dsa_switch *ds = dp->ds; 298123abc06SVladimir Oltean int bridge_num, err; 299123abc06SVladimir Oltean 300123abc06SVladimir Oltean if (!ds->ops->port_bridge_tx_fwd_offload) 301123abc06SVladimir Oltean return false; 302123abc06SVladimir Oltean 303*f5e165e7SVladimir Oltean bridge_num = dsa_bridge_num_get(bridge_dev, 304*f5e165e7SVladimir Oltean ds->num_fwd_offloading_bridges); 305*f5e165e7SVladimir Oltean if (bridge_num < 0) 306123abc06SVladimir Oltean return false; 307123abc06SVladimir Oltean 308123abc06SVladimir Oltean dp->bridge_num = bridge_num; 309123abc06SVladimir Oltean 310123abc06SVladimir Oltean /* Notify the driver */ 311123abc06SVladimir Oltean err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge_dev, 312123abc06SVladimir Oltean bridge_num); 313123abc06SVladimir Oltean if (err) { 314123abc06SVladimir Oltean dsa_port_bridge_tx_fwd_unoffload(dp, bridge_dev); 315123abc06SVladimir Oltean return false; 316123abc06SVladimir Oltean } 317123abc06SVladimir Oltean 318123abc06SVladimir Oltean return true; 319123abc06SVladimir Oltean } 320123abc06SVladimir Oltean 3212afc526aSVladimir Oltean int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, 3222afc526aSVladimir Oltean struct netlink_ext_ack *extack) 323cfbed329SVivien Didelot { 324cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 325f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 326cfbed329SVivien Didelot .sw_index = dp->ds->index, 327cfbed329SVivien Didelot .port = dp->index, 328cfbed329SVivien Didelot .br = br, 329cfbed329SVivien Didelot }; 3302f5dc00fSVladimir Oltean struct net_device *dev = dp->slave; 3312f5dc00fSVladimir Oltean struct net_device *brport_dev; 332123abc06SVladimir Oltean bool tx_fwd_offload; 333cfbed329SVivien Didelot int err; 334cfbed329SVivien Didelot 335c1388063SRussell King /* Here the interface is already bridged. Reflect the current 336c1388063SRussell King * configuration so that drivers can program their chips accordingly. 337cfbed329SVivien Didelot */ 338cfbed329SVivien Didelot dp->bridge_dev = br; 339cfbed329SVivien Didelot 3402f5dc00fSVladimir Oltean brport_dev = dsa_port_to_bridge_port(dp); 3412f5dc00fSVladimir Oltean 342f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); 3435961d6a1SVladimir Oltean if (err) 3445961d6a1SVladimir Oltean goto out_rollback; 345cfbed329SVivien Didelot 346123abc06SVladimir Oltean tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br); 347123abc06SVladimir Oltean 3484e51bf44SVladimir Oltean err = switchdev_bridge_port_offload(brport_dev, dev, dp, 3494e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3504e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier, 351123abc06SVladimir Oltean tx_fwd_offload, extack); 3525961d6a1SVladimir Oltean if (err) 3535961d6a1SVladimir Oltean goto out_rollback_unbridge; 3545961d6a1SVladimir Oltean 3554e51bf44SVladimir Oltean err = dsa_port_switchdev_sync_attrs(dp, extack); 3562f5dc00fSVladimir Oltean if (err) 3572f5dc00fSVladimir Oltean goto out_rollback_unoffload; 3582f5dc00fSVladimir Oltean 3595961d6a1SVladimir Oltean return 0; 3605961d6a1SVladimir Oltean 3612f5dc00fSVladimir Oltean out_rollback_unoffload: 3624e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3634e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3644e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 3655961d6a1SVladimir Oltean out_rollback_unbridge: 3665961d6a1SVladimir Oltean dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 3675961d6a1SVladimir Oltean out_rollback: 368cfbed329SVivien Didelot dp->bridge_dev = NULL; 369cfbed329SVivien Didelot return err; 370cfbed329SVivien Didelot } 371cfbed329SVivien Didelot 3724e51bf44SVladimir Oltean void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br) 37374918945SVladimir Oltean { 3742f5dc00fSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 3752f5dc00fSVladimir Oltean 3764e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3774e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3784e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 37974918945SVladimir Oltean } 38074918945SVladimir Oltean 381cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 382cfbed329SVivien Didelot { 383cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 384f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 385cfbed329SVivien Didelot .sw_index = dp->ds->index, 386cfbed329SVivien Didelot .port = dp->index, 387cfbed329SVivien Didelot .br = br, 388cfbed329SVivien Didelot }; 389cfbed329SVivien Didelot int err; 390cfbed329SVivien Didelot 391cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 392cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 393cfbed329SVivien Didelot */ 394cfbed329SVivien Didelot dp->bridge_dev = NULL; 395cfbed329SVivien Didelot 396123abc06SVladimir Oltean dsa_port_bridge_tx_fwd_unoffload(dp, br); 397123abc06SVladimir Oltean 398f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 399cfbed329SVivien Didelot if (err) 400ab97462bSVladimir Oltean dev_err(dp->ds->dev, 401ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n", 402ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 403cfbed329SVivien Didelot 40474918945SVladimir Oltean dsa_port_switchdev_unsync_attrs(dp); 405cfbed329SVivien Didelot } 4064d61d304SVivien Didelot 407058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 408058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 409058102a6STobias Waldekranz { 410058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 411058102a6STobias Waldekranz .sw_index = dp->ds->index, 412058102a6STobias Waldekranz .port = dp->index, 413058102a6STobias Waldekranz }; 414058102a6STobias Waldekranz bool tx_enabled; 415058102a6STobias Waldekranz 416058102a6STobias Waldekranz if (!dp->lag_dev) 417058102a6STobias Waldekranz return 0; 418058102a6STobias Waldekranz 419058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 420058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 421058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 422058102a6STobias Waldekranz * in order to include it in the tx set. 423058102a6STobias Waldekranz */ 424058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 425058102a6STobias Waldekranz 426058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 427058102a6STobias Waldekranz return 0; 428058102a6STobias Waldekranz 429058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 430058102a6STobias Waldekranz 431058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 432058102a6STobias Waldekranz } 433058102a6STobias Waldekranz 434058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, 4352afc526aSVladimir Oltean struct netdev_lag_upper_info *uinfo, 4362afc526aSVladimir Oltean struct netlink_ext_ack *extack) 437058102a6STobias Waldekranz { 438058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 439058102a6STobias Waldekranz .sw_index = dp->ds->index, 440058102a6STobias Waldekranz .port = dp->index, 441058102a6STobias Waldekranz .lag = lag, 442058102a6STobias Waldekranz .info = uinfo, 443058102a6STobias Waldekranz }; 444185c9a76SVladimir Oltean struct net_device *bridge_dev; 445058102a6STobias Waldekranz int err; 446058102a6STobias Waldekranz 447058102a6STobias Waldekranz dsa_lag_map(dp->ds->dst, lag); 448058102a6STobias Waldekranz dp->lag_dev = lag; 449058102a6STobias Waldekranz 450058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 451185c9a76SVladimir Oltean if (err) 452185c9a76SVladimir Oltean goto err_lag_join; 453185c9a76SVladimir Oltean 454185c9a76SVladimir Oltean bridge_dev = netdev_master_upper_dev_get(lag); 455185c9a76SVladimir Oltean if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 456185c9a76SVladimir Oltean return 0; 457185c9a76SVladimir Oltean 4582afc526aSVladimir Oltean err = dsa_port_bridge_join(dp, bridge_dev, extack); 459185c9a76SVladimir Oltean if (err) 460185c9a76SVladimir Oltean goto err_bridge_join; 461185c9a76SVladimir Oltean 462185c9a76SVladimir Oltean return 0; 463185c9a76SVladimir Oltean 464185c9a76SVladimir Oltean err_bridge_join: 465185c9a76SVladimir Oltean dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 466185c9a76SVladimir Oltean err_lag_join: 467058102a6STobias Waldekranz dp->lag_dev = NULL; 468058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 469058102a6STobias Waldekranz return err; 470058102a6STobias Waldekranz } 471058102a6STobias Waldekranz 4724e51bf44SVladimir Oltean void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag) 47374918945SVladimir Oltean { 47474918945SVladimir Oltean if (dp->bridge_dev) 4754e51bf44SVladimir Oltean dsa_port_pre_bridge_leave(dp, dp->bridge_dev); 47674918945SVladimir Oltean } 47774918945SVladimir Oltean 478058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) 479058102a6STobias Waldekranz { 480058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 481058102a6STobias Waldekranz .sw_index = dp->ds->index, 482058102a6STobias Waldekranz .port = dp->index, 483058102a6STobias Waldekranz .lag = lag, 484058102a6STobias Waldekranz }; 485058102a6STobias Waldekranz int err; 486058102a6STobias Waldekranz 487058102a6STobias Waldekranz if (!dp->lag_dev) 488058102a6STobias Waldekranz return; 489058102a6STobias Waldekranz 490058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 491058102a6STobias Waldekranz * attached to a bridge. 492058102a6STobias Waldekranz */ 493058102a6STobias Waldekranz if (dp->bridge_dev) 494058102a6STobias Waldekranz dsa_port_bridge_leave(dp, dp->bridge_dev); 495058102a6STobias Waldekranz 496058102a6STobias Waldekranz dp->lag_tx_enabled = false; 497058102a6STobias Waldekranz dp->lag_dev = NULL; 498058102a6STobias Waldekranz 499058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 500058102a6STobias Waldekranz if (err) 501ab97462bSVladimir Oltean dev_err(dp->ds->dev, 502ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n", 503ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 504058102a6STobias Waldekranz 505058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 506058102a6STobias Waldekranz } 507058102a6STobias Waldekranz 508adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 5098f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 51089153ed6SVladimir Oltean bool vlan_filtering, 51189153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5128f5d16f6SVladimir Oltean { 5138f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 514adb256ebSVladimir Oltean int err, i; 515adb256ebSVladimir Oltean 516adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 517adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 518adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 519adb256ebSVladimir Oltean * as long as we have 8021q uppers. 520adb256ebSVladimir Oltean */ 521adb256ebSVladimir Oltean if (vlan_filtering && dsa_is_user_port(ds, dp->index)) { 522adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 523adb256ebSVladimir Oltean struct net_device *br = dp->bridge_dev; 524adb256ebSVladimir Oltean struct list_head *iter; 525adb256ebSVladimir Oltean 526adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 527adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 528adb256ebSVladimir Oltean u16 vid; 529adb256ebSVladimir Oltean 530adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 531adb256ebSVladimir Oltean continue; 532adb256ebSVladimir Oltean 533adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 534adb256ebSVladimir Oltean 535adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 536adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 537adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 538adb256ebSVladimir Oltean */ 539adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 540adb256ebSVladimir Oltean if (err == 0) { 54189153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 54289153ed6SVladimir Oltean "Must first remove VLAN uppers having VIDs also present in bridge"); 543adb256ebSVladimir Oltean return false; 544adb256ebSVladimir Oltean } 545adb256ebSVladimir Oltean } 546adb256ebSVladimir Oltean } 5478f5d16f6SVladimir Oltean 5488f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 5498f5d16f6SVladimir Oltean return true; 5508f5d16f6SVladimir Oltean 5518f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 5528f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 5538f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 5548f5d16f6SVladimir Oltean * different setting than what is being requested. 5558f5d16f6SVladimir Oltean */ 5568f5d16f6SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 5578f5d16f6SVladimir Oltean struct net_device *other_bridge; 5588f5d16f6SVladimir Oltean 5598f5d16f6SVladimir Oltean other_bridge = dsa_to_port(ds, i)->bridge_dev; 5608f5d16f6SVladimir Oltean if (!other_bridge) 5618f5d16f6SVladimir Oltean continue; 5628f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 5638f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 5648f5d16f6SVladimir Oltean */ 5658f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 5668f5d16f6SVladimir Oltean continue; 5678f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 56889153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 56989153ed6SVladimir Oltean "VLAN filtering is a global setting"); 5708f5d16f6SVladimir Oltean return false; 5718f5d16f6SVladimir Oltean } 5728f5d16f6SVladimir Oltean } 5738f5d16f6SVladimir Oltean return true; 5748f5d16f6SVladimir Oltean } 5758f5d16f6SVladimir Oltean 57689153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 57789153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5784d61d304SVivien Didelot { 5794d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 580adb256ebSVladimir Oltean bool apply; 581bae33f2bSVladimir Oltean int err; 582adb256ebSVladimir Oltean 5838f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 584707ec383SVladimir Oltean return -EOPNOTSUPP; 5858f5d16f6SVladimir Oltean 586adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 587adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 588adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 589adb256ebSVladimir Oltean */ 590adb256ebSVladimir Oltean rcu_read_lock(); 59189153ed6SVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack); 592adb256ebSVladimir Oltean rcu_read_unlock(); 593adb256ebSVladimir Oltean if (!apply) 5948f5d16f6SVladimir Oltean return -EINVAL; 595707ec383SVladimir Oltean 596ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 597ec9121e7SVladimir Oltean return 0; 598ec9121e7SVladimir Oltean 59989153ed6SVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering, 60089153ed6SVladimir Oltean extack); 60133162e9aSVladimir Oltean if (err) 60233162e9aSVladimir Oltean return err; 6038f5d16f6SVladimir Oltean 60414574676SVladimir Oltean if (ds->vlan_filtering_is_global) 60514574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 60614574676SVladimir Oltean else 60733162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 6082e554a7aSVladimir Oltean 6094d61d304SVivien Didelot return 0; 6104d61d304SVivien Didelot } 611d87bd94eSVivien Didelot 61254a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 61354a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 61454a0ed0dSRussell King */ 61554a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 61654a0ed0dSRussell King { 61754a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 61854a0ed0dSRussell King 61954a0ed0dSRussell King if (!dp->bridge_dev) 62054a0ed0dSRussell King return false; 62154a0ed0dSRussell King 62254a0ed0dSRussell King return (!ds->configure_vlan_while_not_filtering && 62354a0ed0dSRussell King !br_vlan_enabled(dp->bridge_dev)); 62454a0ed0dSRussell King } 62554a0ed0dSRussell King 626bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 627d87bd94eSVivien Didelot { 628d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 629d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 630bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 631bae33f2bSVladimir Oltean int err; 632d87bd94eSVivien Didelot 633bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 634bae33f2bSVladimir Oltean 635bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 636bae33f2bSVladimir Oltean if (err) 637bae33f2bSVladimir Oltean return err; 638d87bd94eSVivien Didelot 639d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 640d87bd94eSVivien Didelot 64177b61365SVladimir Oltean return 0; 642d87bd94eSVivien Didelot } 643d1cffff0SVivien Didelot 644e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, 645a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 646a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 647ea87005aSFlorian Fainelli { 648ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 649ea87005aSFlorian Fainelli 650a8b659e7SVladimir Oltean if (!ds->ops->port_pre_bridge_flags) 651ea87005aSFlorian Fainelli return -EINVAL; 652ea87005aSFlorian Fainelli 653a8b659e7SVladimir Oltean return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); 654ea87005aSFlorian Fainelli } 655ea87005aSFlorian Fainelli 656045c45d1SVladimir Oltean int dsa_port_bridge_flags(struct dsa_port *dp, 657a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 658a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 65957652796SRussell King { 66057652796SRussell King struct dsa_switch *ds = dp->ds; 661045c45d1SVladimir Oltean int err; 66257652796SRussell King 663a8b659e7SVladimir Oltean if (!ds->ops->port_bridge_flags) 66470a7c484SOleksij Rempel return -EOPNOTSUPP; 66557652796SRussell King 666045c45d1SVladimir Oltean err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack); 667045c45d1SVladimir Oltean if (err) 668045c45d1SVladimir Oltean return err; 669045c45d1SVladimir Oltean 670045c45d1SVladimir Oltean if (flags.mask & BR_LEARNING) { 671045c45d1SVladimir Oltean bool learning = flags.val & BR_LEARNING; 672045c45d1SVladimir Oltean 673045c45d1SVladimir Oltean if (learning == dp->learning) 674045c45d1SVladimir Oltean return 0; 675045c45d1SVladimir Oltean 676bee7c577SVladimir Oltean if ((dp->learning && !learning) && 677bee7c577SVladimir Oltean (dp->stp_state == BR_STATE_LEARNING || 678bee7c577SVladimir Oltean dp->stp_state == BR_STATE_FORWARDING)) 679045c45d1SVladimir Oltean dsa_port_fast_age(dp); 680045c45d1SVladimir Oltean 681045c45d1SVladimir Oltean dp->learning = learning; 682045c45d1SVladimir Oltean } 683045c45d1SVladimir Oltean 684045c45d1SVladimir Oltean return 0; 68557652796SRussell King } 68657652796SRussell King 687bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 68888faba20SVladimir Oltean bool targeted_match) 689bfcb8132SVladimir Oltean { 690bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 691bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 69288faba20SVladimir Oltean .targeted_match = targeted_match, 693bfcb8132SVladimir Oltean .port = dp->index, 694bfcb8132SVladimir Oltean .mtu = new_mtu, 695bfcb8132SVladimir Oltean }; 696bfcb8132SVladimir Oltean 697bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 698bfcb8132SVladimir Oltean } 699bfcb8132SVladimir Oltean 7002acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7012acf4e6aSArkadi Sharshevsky u16 vid) 702d1cffff0SVivien Didelot { 703685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 704685fb6a4SVivien Didelot .sw_index = dp->ds->index, 705685fb6a4SVivien Didelot .port = dp->index, 7062acf4e6aSArkadi Sharshevsky .addr = addr, 7072acf4e6aSArkadi Sharshevsky .vid = vid, 708685fb6a4SVivien Didelot }; 709d1cffff0SVivien Didelot 710685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 711d1cffff0SVivien Didelot } 712d1cffff0SVivien Didelot 7132acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7142acf4e6aSArkadi Sharshevsky u16 vid) 715d1cffff0SVivien Didelot { 716685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 717685fb6a4SVivien Didelot .sw_index = dp->ds->index, 718685fb6a4SVivien Didelot .port = dp->index, 7192acf4e6aSArkadi Sharshevsky .addr = addr, 7202acf4e6aSArkadi Sharshevsky .vid = vid, 7212acf4e6aSArkadi Sharshevsky 722685fb6a4SVivien Didelot }; 723d1cffff0SVivien Didelot 724685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 725d1cffff0SVivien Didelot } 726d1cffff0SVivien Didelot 7273dc80afcSVladimir Oltean int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7283dc80afcSVladimir Oltean u16 vid) 7293dc80afcSVladimir Oltean { 7303dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7313dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7323dc80afcSVladimir Oltean .port = dp->index, 7333dc80afcSVladimir Oltean .addr = addr, 7343dc80afcSVladimir Oltean .vid = vid, 7353dc80afcSVladimir Oltean }; 73626ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 73726ee7b06SVladimir Oltean int err; 73826ee7b06SVladimir Oltean 73926ee7b06SVladimir Oltean err = dev_uc_add(cpu_dp->master, addr); 74026ee7b06SVladimir Oltean if (err) 74126ee7b06SVladimir Oltean return err; 7423dc80afcSVladimir Oltean 7433dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); 7443dc80afcSVladimir Oltean } 7453dc80afcSVladimir Oltean 7463dc80afcSVladimir Oltean int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7473dc80afcSVladimir Oltean u16 vid) 7483dc80afcSVladimir Oltean { 7493dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7503dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7513dc80afcSVladimir Oltean .port = dp->index, 7523dc80afcSVladimir Oltean .addr = addr, 7533dc80afcSVladimir Oltean .vid = vid, 7543dc80afcSVladimir Oltean }; 75526ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 75626ee7b06SVladimir Oltean int err; 75726ee7b06SVladimir Oltean 75826ee7b06SVladimir Oltean err = dev_uc_del(cpu_dp->master, addr); 75926ee7b06SVladimir Oltean if (err) 76026ee7b06SVladimir Oltean return err; 7613dc80afcSVladimir Oltean 7623dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); 7633dc80afcSVladimir Oltean } 7643dc80afcSVladimir Oltean 765de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 766de40fc5dSVivien Didelot { 767de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 768de40fc5dSVivien Didelot int port = dp->index; 769de40fc5dSVivien Didelot 770de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 771de40fc5dSVivien Didelot return -EOPNOTSUPP; 772de40fc5dSVivien Didelot 773de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 774de40fc5dSVivien Didelot } 775de40fc5dSVivien Didelot 776bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 777ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 7783a9afea3SVivien Didelot { 7798ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 7808ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 7818ae5bcdcSVivien Didelot .port = dp->index, 7828ae5bcdcSVivien Didelot .mdb = mdb, 7838ae5bcdcSVivien Didelot }; 7843a9afea3SVivien Didelot 7858ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 7863a9afea3SVivien Didelot } 7873a9afea3SVivien Didelot 788bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 7893a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 7903a9afea3SVivien Didelot { 7918ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 7928ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 7938ae5bcdcSVivien Didelot .port = dp->index, 7948ae5bcdcSVivien Didelot .mdb = mdb, 7958ae5bcdcSVivien Didelot }; 7963a9afea3SVivien Didelot 7978ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 7983a9afea3SVivien Didelot } 7993a9afea3SVivien Didelot 800b8e997c4SVladimir Oltean int dsa_port_host_mdb_add(const struct dsa_port *dp, 801b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 802b8e997c4SVladimir Oltean { 803b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 804b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 805b8e997c4SVladimir Oltean .port = dp->index, 806b8e997c4SVladimir Oltean .mdb = mdb, 807b8e997c4SVladimir Oltean }; 80826ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 80926ee7b06SVladimir Oltean int err; 81026ee7b06SVladimir Oltean 81126ee7b06SVladimir Oltean err = dev_mc_add(cpu_dp->master, mdb->addr); 81226ee7b06SVladimir Oltean if (err) 81326ee7b06SVladimir Oltean return err; 814b8e997c4SVladimir Oltean 815b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); 816b8e997c4SVladimir Oltean } 817b8e997c4SVladimir Oltean 818b8e997c4SVladimir Oltean int dsa_port_host_mdb_del(const struct dsa_port *dp, 819b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 820b8e997c4SVladimir Oltean { 821b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 822b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 823b8e997c4SVladimir Oltean .port = dp->index, 824b8e997c4SVladimir Oltean .mdb = mdb, 825b8e997c4SVladimir Oltean }; 82626ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 82726ee7b06SVladimir Oltean int err; 82826ee7b06SVladimir Oltean 82926ee7b06SVladimir Oltean err = dev_mc_del(cpu_dp->master, mdb->addr); 83026ee7b06SVladimir Oltean if (err) 83126ee7b06SVladimir Oltean return err; 832b8e997c4SVladimir Oltean 833b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); 834b8e997c4SVladimir Oltean } 835b8e997c4SVladimir Oltean 836076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 83731046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 83831046a5fSVladimir Oltean struct netlink_ext_ack *extack) 839076e7133SVivien Didelot { 840d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 841d0c627b8SVivien Didelot .sw_index = dp->ds->index, 842d0c627b8SVivien Didelot .port = dp->index, 843d0c627b8SVivien Didelot .vlan = vlan, 84431046a5fSVladimir Oltean .extack = extack, 845d0c627b8SVivien Didelot }; 846076e7133SVivien Didelot 847d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 848076e7133SVivien Didelot } 849076e7133SVivien Didelot 850076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 851076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 852076e7133SVivien Didelot { 853d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 854d0c627b8SVivien Didelot .sw_index = dp->ds->index, 855d0c627b8SVivien Didelot .port = dp->index, 856d0c627b8SVivien Didelot .vlan = vlan, 857d0c627b8SVivien Didelot }; 858076e7133SVivien Didelot 859d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 860076e7133SVivien Didelot } 86157ab1ca2SVivien Didelot 862c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp, 863c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 864c595c433SHoratiu Vultur { 865c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 866c595c433SHoratiu Vultur .sw_index = dp->ds->index, 867c595c433SHoratiu Vultur .port = dp->index, 868c595c433SHoratiu Vultur .mrp = mrp, 869c595c433SHoratiu Vultur }; 870c595c433SHoratiu Vultur 871c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD, &info); 872c595c433SHoratiu Vultur } 873c595c433SHoratiu Vultur 874c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp, 875c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 876c595c433SHoratiu Vultur { 877c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 878c595c433SHoratiu Vultur .sw_index = dp->ds->index, 879c595c433SHoratiu Vultur .port = dp->index, 880c595c433SHoratiu Vultur .mrp = mrp, 881c595c433SHoratiu Vultur }; 882c595c433SHoratiu Vultur 883c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL, &info); 884c595c433SHoratiu Vultur } 885c595c433SHoratiu Vultur 886c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, 887c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 888c595c433SHoratiu Vultur { 889c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 890c595c433SHoratiu Vultur .sw_index = dp->ds->index, 891c595c433SHoratiu Vultur .port = dp->index, 892c595c433SHoratiu Vultur .mrp = mrp, 893c595c433SHoratiu Vultur }; 894c595c433SHoratiu Vultur 895c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD_RING_ROLE, &info); 896c595c433SHoratiu Vultur } 897c595c433SHoratiu Vultur 898c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, 899c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 900c595c433SHoratiu Vultur { 901c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 902c595c433SHoratiu Vultur .sw_index = dp->ds->index, 903c595c433SHoratiu Vultur .port = dp->index, 904c595c433SHoratiu Vultur .mrp = mrp, 905c595c433SHoratiu Vultur }; 906c595c433SHoratiu Vultur 907c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL_RING_ROLE, &info); 908c595c433SHoratiu Vultur } 909c595c433SHoratiu Vultur 91053da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 91153da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 91253da0ebaSVladimir Oltean { 91353da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 91453da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 91553da0ebaSVladimir Oltean } 91653da0ebaSVladimir Oltean 9176207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 9186207a78cSFlorian Fainelli { 9196207a78cSFlorian Fainelli struct device_node *phy_dn; 9206207a78cSFlorian Fainelli struct phy_device *phydev; 9216207a78cSFlorian Fainelli 9226207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 9236207a78cSFlorian Fainelli if (!phy_dn) 9246207a78cSFlorian Fainelli return NULL; 9256207a78cSFlorian Fainelli 9266207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 9276207a78cSFlorian Fainelli if (!phydev) { 9286207a78cSFlorian Fainelli of_node_put(phy_dn); 9296207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 9306207a78cSFlorian Fainelli } 9316207a78cSFlorian Fainelli 9329919a363SWen Yang of_node_put(phy_dn); 9336207a78cSFlorian Fainelli return phydev; 9346207a78cSFlorian Fainelli } 9356207a78cSFlorian Fainelli 9368ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 93777373d49SIoana Ciornei unsigned long *supported, 93877373d49SIoana Ciornei struct phylink_link_state *state) 93977373d49SIoana Ciornei { 94077373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 94177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 94277373d49SIoana Ciornei 94377373d49SIoana Ciornei if (!ds->ops->phylink_validate) 94477373d49SIoana Ciornei return; 94577373d49SIoana Ciornei 94677373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 94777373d49SIoana Ciornei } 94877373d49SIoana Ciornei 9498ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 95077373d49SIoana Ciornei struct phylink_link_state *state) 95177373d49SIoana Ciornei { 95277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 95377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 95487615c96SRussell King int err; 95577373d49SIoana Ciornei 956d46b7e4fSRussell King /* Only called for inband modes */ 957d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 958d46b7e4fSRussell King state->link = 0; 959d46b7e4fSRussell King return; 96077373d49SIoana Ciornei } 961d46b7e4fSRussell King 96287615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 96387615c96SRussell King if (err < 0) { 96487615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 96587615c96SRussell King dp->index, err); 966d46b7e4fSRussell King state->link = 0; 967d46b7e4fSRussell King } 96887615c96SRussell King } 96977373d49SIoana Ciornei 9708ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 97177373d49SIoana Ciornei unsigned int mode, 97277373d49SIoana Ciornei const struct phylink_link_state *state) 97377373d49SIoana Ciornei { 97477373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 97577373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 97677373d49SIoana Ciornei 97777373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 97877373d49SIoana Ciornei return; 97977373d49SIoana Ciornei 98077373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 98177373d49SIoana Ciornei } 98277373d49SIoana Ciornei 9838ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 98477373d49SIoana Ciornei { 98577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 98677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 98777373d49SIoana Ciornei 98877373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 98977373d49SIoana Ciornei return; 99077373d49SIoana Ciornei 99177373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 99277373d49SIoana Ciornei } 99377373d49SIoana Ciornei 9948ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 99577373d49SIoana Ciornei unsigned int mode, 99677373d49SIoana Ciornei phy_interface_t interface) 99777373d49SIoana Ciornei { 99877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 9990e279218SIoana Ciornei struct phy_device *phydev = NULL; 100077373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 100177373d49SIoana Ciornei 10020e279218SIoana Ciornei if (dsa_is_user_port(ds, dp->index)) 10030e279218SIoana Ciornei phydev = dp->slave->phydev; 10040e279218SIoana Ciornei 100577373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 10060e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 10070e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 100877373d49SIoana Ciornei return; 100977373d49SIoana Ciornei } 101077373d49SIoana Ciornei 101177373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 101277373d49SIoana Ciornei } 101377373d49SIoana Ciornei 10148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 101591a208f2SRussell King struct phy_device *phydev, 101677373d49SIoana Ciornei unsigned int mode, 101777373d49SIoana Ciornei phy_interface_t interface, 101891a208f2SRussell King int speed, int duplex, 101991a208f2SRussell King bool tx_pause, bool rx_pause) 102077373d49SIoana Ciornei { 102177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 102277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 102377373d49SIoana Ciornei 102477373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 10250e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 10260e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 102777373d49SIoana Ciornei return; 102877373d49SIoana Ciornei } 102977373d49SIoana Ciornei 10305b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 10315b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 103277373d49SIoana Ciornei } 103377373d49SIoana Ciornei 103477373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 103577373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 1036d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 103777373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 103877373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 103977373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 104077373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 104177373d49SIoana Ciornei }; 104277373d49SIoana Ciornei 104333615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 104433615367SSebastian Reichel { 104533615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 104633615367SSebastian Reichel struct phy_device *phydev; 104733615367SSebastian Reichel int port = dp->index; 104833615367SSebastian Reichel int err = 0; 104933615367SSebastian Reichel 10506207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 10516207a78cSFlorian Fainelli if (!phydev) 105233615367SSebastian Reichel return 0; 105333615367SSebastian Reichel 10546207a78cSFlorian Fainelli if (IS_ERR(phydev)) 10556207a78cSFlorian Fainelli return PTR_ERR(phydev); 105633615367SSebastian Reichel 105733615367SSebastian Reichel if (enable) { 105833615367SSebastian Reichel err = genphy_resume(phydev); 105933615367SSebastian Reichel if (err < 0) 106033615367SSebastian Reichel goto err_put_dev; 106133615367SSebastian Reichel 106233615367SSebastian Reichel err = genphy_read_status(phydev); 106333615367SSebastian Reichel if (err < 0) 106433615367SSebastian Reichel goto err_put_dev; 106533615367SSebastian Reichel } else { 106633615367SSebastian Reichel err = genphy_suspend(phydev); 106733615367SSebastian Reichel if (err < 0) 106833615367SSebastian Reichel goto err_put_dev; 106933615367SSebastian Reichel } 107033615367SSebastian Reichel 107133615367SSebastian Reichel if (ds->ops->adjust_link) 107233615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 107333615367SSebastian Reichel 107433615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 107533615367SSebastian Reichel 107633615367SSebastian Reichel err_put_dev: 107733615367SSebastian Reichel put_device(&phydev->mdio.dev); 107833615367SSebastian Reichel return err; 107933615367SSebastian Reichel } 108033615367SSebastian Reichel 108133615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 108257ab1ca2SVivien Didelot { 108357ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 108457ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 108557ab1ca2SVivien Didelot struct phy_device *phydev; 108657ab1ca2SVivien Didelot int port = dp->index; 10870c65b2b9SAndrew Lunn phy_interface_t mode; 108857ab1ca2SVivien Didelot int err; 108957ab1ca2SVivien Didelot 109057ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 109157ab1ca2SVivien Didelot if (err) { 109257ab1ca2SVivien Didelot dev_err(ds->dev, 109357ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 109457ab1ca2SVivien Didelot port); 109557ab1ca2SVivien Didelot return err; 109657ab1ca2SVivien Didelot } 109757ab1ca2SVivien Didelot 109857ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 109957ab1ca2SVivien Didelot 11000c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 11010c65b2b9SAndrew Lunn if (err) 110257ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 110357ab1ca2SVivien Didelot phydev->interface = mode; 110457ab1ca2SVivien Didelot 110557ab1ca2SVivien Didelot genphy_read_status(phydev); 110657ab1ca2SVivien Didelot 110757ab1ca2SVivien Didelot if (ds->ops->adjust_link) 110857ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 110957ab1ca2SVivien Didelot 111057ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 111157ab1ca2SVivien Didelot 111257ab1ca2SVivien Didelot return 0; 111357ab1ca2SVivien Didelot } 111457ab1ca2SVivien Didelot 11150e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 11160e279218SIoana Ciornei { 11170e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 11180e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 11190c65b2b9SAndrew Lunn phy_interface_t mode; 11200c65b2b9SAndrew Lunn int err; 11210e279218SIoana Ciornei 11220c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 11230c65b2b9SAndrew Lunn if (err) 11240e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 11250e279218SIoana Ciornei 11260e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 11270e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 1128787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 11290e279218SIoana Ciornei 11300e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 11310e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 11320e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 11330e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 11340e279218SIoana Ciornei return PTR_ERR(dp->pl); 11350e279218SIoana Ciornei } 11360e279218SIoana Ciornei 11370e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 11382131fba5SFlorian Fainelli if (err && err != -ENODEV) { 11390e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 11400e279218SIoana Ciornei goto err_phy_connect; 11410e279218SIoana Ciornei } 11420e279218SIoana Ciornei 11430e279218SIoana Ciornei return 0; 11440e279218SIoana Ciornei 11450e279218SIoana Ciornei err_phy_connect: 11460e279218SIoana Ciornei phylink_destroy(dp->pl); 11470e279218SIoana Ciornei return err; 11480e279218SIoana Ciornei } 11490e279218SIoana Ciornei 115033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 115157ab1ca2SVivien Didelot { 11520e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 1153a20f9970SAndrew Lunn struct device_node *phy_np; 11543be98b2dSAndrew Lunn int port = dp->index; 11550e279218SIoana Ciornei 1156a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 1157a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 11583be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 11593be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 11603be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 11613be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 11620e279218SIoana Ciornei return dsa_port_phylink_register(dp); 11633be98b2dSAndrew Lunn } 1164a20f9970SAndrew Lunn return 0; 1165a20f9970SAndrew Lunn } 11660e279218SIoana Ciornei 11670e279218SIoana Ciornei dev_warn(ds->dev, 11680e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 11690e279218SIoana Ciornei 117033615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 117133615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 117233615367SSebastian Reichel else 117333615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 117433615367SSebastian Reichel } 117557ab1ca2SVivien Didelot 117633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 117733615367SSebastian Reichel { 11780e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 11790e279218SIoana Ciornei 1180a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 11810e279218SIoana Ciornei rtnl_lock(); 11820e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 11830e279218SIoana Ciornei rtnl_unlock(); 11840e279218SIoana Ciornei phylink_destroy(dp->pl); 1185a20f9970SAndrew Lunn dp->pl = NULL; 11860e279218SIoana Ciornei return; 11870e279218SIoana Ciornei } 11880e279218SIoana Ciornei 118933615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 119033615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 119133615367SSebastian Reichel else 119233615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 119357ab1ca2SVivien Didelot } 1194cf963573SFlorian Fainelli 1195cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 1196cf963573SFlorian Fainelli { 1197cf963573SFlorian Fainelli struct phy_device *phydev; 1198cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1199cf963573SFlorian Fainelli 1200cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1201cf963573SFlorian Fainelli return ret; 1202cf963573SFlorian Fainelli 1203cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1204cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1205cf963573SFlorian Fainelli return ret; 1206cf963573SFlorian Fainelli 1207cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 1208cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1209cf963573SFlorian Fainelli 1210cf963573SFlorian Fainelli return ret; 1211cf963573SFlorian Fainelli } 1212cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 1213cf963573SFlorian Fainelli 1214cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 1215cf963573SFlorian Fainelli { 1216cf963573SFlorian Fainelli struct phy_device *phydev; 1217cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1218cf963573SFlorian Fainelli 1219cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1220cf963573SFlorian Fainelli return ret; 1221cf963573SFlorian Fainelli 1222cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1223cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1224cf963573SFlorian Fainelli return ret; 1225cf963573SFlorian Fainelli 1226cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 1227cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1228cf963573SFlorian Fainelli 1229cf963573SFlorian Fainelli return ret; 1230cf963573SFlorian Fainelli } 1231cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 1232cf963573SFlorian Fainelli 1233cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 1234cf963573SFlorian Fainelli { 1235cf963573SFlorian Fainelli struct phy_device *phydev; 1236cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1237cf963573SFlorian Fainelli 1238cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1239cf963573SFlorian Fainelli return ret; 1240cf963573SFlorian Fainelli 1241cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1242cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1243cf963573SFlorian Fainelli return ret; 1244cf963573SFlorian Fainelli 1245cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 1246cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1247cf963573SFlorian Fainelli 1248cf963573SFlorian Fainelli return ret; 1249cf963573SFlorian Fainelli } 1250cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 125118596f50SGeorge McCollister 125218596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 125318596f50SGeorge McCollister { 125418596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 125518596f50SGeorge McCollister .sw_index = dp->ds->index, 125618596f50SGeorge McCollister .port = dp->index, 125718596f50SGeorge McCollister .hsr = hsr, 125818596f50SGeorge McCollister }; 125918596f50SGeorge McCollister int err; 126018596f50SGeorge McCollister 126118596f50SGeorge McCollister dp->hsr_dev = hsr; 126218596f50SGeorge McCollister 126318596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info); 126418596f50SGeorge McCollister if (err) 126518596f50SGeorge McCollister dp->hsr_dev = NULL; 126618596f50SGeorge McCollister 126718596f50SGeorge McCollister return err; 126818596f50SGeorge McCollister } 126918596f50SGeorge McCollister 127018596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 127118596f50SGeorge McCollister { 127218596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 127318596f50SGeorge McCollister .sw_index = dp->ds->index, 127418596f50SGeorge McCollister .port = dp->index, 127518596f50SGeorge McCollister .hsr = hsr, 127618596f50SGeorge McCollister }; 127718596f50SGeorge McCollister int err; 127818596f50SGeorge McCollister 127918596f50SGeorge McCollister dp->hsr_dev = NULL; 128018596f50SGeorge McCollister 128118596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info); 128218596f50SGeorge McCollister if (err) 1283ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1284ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_HSR_LEAVE: %pe\n", 1285ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 128618596f50SGeorge McCollister } 1287c64b9c05SVladimir Oltean 1288724395f4SVladimir Oltean int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast) 1289c64b9c05SVladimir Oltean { 1290c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1291c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1292c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1293c64b9c05SVladimir Oltean .port = dp->index, 1294c64b9c05SVladimir Oltean .vid = vid, 1295c64b9c05SVladimir Oltean }; 1296c64b9c05SVladimir Oltean 1297724395f4SVladimir Oltean if (broadcast) 1298c64b9c05SVladimir Oltean return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1299724395f4SVladimir Oltean 1300724395f4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1301c64b9c05SVladimir Oltean } 1302c64b9c05SVladimir Oltean 1303724395f4SVladimir Oltean void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast) 1304c64b9c05SVladimir Oltean { 1305c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1306c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1307c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1308c64b9c05SVladimir Oltean .port = dp->index, 1309c64b9c05SVladimir Oltean .vid = vid, 1310c64b9c05SVladimir Oltean }; 1311c64b9c05SVladimir Oltean int err; 1312c64b9c05SVladimir Oltean 1313724395f4SVladimir Oltean if (broadcast) 1314c64b9c05SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1315724395f4SVladimir Oltean else 1316724395f4SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1317c64b9c05SVladimir Oltean if (err) 1318ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1319ab97462bSVladimir Oltean "port %d failed to notify tag_8021q VLAN %d deletion: %pe\n", 1320ab97462bSVladimir Oltean dp->index, vid, ERR_PTR(err)); 1321c64b9c05SVladimir Oltean } 1322