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 66fb8a6a2bSVivien Didelot int dsa_port_enable(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 81fb8a6a2bSVivien Didelot return 0; 82fb8a6a2bSVivien Didelot } 83fb8a6a2bSVivien Didelot 8475104db0SAndrew Lunn void dsa_port_disable(struct dsa_port *dp) 85fb8a6a2bSVivien Didelot { 86fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 87fb8a6a2bSVivien Didelot int port = dp->index; 88fb8a6a2bSVivien Didelot 899c2054a5SRussell King if (!dp->bridge_dev) 90fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 91fb8a6a2bSVivien Didelot 92fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 9375104db0SAndrew Lunn ds->ops->port_disable(ds, port); 94fb8a6a2bSVivien Didelot } 95fb8a6a2bSVivien Didelot 96cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 97cfbed329SVivien Didelot { 98cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 99cfbed329SVivien Didelot .sw_index = dp->ds->index, 100cfbed329SVivien Didelot .port = dp->index, 101cfbed329SVivien Didelot .br = br, 102cfbed329SVivien Didelot }; 103cfbed329SVivien Didelot int err; 104cfbed329SVivien Didelot 105c1388063SRussell King /* Set the flooding mode before joining the port in the switch */ 106c1388063SRussell King err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); 107c1388063SRussell King if (err) 108c1388063SRussell King return err; 109c1388063SRussell King 110c1388063SRussell King /* Here the interface is already bridged. Reflect the current 111c1388063SRussell King * configuration so that drivers can program their chips accordingly. 112cfbed329SVivien Didelot */ 113cfbed329SVivien Didelot dp->bridge_dev = br; 114cfbed329SVivien Didelot 115cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); 116cfbed329SVivien Didelot 117cfbed329SVivien Didelot /* The bridging is rolled back on error */ 118c1388063SRussell King if (err) { 119c1388063SRussell King dsa_port_bridge_flags(dp, 0, NULL); 120cfbed329SVivien Didelot dp->bridge_dev = NULL; 121c1388063SRussell King } 122cfbed329SVivien Didelot 123cfbed329SVivien Didelot return err; 124cfbed329SVivien Didelot } 125cfbed329SVivien Didelot 126cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 127cfbed329SVivien Didelot { 128cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 129cfbed329SVivien Didelot .sw_index = dp->ds->index, 130cfbed329SVivien Didelot .port = dp->index, 131cfbed329SVivien Didelot .br = br, 132cfbed329SVivien Didelot }; 133cfbed329SVivien Didelot int err; 134cfbed329SVivien Didelot 135cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 136cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 137cfbed329SVivien Didelot */ 138cfbed329SVivien Didelot dp->bridge_dev = NULL; 139cfbed329SVivien Didelot 140cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); 141cfbed329SVivien Didelot if (err) 142cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 143cfbed329SVivien Didelot 144c1388063SRussell King /* Port is leaving the bridge, disable flooding */ 145c1388063SRussell King dsa_port_bridge_flags(dp, 0, NULL); 146c1388063SRussell King 147cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 148cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 149cfbed329SVivien Didelot */ 150cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 151cfbed329SVivien Didelot } 1524d61d304SVivien Didelot 1538f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 1548f5d16f6SVladimir Oltean bool vlan_filtering) 1558f5d16f6SVladimir Oltean { 1568f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 1578f5d16f6SVladimir Oltean int i; 1588f5d16f6SVladimir Oltean 1598f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 1608f5d16f6SVladimir Oltean return true; 1618f5d16f6SVladimir Oltean 1628f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 1638f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 1648f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 1658f5d16f6SVladimir Oltean * different setting than what is being requested. 1668f5d16f6SVladimir Oltean */ 1678f5d16f6SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 1688f5d16f6SVladimir Oltean struct net_device *other_bridge; 1698f5d16f6SVladimir Oltean 1708f5d16f6SVladimir Oltean other_bridge = dsa_to_port(ds, i)->bridge_dev; 1718f5d16f6SVladimir Oltean if (!other_bridge) 1728f5d16f6SVladimir Oltean continue; 1738f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 1748f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 1758f5d16f6SVladimir Oltean */ 1768f5d16f6SVladimir Oltean if (other_bridge == dp->bridge_dev) 1778f5d16f6SVladimir Oltean continue; 1788f5d16f6SVladimir Oltean if (br_vlan_enabled(other_bridge) != vlan_filtering) { 1798f5d16f6SVladimir Oltean dev_err(ds->dev, "VLAN filtering is a global setting\n"); 1808f5d16f6SVladimir Oltean return false; 1818f5d16f6SVladimir Oltean } 1828f5d16f6SVladimir Oltean } 1838f5d16f6SVladimir Oltean return true; 1848f5d16f6SVladimir Oltean } 1858f5d16f6SVladimir Oltean 1864d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 1874d61d304SVivien Didelot struct switchdev_trans *trans) 1884d61d304SVivien Didelot { 1894d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 19033162e9aSVladimir Oltean int err; 1914d61d304SVivien Didelot 1924d61d304SVivien Didelot /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ 1934d61d304SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 1944d61d304SVivien Didelot return 0; 1954d61d304SVivien Didelot 1968f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 1978f5d16f6SVladimir Oltean return 0; 1988f5d16f6SVladimir Oltean 1998f5d16f6SVladimir Oltean if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering)) 2008f5d16f6SVladimir Oltean return -EINVAL; 2018f5d16f6SVladimir Oltean 202ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 203ec9121e7SVladimir Oltean return 0; 204ec9121e7SVladimir Oltean 20533162e9aSVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, 2064d61d304SVivien Didelot vlan_filtering); 20733162e9aSVladimir Oltean if (err) 20833162e9aSVladimir Oltean return err; 2098f5d16f6SVladimir Oltean 21014574676SVladimir Oltean if (ds->vlan_filtering_is_global) 21114574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 21214574676SVladimir Oltean else 21333162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 2144d61d304SVivien Didelot return 0; 2154d61d304SVivien Didelot } 216d87bd94eSVivien Didelot 217d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, 218d87bd94eSVivien Didelot struct switchdev_trans *trans) 219d87bd94eSVivien Didelot { 220d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 221d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 2221faabf74SVivien Didelot struct dsa_notifier_ageing_time_info info = { 2231faabf74SVivien Didelot .ageing_time = ageing_time, 2241faabf74SVivien Didelot .trans = trans, 2251faabf74SVivien Didelot }; 226d87bd94eSVivien Didelot 2271faabf74SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 2281faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 229d87bd94eSVivien Didelot 230d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 231d87bd94eSVivien Didelot 2321faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 233d87bd94eSVivien Didelot } 234d1cffff0SVivien Didelot 235ea87005aSFlorian Fainelli int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, 236ea87005aSFlorian Fainelli struct switchdev_trans *trans) 237ea87005aSFlorian Fainelli { 238ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 239ea87005aSFlorian Fainelli 240ea87005aSFlorian Fainelli if (!ds->ops->port_egress_floods || 241ea87005aSFlorian Fainelli (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) 242ea87005aSFlorian Fainelli return -EINVAL; 243ea87005aSFlorian Fainelli 244ea87005aSFlorian Fainelli return 0; 245ea87005aSFlorian Fainelli } 246ea87005aSFlorian Fainelli 24757652796SRussell King int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, 24857652796SRussell King struct switchdev_trans *trans) 24957652796SRussell King { 25057652796SRussell King struct dsa_switch *ds = dp->ds; 25157652796SRussell King int port = dp->index; 25257652796SRussell King int err = 0; 25357652796SRussell King 25457652796SRussell King if (switchdev_trans_ph_prepare(trans)) 25557652796SRussell King return 0; 25657652796SRussell King 25757652796SRussell King if (ds->ops->port_egress_floods) 25857652796SRussell King err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, 25957652796SRussell King flags & BR_MCAST_FLOOD); 26057652796SRussell King 26157652796SRussell King return err; 26257652796SRussell King } 26357652796SRussell King 26408cc83ccSVivien Didelot int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, 26508cc83ccSVivien Didelot struct switchdev_trans *trans) 26608cc83ccSVivien Didelot { 26708cc83ccSVivien Didelot struct dsa_switch *ds = dp->ds; 26808cc83ccSVivien Didelot int port = dp->index; 26908cc83ccSVivien Didelot 27008cc83ccSVivien Didelot if (switchdev_trans_ph_prepare(trans)) 27108cc83ccSVivien Didelot return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; 27208cc83ccSVivien Didelot 27308cc83ccSVivien Didelot return ds->ops->port_egress_floods(ds, port, true, mrouter); 27408cc83ccSVivien Didelot } 27508cc83ccSVivien Didelot 2762acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 2772acf4e6aSArkadi Sharshevsky u16 vid) 278d1cffff0SVivien Didelot { 279685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 280685fb6a4SVivien Didelot .sw_index = dp->ds->index, 281685fb6a4SVivien Didelot .port = dp->index, 2822acf4e6aSArkadi Sharshevsky .addr = addr, 2832acf4e6aSArkadi Sharshevsky .vid = vid, 284685fb6a4SVivien Didelot }; 285d1cffff0SVivien Didelot 286685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 287d1cffff0SVivien Didelot } 288d1cffff0SVivien Didelot 2892acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 2902acf4e6aSArkadi Sharshevsky u16 vid) 291d1cffff0SVivien Didelot { 292685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 293685fb6a4SVivien Didelot .sw_index = dp->ds->index, 294685fb6a4SVivien Didelot .port = dp->index, 2952acf4e6aSArkadi Sharshevsky .addr = addr, 2962acf4e6aSArkadi Sharshevsky .vid = vid, 2972acf4e6aSArkadi Sharshevsky 298685fb6a4SVivien Didelot }; 299d1cffff0SVivien Didelot 300685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 301d1cffff0SVivien Didelot } 302d1cffff0SVivien Didelot 303de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 304de40fc5dSVivien Didelot { 305de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 306de40fc5dSVivien Didelot int port = dp->index; 307de40fc5dSVivien Didelot 308de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 309de40fc5dSVivien Didelot return -EOPNOTSUPP; 310de40fc5dSVivien Didelot 311de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 312de40fc5dSVivien Didelot } 313de40fc5dSVivien Didelot 314bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 3153a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb, 3163a9afea3SVivien Didelot struct switchdev_trans *trans) 3173a9afea3SVivien Didelot { 3188ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 3198ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 3208ae5bcdcSVivien Didelot .port = dp->index, 3218ae5bcdcSVivien Didelot .trans = trans, 3228ae5bcdcSVivien Didelot .mdb = mdb, 3238ae5bcdcSVivien Didelot }; 3243a9afea3SVivien Didelot 3258ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 3263a9afea3SVivien Didelot } 3273a9afea3SVivien Didelot 328bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 3293a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 3303a9afea3SVivien Didelot { 3318ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 3328ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 3338ae5bcdcSVivien Didelot .port = dp->index, 3348ae5bcdcSVivien Didelot .mdb = mdb, 3358ae5bcdcSVivien Didelot }; 3363a9afea3SVivien Didelot 3378ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 3383a9afea3SVivien Didelot } 3393a9afea3SVivien Didelot 340076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 341076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 342076e7133SVivien Didelot struct switchdev_trans *trans) 343076e7133SVivien Didelot { 344d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 345d0c627b8SVivien Didelot .sw_index = dp->ds->index, 346d0c627b8SVivien Didelot .port = dp->index, 347d0c627b8SVivien Didelot .trans = trans, 348d0c627b8SVivien Didelot .vlan = vlan, 349d0c627b8SVivien Didelot }; 350076e7133SVivien Didelot 351d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 352076e7133SVivien Didelot } 353076e7133SVivien Didelot 354076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 355076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 356076e7133SVivien Didelot { 357d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 358d0c627b8SVivien Didelot .sw_index = dp->ds->index, 359d0c627b8SVivien Didelot .port = dp->index, 360d0c627b8SVivien Didelot .vlan = vlan, 361d0c627b8SVivien Didelot }; 362076e7133SVivien Didelot 363d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 364076e7133SVivien Didelot } 36557ab1ca2SVivien Didelot 366314f76d7SVladimir Oltean int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags) 367314f76d7SVladimir Oltean { 368314f76d7SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 369314f76d7SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 370314f76d7SVladimir Oltean .flags = flags, 371314f76d7SVladimir Oltean .vid_begin = vid, 372314f76d7SVladimir Oltean .vid_end = vid, 373314f76d7SVladimir Oltean }; 374314f76d7SVladimir Oltean struct switchdev_trans trans; 375314f76d7SVladimir Oltean int err; 376314f76d7SVladimir Oltean 377314f76d7SVladimir Oltean trans.ph_prepare = true; 378314f76d7SVladimir Oltean err = dsa_port_vlan_add(dp, &vlan, &trans); 379cf360866SVivien Didelot if (err) 380cf360866SVivien Didelot return err; 381314f76d7SVladimir Oltean 382314f76d7SVladimir Oltean trans.ph_prepare = false; 383314f76d7SVladimir Oltean return dsa_port_vlan_add(dp, &vlan, &trans); 384314f76d7SVladimir Oltean } 385146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_add); 386314f76d7SVladimir Oltean 387314f76d7SVladimir Oltean int dsa_port_vid_del(struct dsa_port *dp, u16 vid) 388314f76d7SVladimir Oltean { 389314f76d7SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 390314f76d7SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 391314f76d7SVladimir Oltean .vid_begin = vid, 392314f76d7SVladimir Oltean .vid_end = vid, 393314f76d7SVladimir Oltean }; 394314f76d7SVladimir Oltean 395314f76d7SVladimir Oltean return dsa_port_vlan_del(dp, &vlan); 396314f76d7SVladimir Oltean } 397146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_del); 398314f76d7SVladimir Oltean 3996207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 4006207a78cSFlorian Fainelli { 4016207a78cSFlorian Fainelli struct device_node *phy_dn; 4026207a78cSFlorian Fainelli struct phy_device *phydev; 4036207a78cSFlorian Fainelli 4046207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 4056207a78cSFlorian Fainelli if (!phy_dn) 4066207a78cSFlorian Fainelli return NULL; 4076207a78cSFlorian Fainelli 4086207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 4096207a78cSFlorian Fainelli if (!phydev) { 4106207a78cSFlorian Fainelli of_node_put(phy_dn); 4116207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 4126207a78cSFlorian Fainelli } 4136207a78cSFlorian Fainelli 4149919a363SWen Yang of_node_put(phy_dn); 4156207a78cSFlorian Fainelli return phydev; 4166207a78cSFlorian Fainelli } 4176207a78cSFlorian Fainelli 4188ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 41977373d49SIoana Ciornei unsigned long *supported, 42077373d49SIoana Ciornei struct phylink_link_state *state) 42177373d49SIoana Ciornei { 42277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 42377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 42477373d49SIoana Ciornei 42577373d49SIoana Ciornei if (!ds->ops->phylink_validate) 42677373d49SIoana Ciornei return; 42777373d49SIoana Ciornei 42877373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 42977373d49SIoana Ciornei } 43077373d49SIoana Ciornei 4318ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 43277373d49SIoana Ciornei struct phylink_link_state *state) 43377373d49SIoana Ciornei { 43477373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 43577373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 43677373d49SIoana Ciornei 437d46b7e4fSRussell King /* Only called for inband modes */ 438d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 439d46b7e4fSRussell King state->link = 0; 440d46b7e4fSRussell King return; 44177373d49SIoana Ciornei } 442d46b7e4fSRussell King 443d46b7e4fSRussell King if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0) 444d46b7e4fSRussell King state->link = 0; 445d46b7e4fSRussell King } 44677373d49SIoana Ciornei 4478ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 44877373d49SIoana Ciornei unsigned int mode, 44977373d49SIoana Ciornei const struct phylink_link_state *state) 45077373d49SIoana Ciornei { 45177373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 45277373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 45377373d49SIoana Ciornei 45477373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 45577373d49SIoana Ciornei return; 45677373d49SIoana Ciornei 45777373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 45877373d49SIoana Ciornei } 45977373d49SIoana Ciornei 4608ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 46177373d49SIoana Ciornei { 46277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 46377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 46477373d49SIoana Ciornei 46577373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 46677373d49SIoana Ciornei return; 46777373d49SIoana Ciornei 46877373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 46977373d49SIoana Ciornei } 47077373d49SIoana Ciornei 4718ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 47277373d49SIoana Ciornei unsigned int mode, 47377373d49SIoana Ciornei phy_interface_t interface) 47477373d49SIoana Ciornei { 47577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 4760e279218SIoana Ciornei struct phy_device *phydev = NULL; 47777373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 47877373d49SIoana Ciornei 4790e279218SIoana Ciornei if (dsa_is_user_port(ds, dp->index)) 4800e279218SIoana Ciornei phydev = dp->slave->phydev; 4810e279218SIoana Ciornei 48277373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 4830e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 4840e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 48577373d49SIoana Ciornei return; 48677373d49SIoana Ciornei } 48777373d49SIoana Ciornei 48877373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 48977373d49SIoana Ciornei } 49077373d49SIoana Ciornei 4918ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 49291a208f2SRussell King struct phy_device *phydev, 49377373d49SIoana Ciornei unsigned int mode, 49477373d49SIoana Ciornei phy_interface_t interface, 49591a208f2SRussell King int speed, int duplex, 49691a208f2SRussell King bool tx_pause, bool rx_pause) 49777373d49SIoana Ciornei { 49877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 49977373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 50077373d49SIoana Ciornei 50177373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 5020e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 5030e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 50477373d49SIoana Ciornei return; 50577373d49SIoana Ciornei } 50677373d49SIoana Ciornei 50777373d49SIoana Ciornei ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); 50877373d49SIoana Ciornei } 50977373d49SIoana Ciornei 51077373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 51177373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 512d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 51377373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 51477373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 51577373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 51677373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 51777373d49SIoana Ciornei }; 51877373d49SIoana Ciornei 51933615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 52033615367SSebastian Reichel { 52133615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 52233615367SSebastian Reichel struct phy_device *phydev; 52333615367SSebastian Reichel int port = dp->index; 52433615367SSebastian Reichel int err = 0; 52533615367SSebastian Reichel 5266207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 5276207a78cSFlorian Fainelli if (!phydev) 52833615367SSebastian Reichel return 0; 52933615367SSebastian Reichel 5306207a78cSFlorian Fainelli if (IS_ERR(phydev)) 5316207a78cSFlorian Fainelli return PTR_ERR(phydev); 53233615367SSebastian Reichel 53333615367SSebastian Reichel if (enable) { 53433615367SSebastian Reichel err = genphy_resume(phydev); 53533615367SSebastian Reichel if (err < 0) 53633615367SSebastian Reichel goto err_put_dev; 53733615367SSebastian Reichel 53833615367SSebastian Reichel err = genphy_read_status(phydev); 53933615367SSebastian Reichel if (err < 0) 54033615367SSebastian Reichel goto err_put_dev; 54133615367SSebastian Reichel } else { 54233615367SSebastian Reichel err = genphy_suspend(phydev); 54333615367SSebastian Reichel if (err < 0) 54433615367SSebastian Reichel goto err_put_dev; 54533615367SSebastian Reichel } 54633615367SSebastian Reichel 54733615367SSebastian Reichel if (ds->ops->adjust_link) 54833615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 54933615367SSebastian Reichel 55033615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 55133615367SSebastian Reichel 55233615367SSebastian Reichel err_put_dev: 55333615367SSebastian Reichel put_device(&phydev->mdio.dev); 55433615367SSebastian Reichel return err; 55533615367SSebastian Reichel } 55633615367SSebastian Reichel 55733615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 55857ab1ca2SVivien Didelot { 55957ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 56057ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 56157ab1ca2SVivien Didelot struct phy_device *phydev; 56257ab1ca2SVivien Didelot int port = dp->index; 5630c65b2b9SAndrew Lunn phy_interface_t mode; 56457ab1ca2SVivien Didelot int err; 56557ab1ca2SVivien Didelot 56657ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 56757ab1ca2SVivien Didelot if (err) { 56857ab1ca2SVivien Didelot dev_err(ds->dev, 56957ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 57057ab1ca2SVivien Didelot port); 57157ab1ca2SVivien Didelot return err; 57257ab1ca2SVivien Didelot } 57357ab1ca2SVivien Didelot 57457ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 57557ab1ca2SVivien Didelot 5760c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 5770c65b2b9SAndrew Lunn if (err) 57857ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 57957ab1ca2SVivien Didelot phydev->interface = mode; 58057ab1ca2SVivien Didelot 58157ab1ca2SVivien Didelot genphy_read_status(phydev); 58257ab1ca2SVivien Didelot 58357ab1ca2SVivien Didelot if (ds->ops->adjust_link) 58457ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 58557ab1ca2SVivien Didelot 58657ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 58757ab1ca2SVivien Didelot 58857ab1ca2SVivien Didelot return 0; 58957ab1ca2SVivien Didelot } 59057ab1ca2SVivien Didelot 5910e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 5920e279218SIoana Ciornei { 5930e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 5940e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 5950c65b2b9SAndrew Lunn phy_interface_t mode; 5960c65b2b9SAndrew Lunn int err; 5970e279218SIoana Ciornei 5980c65b2b9SAndrew Lunn err = of_get_phy_mode(port_dn, &mode); 5990c65b2b9SAndrew Lunn if (err) 6000e279218SIoana Ciornei mode = PHY_INTERFACE_MODE_NA; 6010e279218SIoana Ciornei 6020e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 6030e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 604787cac3fSVladimir Oltean dp->pl_config.pcs_poll = ds->pcs_poll; 6050e279218SIoana Ciornei 6060e279218SIoana Ciornei dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), 6070e279218SIoana Ciornei mode, &dsa_port_phylink_mac_ops); 6080e279218SIoana Ciornei if (IS_ERR(dp->pl)) { 6090e279218SIoana Ciornei pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 6100e279218SIoana Ciornei return PTR_ERR(dp->pl); 6110e279218SIoana Ciornei } 6120e279218SIoana Ciornei 6130e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 6142131fba5SFlorian Fainelli if (err && err != -ENODEV) { 6150e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 6160e279218SIoana Ciornei goto err_phy_connect; 6170e279218SIoana Ciornei } 6180e279218SIoana Ciornei 6190e279218SIoana Ciornei rtnl_lock(); 6200e279218SIoana Ciornei phylink_start(dp->pl); 6210e279218SIoana Ciornei rtnl_unlock(); 6220e279218SIoana Ciornei 6230e279218SIoana Ciornei return 0; 6240e279218SIoana Ciornei 6250e279218SIoana Ciornei err_phy_connect: 6260e279218SIoana Ciornei phylink_destroy(dp->pl); 6270e279218SIoana Ciornei return err; 6280e279218SIoana Ciornei } 6290e279218SIoana Ciornei 63033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 63157ab1ca2SVivien Didelot { 6320e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 6330e279218SIoana Ciornei 6340e279218SIoana Ciornei if (!ds->ops->adjust_link) 6350e279218SIoana Ciornei return dsa_port_phylink_register(dp); 6360e279218SIoana Ciornei 6370e279218SIoana Ciornei dev_warn(ds->dev, 6380e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 6390e279218SIoana Ciornei 64033615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 64133615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 64233615367SSebastian Reichel else 64333615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 64433615367SSebastian Reichel } 64557ab1ca2SVivien Didelot 64633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 64733615367SSebastian Reichel { 6480e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 6490e279218SIoana Ciornei 6500e279218SIoana Ciornei if (!ds->ops->adjust_link) { 6510e279218SIoana Ciornei rtnl_lock(); 6520e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 6530e279218SIoana Ciornei rtnl_unlock(); 6540e279218SIoana Ciornei phylink_destroy(dp->pl); 6550e279218SIoana Ciornei return; 6560e279218SIoana Ciornei } 6570e279218SIoana Ciornei 65833615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 65933615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 66033615367SSebastian Reichel else 66133615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 66257ab1ca2SVivien Didelot } 663cf963573SFlorian Fainelli 664cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 665cf963573SFlorian Fainelli { 666cf963573SFlorian Fainelli struct phy_device *phydev; 667cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 668cf963573SFlorian Fainelli 669cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 670cf963573SFlorian Fainelli return ret; 671cf963573SFlorian Fainelli 672cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 673cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 674cf963573SFlorian Fainelli return ret; 675cf963573SFlorian Fainelli 676cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 677cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 678cf963573SFlorian Fainelli 679cf963573SFlorian Fainelli return ret; 680cf963573SFlorian Fainelli } 681cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 682cf963573SFlorian Fainelli 683cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 684cf963573SFlorian Fainelli { 685cf963573SFlorian Fainelli struct phy_device *phydev; 686cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 687cf963573SFlorian Fainelli 688cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 689cf963573SFlorian Fainelli return ret; 690cf963573SFlorian Fainelli 691cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 692cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 693cf963573SFlorian Fainelli return ret; 694cf963573SFlorian Fainelli 695cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 696cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 697cf963573SFlorian Fainelli 698cf963573SFlorian Fainelli return ret; 699cf963573SFlorian Fainelli } 700cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 701cf963573SFlorian Fainelli 702cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 703cf963573SFlorian Fainelli { 704cf963573SFlorian Fainelli struct phy_device *phydev; 705cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 706cf963573SFlorian Fainelli 707cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 708cf963573SFlorian Fainelli return ret; 709cf963573SFlorian Fainelli 710cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 711cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 712cf963573SFlorian Fainelli return ret; 713cf963573SFlorian Fainelli 714cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 715cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 716cf963573SFlorian Fainelli 717cf963573SFlorian Fainelli return ret; 718cf963573SFlorian Fainelli } 719cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 720