Skip to content

Commit dc96ee3

Browse files
alexandrebellonidavem330
authored andcommitted
net: mscc: ocelot: add bonding support
Add link aggregation hardware offload support for Ocelot. ocelot_get_link_ksettings() is not great but it does work until the driver is reworked to switch to phylink. Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 964fc35 commit dc96ee3

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

drivers/net/ethernet/mscc/ocelot.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,8 @@ static const struct ethtool_ops ocelot_ethtool_ops = {
780780
.get_strings = ocelot_get_strings,
781781
.get_ethtool_stats = ocelot_get_ethtool_stats,
782782
.get_sset_count = ocelot_get_sset_count,
783+
.get_link_ksettings = phy_ethtool_get_link_ksettings,
784+
.set_link_ksettings = phy_ethtool_set_link_ksettings,
783785
};
784786

785787
static int ocelot_port_attr_get(struct net_device *dev,
@@ -1088,6 +1090,137 @@ static void ocelot_port_bridge_leave(struct ocelot_port *ocelot_port,
10881090
ocelot->hw_bridge_dev = NULL;
10891091
}
10901092

1093+
static void ocelot_set_aggr_pgids(struct ocelot *ocelot)
1094+
{
1095+
int i, port, lag;
1096+
1097+
/* Reset destination and aggregation PGIDS */
1098+
for (port = 0; port < ocelot->num_phys_ports; port++)
1099+
ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
1100+
1101+
for (i = PGID_AGGR; i < PGID_SRC; i++)
1102+
ocelot_write_rix(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
1103+
ANA_PGID_PGID, i);
1104+
1105+
/* Now, set PGIDs for each LAG */
1106+
for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
1107+
unsigned long bond_mask;
1108+
int aggr_count = 0;
1109+
u8 aggr_idx[16];
1110+
1111+
bond_mask = ocelot->lags[lag];
1112+
if (!bond_mask)
1113+
continue;
1114+
1115+
for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) {
1116+
// Destination mask
1117+
ocelot_write_rix(ocelot, bond_mask,
1118+
ANA_PGID_PGID, port);
1119+
aggr_idx[aggr_count] = port;
1120+
aggr_count++;
1121+
}
1122+
1123+
for (i = PGID_AGGR; i < PGID_SRC; i++) {
1124+
u32 ac;
1125+
1126+
ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i);
1127+
ac &= ~bond_mask;
1128+
ac |= BIT(aggr_idx[i % aggr_count]);
1129+
ocelot_write_rix(ocelot, ac, ANA_PGID_PGID, i);
1130+
}
1131+
}
1132+
}
1133+
1134+
static void ocelot_setup_lag(struct ocelot *ocelot, int lag)
1135+
{
1136+
unsigned long bond_mask = ocelot->lags[lag];
1137+
unsigned int p;
1138+
1139+
for_each_set_bit(p, &bond_mask, ocelot->num_phys_ports) {
1140+
u32 port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p);
1141+
1142+
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
1143+
1144+
/* Use lag port as logical port for port i */
1145+
ocelot_write_gix(ocelot, port_cfg |
1146+
ANA_PORT_PORT_CFG_PORTID_VAL(lag),
1147+
ANA_PORT_PORT_CFG, p);
1148+
}
1149+
}
1150+
1151+
static int ocelot_port_lag_join(struct ocelot_port *ocelot_port,
1152+
struct net_device *bond)
1153+
{
1154+
struct ocelot *ocelot = ocelot_port->ocelot;
1155+
int p = ocelot_port->chip_port;
1156+
int lag, lp;
1157+
struct net_device *ndev;
1158+
u32 bond_mask = 0;
1159+
1160+
rcu_read_lock();
1161+
for_each_netdev_in_bond_rcu(bond, ndev) {
1162+
struct ocelot_port *port = netdev_priv(ndev);
1163+
1164+
bond_mask |= BIT(port->chip_port);
1165+
}
1166+
rcu_read_unlock();
1167+
1168+
lp = __ffs(bond_mask);
1169+
1170+
/* If the new port is the lowest one, use it as the logical port from
1171+
* now on
1172+
*/
1173+
if (p == lp) {
1174+
lag = p;
1175+
ocelot->lags[p] = bond_mask;
1176+
bond_mask &= ~BIT(p);
1177+
if (bond_mask) {
1178+
lp = __ffs(bond_mask);
1179+
ocelot->lags[lp] = 0;
1180+
}
1181+
} else {
1182+
lag = lp;
1183+
ocelot->lags[lp] |= BIT(p);
1184+
}
1185+
1186+
ocelot_setup_lag(ocelot, lag);
1187+
ocelot_set_aggr_pgids(ocelot);
1188+
1189+
return 0;
1190+
}
1191+
1192+
static void ocelot_port_lag_leave(struct ocelot_port *ocelot_port,
1193+
struct net_device *bond)
1194+
{
1195+
struct ocelot *ocelot = ocelot_port->ocelot;
1196+
int p = ocelot_port->chip_port;
1197+
u32 port_cfg;
1198+
int i;
1199+
1200+
/* Remove port from any lag */
1201+
for (i = 0; i < ocelot->num_phys_ports; i++)
1202+
ocelot->lags[i] &= ~BIT(ocelot_port->chip_port);
1203+
1204+
/* if it was the logical port of the lag, move the lag config to the
1205+
* next port
1206+
*/
1207+
if (ocelot->lags[p]) {
1208+
int n = __ffs(ocelot->lags[p]);
1209+
1210+
ocelot->lags[n] = ocelot->lags[p];
1211+
ocelot->lags[p] = 0;
1212+
1213+
ocelot_setup_lag(ocelot, n);
1214+
}
1215+
1216+
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p);
1217+
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
1218+
ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(p),
1219+
ANA_PORT_PORT_CFG, p);
1220+
1221+
ocelot_set_aggr_pgids(ocelot);
1222+
}
1223+
10911224
/* Checks if the net_device instance given to us originate from our driver. */
10921225
static bool ocelot_netdevice_dev_check(const struct net_device *dev)
10931226
{
@@ -1114,6 +1247,14 @@ static int ocelot_netdevice_port_event(struct net_device *dev,
11141247
ocelot_port_bridge_leave(ocelot_port,
11151248
info->upper_dev);
11161249
}
1250+
if (netif_is_lag_master(info->upper_dev)) {
1251+
if (info->linking)
1252+
err = ocelot_port_lag_join(ocelot_port,
1253+
info->upper_dev);
1254+
else
1255+
ocelot_port_lag_leave(ocelot_port,
1256+
info->upper_dev);
1257+
}
11171258
break;
11181259
default:
11191260
break;
@@ -1129,6 +1270,20 @@ static int ocelot_netdevice_event(struct notifier_block *unused,
11291270
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
11301271
int ret = 0;
11311272

1273+
if (event == NETDEV_PRECHANGEUPPER &&
1274+
netif_is_lag_master(info->upper_dev)) {
1275+
struct netdev_lag_upper_info *lag_upper_info = info->upper_info;
1276+
struct netlink_ext_ack *extack;
1277+
1278+
if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
1279+
extack = netdev_notifier_info_to_extack(&info->info);
1280+
NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
1281+
1282+
ret = -EINVAL;
1283+
goto notify;
1284+
}
1285+
}
1286+
11321287
if (netif_is_lag_master(dev)) {
11331288
struct net_device *slave;
11341289
struct list_head *iter;
@@ -1201,6 +1356,11 @@ int ocelot_init(struct ocelot *ocelot)
12011356
int i, cpu = ocelot->num_phys_ports;
12021357
char queue_name[32];
12031358

1359+
ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
1360+
sizeof(u32), GFP_KERNEL);
1361+
if (!ocelot->lags)
1362+
return -ENOMEM;
1363+
12041364
ocelot->stats = devm_kcalloc(ocelot->dev,
12051365
ocelot->num_phys_ports * ocelot->num_stats,
12061366
sizeof(u64), GFP_KERNEL);

drivers/net/ethernet/mscc/ocelot.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ struct ocelot {
493493
u8 num_cpu_ports;
494494
struct ocelot_port **ports;
495495

496-
u16 lags[16];
496+
u32 *lags;
497497

498498
/* Keep track of the vlan port masks */
499499
u32 vlan_mask[VLAN_N_VID];

0 commit comments

Comments
 (0)