|
|
|
|
@@ -162,48 +162,117 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
|
|
|
|
|
return ocelot_vlant_wait_for_completion(ocelot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
|
|
|
|
|
struct ocelot_vlan native_vlan)
|
|
|
|
|
static int ocelot_port_num_untagged_vlans(struct ocelot *ocelot, int port)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_bridge_vlan *vlan;
|
|
|
|
|
int num_untagged = 0;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &ocelot->vlans, list) {
|
|
|
|
|
if (!(vlan->portmask & BIT(port)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (vlan->untagged & BIT(port))
|
|
|
|
|
num_untagged++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return num_untagged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocelot_port_num_tagged_vlans(struct ocelot *ocelot, int port)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_bridge_vlan *vlan;
|
|
|
|
|
int num_tagged = 0;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &ocelot->vlans, list) {
|
|
|
|
|
if (!(vlan->portmask & BIT(port)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!(vlan->untagged & BIT(port)))
|
|
|
|
|
num_tagged++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return num_tagged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We use native VLAN when we have to mix egress-tagged VLANs with exactly
|
|
|
|
|
* _one_ egress-untagged VLAN (_the_ native VLAN)
|
|
|
|
|
*/
|
|
|
|
|
static bool ocelot_port_uses_native_vlan(struct ocelot *ocelot, int port)
|
|
|
|
|
{
|
|
|
|
|
return ocelot_port_num_tagged_vlans(ocelot, port) &&
|
|
|
|
|
ocelot_port_num_untagged_vlans(ocelot, port) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ocelot_bridge_vlan *
|
|
|
|
|
ocelot_port_find_native_vlan(struct ocelot *ocelot, int port)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_bridge_vlan *vlan;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &ocelot->vlans, list)
|
|
|
|
|
if (vlan->portmask & BIT(port) && vlan->untagged & BIT(port))
|
|
|
|
|
return vlan;
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Keep in sync REW_TAG_CFG_TAG_CFG and, if applicable,
|
|
|
|
|
* REW_PORT_VLAN_CFG_PORT_VID, with the bridge VLAN table and VLAN awareness
|
|
|
|
|
* state of the port.
|
|
|
|
|
*/
|
|
|
|
|
static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
u32 val = 0;
|
|
|
|
|
|
|
|
|
|
ocelot_port->native_vlan = native_vlan;
|
|
|
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(native_vlan.vid),
|
|
|
|
|
REW_PORT_VLAN_CFG_PORT_VID_M,
|
|
|
|
|
REW_PORT_VLAN_CFG, port);
|
|
|
|
|
enum ocelot_port_tag_config tag_cfg;
|
|
|
|
|
bool uses_native_vlan = false;
|
|
|
|
|
|
|
|
|
|
if (ocelot_port->vlan_aware) {
|
|
|
|
|
if (native_vlan.valid)
|
|
|
|
|
/* Tag all frames except when VID == DEFAULT_VLAN */
|
|
|
|
|
val = REW_TAG_CFG_TAG_CFG(1);
|
|
|
|
|
uses_native_vlan = ocelot_port_uses_native_vlan(ocelot, port);
|
|
|
|
|
|
|
|
|
|
if (uses_native_vlan)
|
|
|
|
|
tag_cfg = OCELOT_PORT_TAG_NATIVE;
|
|
|
|
|
else if (ocelot_port_num_untagged_vlans(ocelot, port))
|
|
|
|
|
tag_cfg = OCELOT_PORT_TAG_DISABLED;
|
|
|
|
|
else
|
|
|
|
|
/* Tag all frames */
|
|
|
|
|
val = REW_TAG_CFG_TAG_CFG(3);
|
|
|
|
|
tag_cfg = OCELOT_PORT_TAG_TRUNK;
|
|
|
|
|
} else {
|
|
|
|
|
/* Port tagging disabled. */
|
|
|
|
|
val = REW_TAG_CFG_TAG_CFG(0);
|
|
|
|
|
tag_cfg = OCELOT_PORT_TAG_DISABLED;
|
|
|
|
|
}
|
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg),
|
|
|
|
|
REW_TAG_CFG_TAG_CFG_M,
|
|
|
|
|
REW_TAG_CFG, port);
|
|
|
|
|
|
|
|
|
|
if (uses_native_vlan) {
|
|
|
|
|
struct ocelot_bridge_vlan *native_vlan;
|
|
|
|
|
|
|
|
|
|
/* Not having a native VLAN is impossible, because
|
|
|
|
|
* ocelot_port_num_untagged_vlans has returned 1.
|
|
|
|
|
* So there is no use in checking for NULL here.
|
|
|
|
|
*/
|
|
|
|
|
native_vlan = ocelot_port_find_native_vlan(ocelot, port);
|
|
|
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot,
|
|
|
|
|
REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid),
|
|
|
|
|
REW_PORT_VLAN_CFG_PORT_VID_M,
|
|
|
|
|
REW_PORT_VLAN_CFG, port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Default vlan to clasify for untagged frames (may be zero) */
|
|
|
|
|
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
|
|
|
|
|
struct ocelot_vlan pvid_vlan)
|
|
|
|
|
const struct ocelot_bridge_vlan *pvid_vlan)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
u16 pvid = OCELOT_VLAN_UNAWARE_PVID;
|
|
|
|
|
u32 val = 0;
|
|
|
|
|
|
|
|
|
|
ocelot_port->pvid_vlan = pvid_vlan;
|
|
|
|
|
|
|
|
|
|
if (!ocelot_port->vlan_aware)
|
|
|
|
|
pvid_vlan.vid = 0;
|
|
|
|
|
if (ocelot_port->vlan_aware && pvid_vlan)
|
|
|
|
|
pvid = pvid_vlan->vid;
|
|
|
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot,
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid),
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID(pvid),
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID_M,
|
|
|
|
|
ANA_PORT_VLAN_CFG, port);
|
|
|
|
|
|
|
|
|
|
@@ -212,7 +281,7 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
|
|
|
|
|
* classified to VLAN 0, but that is always in our RX filter, so it
|
|
|
|
|
* would get accepted were it not for this setting.
|
|
|
|
|
*/
|
|
|
|
|
if (!pvid_vlan.valid && ocelot_port->vlan_aware)
|
|
|
|
|
if (!pvid_vlan && ocelot_port->vlan_aware)
|
|
|
|
|
val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
|
|
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
|
|
|
|
|
|
|
|
|
|
@@ -222,31 +291,90 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
|
|
|
|
|
ANA_PORT_DROP_CFG, port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocelot_vlan_member_set(struct ocelot *ocelot, u32 vlan_mask, u16 vid)
|
|
|
|
|
static struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
|
|
|
|
|
u16 vid)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_bridge_vlan *vlan;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &ocelot->vlans, list)
|
|
|
|
|
if (vlan->vid == vid)
|
|
|
|
|
return vlan;
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid,
|
|
|
|
|
bool untagged)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
|
|
|
|
|
unsigned long portmask;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = ocelot_vlant_set_mask(ocelot, vid, vlan_mask);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
if (vlan) {
|
|
|
|
|
portmask = vlan->portmask | BIT(port);
|
|
|
|
|
|
|
|
|
|
ocelot->vlan_mask[vid] = vlan_mask;
|
|
|
|
|
err = ocelot_vlant_set_mask(ocelot, vid, portmask);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
vlan->portmask = portmask;
|
|
|
|
|
/* Bridge VLANs can be overwritten with a different
|
|
|
|
|
* egress-tagging setting, so make sure to override an untagged
|
|
|
|
|
* with a tagged VID if that's going on.
|
|
|
|
|
*/
|
|
|
|
|
if (untagged)
|
|
|
|
|
vlan->untagged |= BIT(port);
|
|
|
|
|
else
|
|
|
|
|
vlan->untagged &= ~BIT(port);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
|
|
|
|
|
if (!vlan)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
portmask = BIT(port);
|
|
|
|
|
|
|
|
|
|
err = ocelot_vlant_set_mask(ocelot, vid, portmask);
|
|
|
|
|
if (err) {
|
|
|
|
|
kfree(vlan);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vlan->vid = vid;
|
|
|
|
|
vlan->portmask = portmask;
|
|
|
|
|
if (untagged)
|
|
|
|
|
vlan->untagged = BIT(port);
|
|
|
|
|
INIT_LIST_HEAD(&vlan->list);
|
|
|
|
|
list_add_tail(&vlan->list, &ocelot->vlans);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid)
|
|
|
|
|
{
|
|
|
|
|
return ocelot_vlan_member_set(ocelot,
|
|
|
|
|
ocelot->vlan_mask[vid] | BIT(port),
|
|
|
|
|
vid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid)
|
|
|
|
|
{
|
|
|
|
|
return ocelot_vlan_member_set(ocelot,
|
|
|
|
|
ocelot->vlan_mask[vid] & ~BIT(port),
|
|
|
|
|
vid);
|
|
|
|
|
struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
|
|
|
|
|
unsigned long portmask;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (!vlan)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
portmask = vlan->portmask & ~BIT(port);
|
|
|
|
|
|
|
|
|
|
err = ocelot_vlant_set_mask(ocelot, vid, portmask);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
vlan->portmask = portmask;
|
|
|
|
|
if (vlan->portmask)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
list_del(&vlan->list);
|
|
|
|
|
kfree(vlan);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
|
|
|
|
|
@@ -279,7 +407,7 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
|
|
|
|
|
ANA_PORT_VLAN_CFG, port);
|
|
|
|
|
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
|
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, ocelot_port->native_vlan);
|
|
|
|
|
ocelot_port_manage_port_tag(ocelot, port);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@@ -288,14 +416,20 @@ EXPORT_SYMBOL(ocelot_port_vlan_filtering);
|
|
|
|
|
int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
|
|
|
|
|
bool untagged, struct netlink_ext_ack *extack)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
|
|
|
|
|
/* Deny changing the native VLAN, but always permit deleting it */
|
|
|
|
|
if (untagged && ocelot_port->native_vlan.vid != vid &&
|
|
|
|
|
ocelot_port->native_vlan.valid) {
|
|
|
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
|
|
|
"Port already has a native VLAN");
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
if (untagged) {
|
|
|
|
|
/* We are adding an egress-tagged VLAN */
|
|
|
|
|
if (ocelot_port_uses_native_vlan(ocelot, port)) {
|
|
|
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
|
|
|
"Port with egress-tagged VLANs cannot have more than one egress-untagged (native) VLAN");
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* We are adding an egress-tagged VLAN */
|
|
|
|
|
if (ocelot_port_num_untagged_vlans(ocelot, port) > 1) {
|
|
|
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
|
|
|
"Port with more than one egress-untagged VLAN cannot have egress-tagged VLANs");
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -307,27 +441,17 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = ocelot_vlan_member_add(ocelot, port, vid);
|
|
|
|
|
err = ocelot_vlan_member_add(ocelot, port, vid, untagged);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/* Default ingress vlan classification */
|
|
|
|
|
if (pvid) {
|
|
|
|
|
struct ocelot_vlan pvid_vlan;
|
|
|
|
|
|
|
|
|
|
pvid_vlan.vid = vid;
|
|
|
|
|
pvid_vlan.valid = true;
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, pvid_vlan);
|
|
|
|
|
}
|
|
|
|
|
if (pvid)
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port,
|
|
|
|
|
ocelot_bridge_vlan_find(ocelot, vid));
|
|
|
|
|
|
|
|
|
|
/* Untagged egress vlan clasification */
|
|
|
|
|
if (untagged) {
|
|
|
|
|
struct ocelot_vlan native_vlan;
|
|
|
|
|
|
|
|
|
|
native_vlan.vid = vid;
|
|
|
|
|
native_vlan.valid = true;
|
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
|
|
|
|
}
|
|
|
|
|
ocelot_port_manage_port_tag(ocelot, port);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@@ -343,18 +467,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/* Ingress */
|
|
|
|
|
if (ocelot_port->pvid_vlan.vid == vid) {
|
|
|
|
|
struct ocelot_vlan pvid_vlan = {0};
|
|
|
|
|
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, pvid_vlan);
|
|
|
|
|
}
|
|
|
|
|
if (ocelot_port->pvid_vlan && ocelot_port->pvid_vlan->vid == vid)
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, NULL);
|
|
|
|
|
|
|
|
|
|
/* Egress */
|
|
|
|
|
if (ocelot_port->native_vlan.vid == vid) {
|
|
|
|
|
struct ocelot_vlan native_vlan = {0};
|
|
|
|
|
|
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
|
|
|
|
}
|
|
|
|
|
ocelot_port_manage_port_tag(ocelot, port);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@@ -372,13 +489,13 @@ static void ocelot_vlan_init(struct ocelot *ocelot)
|
|
|
|
|
|
|
|
|
|
/* Configure the port VLAN memberships */
|
|
|
|
|
for (vid = 1; vid < VLAN_N_VID; vid++)
|
|
|
|
|
ocelot_vlan_member_set(ocelot, 0, vid);
|
|
|
|
|
ocelot_vlant_set_mask(ocelot, vid, 0);
|
|
|
|
|
|
|
|
|
|
/* Because VLAN filtering is enabled, we need VID 0 to get untagged
|
|
|
|
|
* traffic. It is added automatically if 8021q module is loaded, but
|
|
|
|
|
* we can't rely on it since module may be not loaded.
|
|
|
|
|
*/
|
|
|
|
|
ocelot_vlan_member_set(ocelot, all_ports, 0);
|
|
|
|
|
ocelot_vlant_set_mask(ocelot, OCELOT_VLAN_UNAWARE_PVID, all_ports);
|
|
|
|
|
|
|
|
|
|
/* Set vlan ingress filter mask to all ports but the CPU port by
|
|
|
|
|
* default.
|
|
|
|
|
@@ -1680,12 +1797,11 @@ void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
|
|
|
|
|
struct net_device *bridge)
|
|
|
|
|
{
|
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
struct ocelot_vlan pvid = {0}, native_vlan = {0};
|
|
|
|
|
|
|
|
|
|
ocelot_port->bridge = NULL;
|
|
|
|
|
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, pvid);
|
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
|
|
|
|
ocelot_port_set_pvid(ocelot, port, NULL);
|
|
|
|
|
ocelot_port_manage_port_tag(ocelot, port);
|
|
|
|
|
ocelot_apply_bridge_fwd_mask(ocelot);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(ocelot_port_bridge_leave);
|
|
|
|
|
@@ -2071,9 +2187,10 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)
|
|
|
|
|
OCELOT_TAG_PREFIX_NONE);
|
|
|
|
|
|
|
|
|
|
/* Configure the CPU port to be VLAN aware */
|
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) |
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
|
|
|
|
|
ocelot_write_gix(ocelot,
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) |
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
|
|
|
|
|
ANA_PORT_VLAN_CFG, cpu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2130,6 +2247,7 @@ int ocelot_init(struct ocelot *ocelot)
|
|
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&ocelot->multicast);
|
|
|
|
|
INIT_LIST_HEAD(&ocelot->pgids);
|
|
|
|
|
INIT_LIST_HEAD(&ocelot->vlans);
|
|
|
|
|
ocelot_detect_features(ocelot);
|
|
|
|
|
ocelot_mact_init(ocelot);
|
|
|
|
|
ocelot_vlan_init(ocelot);
|
|
|
|
|
|