mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-07 22:42:04 +00:00
net: dsa: vsc73xx: implement FDB operations
This commit introduces implementations of three functions: .port_fdb_dump .port_fdb_add .port_fdb_del The FDB database organization is the same as in other old Vitesse chips: It has 2048 rows and 4 columns (buckets). The row index is calculated by the hash function 'vsc73xx_calc_hash' and the FDB entry must be placed exactly into row[hash]. The chip selects the bucket number by itself. Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> Link: https://patch.msgid.link/20240827123938.582789-1-paweldembicki@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
da4f3b72c8
commit
075e3d30e4
@ -46,6 +46,8 @@
|
||||
#define VSC73XX_BLOCK_MII_EXTERNAL 0x1 /* External MDIO subblock */
|
||||
|
||||
#define CPU_PORT 6 /* CPU port */
|
||||
#define VSC73XX_NUM_FDB_ROWS 2048
|
||||
#define VSC73XX_NUM_BUCKETS 4
|
||||
|
||||
/* MAC Block registers */
|
||||
#define VSC73XX_MAC_CFG 0x00
|
||||
@ -197,6 +199,40 @@
|
||||
#define VSC73XX_SRCMASKS_MIRROR BIT(26)
|
||||
#define VSC73XX_SRCMASKS_PORTS_MASK GENMASK(7, 0)
|
||||
|
||||
#define VSC73XX_MACHDATA_VID GENMASK(27, 16)
|
||||
#define VSC73XX_MACHDATA_MAC0 GENMASK(15, 8)
|
||||
#define VSC73XX_MACHDATA_MAC1 GENMASK(7, 0)
|
||||
#define VSC73XX_MACLDATA_MAC2 GENMASK(31, 24)
|
||||
#define VSC73XX_MACLDATA_MAC3 GENMASK(23, 16)
|
||||
#define VSC73XX_MACLDATA_MAC4 GENMASK(15, 8)
|
||||
#define VSC73XX_MACLDATA_MAC5 GENMASK(7, 0)
|
||||
|
||||
#define VSC73XX_HASH0_VID_FROM_MASK GENMASK(5, 0)
|
||||
#define VSC73XX_HASH0_MAC0_FROM_MASK GENMASK(7, 4)
|
||||
#define VSC73XX_HASH1_MAC0_FROM_MASK GENMASK(3, 0)
|
||||
#define VSC73XX_HASH1_MAC1_FROM_MASK GENMASK(7, 1)
|
||||
#define VSC73XX_HASH2_MAC1_FROM_MASK BIT(0)
|
||||
#define VSC73XX_HASH2_MAC2_FROM_MASK GENMASK(7, 0)
|
||||
#define VSC73XX_HASH2_MAC3_FROM_MASK GENMASK(7, 6)
|
||||
#define VSC73XX_HASH3_MAC3_FROM_MASK GENMASK(5, 0)
|
||||
#define VSC73XX_HASH3_MAC4_FROM_MASK GENMASK(7, 3)
|
||||
#define VSC73XX_HASH4_MAC4_FROM_MASK GENMASK(2, 0)
|
||||
|
||||
#define VSC73XX_HASH0_VID_TO_MASK GENMASK(9, 4)
|
||||
#define VSC73XX_HASH0_MAC0_TO_MASK GENMASK(3, 0)
|
||||
#define VSC73XX_HASH1_MAC0_TO_MASK GENMASK(10, 7)
|
||||
#define VSC73XX_HASH1_MAC1_TO_MASK GENMASK(6, 0)
|
||||
#define VSC73XX_HASH2_MAC1_TO_MASK BIT(10)
|
||||
#define VSC73XX_HASH2_MAC2_TO_MASK GENMASK(9, 2)
|
||||
#define VSC73XX_HASH2_MAC3_TO_MASK GENMASK(1, 0)
|
||||
#define VSC73XX_HASH3_MAC3_TO_MASK GENMASK(10, 5)
|
||||
#define VSC73XX_HASH3_MAC4_TO_MASK GENMASK(4, 0)
|
||||
#define VSC73XX_HASH4_MAC4_TO_MASK GENMASK(10, 8)
|
||||
|
||||
#define VSC73XX_MACTINDX_SHADOW BIT(13)
|
||||
#define VSC73XX_MACTINDX_BUCKET_MSK GENMASK(12, 11)
|
||||
#define VSC73XX_MACTINDX_INDEX_MSK GENMASK(10, 0)
|
||||
|
||||
#define VSC73XX_MACACCESS_CPU_COPY BIT(14)
|
||||
#define VSC73XX_MACACCESS_FWD_KILL BIT(13)
|
||||
#define VSC73XX_MACACCESS_IGNORE_VLAN BIT(12)
|
||||
@ -332,6 +368,13 @@ struct vsc73xx_counter {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct vsc73xx_fdb {
|
||||
u16 vid;
|
||||
u8 port;
|
||||
u8 mac[ETH_ALEN];
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/* Counters are named according to the MIB standards where applicable.
|
||||
* Some counters are custom, non-standard. The standard counters are
|
||||
* named in accordance with RFC2819, RFC2021 and IEEE Std 802.3-2002 Annex
|
||||
@ -804,6 +847,7 @@ static int vsc73xx_setup(struct dsa_switch *ds)
|
||||
|
||||
ds->untag_bridge_pvid = true;
|
||||
ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES;
|
||||
ds->fdb_isolation = true;
|
||||
|
||||
/* Issue RESET */
|
||||
vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GLORESET,
|
||||
@ -1854,6 +1898,312 @@ static void vsc73xx_port_stp_state_set(struct dsa_switch *ds, int port,
|
||||
vsc73xx_refresh_fwd_map(ds, port, state);
|
||||
}
|
||||
|
||||
static u16 vsc73xx_calc_hash(const unsigned char *addr, u16 vid)
|
||||
{
|
||||
/* VID 5-0, MAC 47-44 */
|
||||
u16 hash = FIELD_PREP(VSC73XX_HASH0_VID_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH0_VID_FROM_MASK, vid)) |
|
||||
FIELD_PREP(VSC73XX_HASH0_MAC0_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH0_MAC0_FROM_MASK, addr[0]));
|
||||
/* MAC 43-33 */
|
||||
hash ^= FIELD_PREP(VSC73XX_HASH1_MAC0_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH1_MAC0_FROM_MASK, addr[0])) |
|
||||
FIELD_PREP(VSC73XX_HASH1_MAC1_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH1_MAC1_FROM_MASK, addr[1]));
|
||||
/* MAC 32-22 */
|
||||
hash ^= FIELD_PREP(VSC73XX_HASH2_MAC1_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH2_MAC1_FROM_MASK, addr[1])) |
|
||||
FIELD_PREP(VSC73XX_HASH2_MAC2_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH2_MAC2_FROM_MASK, addr[2])) |
|
||||
FIELD_PREP(VSC73XX_HASH2_MAC3_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH2_MAC3_FROM_MASK, addr[3]));
|
||||
/* MAC 21-11 */
|
||||
hash ^= FIELD_PREP(VSC73XX_HASH3_MAC3_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH3_MAC3_FROM_MASK, addr[3])) |
|
||||
FIELD_PREP(VSC73XX_HASH3_MAC4_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH3_MAC4_FROM_MASK, addr[4]));
|
||||
/* MAC 10-0 */
|
||||
hash ^= FIELD_PREP(VSC73XX_HASH4_MAC4_TO_MASK,
|
||||
FIELD_GET(VSC73XX_HASH4_MAC4_FROM_MASK, addr[4])) |
|
||||
addr[5];
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int
|
||||
vsc73xx_port_wait_for_mac_table_cmd(struct vsc73xx *vsc)
|
||||
{
|
||||
int ret, err;
|
||||
u32 val;
|
||||
|
||||
ret = read_poll_timeout(vsc73xx_read, err,
|
||||
err < 0 ||
|
||||
((val & VSC73XX_MACACCESS_CMD_MASK) ==
|
||||
VSC73XX_MACACCESS_CMD_IDLE),
|
||||
VSC73XX_POLL_SLEEP_US, VSC73XX_POLL_TIMEOUT_US,
|
||||
false, vsc, VSC73XX_BLOCK_ANALYZER,
|
||||
0, VSC73XX_MACACCESS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vsc73xx_port_read_mac_table_row(struct vsc73xx *vsc, u16 index,
|
||||
struct vsc73xx_fdb *fdb)
|
||||
{
|
||||
int ret, i;
|
||||
u32 val;
|
||||
|
||||
if (!fdb)
|
||||
return -EINVAL;
|
||||
if (index >= VSC73XX_NUM_FDB_ROWS)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < VSC73XX_NUM_BUCKETS; i++) {
|
||||
ret = vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACTINDX,
|
||||
(i ? 0 : VSC73XX_MACTINDX_SHADOW) |
|
||||
FIELD_PREP(VSC73XX_MACTINDX_BUCKET_MSK, i) |
|
||||
index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_port_wait_for_mac_table_cmd(vsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACACCESS,
|
||||
VSC73XX_MACACCESS_CMD_MASK,
|
||||
VSC73XX_MACACCESS_CMD_READ_ENTRY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_port_wait_for_mac_table_cmd(vsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACACCESS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fdb[i].valid = FIELD_GET(VSC73XX_MACACCESS_VALID, val);
|
||||
if (!fdb[i].valid)
|
||||
continue;
|
||||
|
||||
fdb[i].port = FIELD_GET(VSC73XX_MACACCESS_DEST_IDX_MASK, val);
|
||||
|
||||
ret = vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACHDATA, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fdb[i].vid = FIELD_GET(VSC73XX_MACHDATA_VID, val);
|
||||
fdb[i].mac[0] = FIELD_GET(VSC73XX_MACHDATA_MAC0, val);
|
||||
fdb[i].mac[1] = FIELD_GET(VSC73XX_MACHDATA_MAC1, val);
|
||||
|
||||
ret = vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACLDATA, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fdb[i].mac[2] = FIELD_GET(VSC73XX_MACLDATA_MAC2, val);
|
||||
fdb[i].mac[3] = FIELD_GET(VSC73XX_MACLDATA_MAC3, val);
|
||||
fdb[i].mac[4] = FIELD_GET(VSC73XX_MACLDATA_MAC4, val);
|
||||
fdb[i].mac[5] = FIELD_GET(VSC73XX_MACLDATA_MAC5, val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vsc73xx_fdb_operation(struct vsc73xx *vsc, const unsigned char *addr, u16 vid,
|
||||
u16 hash, u16 cmd_mask, u16 cmd_val)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
val = FIELD_PREP(VSC73XX_MACHDATA_VID, vid) |
|
||||
FIELD_PREP(VSC73XX_MACHDATA_MAC0, addr[0]) |
|
||||
FIELD_PREP(VSC73XX_MACHDATA_MAC1, addr[1]);
|
||||
ret = vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_MACHDATA,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = FIELD_PREP(VSC73XX_MACLDATA_MAC2, addr[2]) |
|
||||
FIELD_PREP(VSC73XX_MACLDATA_MAC3, addr[3]) |
|
||||
FIELD_PREP(VSC73XX_MACLDATA_MAC4, addr[4]) |
|
||||
FIELD_PREP(VSC73XX_MACLDATA_MAC5, addr[5]);
|
||||
ret = vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_MACLDATA,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_MACTINDX,
|
||||
hash);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_port_wait_for_mac_table_cmd(vsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
|
||||
VSC73XX_MACACCESS, cmd_mask, cmd_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vsc73xx_port_wait_for_mac_table_cmd(vsc);
|
||||
}
|
||||
|
||||
static int vsc73xx_fdb_del_entry(struct vsc73xx *vsc, int port,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct vsc73xx_fdb fdb[VSC73XX_NUM_BUCKETS];
|
||||
u16 hash = vsc73xx_calc_hash(addr, vid);
|
||||
int bucket, ret;
|
||||
|
||||
mutex_lock(&vsc->fdb_lock);
|
||||
|
||||
ret = vsc73xx_port_read_mac_table_row(vsc, hash, fdb);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (bucket = 0; bucket < VSC73XX_NUM_BUCKETS; bucket++) {
|
||||
if (fdb[bucket].valid && fdb[bucket].port == port &&
|
||||
ether_addr_equal(addr, fdb[bucket].mac))
|
||||
break;
|
||||
}
|
||||
|
||||
if (bucket == VSC73XX_NUM_BUCKETS) {
|
||||
/* Can't find MAC in MAC table */
|
||||
ret = -ENODATA;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = vsc73xx_fdb_operation(vsc, addr, vid, hash,
|
||||
VSC73XX_MACACCESS_CMD_MASK,
|
||||
VSC73XX_MACACCESS_CMD_FORGET);
|
||||
err:
|
||||
mutex_unlock(&vsc->fdb_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vsc73xx_fdb_add_entry(struct vsc73xx *vsc, int port,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct vsc73xx_fdb fdb[VSC73XX_NUM_BUCKETS];
|
||||
u16 hash = vsc73xx_calc_hash(addr, vid);
|
||||
int bucket, ret;
|
||||
u32 val;
|
||||
|
||||
mutex_lock(&vsc->fdb_lock);
|
||||
|
||||
ret = vsc73xx_port_read_mac_table_row(vsc, hash, fdb);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (bucket = 0; bucket < VSC73XX_NUM_BUCKETS; bucket++) {
|
||||
if (!fdb[bucket].valid)
|
||||
break;
|
||||
}
|
||||
|
||||
if (bucket == VSC73XX_NUM_BUCKETS) {
|
||||
/* Bucket is full */
|
||||
ret = -EOVERFLOW;
|
||||
goto err;
|
||||
}
|
||||
|
||||
val = VSC73XX_MACACCESS_VALID | VSC73XX_MACACCESS_LOCKED |
|
||||
FIELD_PREP(VSC73XX_MACACCESS_DEST_IDX_MASK, port) |
|
||||
VSC73XX_MACACCESS_CMD_LEARN;
|
||||
ret = vsc73xx_fdb_operation(vsc, addr, vid, hash,
|
||||
VSC73XX_MACACCESS_VALID |
|
||||
VSC73XX_MACACCESS_LOCKED |
|
||||
VSC73XX_MACACCESS_DEST_IDX_MASK |
|
||||
VSC73XX_MACACCESS_CMD_MASK, val);
|
||||
err:
|
||||
mutex_unlock(&vsc->fdb_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vsc73xx_fdb_add(struct dsa_switch *ds, int port,
|
||||
const unsigned char *addr, u16 vid, struct dsa_db db)
|
||||
{
|
||||
struct vsc73xx *vsc = ds->priv;
|
||||
|
||||
if (!vid) {
|
||||
switch (db.type) {
|
||||
case DSA_DB_PORT:
|
||||
vid = dsa_tag_8021q_standalone_vid(db.dp);
|
||||
break;
|
||||
case DSA_DB_BRIDGE:
|
||||
vid = dsa_tag_8021q_bridge_vid(db.bridge.num);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return vsc73xx_fdb_add_entry(vsc, port, addr, vid);
|
||||
}
|
||||
|
||||
static int vsc73xx_fdb_del(struct dsa_switch *ds, int port,
|
||||
const unsigned char *addr, u16 vid, struct dsa_db db)
|
||||
{
|
||||
struct vsc73xx *vsc = ds->priv;
|
||||
|
||||
if (!vid) {
|
||||
switch (db.type) {
|
||||
case DSA_DB_PORT:
|
||||
vid = dsa_tag_8021q_standalone_vid(db.dp);
|
||||
break;
|
||||
case DSA_DB_BRIDGE:
|
||||
vid = dsa_tag_8021q_bridge_vid(db.bridge.num);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return vsc73xx_fdb_del_entry(vsc, port, addr, vid);
|
||||
}
|
||||
|
||||
static int vsc73xx_port_fdb_dump(struct dsa_switch *ds,
|
||||
int port, dsa_fdb_dump_cb_t *cb, void *data)
|
||||
{
|
||||
struct vsc73xx_fdb fdb[VSC73XX_NUM_BUCKETS];
|
||||
struct vsc73xx *vsc = ds->priv;
|
||||
u16 i, bucket;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&vsc->fdb_lock);
|
||||
|
||||
for (i = 0; i < VSC73XX_NUM_FDB_ROWS; i++) {
|
||||
err = vsc73xx_port_read_mac_table_row(vsc, i, fdb);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
for (bucket = 0; bucket < VSC73XX_NUM_BUCKETS; bucket++) {
|
||||
if (!fdb[bucket].valid || fdb[bucket].port != port)
|
||||
continue;
|
||||
|
||||
/* We need to hide dsa_8021q VLANs from the user */
|
||||
if (vid_is_dsa_8021q(fdb[bucket].vid))
|
||||
fdb[bucket].vid = 0;
|
||||
|
||||
err = cb(fdb[bucket].mac, fdb[bucket].vid, false, data);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&vsc->fdb_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {
|
||||
.mac_config = vsc73xx_mac_config,
|
||||
.mac_link_down = vsc73xx_mac_link_down,
|
||||
@ -1876,6 +2226,9 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {
|
||||
.port_bridge_join = dsa_tag_8021q_bridge_join,
|
||||
.port_bridge_leave = dsa_tag_8021q_bridge_leave,
|
||||
.port_change_mtu = vsc73xx_change_mtu,
|
||||
.port_fdb_add = vsc73xx_fdb_add,
|
||||
.port_fdb_del = vsc73xx_fdb_del,
|
||||
.port_fdb_dump = vsc73xx_port_fdb_dump,
|
||||
.port_max_mtu = vsc73xx_get_max_mtu,
|
||||
.port_stp_state_set = vsc73xx_port_stp_state_set,
|
||||
.port_vlan_filtering = vsc73xx_port_vlan_filtering,
|
||||
@ -2006,6 +2359,8 @@ int vsc73xx_probe(struct vsc73xx *vsc)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_init(&vsc->fdb_lock);
|
||||
|
||||
eth_random_addr(vsc->addr);
|
||||
dev_info(vsc->dev,
|
||||
"MAC for control frames: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
|
@ -45,6 +45,7 @@ struct vsc73xx_portinfo {
|
||||
* @vlans: List of configured vlans. Contains port mask and untagged status of
|
||||
* every vlan configured in port vlan operation. It doesn't cover tag_8021q
|
||||
* vlans.
|
||||
* @fdb_lock: Mutex protects fdb access
|
||||
*/
|
||||
struct vsc73xx {
|
||||
struct device *dev;
|
||||
@ -57,6 +58,7 @@ struct vsc73xx {
|
||||
void *priv;
|
||||
struct vsc73xx_portinfo portinfo[VSC73XX_MAX_NUM_PORTS];
|
||||
struct list_head vlans;
|
||||
struct mutex fdb_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user