blob: 9a6ed138878c356fc9c088545f0d848ff02cbc57 [file] [log] [blame]
Vivien Didelota40c1752017-05-19 17:00:44 -04001/*
2 * Handling of a single switch port
3 *
4 * Copyright (c) 2017 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <linux/if_bridge.h>
Vivien Didelotcfbed322017-05-19 17:00:45 -040014#include <linux/notifier.h>
Vivien Didelot57ab1ca2017-10-26 10:50:07 -040015#include <linux/of_mdio.h>
16#include <linux/of_net.h>
Vivien Didelota40c1752017-05-19 17:00:44 -040017
18#include "dsa_priv.h"
19
Andrew Lunnbb9f603172017-11-09 23:11:01 +010020static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
Vivien Didelotcfbed322017-05-19 17:00:45 -040021{
22 struct raw_notifier_head *nh = &dp->ds->dst->nh;
23 int err;
24
25 err = raw_notifier_call_chain(nh, e, v);
26
27 return notifier_to_errno(err);
28}
29
Vivien Didelota40c1752017-05-19 17:00:44 -040030int dsa_port_set_state(struct dsa_port *dp, u8 state,
31 struct switchdev_trans *trans)
32{
33 struct dsa_switch *ds = dp->ds;
34 int port = dp->index;
35
36 if (switchdev_trans_ph_prepare(trans))
37 return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
38
39 if (ds->ops->port_stp_state_set)
40 ds->ops->port_stp_state_set(ds, port, state);
41
42 if (ds->ops->port_fast_age) {
43 /* Fast age FDB entries or flush appropriate forwarding database
44 * for the given port, if we are moving it from Learning or
45 * Forwarding state, to Disabled or Blocking or Listening state.
46 */
47
48 if ((dp->stp_state == BR_STATE_LEARNING ||
49 dp->stp_state == BR_STATE_FORWARDING) &&
50 (state == BR_STATE_DISABLED ||
51 state == BR_STATE_BLOCKING ||
52 state == BR_STATE_LISTENING))
53 ds->ops->port_fast_age(ds, port);
54 }
55
56 dp->stp_state = state;
57
58 return 0;
59}
60
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040061static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
Vivien Didelota40c1752017-05-19 17:00:44 -040062{
63 int err;
64
65 err = dsa_port_set_state(dp, state, NULL);
66 if (err)
67 pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
68}
Vivien Didelotcfbed322017-05-19 17:00:45 -040069
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040070int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
71{
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040072 struct dsa_switch *ds = dp->ds;
73 int port = dp->index;
74 int err;
75
76 if (ds->ops->port_enable) {
77 err = ds->ops->port_enable(ds, port, phy);
78 if (err)
79 return err;
80 }
81
Russell King9c2054a2019-02-20 10:32:52 +000082 if (!dp->bridge_dev)
83 dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040084
85 return 0;
86}
87
Andrew Lunn75104db2019-02-24 20:44:43 +010088void dsa_port_disable(struct dsa_port *dp)
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040089{
90 struct dsa_switch *ds = dp->ds;
91 int port = dp->index;
92
Russell King9c2054a2019-02-20 10:32:52 +000093 if (!dp->bridge_dev)
94 dsa_port_set_state_now(dp, BR_STATE_DISABLED);
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040095
96 if (ds->ops->port_disable)
Andrew Lunn75104db2019-02-24 20:44:43 +010097 ds->ops->port_disable(ds, port);
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040098}
99
Vivien Didelotcfbed322017-05-19 17:00:45 -0400100int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
101{
102 struct dsa_notifier_bridge_info info = {
103 .sw_index = dp->ds->index,
104 .port = dp->index,
105 .br = br,
106 };
107 int err;
108
Russell Kingc1388062019-02-20 15:35:06 -0800109 /* Set the flooding mode before joining the port in the switch */
110 err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
111 if (err)
112 return err;
113
114 /* Here the interface is already bridged. Reflect the current
115 * configuration so that drivers can program their chips accordingly.
Vivien Didelotcfbed322017-05-19 17:00:45 -0400116 */
117 dp->bridge_dev = br;
118
119 err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
120
121 /* The bridging is rolled back on error */
Russell Kingc1388062019-02-20 15:35:06 -0800122 if (err) {
123 dsa_port_bridge_flags(dp, 0, NULL);
Vivien Didelotcfbed322017-05-19 17:00:45 -0400124 dp->bridge_dev = NULL;
Russell Kingc1388062019-02-20 15:35:06 -0800125 }
Vivien Didelotcfbed322017-05-19 17:00:45 -0400126
127 return err;
128}
129
130void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
131{
132 struct dsa_notifier_bridge_info info = {
133 .sw_index = dp->ds->index,
134 .port = dp->index,
135 .br = br,
136 };
137 int err;
138
139 /* Here the port is already unbridged. Reflect the current configuration
140 * so that drivers can program their chips accordingly.
141 */
142 dp->bridge_dev = NULL;
143
144 err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
145 if (err)
146 pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
147
Russell Kingc1388062019-02-20 15:35:06 -0800148 /* Port is leaving the bridge, disable flooding */
149 dsa_port_bridge_flags(dp, 0, NULL);
150
Vivien Didelotcfbed322017-05-19 17:00:45 -0400151 /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
152 * so allow it to be in BR_STATE_FORWARDING to be kept functional
153 */
154 dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
155}
Vivien Didelot4d61d302017-05-19 17:00:46 -0400156
Vladimir Oltean8f5d16f2019-04-28 21:45:44 +0300157static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
158 bool vlan_filtering)
159{
160 struct dsa_switch *ds = dp->ds;
161 int i;
162
163 if (!ds->vlan_filtering_is_global)
164 return true;
165
166 /* For cases where enabling/disabling VLAN awareness is global to the
167 * switch, we need to handle the case where multiple bridges span
168 * different ports of the same switch device and one of them has a
169 * different setting than what is being requested.
170 */
171 for (i = 0; i < ds->num_ports; i++) {
172 struct net_device *other_bridge;
173
174 other_bridge = dsa_to_port(ds, i)->bridge_dev;
175 if (!other_bridge)
176 continue;
177 /* If it's the same bridge, it also has same
178 * vlan_filtering setting => no need to check
179 */
180 if (other_bridge == dp->bridge_dev)
181 continue;
182 if (br_vlan_enabled(other_bridge) != vlan_filtering) {
183 dev_err(ds->dev, "VLAN filtering is a global setting\n");
184 return false;
185 }
186 }
187 return true;
188}
189
Vivien Didelot4d61d302017-05-19 17:00:46 -0400190int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
191 struct switchdev_trans *trans)
192{
193 struct dsa_switch *ds = dp->ds;
Vladimir Oltean33162e92019-04-28 21:45:43 +0300194 int err;
Vivien Didelot4d61d302017-05-19 17:00:46 -0400195
196 /* bridge skips -EOPNOTSUPP, so skip the prepare phase */
197 if (switchdev_trans_ph_prepare(trans))
198 return 0;
199
Vladimir Oltean8f5d16f2019-04-28 21:45:44 +0300200 if (!ds->ops->port_vlan_filtering)
201 return 0;
202
203 if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering))
204 return -EINVAL;
205
206 err = ds->ops->port_vlan_filtering(ds, dp->index,
207 vlan_filtering);
208 if (err)
209 return err;
210
211 dp->vlan_filtering = vlan_filtering;
Vivien Didelot4d61d302017-05-19 17:00:46 -0400212 return 0;
213}
Vivien Didelotd87bd942017-05-19 17:00:47 -0400214
Vivien Didelotd87bd942017-05-19 17:00:47 -0400215int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
216 struct switchdev_trans *trans)
217{
218 unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
219 unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
Vivien Didelot1faabf72017-05-19 17:00:52 -0400220 struct dsa_notifier_ageing_time_info info = {
221 .ageing_time = ageing_time,
Vivien Didelot1faabf72017-05-19 17:00:52 -0400222 .trans = trans,
223 };
Vivien Didelotd87bd942017-05-19 17:00:47 -0400224
Vivien Didelot1faabf72017-05-19 17:00:52 -0400225 if (switchdev_trans_ph_prepare(trans))
226 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
Vivien Didelotd87bd942017-05-19 17:00:47 -0400227
Vivien Didelotd87bd942017-05-19 17:00:47 -0400228 dp->ageing_time = ageing_time;
Vivien Didelotd87bd942017-05-19 17:00:47 -0400229
Vivien Didelot1faabf72017-05-19 17:00:52 -0400230 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
Vivien Didelotd87bd942017-05-19 17:00:47 -0400231}
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400232
Florian Fainelliea870052019-02-20 16:58:22 -0800233int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
234 struct switchdev_trans *trans)
235{
236 struct dsa_switch *ds = dp->ds;
237
238 if (!ds->ops->port_egress_floods ||
239 (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
240 return -EINVAL;
241
242 return 0;
243}
244
Russell King57652792019-02-20 15:35:04 -0800245int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
246 struct switchdev_trans *trans)
247{
248 struct dsa_switch *ds = dp->ds;
249 int port = dp->index;
250 int err = 0;
251
252 if (switchdev_trans_ph_prepare(trans))
253 return 0;
254
255 if (ds->ops->port_egress_floods)
256 err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
257 flags & BR_MCAST_FLOOD);
258
259 return err;
260}
261
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300262int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
263 u16 vid)
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400264{
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400265 struct dsa_notifier_fdb_info info = {
266 .sw_index = dp->ds->index,
267 .port = dp->index,
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300268 .addr = addr,
269 .vid = vid,
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400270 };
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400271
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400272 return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400273}
274
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300275int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
276 u16 vid)
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400277{
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400278 struct dsa_notifier_fdb_info info = {
279 .sw_index = dp->ds->index,
280 .port = dp->index,
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300281 .addr = addr,
282 .vid = vid,
283
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400284 };
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400285
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400286 return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400287}
288
Vivien Didelotde40fc52017-09-20 19:32:14 -0400289int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
290{
291 struct dsa_switch *ds = dp->ds;
292 int port = dp->index;
293
294 if (!ds->ops->port_fdb_dump)
295 return -EOPNOTSUPP;
296
297 return ds->ops->port_fdb_dump(ds, port, cb, data);
298}
299
Andrew Lunnbb9f603172017-11-09 23:11:01 +0100300int dsa_port_mdb_add(const struct dsa_port *dp,
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400301 const struct switchdev_obj_port_mdb *mdb,
302 struct switchdev_trans *trans)
303{
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400304 struct dsa_notifier_mdb_info info = {
305 .sw_index = dp->ds->index,
306 .port = dp->index,
307 .trans = trans,
308 .mdb = mdb,
309 };
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400310
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400311 return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400312}
313
Andrew Lunnbb9f603172017-11-09 23:11:01 +0100314int dsa_port_mdb_del(const struct dsa_port *dp,
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400315 const struct switchdev_obj_port_mdb *mdb)
316{
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400317 struct dsa_notifier_mdb_info info = {
318 .sw_index = dp->ds->index,
319 .port = dp->index,
320 .mdb = mdb,
321 };
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400322
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400323 return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400324}
325
Vivien Didelot076e7132017-05-19 17:00:50 -0400326int dsa_port_vlan_add(struct dsa_port *dp,
327 const struct switchdev_obj_port_vlan *vlan,
328 struct switchdev_trans *trans)
329{
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400330 struct dsa_notifier_vlan_info info = {
331 .sw_index = dp->ds->index,
332 .port = dp->index,
333 .trans = trans,
334 .vlan = vlan,
335 };
Vivien Didelot076e7132017-05-19 17:00:50 -0400336
Florian Fainelli061f6a52019-02-20 14:35:39 -0800337 /* Can be called from dsa_slave_port_obj_add() or
338 * dsa_slave_vlan_rx_add_vid()
339 */
340 if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev))
Andrew Lunn2ea7a672017-11-07 00:04:24 +0100341 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
342
343 return 0;
Vivien Didelot076e7132017-05-19 17:00:50 -0400344}
345
346int dsa_port_vlan_del(struct dsa_port *dp,
347 const struct switchdev_obj_port_vlan *vlan)
348{
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400349 struct dsa_notifier_vlan_info info = {
350 .sw_index = dp->ds->index,
351 .port = dp->index,
352 .vlan = vlan,
353 };
Vivien Didelot076e7132017-05-19 17:00:50 -0400354
Florian Fainelli061f6a52019-02-20 14:35:39 -0800355 if (vlan->obj.orig_dev && netif_is_bridge_master(vlan->obj.orig_dev))
Petr Machatada0efa82018-05-30 02:59:26 +0200356 return -EOPNOTSUPP;
357
Florian Fainelli061f6a52019-02-20 14:35:39 -0800358 /* Can be called from dsa_slave_port_obj_del() or
359 * dsa_slave_vlan_rx_kill_vid()
360 */
361 if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev))
Andrew Lunn2ea7a672017-11-07 00:04:24 +0100362 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
363
364 return 0;
Vivien Didelot076e7132017-05-19 17:00:50 -0400365}
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400366
Florian Fainelli6207a782018-04-25 12:12:51 -0700367static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
368{
369 struct device_node *phy_dn;
370 struct phy_device *phydev;
371
372 phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
373 if (!phy_dn)
374 return NULL;
375
376 phydev = of_phy_find_device(phy_dn);
377 if (!phydev) {
378 of_node_put(phy_dn);
379 return ERR_PTR(-EPROBE_DEFER);
380 }
381
Wen Yang9919a362019-02-25 15:22:19 +0800382 of_node_put(phy_dn);
Florian Fainelli6207a782018-04-25 12:12:51 -0700383 return phydev;
384}
385
Sebastian Reichel33615362018-01-23 16:03:46 +0100386static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
387{
Sebastian Reichel33615362018-01-23 16:03:46 +0100388 struct dsa_switch *ds = dp->ds;
389 struct phy_device *phydev;
390 int port = dp->index;
391 int err = 0;
392
Florian Fainelli6207a782018-04-25 12:12:51 -0700393 phydev = dsa_port_get_phy_device(dp);
394 if (!phydev)
Sebastian Reichel33615362018-01-23 16:03:46 +0100395 return 0;
396
Florian Fainelli6207a782018-04-25 12:12:51 -0700397 if (IS_ERR(phydev))
398 return PTR_ERR(phydev);
Sebastian Reichel33615362018-01-23 16:03:46 +0100399
400 if (enable) {
401 err = genphy_config_init(phydev);
402 if (err < 0)
403 goto err_put_dev;
404
405 err = genphy_resume(phydev);
406 if (err < 0)
407 goto err_put_dev;
408
409 err = genphy_read_status(phydev);
410 if (err < 0)
411 goto err_put_dev;
412 } else {
413 err = genphy_suspend(phydev);
414 if (err < 0)
415 goto err_put_dev;
416 }
417
418 if (ds->ops->adjust_link)
419 ds->ops->adjust_link(ds, port, phydev);
420
421 dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
422
423err_put_dev:
424 put_device(&phydev->mdio.dev);
Sebastian Reichel33615362018-01-23 16:03:46 +0100425 return err;
426}
427
428static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400429{
430 struct device_node *dn = dp->dn;
431 struct dsa_switch *ds = dp->ds;
432 struct phy_device *phydev;
433 int port = dp->index;
434 int mode;
435 int err;
436
Sebastian Reichel33615362018-01-23 16:03:46 +0100437 err = of_phy_register_fixed_link(dn);
438 if (err) {
439 dev_err(ds->dev,
440 "failed to register the fixed PHY of port %d\n",
441 port);
442 return err;
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400443 }
444
Sebastian Reichel33615362018-01-23 16:03:46 +0100445 phydev = of_phy_find_device(dn);
446
447 mode = of_get_phy_mode(dn);
448 if (mode < 0)
449 mode = PHY_INTERFACE_MODE_NA;
450 phydev->interface = mode;
451
452 genphy_config_init(phydev);
453 genphy_read_status(phydev);
454
455 if (ds->ops->adjust_link)
456 ds->ops->adjust_link(ds, port, phydev);
457
458 put_device(&phydev->mdio.dev);
459
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400460 return 0;
461}
462
Sebastian Reichel33615362018-01-23 16:03:46 +0100463int dsa_port_link_register_of(struct dsa_port *dp)
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400464{
Sebastian Reichel33615362018-01-23 16:03:46 +0100465 if (of_phy_is_fixed_link(dp->dn))
466 return dsa_port_fixed_link_register_of(dp);
467 else
468 return dsa_port_setup_phy_of(dp, true);
469}
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400470
Sebastian Reichel33615362018-01-23 16:03:46 +0100471void dsa_port_link_unregister_of(struct dsa_port *dp)
472{
473 if (of_phy_is_fixed_link(dp->dn))
474 of_phy_deregister_fixed_link(dp->dn);
475 else
476 dsa_port_setup_phy_of(dp, false);
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400477}
Florian Fainellicf963572018-04-25 12:12:52 -0700478
479int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
480{
481 struct phy_device *phydev;
482 int ret = -EOPNOTSUPP;
483
484 if (of_phy_is_fixed_link(dp->dn))
485 return ret;
486
487 phydev = dsa_port_get_phy_device(dp);
488 if (IS_ERR_OR_NULL(phydev))
489 return ret;
490
491 ret = phy_ethtool_get_strings(phydev, data);
492 put_device(&phydev->mdio.dev);
493
494 return ret;
495}
496EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
497
498int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
499{
500 struct phy_device *phydev;
501 int ret = -EOPNOTSUPP;
502
503 if (of_phy_is_fixed_link(dp->dn))
504 return ret;
505
506 phydev = dsa_port_get_phy_device(dp);
507 if (IS_ERR_OR_NULL(phydev))
508 return ret;
509
510 ret = phy_ethtool_get_stats(phydev, NULL, data);
511 put_device(&phydev->mdio.dev);
512
513 return ret;
514}
515EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
516
517int dsa_port_get_phy_sset_count(struct dsa_port *dp)
518{
519 struct phy_device *phydev;
520 int ret = -EOPNOTSUPP;
521
522 if (of_phy_is_fixed_link(dp->dn))
523 return ret;
524
525 phydev = dsa_port_get_phy_device(dp);
526 if (IS_ERR_OR_NULL(phydev))
527 return ret;
528
529 ret = phy_ethtool_get_sset_count(phydev);
530 put_device(&phydev->mdio.dev);
531
532 return ret;
533}
534EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);