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 16bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) 17cfbed329SVivien Didelot { 18cfbed329SVivien Didelot struct raw_notifier_head *nh = &dp->ds->dst->nh; 19cfbed329SVivien Didelot int err; 20cfbed329SVivien Didelot 21cfbed329SVivien Didelot err = raw_notifier_call_chain(nh, e, v); 22cfbed329SVivien Didelot 23cfbed329SVivien Didelot return notifier_to_errno(err); 24cfbed329SVivien Didelot } 25cfbed329SVivien Didelot 26a40c175bSVivien Didelot int dsa_port_set_state(struct dsa_port *dp, u8 state, 27a40c175bSVivien Didelot struct switchdev_trans *trans) 28a40c175bSVivien Didelot { 29a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 30a40c175bSVivien Didelot int port = dp->index; 31a40c175bSVivien Didelot 32a40c175bSVivien Didelot if (switchdev_trans_ph_prepare(trans)) 33a40c175bSVivien Didelot return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP; 34a40c175bSVivien Didelot 35a40c175bSVivien Didelot if (ds->ops->port_stp_state_set) 36a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 37a40c175bSVivien Didelot 38a40c175bSVivien Didelot if (ds->ops->port_fast_age) { 39a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 40a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 41a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 42a40c175bSVivien Didelot */ 43a40c175bSVivien Didelot 44a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 45a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 46a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 47a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 48a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 49a40c175bSVivien Didelot ds->ops->port_fast_age(ds, port); 50a40c175bSVivien Didelot } 51a40c175bSVivien Didelot 52a40c175bSVivien Didelot dp->stp_state = state; 53a40c175bSVivien Didelot 54a40c175bSVivien Didelot return 0; 55a40c175bSVivien Didelot } 56a40c175bSVivien Didelot 57fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) 58a40c175bSVivien Didelot { 59a40c175bSVivien Didelot int err; 60a40c175bSVivien Didelot 61a40c175bSVivien Didelot err = dsa_port_set_state(dp, state, NULL); 62a40c175bSVivien Didelot if (err) 63a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 64a40c175bSVivien Didelot } 65cfbed329SVivien Didelot 668640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 67fb8a6a2bSVivien Didelot { 68fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 69fb8a6a2bSVivien Didelot int port = dp->index; 70fb8a6a2bSVivien Didelot int err; 71fb8a6a2bSVivien Didelot 72fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 73fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 74fb8a6a2bSVivien Didelot if (err) 75fb8a6a2bSVivien Didelot return err; 76fb8a6a2bSVivien Didelot } 77fb8a6a2bSVivien Didelot 789c2054a5SRussell King if (!dp->bridge_dev) 799c2054a5SRussell King dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 80fb8a6a2bSVivien Didelot 818640f8dcSRussell King if (dp->pl) 828640f8dcSRussell King phylink_start(dp->pl); 838640f8dcSRussell King 84fb8a6a2bSVivien Didelot return 0; 85fb8a6a2bSVivien Didelot } 86fb8a6a2bSVivien Didelot 878640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 888640f8dcSRussell King { 898640f8dcSRussell King int err; 908640f8dcSRussell King 918640f8dcSRussell King rtnl_lock(); 928640f8dcSRussell King err = dsa_port_enable_rt(dp, phy); 938640f8dcSRussell King rtnl_unlock(); 948640f8dcSRussell King 958640f8dcSRussell King return err; 968640f8dcSRussell King } 978640f8dcSRussell King 988640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp) 99fb8a6a2bSVivien Didelot { 100fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 101fb8a6a2bSVivien Didelot int port = dp->index; 102fb8a6a2bSVivien Didelot 1038640f8dcSRussell King if (dp->pl) 1048640f8dcSRussell King phylink_stop(dp->pl); 1058640f8dcSRussell King 1069c2054a5SRussell King if (!dp->bridge_dev) 107fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 108fb8a6a2bSVivien Didelot 109fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 11075104db0SAndrew Lunn ds->ops->port_disable(ds, port); 111fb8a6a2bSVivien Didelot } 112fb8a6a2bSVivien Didelot 1138640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp) 1148640f8dcSRussell King { 1158640f8dcSRussell King rtnl_lock(); 1168640f8dcSRussell King dsa_port_disable_rt(dp); 1178640f8dcSRussell King rtnl_unlock(); 1188640f8dcSRussell King } 1198640f8dcSRussell King 120cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 121cfbed329SVivien Didelot { 122cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 123cfbed329SVivien Didelot .sw_index = dp->ds->index, 124cfbed329SVivien Didelot .port = dp->index, 125cfbed329SVivien Didelot .br = br, 126cfbed329SVivien Didelot }; 127cfbed329SVivien Didelot int err; 128cfbed329SVivien Didelot 129c1388063SRussell King /* Set the flooding mode before joining the port in the switch */ 130c1388063SRussell King err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); 131c1388063SRussell King if (err) 132c1388063SRussell King return err; 133c1388063SRussell King 134c1388063SRussell King /* Here the interface is already bridged. Reflect the current 135c1388063SRussell King * configuration so that drivers can program their chips accordingly. 136cfbed329SVivien Didelot */ 137cfbed329SVivien Didelot dp->bridge_dev = br; 138cfbed329SVivien Didelot 139cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); 140cfbed329SVivien Didelot 141cfbed329SVivien Didelot /* The bridging is rolled back on error */ 142c1388063SRussell King if (err) { 143c1388063SRussell King dsa_port_bridge_flags(dp, 0, NULL); 144cfbed329SVivien Didelot dp->bridge_dev = NULL; 145c1388063SRussell King } 146cfbed329SVivien Didelot 147cfbed329SVivien Didelot return err; 148cfbed329SVivien Didelot } 149cfbed329SVivien Didelot 150cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 151cfbed329SVivien Didelot { 152cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 153cfbed329SVivien Didelot .sw_index = dp->ds->index, 154cfbed329SVivien Didelot .port = dp->index, 155cfbed329SVivien Didelot .br = br, 156cfbed329SVivien Didelot }; 157cfbed329SVivien Didelot int err; 158cfbed329SVivien Didelot 159cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 160cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 161cfbed329SVivien Didelot */ 162cfbed329SVivien Didelot dp->bridge_dev = NULL; 163cfbed329SVivien Didelot 164cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); 165cfbed329SVivien Didelot if (err) 166cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 167cfbed329SVivien Didelot 168c1388063SRussell King /* Port is leaving the bridge, disable flooding */ 169c1388063SRussell King dsa_port_bridge_flags(dp, 0, NULL); 170c1388063SRussell King 171cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 172cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 173cfbed329SVivien Didelot */ 174cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 175cfbed329SVivien Didelot } 1764d61d304SVivien Didelot 1778f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 1788f5d16f6SVladimir Oltean bool vlan_filtering) 1798f5d16f6SVladimir Oltean { 1808f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 1818f5d16f6SVladimir Oltean int i; 1828f5d16f6SVladimir Oltean 1838f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 1848f5d16f6SVladimir Oltean return true; 1858f5d16f6SVladimir Oltean 1868f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 1878f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 1888f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 1898f5d16f6SVladimir Oltean * different setting than what is being requested. 1908f5d16f6SVladimir Oltean */ 1918f5d16f6SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 1928f5d16f6SVladimir Oltean struct net_device *other_bridge; 1938f5d16f6SVladimir Oltean 1948f5d16f6SVladimir Oltean other_bridge = dsa_to_port(ds, i)->bridge_dev; 1958f5d16f6SVladimir Oltean if (!other_bridge) 1968f5d16f6SVladimir Oltean continue; 1978f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 1988f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 1998f5d16f6SVladimir Oltean */ 2008f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 2018f5d16f6SVladimir Oltean continue; 2028f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 2038f5d16f6SVladimir Oltean dev_err(ds->dev, "VLAN filtering is a global setting\n"); 2048f5d16f6SVladimir Oltean return false; 2058f5d16f6SVladimir Oltean } 2068f5d16f6SVladimir Oltean } 2078f5d16f6SVladimir Oltean return true; 2088f5d16f6SVladimir Oltean } 2098f5d16f6SVladimir Oltean 2104d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 2114d61d304SVivien Didelot struct switchdev_trans *trans) 2124d61d304SVivien Didelot { 2134d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 21433162e9aSVladimir Oltean int err; 2154d61d304SVivien Didelot 2164d61d304SVivien Didelot /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ 2174d61d304SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 2184d61d304SVivien Didelot return 0; 2194d61d304SVivien Didelot 2208f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 2218f5d16f6SVladimir Oltean return 0; 2228f5d16f6SVladimir Oltean 2238f5d16f6SVladimir Oltean if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering)) 2248f5d16f6SVladimir Oltean return -EINVAL; 2258f5d16f6SVladimir Oltean 226ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 227ec9121e7SVladimir Oltean return 0; 228ec9121e7SVladimir Oltean 22933162e9aSVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, 2304d61d304SVivien Didelot vlan_filtering); 23133162e9aSVladimir Oltean if (err) 23233162e9aSVladimir Oltean return err; 2338f5d16f6SVladimir Oltean 23414574676SVladimir Oltean if (ds->vlan_filtering_is_global) 23514574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 23614574676SVladimir Oltean else 23733162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 2384d61d304SVivien Didelot return 0; 2394d61d304SVivien Didelot } 240d87bd94eSVivien Didelot 241d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, 242d87bd94eSVivien Didelot struct switchdev_trans *trans) 243d87bd94eSVivien Didelot { 244d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 245d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 2461faabf74SVivien Didelot struct dsa_notifier_ageing_time_info info = { 2471faabf74SVivien Didelot .ageing_time = ageing_time, 2481faabf74SVivien Didelot .trans = trans, 2491faabf74SVivien Didelot }; 250d87bd94eSVivien Didelot 2511faabf74SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 2521faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 253d87bd94eSVivien Didelot 254d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 255d87bd94eSVivien Didelot 2561faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 257d87bd94eSVivien Didelot } 258d1cffff0SVivien Didelot 259ea87005aSFlorian Fainelli int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, 260ea87005aSFlorian Fainelli struct switchdev_trans *trans) 261ea87005aSFlorian Fainelli { 262ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 263ea87005aSFlorian Fainelli 264ea87005aSFlorian Fainelli if (!ds->ops->port_egress_floods || 265ea87005aSFlorian Fainelli (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) 266ea87005aSFlorian Fainelli return -EINVAL; 267ea87005aSFlorian Fainelli 268ea87005aSFlorian Fainelli return 0; 269ea87005aSFlorian Fainelli } 270ea87005aSFlorian Fainelli 27157652796SRussell King int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, 27257652796SRussell King struct switchdev_trans *trans) 27357652796SRussell King { 27457652796SRussell King struct dsa_switch *ds = dp->ds; 27557652796SRussell King int port = dp->index; 27657652796SRussell King int err = 0; 27757652796SRussell King 27857652796SRussell King if (switchdev_trans_ph_prepare(trans)) 27957652796SRussell King return 0; 28057652796SRussell King 28157652796SRussell King if (ds->ops->port_egress_floods) 28257652796SRussell King err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, 28357652796SRussell King flags & BR_MCAST_FLOOD); 28457652796SRussell King 28557652796SRussell King return err; 28657652796SRussell King } 28757652796SRussell King 28808cc83ccSVivien Didelot int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, 28908cc83ccSVivien Didelot struct switchdev_trans *trans) 29008cc83ccSVivien Didelot { 29108cc83ccSVivien Didelot struct dsa_switch *ds = dp->ds; 29208cc83ccSVivien Didelot int port = dp->index; 29308cc83ccSVivien Didelot 29408cc83ccSVivien Didelot if (switchdev_trans_ph_prepare(trans)) 29508cc83ccSVivien Didelot return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; 29608cc83ccSVivien Didelot 29708cc83ccSVivien Didelot return ds->ops->port_egress_floods(ds, port, true, mrouter); 29808cc83ccSVivien Didelot } 29908cc83ccSVivien Didelot 3002acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 3012acf4e6aSArkadi Sharshevsky u16 vid) 302d1cffff0SVivien Didelot { 303685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 304685fb6a4SVivien Didelot .sw_index = dp->ds->index, 305685fb6a4SVivien Didelot .port = dp->index, 3062acf4e6aSArkadi Sharshevsky .addr = addr, 3072acf4e6aSArkadi Sharshevsky .vid = vid, 308685fb6a4SVivien Didelot }; 309d1cffff0SVivien Didelot 310685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 311d1cffff0SVivien Didelot } 312d1cffff0SVivien Didelot 3132acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 3142acf4e6aSArkadi Sharshevsky u16 vid) 315d1cffff0SVivien Didelot { 316685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 317685fb6a4SVivien Didelot .sw_index = dp->ds->index, 318685fb6a4SVivien Didelot .port = dp->index, 3192acf4e6aSArkadi Sharshevsky .addr = addr, 3202acf4e6aSArkadi Sharshevsky .vid = vid, 3212acf4e6aSArkadi Sharshevsky 322685fb6a4SVivien Didelot }; 323d1cffff0SVivien Didelot 324685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 325d1cffff0SVivien Didelot } 326d1cffff0SVivien Didelot 327de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 328de40fc5dSVivien Didelot { 329de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 330de40fc5dSVivien Didelot int port = dp->index; 331de40fc5dSVivien Didelot 332de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 333de40fc5dSVivien Didelot return -EOPNOTSUPP; 334de40fc5dSVivien Didelot 335de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 336de40fc5dSVivien Didelot } 337de40fc5dSVivien Didelot 338bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 3393a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb, 3403a9afea3SVivien Didelot struct switchdev_trans *trans) 3413a9afea3SVivien Didelot { 3428ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 3438ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 3448ae5bcdcSVivien Didelot .port = dp->index, 3458ae5bcdcSVivien Didelot .trans = trans, 3468ae5bcdcSVivien Didelot .mdb = mdb, 3478ae5bcdcSVivien Didelot }; 3483a9afea3SVivien Didelot 3498ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 3503a9afea3SVivien Didelot } 3513a9afea3SVivien Didelot 352bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 3533a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 3543a9afea3SVivien Didelot { 3558ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 3568ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 3578ae5bcdcSVivien Didelot .port = dp->index, 3588ae5bcdcSVivien Didelot .mdb = mdb, 3598ae5bcdcSVivien Didelot }; 3603a9afea3SVivien Didelot 3618ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 3623a9afea3SVivien Didelot } 3633a9afea3SVivien Didelot 364076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 365076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 366076e7133SVivien Didelot struct switchdev_trans *trans) 367076e7133SVivien Didelot { 368d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 369d0c627b8SVivien Didelot .sw_index = dp->ds->index, 370d0c627b8SVivien Didelot .port = dp->index, 371d0c627b8SVivien Didelot .trans = trans, 372d0c627b8SVivien Didelot .vlan = vlan, 373d0c627b8SVivien Didelot }; 374076e7133SVivien Didelot 375d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 376076e7133SVivien Didelot } 377076e7133SVivien Didelot 378076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 379076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 380076e7133SVivien Didelot { 381d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 382d0c627b8SVivien Didelot .sw_index = dp->ds->index, 383d0c627b8SVivien Didelot .port = dp->index, 384d0c627b8SVivien Didelot .vlan = vlan, 385d0c627b8SVivien Didelot }; 386076e7133SVivien Didelot 387d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 388076e7133SVivien Didelot } 38957ab1ca2SVivien Didelot 390314f76d7SVladimir Oltean int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags) 391314f76d7SVladimir Oltean { 392314f76d7SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 393314f76d7SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 394314f76d7SVladimir Oltean .flags = flags, 395314f76d7SVladimir Oltean .vid_begin = vid, 396314f76d7SVladimir Oltean .vid_end = vid, 397314f76d7SVladimir Oltean }; 398314f76d7SVladimir Oltean struct switchdev_trans trans; 399314f76d7SVladimir Oltean int err; 400314f76d7SVladimir Oltean 401314f76d7SVladimir Oltean trans.ph_prepare = true; 402314f76d7SVladimir Oltean err = dsa_port_vlan_add(dp, &vlan, &trans); 403cf360866SVivien Didelot if (err) 404cf360866SVivien Didelot return err; 405314f76d7SVladimir Oltean 406314f76d7SVladimir Oltean trans.ph_prepare = false; 407314f76d7SVladimir Oltean return dsa_port_vlan_add(dp, &vlan, &trans); 408314f76d7SVladimir Oltean } 409146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_add); 410314f76d7SVladimir Oltean 411314f76d7SVladimir Oltean int dsa_port_vid_del(struct dsa_port *dp, u16 vid) 412314f76d7SVladimir Oltean { 413314f76d7SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 414314f76d7SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 415314f76d7SVladimir Oltean .vid_begin = vid, 416314f76d7SVladimir Oltean .vid_end = vid, 417314f76d7SVladimir Oltean }; 418314f76d7SVladimir Oltean 419314f76d7SVladimir Oltean return dsa_port_vlan_del(dp, &vlan); 420314f76d7SVladimir Oltean } 421146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_del); 422314f76d7SVladimir Oltean 4236207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 4246207a78cSFlorian Fainelli { 4256207a78cSFlorian Fainelli struct device_node *phy_dn; 4266207a78cSFlorian Fainelli struct phy_device *phydev; 4276207a78cSFlorian Fainelli 4286207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 4296207a78cSFlorian Fainelli if (!phy_dn) 4306207a78cSFlorian Fainelli return NULL; 4316207a78cSFlorian Fainelli 4326207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 4336207a78cSFlorian Fainelli if (!phydev) { 4346207a78cSFlorian Fainelli of_node_put(phy_dn); 4356207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 4366207a78cSFlorian Fainelli } 4376207a78cSFlorian Fainelli 4389919a363SWen Yang of_node_put(phy_dn); 4396207a78cSFlorian Fainelli return phydev; 4406207a78cSFlorian Fainelli } 4416207a78cSFlorian Fainelli 4428ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 44377373d49SIoana Ciornei unsigned long *supported, 44477373d49SIoana Ciornei struct phylink_link_state *state) 44577373d49SIoana Ciornei { 44677373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 44777373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 44877373d49SIoana Ciornei 44977373d49SIoana Ciornei if (!ds->ops->phylink_validate) 45077373d49SIoana Ciornei return; 45177373d49SIoana Ciornei 45277373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 45377373d49SIoana Ciornei } 45477373d49SIoana Ciornei 4558ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 45677373d49SIoana Ciornei struct phylink_link_state *state) 45777373d49SIoana Ciornei { 45877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 45977373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 46077373d49SIoana Ciornei 461d46b7e4fSRussell King /* Only called for inband modes */ 462d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 463d46b7e4fSRussell King state->link = 0; 464d46b7e4fSRussell King return; 46577373d49SIoana Ciornei } 466d46b7e4fSRussell King 467d46b7e4fSRussell King if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0) 468d46b7e4fSRussell King state->link = 0; 469d46b7e4fSRussell King } 47077373d49SIoana Ciornei 4718ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 47277373d49SIoana Ciornei unsigned int mode, 47377373d49SIoana Ciornei const struct phylink_link_state *state) 47477373d49SIoana Ciornei { 47577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 47677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 47777373d49SIoana Ciornei 47877373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 47977373d49SIoana Ciornei return; 48077373d49SIoana Ciornei 48177373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 48277373d49SIoana Ciornei } 48377373d49SIoana Ciornei 4848ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 48577373d49SIoana Ciornei { 48677373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 48777373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 48877373d49SIoana Ciornei 48977373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 49077373d49SIoana Ciornei return; 49177373d49SIoana Ciornei 49277373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 49377373d49SIoana Ciornei } 49477373d49SIoana Ciornei 4958ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 49677373d49SIoana Ciornei unsigned int mode, 49777373d49SIoana Ciornei phy_interface_t interface) 49877373d49SIoana Ciornei { 49977373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 5000e279218SIoana Ciornei struct phy_device *phydev = NULL; 50177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 50277373d49SIoana Ciornei 5030e279218SIoana Ciornei if (dsa_is_user_port(ds, dp->index)) 5040e279218SIoana Ciornei phydev = dp->slave->phydev; 5050e279218SIoana Ciornei 50677373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 5070e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 5080e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 50977373d49SIoana Ciornei return; 51077373d49SIoana Ciornei } 51177373d49SIoana Ciornei 51277373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 51377373d49SIoana Ciornei } 51477373d49SIoana Ciornei 5158ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 51677373d49SIoana Ciornei unsigned int mode, 51777373d49SIoana Ciornei phy_interface_t interface, 51877373d49SIoana Ciornei struct phy_device *phydev) 51977373d49SIoana Ciornei { 52077373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 52177373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 52277373d49SIoana Ciornei 52377373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 5240e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 5250e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 52677373d49SIoana Ciornei return; 52777373d49SIoana Ciornei } 52877373d49SIoana Ciornei 52977373d49SIoana Ciornei ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); 53077373d49SIoana Ciornei } 53177373d49SIoana Ciornei 53277373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 53377373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 534d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 53577373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 53677373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 53777373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 53877373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 53977373d49SIoana Ciornei }; 54077373d49SIoana Ciornei 54133615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 54233615367SSebastian Reichel { 54333615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 54433615367SSebastian Reichel struct phy_device *phydev; 54533615367SSebastian Reichel int port = dp->index; 54633615367SSebastian Reichel int err = 0; 54733615367SSebastian Reichel 5486207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 5496207a78cSFlorian Fainelli if (!phydev) 55033615367SSebastian Reichel return 0; 55133615367SSebastian Reichel 5526207a78cSFlorian Fainelli if (IS_ERR(phydev)) 5536207a78cSFlorian Fainelli return PTR_ERR(phydev); 55433615367SSebastian Reichel 55533615367SSebastian Reichel if (enable) { 55633615367SSebastian Reichel err = genphy_resume(phydev); 55733615367SSebastian Reichel if (err < 0) 55833615367SSebastian Reichel goto err_put_dev; 55933615367SSebastian Reichel 56033615367SSebastian Reichel err = genphy_read_status(phydev); 56133615367SSebastian Reichel if (err < 0) 56233615367SSebastian Reichel goto err_put_dev; 56333615367SSebastian Reichel } else { 56433615367SSebastian Reichel err = genphy_suspend(phydev); 56533615367SSebastian Reichel if (err < 0) 56633615367SSebastian Reichel goto err_put_dev; 56733615367SSebastian Reichel } 56833615367SSebastian Reichel 56933615367SSebastian Reichel if (ds->ops->adjust_link) 57033615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 57133615367SSebastian Reichel 57233615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 57333615367SSebastian Reichel 57433615367SSebastian Reichel err_put_dev: 57533615367SSebastian Reichel put_device(&phydev->mdio.dev); 57633615367SSebastian Reichel return err; 57733615367SSebastian Reichel } 57833615367SSebastian Reichel 57933615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 58057ab1ca2SVivien Didelot { 58157ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 58257ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 58357ab1ca2SVivien Didelot struct phy_device *phydev; 58457ab1ca2SVivien Didelot int port = dp->index; 5850c65b2b9SAndrew Lunn phy_interface_t mode; 58657ab1ca2SVivien Didelot int err; 58757ab1ca2SVivien Didelot 58857ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 58957ab1ca2SVivien Didelot if (err) { 59057ab1ca2SVivien Didelot dev_err(ds->dev, 59157ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 59257ab1ca2SVivien Didelot port); 59357ab1ca2SVivien Didelot return err; 59457ab1ca2SVivien Didelot } 59557ab1ca2SVivien Didelot 59657ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 59757ab1ca2SVivien Didelot 5980c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 5990c65b2b9SAndrew Lunn if (err) 60057ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 60157ab1ca2SVivien Didelot phydev->interface = mode; 60257ab1ca2SVivien Didelot 60357ab1ca2SVivien Didelot genphy_read_status(phydev); 60457ab1ca2SVivien Didelot 60557ab1ca2SVivien Didelot if (ds->ops->adjust_link) 60657ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 60757ab1ca2SVivien Didelot 60857ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 60957ab1ca2SVivien Didelot 61057ab1ca2SVivien Didelot return 0; 61157ab1ca2SVivien Didelot } 61257ab1ca2SVivien Didelot 6130e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 6140e279218SIoana Ciornei { 6150e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 6160e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 6170c65b2b9SAndrew Lunn phy_interface_t mode; 6180c65b2b9SAndrew Lunn int err; 6190e279218SIoana Ciornei 6200c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 6210c65b2b9SAndrew Lunn if (err) 6220e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 6230e279218SIoana Ciornei 6240e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 6250e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 626787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 6270e279218SIoana Ciornei 6280e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 6290e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 6300e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 6310e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 6320e279218SIoana Ciornei return PTR_ERR(dp->pl); 6330e279218SIoana Ciornei } 6340e279218SIoana Ciornei 6350e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 6362131fba5SFlorian Fainelli if (err && err != -ENODEV) { 6370e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 6380e279218SIoana Ciornei goto err_phy_connect; 6390e279218SIoana Ciornei } 6400e279218SIoana Ciornei 6410e279218SIoana Ciornei return 0; 6420e279218SIoana Ciornei 6430e279218SIoana Ciornei err_phy_connect: 6440e279218SIoana Ciornei phylink_destroy(dp->pl); 6450e279218SIoana Ciornei return err; 6460e279218SIoana Ciornei } 6470e279218SIoana Ciornei 64833615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 64957ab1ca2SVivien Didelot { 6500e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 6510e279218SIoana Ciornei 6520e279218SIoana Ciornei if (!ds->ops->adjust_link) 6530e279218SIoana Ciornei return dsa_port_phylink_register(dp); 6540e279218SIoana Ciornei 6550e279218SIoana Ciornei dev_warn(ds->dev, 6560e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 6570e279218SIoana Ciornei 65833615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 65933615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 66033615367SSebastian Reichel else 66133615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 66233615367SSebastian Reichel } 66357ab1ca2SVivien Didelot 66433615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 66533615367SSebastian Reichel { 6660e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 6670e279218SIoana Ciornei 6680e279218SIoana Ciornei if (!ds->ops->adjust_link) { 6690e279218SIoana Ciornei rtnl_lock(); 6700e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 6710e279218SIoana Ciornei rtnl_unlock(); 6720e279218SIoana Ciornei phylink_destroy(dp->pl); 6730e279218SIoana Ciornei return; 6740e279218SIoana Ciornei } 6750e279218SIoana Ciornei 67633615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 67733615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 67833615367SSebastian Reichel else 67933615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 68057ab1ca2SVivien Didelot } 681cf963573SFlorian Fainelli 682cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 683cf963573SFlorian Fainelli { 684cf963573SFlorian Fainelli struct phy_device *phydev; 685cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 686cf963573SFlorian Fainelli 687cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 688cf963573SFlorian Fainelli return ret; 689cf963573SFlorian Fainelli 690cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 691cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 692cf963573SFlorian Fainelli return ret; 693cf963573SFlorian Fainelli 694cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 695cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 696cf963573SFlorian Fainelli 697cf963573SFlorian Fainelli return ret; 698cf963573SFlorian Fainelli } 699cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 700cf963573SFlorian Fainelli 701cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 702cf963573SFlorian Fainelli { 703cf963573SFlorian Fainelli struct phy_device *phydev; 704cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 705cf963573SFlorian Fainelli 706cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 707cf963573SFlorian Fainelli return ret; 708cf963573SFlorian Fainelli 709cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 710cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 711cf963573SFlorian Fainelli return ret; 712cf963573SFlorian Fainelli 713cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 714cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 715cf963573SFlorian Fainelli 716cf963573SFlorian Fainelli return ret; 717cf963573SFlorian Fainelli } 718cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 719cf963573SFlorian Fainelli 720cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 721cf963573SFlorian Fainelli { 722cf963573SFlorian Fainelli struct phy_device *phydev; 723cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 724cf963573SFlorian Fainelli 725cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 726cf963573SFlorian Fainelli return ret; 727cf963573SFlorian Fainelli 728cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 729cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 730cf963573SFlorian Fainelli return ret; 731cf963573SFlorian Fainelli 732cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 733cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 734cf963573SFlorian Fainelli 735cf963573SFlorian Fainelli return ret; 736cf963573SFlorian Fainelli } 737cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 738