178eab33bSSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
278eab33bSSteen Hegelund /* Microchip Sparx5 Switch driver
378eab33bSSteen Hegelund *
478eab33bSSteen Hegelund * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
578eab33bSSteen Hegelund */
678eab33bSSteen Hegelund
778eab33bSSteen Hegelund #include "sparx5_main_regs.h"
878eab33bSSteen Hegelund #include "sparx5_main.h"
978eab33bSSteen Hegelund
sparx5_vlant_set_mask(struct sparx5 * sparx5,u16 vid)1078eab33bSSteen Hegelund static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
1178eab33bSSteen Hegelund {
1278eab33bSSteen Hegelund u32 mask[3];
1378eab33bSSteen Hegelund
1478eab33bSSteen Hegelund /* Divide up mask in 32 bit words */
1578eab33bSSteen Hegelund bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
1678eab33bSSteen Hegelund
1778eab33bSSteen Hegelund /* Output mask to respective registers */
1878eab33bSSteen Hegelund spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
1978eab33bSSteen Hegelund spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
2078eab33bSSteen Hegelund spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
2178eab33bSSteen Hegelund
2278eab33bSSteen Hegelund return 0;
2378eab33bSSteen Hegelund }
2478eab33bSSteen Hegelund
sparx5_vlan_init(struct sparx5 * sparx5)2578eab33bSSteen Hegelund void sparx5_vlan_init(struct sparx5 *sparx5)
2678eab33bSSteen Hegelund {
2778eab33bSSteen Hegelund u16 vid;
2878eab33bSSteen Hegelund
2978eab33bSSteen Hegelund spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
3078eab33bSSteen Hegelund ANA_L3_VLAN_CTRL_VLAN_ENA,
3178eab33bSSteen Hegelund sparx5,
3278eab33bSSteen Hegelund ANA_L3_VLAN_CTRL);
3378eab33bSSteen Hegelund
3478eab33bSSteen Hegelund /* Map VLAN = FID */
3578eab33bSSteen Hegelund for (vid = NULL_VID; vid < VLAN_N_VID; vid++)
3678eab33bSSteen Hegelund spx5_rmw(ANA_L3_VLAN_CFG_VLAN_FID_SET(vid),
3778eab33bSSteen Hegelund ANA_L3_VLAN_CFG_VLAN_FID,
3878eab33bSSteen Hegelund sparx5,
3978eab33bSSteen Hegelund ANA_L3_VLAN_CFG(vid));
4078eab33bSSteen Hegelund }
4178eab33bSSteen Hegelund
sparx5_vlan_port_setup(struct sparx5 * sparx5,int portno)4278eab33bSSteen Hegelund void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
4378eab33bSSteen Hegelund {
4478eab33bSSteen Hegelund struct sparx5_port *port = sparx5->ports[portno];
4578eab33bSSteen Hegelund
4678eab33bSSteen Hegelund /* Configure PVID */
4778eab33bSSteen Hegelund spx5_rmw(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(0) |
4878eab33bSSteen Hegelund ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid),
4978eab33bSSteen Hegelund ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA |
5078eab33bSSteen Hegelund ANA_CL_VLAN_CTRL_PORT_VID,
5178eab33bSSteen Hegelund sparx5,
5278eab33bSSteen Hegelund ANA_CL_VLAN_CTRL(port->portno));
5378eab33bSSteen Hegelund }
5478eab33bSSteen Hegelund
sparx5_vlan_vid_add(struct sparx5_port * port,u16 vid,bool pvid,bool untagged)5578eab33bSSteen Hegelund int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
5678eab33bSSteen Hegelund bool untagged)
5778eab33bSSteen Hegelund {
5878eab33bSSteen Hegelund struct sparx5 *sparx5 = port->sparx5;
5978eab33bSSteen Hegelund int ret;
6078eab33bSSteen Hegelund
6178eab33bSSteen Hegelund /* Untagged egress vlan classification */
6278eab33bSSteen Hegelund if (untagged && port->vid != vid) {
6378eab33bSSteen Hegelund if (port->vid) {
6478eab33bSSteen Hegelund netdev_err(port->ndev,
6578eab33bSSteen Hegelund "Port already has a native VLAN: %d\n",
6678eab33bSSteen Hegelund port->vid);
6778eab33bSSteen Hegelund return -EBUSY;
6878eab33bSSteen Hegelund }
6978eab33bSSteen Hegelund port->vid = vid;
7078eab33bSSteen Hegelund }
7178eab33bSSteen Hegelund
72b3a34dc3SCasper Andersson /* Make the port a member of the VLAN */
73b3a34dc3SCasper Andersson set_bit(port->portno, sparx5->vlan_mask[vid]);
74b3a34dc3SCasper Andersson ret = sparx5_vlant_set_mask(sparx5, vid);
75b3a34dc3SCasper Andersson if (ret)
76b3a34dc3SCasper Andersson return ret;
77b3a34dc3SCasper Andersson
78b3a34dc3SCasper Andersson /* Default ingress vlan classification */
79b3a34dc3SCasper Andersson if (pvid)
80b3a34dc3SCasper Andersson port->pvid = vid;
81b3a34dc3SCasper Andersson
8278eab33bSSteen Hegelund sparx5_vlan_port_apply(sparx5, port);
8378eab33bSSteen Hegelund
8478eab33bSSteen Hegelund return 0;
8578eab33bSSteen Hegelund }
8678eab33bSSteen Hegelund
sparx5_vlan_vid_del(struct sparx5_port * port,u16 vid)8778eab33bSSteen Hegelund int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
8878eab33bSSteen Hegelund {
8978eab33bSSteen Hegelund struct sparx5 *sparx5 = port->sparx5;
9078eab33bSSteen Hegelund int ret;
9178eab33bSSteen Hegelund
9278eab33bSSteen Hegelund /* 8021q removes VID 0 on module unload for all interfaces
9378eab33bSSteen Hegelund * with VLAN filtering feature. We need to keep it to receive
9478eab33bSSteen Hegelund * untagged traffic.
9578eab33bSSteen Hegelund */
9678eab33bSSteen Hegelund if (vid == 0)
9778eab33bSSteen Hegelund return 0;
9878eab33bSSteen Hegelund
9978eab33bSSteen Hegelund /* Stop the port from being a member of the vlan */
10078eab33bSSteen Hegelund clear_bit(port->portno, sparx5->vlan_mask[vid]);
10178eab33bSSteen Hegelund ret = sparx5_vlant_set_mask(sparx5, vid);
10278eab33bSSteen Hegelund if (ret)
10378eab33bSSteen Hegelund return ret;
10478eab33bSSteen Hegelund
10578eab33bSSteen Hegelund /* Ingress */
10678eab33bSSteen Hegelund if (port->pvid == vid)
10778eab33bSSteen Hegelund port->pvid = 0;
10878eab33bSSteen Hegelund
10978eab33bSSteen Hegelund /* Egress */
11078eab33bSSteen Hegelund if (port->vid == vid)
11178eab33bSSteen Hegelund port->vid = 0;
11278eab33bSSteen Hegelund
11378eab33bSSteen Hegelund sparx5_vlan_port_apply(sparx5, port);
11478eab33bSSteen Hegelund
11578eab33bSSteen Hegelund return 0;
11678eab33bSSteen Hegelund }
11778eab33bSSteen Hegelund
sparx5_pgid_update_mask(struct sparx5_port * port,int pgid,bool enable)11878eab33bSSteen Hegelund void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
11978eab33bSSteen Hegelund {
12078eab33bSSteen Hegelund struct sparx5 *sparx5 = port->sparx5;
12178eab33bSSteen Hegelund u32 val, mask;
12278eab33bSSteen Hegelund
12378eab33bSSteen Hegelund /* mask is spread across 3 registers x 32 bit */
12478eab33bSSteen Hegelund if (port->portno < 32) {
12578eab33bSSteen Hegelund mask = BIT(port->portno);
12678eab33bSSteen Hegelund val = enable ? mask : 0;
12778eab33bSSteen Hegelund spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG(pgid));
12878eab33bSSteen Hegelund } else if (port->portno < 64) {
12978eab33bSSteen Hegelund mask = BIT(port->portno - 32);
13078eab33bSSteen Hegelund val = enable ? mask : 0;
13178eab33bSSteen Hegelund spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG1(pgid));
13278eab33bSSteen Hegelund } else if (port->portno < SPX5_PORTS) {
13378eab33bSSteen Hegelund mask = BIT(port->portno - 64);
13478eab33bSSteen Hegelund val = enable ? mask : 0;
13578eab33bSSteen Hegelund spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG2(pgid));
13678eab33bSSteen Hegelund } else {
13778eab33bSSteen Hegelund netdev_err(port->ndev, "Invalid port no: %d\n", port->portno);
13878eab33bSSteen Hegelund }
13978eab33bSSteen Hegelund }
14078eab33bSSteen Hegelund
sparx5_pgid_clear(struct sparx5 * spx5,int pgid)14104e551d6SCasper Andersson void sparx5_pgid_clear(struct sparx5 *spx5, int pgid)
14204e551d6SCasper Andersson {
14304e551d6SCasper Andersson spx5_wr(0, spx5, ANA_AC_PGID_CFG(pgid));
14404e551d6SCasper Andersson spx5_wr(0, spx5, ANA_AC_PGID_CFG1(pgid));
14504e551d6SCasper Andersson spx5_wr(0, spx5, ANA_AC_PGID_CFG2(pgid));
14604e551d6SCasper Andersson }
14704e551d6SCasper Andersson
sparx5_pgid_read_mask(struct sparx5 * spx5,int pgid,u32 portmask[3])148ad238fc6SCasper Andersson void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3])
149ad238fc6SCasper Andersson {
150ad238fc6SCasper Andersson portmask[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid));
151ad238fc6SCasper Andersson portmask[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid));
152ad238fc6SCasper Andersson portmask[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid));
153ad238fc6SCasper Andersson }
154ad238fc6SCasper Andersson
sparx5_update_fwd(struct sparx5 * sparx5)15578eab33bSSteen Hegelund void sparx5_update_fwd(struct sparx5 *sparx5)
15678eab33bSSteen Hegelund {
15778eab33bSSteen Hegelund DECLARE_BITMAP(workmask, SPX5_PORTS);
15878eab33bSSteen Hegelund u32 mask[3];
15978eab33bSSteen Hegelund int port;
16078eab33bSSteen Hegelund
16178eab33bSSteen Hegelund /* Divide up fwd mask in 32 bit words */
16278eab33bSSteen Hegelund bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
16378eab33bSSteen Hegelund
16478eab33bSSteen Hegelund /* Update flood masks */
16578eab33bSSteen Hegelund for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
16678eab33bSSteen Hegelund spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
16778eab33bSSteen Hegelund spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
16878eab33bSSteen Hegelund spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
16978eab33bSSteen Hegelund }
17078eab33bSSteen Hegelund
17178eab33bSSteen Hegelund /* Update SRC masks */
17278eab33bSSteen Hegelund for (port = 0; port < SPX5_PORTS; port++) {
17378eab33bSSteen Hegelund if (test_bit(port, sparx5->bridge_fwd_mask)) {
17478eab33bSSteen Hegelund /* Allow to send to all bridged but self */
17578eab33bSSteen Hegelund bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
17678eab33bSSteen Hegelund clear_bit(port, workmask);
17778eab33bSSteen Hegelund bitmap_to_arr32(mask, workmask, SPX5_PORTS);
17878eab33bSSteen Hegelund spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
17978eab33bSSteen Hegelund spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
18078eab33bSSteen Hegelund spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
18178eab33bSSteen Hegelund } else {
18278eab33bSSteen Hegelund spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
18378eab33bSSteen Hegelund spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
18478eab33bSSteen Hegelund spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
18578eab33bSSteen Hegelund }
18678eab33bSSteen Hegelund }
18778eab33bSSteen Hegelund
18878eab33bSSteen Hegelund /* Learning enabled only for bridged ports */
18978eab33bSSteen Hegelund bitmap_and(workmask, sparx5->bridge_fwd_mask,
19078eab33bSSteen Hegelund sparx5->bridge_lrn_mask, SPX5_PORTS);
19178eab33bSSteen Hegelund bitmap_to_arr32(mask, workmask, SPX5_PORTS);
19278eab33bSSteen Hegelund
19378eab33bSSteen Hegelund /* Apply learning mask */
19478eab33bSSteen Hegelund spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
19578eab33bSSteen Hegelund spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
19678eab33bSSteen Hegelund spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
19778eab33bSSteen Hegelund }
19878eab33bSSteen Hegelund
sparx5_vlan_port_apply(struct sparx5 * sparx5,struct sparx5_port * port)19978eab33bSSteen Hegelund void sparx5_vlan_port_apply(struct sparx5 *sparx5,
20078eab33bSSteen Hegelund struct sparx5_port *port)
20178eab33bSSteen Hegelund
20278eab33bSSteen Hegelund {
20378eab33bSSteen Hegelund u32 val;
20478eab33bSSteen Hegelund
20578eab33bSSteen Hegelund /* Configure PVID, vlan aware */
20678eab33bSSteen Hegelund val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) |
20778eab33bSSteen Hegelund ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) |
20878eab33bSSteen Hegelund ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid);
20978eab33bSSteen Hegelund spx5_wr(val, sparx5, ANA_CL_VLAN_CTRL(port->portno));
21078eab33bSSteen Hegelund
21178eab33bSSteen Hegelund val = 0;
21278eab33bSSteen Hegelund if (port->vlan_aware && !port->pvid)
21378eab33bSSteen Hegelund /* If port is vlan-aware and tagged, drop untagged and
21478eab33bSSteen Hegelund * priority tagged frames.
21578eab33bSSteen Hegelund */
21678eab33bSSteen Hegelund val = ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(1) |
21778eab33bSSteen Hegelund ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(1) |
21878eab33bSSteen Hegelund ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(1);
21978eab33bSSteen Hegelund spx5_wr(val, sparx5,
22078eab33bSSteen Hegelund ANA_CL_VLAN_FILTER_CTRL(port->portno, 0));
22178eab33bSSteen Hegelund
222*38f6408cSSteen Hegelund /* Egress configuration (REW_TAG_CFG): VLAN tag selected via IFH */
223*38f6408cSSteen Hegelund val = REW_TAG_CTRL_TAG_TPID_CFG_SET(5);
22478eab33bSSteen Hegelund if (port->vlan_aware) {
22578eab33bSSteen Hegelund if (port->vid)
22678eab33bSSteen Hegelund /* Tag all frames except when VID == DEFAULT_VLAN */
22778eab33bSSteen Hegelund val |= REW_TAG_CTRL_TAG_CFG_SET(1);
22878eab33bSSteen Hegelund else
22978eab33bSSteen Hegelund val |= REW_TAG_CTRL_TAG_CFG_SET(3);
23078eab33bSSteen Hegelund }
23178eab33bSSteen Hegelund spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
23278eab33bSSteen Hegelund
23378eab33bSSteen Hegelund /* Egress VID */
23478eab33bSSteen Hegelund spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
23578eab33bSSteen Hegelund REW_PORT_VLAN_CFG_PORT_VID,
23678eab33bSSteen Hegelund sparx5,
23778eab33bSSteen Hegelund REW_PORT_VLAN_CFG(port->portno));
23878eab33bSSteen Hegelund }
239