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 struct dsa_switch *ds = dp->ds; 73fb8a6a2bSVivien Didelot int port = dp->index; 74fb8a6a2bSVivien Didelot int err; 75fb8a6a2bSVivien Didelot 76fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 77fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 78fb8a6a2bSVivien Didelot if (err) 79fb8a6a2bSVivien Didelot return err; 80fb8a6a2bSVivien Didelot } 81fb8a6a2bSVivien Didelot 829c2054a5SRussell King if (!dp->bridge_dev) 839c2054a5SRussell King dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 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 939c2054a5SRussell King if (!dp->bridge_dev) 94fb8a6a2bSVivien Didelot dsa_port_set_state_now(dp, BR_STATE_DISABLED); 95fb8a6a2bSVivien Didelot 96fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 97fb8a6a2bSVivien Didelot ds->ops->port_disable(ds, port, phy); 98fb8a6a2bSVivien Didelot } 99fb8a6a2bSVivien Didelot 100cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) 101cfbed329SVivien Didelot { 102cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 103cfbed329SVivien Didelot .sw_index = dp->ds->index, 104cfbed329SVivien Didelot .port = dp->index, 105cfbed329SVivien Didelot .br = br, 106cfbed329SVivien Didelot }; 107cfbed329SVivien Didelot int err; 108cfbed329SVivien Didelot 109cfbed329SVivien Didelot /* Here the port is already bridged. Reflect the current configuration 110cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 111cfbed329SVivien Didelot */ 112cfbed329SVivien Didelot dp->bridge_dev = br; 113cfbed329SVivien Didelot 114cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); 115cfbed329SVivien Didelot 116cfbed329SVivien Didelot /* The bridging is rolled back on error */ 117cfbed329SVivien Didelot if (err) 118cfbed329SVivien Didelot dp->bridge_dev = NULL; 119cfbed329SVivien Didelot 120cfbed329SVivien Didelot return err; 121cfbed329SVivien Didelot } 122cfbed329SVivien Didelot 123cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 124cfbed329SVivien Didelot { 125cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 126cfbed329SVivien Didelot .sw_index = dp->ds->index, 127cfbed329SVivien Didelot .port = dp->index, 128cfbed329SVivien Didelot .br = br, 129cfbed329SVivien Didelot }; 130cfbed329SVivien Didelot int err; 131cfbed329SVivien Didelot 132cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 133cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 134cfbed329SVivien Didelot */ 135cfbed329SVivien Didelot dp->bridge_dev = NULL; 136cfbed329SVivien Didelot 137cfbed329SVivien Didelot err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); 138cfbed329SVivien Didelot if (err) 139cfbed329SVivien Didelot pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); 140cfbed329SVivien Didelot 141cfbed329SVivien Didelot /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 142cfbed329SVivien Didelot * so allow it to be in BR_STATE_FORWARDING to be kept functional 143cfbed329SVivien Didelot */ 144cfbed329SVivien Didelot dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 145cfbed329SVivien Didelot } 1464d61d304SVivien Didelot 1474d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 1484d61d304SVivien Didelot struct switchdev_trans *trans) 1494d61d304SVivien Didelot { 1504d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 1514d61d304SVivien Didelot 1524d61d304SVivien Didelot /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ 1534d61d304SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 1544d61d304SVivien Didelot return 0; 1554d61d304SVivien Didelot 1564d61d304SVivien Didelot if (ds->ops->port_vlan_filtering) 1574d61d304SVivien Didelot return ds->ops->port_vlan_filtering(ds, dp->index, 1584d61d304SVivien Didelot vlan_filtering); 1594d61d304SVivien Didelot 1604d61d304SVivien Didelot return 0; 1614d61d304SVivien Didelot } 162d87bd94eSVivien Didelot 163d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, 164d87bd94eSVivien Didelot struct switchdev_trans *trans) 165d87bd94eSVivien Didelot { 166d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 167d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 1681faabf74SVivien Didelot struct dsa_notifier_ageing_time_info info = { 1691faabf74SVivien Didelot .ageing_time = ageing_time, 1701faabf74SVivien Didelot .trans = trans, 1711faabf74SVivien Didelot }; 172d87bd94eSVivien Didelot 1731faabf74SVivien Didelot if (switchdev_trans_ph_prepare(trans)) 1741faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 175d87bd94eSVivien Didelot 176d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 177d87bd94eSVivien Didelot 1781faabf74SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 179d87bd94eSVivien Didelot } 180d1cffff0SVivien Didelot 1812acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 1822acf4e6aSArkadi Sharshevsky u16 vid) 183d1cffff0SVivien Didelot { 184685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 185685fb6a4SVivien Didelot .sw_index = dp->ds->index, 186685fb6a4SVivien Didelot .port = dp->index, 1872acf4e6aSArkadi Sharshevsky .addr = addr, 1882acf4e6aSArkadi Sharshevsky .vid = vid, 189685fb6a4SVivien Didelot }; 190d1cffff0SVivien Didelot 191685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 192d1cffff0SVivien Didelot } 193d1cffff0SVivien Didelot 1942acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 1952acf4e6aSArkadi Sharshevsky u16 vid) 196d1cffff0SVivien Didelot { 197685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 198685fb6a4SVivien Didelot .sw_index = dp->ds->index, 199685fb6a4SVivien Didelot .port = dp->index, 2002acf4e6aSArkadi Sharshevsky .addr = addr, 2012acf4e6aSArkadi Sharshevsky .vid = vid, 2022acf4e6aSArkadi Sharshevsky 203685fb6a4SVivien Didelot }; 204d1cffff0SVivien Didelot 205685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 206d1cffff0SVivien Didelot } 207d1cffff0SVivien Didelot 208de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 209de40fc5dSVivien Didelot { 210de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 211de40fc5dSVivien Didelot int port = dp->index; 212de40fc5dSVivien Didelot 213de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 214de40fc5dSVivien Didelot return -EOPNOTSUPP; 215de40fc5dSVivien Didelot 216de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 217de40fc5dSVivien Didelot } 218de40fc5dSVivien Didelot 219bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 2203a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb, 2213a9afea3SVivien Didelot struct switchdev_trans *trans) 2223a9afea3SVivien Didelot { 2238ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 2248ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 2258ae5bcdcSVivien Didelot .port = dp->index, 2268ae5bcdcSVivien Didelot .trans = trans, 2278ae5bcdcSVivien Didelot .mdb = mdb, 2288ae5bcdcSVivien Didelot }; 2293a9afea3SVivien Didelot 2308ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 2313a9afea3SVivien Didelot } 2323a9afea3SVivien Didelot 233bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 2343a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 2353a9afea3SVivien Didelot { 2368ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 2378ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 2388ae5bcdcSVivien Didelot .port = dp->index, 2398ae5bcdcSVivien Didelot .mdb = mdb, 2408ae5bcdcSVivien Didelot }; 2413a9afea3SVivien Didelot 2428ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 2433a9afea3SVivien Didelot } 2443a9afea3SVivien Didelot 245076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 246076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 247076e7133SVivien Didelot struct switchdev_trans *trans) 248076e7133SVivien Didelot { 249d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 250d0c627b8SVivien Didelot .sw_index = dp->ds->index, 251d0c627b8SVivien Didelot .port = dp->index, 252d0c627b8SVivien Didelot .trans = trans, 253d0c627b8SVivien Didelot .vlan = vlan, 254d0c627b8SVivien Didelot }; 255076e7133SVivien Didelot 2562ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 257d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 2582ea7a679SAndrew Lunn 2592ea7a679SAndrew Lunn return 0; 260076e7133SVivien Didelot } 261076e7133SVivien Didelot 262076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 263076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 264076e7133SVivien Didelot { 265d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 266d0c627b8SVivien Didelot .sw_index = dp->ds->index, 267d0c627b8SVivien Didelot .port = dp->index, 268d0c627b8SVivien Didelot .vlan = vlan, 269d0c627b8SVivien Didelot }; 270076e7133SVivien Didelot 271da0efa88SPetr Machata if (netif_is_bridge_master(vlan->obj.orig_dev)) 272da0efa88SPetr Machata return -EOPNOTSUPP; 273da0efa88SPetr Machata 2742ea7a679SAndrew Lunn if (br_vlan_enabled(dp->bridge_dev)) 275d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 2762ea7a679SAndrew Lunn 2772ea7a679SAndrew Lunn return 0; 278076e7133SVivien Didelot } 27957ab1ca2SVivien Didelot 2806207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 2816207a78cSFlorian Fainelli { 2826207a78cSFlorian Fainelli struct device_node *phy_dn; 2836207a78cSFlorian Fainelli struct phy_device *phydev; 2846207a78cSFlorian Fainelli 2856207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 2866207a78cSFlorian Fainelli if (!phy_dn) 2876207a78cSFlorian Fainelli return NULL; 2886207a78cSFlorian Fainelli 2896207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 2906207a78cSFlorian Fainelli if (!phydev) { 2916207a78cSFlorian Fainelli of_node_put(phy_dn); 2926207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 2936207a78cSFlorian Fainelli } 2946207a78cSFlorian Fainelli 2956207a78cSFlorian Fainelli return phydev; 2966207a78cSFlorian Fainelli } 2976207a78cSFlorian Fainelli 29833615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 29933615367SSebastian Reichel { 30033615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 30133615367SSebastian Reichel struct phy_device *phydev; 30233615367SSebastian Reichel int port = dp->index; 30333615367SSebastian Reichel int err = 0; 30433615367SSebastian Reichel 3056207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 3066207a78cSFlorian Fainelli if (!phydev) 30733615367SSebastian Reichel return 0; 30833615367SSebastian Reichel 3096207a78cSFlorian Fainelli if (IS_ERR(phydev)) 3106207a78cSFlorian Fainelli return PTR_ERR(phydev); 31133615367SSebastian Reichel 31233615367SSebastian Reichel if (enable) { 31333615367SSebastian Reichel err = genphy_config_init(phydev); 31433615367SSebastian Reichel if (err < 0) 31533615367SSebastian Reichel goto err_put_dev; 31633615367SSebastian Reichel 31733615367SSebastian Reichel err = genphy_resume(phydev); 31833615367SSebastian Reichel if (err < 0) 31933615367SSebastian Reichel goto err_put_dev; 32033615367SSebastian Reichel 32133615367SSebastian Reichel err = genphy_read_status(phydev); 32233615367SSebastian Reichel if (err < 0) 32333615367SSebastian Reichel goto err_put_dev; 32433615367SSebastian Reichel } else { 32533615367SSebastian Reichel err = genphy_suspend(phydev); 32633615367SSebastian Reichel if (err < 0) 32733615367SSebastian Reichel goto err_put_dev; 32833615367SSebastian Reichel } 32933615367SSebastian Reichel 33033615367SSebastian Reichel if (ds->ops->adjust_link) 33133615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 33233615367SSebastian Reichel 33333615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 33433615367SSebastian Reichel 33533615367SSebastian Reichel err_put_dev: 33633615367SSebastian Reichel put_device(&phydev->mdio.dev); 33733615367SSebastian Reichel return err; 33833615367SSebastian Reichel } 33933615367SSebastian Reichel 34033615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 34157ab1ca2SVivien Didelot { 34257ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 34357ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 34457ab1ca2SVivien Didelot struct phy_device *phydev; 34557ab1ca2SVivien Didelot int port = dp->index; 34657ab1ca2SVivien Didelot int mode; 34757ab1ca2SVivien Didelot int err; 34857ab1ca2SVivien Didelot 34957ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 35057ab1ca2SVivien Didelot if (err) { 35157ab1ca2SVivien Didelot dev_err(ds->dev, 35257ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 35357ab1ca2SVivien Didelot port); 35457ab1ca2SVivien Didelot return err; 35557ab1ca2SVivien Didelot } 35657ab1ca2SVivien Didelot 35757ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 35857ab1ca2SVivien Didelot 35957ab1ca2SVivien Didelot mode = of_get_phy_mode(dn); 36057ab1ca2SVivien Didelot if (mode < 0) 36157ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 36257ab1ca2SVivien Didelot phydev->interface = mode; 36357ab1ca2SVivien Didelot 36457ab1ca2SVivien Didelot genphy_config_init(phydev); 36557ab1ca2SVivien Didelot genphy_read_status(phydev); 36657ab1ca2SVivien Didelot 36757ab1ca2SVivien Didelot if (ds->ops->adjust_link) 36857ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 36957ab1ca2SVivien Didelot 37057ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 37157ab1ca2SVivien Didelot 37257ab1ca2SVivien Didelot return 0; 37357ab1ca2SVivien Didelot } 37457ab1ca2SVivien Didelot 37533615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 37657ab1ca2SVivien Didelot { 37733615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 37833615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 37933615367SSebastian Reichel else 38033615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 38133615367SSebastian Reichel } 38257ab1ca2SVivien Didelot 38333615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 38433615367SSebastian Reichel { 38533615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 38633615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 38733615367SSebastian Reichel else 38833615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 38957ab1ca2SVivien Didelot } 390cf963573SFlorian Fainelli 391cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) 392cf963573SFlorian Fainelli { 393cf963573SFlorian Fainelli struct phy_device *phydev; 394cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 395cf963573SFlorian Fainelli 396cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 397cf963573SFlorian Fainelli return ret; 398cf963573SFlorian Fainelli 399cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 400cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 401cf963573SFlorian Fainelli return ret; 402cf963573SFlorian Fainelli 403cf963573SFlorian Fainelli ret = phy_ethtool_get_strings(phydev, data); 404cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 405cf963573SFlorian Fainelli 406cf963573SFlorian Fainelli return ret; 407cf963573SFlorian Fainelli } 408cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); 409cf963573SFlorian Fainelli 410cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) 411cf963573SFlorian Fainelli { 412cf963573SFlorian Fainelli struct phy_device *phydev; 413cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 414cf963573SFlorian Fainelli 415cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 416cf963573SFlorian Fainelli return ret; 417cf963573SFlorian Fainelli 418cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 419cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 420cf963573SFlorian Fainelli return ret; 421cf963573SFlorian Fainelli 422cf963573SFlorian Fainelli ret = phy_ethtool_get_stats(phydev, NULL, data); 423cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 424cf963573SFlorian Fainelli 425cf963573SFlorian Fainelli return ret; 426cf963573SFlorian Fainelli } 427cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); 428cf963573SFlorian Fainelli 429cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp) 430cf963573SFlorian Fainelli { 431cf963573SFlorian Fainelli struct phy_device *phydev; 432cf963573SFlorian Fainelli int ret = -EOPNOTSUPP; 433cf963573SFlorian Fainelli 434cf963573SFlorian Fainelli if (of_phy_is_fixed_link(dp->dn)) 435cf963573SFlorian Fainelli return ret; 436cf963573SFlorian Fainelli 437cf963573SFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 438cf963573SFlorian Fainelli if (IS_ERR_OR_NULL(phydev)) 439cf963573SFlorian Fainelli return ret; 440cf963573SFlorian Fainelli 441cf963573SFlorian Fainelli ret = phy_ethtool_get_sset_count(phydev); 442cf963573SFlorian Fainelli put_device(&phydev->mdio.dev); 443cf963573SFlorian Fainelli 444cf963573SFlorian Fainelli return ret; 445cf963573SFlorian Fainelli } 446cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); 447