@@ -876,7 +876,8 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_get_ts_info);
-static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond)
+static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond,
+ bool only_active_ports)
{
u32 bond_mask = 0;
int port;
@@ -887,8 +888,12 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond)
if (!ocelot_port)
continue;
- if (ocelot_port->bond == bond)
+ if (ocelot_port->bond == bond) {
+ if (only_active_ports && !ocelot_port->lag_tx_active)
+ continue;
+
bond_mask |= BIT(port);
+ }
}
return bond_mask;
@@ -914,7 +919,7 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
struct net_device *bond = ocelot_port->bond;
if (bond)
- mask &= ~ocelot_get_bond_mask(ocelot, bond);
+ mask &= ~ocelot_get_bond_mask(ocelot, bond, false);
ocelot_write_rix(ocelot, mask,
ANA_PGID_PGID, PGID_SRC + port);
@@ -1242,22 +1247,22 @@ static int ocelot_set_aggr_pgids(struct ocelot *ocelot)
bonds[port] = ocelot_port->bond;
}
- /* Now, set PGIDs for each LAG */
+ /* Now, set PGIDs for each active LAG */
for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
- int num_ports_in_lag = 0;
+ int num_active_ports = 0;
unsigned long bond_mask;
u8 aggr_idx[16];
if (!bonds[lag])
continue;
- bond_mask = ocelot_get_bond_mask(ocelot, bonds[lag]);
+ bond_mask = ocelot_get_bond_mask(ocelot, bonds[lag], true);
for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) {
// Destination mask
ocelot_write_rix(ocelot, bond_mask,
ANA_PGID_PGID, port);
- aggr_idx[num_ports_in_lag++] = port;
+ aggr_idx[num_active_ports++] = port;
}
for_each_aggr_pgid(ocelot, i) {
@@ -1265,7 +1270,11 @@ static int ocelot_set_aggr_pgids(struct ocelot *ocelot)
ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i);
ac &= ~bond_mask;
- ac |= BIT(aggr_idx[i % num_ports_in_lag]);
+ /* Don't do division by zero if there was no active
+ * port. Just make all aggregation codes zero.
+ */
+ if (num_active_ports)
+ ac |= BIT(aggr_idx[i % num_active_ports]);
ocelot_write_rix(ocelot, ac, ANA_PGID_PGID, i);
}
@@ -1301,7 +1310,8 @@ static void ocelot_setup_logical_port_ids(struct ocelot *ocelot)
bond = ocelot_port->bond;
if (bond) {
- int lag = __ffs(ocelot_get_bond_mask(ocelot, bond));
+ int lag = __ffs(ocelot_get_bond_mask(ocelot, bond,
+ false));
ocelot_rmw_gix(ocelot,
ANA_PORT_PORT_CFG_PORTID_VAL(lag),
@@ -1342,6 +1352,16 @@ int ocelot_port_lag_leave(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_port_lag_leave);
+int ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+ ocelot_port->lag_tx_active = lag_tx_active;
+
+ /* Rebalance the LAGs */
+ return ocelot_set_aggr_pgids(ocelot);
+}
+
/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
* The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
* In the special case that it's the NPI port that we're configuring, the
@@ -114,6 +114,7 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct netdev_lag_upper_info *info);
int ocelot_port_lag_leave(struct ocelot *ocelot, int port,
struct net_device *bond);
+int ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active);
struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port);
int ocelot_netdev_to_port(struct net_device *dev);
@@ -1163,6 +1163,27 @@ ocelot_netdevice_lag_changeupper(struct net_device *dev,
return NOTIFY_DONE;
}
+static int
+ocelot_netdevice_changelowerstate(struct net_device *dev,
+ struct netdev_lag_lower_state_info *info)
+{
+ struct ocelot_port_private *priv = netdev_priv(dev);
+ bool is_active = info->link_up && info->tx_enabled;
+ struct ocelot_port *ocelot_port = &priv->port;
+ struct ocelot *ocelot = ocelot_port->ocelot;
+ int port = priv->chip_port;
+ int err;
+
+ if (!ocelot_port->bond)
+ return NOTIFY_DONE;
+
+ if (ocelot_port->lag_tx_active == is_active)
+ return 0;
+
+ err = ocelot_port_lag_change(ocelot, port, is_active);
+ return notifier_from_errno(err);
+}
+
static int ocelot_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
@@ -1180,6 +1201,15 @@ static int ocelot_netdevice_event(struct notifier_block *unused,
break;
}
+ case NETDEV_CHANGELOWERSTATE: {
+ struct netdev_notifier_changelowerstate_info *info = ptr;
+
+ if (!ocelot_netdevice_dev_check(dev))
+ break;
+
+ return ocelot_netdevice_changelowerstate(dev,
+ info->lower_state_info);
+ }
default:
break;
}
@@ -612,6 +612,7 @@ struct ocelot_port {
u8 *xmit_template;
struct net_device *bond;
+ bool lag_tx_active;
};
struct ocelot {