net: dsa: yt921x: Add LAG offloading support

Add offloading for a link aggregation group supported by the YT921x
switches.

Signed-off-by: David Yang <mmyangfl@gmail.com>
Link: https://patch.msgid.link/20260117162116.1063043-1-mmyangfl@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
David Yang 2026-01-18 00:21:11 +08:00 committed by Paolo Abeni
parent a7c708dc0d
commit 0b42aeb468
2 changed files with 206 additions and 0 deletions

View File

@ -1117,6 +1117,188 @@ yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
return res;
}
static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag,
struct netlink_ext_ack *extack)
{
u32 val;
int res;
/* Hash Mode is global. Make sure the same Hash Mode is set to all the
* 2 possible lags.
* If we are the unique LAG we can set whatever hash mode we want.
* To change hash mode it's needed to remove all LAG and change the mode
* with the latest.
*/
if (unique_lag) {
res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl);
if (res)
return res;
} else {
res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val);
if (res)
return res;
if (val != ctrl) {
NL_SET_ERR_MSG_MOD(extack,
"Mismatched Hash Mode across different lags is not supported");
return -EOPNOTSUPP;
}
}
return 0;
}
static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask)
{
unsigned long targets_mask = ports_mask;
unsigned int cnt;
u32 ctrl;
int port;
int res;
cnt = 0;
for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
ctrl = YT921X_LAG_MEMBER_PORT(port);
res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt),
ctrl);
if (res)
return res;
cnt++;
}
ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) |
YT921X_LAG_GROUP_MEMBER_NUM(cnt);
return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl);
}
static int
yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct dsa_port *dp;
u32 ctrl;
int res;
if (!lag.id)
return -EINVAL;
ctrl = 0;
dsa_lag_foreach_port(dp, ds->dst, &lag)
ctrl |= BIT(dp->index);
mutex_lock(&priv->reg_lock);
res = yt921x_lag_set(priv, lag.id - 1, ctrl);
mutex_unlock(&priv->reg_lock);
return res;
}
static int
yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag,
struct netdev_lag_upper_info *info,
struct netlink_ext_ack *extack)
{
unsigned int members;
struct dsa_port *dp;
if (!lag.id)
return -EINVAL;
members = 0;
dsa_lag_foreach_port(dp, ds->dst, &lag)
/* Includes the port joining the LAG */
members++;
if (members > YT921X_LAG_PORT_NUM) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot offload more than 4 LAG ports");
return -EOPNOTSUPP;
}
if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
NL_SET_ERR_MSG_MOD(extack,
"Can only offload LAG using hash TX type");
return -EOPNOTSUPP;
}
if (info->hash_type != NETDEV_LAG_HASH_L2 &&
info->hash_type != NETDEV_LAG_HASH_L23 &&
info->hash_type != NETDEV_LAG_HASH_L34) {
NL_SET_ERR_MSG_MOD(extack,
"Can only offload L2 or L2+L3 or L3+L4 TX hash");
return -EOPNOTSUPP;
}
return 0;
}
static int
yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
struct netdev_lag_upper_info *info,
struct netlink_ext_ack *extack)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct dsa_port *dp;
bool unique_lag;
unsigned int i;
u32 ctrl;
int res;
res = yt921x_dsa_port_lag_check(ds, lag, info, extack);
if (res)
return res;
ctrl = 0;
switch (info->hash_type) {
case NETDEV_LAG_HASH_L34:
ctrl |= YT921X_LAG_HASH_IP_DST;
ctrl |= YT921X_LAG_HASH_IP_SRC;
ctrl |= YT921X_LAG_HASH_IP_PROTO;
ctrl |= YT921X_LAG_HASH_L4_DPORT;
ctrl |= YT921X_LAG_HASH_L4_SPORT;
break;
case NETDEV_LAG_HASH_L23:
ctrl |= YT921X_LAG_HASH_MAC_DA;
ctrl |= YT921X_LAG_HASH_MAC_SA;
ctrl |= YT921X_LAG_HASH_IP_DST;
ctrl |= YT921X_LAG_HASH_IP_SRC;
ctrl |= YT921X_LAG_HASH_IP_PROTO;
break;
case NETDEV_LAG_HASH_L2:
ctrl |= YT921X_LAG_HASH_MAC_DA;
ctrl |= YT921X_LAG_HASH_MAC_SA;
break;
default:
return -EOPNOTSUPP;
}
/* Check if we are the unique configured LAG */
unique_lag = true;
dsa_lags_foreach_id(i, ds->dst)
if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
unique_lag = false;
break;
}
mutex_lock(&priv->reg_lock);
do {
res = yt921x_lag_hash(priv, ctrl, unique_lag, extack);
if (res)
break;
ctrl = 0;
dsa_lag_foreach_port(dp, ds->dst, &lag)
ctrl |= BIT(dp->index);
res = yt921x_lag_set(priv, lag.id - 1, ctrl);
} while (0);
mutex_unlock(&priv->reg_lock);
return res;
}
static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
{
struct device *dev = to_device(priv);
@ -2880,6 +3062,9 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* mirror */
.port_mirror_del = yt921x_dsa_port_mirror_del,
.port_mirror_add = yt921x_dsa_port_mirror_add,
/* lag */
.port_lag_leave = yt921x_dsa_port_lag_leave,
.port_lag_join = yt921x_dsa_port_lag_join,
/* fdb */
.port_fdb_dump = yt921x_dsa_port_fdb_dump,
.port_fast_age = yt921x_dsa_port_fast_age,
@ -2976,6 +3161,7 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
ds->ageing_time_min = 1 * 5000;
ds->ageing_time_max = U16_MAX * 5000;
ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
ds->num_lag_ids = YT921X_LAG_NUM;
ds->num_ports = YT921X_PORT_NUM;
mdiodev_set_drvdata(mdiodev, priv);

View File

@ -370,6 +370,14 @@
#define YT921X_FILTER_PORTn(port) BIT(port)
#define YT921X_VLAN_EGR_FILTER 0x180598
#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port)
#define YT921X_LAG_GROUPn(n) (0x1805a8 + 4 * (n))
#define YT921X_LAG_GROUP_PORTS_M GENMASK(13, 3)
#define YT921X_LAG_GROUP_PORTS(x) FIELD_PREP(YT921X_LAG_GROUP_PORTS_M, (x))
#define YT921X_LAG_GROUP_MEMBER_NUM_M GENMASK(2, 0)
#define YT921X_LAG_GROUP_MEMBER_NUM(x) FIELD_PREP(YT921X_LAG_GROUP_MEMBER_NUM_M, (x))
#define YT921X_LAG_MEMBERnm(n, m) (0x1805b0 + 4 * (4 * (n) + (m)))
#define YT921X_LAG_MEMBER_PORT_M GENMASK(3, 0)
#define YT921X_LAG_MEMBER_PORT(x) FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x))
#define YT921X_CPU_COPY 0x180690
#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2)
#define YT921X_CPU_COPY_TO_INT_CPU BIT(1)
@ -414,6 +422,15 @@
#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4)
#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0)
#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x)
#define YT921X_LAG_HASH 0x210090
#define YT921X_LAG_HASH_L4_SPORT BIT(7)
#define YT921X_LAG_HASH_L4_DPORT BIT(6)
#define YT921X_LAG_HASH_IP_PROTO BIT(5)
#define YT921X_LAG_HASH_IP_SRC BIT(4)
#define YT921X_LAG_HASH_IP_DST BIT(3)
#define YT921X_LAG_HASH_MAC_SA BIT(2)
#define YT921X_LAG_HASH_MAC_DA BIT(1)
#define YT921X_LAG_HASH_SRC_PORT BIT(0)
#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port))
#define YT921X_PORT_VLAN_CTRL_SVLAN_PRI_EN BIT(31)
@ -458,6 +475,9 @@ enum yt921x_fdb_entry_status {
#define YT921X_MSTI_NUM 16
#define YT921X_LAG_NUM 2
#define YT921X_LAG_PORT_NUM 4
#define YT9215_MAJOR 0x9002
#define YT9218_MAJOR 0x9001