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 133d3eed0e5SVladimir Oltean if (!dp->bridge) 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 161d3eed0e5SVladimir Oltean if (!dp->bridge) 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); 22436cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 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 273947c8746SVladimir Oltean static int dsa_port_bridge_create(struct dsa_port *dp, 274947c8746SVladimir Oltean struct net_device *br, 275947c8746SVladimir Oltean struct netlink_ext_ack *extack) 276947c8746SVladimir Oltean { 277947c8746SVladimir Oltean struct dsa_switch *ds = dp->ds; 278d3eed0e5SVladimir Oltean struct dsa_bridge *bridge; 279947c8746SVladimir Oltean 280d3eed0e5SVladimir Oltean bridge = dsa_tree_bridge_find(ds->dst, br); 281d3eed0e5SVladimir Oltean if (bridge) { 282d3eed0e5SVladimir Oltean refcount_inc(&bridge->refcount); 283d3eed0e5SVladimir Oltean dp->bridge = bridge; 284947c8746SVladimir Oltean return 0; 285d3eed0e5SVladimir Oltean } 286947c8746SVladimir Oltean 287d3eed0e5SVladimir Oltean bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); 288d3eed0e5SVladimir Oltean if (!bridge) 289d3eed0e5SVladimir Oltean return -ENOMEM; 290d3eed0e5SVladimir Oltean 291d3eed0e5SVladimir Oltean refcount_set(&bridge->refcount, 1); 292d3eed0e5SVladimir Oltean 293d3eed0e5SVladimir Oltean bridge->dev = br; 294d3eed0e5SVladimir Oltean 295d3eed0e5SVladimir Oltean bridge->num = dsa_bridge_num_get(br, ds->max_num_bridges); 296d3eed0e5SVladimir Oltean if (ds->max_num_bridges && !bridge->num) { 297947c8746SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 298947c8746SVladimir Oltean "Range of offloadable bridges exceeded"); 299d3eed0e5SVladimir Oltean kfree(bridge); 300947c8746SVladimir Oltean return -EOPNOTSUPP; 301947c8746SVladimir Oltean } 302947c8746SVladimir Oltean 303d3eed0e5SVladimir Oltean dp->bridge = bridge; 304947c8746SVladimir Oltean 305947c8746SVladimir Oltean return 0; 306947c8746SVladimir Oltean } 307947c8746SVladimir Oltean 308947c8746SVladimir Oltean static void dsa_port_bridge_destroy(struct dsa_port *dp, 309947c8746SVladimir Oltean const struct net_device *br) 310947c8746SVladimir Oltean { 311d3eed0e5SVladimir Oltean struct dsa_bridge *bridge = dp->bridge; 312947c8746SVladimir Oltean 313d3eed0e5SVladimir Oltean dp->bridge = NULL; 314947c8746SVladimir Oltean 315d3eed0e5SVladimir Oltean if (!refcount_dec_and_test(&bridge->refcount)) 316d3eed0e5SVladimir Oltean return; 317947c8746SVladimir Oltean 318d3eed0e5SVladimir Oltean if (bridge->num) 319d3eed0e5SVladimir Oltean dsa_bridge_num_put(br, bridge->num); 320d3eed0e5SVladimir Oltean 321d3eed0e5SVladimir Oltean kfree(bridge); 322123abc06SVladimir Oltean } 323123abc06SVladimir Oltean 3242afc526aSVladimir Oltean int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, 3252afc526aSVladimir Oltean struct netlink_ext_ack *extack) 326cfbed329SVivien Didelot { 327cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 328f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 329cfbed329SVivien Didelot .sw_index = dp->ds->index, 330cfbed329SVivien Didelot .port = dp->index, 331cfbed329SVivien Didelot }; 3322f5dc00fSVladimir Oltean struct net_device *dev = dp->slave; 3332f5dc00fSVladimir Oltean struct net_device *brport_dev; 334cfbed329SVivien Didelot int err; 335cfbed329SVivien Didelot 336c1388063SRussell King /* Here the interface is already bridged. Reflect the current 337c1388063SRussell King * configuration so that drivers can program their chips accordingly. 338cfbed329SVivien Didelot */ 339947c8746SVladimir Oltean err = dsa_port_bridge_create(dp, br, extack); 340947c8746SVladimir Oltean if (err) 341947c8746SVladimir Oltean return err; 342cfbed329SVivien Didelot 3432f5dc00fSVladimir Oltean brport_dev = dsa_port_to_bridge_port(dp); 3442f5dc00fSVladimir Oltean 345d3eed0e5SVladimir Oltean info.bridge = *dp->bridge; 346f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); 3475961d6a1SVladimir Oltean if (err) 3485961d6a1SVladimir Oltean goto out_rollback; 349cfbed329SVivien Didelot 350857fdd74SVladimir Oltean /* Drivers which support bridge TX forwarding should set this */ 351857fdd74SVladimir Oltean dp->bridge->tx_fwd_offload = info.tx_fwd_offload; 352123abc06SVladimir Oltean 3534e51bf44SVladimir Oltean err = switchdev_bridge_port_offload(brport_dev, dev, dp, 3544e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3554e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier, 356857fdd74SVladimir Oltean dp->bridge->tx_fwd_offload, extack); 3575961d6a1SVladimir Oltean if (err) 3585961d6a1SVladimir Oltean goto out_rollback_unbridge; 3595961d6a1SVladimir Oltean 3604e51bf44SVladimir Oltean err = dsa_port_switchdev_sync_attrs(dp, extack); 3612f5dc00fSVladimir Oltean if (err) 3622f5dc00fSVladimir Oltean goto out_rollback_unoffload; 3632f5dc00fSVladimir Oltean 3645961d6a1SVladimir Oltean return 0; 3655961d6a1SVladimir Oltean 3662f5dc00fSVladimir Oltean out_rollback_unoffload: 3674e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3684e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3694e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 3705961d6a1SVladimir Oltean out_rollback_unbridge: 3715961d6a1SVladimir Oltean dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 3725961d6a1SVladimir Oltean out_rollback: 373947c8746SVladimir Oltean dsa_port_bridge_destroy(dp, br); 374cfbed329SVivien Didelot return err; 375cfbed329SVivien Didelot } 376cfbed329SVivien Didelot 3774e51bf44SVladimir Oltean void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br) 37874918945SVladimir Oltean { 3792f5dc00fSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 3802f5dc00fSVladimir Oltean 38109dba21bSVladimir Oltean /* Don't try to unoffload something that is not offloaded */ 38209dba21bSVladimir Oltean if (!brport_dev) 38309dba21bSVladimir Oltean return; 38409dba21bSVladimir Oltean 3854e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3864e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3874e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 388d7d0d423SVladimir Oltean 389d7d0d423SVladimir Oltean dsa_flush_workqueue(); 39074918945SVladimir Oltean } 39174918945SVladimir Oltean 392cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 393cfbed329SVivien Didelot { 394cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 395f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 396cfbed329SVivien Didelot .sw_index = dp->ds->index, 397cfbed329SVivien Didelot .port = dp->index, 398d3eed0e5SVladimir Oltean .bridge = *dp->bridge, 399cfbed329SVivien Didelot }; 400cfbed329SVivien Didelot int err; 401cfbed329SVivien Didelot 402cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 403cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 404cfbed329SVivien Didelot */ 405947c8746SVladimir Oltean dsa_port_bridge_destroy(dp, br); 406cfbed329SVivien Didelot 407f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 408cfbed329SVivien Didelot if (err) 409ab97462bSVladimir Oltean dev_err(dp->ds->dev, 410ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n", 411ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 412cfbed329SVivien Didelot 41374918945SVladimir Oltean dsa_port_switchdev_unsync_attrs(dp); 414cfbed329SVivien Didelot } 4154d61d304SVivien Didelot 416058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 417058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 418058102a6STobias Waldekranz { 419058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 420058102a6STobias Waldekranz .sw_index = dp->ds->index, 421058102a6STobias Waldekranz .port = dp->index, 422058102a6STobias Waldekranz }; 423058102a6STobias Waldekranz bool tx_enabled; 424058102a6STobias Waldekranz 425058102a6STobias Waldekranz if (!dp->lag_dev) 426058102a6STobias Waldekranz return 0; 427058102a6STobias Waldekranz 428058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 429058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 430058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 431058102a6STobias Waldekranz * in order to include it in the tx set. 432058102a6STobias Waldekranz */ 433058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 434058102a6STobias Waldekranz 435058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 436058102a6STobias Waldekranz return 0; 437058102a6STobias Waldekranz 438058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 439058102a6STobias Waldekranz 440058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 441058102a6STobias Waldekranz } 442058102a6STobias Waldekranz 443058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, 4442afc526aSVladimir Oltean struct netdev_lag_upper_info *uinfo, 4452afc526aSVladimir Oltean struct netlink_ext_ack *extack) 446058102a6STobias Waldekranz { 447058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 448058102a6STobias Waldekranz .sw_index = dp->ds->index, 449058102a6STobias Waldekranz .port = dp->index, 450058102a6STobias Waldekranz .lag = lag, 451058102a6STobias Waldekranz .info = uinfo, 452058102a6STobias Waldekranz }; 453185c9a76SVladimir Oltean struct net_device *bridge_dev; 454058102a6STobias Waldekranz int err; 455058102a6STobias Waldekranz 456058102a6STobias Waldekranz dsa_lag_map(dp->ds->dst, lag); 457058102a6STobias Waldekranz dp->lag_dev = lag; 458058102a6STobias Waldekranz 459058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 460185c9a76SVladimir Oltean if (err) 461185c9a76SVladimir Oltean goto err_lag_join; 462185c9a76SVladimir Oltean 463185c9a76SVladimir Oltean bridge_dev = netdev_master_upper_dev_get(lag); 464185c9a76SVladimir Oltean if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 465185c9a76SVladimir Oltean return 0; 466185c9a76SVladimir Oltean 4672afc526aSVladimir Oltean err = dsa_port_bridge_join(dp, bridge_dev, extack); 468185c9a76SVladimir Oltean if (err) 469185c9a76SVladimir Oltean goto err_bridge_join; 470185c9a76SVladimir Oltean 471185c9a76SVladimir Oltean return 0; 472185c9a76SVladimir Oltean 473185c9a76SVladimir Oltean err_bridge_join: 474185c9a76SVladimir Oltean dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 475185c9a76SVladimir Oltean err_lag_join: 476058102a6STobias Waldekranz dp->lag_dev = NULL; 477058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 478058102a6STobias Waldekranz return err; 479058102a6STobias Waldekranz } 480058102a6STobias Waldekranz 4814e51bf44SVladimir Oltean void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag) 48274918945SVladimir Oltean { 48336cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 48436cbf39bSVladimir Oltean 48536cbf39bSVladimir Oltean if (br) 48636cbf39bSVladimir Oltean dsa_port_pre_bridge_leave(dp, br); 48774918945SVladimir Oltean } 48874918945SVladimir Oltean 489058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) 490058102a6STobias Waldekranz { 49136cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 492058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 493058102a6STobias Waldekranz .sw_index = dp->ds->index, 494058102a6STobias Waldekranz .port = dp->index, 495058102a6STobias Waldekranz .lag = lag, 496058102a6STobias Waldekranz }; 497058102a6STobias Waldekranz int err; 498058102a6STobias Waldekranz 499058102a6STobias Waldekranz if (!dp->lag_dev) 500058102a6STobias Waldekranz return; 501058102a6STobias Waldekranz 502058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 503058102a6STobias Waldekranz * attached to a bridge. 504058102a6STobias Waldekranz */ 50536cbf39bSVladimir Oltean if (br) 50636cbf39bSVladimir Oltean dsa_port_bridge_leave(dp, br); 507058102a6STobias Waldekranz 508058102a6STobias Waldekranz dp->lag_tx_enabled = false; 509058102a6STobias Waldekranz dp->lag_dev = NULL; 510058102a6STobias Waldekranz 511058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 512058102a6STobias Waldekranz if (err) 513ab97462bSVladimir Oltean dev_err(dp->ds->dev, 514ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n", 515ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 516058102a6STobias Waldekranz 517058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 518058102a6STobias Waldekranz } 519058102a6STobias Waldekranz 520adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 5218f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 52289153ed6SVladimir Oltean bool vlan_filtering, 52389153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5248f5d16f6SVladimir Oltean { 5258f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 526d0004a02SVladimir Oltean struct dsa_port *other_dp; 527d0004a02SVladimir Oltean int err; 528adb256ebSVladimir Oltean 529adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 530adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 531adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 532adb256ebSVladimir Oltean * as long as we have 8021q uppers. 533adb256ebSVladimir Oltean */ 53457d77986SVladimir Oltean if (vlan_filtering && dsa_port_is_user(dp)) { 53536cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 536adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 537adb256ebSVladimir Oltean struct list_head *iter; 538adb256ebSVladimir Oltean 539adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 540adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 541adb256ebSVladimir Oltean u16 vid; 542adb256ebSVladimir Oltean 543adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 544adb256ebSVladimir Oltean continue; 545adb256ebSVladimir Oltean 546adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 547adb256ebSVladimir Oltean 548adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 549adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 550adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 551adb256ebSVladimir Oltean */ 552adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 553adb256ebSVladimir Oltean if (err == 0) { 55489153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 55589153ed6SVladimir Oltean "Must first remove VLAN uppers having VIDs also present in bridge"); 556adb256ebSVladimir Oltean return false; 557adb256ebSVladimir Oltean } 558adb256ebSVladimir Oltean } 559adb256ebSVladimir Oltean } 5608f5d16f6SVladimir Oltean 5618f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 5628f5d16f6SVladimir Oltean return true; 5638f5d16f6SVladimir Oltean 5648f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 5658f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 5668f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 5678f5d16f6SVladimir Oltean * different setting than what is being requested. 5688f5d16f6SVladimir Oltean */ 569d0004a02SVladimir Oltean dsa_switch_for_each_port(other_dp, ds) { 57036cbf39bSVladimir Oltean struct net_device *other_br = dsa_port_bridge_dev_get(other_dp); 5718f5d16f6SVladimir Oltean 5728f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 5738f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 5748f5d16f6SVladimir Oltean */ 57536cbf39bSVladimir Oltean if (!other_br || other_br == dsa_port_bridge_dev_get(dp)) 5768f5d16f6SVladimir Oltean continue; 57736cbf39bSVladimir Oltean 57836cbf39bSVladimir Oltean if (br_vlan_enabled(other_br) != vlan_filtering) { 57989153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 58089153ed6SVladimir Oltean "VLAN filtering is a global setting"); 5818f5d16f6SVladimir Oltean return false; 5828f5d16f6SVladimir Oltean } 5838f5d16f6SVladimir Oltean } 5848f5d16f6SVladimir Oltean return true; 5858f5d16f6SVladimir Oltean } 5868f5d16f6SVladimir Oltean 58789153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 58889153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5894d61d304SVivien Didelot { 59006cfb2dfSVladimir Oltean bool old_vlan_filtering = dsa_port_is_vlan_filtering(dp); 5914d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 592adb256ebSVladimir Oltean bool apply; 593bae33f2bSVladimir Oltean int err; 594adb256ebSVladimir Oltean 5958f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 596707ec383SVladimir Oltean return -EOPNOTSUPP; 5978f5d16f6SVladimir Oltean 598adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 599adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 600adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 601adb256ebSVladimir Oltean */ 602adb256ebSVladimir Oltean rcu_read_lock(); 60389153ed6SVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack); 604adb256ebSVladimir Oltean rcu_read_unlock(); 605adb256ebSVladimir Oltean if (!apply) 6068f5d16f6SVladimir Oltean return -EINVAL; 607707ec383SVladimir Oltean 608ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 609ec9121e7SVladimir Oltean return 0; 610ec9121e7SVladimir Oltean 61189153ed6SVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering, 61289153ed6SVladimir Oltean extack); 61333162e9aSVladimir Oltean if (err) 61433162e9aSVladimir Oltean return err; 6158f5d16f6SVladimir Oltean 61606cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) { 617d0004a02SVladimir Oltean struct dsa_port *other_dp; 61806cfb2dfSVladimir Oltean 61914574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 62006cfb2dfSVladimir Oltean 621d0004a02SVladimir Oltean dsa_switch_for_each_user_port(other_dp, ds) { 622d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 62306cfb2dfSVladimir Oltean 62406cfb2dfSVladimir Oltean /* We might be called in the unbind path, so not 62506cfb2dfSVladimir Oltean * all slave devices might still be registered. 62606cfb2dfSVladimir Oltean */ 62706cfb2dfSVladimir Oltean if (!slave) 62806cfb2dfSVladimir Oltean continue; 62906cfb2dfSVladimir Oltean 63006cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(slave, 63106cfb2dfSVladimir Oltean vlan_filtering); 63206cfb2dfSVladimir Oltean if (err) 63306cfb2dfSVladimir Oltean goto restore; 63406cfb2dfSVladimir Oltean } 63506cfb2dfSVladimir Oltean } else { 63633162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 6372e554a7aSVladimir Oltean 63806cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(dp->slave, 63906cfb2dfSVladimir Oltean vlan_filtering); 64006cfb2dfSVladimir Oltean if (err) 64106cfb2dfSVladimir Oltean goto restore; 64206cfb2dfSVladimir Oltean } 64306cfb2dfSVladimir Oltean 6444d61d304SVivien Didelot return 0; 64506cfb2dfSVladimir Oltean 64606cfb2dfSVladimir Oltean restore: 64706cfb2dfSVladimir Oltean ds->ops->port_vlan_filtering(ds, dp->index, old_vlan_filtering, NULL); 64806cfb2dfSVladimir Oltean 64906cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) 65006cfb2dfSVladimir Oltean ds->vlan_filtering = old_vlan_filtering; 65106cfb2dfSVladimir Oltean else 65206cfb2dfSVladimir Oltean dp->vlan_filtering = old_vlan_filtering; 65306cfb2dfSVladimir Oltean 65406cfb2dfSVladimir Oltean return err; 6554d61d304SVivien Didelot } 656d87bd94eSVivien Didelot 65754a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 65854a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 65954a0ed0dSRussell King */ 66054a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 66154a0ed0dSRussell King { 66236cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 66354a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 66454a0ed0dSRussell King 66536cbf39bSVladimir Oltean if (!br) 66654a0ed0dSRussell King return false; 66754a0ed0dSRussell King 66836cbf39bSVladimir Oltean return !ds->configure_vlan_while_not_filtering && !br_vlan_enabled(br); 66954a0ed0dSRussell King } 67054a0ed0dSRussell King 671bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 672d87bd94eSVivien Didelot { 673d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 674d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 675bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 676bae33f2bSVladimir Oltean int err; 677d87bd94eSVivien Didelot 678bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 679bae33f2bSVladimir Oltean 680bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 681bae33f2bSVladimir Oltean if (err) 682bae33f2bSVladimir Oltean return err; 683d87bd94eSVivien Didelot 684d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 685d87bd94eSVivien Didelot 68677b61365SVladimir Oltean return 0; 687d87bd94eSVivien Didelot } 688d1cffff0SVivien Didelot 689e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, 690a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 691a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 692ea87005aSFlorian Fainelli { 693ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 694ea87005aSFlorian Fainelli 695a8b659e7SVladimir Oltean if (!ds->ops->port_pre_bridge_flags) 696ea87005aSFlorian Fainelli return -EINVAL; 697ea87005aSFlorian Fainelli 698a8b659e7SVladimir Oltean return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); 699ea87005aSFlorian Fainelli } 700ea87005aSFlorian Fainelli 701045c45d1SVladimir Oltean int dsa_port_bridge_flags(struct dsa_port *dp, 702a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 703a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 70457652796SRussell King { 70557652796SRussell King struct dsa_switch *ds = dp->ds; 706045c45d1SVladimir Oltean int err; 70757652796SRussell King 708a8b659e7SVladimir Oltean if (!ds->ops->port_bridge_flags) 70970a7c484SOleksij Rempel return -EOPNOTSUPP; 71057652796SRussell King 711045c45d1SVladimir Oltean err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack); 712045c45d1SVladimir Oltean if (err) 713045c45d1SVladimir Oltean return err; 714045c45d1SVladimir Oltean 715045c45d1SVladimir Oltean if (flags.mask & BR_LEARNING) { 716045c45d1SVladimir Oltean bool learning = flags.val & BR_LEARNING; 717045c45d1SVladimir Oltean 718045c45d1SVladimir Oltean if (learning == dp->learning) 719045c45d1SVladimir Oltean return 0; 720045c45d1SVladimir Oltean 721bee7c577SVladimir Oltean if ((dp->learning && !learning) && 722bee7c577SVladimir Oltean (dp->stp_state == BR_STATE_LEARNING || 723bee7c577SVladimir Oltean dp->stp_state == BR_STATE_FORWARDING)) 724045c45d1SVladimir Oltean dsa_port_fast_age(dp); 725045c45d1SVladimir Oltean 726045c45d1SVladimir Oltean dp->learning = learning; 727045c45d1SVladimir Oltean } 728045c45d1SVladimir Oltean 729045c45d1SVladimir Oltean return 0; 73057652796SRussell King } 73157652796SRussell King 732bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 73388faba20SVladimir Oltean bool targeted_match) 734bfcb8132SVladimir Oltean { 735bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 736bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 73788faba20SVladimir Oltean .targeted_match = targeted_match, 738bfcb8132SVladimir Oltean .port = dp->index, 739bfcb8132SVladimir Oltean .mtu = new_mtu, 740bfcb8132SVladimir Oltean }; 741bfcb8132SVladimir Oltean 742bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 743bfcb8132SVladimir Oltean } 744bfcb8132SVladimir Oltean 7452acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7462acf4e6aSArkadi Sharshevsky u16 vid) 747d1cffff0SVivien Didelot { 748685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 749685fb6a4SVivien Didelot .sw_index = dp->ds->index, 750685fb6a4SVivien Didelot .port = dp->index, 7512acf4e6aSArkadi Sharshevsky .addr = addr, 7522acf4e6aSArkadi Sharshevsky .vid = vid, 753685fb6a4SVivien Didelot }; 754d1cffff0SVivien Didelot 755685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 756d1cffff0SVivien Didelot } 757d1cffff0SVivien Didelot 7582acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7592acf4e6aSArkadi Sharshevsky u16 vid) 760d1cffff0SVivien Didelot { 761685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 762685fb6a4SVivien Didelot .sw_index = dp->ds->index, 763685fb6a4SVivien Didelot .port = dp->index, 7642acf4e6aSArkadi Sharshevsky .addr = addr, 7652acf4e6aSArkadi Sharshevsky .vid = vid, 7662acf4e6aSArkadi Sharshevsky 767685fb6a4SVivien Didelot }; 768d1cffff0SVivien Didelot 769685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 770d1cffff0SVivien Didelot } 771d1cffff0SVivien Didelot 7723dc80afcSVladimir Oltean int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7733dc80afcSVladimir Oltean u16 vid) 7743dc80afcSVladimir Oltean { 7753dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7763dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7773dc80afcSVladimir Oltean .port = dp->index, 7783dc80afcSVladimir Oltean .addr = addr, 7793dc80afcSVladimir Oltean .vid = vid, 7803dc80afcSVladimir Oltean }; 78126ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 78226ee7b06SVladimir Oltean int err; 78326ee7b06SVladimir Oltean 78426ee7b06SVladimir Oltean err = dev_uc_add(cpu_dp->master, addr); 78526ee7b06SVladimir Oltean if (err) 78626ee7b06SVladimir Oltean return err; 7873dc80afcSVladimir Oltean 7883dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); 7893dc80afcSVladimir Oltean } 7903dc80afcSVladimir Oltean 7913dc80afcSVladimir Oltean int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, 7923dc80afcSVladimir Oltean u16 vid) 7933dc80afcSVladimir Oltean { 7943dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 7953dc80afcSVladimir Oltean .sw_index = dp->ds->index, 7963dc80afcSVladimir Oltean .port = dp->index, 7973dc80afcSVladimir Oltean .addr = addr, 7983dc80afcSVladimir Oltean .vid = vid, 7993dc80afcSVladimir Oltean }; 80026ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 80126ee7b06SVladimir Oltean int err; 80226ee7b06SVladimir Oltean 80326ee7b06SVladimir Oltean err = dev_uc_del(cpu_dp->master, addr); 80426ee7b06SVladimir Oltean if (err) 80526ee7b06SVladimir Oltean return err; 8063dc80afcSVladimir Oltean 8073dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); 8083dc80afcSVladimir Oltean } 8093dc80afcSVladimir Oltean 810de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 811de40fc5dSVivien Didelot { 812de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 813de40fc5dSVivien Didelot int port = dp->index; 814de40fc5dSVivien Didelot 815de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 816de40fc5dSVivien Didelot return -EOPNOTSUPP; 817de40fc5dSVivien Didelot 818de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 819de40fc5dSVivien Didelot } 820de40fc5dSVivien Didelot 821bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 822ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 8233a9afea3SVivien Didelot { 8248ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 8258ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 8268ae5bcdcSVivien Didelot .port = dp->index, 8278ae5bcdcSVivien Didelot .mdb = mdb, 8288ae5bcdcSVivien Didelot }; 8293a9afea3SVivien Didelot 8308ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 8313a9afea3SVivien Didelot } 8323a9afea3SVivien Didelot 833bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 8343a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 8353a9afea3SVivien Didelot { 8368ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 8378ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 8388ae5bcdcSVivien Didelot .port = dp->index, 8398ae5bcdcSVivien Didelot .mdb = mdb, 8408ae5bcdcSVivien Didelot }; 8413a9afea3SVivien Didelot 8428ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 8433a9afea3SVivien Didelot } 8443a9afea3SVivien Didelot 845b8e997c4SVladimir Oltean int dsa_port_host_mdb_add(const struct dsa_port *dp, 846b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 847b8e997c4SVladimir Oltean { 848b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 849b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 850b8e997c4SVladimir Oltean .port = dp->index, 851b8e997c4SVladimir Oltean .mdb = mdb, 852b8e997c4SVladimir Oltean }; 85326ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 85426ee7b06SVladimir Oltean int err; 85526ee7b06SVladimir Oltean 85626ee7b06SVladimir Oltean err = dev_mc_add(cpu_dp->master, mdb->addr); 85726ee7b06SVladimir Oltean if (err) 85826ee7b06SVladimir Oltean return err; 859b8e997c4SVladimir Oltean 860b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); 861b8e997c4SVladimir Oltean } 862b8e997c4SVladimir Oltean 863b8e997c4SVladimir Oltean int dsa_port_host_mdb_del(const struct dsa_port *dp, 864b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 865b8e997c4SVladimir Oltean { 866b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 867b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 868b8e997c4SVladimir Oltean .port = dp->index, 869b8e997c4SVladimir Oltean .mdb = mdb, 870b8e997c4SVladimir Oltean }; 87126ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 87226ee7b06SVladimir Oltean int err; 87326ee7b06SVladimir Oltean 87426ee7b06SVladimir Oltean err = dev_mc_del(cpu_dp->master, mdb->addr); 87526ee7b06SVladimir Oltean if (err) 87626ee7b06SVladimir Oltean return err; 877b8e997c4SVladimir Oltean 878b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); 879b8e997c4SVladimir Oltean } 880b8e997c4SVladimir Oltean 881076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 88231046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 88331046a5fSVladimir Oltean struct netlink_ext_ack *extack) 884076e7133SVivien Didelot { 885d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 886d0c627b8SVivien Didelot .sw_index = dp->ds->index, 887d0c627b8SVivien Didelot .port = dp->index, 888d0c627b8SVivien Didelot .vlan = vlan, 88931046a5fSVladimir Oltean .extack = extack, 890d0c627b8SVivien Didelot }; 891076e7133SVivien Didelot 892d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 893076e7133SVivien Didelot } 894076e7133SVivien Didelot 895076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 896076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 897076e7133SVivien Didelot { 898d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 899d0c627b8SVivien Didelot .sw_index = dp->ds->index, 900d0c627b8SVivien Didelot .port = dp->index, 901d0c627b8SVivien Didelot .vlan = vlan, 902d0c627b8SVivien Didelot }; 903076e7133SVivien Didelot 904d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 905076e7133SVivien Didelot } 90657ab1ca2SVivien Didelot 907134ef238SVladimir Oltean int dsa_port_host_vlan_add(struct dsa_port *dp, 908134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 909134ef238SVladimir Oltean struct netlink_ext_ack *extack) 910134ef238SVladimir Oltean { 911134ef238SVladimir Oltean struct dsa_notifier_vlan_info info = { 912134ef238SVladimir Oltean .sw_index = dp->ds->index, 913134ef238SVladimir Oltean .port = dp->index, 914134ef238SVladimir Oltean .vlan = vlan, 915134ef238SVladimir Oltean .extack = extack, 916134ef238SVladimir Oltean }; 917134ef238SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 918134ef238SVladimir Oltean int err; 919134ef238SVladimir Oltean 920134ef238SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_ADD, &info); 921134ef238SVladimir Oltean if (err && err != -EOPNOTSUPP) 922134ef238SVladimir Oltean return err; 923134ef238SVladimir Oltean 924134ef238SVladimir Oltean vlan_vid_add(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); 925134ef238SVladimir Oltean 926134ef238SVladimir Oltean return err; 927134ef238SVladimir Oltean } 928134ef238SVladimir Oltean 929134ef238SVladimir Oltean int dsa_port_host_vlan_del(struct dsa_port *dp, 930134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 931134ef238SVladimir Oltean { 932134ef238SVladimir Oltean struct dsa_notifier_vlan_info info = { 933134ef238SVladimir Oltean .sw_index = dp->ds->index, 934134ef238SVladimir Oltean .port = dp->index, 935134ef238SVladimir Oltean .vlan = vlan, 936134ef238SVladimir Oltean }; 937134ef238SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 938134ef238SVladimir Oltean int err; 939134ef238SVladimir Oltean 940134ef238SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_DEL, &info); 941134ef238SVladimir Oltean if (err && err != -EOPNOTSUPP) 942134ef238SVladimir Oltean return err; 943134ef238SVladimir Oltean 944134ef238SVladimir Oltean vlan_vid_del(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); 945134ef238SVladimir Oltean 946134ef238SVladimir Oltean return err; 947134ef238SVladimir Oltean } 948134ef238SVladimir Oltean 949c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp, 950c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 951c595c433SHoratiu Vultur { 952cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 953c595c433SHoratiu Vultur 954cad69019SVladimir Oltean if (!ds->ops->port_mrp_add) 955cad69019SVladimir Oltean return -EOPNOTSUPP; 956cad69019SVladimir Oltean 957cad69019SVladimir Oltean return ds->ops->port_mrp_add(ds, dp->index, mrp); 958c595c433SHoratiu Vultur } 959c595c433SHoratiu Vultur 960c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp, 961c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 962c595c433SHoratiu Vultur { 963cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 964c595c433SHoratiu Vultur 965cad69019SVladimir Oltean if (!ds->ops->port_mrp_del) 966cad69019SVladimir Oltean return -EOPNOTSUPP; 967cad69019SVladimir Oltean 968cad69019SVladimir Oltean return ds->ops->port_mrp_del(ds, dp->index, mrp); 969c595c433SHoratiu Vultur } 970c595c433SHoratiu Vultur 971c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, 972c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 973c595c433SHoratiu Vultur { 974cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 975c595c433SHoratiu Vultur 976cad69019SVladimir Oltean if (!ds->ops->port_mrp_add_ring_role) 977cad69019SVladimir Oltean return -EOPNOTSUPP; 978cad69019SVladimir Oltean 979cad69019SVladimir Oltean return ds->ops->port_mrp_add_ring_role(ds, dp->index, mrp); 980c595c433SHoratiu Vultur } 981c595c433SHoratiu Vultur 982c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, 983c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 984c595c433SHoratiu Vultur { 985cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 986c595c433SHoratiu Vultur 987cad69019SVladimir Oltean if (!ds->ops->port_mrp_del_ring_role) 988cad69019SVladimir Oltean return -EOPNOTSUPP; 989cad69019SVladimir Oltean 990cad69019SVladimir Oltean return ds->ops->port_mrp_del_ring_role(ds, dp->index, mrp); 991c595c433SHoratiu Vultur } 992c595c433SHoratiu Vultur 99353da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 99453da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 99553da0ebaSVladimir Oltean { 99653da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 99753da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 99853da0ebaSVladimir Oltean } 99953da0ebaSVladimir Oltean 10006207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 10016207a78cSFlorian Fainelli { 10026207a78cSFlorian Fainelli struct device_node *phy_dn; 10036207a78cSFlorian Fainelli struct phy_device *phydev; 10046207a78cSFlorian Fainelli 10056207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 10066207a78cSFlorian Fainelli if (!phy_dn) 10076207a78cSFlorian Fainelli return NULL; 10086207a78cSFlorian Fainelli 10096207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 10106207a78cSFlorian Fainelli if (!phydev) { 10116207a78cSFlorian Fainelli of_node_put(phy_dn); 10126207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 10136207a78cSFlorian Fainelli } 10146207a78cSFlorian Fainelli 10159919a363SWen Yang of_node_put(phy_dn); 10166207a78cSFlorian Fainelli return phydev; 10176207a78cSFlorian Fainelli } 10186207a78cSFlorian Fainelli 10198ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 102077373d49SIoana Ciornei unsigned long *supported, 102177373d49SIoana Ciornei struct phylink_link_state *state) 102277373d49SIoana Ciornei { 102377373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 102477373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 102577373d49SIoana Ciornei 10265938bce4SRussell King (Oracle) if (!ds->ops->phylink_validate) { 10275938bce4SRussell King (Oracle) if (config->mac_capabilities) 10285938bce4SRussell King (Oracle) phylink_generic_validate(config, supported, state); 102977373d49SIoana Ciornei return; 10305938bce4SRussell King (Oracle) } 103177373d49SIoana Ciornei 103277373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 103377373d49SIoana Ciornei } 103477373d49SIoana Ciornei 10358ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 103677373d49SIoana Ciornei struct phylink_link_state *state) 103777373d49SIoana Ciornei { 103877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 103977373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 104087615c96SRussell King int err; 104177373d49SIoana Ciornei 1042d46b7e4fSRussell King /* Only called for inband modes */ 1043d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 1044d46b7e4fSRussell King state->link = 0; 1045d46b7e4fSRussell King return; 104677373d49SIoana Ciornei } 1047d46b7e4fSRussell King 104887615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 104987615c96SRussell King if (err < 0) { 105087615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 105187615c96SRussell King dp->index, err); 1052d46b7e4fSRussell King state->link = 0; 1053d46b7e4fSRussell King } 105487615c96SRussell King } 105577373d49SIoana Ciornei 1056bde01822SRussell King (Oracle) static struct phylink_pcs * 1057bde01822SRussell King (Oracle) dsa_port_phylink_mac_select_pcs(struct phylink_config *config, 1058bde01822SRussell King (Oracle) phy_interface_t interface) 1059bde01822SRussell King (Oracle) { 1060bde01822SRussell King (Oracle) struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 1061*10544570SRussell King (Oracle) struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); 1062bde01822SRussell King (Oracle) struct dsa_switch *ds = dp->ds; 1063bde01822SRussell King (Oracle) 1064bde01822SRussell King (Oracle) if (ds->ops->phylink_mac_select_pcs) 1065bde01822SRussell King (Oracle) pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); 1066bde01822SRussell King (Oracle) 1067bde01822SRussell King (Oracle) return pcs; 1068bde01822SRussell King (Oracle) } 1069bde01822SRussell King (Oracle) 10708ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 107177373d49SIoana Ciornei unsigned int mode, 107277373d49SIoana Ciornei const struct phylink_link_state *state) 107377373d49SIoana Ciornei { 107477373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 107577373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 107677373d49SIoana Ciornei 107777373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 107877373d49SIoana Ciornei return; 107977373d49SIoana Ciornei 108077373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 108177373d49SIoana Ciornei } 108277373d49SIoana Ciornei 10838ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 108477373d49SIoana Ciornei { 108577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 108677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 108777373d49SIoana Ciornei 108877373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 108977373d49SIoana Ciornei return; 109077373d49SIoana Ciornei 109177373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 109277373d49SIoana Ciornei } 109377373d49SIoana Ciornei 10948ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 109577373d49SIoana Ciornei unsigned int mode, 109677373d49SIoana Ciornei phy_interface_t interface) 109777373d49SIoana Ciornei { 109877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 10990e279218SIoana Ciornei struct phy_device *phydev = NULL; 110077373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 110177373d49SIoana Ciornei 110257d77986SVladimir Oltean if (dsa_port_is_user(dp)) 11030e279218SIoana Ciornei phydev = dp->slave->phydev; 11040e279218SIoana Ciornei 110577373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 11060e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 11070e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 110877373d49SIoana Ciornei return; 110977373d49SIoana Ciornei } 111077373d49SIoana Ciornei 111177373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 111277373d49SIoana Ciornei } 111377373d49SIoana Ciornei 11148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 111591a208f2SRussell King struct phy_device *phydev, 111677373d49SIoana Ciornei unsigned int mode, 111777373d49SIoana Ciornei phy_interface_t interface, 111891a208f2SRussell King int speed, int duplex, 111991a208f2SRussell King bool tx_pause, bool rx_pause) 112077373d49SIoana Ciornei { 112177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 112277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 112377373d49SIoana Ciornei 112477373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 11250e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 11260e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 112777373d49SIoana Ciornei return; 112877373d49SIoana Ciornei } 112977373d49SIoana Ciornei 11305b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 11315b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 113277373d49SIoana Ciornei } 113377373d49SIoana Ciornei 113421bd64bdSRussell King (Oracle) static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 113577373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 1136bde01822SRussell King (Oracle) .mac_select_pcs = dsa_port_phylink_mac_select_pcs, 1137d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 113877373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 113977373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 114077373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 114177373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 114277373d49SIoana Ciornei }; 114377373d49SIoana Ciornei 114421bd64bdSRussell King (Oracle) int dsa_port_phylink_create(struct dsa_port *dp) 114521bd64bdSRussell King (Oracle) { 114621bd64bdSRussell King (Oracle) struct dsa_switch *ds = dp->ds; 114721bd64bdSRussell King (Oracle) phy_interface_t mode; 114821bd64bdSRussell King (Oracle) int err; 114921bd64bdSRussell King (Oracle) 115021bd64bdSRussell King (Oracle) err = of_get_phy_mode(dp->dn, &mode); 115121bd64bdSRussell King (Oracle) if (err) 115221bd64bdSRussell King (Oracle) mode = PHY_INTERFACE_MODE_NA; 115321bd64bdSRussell King (Oracle) 11540a9f0794SRussell King (Oracle) /* Presence of phylink_mac_link_state or phylink_mac_an_restart is 11550a9f0794SRussell King (Oracle) * an indicator of a legacy phylink driver. 11560a9f0794SRussell King (Oracle) */ 11570a9f0794SRussell King (Oracle) if (ds->ops->phylink_mac_link_state || 11580a9f0794SRussell King (Oracle) ds->ops->phylink_mac_an_restart) 11590a9f0794SRussell King (Oracle) dp->pl_config.legacy_pre_march2020 = true; 11600a9f0794SRussell King (Oracle) 1161072eea6cSRussell King (Oracle) if (ds->ops->phylink_get_caps) 1162072eea6cSRussell King (Oracle) ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); 116321bd64bdSRussell King (Oracle) 116421bd64bdSRussell King (Oracle) dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), 116521bd64bdSRussell King (Oracle) mode, &dsa_port_phylink_mac_ops); 116621bd64bdSRussell King (Oracle) if (IS_ERR(dp->pl)) { 116721bd64bdSRussell King (Oracle) pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 116821bd64bdSRussell King (Oracle) return PTR_ERR(dp->pl); 116921bd64bdSRussell King (Oracle) } 117021bd64bdSRussell King (Oracle) 117121bd64bdSRussell King (Oracle) return 0; 117221bd64bdSRussell King (Oracle) } 117321bd64bdSRussell King (Oracle) 117433615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 117533615367SSebastian Reichel { 117633615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 117733615367SSebastian Reichel struct phy_device *phydev; 117833615367SSebastian Reichel int port = dp->index; 117933615367SSebastian Reichel int err = 0; 118033615367SSebastian Reichel 11816207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 11826207a78cSFlorian Fainelli if (!phydev) 118333615367SSebastian Reichel return 0; 118433615367SSebastian Reichel 11856207a78cSFlorian Fainelli if (IS_ERR(phydev)) 11866207a78cSFlorian Fainelli return PTR_ERR(phydev); 118733615367SSebastian Reichel 118833615367SSebastian Reichel if (enable) { 118933615367SSebastian Reichel err = genphy_resume(phydev); 119033615367SSebastian Reichel if (err < 0) 119133615367SSebastian Reichel goto err_put_dev; 119233615367SSebastian Reichel 119333615367SSebastian Reichel err = genphy_read_status(phydev); 119433615367SSebastian Reichel if (err < 0) 119533615367SSebastian Reichel goto err_put_dev; 119633615367SSebastian Reichel } else { 119733615367SSebastian Reichel err = genphy_suspend(phydev); 119833615367SSebastian Reichel if (err < 0) 119933615367SSebastian Reichel goto err_put_dev; 120033615367SSebastian Reichel } 120133615367SSebastian Reichel 120233615367SSebastian Reichel if (ds->ops->adjust_link) 120333615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 120433615367SSebastian Reichel 120533615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 120633615367SSebastian Reichel 120733615367SSebastian Reichel err_put_dev: 120833615367SSebastian Reichel put_device(&phydev->mdio.dev); 120933615367SSebastian Reichel return err; 121033615367SSebastian Reichel } 121133615367SSebastian Reichel 121233615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 121357ab1ca2SVivien Didelot { 121457ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 121557ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 121657ab1ca2SVivien Didelot struct phy_device *phydev; 121757ab1ca2SVivien Didelot int port = dp->index; 12180c65b2b9SAndrew Lunn phy_interface_t mode; 121957ab1ca2SVivien Didelot int err; 122057ab1ca2SVivien Didelot 122157ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 122257ab1ca2SVivien Didelot if (err) { 122357ab1ca2SVivien Didelot dev_err(ds->dev, 122457ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 122557ab1ca2SVivien Didelot port); 122657ab1ca2SVivien Didelot return err; 122757ab1ca2SVivien Didelot } 122857ab1ca2SVivien Didelot 122957ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 123057ab1ca2SVivien Didelot 12310c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 12320c65b2b9SAndrew Lunn if (err) 123357ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 123457ab1ca2SVivien Didelot phydev->interface = mode; 123557ab1ca2SVivien Didelot 123657ab1ca2SVivien Didelot genphy_read_status(phydev); 123757ab1ca2SVivien Didelot 123857ab1ca2SVivien Didelot if (ds->ops->adjust_link) 123957ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 124057ab1ca2SVivien Didelot 124157ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 124257ab1ca2SVivien Didelot 124357ab1ca2SVivien Didelot return 0; 124457ab1ca2SVivien Didelot } 124557ab1ca2SVivien Didelot 12460e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 12470e279218SIoana Ciornei { 12480e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 12490e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 12500c65b2b9SAndrew Lunn int err; 12510e279218SIoana Ciornei 12520e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 12530e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 12540e279218SIoana Ciornei 125521bd64bdSRussell King (Oracle) err = dsa_port_phylink_create(dp); 125621bd64bdSRussell King (Oracle) if (err) 125721bd64bdSRussell King (Oracle) return err; 12580e279218SIoana Ciornei 12590e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 12602131fba5SFlorian Fainelli if (err && err != -ENODEV) { 12610e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 12620e279218SIoana Ciornei goto err_phy_connect; 12630e279218SIoana Ciornei } 12640e279218SIoana Ciornei 12650e279218SIoana Ciornei return 0; 12660e279218SIoana Ciornei 12670e279218SIoana Ciornei err_phy_connect: 12680e279218SIoana Ciornei phylink_destroy(dp->pl); 12690e279218SIoana Ciornei return err; 12700e279218SIoana Ciornei } 12710e279218SIoana Ciornei 127233615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 127357ab1ca2SVivien Didelot { 12740e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 1275a20f9970SAndrew Lunn struct device_node *phy_np; 12763be98b2dSAndrew Lunn int port = dp->index; 12770e279218SIoana Ciornei 1278a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 1279a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 12803be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 12813be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 12823be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 12833be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 12840e279218SIoana Ciornei return dsa_port_phylink_register(dp); 12853be98b2dSAndrew Lunn } 1286a20f9970SAndrew Lunn return 0; 1287a20f9970SAndrew Lunn } 12880e279218SIoana Ciornei 12890e279218SIoana Ciornei dev_warn(ds->dev, 12900e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 12910e279218SIoana Ciornei 129233615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 129333615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 129433615367SSebastian Reichel else 129533615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 129633615367SSebastian Reichel } 129757ab1ca2SVivien Didelot 129833615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 129933615367SSebastian Reichel { 13000e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 13010e279218SIoana Ciornei 1302a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 13030e279218SIoana Ciornei rtnl_lock(); 13040e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 13050e279218SIoana Ciornei rtnl_unlock(); 13060e279218SIoana Ciornei phylink_destroy(dp->pl); 1307a20f9970SAndrew Lunn dp->pl = NULL; 13080e279218SIoana Ciornei return; 13090e279218SIoana Ciornei } 13100e279218SIoana Ciornei 131133615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 131233615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 131333615367SSebastian Reichel else 131433615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 131557ab1ca2SVivien Didelot } 1316cf963573SFlorian Fainelli 131718596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 131818596f50SGeorge McCollister { 1319a68dc7b9SVladimir Oltean struct dsa_switch *ds = dp->ds; 132018596f50SGeorge McCollister int err; 132118596f50SGeorge McCollister 1322a68dc7b9SVladimir Oltean if (!ds->ops->port_hsr_join) 1323a68dc7b9SVladimir Oltean return -EOPNOTSUPP; 1324a68dc7b9SVladimir Oltean 132518596f50SGeorge McCollister dp->hsr_dev = hsr; 132618596f50SGeorge McCollister 1327a68dc7b9SVladimir Oltean err = ds->ops->port_hsr_join(ds, dp->index, hsr); 132818596f50SGeorge McCollister if (err) 132918596f50SGeorge McCollister dp->hsr_dev = NULL; 133018596f50SGeorge McCollister 133118596f50SGeorge McCollister return err; 133218596f50SGeorge McCollister } 133318596f50SGeorge McCollister 133418596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 133518596f50SGeorge McCollister { 1336a68dc7b9SVladimir Oltean struct dsa_switch *ds = dp->ds; 133718596f50SGeorge McCollister int err; 133818596f50SGeorge McCollister 133918596f50SGeorge McCollister dp->hsr_dev = NULL; 134018596f50SGeorge McCollister 1341a68dc7b9SVladimir Oltean if (ds->ops->port_hsr_leave) { 1342a68dc7b9SVladimir Oltean err = ds->ops->port_hsr_leave(ds, dp->index, hsr); 134318596f50SGeorge McCollister if (err) 1344ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1345a68dc7b9SVladimir Oltean "port %d failed to leave HSR %s: %pe\n", 1346a68dc7b9SVladimir Oltean dp->index, hsr->name, ERR_PTR(err)); 1347a68dc7b9SVladimir Oltean } 134818596f50SGeorge McCollister } 1349c64b9c05SVladimir Oltean 1350724395f4SVladimir Oltean int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast) 1351c64b9c05SVladimir Oltean { 1352c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1353c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1354c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1355c64b9c05SVladimir Oltean .port = dp->index, 1356c64b9c05SVladimir Oltean .vid = vid, 1357c64b9c05SVladimir Oltean }; 1358c64b9c05SVladimir Oltean 1359724395f4SVladimir Oltean if (broadcast) 1360c64b9c05SVladimir Oltean return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1361724395f4SVladimir Oltean 1362724395f4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1363c64b9c05SVladimir Oltean } 1364c64b9c05SVladimir Oltean 1365724395f4SVladimir Oltean void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast) 1366c64b9c05SVladimir Oltean { 1367c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1368c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1369c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1370c64b9c05SVladimir Oltean .port = dp->index, 1371c64b9c05SVladimir Oltean .vid = vid, 1372c64b9c05SVladimir Oltean }; 1373c64b9c05SVladimir Oltean int err; 1374c64b9c05SVladimir Oltean 1375724395f4SVladimir Oltean if (broadcast) 1376c64b9c05SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1377724395f4SVladimir Oltean else 1378724395f4SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1379c64b9c05SVladimir Oltean if (err) 1380ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1381ab97462bSVladimir Oltean "port %d failed to notify tag_8021q VLAN %d deletion: %pe\n", 1382ab97462bSVladimir Oltean dp->index, vid, ERR_PTR(err)); 1383c64b9c05SVladimir Oltean } 1384