1a40c175bSVivien Didelot /* 2a40c175bSVivien Didelot * Handling of a single switch port 3a40c175bSVivien Didelot * 4a40c175bSVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 5a40c175bSVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6a40c175bSVivien Didelot * 7a40c175bSVivien Didelot * This program is free software; you can redistribute it and/or modify 8a40c175bSVivien Didelot * it under the terms of the GNU General Public License as published by 9a40c175bSVivien Didelot * the Free Software Foundation; either version 2 of the License, or 10a40c175bSVivien Didelot * (at your option) any later version. 11a40c175bSVivien Didelot */ 12a40c175bSVivien Didelot 13a40c175bSVivien Didelot #include <linux/if_bridge.h> 14cfbed329SVivien Didelot #include <linux/notifier.h> 1557ab1ca2SVivien Didelot #include <linux/of_mdio.h> 1657ab1ca2SVivien Didelot #include <linux/of_net.h> 17a40c175bSVivien Didelot 18a40c175bSVivien Didelot #include "dsa_priv.h" 19a40c175bSVivien Didelot 20bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) 21cfbed329SVivien Didelot { 22cfbed329SVivien Didelot struct raw_notifier_head *nh = &dp->ds->dst->nh; 23cfbed329SVivien Didelot int err; 24cfbed329SVivien Didelot 25cfbed329SVivien Didelot err = raw_notifier_call_chain(nh, e, v); 26cfbed329SVivien Didelot 27cfbed329SVivien Didelot return notifier_to_errno(err); 28cfbed329SVivien Didelot } 29cfbed329SVivien Didelot 30a40c175bSVivien Didelot int dsa_port_set_state(struct dsa_port *dp, u8 state, 31a40c175bSVivien Didelot struct switchdev_trans *trans) 32a40c175bSVivien Didelot { 33a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 34a40c175bSVivien Didelot int port = dp->index; 35a40c175bSVivien Didelot 36a40c175bSVivien Didelot if (switchdev_trans_ph_prepare(trans)) 37a40c175bSVivien Didelot return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP; 38a40c175bSVivien Didelot 39a40c175bSVivien Didelot if (ds->ops->port_stp_state_set) 40a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 41a40c175bSVivien Didelot 42a40c175bSVivien Didelot if (ds->ops->port_fast_age) { 43a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 44a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 45a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 46a40c175bSVivien Didelot */ 47a40c175bSVivien Didelot 48a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 49a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 50a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 51a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 52a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 53a40c175bSVivien Didelot ds->ops->port_fast_age(ds, port); 54a40c175bSVivien Didelot } 55a40c175bSVivien Didelot 56a40c175bSVivien Didelot dp->stp_state = state; 57a40c175bSVivien Didelot 58a40c175bSVivien Didelot return 0; 59a40c175bSVivien Didelot } 60a40c175bSVivien Didelot 61fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) 62a40c175bSVivien Didelot { 63a40c175bSVivien Didelot int err; 64a40c175bSVivien Didelot 65a40c175bSVivien Didelot err = dsa_port_set_state(dp, state, NULL); 66a40c175bSVivien Didelot if (err) 67a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 68a40c175bSVivien Didelot } 69cfbed329SVivien Didelot 70fb8a6a2bSVivien Didelot int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 71fb8a6a2bSVivien Didelot { 72fb8a6a2bSVivien Didelot u8 stp_state = dp->bridge_dev ? BR_STATE_BLOCKING : BR_STATE_FORWARDING; 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 83fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, stp_state); 84fb8a6a2bSVivien Didelot 85fb8a6a2bSVivien Didelot return 0; 86fb8a6a2bSVivien Didelot } 87fb8a6a2bSVivien Didelot 88fb8a6a2bSVivien Didelot void dsa_port_disable(struct dsa_port *dp, struct phy_device *phy) 89fb8a6a2bSVivien Didelot { 90fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 91fb8a6a2bSVivien Didelot int port = dp->index; 92fb8a6a2bSVivien Didelot 93fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 94fb8a6a2bSVivien Didelot 95fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 96fb8a6a2bSVivien Didelot ds->ops->port_disable(ds, port, phy); 97fb8a6a2bSVivien Didelot } 98fb8a6a2bSVivien Didelot 99cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 100cfbed329SVivien Didelot { 101cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 102cfbed329SVivien Didelot .sw_index = dp->ds->index, 103cfbed329SVivien Didelot .port = dp->index, 104cfbed329SVivien Didelot .br = br, 105cfbed329SVivien Didelot }; 106cfbed329SVivien Didelot int err; 107cfbed329SVivien Didelot 108cfbed329SVivien Didelot /* Here the port is already bridged. Reflect the current configuration 109cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 110cfbed329SVivien Didelot */ 111cfbed329SVivien Didelot dp->bridge_dev = br; 112cfbed329SVivien Didelot 113cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); 114cfbed329SVivien Didelot 115cfbed329SVivien Didelot /* The bridging is rolled back on error */ 116cfbed329SVivien Didelot if (err) 117cfbed329SVivien Didelot dp->bridge_dev = NULL; 118cfbed329SVivien Didelot 119cfbed329SVivien Didelot return err; 120cfbed329SVivien Didelot } 121cfbed329SVivien Didelot 122cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 123cfbed329SVivien Didelot { 124cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 125cfbed329SVivien Didelot .sw_index = dp->ds->index, 126cfbed329SVivien Didelot .port = dp->index, 127cfbed329SVivien Didelot .br = br, 128cfbed329SVivien Didelot }; 129cfbed329SVivien Didelot int err; 130cfbed329SVivien Didelot 131cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 132cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 133cfbed329SVivien Didelot */ 134cfbed329SVivien Didelot dp->bridge_dev = NULL; 135cfbed329SVivien Didelot 136cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); 137cfbed329SVivien Didelot if (err) 138cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 139cfbed329SVivien Didelot 140cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 141cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 142cfbed329SVivien Didelot */ 143cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 144cfbed329SVivien Didelot } 1454d61d304SVivien Didelot 1464d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 1474d61d304SVivien Didelot struct switchdev_trans *trans) 1484d61d304SVivien Didelot { 1494d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 1504d61d304SVivien Didelot 1514d61d304SVivien Didelot /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ 1524d61d304SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 1534d61d304SVivien Didelot return 0; 1544d61d304SVivien Didelot 1554d61d304SVivien Didelot if (ds->ops->port_vlan_filtering) 1564d61d304SVivien Didelot return ds->ops->port_vlan_filtering(ds, dp->index, 1574d61d304SVivien Didelot vlan_filtering); 1584d61d304SVivien Didelot 1594d61d304SVivien Didelot return 0; 1604d61d304SVivien Didelot } 161d87bd94eSVivien Didelot 162d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, 163d87bd94eSVivien Didelot struct switchdev_trans *trans) 164d87bd94eSVivien Didelot { 165d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 166d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 1671faabf74SVivien Didelot struct dsa_notifier_ageing_time_info info = { 1681faabf74SVivien Didelot .ageing_time = ageing_time, 1691faabf74SVivien Didelot .trans = trans, 1701faabf74SVivien Didelot }; 171d87bd94eSVivien Didelot 1721faabf74SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 1731faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 174d87bd94eSVivien Didelot 175d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 176d87bd94eSVivien Didelot 1771faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 178d87bd94eSVivien Didelot } 179d1cffff0SVivien Didelot 1802acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 1812acf4e6aSArkadi Sharshevsky u16 vid) 182d1cffff0SVivien Didelot { 183685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 184685fb6a4SVivien Didelot .sw_index = dp->ds->index, 185685fb6a4SVivien Didelot .port = dp->index, 1862acf4e6aSArkadi Sharshevsky .addr = addr, 1872acf4e6aSArkadi Sharshevsky .vid = vid, 188685fb6a4SVivien Didelot }; 189d1cffff0SVivien Didelot 190685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 191d1cffff0SVivien Didelot } 192d1cffff0SVivien Didelot 1932acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 1942acf4e6aSArkadi Sharshevsky u16 vid) 195d1cffff0SVivien Didelot { 196685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 197685fb6a4SVivien Didelot .sw_index = dp->ds->index, 198685fb6a4SVivien Didelot .port = dp->index, 1992acf4e6aSArkadi Sharshevsky .addr = addr, 2002acf4e6aSArkadi Sharshevsky .vid = vid, 2012acf4e6aSArkadi Sharshevsky 202685fb6a4SVivien Didelot }; 203d1cffff0SVivien Didelot 204685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 205d1cffff0SVivien Didelot } 206d1cffff0SVivien Didelot 207de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 208de40fc5dSVivien Didelot { 209de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 210de40fc5dSVivien Didelot int port = dp->index; 211de40fc5dSVivien Didelot 212de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 213de40fc5dSVivien Didelot return -EOPNOTSUPP; 214de40fc5dSVivien Didelot 215de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 216de40fc5dSVivien Didelot } 217de40fc5dSVivien Didelot 218bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 2193a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb, 2203a9afea3SVivien Didelot struct switchdev_trans *trans) 2213a9afea3SVivien Didelot { 2228ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 2238ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 2248ae5bcdcSVivien Didelot .port = dp->index, 2258ae5bcdcSVivien Didelot .trans = trans, 2268ae5bcdcSVivien Didelot .mdb = mdb, 2278ae5bcdcSVivien Didelot }; 2283a9afea3SVivien Didelot 2298ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 2303a9afea3SVivien Didelot } 2313a9afea3SVivien Didelot 232bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 2333a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 2343a9afea3SVivien Didelot { 2358ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 2368ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 2378ae5bcdcSVivien Didelot .port = dp->index, 2388ae5bcdcSVivien Didelot .mdb = mdb, 2398ae5bcdcSVivien Didelot }; 2403a9afea3SVivien Didelot 2418ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 2423a9afea3SVivien Didelot } 2433a9afea3SVivien Didelot 244076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 245076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 246076e7133SVivien Didelot struct switchdev_trans *trans) 247076e7133SVivien Didelot { 248d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 249d0c627b8SVivien Didelot .sw_index = dp->ds->index, 250d0c627b8SVivien Didelot .port = dp->index, 251d0c627b8SVivien Didelot .trans = trans, 252d0c627b8SVivien Didelot .vlan = vlan, 253d0c627b8SVivien Didelot }; 254076e7133SVivien Didelot 255da0efa88SPetr Machata if (netif_is_bridge_master(vlan->obj.orig_dev)) 256da0efa88SPetr Machata return -EOPNOTSUPP; 257da0efa88SPetr Machata 2582ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 259d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 2602ea7a679SAndrew Lunn 2612ea7a679SAndrew Lunn return 0; 262076e7133SVivien Didelot } 263076e7133SVivien Didelot 264076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 265076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 266076e7133SVivien Didelot { 267d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 268d0c627b8SVivien Didelot .sw_index = dp->ds->index, 269d0c627b8SVivien Didelot .port = dp->index, 270d0c627b8SVivien Didelot .vlan = vlan, 271d0c627b8SVivien Didelot }; 272076e7133SVivien Didelot 273da0efa88SPetr Machata if (netif_is_bridge_master(vlan->obj.orig_dev)) 274da0efa88SPetr Machata return -EOPNOTSUPP; 275da0efa88SPetr Machata 2762ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 277d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 2782ea7a679SAndrew Lunn 2792ea7a679SAndrew Lunn return 0; 280076e7133SVivien Didelot } 28157ab1ca2SVivien Didelot 2826207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 2836207a78cSFlorian Fainelli { 2846207a78cSFlorian Fainelli struct device_node *phy_dn; 2856207a78cSFlorian Fainelli struct phy_device *phydev; 2866207a78cSFlorian Fainelli 2876207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 2886207a78cSFlorian Fainelli if (!phy_dn) 2896207a78cSFlorian Fainelli return NULL; 2906207a78cSFlorian Fainelli 2916207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 2926207a78cSFlorian Fainelli if (!phydev) { 2936207a78cSFlorian Fainelli of_node_put(phy_dn); 2946207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 2956207a78cSFlorian Fainelli } 2966207a78cSFlorian Fainelli 2976207a78cSFlorian Fainelli return phydev; 2986207a78cSFlorian Fainelli } 2996207a78cSFlorian Fainelli 30033615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 30133615367SSebastian Reichel { 30233615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 30333615367SSebastian Reichel struct phy_device *phydev; 30433615367SSebastian Reichel int port = dp->index; 30533615367SSebastian Reichel int err = 0; 30633615367SSebastian Reichel 3076207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 3086207a78cSFlorian Fainelli if (!phydev) 30933615367SSebastian Reichel return 0; 31033615367SSebastian Reichel 3116207a78cSFlorian Fainelli if (IS_ERR(phydev)) 3126207a78cSFlorian Fainelli return PTR_ERR(phydev); 31333615367SSebastian Reichel 31433615367SSebastian Reichel if (enable) { 31533615367SSebastian Reichel err = genphy_config_init(phydev); 31633615367SSebastian Reichel if (err < 0) 31733615367SSebastian Reichel goto err_put_dev; 31833615367SSebastian Reichel 31933615367SSebastian Reichel err = genphy_resume(phydev); 32033615367SSebastian Reichel if (err < 0) 32133615367SSebastian Reichel goto err_put_dev; 32233615367SSebastian Reichel 32333615367SSebastian Reichel err = genphy_read_status(phydev); 32433615367SSebastian Reichel if (err < 0) 32533615367SSebastian Reichel goto err_put_dev; 32633615367SSebastian Reichel } else { 32733615367SSebastian Reichel err = genphy_suspend(phydev); 32833615367SSebastian Reichel if (err < 0) 32933615367SSebastian Reichel goto err_put_dev; 33033615367SSebastian Reichel } 33133615367SSebastian Reichel 33233615367SSebastian Reichel if (ds->ops->adjust_link) 33333615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 33433615367SSebastian Reichel 33533615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 33633615367SSebastian Reichel 33733615367SSebastian Reichel err_put_dev: 33833615367SSebastian Reichel put_device(&phydev->mdio.dev); 33933615367SSebastian Reichel return err; 34033615367SSebastian Reichel } 34133615367SSebastian Reichel 34233615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 34357ab1ca2SVivien Didelot { 34457ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 34557ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 34657ab1ca2SVivien Didelot struct phy_device *phydev; 34757ab1ca2SVivien Didelot int port = dp->index; 34857ab1ca2SVivien Didelot int mode; 34957ab1ca2SVivien Didelot int err; 35057ab1ca2SVivien Didelot 35157ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 35257ab1ca2SVivien Didelot if (err) { 35357ab1ca2SVivien Didelot dev_err(ds->dev, 35457ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 35557ab1ca2SVivien Didelot port); 35657ab1ca2SVivien Didelot return err; 35757ab1ca2SVivien Didelot } 35857ab1ca2SVivien Didelot 35957ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 36057ab1ca2SVivien Didelot 36157ab1ca2SVivien Didelot mode = of_get_phy_mode(dn); 36257ab1ca2SVivien Didelot if (mode < 0) 36357ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 36457ab1ca2SVivien Didelot phydev->interface = mode; 36557ab1ca2SVivien Didelot 36657ab1ca2SVivien Didelot genphy_config_init(phydev); 36757ab1ca2SVivien Didelot genphy_read_status(phydev); 36857ab1ca2SVivien Didelot 36957ab1ca2SVivien Didelot if (ds->ops->adjust_link) 37057ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 37157ab1ca2SVivien Didelot 37257ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 37357ab1ca2SVivien Didelot 37457ab1ca2SVivien Didelot return 0; 37557ab1ca2SVivien Didelot } 37657ab1ca2SVivien Didelot 37733615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 37857ab1ca2SVivien Didelot { 37933615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 38033615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 38133615367SSebastian Reichel else 38233615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 38333615367SSebastian Reichel } 38457ab1ca2SVivien Didelot 38533615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 38633615367SSebastian Reichel { 38733615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 38833615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 38933615367SSebastian Reichel else 39033615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 39157ab1ca2SVivien Didelot } 392cf963573SFlorian Fainelli 393cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 394cf963573SFlorian Fainelli { 395cf963573SFlorian Fainelli struct phy_device *phydev; 396cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 397cf963573SFlorian Fainelli 398cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 399cf963573SFlorian Fainelli return ret; 400cf963573SFlorian Fainelli 401cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 402cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 403cf963573SFlorian Fainelli return ret; 404cf963573SFlorian Fainelli 405cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 406cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 407cf963573SFlorian Fainelli 408cf963573SFlorian Fainelli return ret; 409cf963573SFlorian Fainelli } 410cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 411cf963573SFlorian Fainelli 412cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 413cf963573SFlorian Fainelli { 414cf963573SFlorian Fainelli struct phy_device *phydev; 415cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 416cf963573SFlorian Fainelli 417cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 418cf963573SFlorian Fainelli return ret; 419cf963573SFlorian Fainelli 420cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 421cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 422cf963573SFlorian Fainelli return ret; 423cf963573SFlorian Fainelli 424cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 425cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 426cf963573SFlorian Fainelli 427cf963573SFlorian Fainelli return ret; 428cf963573SFlorian Fainelli } 429cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 430cf963573SFlorian Fainelli 431cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 432cf963573SFlorian Fainelli { 433cf963573SFlorian Fainelli struct phy_device *phydev; 434cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 435cf963573SFlorian Fainelli 436cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 437cf963573SFlorian Fainelli return ret; 438cf963573SFlorian Fainelli 439cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 440cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 441cf963573SFlorian Fainelli return ret; 442cf963573SFlorian Fainelli 443cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 444cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 445cf963573SFlorian Fainelli 446cf963573SFlorian Fainelli return ret; 447cf963573SFlorian Fainelli } 448cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 449