12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a40c175bSVivien Didelot /* 3a40c175bSVivien Didelot * Handling of a single switch port 4a40c175bSVivien Didelot * 5a40c175bSVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 6a40c175bSVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7a40c175bSVivien Didelot */ 8a40c175bSVivien Didelot 9a40c175bSVivien Didelot #include <linux/if_bridge.h> 10cfbed329SVivien Didelot #include <linux/notifier.h> 1157ab1ca2SVivien Didelot #include <linux/of_mdio.h> 1257ab1ca2SVivien Didelot #include <linux/of_net.h> 13a40c175bSVivien Didelot 14a40c175bSVivien Didelot #include "dsa_priv.h" 15a40c175bSVivien Didelot 16886f8e26SVladimir Oltean /** 17886f8e26SVladimir Oltean * dsa_port_notify - Notify the switching fabric of changes to a port 18886f8e26SVladimir Oltean * @dp: port on which change occurred 19886f8e26SVladimir Oltean * @e: event, must be of type DSA_NOTIFIER_* 20886f8e26SVladimir Oltean * @v: event-specific value. 21886f8e26SVladimir Oltean * 22886f8e26SVladimir Oltean * Notify all switches in the DSA tree that this port's switch belongs to, 23886f8e26SVladimir Oltean * including this switch itself, of an event. Allows the other switches to 24886f8e26SVladimir Oltean * reconfigure themselves for cross-chip operations. Can also be used to 25886f8e26SVladimir Oltean * reconfigure ports without net_devices (CPU ports, DSA links) whenever 26886f8e26SVladimir Oltean * a user port's state changes. 27886f8e26SVladimir Oltean */ 28bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) 29cfbed329SVivien Didelot { 30886f8e26SVladimir Oltean return dsa_tree_notify(dp->ds->dst, e, v); 31cfbed329SVivien Didelot } 32cfbed329SVivien Didelot 33bae33f2bSVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state) 34a40c175bSVivien Didelot { 35a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 36a40c175bSVivien Didelot int port = dp->index; 37a40c175bSVivien Didelot 38bae33f2bSVladimir Oltean if (!ds->ops->port_stp_state_set) 39bae33f2bSVladimir Oltean return -EOPNOTSUPP; 40a40c175bSVivien Didelot 41a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 42a40c175bSVivien Didelot 43a40c175bSVivien Didelot if (ds->ops->port_fast_age) { 44a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 45a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 46a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 47a40c175bSVivien Didelot */ 48a40c175bSVivien Didelot 49a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 50a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 51a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 52a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 53a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 54a40c175bSVivien Didelot ds->ops->port_fast_age(ds, port); 55a40c175bSVivien Didelot } 56a40c175bSVivien Didelot 57a40c175bSVivien Didelot dp->stp_state = state; 58a40c175bSVivien Didelot 59a40c175bSVivien Didelot return 0; 60a40c175bSVivien Didelot } 61a40c175bSVivien Didelot 62fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) 63a40c175bSVivien Didelot { 64a40c175bSVivien Didelot int err; 65a40c175bSVivien Didelot 66bae33f2bSVladimir Oltean err = dsa_port_set_state(dp, state); 67a40c175bSVivien Didelot if (err) 68a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 69a40c175bSVivien Didelot } 70cfbed329SVivien Didelot 718640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 72fb8a6a2bSVivien Didelot { 73fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 74fb8a6a2bSVivien Didelot int port = dp->index; 75fb8a6a2bSVivien Didelot int err; 76fb8a6a2bSVivien Didelot 77fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 78fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 79fb8a6a2bSVivien Didelot if (err) 80fb8a6a2bSVivien Didelot return err; 81fb8a6a2bSVivien Didelot } 82fb8a6a2bSVivien Didelot 839c2054a5SRussell King if (!dp->bridge_dev) 849c2054a5SRussell King dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 85fb8a6a2bSVivien Didelot 868640f8dcSRussell King if (dp->pl) 878640f8dcSRussell King phylink_start(dp->pl); 888640f8dcSRussell King 89fb8a6a2bSVivien Didelot return 0; 90fb8a6a2bSVivien Didelot } 91fb8a6a2bSVivien Didelot 928640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 938640f8dcSRussell King { 948640f8dcSRussell King int err; 958640f8dcSRussell King 968640f8dcSRussell King rtnl_lock(); 978640f8dcSRussell King err = dsa_port_enable_rt(dp, phy); 988640f8dcSRussell King rtnl_unlock(); 998640f8dcSRussell King 1008640f8dcSRussell King return err; 1018640f8dcSRussell King } 1028640f8dcSRussell King 1038640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp) 104fb8a6a2bSVivien Didelot { 105fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 106fb8a6a2bSVivien Didelot int port = dp->index; 107fb8a6a2bSVivien Didelot 1088640f8dcSRussell King if (dp->pl) 1098640f8dcSRussell King phylink_stop(dp->pl); 1108640f8dcSRussell King 1119c2054a5SRussell King if (!dp->bridge_dev) 112fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 113fb8a6a2bSVivien Didelot 114fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 11575104db0SAndrew Lunn ds->ops->port_disable(ds, port); 116fb8a6a2bSVivien Didelot } 117fb8a6a2bSVivien Didelot 1188640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp) 1198640f8dcSRussell King { 1208640f8dcSRussell King rtnl_lock(); 1218640f8dcSRussell King dsa_port_disable_rt(dp); 1228640f8dcSRussell King rtnl_unlock(); 1238640f8dcSRussell King } 1248640f8dcSRussell King 1255e38c158SVladimir Oltean static void dsa_port_change_brport_flags(struct dsa_port *dp, 1265e38c158SVladimir Oltean bool bridge_offload) 1275e38c158SVladimir Oltean { 128e18f4c18SVladimir Oltean struct switchdev_brport_flags flags; 129e18f4c18SVladimir Oltean int flag; 1305e38c158SVladimir Oltean 131e18f4c18SVladimir Oltean flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; 1325e38c158SVladimir Oltean if (bridge_offload) 133e18f4c18SVladimir Oltean flags.val = flags.mask; 1345e38c158SVladimir Oltean else 135e18f4c18SVladimir Oltean flags.val = flags.mask & ~BR_LEARNING; 1365e38c158SVladimir Oltean 137e18f4c18SVladimir Oltean for_each_set_bit(flag, &flags.mask, 32) { 138e18f4c18SVladimir Oltean struct switchdev_brport_flags tmp; 1395e38c158SVladimir Oltean 140e18f4c18SVladimir Oltean tmp.val = flags.val & BIT(flag); 141e18f4c18SVladimir Oltean tmp.mask = BIT(flag); 142e18f4c18SVladimir Oltean 143a8b659e7SVladimir Oltean dsa_port_bridge_flags(dp, tmp, NULL); 1445e38c158SVladimir Oltean } 1455e38c158SVladimir Oltean } 1465e38c158SVladimir Oltean 147cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 148cfbed329SVivien Didelot { 149cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 150f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 151cfbed329SVivien Didelot .sw_index = dp->ds->index, 152cfbed329SVivien Didelot .port = dp->index, 153cfbed329SVivien Didelot .br = br, 154cfbed329SVivien Didelot }; 155cfbed329SVivien Didelot int err; 156cfbed329SVivien Didelot 1575e38c158SVladimir Oltean /* Notify the port driver to set its configurable flags in a way that 1585e38c158SVladimir Oltean * matches the initial settings of a bridge port. 1595e38c158SVladimir Oltean */ 1605e38c158SVladimir Oltean dsa_port_change_brport_flags(dp, true); 161c1388063SRussell King 162c1388063SRussell King /* Here the interface is already bridged. Reflect the current 163c1388063SRussell King * configuration so that drivers can program their chips accordingly. 164cfbed329SVivien Didelot */ 165cfbed329SVivien Didelot dp->bridge_dev = br; 166cfbed329SVivien Didelot 167f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); 168cfbed329SVivien Didelot 169cfbed329SVivien Didelot /* The bridging is rolled back on error */ 170c1388063SRussell King if (err) { 1715e38c158SVladimir Oltean dsa_port_change_brport_flags(dp, false); 172cfbed329SVivien Didelot dp->bridge_dev = NULL; 173c1388063SRussell King } 174cfbed329SVivien Didelot 175cfbed329SVivien Didelot return err; 176cfbed329SVivien Didelot } 177cfbed329SVivien Didelot 178cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 179cfbed329SVivien Didelot { 180cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 181f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 182cfbed329SVivien Didelot .sw_index = dp->ds->index, 183cfbed329SVivien Didelot .port = dp->index, 184cfbed329SVivien Didelot .br = br, 185cfbed329SVivien Didelot }; 186cfbed329SVivien Didelot int err; 187cfbed329SVivien Didelot 188cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 189cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 190cfbed329SVivien Didelot */ 191cfbed329SVivien Didelot dp->bridge_dev = NULL; 192cfbed329SVivien Didelot 193f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 194cfbed329SVivien Didelot if (err) 195cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 196cfbed329SVivien Didelot 1975e38c158SVladimir Oltean /* Configure the port for standalone mode (no address learning, 1985e38c158SVladimir Oltean * flood everything). 1995e38c158SVladimir Oltean * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events 2005e38c158SVladimir Oltean * when the user requests it through netlink or sysfs, but not 2015e38c158SVladimir Oltean * automatically at port join or leave, so we need to handle resetting 2025e38c158SVladimir Oltean * the brport flags ourselves. But we even prefer it that way, because 2035e38c158SVladimir Oltean * otherwise, some setups might never get the notification they need, 2045e38c158SVladimir Oltean * for example, when a port leaves a LAG that offloads the bridge, 2055e38c158SVladimir Oltean * it becomes standalone, but as far as the bridge is concerned, no 2065e38c158SVladimir Oltean * port ever left. 2075e38c158SVladimir Oltean */ 2085e38c158SVladimir Oltean dsa_port_change_brport_flags(dp, false); 209c1388063SRussell King 210cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 211cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 212cfbed329SVivien Didelot */ 213cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 214cfbed329SVivien Didelot } 2154d61d304SVivien Didelot 216058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 217058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 218058102a6STobias Waldekranz { 219058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 220058102a6STobias Waldekranz .sw_index = dp->ds->index, 221058102a6STobias Waldekranz .port = dp->index, 222058102a6STobias Waldekranz }; 223058102a6STobias Waldekranz bool tx_enabled; 224058102a6STobias Waldekranz 225058102a6STobias Waldekranz if (!dp->lag_dev) 226058102a6STobias Waldekranz return 0; 227058102a6STobias Waldekranz 228058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 229058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 230058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 231058102a6STobias Waldekranz * in order to include it in the tx set. 232058102a6STobias Waldekranz */ 233058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 234058102a6STobias Waldekranz 235058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 236058102a6STobias Waldekranz return 0; 237058102a6STobias Waldekranz 238058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 239058102a6STobias Waldekranz 240058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 241058102a6STobias Waldekranz } 242058102a6STobias Waldekranz 243058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, 244058102a6STobias Waldekranz struct netdev_lag_upper_info *uinfo) 245058102a6STobias Waldekranz { 246058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 247058102a6STobias Waldekranz .sw_index = dp->ds->index, 248058102a6STobias Waldekranz .port = dp->index, 249058102a6STobias Waldekranz .lag = lag, 250058102a6STobias Waldekranz .info = uinfo, 251058102a6STobias Waldekranz }; 252*185c9a76SVladimir Oltean struct net_device *bridge_dev; 253058102a6STobias Waldekranz int err; 254058102a6STobias Waldekranz 255058102a6STobias Waldekranz dsa_lag_map(dp->ds->dst, lag); 256058102a6STobias Waldekranz dp->lag_dev = lag; 257058102a6STobias Waldekranz 258058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 259*185c9a76SVladimir Oltean if (err) 260*185c9a76SVladimir Oltean goto err_lag_join; 261*185c9a76SVladimir Oltean 262*185c9a76SVladimir Oltean bridge_dev = netdev_master_upper_dev_get(lag); 263*185c9a76SVladimir Oltean if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 264*185c9a76SVladimir Oltean return 0; 265*185c9a76SVladimir Oltean 266*185c9a76SVladimir Oltean err = dsa_port_bridge_join(dp, bridge_dev); 267*185c9a76SVladimir Oltean if (err) 268*185c9a76SVladimir Oltean goto err_bridge_join; 269*185c9a76SVladimir Oltean 270*185c9a76SVladimir Oltean return 0; 271*185c9a76SVladimir Oltean 272*185c9a76SVladimir Oltean err_bridge_join: 273*185c9a76SVladimir Oltean dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 274*185c9a76SVladimir Oltean err_lag_join: 275058102a6STobias Waldekranz dp->lag_dev = NULL; 276058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 277058102a6STobias Waldekranz return err; 278058102a6STobias Waldekranz } 279058102a6STobias Waldekranz 280058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) 281058102a6STobias Waldekranz { 282058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 283058102a6STobias Waldekranz .sw_index = dp->ds->index, 284058102a6STobias Waldekranz .port = dp->index, 285058102a6STobias Waldekranz .lag = lag, 286058102a6STobias Waldekranz }; 287058102a6STobias Waldekranz int err; 288058102a6STobias Waldekranz 289058102a6STobias Waldekranz if (!dp->lag_dev) 290058102a6STobias Waldekranz return; 291058102a6STobias Waldekranz 292058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 293058102a6STobias Waldekranz * attached to a bridge. 294058102a6STobias Waldekranz */ 295058102a6STobias Waldekranz if (dp->bridge_dev) 296058102a6STobias Waldekranz dsa_port_bridge_leave(dp, dp->bridge_dev); 297058102a6STobias Waldekranz 298058102a6STobias Waldekranz dp->lag_tx_enabled = false; 299058102a6STobias Waldekranz dp->lag_dev = NULL; 300058102a6STobias Waldekranz 301058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 302058102a6STobias Waldekranz if (err) 303058102a6STobias Waldekranz pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", 304058102a6STobias Waldekranz err); 305058102a6STobias Waldekranz 306058102a6STobias Waldekranz dsa_lag_unmap(dp->ds->dst, lag); 307058102a6STobias Waldekranz } 308058102a6STobias Waldekranz 309adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 3108f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 31189153ed6SVladimir Oltean bool vlan_filtering, 31289153ed6SVladimir Oltean struct netlink_ext_ack *extack) 3138f5d16f6SVladimir Oltean { 3148f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 315adb256ebSVladimir Oltean int err, i; 316adb256ebSVladimir Oltean 317adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 318adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 319adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 320adb256ebSVladimir Oltean * as long as we have 8021q uppers. 321adb256ebSVladimir Oltean */ 322adb256ebSVladimir Oltean if (vlan_filtering && dsa_is_user_port(ds, dp->index)) { 323adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 324adb256ebSVladimir Oltean struct net_device *br = dp->bridge_dev; 325adb256ebSVladimir Oltean struct list_head *iter; 326adb256ebSVladimir Oltean 327adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 328adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 329adb256ebSVladimir Oltean u16 vid; 330adb256ebSVladimir Oltean 331adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 332adb256ebSVladimir Oltean continue; 333adb256ebSVladimir Oltean 334adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 335adb256ebSVladimir Oltean 336adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 337adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 338adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 339adb256ebSVladimir Oltean */ 340adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 341adb256ebSVladimir Oltean if (err == 0) { 34289153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 34389153ed6SVladimir Oltean "Must first remove VLAN uppers having VIDs also present in bridge"); 344adb256ebSVladimir Oltean return false; 345adb256ebSVladimir Oltean } 346adb256ebSVladimir Oltean } 347adb256ebSVladimir Oltean } 3488f5d16f6SVladimir Oltean 3498f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 3508f5d16f6SVladimir Oltean return true; 3518f5d16f6SVladimir Oltean 3528f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 3538f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 3548f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 3558f5d16f6SVladimir Oltean * different setting than what is being requested. 3568f5d16f6SVladimir Oltean */ 3578f5d16f6SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 3588f5d16f6SVladimir Oltean struct net_device *other_bridge; 3598f5d16f6SVladimir Oltean 3608f5d16f6SVladimir Oltean other_bridge = dsa_to_port(ds, i)->bridge_dev; 3618f5d16f6SVladimir Oltean if (!other_bridge) 3628f5d16f6SVladimir Oltean continue; 3638f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 3648f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 3658f5d16f6SVladimir Oltean */ 3668f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 3678f5d16f6SVladimir Oltean continue; 3688f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 36989153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 37089153ed6SVladimir Oltean "VLAN filtering is a global setting"); 3718f5d16f6SVladimir Oltean return false; 3728f5d16f6SVladimir Oltean } 3738f5d16f6SVladimir Oltean } 3748f5d16f6SVladimir Oltean return true; 3758f5d16f6SVladimir Oltean } 3768f5d16f6SVladimir Oltean 37789153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 37889153ed6SVladimir Oltean struct netlink_ext_ack *extack) 3794d61d304SVivien Didelot { 3804d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 381adb256ebSVladimir Oltean bool apply; 382bae33f2bSVladimir Oltean int err; 383adb256ebSVladimir Oltean 3848f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 385707ec383SVladimir Oltean return -EOPNOTSUPP; 3868f5d16f6SVladimir Oltean 387adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 388adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 389adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 390adb256ebSVladimir Oltean */ 391adb256ebSVladimir Oltean rcu_read_lock(); 39289153ed6SVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack); 393adb256ebSVladimir Oltean rcu_read_unlock(); 394adb256ebSVladimir Oltean if (!apply) 3958f5d16f6SVladimir Oltean return -EINVAL; 396707ec383SVladimir Oltean 397ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 398ec9121e7SVladimir Oltean return 0; 399ec9121e7SVladimir Oltean 40089153ed6SVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering, 40189153ed6SVladimir Oltean extack); 40233162e9aSVladimir Oltean if (err) 40333162e9aSVladimir Oltean return err; 4048f5d16f6SVladimir Oltean 40514574676SVladimir Oltean if (ds->vlan_filtering_is_global) 40614574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 40714574676SVladimir Oltean else 40833162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 4092e554a7aSVladimir Oltean 4104d61d304SVivien Didelot return 0; 4114d61d304SVivien Didelot } 412d87bd94eSVivien Didelot 41354a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 41454a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 41554a0ed0dSRussell King */ 41654a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 41754a0ed0dSRussell King { 41854a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 41954a0ed0dSRussell King 42054a0ed0dSRussell King if (!dp->bridge_dev) 42154a0ed0dSRussell King return false; 42254a0ed0dSRussell King 42354a0ed0dSRussell King return (!ds->configure_vlan_while_not_filtering && 42454a0ed0dSRussell King !br_vlan_enabled(dp->bridge_dev)); 42554a0ed0dSRussell King } 42654a0ed0dSRussell King 427bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 428d87bd94eSVivien Didelot { 429d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 430d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 431bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 432bae33f2bSVladimir Oltean int err; 433d87bd94eSVivien Didelot 434bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 435bae33f2bSVladimir Oltean 436bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 437bae33f2bSVladimir Oltean if (err) 438bae33f2bSVladimir Oltean return err; 439d87bd94eSVivien Didelot 440d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 441d87bd94eSVivien Didelot 44277b61365SVladimir Oltean return 0; 443d87bd94eSVivien Didelot } 444d1cffff0SVivien Didelot 445e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, 446a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 447a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 448ea87005aSFlorian Fainelli { 449ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 450ea87005aSFlorian Fainelli 451a8b659e7SVladimir Oltean if (!ds->ops->port_pre_bridge_flags) 452ea87005aSFlorian Fainelli return -EINVAL; 453ea87005aSFlorian Fainelli 454a8b659e7SVladimir Oltean return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); 455ea87005aSFlorian Fainelli } 456ea87005aSFlorian Fainelli 457e18f4c18SVladimir Oltean int dsa_port_bridge_flags(const struct dsa_port *dp, 458a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 459a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 46057652796SRussell King { 46157652796SRussell King struct dsa_switch *ds = dp->ds; 46257652796SRussell King 463a8b659e7SVladimir Oltean if (!ds->ops->port_bridge_flags) 464a8b659e7SVladimir Oltean return -EINVAL; 46557652796SRussell King 466a8b659e7SVladimir Oltean return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); 46757652796SRussell King } 46857652796SRussell King 469a8b659e7SVladimir Oltean int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, 470a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 47108cc83ccSVivien Didelot { 47208cc83ccSVivien Didelot struct dsa_switch *ds = dp->ds; 47308cc83ccSVivien Didelot 474a8b659e7SVladimir Oltean if (!ds->ops->port_set_mrouter) 475bae33f2bSVladimir Oltean return -EOPNOTSUPP; 47608cc83ccSVivien Didelot 477a8b659e7SVladimir Oltean return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack); 47808cc83ccSVivien Didelot } 47908cc83ccSVivien Didelot 480bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 481bfcb8132SVladimir Oltean bool propagate_upstream) 482bfcb8132SVladimir Oltean { 483bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 484bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 485bfcb8132SVladimir Oltean .propagate_upstream = propagate_upstream, 486bfcb8132SVladimir Oltean .port = dp->index, 487bfcb8132SVladimir Oltean .mtu = new_mtu, 488bfcb8132SVladimir Oltean }; 489bfcb8132SVladimir Oltean 490bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 491bfcb8132SVladimir Oltean } 492bfcb8132SVladimir Oltean 4932acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 4942acf4e6aSArkadi Sharshevsky u16 vid) 495d1cffff0SVivien Didelot { 496685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 497685fb6a4SVivien Didelot .sw_index = dp->ds->index, 498685fb6a4SVivien Didelot .port = dp->index, 4992acf4e6aSArkadi Sharshevsky .addr = addr, 5002acf4e6aSArkadi Sharshevsky .vid = vid, 501685fb6a4SVivien Didelot }; 502d1cffff0SVivien Didelot 503685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 504d1cffff0SVivien Didelot } 505d1cffff0SVivien Didelot 5062acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 5072acf4e6aSArkadi Sharshevsky u16 vid) 508d1cffff0SVivien Didelot { 509685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 510685fb6a4SVivien Didelot .sw_index = dp->ds->index, 511685fb6a4SVivien Didelot .port = dp->index, 5122acf4e6aSArkadi Sharshevsky .addr = addr, 5132acf4e6aSArkadi Sharshevsky .vid = vid, 5142acf4e6aSArkadi Sharshevsky 515685fb6a4SVivien Didelot }; 516d1cffff0SVivien Didelot 517685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 518d1cffff0SVivien Didelot } 519d1cffff0SVivien Didelot 520de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 521de40fc5dSVivien Didelot { 522de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 523de40fc5dSVivien Didelot int port = dp->index; 524de40fc5dSVivien Didelot 525de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 526de40fc5dSVivien Didelot return -EOPNOTSUPP; 527de40fc5dSVivien Didelot 528de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 529de40fc5dSVivien Didelot } 530de40fc5dSVivien Didelot 531bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 532ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 5333a9afea3SVivien Didelot { 5348ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 5358ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 5368ae5bcdcSVivien Didelot .port = dp->index, 5378ae5bcdcSVivien Didelot .mdb = mdb, 5388ae5bcdcSVivien Didelot }; 5393a9afea3SVivien Didelot 5408ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 5413a9afea3SVivien Didelot } 5423a9afea3SVivien Didelot 543bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 5443a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 5453a9afea3SVivien Didelot { 5468ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 5478ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 5488ae5bcdcSVivien Didelot .port = dp->index, 5498ae5bcdcSVivien Didelot .mdb = mdb, 5508ae5bcdcSVivien Didelot }; 5513a9afea3SVivien Didelot 5528ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 5533a9afea3SVivien Didelot } 5543a9afea3SVivien Didelot 555076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 55631046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 55731046a5fSVladimir Oltean struct netlink_ext_ack *extack) 558076e7133SVivien Didelot { 559d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 560d0c627b8SVivien Didelot .sw_index = dp->ds->index, 561d0c627b8SVivien Didelot .port = dp->index, 562d0c627b8SVivien Didelot .vlan = vlan, 56331046a5fSVladimir Oltean .extack = extack, 564d0c627b8SVivien Didelot }; 565076e7133SVivien Didelot 566d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 567076e7133SVivien Didelot } 568076e7133SVivien Didelot 569076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 570076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 571076e7133SVivien Didelot { 572d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 573d0c627b8SVivien Didelot .sw_index = dp->ds->index, 574d0c627b8SVivien Didelot .port = dp->index, 575d0c627b8SVivien Didelot .vlan = vlan, 576d0c627b8SVivien Didelot }; 577076e7133SVivien Didelot 578d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 579076e7133SVivien Didelot } 58057ab1ca2SVivien Didelot 581c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp, 582c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 583c595c433SHoratiu Vultur { 584c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 585c595c433SHoratiu Vultur .sw_index = dp->ds->index, 586c595c433SHoratiu Vultur .port = dp->index, 587c595c433SHoratiu Vultur .mrp = mrp, 588c595c433SHoratiu Vultur }; 589c595c433SHoratiu Vultur 590c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD, &info); 591c595c433SHoratiu Vultur } 592c595c433SHoratiu Vultur 593c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp, 594c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 595c595c433SHoratiu Vultur { 596c595c433SHoratiu Vultur struct dsa_notifier_mrp_info info = { 597c595c433SHoratiu Vultur .sw_index = dp->ds->index, 598c595c433SHoratiu Vultur .port = dp->index, 599c595c433SHoratiu Vultur .mrp = mrp, 600c595c433SHoratiu Vultur }; 601c595c433SHoratiu Vultur 602c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL, &info); 603c595c433SHoratiu Vultur } 604c595c433SHoratiu Vultur 605c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, 606c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 607c595c433SHoratiu Vultur { 608c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 609c595c433SHoratiu Vultur .sw_index = dp->ds->index, 610c595c433SHoratiu Vultur .port = dp->index, 611c595c433SHoratiu Vultur .mrp = mrp, 612c595c433SHoratiu Vultur }; 613c595c433SHoratiu Vultur 614c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD_RING_ROLE, &info); 615c595c433SHoratiu Vultur } 616c595c433SHoratiu Vultur 617c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, 618c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 619c595c433SHoratiu Vultur { 620c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info info = { 621c595c433SHoratiu Vultur .sw_index = dp->ds->index, 622c595c433SHoratiu Vultur .port = dp->index, 623c595c433SHoratiu Vultur .mrp = mrp, 624c595c433SHoratiu Vultur }; 625c595c433SHoratiu Vultur 626c595c433SHoratiu Vultur return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL_RING_ROLE, &info); 627c595c433SHoratiu Vultur } 628c595c433SHoratiu Vultur 62953da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 63053da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 63153da0ebaSVladimir Oltean { 63253da0ebaSVladimir Oltean cpu_dp->filter = tag_ops->filter; 63353da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 63453da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 63553da0ebaSVladimir Oltean } 63653da0ebaSVladimir Oltean 6376207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 6386207a78cSFlorian Fainelli { 6396207a78cSFlorian Fainelli struct device_node *phy_dn; 6406207a78cSFlorian Fainelli struct phy_device *phydev; 6416207a78cSFlorian Fainelli 6426207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 6436207a78cSFlorian Fainelli if (!phy_dn) 6446207a78cSFlorian Fainelli return NULL; 6456207a78cSFlorian Fainelli 6466207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 6476207a78cSFlorian Fainelli if (!phydev) { 6486207a78cSFlorian Fainelli of_node_put(phy_dn); 6496207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 6506207a78cSFlorian Fainelli } 6516207a78cSFlorian Fainelli 6529919a363SWen Yang of_node_put(phy_dn); 6536207a78cSFlorian Fainelli return phydev; 6546207a78cSFlorian Fainelli } 6556207a78cSFlorian Fainelli 6568ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 65777373d49SIoana Ciornei unsigned long *supported, 65877373d49SIoana Ciornei struct phylink_link_state *state) 65977373d49SIoana Ciornei { 66077373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 66177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 66277373d49SIoana Ciornei 66377373d49SIoana Ciornei if (!ds->ops->phylink_validate) 66477373d49SIoana Ciornei return; 66577373d49SIoana Ciornei 66677373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 66777373d49SIoana Ciornei } 66877373d49SIoana Ciornei 6698ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 67077373d49SIoana Ciornei struct phylink_link_state *state) 67177373d49SIoana Ciornei { 67277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 67377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 67487615c96SRussell King int err; 67577373d49SIoana Ciornei 676d46b7e4fSRussell King /* Only called for inband modes */ 677d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 678d46b7e4fSRussell King state->link = 0; 679d46b7e4fSRussell King return; 68077373d49SIoana Ciornei } 681d46b7e4fSRussell King 68287615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 68387615c96SRussell King if (err < 0) { 68487615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 68587615c96SRussell King dp->index, err); 686d46b7e4fSRussell King state->link = 0; 687d46b7e4fSRussell King } 68887615c96SRussell King } 68977373d49SIoana Ciornei 6908ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 69177373d49SIoana Ciornei unsigned int mode, 69277373d49SIoana Ciornei const struct phylink_link_state *state) 69377373d49SIoana Ciornei { 69477373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 69577373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 69677373d49SIoana Ciornei 69777373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 69877373d49SIoana Ciornei return; 69977373d49SIoana Ciornei 70077373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 70177373d49SIoana Ciornei } 70277373d49SIoana Ciornei 7038ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 70477373d49SIoana Ciornei { 70577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 70677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 70777373d49SIoana Ciornei 70877373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 70977373d49SIoana Ciornei return; 71077373d49SIoana Ciornei 71177373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 71277373d49SIoana Ciornei } 71377373d49SIoana Ciornei 7148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 71577373d49SIoana Ciornei unsigned int mode, 71677373d49SIoana Ciornei phy_interface_t interface) 71777373d49SIoana Ciornei { 71877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 7190e279218SIoana Ciornei struct phy_device *phydev = NULL; 72077373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 72177373d49SIoana Ciornei 7220e279218SIoana Ciornei if (dsa_is_user_port(ds, dp->index)) 7230e279218SIoana Ciornei phydev = dp->slave->phydev; 7240e279218SIoana Ciornei 72577373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 7260e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 7270e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 72877373d49SIoana Ciornei return; 72977373d49SIoana Ciornei } 73077373d49SIoana Ciornei 73177373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 73277373d49SIoana Ciornei } 73377373d49SIoana Ciornei 7348ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 73591a208f2SRussell King struct phy_device *phydev, 73677373d49SIoana Ciornei unsigned int mode, 73777373d49SIoana Ciornei phy_interface_t interface, 73891a208f2SRussell King int speed, int duplex, 73991a208f2SRussell King bool tx_pause, bool rx_pause) 74077373d49SIoana Ciornei { 74177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 74277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 74377373d49SIoana Ciornei 74477373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 7450e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 7460e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 74777373d49SIoana Ciornei return; 74877373d49SIoana Ciornei } 74977373d49SIoana Ciornei 7505b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 7515b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 75277373d49SIoana Ciornei } 75377373d49SIoana Ciornei 75477373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 75577373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 756d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 75777373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 75877373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 75977373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 76077373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 76177373d49SIoana Ciornei }; 76277373d49SIoana Ciornei 76333615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 76433615367SSebastian Reichel { 76533615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 76633615367SSebastian Reichel struct phy_device *phydev; 76733615367SSebastian Reichel int port = dp->index; 76833615367SSebastian Reichel int err = 0; 76933615367SSebastian Reichel 7706207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 7716207a78cSFlorian Fainelli if (!phydev) 77233615367SSebastian Reichel return 0; 77333615367SSebastian Reichel 7746207a78cSFlorian Fainelli if (IS_ERR(phydev)) 7756207a78cSFlorian Fainelli return PTR_ERR(phydev); 77633615367SSebastian Reichel 77733615367SSebastian Reichel if (enable) { 77833615367SSebastian Reichel err = genphy_resume(phydev); 77933615367SSebastian Reichel if (err < 0) 78033615367SSebastian Reichel goto err_put_dev; 78133615367SSebastian Reichel 78233615367SSebastian Reichel err = genphy_read_status(phydev); 78333615367SSebastian Reichel if (err < 0) 78433615367SSebastian Reichel goto err_put_dev; 78533615367SSebastian Reichel } else { 78633615367SSebastian Reichel err = genphy_suspend(phydev); 78733615367SSebastian Reichel if (err < 0) 78833615367SSebastian Reichel goto err_put_dev; 78933615367SSebastian Reichel } 79033615367SSebastian Reichel 79133615367SSebastian Reichel if (ds->ops->adjust_link) 79233615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 79333615367SSebastian Reichel 79433615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 79533615367SSebastian Reichel 79633615367SSebastian Reichel err_put_dev: 79733615367SSebastian Reichel put_device(&phydev->mdio.dev); 79833615367SSebastian Reichel return err; 79933615367SSebastian Reichel } 80033615367SSebastian Reichel 80133615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 80257ab1ca2SVivien Didelot { 80357ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 80457ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 80557ab1ca2SVivien Didelot struct phy_device *phydev; 80657ab1ca2SVivien Didelot int port = dp->index; 8070c65b2b9SAndrew Lunn phy_interface_t mode; 80857ab1ca2SVivien Didelot int err; 80957ab1ca2SVivien Didelot 81057ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 81157ab1ca2SVivien Didelot if (err) { 81257ab1ca2SVivien Didelot dev_err(ds->dev, 81357ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 81457ab1ca2SVivien Didelot port); 81557ab1ca2SVivien Didelot return err; 81657ab1ca2SVivien Didelot } 81757ab1ca2SVivien Didelot 81857ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 81957ab1ca2SVivien Didelot 8200c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 8210c65b2b9SAndrew Lunn if (err) 82257ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 82357ab1ca2SVivien Didelot phydev->interface = mode; 82457ab1ca2SVivien Didelot 82557ab1ca2SVivien Didelot genphy_read_status(phydev); 82657ab1ca2SVivien Didelot 82757ab1ca2SVivien Didelot if (ds->ops->adjust_link) 82857ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 82957ab1ca2SVivien Didelot 83057ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 83157ab1ca2SVivien Didelot 83257ab1ca2SVivien Didelot return 0; 83357ab1ca2SVivien Didelot } 83457ab1ca2SVivien Didelot 8350e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 8360e279218SIoana Ciornei { 8370e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 8380e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 8390c65b2b9SAndrew Lunn phy_interface_t mode; 8400c65b2b9SAndrew Lunn int err; 8410e279218SIoana Ciornei 8420c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 8430c65b2b9SAndrew Lunn if (err) 8440e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 8450e279218SIoana Ciornei 8460e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 8470e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 848787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 8490e279218SIoana Ciornei 8500e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 8510e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 8520e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 8530e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 8540e279218SIoana Ciornei return PTR_ERR(dp->pl); 8550e279218SIoana Ciornei } 8560e279218SIoana Ciornei 8570e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 8582131fba5SFlorian Fainelli if (err && err != -ENODEV) { 8590e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 8600e279218SIoana Ciornei goto err_phy_connect; 8610e279218SIoana Ciornei } 8620e279218SIoana Ciornei 8630e279218SIoana Ciornei return 0; 8640e279218SIoana Ciornei 8650e279218SIoana Ciornei err_phy_connect: 8660e279218SIoana Ciornei phylink_destroy(dp->pl); 8670e279218SIoana Ciornei return err; 8680e279218SIoana Ciornei } 8690e279218SIoana Ciornei 87033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 87157ab1ca2SVivien Didelot { 8720e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 873a20f9970SAndrew Lunn struct device_node *phy_np; 8743be98b2dSAndrew Lunn int port = dp->index; 8750e279218SIoana Ciornei 876a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 877a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 8783be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 8793be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 8803be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 8813be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 8820e279218SIoana Ciornei return dsa_port_phylink_register(dp); 8833be98b2dSAndrew Lunn } 884a20f9970SAndrew Lunn return 0; 885a20f9970SAndrew Lunn } 8860e279218SIoana Ciornei 8870e279218SIoana Ciornei dev_warn(ds->dev, 8880e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 8890e279218SIoana Ciornei 89033615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 89133615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 89233615367SSebastian Reichel else 89333615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 89433615367SSebastian Reichel } 89557ab1ca2SVivien Didelot 89633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 89733615367SSebastian Reichel { 8980e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 8990e279218SIoana Ciornei 900a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 9010e279218SIoana Ciornei rtnl_lock(); 9020e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 9030e279218SIoana Ciornei rtnl_unlock(); 9040e279218SIoana Ciornei phylink_destroy(dp->pl); 905a20f9970SAndrew Lunn dp->pl = NULL; 9060e279218SIoana Ciornei return; 9070e279218SIoana Ciornei } 9080e279218SIoana Ciornei 90933615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 91033615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 91133615367SSebastian Reichel else 91233615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 91357ab1ca2SVivien Didelot } 914cf963573SFlorian Fainelli 915cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 916cf963573SFlorian Fainelli { 917cf963573SFlorian Fainelli struct phy_device *phydev; 918cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 919cf963573SFlorian Fainelli 920cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 921cf963573SFlorian Fainelli return ret; 922cf963573SFlorian Fainelli 923cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 924cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 925cf963573SFlorian Fainelli return ret; 926cf963573SFlorian Fainelli 927cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 928cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 929cf963573SFlorian Fainelli 930cf963573SFlorian Fainelli return ret; 931cf963573SFlorian Fainelli } 932cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 933cf963573SFlorian Fainelli 934cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 935cf963573SFlorian Fainelli { 936cf963573SFlorian Fainelli struct phy_device *phydev; 937cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 938cf963573SFlorian Fainelli 939cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 940cf963573SFlorian Fainelli return ret; 941cf963573SFlorian Fainelli 942cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 943cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 944cf963573SFlorian Fainelli return ret; 945cf963573SFlorian Fainelli 946cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 947cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 948cf963573SFlorian Fainelli 949cf963573SFlorian Fainelli return ret; 950cf963573SFlorian Fainelli } 951cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 952cf963573SFlorian Fainelli 953cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 954cf963573SFlorian Fainelli { 955cf963573SFlorian Fainelli struct phy_device *phydev; 956cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 957cf963573SFlorian Fainelli 958cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 959cf963573SFlorian Fainelli return ret; 960cf963573SFlorian Fainelli 961cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 962cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 963cf963573SFlorian Fainelli return ret; 964cf963573SFlorian Fainelli 965cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 966cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 967cf963573SFlorian Fainelli 968cf963573SFlorian Fainelli return ret; 969cf963573SFlorian Fainelli } 970cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 97118596f50SGeorge McCollister 97218596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 97318596f50SGeorge McCollister { 97418596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 97518596f50SGeorge McCollister .sw_index = dp->ds->index, 97618596f50SGeorge McCollister .port = dp->index, 97718596f50SGeorge McCollister .hsr = hsr, 97818596f50SGeorge McCollister }; 97918596f50SGeorge McCollister int err; 98018596f50SGeorge McCollister 98118596f50SGeorge McCollister dp->hsr_dev = hsr; 98218596f50SGeorge McCollister 98318596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info); 98418596f50SGeorge McCollister if (err) 98518596f50SGeorge McCollister dp->hsr_dev = NULL; 98618596f50SGeorge McCollister 98718596f50SGeorge McCollister return err; 98818596f50SGeorge McCollister } 98918596f50SGeorge McCollister 99018596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 99118596f50SGeorge McCollister { 99218596f50SGeorge McCollister struct dsa_notifier_hsr_info info = { 99318596f50SGeorge McCollister .sw_index = dp->ds->index, 99418596f50SGeorge McCollister .port = dp->index, 99518596f50SGeorge McCollister .hsr = hsr, 99618596f50SGeorge McCollister }; 99718596f50SGeorge McCollister int err; 99818596f50SGeorge McCollister 99918596f50SGeorge McCollister dp->hsr_dev = NULL; 100018596f50SGeorge McCollister 100118596f50SGeorge McCollister err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info); 100218596f50SGeorge McCollister if (err) 100318596f50SGeorge McCollister pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n"); 100418596f50SGeorge McCollister } 1005