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 2552ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 256d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 2572ea7a679SAndrew Lunn 2582ea7a679SAndrew Lunn return 0; 259076e7133SVivien Didelot } 260076e7133SVivien Didelot 261076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 262076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 263076e7133SVivien Didelot { 264d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 265d0c627b8SVivien Didelot .sw_index = dp->ds->index, 266d0c627b8SVivien Didelot .port = dp->index, 267d0c627b8SVivien Didelot .vlan = vlan, 268d0c627b8SVivien Didelot }; 269076e7133SVivien Didelot 2702ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 271d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 2722ea7a679SAndrew Lunn 2732ea7a679SAndrew Lunn return 0; 274076e7133SVivien Didelot } 27557ab1ca2SVivien Didelot 2766207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 2776207a78cSFlorian Fainelli { 2786207a78cSFlorian Fainelli struct device_node *phy_dn; 2796207a78cSFlorian Fainelli struct phy_device *phydev; 2806207a78cSFlorian Fainelli 2816207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 2826207a78cSFlorian Fainelli if (!phy_dn) 2836207a78cSFlorian Fainelli return NULL; 2846207a78cSFlorian Fainelli 2856207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 2866207a78cSFlorian Fainelli if (!phydev) { 2876207a78cSFlorian Fainelli of_node_put(phy_dn); 2886207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 2896207a78cSFlorian Fainelli } 2906207a78cSFlorian Fainelli 2916207a78cSFlorian Fainelli return phydev; 2926207a78cSFlorian Fainelli } 2936207a78cSFlorian Fainelli 29433615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 29533615367SSebastian Reichel { 29633615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 29733615367SSebastian Reichel struct phy_device *phydev; 29833615367SSebastian Reichel int port = dp->index; 29933615367SSebastian Reichel int err = 0; 30033615367SSebastian Reichel 3016207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 3026207a78cSFlorian Fainelli if (!phydev) 30333615367SSebastian Reichel return 0; 30433615367SSebastian Reichel 3056207a78cSFlorian Fainelli if (IS_ERR(phydev)) 3066207a78cSFlorian Fainelli return PTR_ERR(phydev); 30733615367SSebastian Reichel 30833615367SSebastian Reichel if (enable) { 30933615367SSebastian Reichel err = genphy_config_init(phydev); 31033615367SSebastian Reichel if (err < 0) 31133615367SSebastian Reichel goto err_put_dev; 31233615367SSebastian Reichel 31333615367SSebastian Reichel err = genphy_resume(phydev); 31433615367SSebastian Reichel if (err < 0) 31533615367SSebastian Reichel goto err_put_dev; 31633615367SSebastian Reichel 31733615367SSebastian Reichel err = genphy_read_status(phydev); 31833615367SSebastian Reichel if (err < 0) 31933615367SSebastian Reichel goto err_put_dev; 32033615367SSebastian Reichel } else { 32133615367SSebastian Reichel err = genphy_suspend(phydev); 32233615367SSebastian Reichel if (err < 0) 32333615367SSebastian Reichel goto err_put_dev; 32433615367SSebastian Reichel } 32533615367SSebastian Reichel 32633615367SSebastian Reichel if (ds->ops->adjust_link) 32733615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 32833615367SSebastian Reichel 32933615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 33033615367SSebastian Reichel 33133615367SSebastian Reichel err_put_dev: 33233615367SSebastian Reichel put_device(&phydev->mdio.dev); 33333615367SSebastian Reichel return err; 33433615367SSebastian Reichel } 33533615367SSebastian Reichel 33633615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 33757ab1ca2SVivien Didelot { 33857ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 33957ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 34057ab1ca2SVivien Didelot struct phy_device *phydev; 34157ab1ca2SVivien Didelot int port = dp->index; 34257ab1ca2SVivien Didelot int mode; 34357ab1ca2SVivien Didelot int err; 34457ab1ca2SVivien Didelot 34557ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 34657ab1ca2SVivien Didelot if (err) { 34757ab1ca2SVivien Didelot dev_err(ds->dev, 34857ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 34957ab1ca2SVivien Didelot port); 35057ab1ca2SVivien Didelot return err; 35157ab1ca2SVivien Didelot } 35257ab1ca2SVivien Didelot 35357ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 35457ab1ca2SVivien Didelot 35557ab1ca2SVivien Didelot mode = of_get_phy_mode(dn); 35657ab1ca2SVivien Didelot if (mode < 0) 35757ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 35857ab1ca2SVivien Didelot phydev->interface = mode; 35957ab1ca2SVivien Didelot 36057ab1ca2SVivien Didelot genphy_config_init(phydev); 36157ab1ca2SVivien Didelot genphy_read_status(phydev); 36257ab1ca2SVivien Didelot 36357ab1ca2SVivien Didelot if (ds->ops->adjust_link) 36457ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 36557ab1ca2SVivien Didelot 36657ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 36757ab1ca2SVivien Didelot 36857ab1ca2SVivien Didelot return 0; 36957ab1ca2SVivien Didelot } 37057ab1ca2SVivien Didelot 37133615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 37257ab1ca2SVivien Didelot { 37333615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 37433615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 37533615367SSebastian Reichel else 37633615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 37733615367SSebastian Reichel } 37857ab1ca2SVivien Didelot 37933615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 38033615367SSebastian Reichel { 38133615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 38233615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 38333615367SSebastian Reichel else 38433615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 38557ab1ca2SVivien Didelot } 386cf963573SFlorian Fainelli 387cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 388cf963573SFlorian Fainelli { 389cf963573SFlorian Fainelli struct phy_device *phydev; 390cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 391cf963573SFlorian Fainelli 392cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 393cf963573SFlorian Fainelli return ret; 394cf963573SFlorian Fainelli 395cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 396cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 397cf963573SFlorian Fainelli return ret; 398cf963573SFlorian Fainelli 399cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 400cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 401cf963573SFlorian Fainelli 402cf963573SFlorian Fainelli return ret; 403cf963573SFlorian Fainelli } 404cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 405cf963573SFlorian Fainelli 406cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 407cf963573SFlorian Fainelli { 408cf963573SFlorian Fainelli struct phy_device *phydev; 409cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 410cf963573SFlorian Fainelli 411cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 412cf963573SFlorian Fainelli return ret; 413cf963573SFlorian Fainelli 414cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 415cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 416cf963573SFlorian Fainelli return ret; 417cf963573SFlorian Fainelli 418cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 419cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 420cf963573SFlorian Fainelli 421cf963573SFlorian Fainelli return ret; 422cf963573SFlorian Fainelli } 423cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 424cf963573SFlorian Fainelli 425cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 426cf963573SFlorian Fainelli { 427cf963573SFlorian Fainelli struct phy_device *phydev; 428cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 429cf963573SFlorian Fainelli 430cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 431cf963573SFlorian Fainelli return ret; 432cf963573SFlorian Fainelli 433cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 434cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 435cf963573SFlorian Fainelli return ret; 436cf963573SFlorian Fainelli 437cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 438cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 439cf963573SFlorian Fainelli 440cf963573SFlorian Fainelli return ret; 441cf963573SFlorian Fainelli } 442cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 443