mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
Merge branch 'net-dsa-mt7530-assorted-fixes'
Daniel Golle says: ==================== net: dsa: mt7530: assorted fixes A batch of small, independent fixes for the MediaTek MT7530 family DSA driver, addressing long-standing correctness issues that surface on hardware with bridge VLAN filtering enabled, on link-local frame reception, and during bridge join/leave transitions. ==================== Link: https://patch.msgid.link/cover.1778766629.git.daniel@makrotopia.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
2d85ae5d0f
|
|
@ -1023,12 +1023,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
|
|||
unsigned int age_count;
|
||||
unsigned int age_unit;
|
||||
|
||||
/* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */
|
||||
if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1))
|
||||
return -ERANGE;
|
||||
|
||||
/* iterate through all possible age_count to find the closest pair */
|
||||
for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) {
|
||||
/* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds.
|
||||
* The DSA core has already validated the range using
|
||||
* ds->ageing_time_min and ds->ageing_time_max.
|
||||
*
|
||||
* Iterate through all possible age_count values to find the closest
|
||||
* pair. Start from 1 because the per-entry aging counter is
|
||||
* initialized to AGE_CNT and a value of 0 means the entry will
|
||||
* never be aged out.
|
||||
*/
|
||||
for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) {
|
||||
unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1;
|
||||
|
||||
if (tmp_age_unit <= AGE_UNIT_MAX) {
|
||||
|
|
@ -1296,37 +1300,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)
|
|||
static void
|
||||
mt753x_trap_frames(struct mt7530_priv *priv)
|
||||
{
|
||||
/* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them
|
||||
* VLAN-untagged.
|
||||
/* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress
|
||||
* them with the EG_TAG attribute set to disabled (system default)
|
||||
* so that any VLAN tags in the frame are not modified by the
|
||||
* switch egress VLAN tag processing. This preserves VLAN tags
|
||||
* for reception on VLAN sub-interfaces.
|
||||
*/
|
||||
mt7530_rmw(priv, MT753X_BPC,
|
||||
PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK |
|
||||
BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK,
|
||||
PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) |
|
||||
BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
TO_CPU_FW_CPU_ONLY);
|
||||
|
||||
/* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress
|
||||
* them VLAN-untagged.
|
||||
/* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and
|
||||
* egress them with EG_TAG disabled.
|
||||
*/
|
||||
mt7530_rmw(priv, MT753X_RGAC1,
|
||||
R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK |
|
||||
R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK,
|
||||
R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR |
|
||||
R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
R01_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
TO_CPU_FW_CPU_ONLY);
|
||||
|
||||
/* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress
|
||||
* them VLAN-untagged.
|
||||
/* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and
|
||||
* egress them with EG_TAG disabled.
|
||||
*/
|
||||
mt7530_rmw(priv, MT753X_RGAC2,
|
||||
R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK |
|
||||
R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK,
|
||||
R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR |
|
||||
R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
|
||||
R03_EG_TAG(MT7530_VLAN_EG_DISABLED) |
|
||||
TO_CPU_FW_CPU_ONLY);
|
||||
}
|
||||
|
||||
|
|
@ -1616,6 +1623,49 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
|
||||
mt7530_write(priv, MT7530_VTCR, val);
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
|
||||
ret = readx_poll_timeout(_mt7530_read, &p, val,
|
||||
!(val & VTCR_BUSY), 20, 20000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = mt7530_read(priv, MT7530_VTCR);
|
||||
if (val & VTCR_INVALID) {
|
||||
dev_err(priv->dev, "read VTCR invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_setup_vlan0(struct mt7530_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Validate the entry with independent learning, keep the original
|
||||
* ingress tag attribute.
|
||||
*/
|
||||
val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
|
||||
VLAN_VALID;
|
||||
mt7530_write(priv, MT7530_VAWD1, val);
|
||||
mt7530_write(priv, MT7530_VAWD2, 0);
|
||||
|
||||
return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
|
||||
{
|
||||
|
|
@ -1641,6 +1691,8 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
|
|||
G0_PORT_VID_DEF);
|
||||
|
||||
for (i = 0; i < priv->ds->num_ports; i++) {
|
||||
if (i == port)
|
||||
continue;
|
||||
if (dsa_is_user_port(ds, i) &&
|
||||
dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
|
||||
all_user_ports_removed = false;
|
||||
|
|
@ -1652,13 +1704,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
|
|||
* the CPU port get out of VLAN filtering mode.
|
||||
*/
|
||||
if (all_user_ports_removed) {
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
struct dsa_port *cpu_dp = dp->cpu_dp;
|
||||
|
||||
mt7530_write(priv, MT7530_PCR_P(cpu_dp->index),
|
||||
PCR_MATRIX(dsa_user_ports(priv->ds)));
|
||||
mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG
|
||||
| PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
mt7530_setup_vlan0(priv);
|
||||
mutex_unlock(&priv->reg_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1846,33 +1894,6 @@ mt7530_port_mdb_del(struct dsa_switch *ds, int port,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
|
||||
mt7530_write(priv, MT7530_VTCR, val);
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
|
||||
ret = readx_poll_timeout(_mt7530_read, &p, val,
|
||||
!(val & VTCR_BUSY), 20, 20000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = mt7530_read(priv, MT7530_VTCR);
|
||||
if (val & VTCR_INVALID) {
|
||||
dev_err(priv->dev, "read VTCR invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
|
@ -1977,21 +1998,6 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid,
|
|||
mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_setup_vlan0(struct mt7530_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Validate the entry with independent learning, keep the original
|
||||
* ingress tag attribute.
|
||||
*/
|
||||
val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
|
||||
VLAN_VALID;
|
||||
mt7530_write(priv, MT7530_VAWD1, val);
|
||||
|
||||
return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_port_vlan_add(struct dsa_switch *ds, int port,
|
||||
const struct switchdev_obj_port_vlan *vlan,
|
||||
|
|
@ -2004,9 +2010,18 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
|
|||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
/* VID 0 is managed exclusively by mt7530_setup_vlan0() for
|
||||
* VLAN-unaware bridge operation. Don't let the bridge overwrite
|
||||
* its EG_CON flag with VTAG_EN and corrupt PORT_MEM.
|
||||
*/
|
||||
if (vlan->vid == 0)
|
||||
goto skip_vlan_table;
|
||||
|
||||
mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
|
||||
mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add);
|
||||
|
||||
skip_vlan_table:
|
||||
|
||||
if (pvid) {
|
||||
priv->ports[port].pvid = vlan->vid;
|
||||
|
||||
|
|
@ -2046,10 +2061,15 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
|
|||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
/* VID 0 is managed exclusively by mt7530_setup_vlan0(). */
|
||||
if (vlan->vid == 0)
|
||||
goto skip_vlan_table;
|
||||
|
||||
mt7530_hw_vlan_entry_init(&target_entry, port, 0);
|
||||
mt7530_hw_vlan_update(priv, vlan->vid, &target_entry,
|
||||
mt7530_hw_vlan_del);
|
||||
|
||||
skip_vlan_table:
|
||||
/* PVID is being restored to the default whenever the PVID port
|
||||
* is being removed from the VLAN.
|
||||
*/
|
||||
|
|
@ -2427,7 +2447,10 @@ mt7530_setup(struct dsa_switch *ds)
|
|||
}
|
||||
|
||||
ds->assisted_learning_on_cpu_port = true;
|
||||
ds->untag_vlan_aware_bridge_pvid = true;
|
||||
ds->mtu_enforcement_ingress = true;
|
||||
ds->ageing_time_min = 2 * 1000;
|
||||
ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000;
|
||||
|
||||
if (priv->id == ID_MT7530) {
|
||||
regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
|
||||
|
|
@ -2616,7 +2639,10 @@ mt7531_setup_common(struct dsa_switch *ds)
|
|||
int ret, i;
|
||||
|
||||
ds->assisted_learning_on_cpu_port = true;
|
||||
ds->untag_vlan_aware_bridge_pvid = true;
|
||||
ds->mtu_enforcement_ingress = true;
|
||||
ds->ageing_time_min = 2 * 1000;
|
||||
ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000;
|
||||
|
||||
mt753x_trap_frames(priv);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user