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

source code of linux/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c