Loading drivers/net/ethernet/mscc/ocelot.c +195 −77 Original line number Diff line number Diff line Loading @@ -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_port *ocelot_port = ocelot->ports[port]; u32 val = 0; struct ocelot_bridge_vlan *vlan; int num_untagged = 0; ocelot_port->native_vlan = native_vlan; list_for_each_entry(vlan, &ocelot->vlans, list) { if (!(vlan->portmask & BIT(port))) continue; 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); 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]; 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); Loading @@ -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; Loading @@ -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 (vlan) { portmask = vlan->portmask | BIT(port); err = ocelot_vlant_set_mask(ocelot, vid, portmask); if (err) return err; ocelot->vlan_mask[vid] = vlan_mask; 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; } 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); 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_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, Loading Loading @@ -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; } Loading @@ -288,15 +416,21 @@ 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) { 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 already has a native VLAN"); "Port with more than one egress-untagged VLAN cannot have egress-tagged VLANs"); return -EBUSY; } } return 0; } Loading @@ -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; } Loading @@ -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; } Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -2071,7 +2187,8 @@ 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) | 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); Loading Loading @@ -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); Loading drivers/net/ethernet/mscc/ocelot.h +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "ocelot_rew.h" #include "ocelot_qs.h" #define OCELOT_VLAN_UNAWARE_PVID 0 #define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_STATS_CHECK_DELAY (2 * HZ) Loading drivers/net/ethernet/mscc/ocelot_mrp.c +4 −4 Original line number Diff line number Diff line Loading @@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac, port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); } static void ocelot_mrp_del_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID); ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID); } int ocelot_mrp_add(struct ocelot *ocelot, int port, Loading drivers/net/ethernet/mscc/ocelot_net.c +6 −6 Original line number Diff line number Diff line Loading @@ -418,7 +418,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) * with VLAN filtering feature. We need to keep it to receive * untagged traffic. */ if (vid == 0) if (vid == OCELOT_VLAN_UNAWARE_PVID) return 0; ret = ocelot_vlan_del(ocelot, port, vid); Loading Loading @@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.forget.addr, addr); w.forget.vid = ocelot_port->pvid_vlan.vid; w.forget.vid = OCELOT_VLAN_UNAWARE_PVID; w.type = OCELOT_MACT_FORGET; return ocelot_enqueue_mact_action(ocelot, &w); Loading @@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.learn.addr, addr); w.learn.vid = ocelot_port->pvid_vlan.vid; w.learn.vid = OCELOT_VLAN_UNAWARE_PVID; w.learn.pgid = PGID_CPU; w.learn.entry_type = ENTRYTYPE_LOCKED; w.type = OCELOT_MACT_LEARN; Loading Loading @@ -602,9 +602,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) /* Learn the new net device MAC address in the mac table. */ ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid_vlan.vid); ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID); eth_hw_addr_set(dev, addr->sa_data); return 0; Loading Loading @@ -1707,7 +1707,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, eth_hw_addr_gen(dev, ocelot->base_mac, port); ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_init_port(ocelot, port); Loading include/soc/mscc/ocelot.h +17 −7 Original line number Diff line number Diff line Loading @@ -563,9 +563,22 @@ struct ocelot_vcap_block { int pol_lpr; }; struct ocelot_vlan { bool valid; struct ocelot_bridge_vlan { u16 vid; unsigned long portmask; unsigned long untagged; struct list_head list; }; enum ocelot_port_tag_config { /* all VLANs are egress-untagged */ OCELOT_PORT_TAG_DISABLED = 0, /* all VLANs except the native VLAN and VID 0 are egress-tagged */ OCELOT_PORT_TAG_NATIVE = 1, /* all VLANs except VID 0 are egress-tagged */ OCELOT_PORT_TAG_TRUNK_NO_VID0 = 2, /* all VLANs are egress-tagged */ OCELOT_PORT_TAG_TRUNK = 3, }; enum ocelot_sb { Loading @@ -590,9 +603,7 @@ struct ocelot_port { bool vlan_aware; /* VLAN that untagged frames are classified to, on ingress */ struct ocelot_vlan pvid_vlan; /* The VLAN ID that will be transmitted as untagged, on egress */ struct ocelot_vlan native_vlan; const struct ocelot_bridge_vlan *pvid_vlan; unsigned int ptp_skbs_in_flight; u8 ptp_cmd; Loading Loading @@ -635,8 +646,7 @@ struct ocelot { u8 base_mac[ETH_ALEN]; /* Keep track of the vlan port masks */ u32 vlan_mask[VLAN_N_VID]; struct list_head vlans; /* Switches like VSC9959 have flooding per traffic class */ int num_flooding_pgids; Loading Loading
drivers/net/ethernet/mscc/ocelot.c +195 −77 Original line number Diff line number Diff line Loading @@ -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_port *ocelot_port = ocelot->ports[port]; u32 val = 0; struct ocelot_bridge_vlan *vlan; int num_untagged = 0; ocelot_port->native_vlan = native_vlan; list_for_each_entry(vlan, &ocelot->vlans, list) { if (!(vlan->portmask & BIT(port))) continue; 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); 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]; 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); Loading @@ -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; Loading @@ -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 (vlan) { portmask = vlan->portmask | BIT(port); err = ocelot_vlant_set_mask(ocelot, vid, portmask); if (err) return err; ocelot->vlan_mask[vid] = vlan_mask; 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; } 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); 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_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, Loading Loading @@ -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; } Loading @@ -288,15 +416,21 @@ 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) { 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 already has a native VLAN"); "Port with more than one egress-untagged VLAN cannot have egress-tagged VLANs"); return -EBUSY; } } return 0; } Loading @@ -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; } Loading @@ -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; } Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -2071,7 +2187,8 @@ 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) | 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); Loading Loading @@ -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); Loading
drivers/net/ethernet/mscc/ocelot.h +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "ocelot_rew.h" #include "ocelot_qs.h" #define OCELOT_VLAN_UNAWARE_PVID 0 #define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_STATS_CHECK_DELAY (2 * HZ) Loading
drivers/net/ethernet/mscc/ocelot_mrp.c +4 −4 Original line number Diff line number Diff line Loading @@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac, port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); } static void ocelot_mrp_del_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID); ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID); } int ocelot_mrp_add(struct ocelot *ocelot, int port, Loading
drivers/net/ethernet/mscc/ocelot_net.c +6 −6 Original line number Diff line number Diff line Loading @@ -418,7 +418,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) * with VLAN filtering feature. We need to keep it to receive * untagged traffic. */ if (vid == 0) if (vid == OCELOT_VLAN_UNAWARE_PVID) return 0; ret = ocelot_vlan_del(ocelot, port, vid); Loading Loading @@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.forget.addr, addr); w.forget.vid = ocelot_port->pvid_vlan.vid; w.forget.vid = OCELOT_VLAN_UNAWARE_PVID; w.type = OCELOT_MACT_FORGET; return ocelot_enqueue_mact_action(ocelot, &w); Loading @@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.learn.addr, addr); w.learn.vid = ocelot_port->pvid_vlan.vid; w.learn.vid = OCELOT_VLAN_UNAWARE_PVID; w.learn.pgid = PGID_CPU; w.learn.entry_type = ENTRYTYPE_LOCKED; w.type = OCELOT_MACT_LEARN; Loading Loading @@ -602,9 +602,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) /* Learn the new net device MAC address in the mac table. */ ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid_vlan.vid); ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID); eth_hw_addr_set(dev, addr->sa_data); return 0; Loading Loading @@ -1707,7 +1707,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, eth_hw_addr_gen(dev, ocelot->base_mac, port); ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_init_port(ocelot, port); Loading
include/soc/mscc/ocelot.h +17 −7 Original line number Diff line number Diff line Loading @@ -563,9 +563,22 @@ struct ocelot_vcap_block { int pol_lpr; }; struct ocelot_vlan { bool valid; struct ocelot_bridge_vlan { u16 vid; unsigned long portmask; unsigned long untagged; struct list_head list; }; enum ocelot_port_tag_config { /* all VLANs are egress-untagged */ OCELOT_PORT_TAG_DISABLED = 0, /* all VLANs except the native VLAN and VID 0 are egress-tagged */ OCELOT_PORT_TAG_NATIVE = 1, /* all VLANs except VID 0 are egress-tagged */ OCELOT_PORT_TAG_TRUNK_NO_VID0 = 2, /* all VLANs are egress-tagged */ OCELOT_PORT_TAG_TRUNK = 3, }; enum ocelot_sb { Loading @@ -590,9 +603,7 @@ struct ocelot_port { bool vlan_aware; /* VLAN that untagged frames are classified to, on ingress */ struct ocelot_vlan pvid_vlan; /* The VLAN ID that will be transmitted as untagged, on egress */ struct ocelot_vlan native_vlan; const struct ocelot_bridge_vlan *pvid_vlan; unsigned int ptp_skbs_in_flight; u8 ptp_cmd; Loading Loading @@ -635,8 +646,7 @@ struct ocelot { u8 base_mac[ETH_ALEN]; /* Keep track of the vlan port masks */ u32 vlan_mask[VLAN_N_VID]; struct list_head vlans; /* Switches like VSC9959 have flooding per traffic class */ int num_flooding_pgids; Loading