Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Microchip Sparx5 Switch driver
  3 *
  4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
  5 */
  6
  7#include "sparx5_main_regs.h"
  8#include "sparx5_main.h"
  9
 10static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
 11{
 12	u32 mask[3];
 13
 14	/* Divide up mask in 32 bit words */
 15	bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
 16
 17	/* Output mask to respective registers */
 18	spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
 19	if (is_sparx5(sparx5)) {
 20		spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
 21		spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
 22	}
 23
 24	return 0;
 25}
 26
 27void sparx5_vlan_init(struct sparx5 *sparx5)
 28{
 29	u16 vid;
 30
 31	spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
 32		 ANA_L3_VLAN_CTRL_VLAN_ENA,
 33		 sparx5,
 34		 ANA_L3_VLAN_CTRL);
 35
 36	/* Map VLAN = FID */
 37	for (vid = NULL_VID; vid < VLAN_N_VID; vid++)
 38		spx5_rmw(ANA_L3_VLAN_CFG_VLAN_FID_SET(vid),
 39			 ANA_L3_VLAN_CFG_VLAN_FID,
 40			 sparx5,
 41			 ANA_L3_VLAN_CFG(vid));
 42}
 43
 44void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
 45{
 46	struct sparx5_port *port = sparx5->ports[portno];
 47
 48	/* Configure PVID */
 49	spx5_rmw(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(0) |
 50		 ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid),
 51		 ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA |
 52		 ANA_CL_VLAN_CTRL_PORT_VID,
 53		 sparx5,
 54		 ANA_CL_VLAN_CTRL(port->portno));
 55}
 56
 57int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
 58			bool untagged)
 59{
 60	struct sparx5 *sparx5 = port->sparx5;
 61	int ret;
 62
 63	/* Untagged egress vlan classification */
 64	if (untagged && port->vid != vid) {
 65		if (port->vid) {
 66			netdev_err(port->ndev,
 67				   "Port already has a native VLAN: %d\n",
 68				   port->vid);
 69			return -EBUSY;
 70		}
 71		port->vid = vid;
 72	}
 73
 74	/* Make the port a member of the VLAN */
 75	set_bit(port->portno, sparx5->vlan_mask[vid]);
 76	ret = sparx5_vlant_set_mask(sparx5, vid);
 77	if (ret)
 78		return ret;
 79
 80	/* Default ingress vlan classification */
 81	if (pvid)
 82		port->pvid = vid;
 83
 84	sparx5_vlan_port_apply(sparx5, port);
 85
 86	return 0;
 87}
 88
 89int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
 90{
 91	struct sparx5 *sparx5 = port->sparx5;
 92	int ret;
 93
 94	/* 8021q removes VID 0 on module unload for all interfaces
 95	 * with VLAN filtering feature. We need to keep it to receive
 96	 * untagged traffic.
 97	 */
 98	if (vid == 0)
 99		return 0;
100
101	/* Stop the port from being a member of the vlan */
102	clear_bit(port->portno, sparx5->vlan_mask[vid]);
103	ret = sparx5_vlant_set_mask(sparx5, vid);
104	if (ret)
105		return ret;
106
107	/* Ingress */
108	if (port->pvid == vid)
109		port->pvid = 0;
110
111	/* Egress */
112	if (port->vid == vid)
113		port->vid = 0;
114
115	sparx5_vlan_port_apply(sparx5, port);
116
117	return 0;
118}
119
120void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
121{
122	struct sparx5 *sparx5 = port->sparx5;
123	u32 val, mask;
124
125	/* mask is spread across 3 registers x 32 bit */
126	if (port->portno < 32) {
127		mask = BIT(port->portno);
128		val = enable ? mask : 0;
129		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG(pgid));
130	} else if (port->portno < 64) {
131		mask = BIT(port->portno - 32);
132		val = enable ? mask : 0;
133		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG1(pgid));
134	} else if (port->portno < SPX5_PORTS) {
135		mask = BIT(port->portno - 64);
136		val = enable ? mask : 0;
137		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG2(pgid));
138	} else {
139		netdev_err(port->ndev, "Invalid port no: %d\n", port->portno);
140	}
141}
142
143void sparx5_pgid_clear(struct sparx5 *spx5, int pgid)
144{
145	spx5_wr(0, spx5, ANA_AC_PGID_CFG(pgid));
146	if (is_sparx5(spx5)) {
147		spx5_wr(0, spx5, ANA_AC_PGID_CFG1(pgid));
148		spx5_wr(0, spx5, ANA_AC_PGID_CFG2(pgid));
149	}
150}
151
152void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3])
153{
154	portmask[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid));
155	if (is_sparx5(spx5)) {
156		portmask[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid));
157		portmask[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid));
158	}
159}
160
161void sparx5_update_fwd(struct sparx5 *sparx5)
162{
163	DECLARE_BITMAP(workmask, SPX5_PORTS);
164	u32 mask[3];
165	int port;
166
167	/* Divide up fwd mask in 32 bit words */
168	bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
169
170	/* Update flood masks */
171	for (port = sparx5_get_pgid(sparx5, PGID_UC_FLOOD);
172	     port <= sparx5_get_pgid(sparx5, PGID_BCAST); port++) {
173		spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
174		if (is_sparx5(sparx5)) {
175			spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
176			spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
177		}
178	}
179
180	/* Update SRC masks */
181	for (port = 0; port < sparx5->data->consts->n_ports; port++) {
182		if (test_bit(port, sparx5->bridge_fwd_mask)) {
183			/* Allow to send to all bridged but self */
184			bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
185			clear_bit(port, workmask);
186			bitmap_to_arr32(mask, workmask, SPX5_PORTS);
187			spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
188			if (is_sparx5(sparx5)) {
189				spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
190				spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
191			}
192		} else {
193			spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
194			if (is_sparx5(sparx5)) {
195				spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
196				spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
197			}
198		}
199	}
200
201	/* Learning enabled only for bridged ports */
202	bitmap_and(workmask, sparx5->bridge_fwd_mask,
203		   sparx5->bridge_lrn_mask, SPX5_PORTS);
204	bitmap_to_arr32(mask, workmask, SPX5_PORTS);
205
206	/* Apply learning mask */
207	spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
208	if (is_sparx5(sparx5)) {
209		spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
210		spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
211	}
212}
213
214void sparx5_vlan_port_apply(struct sparx5 *sparx5,
215			    struct sparx5_port *port)
216
217{
218	u32 val;
219
220	/* Configure PVID, vlan aware */
221	val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) |
222		ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) |
223		ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid);
224	spx5_wr(val, sparx5, ANA_CL_VLAN_CTRL(port->portno));
225
226	val = 0;
227	if (port->vlan_aware && !port->pvid)
228		/* If port is vlan-aware and tagged, drop untagged and
229		 * priority tagged frames.
230		 */
231		val = ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(1) |
232			ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(1) |
233			ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(1);
234	spx5_wr(val, sparx5,
235		ANA_CL_VLAN_FILTER_CTRL(port->portno, 0));
236
237	/* Egress configuration (REW_TAG_CFG): VLAN tag selected via IFH */
238	val = REW_TAG_CTRL_TAG_TPID_CFG_SET(5);
239	if (port->vlan_aware) {
240		if (port->vid)
241			/* Tag all frames except when VID == DEFAULT_VLAN */
242			val |= REW_TAG_CTRL_TAG_CFG_SET(1);
243		else
244			val |= REW_TAG_CTRL_TAG_CFG_SET(3);
245	}
246	spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
247
248	/* Egress VID */
249	spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
250		 REW_PORT_VLAN_CFG_PORT_VID,
251		 sparx5,
252		 REW_PORT_VLAN_CFG(port->portno));
253}