Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / net / ethernet / microchip / sparx5 / sparx5_vlan.c
blobd42097aa60a0e4c31c9294b3ec34759ad9313f93
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
5 */
7 #include "sparx5_main_regs.h"
8 #include "sparx5_main.h"
10 static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
12 u32 mask[3];
14 /* Divide up mask in 32 bit words */
15 bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
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));
24 return 0;
27 void sparx5_vlan_init(struct sparx5 *sparx5)
29 u16 vid;
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);
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));
44 void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
46 struct sparx5_port *port = sparx5->ports[portno];
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));
57 int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
58 bool untagged)
60 struct sparx5 *sparx5 = port->sparx5;
61 int ret;
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;
71 port->vid = vid;
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;
80 /* Default ingress vlan classification */
81 if (pvid)
82 port->pvid = vid;
84 sparx5_vlan_port_apply(sparx5, port);
86 return 0;
89 int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
91 struct sparx5 *sparx5 = port->sparx5;
92 int ret;
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.
98 if (vid == 0)
99 return 0;
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;
107 /* Ingress */
108 if (port->pvid == vid)
109 port->pvid = 0;
111 /* Egress */
112 if (port->vid == vid)
113 port->vid = 0;
115 sparx5_vlan_port_apply(sparx5, port);
117 return 0;
120 void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
122 struct sparx5 *sparx5 = port->sparx5;
123 u32 val, mask;
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);
143 void sparx5_pgid_clear(struct sparx5 *spx5, int pgid)
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));
152 void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3])
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));
161 void sparx5_update_fwd(struct sparx5 *sparx5)
163 DECLARE_BITMAP(workmask, SPX5_PORTS);
164 u32 mask[3];
165 int port;
167 /* Divide up fwd mask in 32 bit words */
168 bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
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));
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));
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));
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);
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);
214 void sparx5_vlan_port_apply(struct sparx5 *sparx5,
215 struct sparx5_port *port)
218 u32 val;
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));
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.
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));
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);
246 spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
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));