1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
7 #include "sparx5_main_regs.h"
8 #include "sparx5_main.h"
10 static int sparx5_vlant_set_mask(struct sparx5
*sparx5
, u16 vid
)
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
));
27 void sparx5_vlan_init(struct sparx5
*sparx5
)
31 spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
32 ANA_L3_VLAN_CTRL_VLAN_ENA
,
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
,
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
];
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
,
54 ANA_CL_VLAN_CTRL(port
->portno
));
57 int sparx5_vlan_vid_add(struct sparx5_port
*port
, u16 vid
, bool pvid
,
60 struct sparx5
*sparx5
= port
->sparx5
;
63 /* Untagged egress vlan classification */
64 if (untagged
&& port
->vid
!= vid
) {
66 netdev_err(port
->ndev
,
67 "Port already has a native VLAN: %d\n",
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
);
80 /* Default ingress vlan classification */
84 sparx5_vlan_port_apply(sparx5
, port
);
89 int sparx5_vlan_vid_del(struct sparx5_port
*port
, u16 vid
)
91 struct sparx5
*sparx5
= port
->sparx5
;
94 /* 8021q removes VID 0 on module unload for all interfaces
95 * with VLAN filtering feature. We need to keep it to receive
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
);
108 if (port
->pvid
== vid
)
112 if (port
->vid
== vid
)
115 sparx5_vlan_port_apply(sparx5
, port
);
120 void sparx5_pgid_update_mask(struct sparx5_port
*port
, int pgid
, bool enable
)
122 struct sparx5
*sparx5
= port
->sparx5
;
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
));
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
);
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
));
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
)
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
));
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);
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
) {
241 /* Tag all frames except when VID == DEFAULT_VLAN */
242 val
|= REW_TAG_CTRL_TAG_CFG_SET(1);
244 val
|= REW_TAG_CTRL_TAG_CFG_SET(3);
246 spx5_wr(val
, sparx5
, REW_TAG_CTRL(port
->portno
));
249 spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port
->vid
),
250 REW_PORT_VLAN_CFG_PORT_VID
,
252 REW_PORT_VLAN_CFG(port
->portno
));