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