Commit | Line | Data |
---|---|---|
b0531225 HV |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | ||
3 | #include "lan966x_main.h" | |
39bedc16 | 4 | #include "lan966x_vcap_ag_api.h" |
b0531225 | 5 | #include "vcap_api.h" |
3643abd6 | 6 | #include "vcap_api_client.h" |
94281484 | 7 | #include "vcap_api_debugfs.h" |
3643abd6 | 8 | |
3643abd6 HV |
9 | #define STREAMSIZE (64 * 4) |
10 | ||
a4d9b3ec | 11 | #define LAN966X_IS1_LOOKUPS 3 |
3643abd6 HV |
12 | #define LAN966X_IS2_LOOKUPS 2 |
13 | ||
14 | static struct lan966x_vcap_inst { | |
15 | enum vcap_type vtype; /* type of vcap */ | |
16 | int tgt_inst; /* hardware instance number */ | |
17 | int lookups; /* number of lookups in this vcap type */ | |
18 | int first_cid; /* first chain id in this vcap */ | |
19 | int last_cid; /* last chain id in this vcap */ | |
20 | int count; /* number of available addresses */ | |
e7e3f514 | 21 | bool ingress; /* is vcap in the ingress path */ |
3643abd6 | 22 | } lan966x_vcap_inst_cfg[] = { |
a4d9b3ec HV |
23 | { |
24 | .vtype = VCAP_TYPE_IS1, /* IS1-0 */ | |
25 | .tgt_inst = 1, | |
26 | .lookups = LAN966X_IS1_LOOKUPS, | |
27 | .first_cid = LAN966X_VCAP_CID_IS1_L0, | |
28 | .last_cid = LAN966X_VCAP_CID_IS1_MAX, | |
29 | .count = 768, | |
30 | .ingress = true, | |
31 | }, | |
3643abd6 HV |
32 | { |
33 | .vtype = VCAP_TYPE_IS2, /* IS2-0 */ | |
34 | .tgt_inst = 2, | |
35 | .lookups = LAN966X_IS2_LOOKUPS, | |
36 | .first_cid = LAN966X_VCAP_CID_IS2_L0, | |
37 | .last_cid = LAN966X_VCAP_CID_IS2_MAX, | |
38 | .count = 256, | |
e7e3f514 | 39 | .ingress = true, |
3643abd6 HV |
40 | }, |
41 | }; | |
42 | ||
43 | struct lan966x_vcap_cmd_cb { | |
44 | struct lan966x *lan966x; | |
45 | u32 instance; | |
46 | }; | |
47 | ||
48 | static u32 lan966x_vcap_read_update_ctrl(const struct lan966x_vcap_cmd_cb *cb) | |
49 | { | |
50 | return lan_rd(cb->lan966x, VCAP_UPDATE_CTRL(cb->instance)); | |
51 | } | |
52 | ||
53 | static void lan966x_vcap_wait_update(struct lan966x *lan966x, int instance) | |
54 | { | |
55 | const struct lan966x_vcap_cmd_cb cb = { .lan966x = lan966x, | |
56 | .instance = instance }; | |
57 | u32 val; | |
58 | ||
59 | readx_poll_timeout(lan966x_vcap_read_update_ctrl, &cb, val, | |
60 | (val & VCAP_UPDATE_CTRL_UPDATE_SHOT) == 0, 10, | |
61 | 100000); | |
62 | } | |
63 | ||
64 | static void __lan966x_vcap_range_init(struct lan966x *lan966x, | |
65 | struct vcap_admin *admin, | |
66 | u32 addr, | |
67 | u32 count) | |
68 | { | |
69 | lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | | |
70 | VCAP_MV_CFG_MV_SIZE_SET(count - 1), | |
71 | lan966x, VCAP_MV_CFG(admin->tgt_inst)); | |
72 | ||
73 | lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | | |
74 | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | | |
75 | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | | |
76 | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | | |
77 | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | | |
78 | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(true) | | |
79 | VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(1), | |
80 | lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); | |
81 | ||
82 | lan966x_vcap_wait_update(lan966x, admin->tgt_inst); | |
83 | } | |
84 | ||
a4d9b3ec HV |
85 | static int lan966x_vcap_is1_cid_to_lookup(int cid) |
86 | { | |
87 | int lookup = 0; | |
88 | ||
89 | if (cid >= LAN966X_VCAP_CID_IS1_L1 && | |
90 | cid < LAN966X_VCAP_CID_IS1_L2) | |
91 | lookup = 1; | |
92 | else if (cid >= LAN966X_VCAP_CID_IS1_L2 && | |
93 | cid < LAN966X_VCAP_CID_IS1_MAX) | |
94 | lookup = 2; | |
95 | ||
96 | return lookup; | |
97 | } | |
98 | ||
99 | static int lan966x_vcap_is2_cid_to_lookup(int cid) | |
4426b78c HV |
100 | { |
101 | if (cid >= LAN966X_VCAP_CID_IS2_L1 && | |
102 | cid < LAN966X_VCAP_CID_IS2_MAX) | |
103 | return 1; | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
a4d9b3ec HV |
108 | /* Return the list of keysets for the vcap port configuration */ |
109 | static int | |
110 | lan966x_vcap_is1_get_port_keysets(struct net_device *ndev, int lookup, | |
111 | struct vcap_keyset_list *keysetlist, | |
112 | u16 l3_proto) | |
113 | { | |
114 | struct lan966x_port *port = netdev_priv(ndev); | |
115 | struct lan966x *lan966x = port->lan966x; | |
116 | u32 val; | |
117 | ||
118 | val = lan_rd(lan966x, ANA_VCAP_S1_CFG(port->chip_port, lookup)); | |
119 | ||
120 | /* Collect all keysets for the port in a list */ | |
121 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { | |
122 | switch (ANA_VCAP_S1_CFG_KEY_IP4_CFG_GET(val)) { | |
123 | case VCAP_IS1_PS_IPV4_7TUPLE: | |
124 | vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); | |
125 | break; | |
126 | case VCAP_IS1_PS_IPV4_5TUPLE_IP4: | |
127 | vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); | |
128 | break; | |
129 | case VCAP_IS1_PS_IPV4_NORMAL: | |
130 | vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); | |
131 | break; | |
132 | } | |
133 | } | |
134 | ||
135 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { | |
136 | switch (ANA_VCAP_S1_CFG_KEY_IP6_CFG_GET(val)) { | |
137 | case VCAP_IS1_PS_IPV6_NORMAL: | |
138 | case VCAP_IS1_PS_IPV6_NORMAL_IP6: | |
139 | vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); | |
140 | vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL_IP6); | |
141 | break; | |
142 | case VCAP_IS1_PS_IPV6_5TUPLE_IP6: | |
143 | vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP6); | |
144 | break; | |
145 | case VCAP_IS1_PS_IPV6_7TUPLE: | |
146 | vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); | |
147 | break; | |
148 | case VCAP_IS1_PS_IPV6_5TUPLE_IP4: | |
149 | vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); | |
150 | break; | |
151 | case VCAP_IS1_PS_IPV6_DMAC_VID: | |
152 | vcap_keyset_list_add(keysetlist, VCAP_KFS_DMAC_VID); | |
153 | break; | |
154 | } | |
155 | } | |
156 | ||
157 | switch (ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(val)) { | |
158 | case VCAP_IS1_PS_OTHER_7TUPLE: | |
159 | vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); | |
160 | break; | |
161 | case VCAP_IS1_PS_OTHER_NORMAL: | |
162 | vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); | |
163 | break; | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
4426b78c HV |
169 | static int |
170 | lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup, | |
171 | struct vcap_keyset_list *keysetlist, | |
172 | u16 l3_proto) | |
173 | { | |
174 | struct lan966x_port *port = netdev_priv(dev); | |
175 | struct lan966x *lan966x = port->lan966x; | |
176 | bool found = false; | |
177 | u32 val; | |
178 | ||
4426b78c | 179 | val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port)); |
4426b78c HV |
180 | |
181 | /* Collect all keysets for the port in a list */ | |
182 | if (l3_proto == ETH_P_ALL) | |
183 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
184 | ||
185 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) { | |
186 | if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup)) | |
187 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC); | |
188 | else | |
189 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP); | |
190 | ||
191 | found = true; | |
192 | } | |
193 | ||
194 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) { | |
195 | if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup)) | |
196 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
197 | else | |
198 | vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM); | |
199 | ||
200 | found = true; | |
201 | } | |
202 | ||
203 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { | |
204 | if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup)) | |
205 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
206 | else | |
207 | vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); | |
208 | ||
209 | found = true; | |
210 | } | |
211 | ||
212 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { | |
213 | if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup)) | |
214 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
215 | else | |
216 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); | |
217 | ||
218 | if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup)) | |
219 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
220 | else | |
221 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); | |
222 | ||
223 | found = true; | |
224 | } | |
225 | ||
226 | if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { | |
227 | switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) { | |
228 | case VCAP_IS2_PS_IPV6_TCPUDP_OTHER: | |
229 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER); | |
230 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP); | |
231 | break; | |
232 | case VCAP_IS2_PS_IPV6_STD: | |
233 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); | |
234 | break; | |
235 | case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER: | |
236 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); | |
237 | vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); | |
238 | break; | |
239 | case VCAP_IS2_PS_IPV6_MAC_ETYPE: | |
240 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
241 | break; | |
242 | } | |
243 | ||
244 | found = true; | |
245 | } | |
246 | ||
247 | if (!found) | |
248 | vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
253 | static enum vcap_keyfield_set | |
254 | lan966x_vcap_validate_keyset(struct net_device *dev, | |
255 | struct vcap_admin *admin, | |
256 | struct vcap_rule *rule, | |
257 | struct vcap_keyset_list *kslist, | |
258 | u16 l3_proto) | |
259 | { | |
260 | struct vcap_keyset_list keysetlist = {}; | |
261 | enum vcap_keyfield_set keysets[10] = {}; | |
262 | int lookup; | |
263 | int err; | |
264 | ||
265 | if (!kslist || kslist->cnt == 0) | |
266 | return VCAP_KFS_NO_VALUE; | |
267 | ||
4426b78c HV |
268 | keysetlist.max = ARRAY_SIZE(keysets); |
269 | keysetlist.keysets = keysets; | |
a4d9b3ec HV |
270 | |
271 | switch (admin->vtype) { | |
272 | case VCAP_TYPE_IS1: | |
273 | lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); | |
274 | err = lan966x_vcap_is1_get_port_keysets(dev, lookup, &keysetlist, | |
275 | l3_proto); | |
276 | break; | |
277 | case VCAP_TYPE_IS2: | |
278 | lookup = lan966x_vcap_is2_cid_to_lookup(rule->vcap_chain_id); | |
279 | err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, | |
280 | l3_proto); | |
281 | break; | |
282 | default: | |
283 | pr_err("vcap type: %s not supported\n", | |
284 | lan966x_vcaps[admin->vtype].name); | |
285 | return VCAP_KFS_NO_VALUE; | |
286 | } | |
287 | ||
4426b78c HV |
288 | if (err) |
289 | return VCAP_KFS_NO_VALUE; | |
290 | ||
291 | /* Check if there is a match and return the match */ | |
292 | for (int i = 0; i < kslist->cnt; ++i) | |
293 | for (int j = 0; j < keysetlist.cnt; ++j) | |
294 | if (kslist->keysets[i] == keysets[j]) | |
295 | return kslist->keysets[i]; | |
296 | ||
297 | return VCAP_KFS_NO_VALUE; | |
298 | } | |
299 | ||
a4d9b3ec | 300 | static bool lan966x_vcap_is2_is_first_chain(struct vcap_rule *rule) |
4426b78c HV |
301 | { |
302 | return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 && | |
303 | rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1); | |
304 | } | |
305 | ||
a4d9b3ec HV |
306 | static void lan966x_vcap_is1_add_default_fields(struct lan966x_port *port, |
307 | struct vcap_admin *admin, | |
308 | struct vcap_rule *rule) | |
309 | { | |
310 | u32 value, mask; | |
311 | u32 lookup; | |
312 | ||
313 | if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, | |
314 | &value, &mask)) | |
315 | vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, | |
316 | ~BIT(port->chip_port)); | |
317 | ||
318 | lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); | |
319 | vcap_rule_add_key_u32(rule, VCAP_KF_LOOKUP_INDEX, lookup, 0x3); | |
320 | } | |
321 | ||
322 | static void lan966x_vcap_is2_add_default_fields(struct lan966x_port *port, | |
323 | struct vcap_admin *admin, | |
324 | struct vcap_rule *rule) | |
4426b78c | 325 | { |
72df3489 | 326 | u32 value, mask; |
4426b78c | 327 | |
72df3489 HV |
328 | if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, |
329 | &value, &mask)) | |
330 | vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, | |
331 | ~BIT(port->chip_port)); | |
4426b78c | 332 | |
a4d9b3ec | 333 | if (lan966x_vcap_is2_is_first_chain(rule)) |
4426b78c HV |
334 | vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, |
335 | VCAP_BIT_1); | |
336 | else | |
337 | vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, | |
338 | VCAP_BIT_0); | |
339 | } | |
340 | ||
a4d9b3ec HV |
341 | static void lan966x_vcap_add_default_fields(struct net_device *dev, |
342 | struct vcap_admin *admin, | |
343 | struct vcap_rule *rule) | |
344 | { | |
345 | struct lan966x_port *port = netdev_priv(dev); | |
346 | ||
347 | switch (admin->vtype) { | |
348 | case VCAP_TYPE_IS1: | |
349 | lan966x_vcap_is1_add_default_fields(port, admin, rule); | |
350 | break; | |
351 | case VCAP_TYPE_IS2: | |
352 | lan966x_vcap_is2_add_default_fields(port, admin, rule); | |
353 | break; | |
354 | default: | |
355 | pr_err("vcap type: %s not supported\n", | |
356 | lan966x_vcaps[admin->vtype].name); | |
357 | break; | |
358 | } | |
359 | } | |
360 | ||
4426b78c HV |
361 | static void lan966x_vcap_cache_erase(struct vcap_admin *admin) |
362 | { | |
363 | memset(admin->cache.keystream, 0, STREAMSIZE); | |
364 | memset(admin->cache.maskstream, 0, STREAMSIZE); | |
365 | memset(admin->cache.actionstream, 0, STREAMSIZE); | |
366 | memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); | |
367 | } | |
368 | ||
369 | static void lan966x_vcap_cache_write(struct net_device *dev, | |
370 | struct vcap_admin *admin, | |
371 | enum vcap_selection sel, | |
372 | u32 start, | |
373 | u32 count) | |
374 | { | |
375 | struct lan966x_port *port = netdev_priv(dev); | |
376 | struct lan966x *lan966x = port->lan966x; | |
377 | u32 *keystr, *mskstr, *actstr; | |
378 | ||
379 | keystr = &admin->cache.keystream[start]; | |
380 | mskstr = &admin->cache.maskstream[start]; | |
381 | actstr = &admin->cache.actionstream[start]; | |
382 | ||
383 | switch (sel) { | |
384 | case VCAP_SEL_ENTRY: | |
385 | for (int i = 0; i < count; ++i) { | |
386 | lan_wr(keystr[i] & mskstr[i], lan966x, | |
387 | VCAP_ENTRY_DAT(admin->tgt_inst, i)); | |
388 | lan_wr(~mskstr[i], lan966x, | |
389 | VCAP_MASK_DAT(admin->tgt_inst, i)); | |
390 | } | |
391 | break; | |
392 | case VCAP_SEL_ACTION: | |
393 | for (int i = 0; i < count; ++i) | |
394 | lan_wr(actstr[i], lan966x, | |
395 | VCAP_ACTION_DAT(admin->tgt_inst, i)); | |
396 | break; | |
397 | case VCAP_SEL_COUNTER: | |
398 | admin->cache.sticky = admin->cache.counter > 0; | |
399 | lan_wr(admin->cache.counter, lan966x, | |
400 | VCAP_CNT_DAT(admin->tgt_inst, 0)); | |
401 | break; | |
402 | default: | |
403 | break; | |
404 | } | |
405 | } | |
406 | ||
407 | static void lan966x_vcap_cache_read(struct net_device *dev, | |
408 | struct vcap_admin *admin, | |
409 | enum vcap_selection sel, | |
410 | u32 start, | |
411 | u32 count) | |
412 | { | |
413 | struct lan966x_port *port = netdev_priv(dev); | |
414 | struct lan966x *lan966x = port->lan966x; | |
415 | int instance = admin->tgt_inst; | |
416 | u32 *keystr, *mskstr, *actstr; | |
417 | ||
418 | keystr = &admin->cache.keystream[start]; | |
419 | mskstr = &admin->cache.maskstream[start]; | |
420 | actstr = &admin->cache.actionstream[start]; | |
421 | ||
422 | if (sel & VCAP_SEL_ENTRY) { | |
423 | for (int i = 0; i < count; ++i) { | |
424 | keystr[i] = | |
425 | lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i)); | |
426 | mskstr[i] = | |
427 | ~lan_rd(lan966x, VCAP_MASK_DAT(instance, i)); | |
428 | } | |
429 | } | |
430 | ||
431 | if (sel & VCAP_SEL_ACTION) | |
432 | for (int i = 0; i < count; ++i) | |
433 | actstr[i] = | |
434 | lan_rd(lan966x, VCAP_ACTION_DAT(instance, i)); | |
435 | ||
436 | if (sel & VCAP_SEL_COUNTER) { | |
437 | admin->cache.counter = | |
438 | lan_rd(lan966x, VCAP_CNT_DAT(instance, 0)); | |
439 | admin->cache.sticky = admin->cache.counter > 0; | |
440 | } | |
441 | } | |
442 | ||
443 | static void lan966x_vcap_range_init(struct net_device *dev, | |
444 | struct vcap_admin *admin, | |
445 | u32 addr, | |
446 | u32 count) | |
447 | { | |
448 | struct lan966x_port *port = netdev_priv(dev); | |
449 | struct lan966x *lan966x = port->lan966x; | |
450 | ||
451 | __lan966x_vcap_range_init(lan966x, admin, addr, count); | |
452 | } | |
453 | ||
454 | static void lan966x_vcap_update(struct net_device *dev, | |
455 | struct vcap_admin *admin, | |
456 | enum vcap_command cmd, | |
457 | enum vcap_selection sel, | |
458 | u32 addr) | |
459 | { | |
460 | struct lan966x_port *port = netdev_priv(dev); | |
461 | struct lan966x *lan966x = port->lan966x; | |
462 | bool clear; | |
463 | ||
464 | clear = (cmd == VCAP_CMD_INITIALIZE); | |
465 | ||
466 | lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | | |
467 | VCAP_MV_CFG_MV_SIZE_SET(0), | |
468 | lan966x, VCAP_MV_CFG(admin->tgt_inst)); | |
469 | ||
470 | lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | | |
471 | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | | |
472 | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | | |
473 | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | | |
474 | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | | |
475 | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) | | |
476 | VCAP_UPDATE_CTRL_UPDATE_SHOT, | |
477 | lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); | |
478 | ||
479 | lan966x_vcap_wait_update(lan966x, admin->tgt_inst); | |
480 | } | |
481 | ||
482 | static void lan966x_vcap_move(struct net_device *dev, | |
483 | struct vcap_admin *admin, | |
484 | u32 addr, int offset, int count) | |
485 | { | |
486 | struct lan966x_port *port = netdev_priv(dev); | |
487 | struct lan966x *lan966x = port->lan966x; | |
488 | enum vcap_command cmd; | |
489 | u16 mv_num_pos; | |
490 | u16 mv_size; | |
491 | ||
492 | mv_size = count - 1; | |
493 | if (offset > 0) { | |
494 | mv_num_pos = offset - 1; | |
495 | cmd = VCAP_CMD_MOVE_DOWN; | |
496 | } else { | |
497 | mv_num_pos = -offset - 1; | |
498 | cmd = VCAP_CMD_MOVE_UP; | |
499 | } | |
500 | ||
501 | lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) | | |
502 | VCAP_MV_CFG_MV_SIZE_SET(mv_size), | |
503 | lan966x, VCAP_MV_CFG(admin->tgt_inst)); | |
504 | ||
505 | lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | | |
506 | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | | |
507 | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | | |
508 | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | | |
509 | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | | |
510 | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) | | |
511 | VCAP_UPDATE_CTRL_UPDATE_SHOT, | |
512 | lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); | |
513 | ||
514 | lan966x_vcap_wait_update(lan966x, admin->tgt_inst); | |
515 | } | |
516 | ||
4426b78c HV |
517 | static struct vcap_operations lan966x_vcap_ops = { |
518 | .validate_keyset = lan966x_vcap_validate_keyset, | |
519 | .add_default_fields = lan966x_vcap_add_default_fields, | |
520 | .cache_erase = lan966x_vcap_cache_erase, | |
521 | .cache_write = lan966x_vcap_cache_write, | |
522 | .cache_read = lan966x_vcap_cache_read, | |
523 | .init = lan966x_vcap_range_init, | |
524 | .update = lan966x_vcap_update, | |
525 | .move = lan966x_vcap_move, | |
526 | .port_info = lan966x_vcap_port_info, | |
4426b78c HV |
527 | }; |
528 | ||
3643abd6 HV |
529 | static void lan966x_vcap_admin_free(struct vcap_admin *admin) |
530 | { | |
531 | if (!admin) | |
532 | return; | |
533 | ||
534 | kfree(admin->cache.keystream); | |
535 | kfree(admin->cache.maskstream); | |
536 | kfree(admin->cache.actionstream); | |
537 | mutex_destroy(&admin->lock); | |
538 | kfree(admin); | |
539 | } | |
540 | ||
541 | static struct vcap_admin * | |
542 | lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, | |
543 | const struct lan966x_vcap_inst *cfg) | |
544 | { | |
545 | struct vcap_admin *admin; | |
546 | ||
547 | admin = kzalloc(sizeof(*admin), GFP_KERNEL); | |
548 | if (!admin) | |
549 | return ERR_PTR(-ENOMEM); | |
550 | ||
551 | mutex_init(&admin->lock); | |
552 | INIT_LIST_HEAD(&admin->list); | |
553 | INIT_LIST_HEAD(&admin->rules); | |
4426b78c HV |
554 | INIT_LIST_HEAD(&admin->enabled); |
555 | ||
3643abd6 HV |
556 | admin->vtype = cfg->vtype; |
557 | admin->vinst = 0; | |
e7e3f514 | 558 | admin->ingress = cfg->ingress; |
4426b78c HV |
559 | admin->w32be = true; |
560 | admin->tgt_inst = cfg->tgt_inst; | |
561 | ||
3643abd6 HV |
562 | admin->lookups = cfg->lookups; |
563 | admin->lookups_per_instance = cfg->lookups; | |
4426b78c | 564 | |
3643abd6 HV |
565 | admin->first_cid = cfg->first_cid; |
566 | admin->last_cid = cfg->last_cid; | |
4426b78c | 567 | |
3643abd6 HV |
568 | admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL); |
569 | admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL); | |
570 | admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL); | |
571 | if (!admin->cache.keystream || | |
572 | !admin->cache.maskstream || | |
573 | !admin->cache.actionstream) { | |
574 | lan966x_vcap_admin_free(admin); | |
575 | return ERR_PTR(-ENOMEM); | |
576 | } | |
577 | ||
578 | return admin; | |
579 | } | |
580 | ||
581 | static void lan966x_vcap_block_init(struct lan966x *lan966x, | |
582 | struct vcap_admin *admin, | |
583 | struct lan966x_vcap_inst *cfg) | |
584 | { | |
585 | admin->first_valid_addr = 0; | |
586 | admin->last_used_addr = cfg->count; | |
587 | admin->last_valid_addr = cfg->count - 1; | |
588 | ||
589 | lan_wr(VCAP_CORE_IDX_CORE_IDX_SET(0), | |
590 | lan966x, VCAP_CORE_IDX(admin->tgt_inst)); | |
591 | lan_wr(VCAP_CORE_MAP_CORE_MAP_SET(1), | |
592 | lan966x, VCAP_CORE_MAP(admin->tgt_inst)); | |
593 | ||
594 | __lan966x_vcap_range_init(lan966x, admin, admin->first_valid_addr, | |
595 | admin->last_valid_addr - | |
596 | admin->first_valid_addr); | |
597 | } | |
b0531225 | 598 | |
4426b78c HV |
599 | static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, |
600 | struct vcap_admin *admin) | |
601 | { | |
a4d9b3ec HV |
602 | u32 val; |
603 | ||
604 | switch (admin->vtype) { | |
605 | case VCAP_TYPE_IS1: | |
606 | val = ANA_VCAP_S1_CFG_KEY_IP6_CFG_SET(VCAP_IS1_PS_IPV6_5TUPLE_IP6) | | |
607 | ANA_VCAP_S1_CFG_KEY_IP4_CFG_SET(VCAP_IS1_PS_IPV4_5TUPLE_IP4) | | |
608 | ANA_VCAP_S1_CFG_KEY_OTHER_CFG_SET(VCAP_IS1_PS_OTHER_NORMAL); | |
609 | ||
610 | for (int p = 0; p < lan966x->num_phys_ports; ++p) { | |
611 | if (!lan966x->ports[p]) | |
612 | continue; | |
613 | ||
614 | for (int l = 0; l < LAN966X_IS1_LOOKUPS; ++l) | |
615 | lan_wr(val, lan966x, ANA_VCAP_S1_CFG(p, l)); | |
616 | ||
617 | lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), | |
618 | ANA_VCAP_CFG_S1_ENA, lan966x, | |
619 | ANA_VCAP_CFG(p)); | |
620 | } | |
621 | ||
622 | break; | |
623 | case VCAP_TYPE_IS2: | |
624 | for (int p = 0; p < lan966x->num_phys_ports; ++p) | |
625 | lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); | |
626 | ||
627 | break; | |
628 | default: | |
629 | pr_err("vcap type: %s not supported\n", | |
630 | lan966x_vcaps[admin->vtype].name); | |
631 | break; | |
632 | } | |
4426b78c HV |
633 | } |
634 | ||
b0531225 HV |
635 | int lan966x_vcap_init(struct lan966x *lan966x) |
636 | { | |
3643abd6 | 637 | struct lan966x_vcap_inst *cfg; |
b0531225 | 638 | struct vcap_control *ctrl; |
3643abd6 | 639 | struct vcap_admin *admin; |
94281484 | 640 | struct dentry *dir; |
b0531225 HV |
641 | |
642 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); | |
643 | if (!ctrl) | |
644 | return -ENOMEM; | |
645 | ||
39bedc16 HV |
646 | ctrl->vcaps = lan966x_vcaps; |
647 | ctrl->stats = &lan966x_vcap_stats; | |
4426b78c | 648 | ctrl->ops = &lan966x_vcap_ops; |
39bedc16 | 649 | |
3643abd6 HV |
650 | INIT_LIST_HEAD(&ctrl->list); |
651 | for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { | |
652 | cfg = &lan966x_vcap_inst_cfg[i]; | |
653 | ||
654 | admin = lan966x_vcap_admin_alloc(lan966x, ctrl, cfg); | |
655 | if (IS_ERR(admin)) | |
656 | return PTR_ERR(admin); | |
657 | ||
658 | lan966x_vcap_block_init(lan966x, admin, cfg); | |
4426b78c HV |
659 | lan966x_vcap_port_key_deselection(lan966x, admin); |
660 | ||
3643abd6 HV |
661 | list_add_tail(&admin->list, &ctrl->list); |
662 | } | |
663 | ||
94281484 HV |
664 | dir = vcap_debugfs(lan966x->dev, lan966x->debugfs_root, ctrl); |
665 | for (int p = 0; p < lan966x->num_phys_ports; ++p) { | |
666 | if (lan966x->ports[p]) { | |
667 | vcap_port_debugfs(lan966x->dev, dir, ctrl, | |
668 | lan966x->ports[p]->dev); | |
669 | ||
01ef75a2 SH |
670 | lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(true), |
671 | ANA_VCAP_S2_CFG_ENA, lan966x, | |
672 | ANA_VCAP_S2_CFG(lan966x->ports[p]->chip_port)); | |
a4d9b3ec HV |
673 | |
674 | lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), | |
675 | ANA_VCAP_CFG_S1_ENA, lan966x, | |
676 | ANA_VCAP_CFG(lan966x->ports[p]->chip_port)); | |
94281484 HV |
677 | } |
678 | } | |
01ef75a2 | 679 | |
b0531225 HV |
680 | lan966x->vcap_ctrl = ctrl; |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
685 | void lan966x_vcap_deinit(struct lan966x *lan966x) | |
686 | { | |
3643abd6 | 687 | struct vcap_admin *admin, *admin_next; |
b0531225 HV |
688 | struct vcap_control *ctrl; |
689 | ||
690 | ctrl = lan966x->vcap_ctrl; | |
691 | if (!ctrl) | |
692 | return; | |
693 | ||
3643abd6 | 694 | list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { |
4426b78c | 695 | lan966x_vcap_port_key_deselection(lan966x, admin); |
3643abd6 HV |
696 | vcap_del_rules(ctrl, admin); |
697 | list_del(&admin->list); | |
698 | lan966x_vcap_admin_free(admin); | |
699 | } | |
700 | ||
b0531225 HV |
701 | kfree(ctrl); |
702 | } |