realtek: dsa: rtl93xx: Add link aggregation support

With this commit it is possible to create 802.3ad compatible bond
interface that is interoperable with other 802.3ad compatible switches.

Each trunk group can have maximum of 8 ports as members.
Hardware also supports trunking with stacked switches, however it is not
handled here and the driver only configures the local trunk.

rtl930x and rtl931x has minimal differences in trunk/lag

Co-developed-by: Sven Eckelmann <se@simonwunderlich.de>
Signed-off-by: Sven Eckelmann <se@simonwunderlich.de>
Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
Link: https://github.com/openwrt/openwrt/pull/21740
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Harshal Gohel 2026-01-27 11:33:07 +00:00 committed by Hauke Mehrtens
parent f556b54a2c
commit 05096060ca
4 changed files with 509 additions and 0 deletions

View file

@ -528,6 +528,148 @@ int rtldsa_83xx_lag_setup_algomask(struct rtl838x_switch_priv *priv, int group,
return priv->r->lag_set_distribution_algorithm(priv, group, algoidx, algomsk);
}
static int rtldsa_93xx_lag_set_group2ports(struct rtl838x_switch_priv *priv, int group,
struct netdev_lag_upper_info *info)
{
DECLARE_BITMAP(ports, ARRAY_SIZE(priv->ports));
struct rtldsa_93xx_lag_entry e;
unsigned int table_pos = 0;
u8 num_of_lag_ports = 0;
u8 group_ports[8];
u32 data[3];
int i;
/* Read lag table using Table control register 2 */
struct table_reg *r = priv->r->lag_table();
rtl_table_read(r, group);
bitmap_clear(ports, 0, ARRAY_SIZE(priv->ports));
bitmap_from_arr64(ports, &priv->lags_port_members[group],
ARRAY_SIZE(priv->ports));
for (i = 0; i < 3; i++)
data[i] = sw_r32(rtl_table_data(r, i));
priv->r->lag_fill_data(data, &e);
num_of_lag_ports = bitmap_weight(ports, ARRAY_SIZE(priv->ports));
if (num_of_lag_ports > ARRAY_SIZE(group_ports)) {
pr_err("%s: Number of LAG ports too high: %u", __func__,
num_of_lag_ports);
return -ENOSPC;
}
memset(group_ports, 0x3f, sizeof(group_ports));
table_pos = 0;
for_each_set_bit(i, ports, ARRAY_SIZE(priv->ports)) {
group_ports[table_pos] = i;
table_pos++;
}
/* Remove tx disabled ports */
num_of_lag_ports = table_pos;
e.trk_dev0 = 0;
e.trk_port0 = group_ports[0];
e.trk_dev1 = 0;
e.trk_port1 = group_ports[1];
e.trk_dev2 = 0;
e.trk_port2 = group_ports[2];
e.trk_dev3 = 0;
e.trk_port3 = group_ports[3];
e.trk_dev4 = 0;
e.trk_port4 = group_ports[4];
e.trk_dev5 = 0;
e.trk_port5 = group_ports[5];
e.trk_dev6 = 0;
e.trk_port6 = group_ports[6];
e.trk_dev7 = 0;
e.trk_port7 = group_ports[7];
e.num_tx_candi = num_of_lag_ports;
/* set hash_mask_idx to 0 if we are deleting lag group */
if (info) {
if (info->hash_type == NETDEV_LAG_HASH_L2) {
e.l2_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L2;
e.ip4_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L2;
e.ip6_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L2;
} else if (info->hash_type == NETDEV_LAG_HASH_L23) {
e.l2_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L23;
e.ip4_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L23;
e.ip6_hash_mask_idx = RTL93XX_HASH_MASK_INDEX_L23;
} else {
return -EOPNOTSUPP;
}
}
priv->r->lag_write_data(data, &e);
for (i = 0; i < 3; i++)
sw_w32(data[i], rtl_table_data(r, i));
rtl_table_write(r, group);
rtl_table_release(r);
return 0;
}
static inline void rtldsa_93xx_lag_set_local_group2ports(struct rtl838x_switch_priv *priv, int group,
u64 ports)
{
priv->r->set_port_reg_be(ports, priv->r->trk_mbr_ctr(group));
}
int rtldsa_93xx_lag_set_port_members(struct rtl838x_switch_priv *priv, int group,
u64 members, struct netdev_lag_upper_info *info)
{
DECLARE_BITMAP(affected_members, ARRAY_SIZE(priv->ports));
bool valid_group;
u64 old_members;
u64 affected;
size_t port;
int ret;
/* calculate modifications of the LAG group */
old_members = priv->lags_port_members[group];
priv->lags_port_members[group] = members;
affected = old_members | priv->lags_port_members[group];
bitmap_clear(affected_members, 0, ARRAY_SIZE(priv->ports));
bitmap_from_arr64(affected_members, &affected, BITS_PER_TYPE(affected));
valid_group = __sw_hweight64(priv->lags_port_members[group]);
/* apply global group and port settings */
ret = rtldsa_93xx_lag_set_group2ports(priv, group, info);
if (ret)
return ret;
for_each_set_bit(port, affected_members, ARRAY_SIZE(priv->ports)) {
bool valid = priv->lags_port_members[group] & BIT_ULL(port);
priv->r->lag_set_port2group(group, port, valid);
}
/* apply local group and port settings */
priv->r->lag_set_local_group_id(group, group, valid_group);
rtldsa_93xx_lag_set_local_group2ports(priv, group, priv->lags_port_members[group]);
for_each_set_bit(port, affected_members, ARRAY_SIZE(priv->ports)) {
bool valid = priv->lags_port_members[group] & BIT_ULL(port);
priv->r->lag_set_local_port2group(group, port, valid);
}
/* write lag table (and maybe additional information) to SRAM */
priv->r->lag_sync_tables();
return 0;
}
// Currently Unused
// /* Allocate a 64 bit octet counter located in the LOG HW table */
// static int rtl83xx_octet_cntr_alloc(struct rtl838x_switch_priv *priv)

