| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- // SPDX-License-Identifier: GPL-2.0+
- #include "lan966x_main.h"
- #define VLANACCESS_CMD_IDLE 0
- #define VLANACCESS_CMD_READ 1
- #define VLANACCESS_CMD_WRITE 2
- #define VLANACCESS_CMD_INIT 3
- static int lan966x_vlan_get_status(struct lan966x *lan966x)
- {
- return lan_rd(lan966x, ANA_VLANACCESS);
- }
- static int lan966x_vlan_wait_for_completion(struct lan966x *lan966x)
- {
- u32 val;
- return readx_poll_timeout(lan966x_vlan_get_status,
- lan966x, val,
- (val & ANA_VLANACCESS_VLAN_TBL_CMD) ==
- VLANACCESS_CMD_IDLE,
- TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
- }
- static void lan966x_vlan_set_mask(struct lan966x *lan966x, u16 vid)
- {
- u16 mask = lan966x->vlan_mask[vid];
- bool cpu_dis;
- cpu_dis = !(mask & BIT(CPU_PORT));
- /* Set flags and the VID to configure */
- lan_rmw(ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(cpu_dis) |
- ANA_VLANTIDX_V_INDEX_SET(vid),
- ANA_VLANTIDX_VLAN_PGID_CPU_DIS |
- ANA_VLANTIDX_V_INDEX,
- lan966x, ANA_VLANTIDX);
- /* Set the vlan port members mask */
- lan_rmw(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_SET(mask),
- ANA_VLAN_PORT_MASK_VLAN_PORT_MASK,
- lan966x, ANA_VLAN_PORT_MASK);
- /* Issue a write command */
- lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_WRITE),
- ANA_VLANACCESS_VLAN_TBL_CMD,
- lan966x, ANA_VLANACCESS);
- if (lan966x_vlan_wait_for_completion(lan966x))
- dev_err(lan966x->dev, "Vlan set mask failed\n");
- }
- static void lan966x_vlan_port_add_vlan_mask(struct lan966x_port *port, u16 vid)
- {
- struct lan966x *lan966x = port->lan966x;
- u8 p = port->chip_port;
- lan966x->vlan_mask[vid] |= BIT(p);
- lan966x_vlan_set_mask(lan966x, vid);
- }
- static void lan966x_vlan_port_del_vlan_mask(struct lan966x_port *port, u16 vid)
- {
- struct lan966x *lan966x = port->lan966x;
- u8 p = port->chip_port;
- lan966x->vlan_mask[vid] &= ~BIT(p);
- lan966x_vlan_set_mask(lan966x, vid);
- }
- static bool lan966x_vlan_port_any_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- return !!(lan966x->vlan_mask[vid] & ~BIT(CPU_PORT));
- }
- static void lan966x_vlan_cpu_add_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- lan966x->vlan_mask[vid] |= BIT(CPU_PORT);
- lan966x_vlan_set_mask(lan966x, vid);
- }
- static void lan966x_vlan_cpu_del_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- lan966x->vlan_mask[vid] &= ~BIT(CPU_PORT);
- lan966x_vlan_set_mask(lan966x, vid);
- }
- static void lan966x_vlan_cpu_add_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- __set_bit(vid, lan966x->cpu_vlan_mask);
- }
- static void lan966x_vlan_cpu_del_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- __clear_bit(vid, lan966x->cpu_vlan_mask);
- }
- bool lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
- {
- return test_bit(vid, lan966x->cpu_vlan_mask);
- }
- static u16 lan966x_vlan_port_get_pvid(struct lan966x_port *port)
- {
- struct lan966x *lan966x = port->lan966x;
- if (!(lan966x->bridge_mask & BIT(port->chip_port)))
- return HOST_PVID;
- return port->vlan_aware ? port->pvid : UNAWARE_PVID;
- }
- int lan966x_vlan_port_set_vid(struct lan966x_port *port, u16 vid,
- bool pvid, bool untagged)
- {
- struct lan966x *lan966x = port->lan966x;
- /* Egress vlan classification */
- if (untagged && port->vid != vid) {
- if (port->vid) {
- dev_err(lan966x->dev,
- "Port already has a native VLAN: %d\n",
- port->vid);
- return -EBUSY;
- }
- port->vid = vid;
- }
- /* Default ingress vlan classification */
- if (pvid)
- port->pvid = vid;
- return 0;
- }
- static void lan966x_vlan_port_remove_vid(struct lan966x_port *port, u16 vid)
- {
- if (port->pvid == vid)
- port->pvid = 0;
- if (port->vid == vid)
- port->vid = 0;
- }
- void lan966x_vlan_port_set_vlan_aware(struct lan966x_port *port,
- bool vlan_aware)
- {
- port->vlan_aware = vlan_aware;
- }
- /* When the interface is in host mode, the interface should not be vlan aware
- * but it should insert all the tags that it gets from the network stack.
- * The tags are not in the data of the frame but actually in the skb and the ifh
- * is configured already to get this tag. So what we need to do is to update the
- * rewriter to insert the vlan tag for all frames which have a vlan tag
- * different than 0.
- */
- void lan966x_vlan_port_rew_host(struct lan966x_port *port)
- {
- struct lan966x *lan966x = port->lan966x;
- u32 val;
- /* Tag all frames except when VID=0*/
- val = REW_TAG_CFG_TAG_CFG_SET(2);
- /* Update only some bits in the register */
- lan_rmw(val,
- REW_TAG_CFG_TAG_CFG,
- lan966x, REW_TAG_CFG(port->chip_port));
- }
- void lan966x_vlan_port_apply(struct lan966x_port *port)
- {
- struct lan966x *lan966x = port->lan966x;
- u16 pvid;
- u32 val;
- pvid = lan966x_vlan_port_get_pvid(port);
- /* Ingress classification (ANA_PORT_VLAN_CFG) */
- /* Default vlan to classify for untagged frames (may be zero) */
- val = ANA_VLAN_CFG_VLAN_VID_SET(pvid);
- if (port->vlan_aware)
- val |= ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
- ANA_VLAN_CFG_VLAN_POP_CNT_SET(1);
- lan_rmw(val,
- ANA_VLAN_CFG_VLAN_VID | ANA_VLAN_CFG_VLAN_AWARE_ENA |
- ANA_VLAN_CFG_VLAN_POP_CNT,
- lan966x, ANA_VLAN_CFG(port->chip_port));
- lan_rmw(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(port->vlan_aware) |
- DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA_SET(port->vlan_aware),
- DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
- DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA,
- lan966x, DEV_MAC_TAGS_CFG(port->chip_port));
- /* Drop frames with multicast source address */
- val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1);
- if (port->vlan_aware && !pvid)
- /* If port is vlan-aware and tagged, drop untagged and priority
- * tagged frames.
- */
- val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) |
- ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) |
- ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1);
- lan_wr(val, lan966x, ANA_DROP_CFG(port->chip_port));
- /* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
- val = REW_TAG_CFG_TAG_TPID_CFG_SET(0);
- if (port->vlan_aware) {
- if (port->vid)
- /* Tag all frames except when VID == DEFAULT_VLAN */
- val |= REW_TAG_CFG_TAG_CFG_SET(1);
- else
- val |= REW_TAG_CFG_TAG_CFG_SET(3);
- }
- /* Update only some bits in the register */
- lan_rmw(val,
- REW_TAG_CFG_TAG_TPID_CFG | REW_TAG_CFG_TAG_CFG,
- lan966x, REW_TAG_CFG(port->chip_port));
- /* Set default VLAN and tag type to 8021Q */
- lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021Q) |
- REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
- REW_PORT_VLAN_CFG_PORT_TPID |
- REW_PORT_VLAN_CFG_PORT_VID,
- lan966x, REW_PORT_VLAN_CFG(port->chip_port));
- }
- void lan966x_vlan_port_add_vlan(struct lan966x_port *port,
- u16 vid,
- bool pvid,
- bool untagged)
- {
- struct lan966x *lan966x = port->lan966x;
- /* If the CPU(br) is already part of the vlan then add the fdb
- * entries in MAC table to copy the frames to the CPU(br).
- * If the CPU(br) is not part of the vlan then it would
- * just drop the frames.
- */
- if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid)) {
- lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
- lan966x_fdb_write_entries(lan966x, vid);
- lan966x_mdb_write_entries(lan966x, vid);
- }
- lan966x_vlan_port_set_vid(port, vid, pvid, untagged);
- lan966x_vlan_port_add_vlan_mask(port, vid);
- lan966x_vlan_port_apply(port);
- }
- void lan966x_vlan_port_del_vlan(struct lan966x_port *port, u16 vid)
- {
- struct lan966x *lan966x = port->lan966x;
- lan966x_vlan_port_remove_vid(port, vid);
- lan966x_vlan_port_del_vlan_mask(port, vid);
- lan966x_vlan_port_apply(port);
- /* In case there are no other ports in vlan then remove the CPU from
- * that vlan but still keep it in the mask because it may be needed
- * again then another port gets added in that vlan
- */
- if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid)) {
- lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
- lan966x_fdb_erase_entries(lan966x, vid);
- lan966x_mdb_erase_entries(lan966x, vid);
- }
- }
- void lan966x_vlan_cpu_add_vlan(struct lan966x *lan966x, u16 vid)
- {
- /* Add an entry in the MAC table for the CPU
- * Add the CPU part of the vlan only if there is another port in that
- * vlan otherwise all the broadcast frames in that vlan will go to CPU
- * even if none of the ports are in the vlan and then the CPU will just
- * need to discard these frames. It is required to store this
- * information so when a front port is added then it would add also the
- * CPU port.
- */
- if (lan966x_vlan_port_any_vlan_mask(lan966x, vid)) {
- lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
- lan966x_mdb_write_entries(lan966x, vid);
- }
- lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, vid);
- lan966x_fdb_write_entries(lan966x, vid);
- }
- void lan966x_vlan_cpu_del_vlan(struct lan966x *lan966x, u16 vid)
- {
- /* Remove the CPU part of the vlan */
- lan966x_vlan_cpu_del_cpu_vlan_mask(lan966x, vid);
- lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
- lan966x_fdb_erase_entries(lan966x, vid);
- lan966x_mdb_erase_entries(lan966x, vid);
- }
- void lan966x_vlan_init(struct lan966x *lan966x)
- {
- u16 port, vid;
- /* Clear VLAN table, by default all ports are members of all VLANS */
- lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_INIT),
- ANA_VLANACCESS_VLAN_TBL_CMD,
- lan966x, ANA_VLANACCESS);
- lan966x_vlan_wait_for_completion(lan966x);
- for (vid = 1; vid < VLAN_N_VID; vid++) {
- lan966x->vlan_mask[vid] = 0;
- lan966x_vlan_set_mask(lan966x, vid);
- }
- /* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */
- lan966x->vlan_mask[HOST_PVID] =
- GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
- lan966x_vlan_set_mask(lan966x, HOST_PVID);
- lan966x->vlan_mask[UNAWARE_PVID] =
- GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
- lan966x_vlan_set_mask(lan966x, UNAWARE_PVID);
- lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, UNAWARE_PVID);
- /* Configure the CPU port to be vlan aware */
- lan_wr(ANA_VLAN_CFG_VLAN_VID_SET(0) |
- ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
- ANA_VLAN_CFG_VLAN_POP_CNT_SET(1),
- lan966x, ANA_VLAN_CFG(CPU_PORT));
- /* Set vlan ingress filter mask to all ports */
- lan_wr(GENMASK(lan966x->num_phys_ports, 0),
- lan966x, ANA_VLANMASK);
- for (port = 0; port < lan966x->num_phys_ports; port++) {
- lan_wr(0, lan966x, REW_PORT_VLAN_CFG(port));
- lan_wr(0, lan966x, REW_TAG_CFG(port));
- }
- }
|