Skip to content

Commit ce0aa27

Browse files
Russell Kingdavem330
Russell King
authored andcommitted
sfp: add sfp-bus to bridge between network devices and sfp cages
Signed-off-by: Russell King <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9525ae8 commit ce0aa27

File tree

5 files changed

+1097
-0
lines changed

5 files changed

+1097
-0
lines changed

drivers/net/phy/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
3838
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
3939
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
4040

41+
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
42+
obj-y += $(sfp-obj-y) $(sfp-obj-m)
43+
4144
obj-$(CONFIG_AMD_PHY) += amd.o
4245
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
4346
obj-$(CONFIG_AT803X_PHY) += at803x.o

drivers/net/phy/phylink.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/spinlock.h>
2222
#include <linux/workqueue.h>
2323

24+
#include "sfp.h"
2425
#include "swphy.h"
2526

2627
#define SUPPORTED_INTERFACES \
@@ -32,6 +33,7 @@
3233

3334
enum {
3435
PHYLINK_DISABLE_STOPPED,
36+
PHYLINK_DISABLE_LINK,
3537
};
3638

3739
struct phylink {
@@ -54,6 +56,8 @@ struct phylink {
5456
struct work_struct resolve;
5557

5658
bool mac_link_dropped;
59+
60+
struct sfp_bus *sfp_bus;
5761
};
5862

5963
static inline void linkmode_zero(unsigned long *dst)
@@ -466,6 +470,24 @@ static void phylink_run_resolve(struct phylink *pl)
466470
queue_work(system_power_efficient_wq, &pl->resolve);
467471
}
468472

473+
static const struct sfp_upstream_ops sfp_phylink_ops;
474+
475+
static int phylink_register_sfp(struct phylink *pl, struct device_node *np)
476+
{
477+
struct device_node *sfp_np;
478+
479+
sfp_np = of_parse_phandle(np, "sfp", 0);
480+
if (!sfp_np)
481+
return 0;
482+
483+
pl->sfp_bus = sfp_register_upstream(sfp_np, pl->netdev, pl,
484+
&sfp_phylink_ops);
485+
if (!pl->sfp_bus)
486+
return -ENOMEM;
487+
488+
return 0;
489+
}
490+
469491
struct phylink *phylink_create(struct net_device *ndev, struct device_node *np,
470492
phy_interface_t iface, const struct phylink_mac_ops *ops)
471493
{
@@ -507,12 +529,21 @@ struct phylink *phylink_create(struct net_device *ndev, struct device_node *np,
507529
}
508530
}
509531

532+
ret = phylink_register_sfp(pl, np);
533+
if (ret < 0) {
534+
kfree(pl);
535+
return ERR_PTR(ret);
536+
}
537+
510538
return pl;
511539
}
512540
EXPORT_SYMBOL_GPL(phylink_create);
513541

514542
void phylink_destroy(struct phylink *pl)
515543
{
544+
if (pl->sfp_bus)
545+
sfp_unregister_upstream(pl->sfp_bus);
546+
516547
cancel_work_sync(&pl->resolve);
517548
kfree(pl);
518549
}
@@ -706,6 +737,8 @@ void phylink_start(struct phylink *pl)
706737
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
707738
phylink_run_resolve(pl);
708739

740+
if (pl->sfp_bus)
741+
sfp_upstream_start(pl->sfp_bus);
709742
if (pl->phydev)
710743
phy_start(pl->phydev);
711744
}
@@ -717,6 +750,8 @@ void phylink_stop(struct phylink *pl)
717750

718751
if (pl->phydev)
719752
phy_stop(pl->phydev);
753+
if (pl->sfp_bus)
754+
sfp_upstream_stop(pl->sfp_bus);
720755

721756
set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
722757
flush_work(&pl->resolve);
@@ -1166,4 +1201,126 @@ int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd)
11661201
}
11671202
EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
11681203