View file

@ -406,13 +406,98 @@
#define RTL839X_TRK_HASH_IDX_CTRL (0x2280)
#define RTL839X_TRK_HASH_CTRL (0x2284)
#define RTL930X_LOCAL_PORT_TRK_MAP (0xD0C8)
#define RTL930X_TRK_ID_CTRL (0xA3A8)
#define RTL930X_TRK_MBR_CTRL (0xA41C)
#define RTL930X_TRK_HASH_CTRL (0x9F80)
#define RTL930X_TRK_CTRL (0x9F88)
#define RTL930X_TRK_SHIFT_CTRL (0x9F8C)
#define RTL930X_TRK_LOCAL_TBL_REFRESH (0x9F90)
#define RTL930X_TRK_LOCAL_TBL (0x9F94)
#define RTL930X_TRK_STK_CTRL (0xA07C)
#define RTL930X_TRK_ID_CTRL_TRK_VALID BIT(6)
#define RTL930X_TRK_ID_CTRL_TRK_ID GENMASK(5, 0)
#define RTL930X_LOCAL_PORT_TRK_MAP_IS_TRK_MBR BIT(6)
#define RTL930X_LOCAL_PORT_TRK_MAP_TRK_ID GENMASK(5, 0)
#define RTL930X_SRC_TRK_MAP_TRK_VALID BIT(31)
#define RTL930X_SRC_TRK_MAP_TRK_ID GENMASK(30, 25)
#define RTL931X_LOCAL_PORT_TRK_MAP (0x4CAC)
#define RTL931X_TRK_ID_CTRL (0xB800)
#define RTL931X_TRK_MBR_CTRL (0xB8D0)
#define RTL931X_TRK_HASH_CTRL (0xBA70)
#define RTL931X_TRK_CTRL (0xBA78)
#define RTL931X_TRK_SHIFT_CTRL (0xBA7C)
#define RTL931X_TRK_LOCAL_TBL_REFRESH (0xBA80)
#define RTL931X_TRK_LOCAL_TBL (0xBA84)
#define RTL931X_TRK_STK_CTRL (0xBE94)
#define RLT931X_TRK_ID_CTRL_TRK_ID GENMASK(6, 0)
#define RTL931X_TRK_ID_CTRL_TRK_VALID BIT(7)
#define RTL931X_LOCAL_PORT_TRK_MAP_IS_TRK_MBR BIT(7)
#define RTL931X_LOCAL_PORT_TRK_MAP_TRK_ID GENMASK(6, 0)
#define RTL931X_SRC_TRK_MAP_TRK_ID GENMASK(30, 24)
#define RTL931X_SRC_TRK_MAP_TRK_ID_VALID BIT(31)
#define GENMASK_MOD(high, low) GENMASK((high) % 32, (low) % 32)
#define BIT_MOD(bit) BIT((bit) % 32)
/* RTL930X LAG Table offsets */
#define RTL930X_LAG_NUM_TX_CANDI GENMASK_MOD(92, 89)
#define RTL930X_LAG_L2_HASH_MSK_IDX BIT_MOD(88)
#define RTL930X_LAG_IP4_HASH_MSK_IDX BIT_MOD(87)
#define RTL930X_LAG_IP6_HASH_MSK_IDX BIT_MOD(86)
#define RTL930X_LAG_SEP_DLF_BCAST_EN BIT_MOD(85)
#define RTL930X_LAG_SEP_KWN_MC_EN BIT_MOD(84)
#define RTL930X_LAG_TRK_DEV7 GENMASK_MOD(83, 80)
#define RTL930X_LAG_TRK_PORT7 GENMASK_MOD(79, 74)
#define RTL930X_LAG_TRK_DEV6 GENMASK_MOD(73, 70)
#define RTL930X_LAG_TRK_PORT6 GENMASK_MOD(69, 64)
#define RTL930X_LAG_TRK_DEV5 GENMASK_MOD(61, 58)
#define RTL930X_LAG_TRK_PORT5 GENMASK_MOD(57, 52)
#define RTL930X_LAG_TRK_DEV4 GENMASK_MOD(51, 48)
#define RTL930X_LAG_TRK_PORT4 GENMASK_MOD(47, 42)
#define RTL930X_LAG_TRK_DEV3 GENMASK_MOD(41, 38)
#define RTL930X_LAG_TRK_PORT3 GENMASK_MOD(37, 32)
#define RTL930X_LAG_TRK_DEV2 GENMASK_MOD(29, 26)
#define RTL930X_LAG_TRK_PORT2 GENMASK_MOD(25, 20)
#define RTL930X_LAG_TRK_DEV1 GENMASK_MOD(19, 16)
#define RTL930X_LAG_TRK_PORT1 GENMASK_MOD(15, 10)
#define RTL930X_LAG_TRK_DEV0 GENMASK_MOD(9, 6)
#define RTL930X_LAG_TRK_PORT0 GENMASK_MOD(5, 0)
/* RTL931X LAG Table offsets */
#define RTL931X_LAG_NUM_TX_CANDI GENMASK_MOD(92, 89)
#define RTL931X_LAG_L2_HASH_MSK_IDX BIT_MOD(88)
#define RTL931X_LAG_IP4_HASH_MSK_IDX BIT_MOD(87)
#define RTL931X_LAG_IP6_HASH_MSK_IDX BIT_MOD(86)
#define RTL931X_LAG_SEP_FLOOD_EN BIT_MOD(85)
#define RTL931X_LAG_SEP_KWN_MC_EN BIT_MOD(84)
#define RTL931X_LAG_TRK_DEV7 GENMASK_MOD(83, 80)
#define RTL931X_LAG_TRK_PORT7 GENMASK_MOD(79, 74)
#define RTL931X_LAG_TRK_DEV6 GENMASK_MOD(73, 70)
#define RTL931X_LAG_TRK_PORT6 GENMASK_MOD(69, 64)
#define RTL931X_LAG_TRK_DEV5 GENMASK_MOD(61, 58)
#define RTL931X_LAG_TRK_PORT5 GENMASK_MOD(57, 52)
#define RTL931X_LAG_TRK_DEV4 GENMASK_MOD(51, 48)
#define RTL931X_LAG_TRK_PORT4 GENMASK_MOD(47, 42)
#define RTL931X_LAG_TRK_DEV3 GENMASK_MOD(41, 38)
#define RTL931X_LAG_TRK_PORT3 GENMASK_MOD(37, 32)
#define RTL931X_LAG_TRK_DEV2 GENMASK_MOD(29, 26)
#define RTL931X_LAG_TRK_PORT2 GENMASK_MOD(25, 20)
#define RTL931X_LAG_TRK_DEV1 GENMASK_MOD(19, 16)
#define RTL931X_LAG_TRK_PORT1 GENMASK_MOD(15, 10)
#define RTL931X_LAG_TRK_DEV0 GENMASK_MOD(9, 6)
#define RTL931X_LAG_TRK_PORT0 GENMASK_MOD(5, 0)
/* Attack prevention */
#define RTL838X_ATK_PRVNT_PORT_EN (0x5B00)
@ -886,6 +971,36 @@ struct rtldsa_counter_state {
struct rtnl_link_stats64 link_stat;
};
struct rtldsa_93xx_lag_entry {
u32 trk_port0:6;
u32 trk_dev0:4;
u32 trk_port1:6;
u32 trk_dev1:4;
u32 trk_port2:6;
u32 trk_dev2:4;
u32 trk_port3:6;
u32 trk_dev3:4;
u32 trk_port4:6;
u32 trk_dev4:4;
u32 trk_port5:6;
u32 trk_dev5:4;
u32 trk_port6:6;
u32 trk_dev6:4;
u32 trk_port7:6;
u32 trk_dev7:4;
u32 sep_kwn_mc_en:1;
union {
// for rtl930x
u32 sep_dlf_bcast_en:1;
// for rtl931x
u32 sep_flood_en:1;
} flood_dlf_bcast;
u32 ip6_hash_mask_idx:1;
u32 ip4_hash_mask_idx:1;
u32 l2_hash_mask_idx:1;
u32 num_tx_candi:4;
};
struct rtldsa_port {
bool enable:1;
bool phy_is_integrated:1;
@ -1387,6 +1502,13 @@ struct rtldsa_config {
int (*lag_set_distribution_algorithm)(struct rtl838x_switch_priv *priv,
int group, int algoidx,
u32 algomask);
void (*lag_set_local_group_id)(int local_group, int global_group, bool valid);
void (*lag_write_data)(u32 data[], struct rtldsa_93xx_lag_entry *e);
void (*lag_fill_data)(u32 data[], struct rtldsa_93xx_lag_entry *e);
void (*lag_set_local_port2group)(int group, int port, bool valid);
void (*lag_set_port2group)(int group, int port, bool valid);
struct table_reg* (*lag_table)(void);
void (*lag_sync_tables)(void);
};
struct rtl838x_switch_priv {
@ -1463,6 +1585,8 @@ void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv);
void rtldsa_93xx_lag_switch_init(struct rtl838x_switch_priv *priv);
int rtldsa_93xx_lag_set_distribution_algorithm(struct rtl838x_switch_priv *priv,
int group, int algoidx, u32 algomsk);
int rtldsa_93xx_lag_set_port_members(struct rtl838x_switch_priv *priv, int group,
u64 members, struct netdev_lag_upper_info *info);
void rtldsa_93xx_prepare_lag_fdb(struct rtl838x_l2_entry *e, int lag_group);

View file

@ -532,6 +532,118 @@ static void rtldsa_930x_enable_flood(int port, bool enable)
RTL930X_L2_LRN_PORT_CONSTRT_CTRL + port * 4);
}
static void rtldsa_930x_lag_set_port2group(int group, int port, bool valid)
{
struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 8);
u32 mask = valid ? RTL930X_SRC_TRK_MAP_TRK_VALID : 0;
rtl_table_read(r, port);
mask |= FIELD_PREP(RTL930X_SRC_TRK_MAP_TRK_ID, group); // Update TRK Field.
sw_w32(mask, rtl_table_data(r, 0));
rtl_table_write(r, port);
rtl_table_release(r);
}
/* Write data from the data buffer into the lag-entry strucure */
static void rtldsa_930x_lag_fill_data(u32 data[], struct rtldsa_93xx_lag_entry *e)
{
/* 95-64 */
e->num_tx_candi = FIELD_GET(RTL930X_LAG_NUM_TX_CANDI, data[0]);
e->l2_hash_mask_idx = FIELD_GET(RTL930X_LAG_L2_HASH_MSK_IDX, data[0]);
e->ip4_hash_mask_idx = FIELD_GET(RTL930X_LAG_IP4_HASH_MSK_IDX, data[0]);
e->ip6_hash_mask_idx = FIELD_GET(RTL930X_LAG_IP6_HASH_MSK_IDX, data[0]);
e->flood_dlf_bcast.sep_dlf_bcast_en = FIELD_GET(RTL930X_LAG_SEP_DLF_BCAST_EN, data[0]);
e->sep_kwn_mc_en = FIELD_GET(RTL930X_LAG_SEP_KWN_MC_EN, data[0]);
e->trk_dev7 = FIELD_GET(RTL930X_LAG_TRK_DEV7, data[0]);
e->trk_port7 = FIELD_GET(RTL930X_LAG_TRK_PORT7, data[0]);
e->trk_dev6 = FIELD_GET(RTL930X_LAG_TRK_DEV6, data[0]);
e->trk_port6 = FIELD_GET(RTL930X_LAG_TRK_PORT6, data[0]);
/* 63-32 */
e->trk_dev5 = FIELD_GET(RTL930X_LAG_TRK_DEV5, data[1]);
e->trk_port5 = FIELD_GET(RTL930X_LAG_TRK_PORT5, data[1]);
e->trk_dev4 = FIELD_GET(RTL930X_LAG_TRK_DEV4, data[1]);
e->trk_port4 = FIELD_GET(RTL930X_LAG_TRK_PORT4, data[1]);
e->trk_dev3 = FIELD_GET(RTL930X_LAG_TRK_DEV3, data[1]);
e->trk_port3 = FIELD_GET(RTL930X_LAG_TRK_PORT3, data[1]);
/* 31-0 */
e->trk_dev2 = FIELD_GET(RTL930X_LAG_TRK_DEV2, data[2]);
e->trk_port2 = FIELD_GET(RTL930X_LAG_TRK_PORT2, data[2]);
e->trk_dev1 = FIELD_GET(RTL930X_LAG_TRK_DEV1, data[2]);
e->trk_port1 = FIELD_GET(RTL930X_LAG_TRK_PORT1, data[2]);
e->trk_dev0 = FIELD_GET(RTL930X_LAG_TRK_DEV0, data[2]);
e->trk_port0 = FIELD_GET(RTL930X_LAG_TRK_PORT0, data[2]);
}
/* Write lag-entry data into buffer */
static void rtldsa_930x_lag_write_data(u32 data[], struct rtldsa_93xx_lag_entry *e)
{
/* 95-64 */
data[0] = FIELD_PREP(RTL930X_LAG_NUM_TX_CANDI, e->num_tx_candi);
data[0] |= FIELD_PREP(RTL930X_LAG_L2_HASH_MSK_IDX, e->l2_hash_mask_idx);
data[0] |= FIELD_PREP(RTL930X_LAG_IP4_HASH_MSK_IDX, e->ip4_hash_mask_idx);
data[0] |= FIELD_PREP(RTL930X_LAG_IP6_HASH_MSK_IDX, e->ip6_hash_mask_idx);
data[0] |= FIELD_PREP(RTL930X_LAG_SEP_DLF_BCAST_EN, e->flood_dlf_bcast.sep_dlf_bcast_en);
data[0] |= FIELD_PREP(RTL930X_LAG_SEP_KWN_MC_EN, e->sep_kwn_mc_en);
data[0] |= FIELD_PREP(RTL930X_LAG_TRK_DEV7, e->trk_dev7);
data[0] |= FIELD_PREP(RTL930X_LAG_TRK_PORT7, e->trk_port7);
data[0] |= FIELD_PREP(RTL930X_LAG_TRK_DEV6, e->trk_dev6);
data[0] |= FIELD_PREP(RTL930X_LAG_TRK_PORT6, e->trk_port6);
/* 63-32 */
data[1] = FIELD_PREP(RTL930X_LAG_TRK_DEV5, e->trk_dev5);
data[1] |= FIELD_PREP(RTL930X_LAG_TRK_PORT5, e->trk_port5);
data[1] |= FIELD_PREP(RTL930X_LAG_TRK_DEV4, e->trk_dev4);
data[1] |= FIELD_PREP(RTL930X_LAG_TRK_PORT4, e->trk_port4);
data[1] |= FIELD_PREP(RTL930X_LAG_TRK_DEV3, e->trk_dev3);
data[1] |= FIELD_PREP(RTL930X_LAG_TRK_PORT3, e->trk_port3);
/* 31-0 */
data[2] = FIELD_PREP(RTL930X_LAG_TRK_DEV2, e->trk_dev2);
data[2] |= FIELD_PREP(RTL930X_LAG_TRK_PORT2, e->trk_port2);
data[2] |= FIELD_PREP(RTL930X_LAG_TRK_DEV1, e->trk_dev1);
data[2] |= FIELD_PREP(RTL930X_LAG_TRK_PORT1, e->trk_port1);
data[2] |= FIELD_PREP(RTL930X_LAG_TRK_DEV0, e->trk_dev0);
data[2] |= FIELD_PREP(RTL930X_LAG_TRK_PORT0, e->trk_port0);
}
static void rtldsa_930x_lag_set_local_group_id(int local_group, int global_group, bool valid)
{
u32 mask = 0;
mask |= valid ? RTL930X_TRK_ID_CTRL_TRK_VALID : 0;
mask |= FIELD_PREP(RTL930X_TRK_ID_CTRL_TRK_ID, global_group);
sw_w32(mask, RTL930X_TRK_ID_CTRL + (4 * local_group));
}
static void rtldsa_930x_lag_set_local_port2group(int group, int port, bool valid)
{
u32 mask = 0;
mask |= valid ? RTL930X_LOCAL_PORT_TRK_MAP_IS_TRK_MBR : 0;
mask |= FIELD_PREP(RTL930X_LOCAL_PORT_TRK_MAP_TRK_ID, group);
sw_w32(mask, RTL930X_LOCAL_PORT_TRK_MAP + (4 * port));
}
static void rtldsa_930x_lag_sync_tables(void)
{
u32 val;
int ret;
sw_w32(BIT(0), RTL930X_TRK_LOCAL_TBL_REFRESH);
ret = readx_poll_timeout(sw_r32, RTL930X_TRK_LOCAL_TBL_REFRESH, val,
!(val & BIT(0)), 20, 10000);
if (ret)
pr_err("%s: timeout\n", __func__);
}
static struct table_reg *rtldsa_930x_lag_table(void)
{
return rtl_table_get(RTL9300_TBL_0, 7);
}
static int rtldsa_930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, int port, u32 port_state[])
{
int idx = 1 - ((port + 3) / 16);
@ -2806,4 +2918,11 @@ const struct rtldsa_config rtldsa_930x_cfg = {
.lag_switch_init = rtldsa_93xx_lag_switch_init,
.lag_set_port_members = rtldsa_93xx_lag_set_port_members,
.lag_set_distribution_algorithm = rtldsa_93xx_lag_set_distribution_algorithm,
.lag_set_local_group_id = rtldsa_930x_lag_set_local_group_id,
.lag_write_data = rtldsa_930x_lag_write_data,
.lag_fill_data = rtldsa_930x_lag_fill_data,
.lag_set_local_port2group = rtldsa_930x_lag_set_local_port2group,
.lag_set_port2group = rtldsa_930x_lag_set_port2group,
.lag_sync_tables = rtldsa_930x_lag_sync_tables,
.lag_table = rtldsa_930x_lag_table,
};

View file

@ -1745,6 +1745,123 @@ static void rtldsa_931x_led_init(struct rtl838x_switch_priv *priv)
dev_dbg(dev, "%08x: %08x\n", 0xbb000600 + i * 4, sw_r32(0x0600 + i * 4));
}
static void rtldsa_931x_lag_set_port2group(int group, int port, bool valid)
{
u32 trk_id_valid = valid ? RTL931X_SRC_TRK_MAP_TRK_ID_VALID : 0;
struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 13);
u32 mask = 0;
rtl_table_read(r, port);
mask |= trk_id_valid;
/* Update TRK Field */
mask |= FIELD_PREP(RTL931X_SRC_TRK_MAP_TRK_ID, group);
sw_w32(mask, rtl_table_data(r, 0));
rtl_table_write(r, port);
rtl_table_release(r);
}
/* Write data from the data buffer into the lag-entry strucure */
static void rtldsa_931x_lag_fill_data(u32 data[], struct rtldsa_93xx_lag_entry *e)
{
/* 95-64 */
e->num_tx_candi = FIELD_GET(RTL931X_LAG_NUM_TX_CANDI, data[0]);
e->l2_hash_mask_idx = FIELD_GET(RTL931X_LAG_L2_HASH_MSK_IDX, data[0]);
e->ip4_hash_mask_idx = FIELD_GET(RTL931X_LAG_IP4_HASH_MSK_IDX, data[0]);
e->ip6_hash_mask_idx = FIELD_GET(RTL931X_LAG_IP6_HASH_MSK_IDX, data[0]);
e->flood_dlf_bcast.sep_flood_en = FIELD_GET(RTL931X_LAG_SEP_FLOOD_EN, data[0]);
e->sep_kwn_mc_en = FIELD_GET(RTL931X_LAG_SEP_KWN_MC_EN, data[0]);
e->trk_dev7 = FIELD_GET(RTL931X_LAG_TRK_DEV7, data[0]);
e->trk_port7 = FIELD_GET(RTL931X_LAG_TRK_PORT7, data[0]);
e->trk_dev6 = FIELD_GET(RTL931X_LAG_TRK_DEV6, data[0]);
e->trk_port6 = FIELD_GET(RTL931X_LAG_TRK_PORT6, data[0]);
/* 63-32 */
e->trk_dev5 = FIELD_GET(RTL931X_LAG_TRK_DEV5, data[1]);
e->trk_port5 = FIELD_GET(RTL931X_LAG_TRK_PORT5, data[1]);
e->trk_dev4 = FIELD_GET(RTL931X_LAG_TRK_DEV4, data[1]);
e->trk_port4 = FIELD_GET(RTL931X_LAG_TRK_PORT4, data[1]);
e->trk_dev3 = FIELD_GET(RTL931X_LAG_TRK_DEV3, data[1]);
e->trk_port3 = FIELD_GET(RTL931X_LAG_TRK_PORT3, data[1]);
/* 31-0 */
e->trk_dev2 = FIELD_GET(RTL931X_LAG_TRK_DEV2, data[2]);
e->trk_port2 = FIELD_GET(RTL931X_LAG_TRK_PORT2, data[2]);
e->trk_dev1 = FIELD_GET(RTL931X_LAG_TRK_DEV1, data[2]);
e->trk_port1 = FIELD_GET(RTL931X_LAG_TRK_PORT1, data[2]);
e->trk_dev0 = FIELD_GET(RTL931X_LAG_TRK_DEV0, data[2]);
e->trk_port0 = FIELD_GET(RTL931X_LAG_TRK_PORT0, data[2]);
}
/* Write lag-entry data into buffer */
static void rtldsa_931x_lag_write_data(u32 data[], struct rtldsa_93xx_lag_entry *e)
{
/* 95-64 */
data[0] = FIELD_PREP(RTL931X_LAG_NUM_TX_CANDI, e->num_tx_candi);
data[0] |= FIELD_PREP(RTL931X_LAG_L2_HASH_MSK_IDX, e->l2_hash_mask_idx);
data[0] |= FIELD_PREP(RTL931X_LAG_IP4_HASH_MSK_IDX, e->ip4_hash_mask_idx);
data[0] |= FIELD_PREP(RTL931X_LAG_IP6_HASH_MSK_IDX, e->ip6_hash_mask_idx);
data[0] |= FIELD_PREP(RTL931X_LAG_SEP_FLOOD_EN, e->flood_dlf_bcast.sep_flood_en);
data[0] |= FIELD_PREP(RTL931X_LAG_SEP_KWN_MC_EN, e->sep_kwn_mc_en);
data[0] |= FIELD_PREP(RTL931X_LAG_TRK_DEV7, e->trk_dev7);
data[0] |= FIELD_PREP(RTL931X_LAG_TRK_PORT7, e->trk_port7);
data[0] |= FIELD_PREP(RTL931X_LAG_TRK_DEV6, e->trk_dev6);
data[0] |= FIELD_PREP(RTL931X_LAG_TRK_PORT6, e->trk_port6);
/* 63-32 */
data[1] = FIELD_PREP(RTL931X_LAG_TRK_DEV5, e->trk_dev5);
data[1] |= FIELD_PREP(RTL931X_LAG_TRK_PORT5, e->trk_port5);
data[1] |= FIELD_PREP(RTL931X_LAG_TRK_DEV4, e->trk_dev4);
data[1] |= FIELD_PREP(RTL931X_LAG_TRK_PORT4, e->trk_port4);
data[1] |= FIELD_PREP(RTL931X_LAG_TRK_DEV3, e->trk_dev3);
data[1] |= FIELD_PREP(RTL931X_LAG_TRK_PORT3, e->trk_port3);
/* 31-0 */
data[2] = FIELD_PREP(RTL931X_LAG_TRK_DEV2, e->trk_dev2);
data[2] |= FIELD_PREP(RTL931X_LAG_TRK_PORT2, e->trk_port2);
data[2] |= FIELD_PREP(RTL931X_LAG_TRK_DEV1, e->trk_dev1);
data[2] |= FIELD_PREP(RTL931X_LAG_TRK_PORT1, e->trk_port1);
data[2] |= FIELD_PREP(RTL931X_LAG_TRK_DEV0, e->trk_dev0);
data[2] |= FIELD_PREP(RTL931X_LAG_TRK_PORT0, e->trk_port0);
}
static void rtldsa_931x_lag_set_local_group_id(int local_group, int global_group, bool valid)
{
u32 mask = 0;
mask |= valid ? RTL931X_TRK_ID_CTRL_TRK_VALID : 0;
mask |= FIELD_PREP(RLT931X_TRK_ID_CTRL_TRK_ID, global_group);
sw_w32(mask, RTL931X_TRK_ID_CTRL + (4 * local_group));
}
static void rtldsa_931x_lag_set_local_port2group(int group, int port, bool valid)
{
u32 mask = 0;
mask |= valid ? RTL931X_LOCAL_PORT_TRK_MAP_IS_TRK_MBR : 0;
mask |= FIELD_PREP(RTL931X_LOCAL_PORT_TRK_MAP_TRK_ID, group);
sw_w32(mask, RTL931X_LOCAL_PORT_TRK_MAP + (4 * port));
}
static void rtldsa_931x_lag_sync_tables(void)
{
u32 val;
int ret;
sw_w32(BIT(0), RTL931X_TRK_LOCAL_TBL_REFRESH);
ret = readx_poll_timeout(sw_r32, RTL931X_TRK_LOCAL_TBL_REFRESH, val,
!(val & BIT(0)), 20, 10000);
if (ret)
pr_err("%s: timeout\n", __func__);
}
static struct table_reg *rtldsa_931x_lag_table(void)
{
return rtl_table_get(RTL9310_TBL_2, 0);
}
static u64 rtldsa_931x_stat_port_table_read(int port, unsigned int mib_size,
unsigned int mib_offset, bool is_pvt)
{
@ -1930,4 +2047,11 @@ const struct rtldsa_config rtldsa_931x_cfg = {
.lag_switch_init = rtldsa_93xx_lag_switch_init,
.lag_set_port_members = rtldsa_93xx_lag_set_port_members,
.lag_set_distribution_algorithm = rtldsa_93xx_lag_set_distribution_algorithm,
.lag_set_local_group_id = rtldsa_931x_lag_set_local_group_id,
.lag_write_data = rtldsa_931x_lag_write_data,
.lag_fill_data = rtldsa_931x_lag_fill_data,
.lag_set_local_port2group = rtldsa_931x_lag_set_local_port2group,
.lag_set_port2group = rtldsa_931x_lag_set_port2group,
.lag_sync_tables = rtldsa_931x_lag_sync_tables,
.lag_table = rtldsa_931x_lag_table,
};