|
@@ -2,6 +2,9 @@
|
|
|
* net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support
|
|
|
* Copyright (c) 2008 Marvell Semiconductor
|
|
|
*
|
|
|
+ * Copyright (c) 2015 CMC Electronics, Inc.
|
|
|
+ * Added support for VLAN Table Unit operations
|
|
|
+ *
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
@@ -1182,6 +1185,474 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *pvid = ret & PORT_DEFAULT_VLAN_MASK;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
|
|
|
+{
|
|
|
+ return mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
|
|
|
+ pvid & PORT_DEFAULT_VLAN_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
|
|
|
+{
|
|
|
+ return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
|
|
|
+ GLOBAL_VTU_OP_BUSY);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return _mv88e6xxx_vtu_wait(ds);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry,
|
|
|
+ unsigned int nibble_offset)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ u16 regs[3];
|
|
|
+ int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ for (i = 0; i < 3; ++i) {
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
|
|
|
+ GLOBAL_VTU_DATA_0_3 + i);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ regs[i] = ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ps->num_ports; ++i) {
|
|
|
+ unsigned int shift = (i % 4) * 4 + nibble_offset;
|
|
|
+ u16 reg = regs[i / 4];
|
|
|
+
|
|
|
+ entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry,
|
|
|
+ unsigned int nibble_offset)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ u16 regs[3] = { 0 };
|
|
|
+ int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ for (i = 0; i < ps->num_ports; ++i) {
|
|
|
+ unsigned int shift = (i % 4) * 4 + nibble_offset;
|
|
|
+ u8 data = entry->data[i];
|
|
|
+
|
|
|
+ regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < 3; ++i) {
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL,
|
|
|
+ GLOBAL_VTU_DATA_0_3 + i, regs[i]);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_vtu_stu_entry next = { 0 };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
|
|
|
+ vid & GLOBAL_VTU_VID_MASK);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ next.vid = ret & GLOBAL_VTU_VID_MASK;
|
|
|
+ next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
|
|
|
+
|
|
|
+ if (next.valid) {
|
|
|
+ ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
|
|
|
+ mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
|
|
|
+ GLOBAL_VTU_FID);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ next.fid = ret & GLOBAL_VTU_FID_MASK;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
|
|
|
+ GLOBAL_VTU_SID);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ next.sid = ret & GLOBAL_VTU_SID_MASK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ *entry = next;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ u16 reg = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!entry->valid)
|
|
|
+ goto loadpurge;
|
|
|
+
|
|
|
+ /* Write port member tags */
|
|
|
+ ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
|
|
|
+ mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
|
|
|
+ reg = entry->sid & GLOBAL_VTU_SID_MASK;
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ reg = entry->fid & GLOBAL_VTU_FID_MASK;
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ reg = GLOBAL_VTU_VID_VALID;
|
|
|
+loadpurge:
|
|
|
+ reg |= entry->vid & GLOBAL_VTU_VID_MASK;
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_vtu_stu_entry next = { 0 };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
|
|
|
+ sid & GLOBAL_VTU_SID_MASK);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ next.sid = ret & GLOBAL_VTU_SID_MASK;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
|
|
|
+
|
|
|
+ if (next.valid) {
|
|
|
+ ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ *entry = next;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ u16 reg = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_vtu_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!entry->valid)
|
|
|
+ goto loadpurge;
|
|
|
+
|
|
|
+ /* Write port states */
|
|
|
+ ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ reg = GLOBAL_VTU_VID_VALID;
|
|
|
+loadpurge:
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ reg = entry->sid & GLOBAL_VTU_SID_MASK;
|
|
|
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vlan = {
|
|
|
+ .valid = true,
|
|
|
+ .vid = vid,
|
|
|
+ };
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* exclude all ports except the CPU */
|
|
|
+ for (i = 0; i < ps->num_ports; ++i)
|
|
|
+ vlan.data[i] = dsa_is_cpu_port(ds, i) ?
|
|
|
+ GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED :
|
|
|
+ GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
|
|
|
+
|
|
|
+ if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
|
|
|
+ mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vstp;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* Adding a VTU entry requires a valid STU entry. As VSTP is not
|
|
|
+ * implemented, only one STU entry is needed to cover all VTU
|
|
|
+ * entries. Thus, validate the SID 0.
|
|
|
+ */
|
|
|
+ vlan.sid = 0;
|
|
|
+ err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (vstp.sid != vlan.sid || !vstp.valid) {
|
|
|
+ memset(&vstp, 0, sizeof(vstp));
|
|
|
+ vstp.valid = true;
|
|
|
+ vstp.sid = vlan.sid;
|
|
|
+
|
|
|
+ err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Non-bridged ports and bridge groups use FIDs from 1 to
|
|
|
+ * num_ports; VLANs use FIDs from num_ports+1 to 4095.
|
|
|
+ */
|
|
|
+ vlan.fid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID,
|
|
|
+ ps->num_ports + 1);
|
|
|
+ if (unlikely(vlan.fid == VLAN_N_VID)) {
|
|
|
+ pr_err("no more FID available for VLAN %d\n", vid);
|
|
|
+ return -ENOSPC;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = _mv88e6xxx_flush_fid(ds, vlan.fid);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ set_bit(vlan.fid, ps->fid_bitmap);
|
|
|
+ }
|
|
|
+
|
|
|
+ *entry = vlan;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
|
|
|
+ bool untagged)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vlan;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ mutex_lock(&ps->smi_mutex);
|
|
|
+ err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
|
|
|
+ if (err)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ if (vlan.vid != vid || !vlan.valid) {
|
|
|
+ err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
|
|
|
+ if (err)
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ vlan.data[port] = untagged ?
|
|
|
+ GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
|
|
|
+ GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
|
|
|
+
|
|
|
+ err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
|
|
|
+unlock:
|
|
|
+ mutex_unlock(&ps->smi_mutex);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vlan;
|
|
|
+ bool keep = false;
|
|
|
+ int i, err;
|
|
|
+
|
|
|
+ mutex_lock(&ps->smi_mutex);
|
|
|
+
|
|
|
+ err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
|
|
|
+ if (err)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ if (vlan.vid != vid || !vlan.valid ||
|
|
|
+ vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
|
|
|
+
|
|
|
+ /* keep the VLAN unless all ports are excluded */
|
|
|
+ for (i = 0; i < ps->num_ports; ++i) {
|
|
|
+ if (dsa_is_cpu_port(ds, i))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
|
|
|
+ keep = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vlan.valid = keep;
|
|
|
+ err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
|
|
|
+ if (err)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ if (!keep)
|
|
|
+ clear_bit(vlan.fid, ps->fid_bitmap);
|
|
|
+
|
|
|
+unlock:
|
|
|
+ mutex_unlock(&ps->smi_mutex);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int _mv88e6xxx_port_vtu_getnext(struct dsa_switch *ds, int port, u16 vid,
|
|
|
+ struct mv88e6xxx_vtu_stu_entry *entry)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ do {
|
|
|
+ if (vid == 4095)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ err = _mv88e6xxx_vtu_getnext(ds, vid, entry);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (!entry->valid)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ vid = entry->vid;
|
|
|
+ } while (entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED &&
|
|
|
+ entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
|
|
|
+ unsigned long *ports, unsigned long *untagged)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ struct mv88e6xxx_vtu_stu_entry next;
|
|
|
+ int port;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (*vid == 4095)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ mutex_lock(&ps->smi_mutex);
|
|
|
+ err = _mv88e6xxx_vtu_getnext(ds, *vid, &next);
|
|
|
+ mutex_unlock(&ps->smi_mutex);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (!next.valid)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ *vid = next.vid;
|
|
|
+
|
|
|
+ for (port = 0; port < ps->num_ports; ++port) {
|
|
|
+ clear_bit(port, ports);
|
|
|
+ clear_bit(port, untagged);
|
|
|
+
|
|
|
+ if (dsa_is_cpu_port(ds, port))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED ||
|
|
|
+ next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
|
|
|
+ set_bit(port, ports);
|
|
|
+
|
|
|
+ if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
|
|
|
+ set_bit(port, untagged);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
|
|
|
const unsigned char *addr)
|
|
|
{
|
|
@@ -1255,10 +1726,19 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
|
|
|
static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
|
|
|
{
|
|
|
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vlan;
|
|
|
+ int err;
|
|
|
|
|
|
if (vid == 0)
|
|
|
return ps->fid[port];
|
|
|
|
|
|
+ err = _mv88e6xxx_port_vtu_getnext(ds, port, vid - 1, &vlan);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (vlan.vid == vid)
|
|
|
+ return vlan.fid;
|
|
|
+
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
|
|
@@ -1382,8 +1862,14 @@ int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
|
|
|
|
|
|
do {
|
|
|
if (is_broadcast_ether_addr(addr)) {
|
|
|
- ret = -ENOENT;
|
|
|
- goto unlock;
|
|
|
+ struct mv88e6xxx_vtu_stu_entry vtu;
|
|
|
+
|
|
|
+ ret = _mv88e6xxx_port_vtu_getnext(ds, port, *vid, &vtu);
|
|
|
+ if (ret < 0)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ *vid = vtu.vid;
|
|
|
+ fid = vtu.fid;
|
|
|
}
|
|
|
|
|
|
ret = _mv88e6xxx_atu_getnext(ds, fid, addr, &next);
|
|
@@ -1519,13 +2005,11 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
|
|
|
goto abort;
|
|
|
}
|
|
|
|
|
|
- /* Port Control 2: don't force a good FCS, set the maximum
|
|
|
- * frame size to 10240 bytes, don't let the switch add or
|
|
|
- * strip 802.1q tags, don't discard tagged or untagged frames
|
|
|
- * on this port, do a destination address lookup on all
|
|
|
- * received packets as usual, disable ARP mirroring and don't
|
|
|
- * send a copy of all transmitted/received frames on this port
|
|
|
- * to the CPU.
|
|
|
+ /* Port Control 2: don't force a good FCS, set the maximum frame size to
|
|
|
+ * 10240 bytes, enable secure 802.1q tags, don't discard tagged or
|
|
|
+ * untagged frames on this port, do a destination address lookup on all
|
|
|
+ * received packets as usual, disable ARP mirroring and don't send a
|
|
|
+ * copy of all transmitted/received frames on this port to the CPU.
|
|
|
*/
|
|
|
reg = 0;
|
|
|
if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
|
|
@@ -1547,6 +2031,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
|
|
|
reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
|
|
|
}
|
|
|
|
|
|
+ reg |= PORT_CONTROL_2_8021Q_SECURE;
|
|
|
+
|
|
|
if (reg) {
|
|
|
ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
|
|
|
PORT_CONTROL_2, reg);
|
|
@@ -2071,6 +2557,12 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
|
|
|
/* Wait for the flush to complete. */
|
|
|
mutex_lock(&ps->smi_mutex);
|
|
|
ret = _mv88e6xxx_stats_wait(ds);
|
|
|
+ if (ret < 0)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ /* Clear all the VTU and STU entries */
|
|
|
+ ret = _mv88e6xxx_vtu_stu_flush(ds);
|
|
|
+unlock:
|
|
|
mutex_unlock(&ps->smi_mutex);
|
|
|
|
|
|
return ret;
|