1204+
1205+
1206+
static int phylink_sfp_module_insert(void *upstream,
1207+
const struct sfp_eeprom_id *id)
1208+
{
1209+
struct phylink *pl = upstream;
1210+
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
1211+
struct phylink_link_state config;
1212+
phy_interface_t iface;
1213+
int mode, ret = 0;
1214+
bool changed;
1215+
u8 port;
1216+
1217+
sfp_parse_support(pl->sfp_bus, id, support);
1218+
port = sfp_parse_port(pl->sfp_bus, id, support);
1219+
iface = sfp_parse_interface(pl->sfp_bus, id);
1220+
1221+
WARN_ON(!lockdep_rtnl_is_held());
1222+
1223+
switch (iface) {
1224+
case PHY_INTERFACE_MODE_SGMII:
1225+
mode = MLO_AN_SGMII;
1226+
break;
1227+
case PHY_INTERFACE_MODE_1000BASEX:
1228+
mode = MLO_AN_8023Z;
1229+
break;
1230+
default:
1231+
return -EINVAL;
1232+
}
1233+
1234+
memset(&config, 0, sizeof(config));
1235+
linkmode_copy(config.advertising, support);
1236+
config.interface = iface;
1237+
config.speed = SPEED_UNKNOWN;
1238+
config.duplex = DUPLEX_UNKNOWN;
1239+
config.pause = MLO_PAUSE_AN;
1240+
config.an_enabled = pl->link_config.an_enabled;
1241+
1242+
/* Ignore errors if we're expecting a PHY to attach later */
1243+
ret = phylink_validate(pl, support, &config);
1244+
if (ret) {
1245+
netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
1246+
phylink_an_mode_str(mode), phy_modes(config.interface),
1247+
__ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
1248+
return ret;
1249+
}
1250+
1251+
netdev_dbg(pl->netdev, "requesting link mode %s/%s with support %*pb\n",
1252+
phylink_an_mode_str(mode), phy_modes(config.interface),
1253+
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
1254+
1255+
if (mode == MLO_AN_8023Z && pl->phydev)
1256+
return -EINVAL;
1257+
1258+
changed = !bitmap_equal(pl->supported, support,
1259+
__ETHTOOL_LINK_MODE_MASK_NBITS);
1260+
if (changed) {
1261+
linkmode_copy(pl->supported, support);
1262+
linkmode_copy(pl->link_config.advertising, config.advertising);
1263+
}
1264+
1265+
if (pl->link_an_mode != mode ||
1266+
pl->link_config.interface != config.interface) {
1267+
pl->link_config.interface = config.interface;
1268+
pl->link_an_mode = mode;
1269+
1270+
changed = true;
1271+
1272+
netdev_info(pl->netdev, "switched to %s/%s link mode\n",
1273+
phylink_an_mode_str(mode),
1274+
phy_modes(config.interface));
1275+
}
1276+
1277+
pl->link_port = port;
1278+
1279+
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
1280+
&pl->phylink_disable_state))
1281+
phylink_mac_config(pl, &pl->link_config);
1282+
1283+
return ret;
1284+
}
1285+
1286+
static void phylink_sfp_link_down(void *upstream)
1287+
{
1288+
struct phylink *pl = upstream;
1289+
1290+
WARN_ON(!lockdep_rtnl_is_held());
1291+
1292+
set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
1293+
flush_work(&pl->resolve);
1294+
1295+
netif_carrier_off(pl->netdev);
1296+
}
1297+
1298+
static void phylink_sfp_link_up(void *upstream)
1299+
{
1300+
struct phylink *pl = upstream;
1301+
1302+
WARN_ON(!lockdep_rtnl_is_held());
1303+
1304+
clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
1305+
phylink_run_resolve(pl);
1306+
}
1307+
1308+
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
1309+
{
1310+
return phylink_connect_phy(upstream, phy);
1311+
}
1312+
1313+
static void phylink_sfp_disconnect_phy(void *upstream)
1314+
{
1315+
phylink_disconnect_phy(upstream);
1316+
}
1317+
1318+
static const struct sfp_upstream_ops sfp_phylink_ops = {
1319+
.module_insert = phylink_sfp_module_insert,
1320+
.link_up = phylink_sfp_link_up,
1321+
.link_down = phylink_sfp_link_down,
1322+
.connect_phy = phylink_sfp_connect_phy,
1323+
.disconnect_phy = phylink_sfp_disconnect_phy,
1324+
};
1325+
11691326
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)