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 285f5e165e7SVladimir 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 303f5e165e7SVladimir Oltean bridge_num = dsa_bridge_num_get(bridge_dev, 304f5e165e7SVladimir Oltean ds->num_fwd_offloading_bridges); 305f5e165e7SVladimir 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 37609dba21bSVladimir Oltean /* Don't try to unoffload something that is not offloaded */ 37709dba21bSVladimir Oltean if (!brport_dev) 37809dba21bSVladimir Oltean return; 37909dba21bSVladimir Oltean 3804e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3814e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3824e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 383*d7d0d423SVladimir Oltean 384*d7d0d423SVladimir Oltean dsa_flush_workqueue(); 38574918945SVladimir Oltean } 38674918945SVladimir Oltean 387cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 388cfbed329SVivien Didelot { 389cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 390f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 391cfbed329SVivien Didelot .sw_index = dp->ds->index, 392cfbed329SVivien Didelot .port = dp->index, 393cfbed329SVivien Didelot .br = br, 394cfbed329SVivien Didelot }; 395cfbed329SVivien Didelot int err; 396cfbed329SVivien Didelot 397cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 398cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 399cfbed329SVivien Didelot */ 400cfbed329SVivien Didelot dp->bridge_dev = NULL; 401cfbed329SVivien Didelot 402123abc06SVladimir Oltean dsa_port_bridge_tx_fwd_unoffload(dp, br); 403123abc06SVladimir Oltean 404f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 405cfbed329SVivien Didelot if (err) 406ab97462bSVladimir Oltean dev_err(dp->ds->dev, 407ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n", 408ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 409cfbed329SVivien Didelot 41074918945SVladimir Oltean dsa_port_switchdev_unsync_attrs(dp); 411cfbed329SVivien Didelot } 4124d61d304SVivien Didelot 413058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 414058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 415058102a6STobias Waldekranz { 416058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 417058102a6STobias Waldekranz .sw_index = dp->ds->index, 418058102a6STobias Waldekranz .port = dp->index, 419058102a6STobias Waldekranz }; 420058102a6STobias Waldekranz bool tx_enabled; 421058102a6STobias Waldekranz 422058102a6STobias Waldekranz if (!dp->lag_dev) 423058102a6STobias Waldekranz return 0; 424058102a6STobias Waldekranz 425058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 426058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 427058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 428058102a6STobias Waldekranz * in order to include it in the tx set. 429058102a6STobias Waldekranz */ 430058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 431058102a6STobias Waldekranz 432058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 433058102a6STobias Waldekranz return 0; 434058102a6STobias Waldekranz 435058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 436058102a6STobias Waldekranz 437058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 438058102a6STobias Waldekranz } 439058102a6STobias Waldekranz 440058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, 4412afc526aSVladimir Oltean struct netdev_lag_upper_info *uinfo, 4422afc526aSVladimir Oltean struct netlink_ext_ack *extack) 443058102a6STobias Waldekranz { 444058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 445058102a6STobias Waldekranz .sw_index = dp->ds->index, 446058102a6STobias Waldekranz .port = dp->index, 447058102a6STobias Waldekranz .lag = lag, 448058102a6STobias Waldekranz .info = uinfo, 449058102a6STobias Waldekranz }; 450185c9a76SVladimir Oltean struct net_device *bridge_dev; 451058102a6STobias Waldekranz int err; 452058102a6STobias Waldekranz 453058102a6STobias Waldekranz dsa_lag_map(dp->ds->dst, lag); 454058102a6STobias Waldekranz dp->lag_dev = lag; 455058102a6STobias Waldekranz 456058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 457185c9a76SVladimir Oltean if (err) 458185c9a76SVladimir Oltean goto err_lag_join; 459185c9a76SVladimir Oltean 460185c9a76SVladimir Oltean bridge_dev = netdev_master_upper_dev_get(lag); 461185c9a76SVladimir Oltean if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 462185c9a76SVladimir Oltean return 0; 463185c9a76SVladimir Oltean 4642afc526aSVladimir Oltean err = dsa_port_bridge_join(dp, bridge_dev, extack); 465185c9a76SVladimir Oltean if (err) 466185c9a76SVladimir Oltean goto err_bridge_join; 467185c9a76SVladimir Oltean 468185c9a76SVladimir Oltean return 0; 469185c9a76SVladimir Oltean 470185c9a76SVladimir Oltean err_bridge_join: 471185c9a76SVladimir Oltean dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 472185c9a76SVladimir Oltean err_lag_join: 473058102a6STobias Waldekranz dp->lag_dev = NULL; 474058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 475058102a6STobias Waldekranz return err; 476058102a6STobias Waldekranz } 477058102a6STobias Waldekranz 4784e51bf44SVladimir Oltean void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag) 47974918945SVladimir Oltean { 48074918945SVladimir Oltean if (dp->bridge_dev) 4814e51bf44SVladimir Oltean dsa_port_pre_bridge_leave(dp, dp->bridge_dev); 48274918945SVladimir Oltean } 48374918945SVladimir Oltean 484058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) 485058102a6STobias Waldekranz { 486058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 487058102a6STobias Waldekranz .sw_index = dp->ds->index, 488058102a6STobias Waldekranz .port = dp->index, 489058102a6STobias Waldekranz .lag = lag, 490058102a6STobias Waldekranz }; 491058102a6STobias Waldekranz int err; 492058102a6STobias Waldekranz 493058102a6STobias Waldekranz if (!dp->lag_dev) 494058102a6STobias Waldekranz return; 495058102a6STobias Waldekranz 496058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 497058102a6STobias Waldekranz * attached to a bridge. 498058102a6STobias Waldekranz */ 499058102a6STobias Waldekranz if (dp->bridge_dev) 500058102a6STobias Waldekranz dsa_port_bridge_leave(dp, dp->bridge_dev); 501058102a6STobias Waldekranz 502058102a6STobias Waldekranz dp->lag_tx_enabled = false; 503058102a6STobias Waldekranz dp->lag_dev = NULL; 504058102a6STobias Waldekranz 505058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 506058102a6STobias Waldekranz if (err) 507ab97462bSVladimir Oltean dev_err(dp->ds->dev, 508ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n", 509ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 510058102a6STobias Waldekranz 511058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 512058102a6STobias Waldekranz } 513058102a6STobias Waldekranz 514adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 5158f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 51689153ed6SVladimir Oltean bool vlan_filtering, 51789153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5188f5d16f6SVladimir Oltean { 5198f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 520d0004a02SVladimir Oltean struct dsa_port *other_dp; 521d0004a02SVladimir Oltean int err; 522adb256ebSVladimir Oltean 523adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 524adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 525adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 526adb256ebSVladimir Oltean * as long as we have 8021q uppers. 527adb256ebSVladimir Oltean */ 52857d77986SVladimir Oltean if (vlan_filtering && dsa_port_is_user(dp)) { 529adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 530adb256ebSVladimir Oltean struct net_device *br = dp->bridge_dev; 531adb256ebSVladimir Oltean struct list_head *iter; 532adb256ebSVladimir Oltean 533adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 534adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 535adb256ebSVladimir Oltean u16 vid; 536adb256ebSVladimir Oltean 537adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 538adb256ebSVladimir Oltean continue; 539adb256ebSVladimir Oltean 540adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 541adb256ebSVladimir Oltean 542adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 543adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 544adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 545adb256ebSVladimir Oltean */ 546adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 547adb256ebSVladimir Oltean if (err == 0) { 54889153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 54989153ed6SVladimir Oltean "Must first remove VLAN uppers having VIDs also present in bridge"); 550adb256ebSVladimir Oltean return false; 551adb256ebSVladimir Oltean } 552adb256ebSVladimir Oltean } 553adb256ebSVladimir Oltean } 5548f5d16f6SVladimir Oltean 5558f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 5568f5d16f6SVladimir Oltean return true; 5578f5d16f6SVladimir Oltean 5588f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 5598f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 5608f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 5618f5d16f6SVladimir Oltean * different setting than what is being requested. 5628f5d16f6SVladimir Oltean */ 563d0004a02SVladimir Oltean dsa_switch_for_each_port(other_dp, ds) { 5648f5d16f6SVladimir Oltean struct net_device *other_bridge; 5658f5d16f6SVladimir Oltean 566d0004a02SVladimir Oltean other_bridge = other_dp->bridge_dev; 5678f5d16f6SVladimir Oltean if (!other_bridge) 5688f5d16f6SVladimir Oltean continue; 5698f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 5708f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 5718f5d16f6SVladimir Oltean */ 5728f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 5738f5d16f6SVladimir Oltean continue; 5748f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 57589153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 57689153ed6SVladimir Oltean "VLAN filtering is a global setting"); 5778f5d16f6SVladimir Oltean return false; 5788f5d16f6SVladimir Oltean } 5798f5d16f6SVladimir Oltean } 5808f5d16f6SVladimir Oltean return true; 5818f5d16f6SVladimir Oltean } 5828f5d16f6SVladimir Oltean 58389153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 58489153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5854d61d304SVivien Didelot { 58606cfb2dfSVladimir Oltean bool old_vlan_filtering = dsa_port_is_vlan_filtering(dp); 5874d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 588adb256ebSVladimir Oltean bool apply; 589bae33f2bSVladimir Oltean int err; 590adb256ebSVladimir Oltean 5918f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 592707ec383SVladimir Oltean return -EOPNOTSUPP; 5938f5d16f6SVladimir Oltean 594adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 595adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 596adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 597adb256ebSVladimir Oltean */ 598adb256ebSVladimir Oltean rcu_read_lock(); 59989153ed6SVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack); 600adb256ebSVladimir Oltean rcu_read_unlock(); 601adb256ebSVladimir Oltean if (!apply) 6028f5d16f6SVladimir Oltean return -EINVAL; 603707ec383SVladimir Oltean 604ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 605ec9121e7SVladimir Oltean return 0; 606ec9121e7SVladimir Oltean 60789153ed6SVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering, 60889153ed6SVladimir Oltean extack); 60933162e9aSVladimir Oltean if (err) 61033162e9aSVladimir Oltean return err; 6118f5d16f6SVladimir Oltean 61206cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) { 613d0004a02SVladimir Oltean struct dsa_port *other_dp; 61406cfb2dfSVladimir Oltean 61514574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 61606cfb2dfSVladimir Oltean 617d0004a02SVladimir Oltean dsa_switch_for_each_user_port(other_dp, ds) { 618d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 61906cfb2dfSVladimir Oltean 62006cfb2dfSVladimir Oltean /* We might be called in the unbind path, so not 62106cfb2dfSVladimir Oltean * all slave devices might still be registered. 62206cfb2dfSVladimir Oltean */ 62306cfb2dfSVladimir Oltean if (!slave) 62406cfb2dfSVladimir Oltean continue; 62506cfb2dfSVladimir Oltean 62606cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(slave, 62706cfb2dfSVladimir Oltean vlan_filtering); 62806cfb2dfSVladimir Oltean if (err) 62906cfb2dfSVladimir Oltean goto restore; 63006cfb2dfSVladimir Oltean } 63106cfb2dfSVladimir Oltean } else { 63233162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 6332e554a7aSVladimir Oltean 63406cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(dp->slave, 63506cfb2dfSVladimir Oltean vlan_filtering); 63606cfb2dfSVladimir Oltean if (err) 63706cfb2dfSVladimir Oltean goto restore; 63806cfb2dfSVladimir Oltean } 63906cfb2dfSVladimir Oltean 6404d61d304SVivien Didelot return 0; 64106cfb2dfSVladimir Oltean 64206cfb2dfSVladimir Oltean restore: 64306cfb2dfSVladimir Oltean ds->ops->port_vlan_filtering(ds, dp->index, old_vlan_filtering, NULL); 64406cfb2dfSVladimir Oltean 64506cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) 64606cfb2dfSVladimir Oltean ds->vlan_filtering = old_vlan_filtering; 64706cfb2dfSVladimir Oltean else 64806cfb2dfSVladimir Oltean dp->vlan_filtering = old_vlan_filtering; 64906cfb2dfSVladimir Oltean 65006cfb2dfSVladimir Oltean return err; 6514d61d304SVivien Didelot } 652d87bd94eSVivien Didelot 65354a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 65454a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 65554a0ed0dSRussell King */ 65654a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 65754a0ed0dSRussell King { 65854a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 65954a0ed0dSRussell King 66054a0ed0dSRussell King if (!dp->bridge_dev) 66154a0ed0dSRussell King return false; 66254a0ed0dSRussell King 66354a0ed0dSRussell King return (!ds->configure_vlan_while_not_filtering && 66454a0ed0dSRussell King !br_vlan_enabled(dp->bridge_dev)); 66554a0ed0dSRussell King } 66654a0ed0dSRussell King 667bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 668d87bd94eSVivien Didelot { 669d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 670d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 671bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 672bae33f2bSVladimir Oltean int err; 673d87bd94eSVivien Didelot 674bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 675bae33f2bSVladimir Oltean 676bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 677bae33f2bSVladimir Oltean if (err) 678bae33f2bSVladimir Oltean return err; 679d87bd94eSVivien Didelot 680d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 681d87bd94eSVivien Didelot 68277b61365SVladimir Oltean return 0; 683d87bd94eSVivien Didelot } 684d1cffff0SVivien Didelot 685e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, 686a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 687a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 688ea87005aSFlorian Fainelli { 689ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 690ea87005aSFlorian Fainelli 691a8b659e7SVladimir Oltean if (!ds->ops->port_pre_bridge_flags) 692ea87005aSFlorian Fainelli return -EINVAL; 693ea87005aSFlorian Fainelli 694a8b659e7SVladimir Oltean return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); 695ea87005aSFlorian Fainelli } 696ea87005aSFlorian Fainelli 697045c45d1SVladimir Oltean int dsa_port_bridge_flags(struct dsa_port *dp, 698a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 699a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 70057652796SRussell King { 70157652796SRussell King struct dsa_switch *ds = dp->ds; 702045c45d1SVladimir Oltean int err; 70357652796SRussell King 704a8b659e7SVladimir Oltean if (!ds->ops->port_bridge_flags) 70570a7c484SOleksij Rempel return -EOPNOTSUPP; 70657652796SRussell King 707045c45d1SVladimir Oltean err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack); 708045c45d1SVladimir Oltean if (err) 709045c45d1SVladimir Oltean return err; 710045c45d1SVladimir Oltean 711045c45d1SVladimir Oltean if (flags.mask & BR_LEARNING) { 712045c45d1SVladimir Oltean bool learning = flags.val & BR_LEARNING; 713045c45d1SVladimir Oltean 714045c45d1SVladimir Oltean if (learning == dp->learning) 715045c45d1SVladimir Oltean return 0; 716045c45d1SVladimir Oltean 717bee7c577SVladimir Oltean if ((dp->learning && !learning) && 718bee7c577SVladimir Oltean (dp->stp_state == BR_STATE_LEARNING || 719bee7c577SVladimir Oltean dp->stp_state == BR_STATE_FORWARDING)) 720045c45d1SVladimir Oltean dsa_port_fast_age(dp); 721045c45d1SVladimir Oltean 722045c45d1SVladimir Oltean dp->learning = learning; 723045c45d1SVladimir Oltean } 724045c45d1SVladimir Oltean 725045c45d1SVladimir Oltean return 0; 72657652796SRussell King } 72757652796SRussell King 728bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 72988faba20SVladimir Oltean bool targeted_match) 730bfcb8132SVladimir Oltean { 731bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 732bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 73388faba20SVladimir Oltean .targeted_match = targeted_match, 734bfcb8132SVladimir Oltean .port = dp->index, 735bfcb8132SVladimir Oltean .mtu = new_mtu, 736bfcb8132SVladimir Oltean }; 737bfcb8132SVladimir Oltean 738bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 739bfcb8132SVladimir Oltean } 740bfcb8132SVladimir Oltean 7412acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7422acf4e6aSArkadi Sharshevsky u16 vid) 743d1cffff0SVivien Didelot { 744685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 745685fb6a4SVivien Didelot .sw_index = dp->ds->index, 746685fb6a4SVivien Didelot .port = dp->index, 7472acf4e6aSArkadi Sharshevsky .addr = addr, 7482acf4e6aSArkadi Sharshevsky .vid = vid, 749685fb6a4SVivien Didelot }; 750d1cffff0SVivien Didelot 751685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 752d1cffff0SVivien Didelot } 753d1cffff0SVivien Didelot 7542acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7552acf4e6aSArkadi Sharshevsky u16 vid) 756d1cffff0SVivien Didelot { 757685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 758685fb6a4SVivien Didelot .sw_index = dp->ds->index, 759685fb6a4SVivien Didelot .port = dp->index, 7602acf4e6aSArkadi Sharshevsky .addr = addr, 7612acf4e6aSArkadi Sharshevsky .vid = vid, 7622acf4e6aSArkadi Sharshevsky 763685fb6a4SVivien Didelot }; 764d1cffff0SVivien Didelot 765685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 766d1cffff0SVivien Didelot } 767d1cffff0SVivien Didelot 7683dc80afcSVladimir Oltean int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7693dc80afcSVladimir Oltean u16 vid) 7703dc80afcSVladimir Oltean { 7713dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7723dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7733dc80afcSVladimir Oltean .port = dp->index, 7743dc80afcSVladimir Oltean .addr = addr, 7753dc80afcSVladimir Oltean .vid = vid, 7763dc80afcSVladimir Oltean }; 77726ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 77826ee7b06SVladimir Oltean int err; 77926ee7b06SVladimir Oltean 78026ee7b06SVladimir Oltean err = dev_uc_add(cpu_dp->master, addr); 78126ee7b06SVladimir Oltean if (err) 78226ee7b06SVladimir Oltean return err; 7833dc80afcSVladimir Oltean 7843dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); 7853dc80afcSVladimir Oltean } 7863dc80afcSVladimir Oltean 7873dc80afcSVladimir Oltean int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7883dc80afcSVladimir Oltean u16 vid) 7893dc80afcSVladimir Oltean { 7903dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7913dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7923dc80afcSVladimir Oltean .port = dp->index, 7933dc80afcSVladimir Oltean .addr = addr, 7943dc80afcSVladimir Oltean .vid = vid, 7953dc80afcSVladimir Oltean }; 79626ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 79726ee7b06SVladimir Oltean int err; 79826ee7b06SVladimir Oltean 79926ee7b06SVladimir Oltean err = dev_uc_del(cpu_dp->master, addr); 80026ee7b06SVladimir Oltean if (err) 80126ee7b06SVladimir Oltean return err; 8023dc80afcSVladimir Oltean 8033dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); 8043dc80afcSVladimir Oltean } 8053dc80afcSVladimir Oltean 806de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 807de40fc5dSVivien Didelot { 808de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 809de40fc5dSVivien Didelot int port = dp->index; 810de40fc5dSVivien Didelot 811de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 812de40fc5dSVivien Didelot return -EOPNOTSUPP; 813de40fc5dSVivien Didelot 814de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 815de40fc5dSVivien Didelot } 816de40fc5dSVivien Didelot 817bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 818ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 8193a9afea3SVivien Didelot { 8208ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 8218ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 8228ae5bcdcSVivien Didelot .port = dp->index, 8238ae5bcdcSVivien Didelot .mdb = mdb, 8248ae5bcdcSVivien Didelot }; 8253a9afea3SVivien Didelot 8268ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 8273a9afea3SVivien Didelot } 8283a9afea3SVivien Didelot 829bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 8303a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 8313a9afea3SVivien Didelot { 8328ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 8338ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 8348ae5bcdcSVivien Didelot .port = dp->index, 8358ae5bcdcSVivien Didelot .mdb = mdb, 8368ae5bcdcSVivien Didelot }; 8373a9afea3SVivien Didelot 8388ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 8393a9afea3SVivien Didelot } 8403a9afea3SVivien Didelot 841b8e997c4SVladimir Oltean int dsa_port_host_mdb_add(const struct dsa_port *dp, 842b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 843b8e997c4SVladimir Oltean { 844b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 845b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 846b8e997c4SVladimir Oltean .port = dp->index, 847b8e997c4SVladimir Oltean .mdb = mdb, 848b8e997c4SVladimir Oltean }; 84926ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 85026ee7b06SVladimir Oltean int err; 85126ee7b06SVladimir Oltean 85226ee7b06SVladimir Oltean err = dev_mc_add(cpu_dp->master, mdb->addr); 85326ee7b06SVladimir Oltean if (err) 85426ee7b06SVladimir Oltean return err; 855b8e997c4SVladimir Oltean 856b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); 857b8e997c4SVladimir Oltean } 858b8e997c4SVladimir Oltean 859b8e997c4SVladimir Oltean int dsa_port_host_mdb_del(const struct dsa_port *dp, 860b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 861b8e997c4SVladimir Oltean { 862b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 863b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 864b8e997c4SVladimir Oltean .port = dp->index, 865b8e997c4SVladimir Oltean .mdb = mdb, 866b8e997c4SVladimir Oltean }; 86726ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 86826ee7b06SVladimir Oltean int err; 86926ee7b06SVladimir Oltean 87026ee7b06SVladimir Oltean err = dev_mc_del(cpu_dp->master, mdb->addr); 87126ee7b06SVladimir Oltean if (err) 87226ee7b06SVladimir Oltean return err; 873b8e997c4SVladimir Oltean 874b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); 875b8e997c4SVladimir Oltean } 876b8e997c4SVladimir Oltean 877076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 87831046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 87931046a5fSVladimir Oltean struct netlink_ext_ack *extack) 880076e7133SVivien Didelot { 881d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 882d0c627b8SVivien Didelot .sw_index = dp->ds->index, 883d0c627b8SVivien Didelot .port = dp->index, 884d0c627b8SVivien Didelot .vlan = vlan, 88531046a5fSVladimir Oltean .extack = extack, 886d0c627b8SVivien Didelot }; 887076e7133SVivien Didelot 888d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 889076e7133SVivien Didelot } 890076e7133SVivien Didelot 891076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 892076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 893076e7133SVivien Didelot { 894d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 895d0c627b8SVivien Didelot .sw_index = dp->ds->index, 896d0c627b8SVivien Didelot .port = dp->index, 897d0c627b8SVivien Didelot .vlan = vlan, 898d0c627b8SVivien Didelot }; 899076e7133SVivien Didelot 900d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 901076e7133SVivien Didelot } 90257ab1ca2SVivien Didelot 903c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp, 904c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 905c595c433SHoratiu Vultur { 906c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 907c595c433SHoratiu Vultur .sw_index = dp->ds->index, 908c595c433SHoratiu Vultur .port = dp->index, 909c595c433SHoratiu Vultur .mrp = mrp, 910c595c433SHoratiu Vultur }; 911c595c433SHoratiu Vultur 912c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD, &info); 913c595c433SHoratiu Vultur } 914c595c433SHoratiu Vultur 915c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp, 916c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 917c595c433SHoratiu Vultur { 918c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 919c595c433SHoratiu Vultur .sw_index = dp->ds->index, 920c595c433SHoratiu Vultur .port = dp->index, 921c595c433SHoratiu Vultur .mrp = mrp, 922c595c433SHoratiu Vultur }; 923c595c433SHoratiu Vultur 924c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL, &info); 925c595c433SHoratiu Vultur } 926c595c433SHoratiu Vultur 927c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, 928c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 929c595c433SHoratiu Vultur { 930c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 931c595c433SHoratiu Vultur .sw_index = dp->ds->index, 932c595c433SHoratiu Vultur .port = dp->index, 933c595c433SHoratiu Vultur .mrp = mrp, 934c595c433SHoratiu Vultur }; 935c595c433SHoratiu Vultur 936c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD_RING_ROLE, &info); 937c595c433SHoratiu Vultur } 938c595c433SHoratiu Vultur 939c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, 940c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 941c595c433SHoratiu Vultur { 942c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 943c595c433SHoratiu Vultur .sw_index = dp->ds->index, 944c595c433SHoratiu Vultur .port = dp->index, 945c595c433SHoratiu Vultur .mrp = mrp, 946c595c433SHoratiu Vultur }; 947c595c433SHoratiu Vultur 948c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL_RING_ROLE, &info); 949c595c433SHoratiu Vultur } 950c595c433SHoratiu Vultur 95153da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 95253da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 95353da0ebaSVladimir Oltean { 95453da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 95553da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 95653da0ebaSVladimir Oltean } 95753da0ebaSVladimir Oltean 9586207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 9596207a78cSFlorian Fainelli { 9606207a78cSFlorian Fainelli struct device_node *phy_dn; 9616207a78cSFlorian Fainelli struct phy_device *phydev; 9626207a78cSFlorian Fainelli 9636207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 9646207a78cSFlorian Fainelli if (!phy_dn) 9656207a78cSFlorian Fainelli return NULL; 9666207a78cSFlorian Fainelli 9676207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 9686207a78cSFlorian Fainelli if (!phydev) { 9696207a78cSFlorian Fainelli of_node_put(phy_dn); 9706207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 9716207a78cSFlorian Fainelli } 9726207a78cSFlorian Fainelli 9739919a363SWen Yang of_node_put(phy_dn); 9746207a78cSFlorian Fainelli return phydev; 9756207a78cSFlorian Fainelli } 9766207a78cSFlorian Fainelli 9778ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 97877373d49SIoana Ciornei unsigned long *supported, 97977373d49SIoana Ciornei struct phylink_link_state *state) 98077373d49SIoana Ciornei { 98177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 98277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 98377373d49SIoana Ciornei 98477373d49SIoana Ciornei if (!ds->ops->phylink_validate) 98577373d49SIoana Ciornei return; 98677373d49SIoana Ciornei 98777373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 98877373d49SIoana Ciornei } 98977373d49SIoana Ciornei 9908ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 99177373d49SIoana Ciornei struct phylink_link_state *state) 99277373d49SIoana Ciornei { 99377373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 99477373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 99587615c96SRussell King int err; 99677373d49SIoana Ciornei 997d46b7e4fSRussell King /* Only called for inband modes */ 998d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 999d46b7e4fSRussell King state->link = 0; 1000d46b7e4fSRussell King return; 100177373d49SIoana Ciornei } 1002d46b7e4fSRussell King 100387615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 100487615c96SRussell King if (err < 0) { 100587615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 100687615c96SRussell King dp->index, err); 1007d46b7e4fSRussell King state->link = 0; 1008d46b7e4fSRussell King } 100987615c96SRussell King } 101077373d49SIoana Ciornei 10118ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 101277373d49SIoana Ciornei unsigned int mode, 101377373d49SIoana Ciornei const struct phylink_link_state *state) 101477373d49SIoana Ciornei { 101577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 101677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 101777373d49SIoana Ciornei 101877373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 101977373d49SIoana Ciornei return; 102077373d49SIoana Ciornei 102177373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 102277373d49SIoana Ciornei } 102377373d49SIoana Ciornei 10248ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 102577373d49SIoana Ciornei { 102677373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 102777373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 102877373d49SIoana Ciornei 102977373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 103077373d49SIoana Ciornei return; 103177373d49SIoana Ciornei 103277373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 103377373d49SIoana Ciornei } 103477373d49SIoana Ciornei 10358ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 103677373d49SIoana Ciornei unsigned int mode, 103777373d49SIoana Ciornei phy_interface_t interface) 103877373d49SIoana Ciornei { 103977373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 10400e279218SIoana Ciornei struct phy_device *phydev = NULL; 104177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 104277373d49SIoana Ciornei 104357d77986SVladimir Oltean if (dsa_port_is_user(dp)) 10440e279218SIoana Ciornei phydev = dp->slave->phydev; 10450e279218SIoana Ciornei 104677373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 10470e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 10480e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 104977373d49SIoana Ciornei return; 105077373d49SIoana Ciornei } 105177373d49SIoana Ciornei 105277373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 105377373d49SIoana Ciornei } 105477373d49SIoana Ciornei 10558ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 105691a208f2SRussell King struct phy_device *phydev, 105777373d49SIoana Ciornei unsigned int mode, 105877373d49SIoana Ciornei phy_interface_t interface, 105991a208f2SRussell King int speed, int duplex, 106091a208f2SRussell King bool tx_pause, bool rx_pause) 106177373d49SIoana Ciornei { 106277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 106377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 106477373d49SIoana Ciornei 106577373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 10660e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 10670e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 106877373d49SIoana Ciornei return; 106977373d49SIoana Ciornei } 107077373d49SIoana Ciornei 10715b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 10725b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 107377373d49SIoana Ciornei } 107477373d49SIoana Ciornei 107577373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 107677373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 1077d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 107877373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 107977373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 108077373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 108177373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 108277373d49SIoana Ciornei }; 108377373d49SIoana Ciornei 108433615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 108533615367SSebastian Reichel { 108633615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 108733615367SSebastian Reichel struct phy_device *phydev; 108833615367SSebastian Reichel int port = dp->index; 108933615367SSebastian Reichel int err = 0; 109033615367SSebastian Reichel 10916207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 10926207a78cSFlorian Fainelli if (!phydev) 109333615367SSebastian Reichel return 0; 109433615367SSebastian Reichel 10956207a78cSFlorian Fainelli if (IS_ERR(phydev)) 10966207a78cSFlorian Fainelli return PTR_ERR(phydev); 109733615367SSebastian Reichel 109833615367SSebastian Reichel if (enable) { 109933615367SSebastian Reichel err = genphy_resume(phydev); 110033615367SSebastian Reichel if (err < 0) 110133615367SSebastian Reichel goto err_put_dev; 110233615367SSebastian Reichel 110333615367SSebastian Reichel err = genphy_read_status(phydev); 110433615367SSebastian Reichel if (err < 0) 110533615367SSebastian Reichel goto err_put_dev; 110633615367SSebastian Reichel } else { 110733615367SSebastian Reichel err = genphy_suspend(phydev); 110833615367SSebastian Reichel if (err < 0) 110933615367SSebastian Reichel goto err_put_dev; 111033615367SSebastian Reichel } 111133615367SSebastian Reichel 111233615367SSebastian Reichel if (ds->ops->adjust_link) 111333615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 111433615367SSebastian Reichel 111533615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 111633615367SSebastian Reichel 111733615367SSebastian Reichel err_put_dev: 111833615367SSebastian Reichel put_device(&phydev->mdio.dev); 111933615367SSebastian Reichel return err; 112033615367SSebastian Reichel } 112133615367SSebastian Reichel 112233615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 112357ab1ca2SVivien Didelot { 112457ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 112557ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 112657ab1ca2SVivien Didelot struct phy_device *phydev; 112757ab1ca2SVivien Didelot int port = dp->index; 11280c65b2b9SAndrew Lunn phy_interface_t mode; 112957ab1ca2SVivien Didelot int err; 113057ab1ca2SVivien Didelot 113157ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 113257ab1ca2SVivien Didelot if (err) { 113357ab1ca2SVivien Didelot dev_err(ds->dev, 113457ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 113557ab1ca2SVivien Didelot port); 113657ab1ca2SVivien Didelot return err; 113757ab1ca2SVivien Didelot } 113857ab1ca2SVivien Didelot 113957ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 114057ab1ca2SVivien Didelot 11410c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 11420c65b2b9SAndrew Lunn if (err) 114357ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 114457ab1ca2SVivien Didelot phydev->interface = mode; 114557ab1ca2SVivien Didelot 114657ab1ca2SVivien Didelot genphy_read_status(phydev); 114757ab1ca2SVivien Didelot 114857ab1ca2SVivien Didelot if (ds->ops->adjust_link) 114957ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 115057ab1ca2SVivien Didelot 115157ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 115257ab1ca2SVivien Didelot 115357ab1ca2SVivien Didelot return 0; 115457ab1ca2SVivien Didelot } 115557ab1ca2SVivien Didelot 11560e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 11570e279218SIoana Ciornei { 11580e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 11590e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 11600c65b2b9SAndrew Lunn phy_interface_t mode; 11610c65b2b9SAndrew Lunn int err; 11620e279218SIoana Ciornei 11630c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 11640c65b2b9SAndrew Lunn if (err) 11650e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 11660e279218SIoana Ciornei 11670e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 11680e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 1169787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 11700e279218SIoana Ciornei 11710e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 11720e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 11730e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 11740e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 11750e279218SIoana Ciornei return PTR_ERR(dp->pl); 11760e279218SIoana Ciornei } 11770e279218SIoana Ciornei 11780e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 11792131fba5SFlorian Fainelli if (err && err != -ENODEV) { 11800e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 11810e279218SIoana Ciornei goto err_phy_connect; 11820e279218SIoana Ciornei } 11830e279218SIoana Ciornei 11840e279218SIoana Ciornei return 0; 11850e279218SIoana Ciornei 11860e279218SIoana Ciornei err_phy_connect: 11870e279218SIoana Ciornei phylink_destroy(dp->pl); 11880e279218SIoana Ciornei return err; 11890e279218SIoana Ciornei } 11900e279218SIoana Ciornei 119133615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 119257ab1ca2SVivien Didelot { 11930e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 1194a20f9970SAndrew Lunn struct device_node *phy_np; 11953be98b2dSAndrew Lunn int port = dp->index; 11960e279218SIoana Ciornei 1197a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 1198a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 11993be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 12003be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 12013be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 12023be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 12030e279218SIoana Ciornei return dsa_port_phylink_register(dp); 12043be98b2dSAndrew Lunn } 1205a20f9970SAndrew Lunn return 0; 1206a20f9970SAndrew Lunn } 12070e279218SIoana Ciornei 12080e279218SIoana Ciornei dev_warn(ds->dev, 12090e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 12100e279218SIoana Ciornei 121133615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 121233615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 121333615367SSebastian Reichel else 121433615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 121533615367SSebastian Reichel } 121657ab1ca2SVivien Didelot 121733615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 121833615367SSebastian Reichel { 12190e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 12200e279218SIoana Ciornei 1221a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 12220e279218SIoana Ciornei rtnl_lock(); 12230e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 12240e279218SIoana Ciornei rtnl_unlock(); 12250e279218SIoana Ciornei phylink_destroy(dp->pl); 1226a20f9970SAndrew Lunn dp->pl = NULL; 12270e279218SIoana Ciornei return; 12280e279218SIoana Ciornei } 12290e279218SIoana Ciornei 123033615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 123133615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 123233615367SSebastian Reichel else 123333615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 123457ab1ca2SVivien Didelot } 1235cf963573SFlorian Fainelli 1236cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 1237cf963573SFlorian Fainelli { 1238cf963573SFlorian Fainelli struct phy_device *phydev; 1239cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1240cf963573SFlorian Fainelli 1241cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1242cf963573SFlorian Fainelli return ret; 1243cf963573SFlorian Fainelli 1244cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1245cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1246cf963573SFlorian Fainelli return ret; 1247cf963573SFlorian Fainelli 1248cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 1249cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1250cf963573SFlorian Fainelli 1251cf963573SFlorian Fainelli return ret; 1252cf963573SFlorian Fainelli } 1253cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 1254cf963573SFlorian Fainelli 1255cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 1256cf963573SFlorian Fainelli { 1257cf963573SFlorian Fainelli struct phy_device *phydev; 1258cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1259cf963573SFlorian Fainelli 1260cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1261cf963573SFlorian Fainelli return ret; 1262cf963573SFlorian Fainelli 1263cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1264cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1265cf963573SFlorian Fainelli return ret; 1266cf963573SFlorian Fainelli 1267cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 1268cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1269cf963573SFlorian Fainelli 1270cf963573SFlorian Fainelli return ret; 1271cf963573SFlorian Fainelli } 1272cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 1273cf963573SFlorian Fainelli 1274cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 1275cf963573SFlorian Fainelli { 1276cf963573SFlorian Fainelli struct phy_device *phydev; 1277cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 1278cf963573SFlorian Fainelli 1279cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 1280cf963573SFlorian Fainelli return ret; 1281cf963573SFlorian Fainelli 1282cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 1283cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 1284cf963573SFlorian Fainelli return ret; 1285cf963573SFlorian Fainelli 1286cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 1287cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 1288cf963573SFlorian Fainelli 1289cf963573SFlorian Fainelli return ret; 1290cf963573SFlorian Fainelli } 1291cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 129218596f50SGeorge McCollister 129318596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 129418596f50SGeorge McCollister { 129518596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 129618596f50SGeorge McCollister .sw_index = dp->ds->index, 129718596f50SGeorge McCollister .port = dp->index, 129818596f50SGeorge McCollister .hsr = hsr, 129918596f50SGeorge McCollister }; 130018596f50SGeorge McCollister int err; 130118596f50SGeorge McCollister 130218596f50SGeorge McCollister dp->hsr_dev = hsr; 130318596f50SGeorge McCollister 130418596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info); 130518596f50SGeorge McCollister if (err) 130618596f50SGeorge McCollister dp->hsr_dev = NULL; 130718596f50SGeorge McCollister 130818596f50SGeorge McCollister return err; 130918596f50SGeorge McCollister } 131018596f50SGeorge McCollister 131118596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 131218596f50SGeorge McCollister { 131318596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 131418596f50SGeorge McCollister .sw_index = dp->ds->index, 131518596f50SGeorge McCollister .port = dp->index, 131618596f50SGeorge McCollister .hsr = hsr, 131718596f50SGeorge McCollister }; 131818596f50SGeorge McCollister int err; 131918596f50SGeorge McCollister 132018596f50SGeorge McCollister dp->hsr_dev = NULL; 132118596f50SGeorge McCollister 132218596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info); 132318596f50SGeorge McCollister if (err) 1324ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1325ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_HSR_LEAVE: %pe\n", 1326ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 132718596f50SGeorge McCollister } 1328c64b9c05SVladimir Oltean 1329724395f4SVladimir Oltean int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast) 1330c64b9c05SVladimir Oltean { 1331c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1332c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1333c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1334c64b9c05SVladimir Oltean .port = dp->index, 1335c64b9c05SVladimir Oltean .vid = vid, 1336c64b9c05SVladimir Oltean }; 1337c64b9c05SVladimir Oltean 1338724395f4SVladimir Oltean if (broadcast) 1339c64b9c05SVladimir Oltean return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1340724395f4SVladimir Oltean 1341724395f4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1342c64b9c05SVladimir Oltean } 1343c64b9c05SVladimir Oltean 1344724395f4SVladimir Oltean void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast) 1345c64b9c05SVladimir Oltean { 1346c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1347c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1348c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1349c64b9c05SVladimir Oltean .port = dp->index, 1350c64b9c05SVladimir Oltean .vid = vid, 1351c64b9c05SVladimir Oltean }; 1352c64b9c05SVladimir Oltean int err; 1353c64b9c05SVladimir Oltean 1354724395f4SVladimir Oltean if (broadcast) 1355c64b9c05SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1356724395f4SVladimir Oltean else 1357724395f4SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1358c64b9c05SVladimir Oltean if (err) 1359ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1360ab97462bSVladimir Oltean "port %d failed to notify tag_8021q VLAN %d deletion: %pe\n", 1361ab97462bSVladimir Oltean dp->index, vid, ERR_PTR(err)); 1362c64b9c05SVladimir Oltean } 1363