1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "common_nvswitch.h"
25 #include "ls10/ls10.h"
26 #include "ls10/multicast_ls10.h"
27
28 #include "nvswitch/ls10/dev_route_ip.h"
29
30 // Source: IAS Table 44. Laguna NXbar TileCol Port Mapping
31 static const NVSWITCH_COLUMN_PORT_OFFSET_LS10 nvswitch_portmap_ls10[NVSWITCH_NUM_LINKS_LS10] = {
32 // ports 0 - 10
33 { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
34 { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 },
35 { 0, 8 }, { 0, 9 }, { 0, 10 },
36 // ports 11 - 16
37 { 2, 0 }, { 2, 3 }, { 2, 4 }, { 2, 5 },
38 { 2, 8 },
39 //ports 16 - 26
40 { 4, 10 }, { 4, 9 }, { 4, 8 }, { 4, 7 },
41 { 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 },
42 { 4, 2 }, { 4, 1 }, { 4, 0 },
43 // ports 27 - 31
44 { 2, 9 }, { 2, 7 }, { 2, 6 }, { 2, 2 },
45 { 2, 1 },
46 // ports 32 - 42
47 { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 },
48 { 1, 4 }, { 1, 5 }, { 1, 6 }, { 1, 7 },
49 { 1, 8 }, { 1, 9 }, { 1, 10 },
50 // ports 43 - 47
51 { 3, 0 }, { 3, 3 }, { 3, 4 }, { 3, 5 },
52 { 3, 8 },
53 // ports 48 - 58
54 { 5, 10 }, { 5, 9 }, { 5, 8 }, { 5, 7 },
55 { 5, 6 }, { 5, 5 }, { 5, 4 }, { 5, 3 },
56 { 5, 2 }, { 5, 1 }, { 5, 0 },
57 // ports 59 - 63
58 { 3, 9 }, { 3, 7 }, { 3, 6 }, { 3, 2 },
59 { 3, 1 }
60 };
61
62 static NvlStatus
_nvswitch_get_column_port_offset_ls10(NvU32 port,NVSWITCH_COLUMN_PORT_OFFSET_LS10 * column_port_offset)63 _nvswitch_get_column_port_offset_ls10
64 (
65 NvU32 port,
66 NVSWITCH_COLUMN_PORT_OFFSET_LS10 *column_port_offset
67 )
68 {
69 if (port >= NVSWITCH_NUM_LINKS_LS10)
70 return -NVL_BAD_ARGS;
71
72 *column_port_offset = nvswitch_portmap_ls10[port];
73
74 return NVL_SUCCESS;
75 }
76
77 #if defined(NVSWITCH_MC_TRACE)
78 static void
_nvswitch_mc_print_directive(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 * mc_directive)79 _nvswitch_mc_print_directive
80 (
81 nvswitch_device *device,
82 NVSWITCH_TCP_DIRECTIVE_LS10 *mc_directive
83 )
84 {
85 if (!mc_directive)
86 {
87 NVSWITCH_PRINT(device, ERROR, "%s: null directive pointer\n", __FUNCTION__);
88 return;
89 }
90
91 NVSWITCH_PRINT(device, INFO, "TCP: %4d ", mc_directive->tcp);
92
93 // pretty-print null ports
94 if (mc_directive->tcpEPort == NVSWITCH_MC_NULL_PORT_LS10)
95 {
96 NVSWITCH_PRINT(device, INFO, "EPort: X OPort: %4d",
97 mc_directive->tcpOPort);
98 }
99 else if (mc_directive->tcpOPort == NVSWITCH_MC_NULL_PORT_LS10)
100 {
101 NVSWITCH_PRINT(device, INFO, "EPort: %4d OPort: X",
102 mc_directive->tcpEPort);
103 }
104 else
105 {
106 NVSWITCH_PRINT(device, INFO, "EPort: %4d OPort: %4d",
107 mc_directive->tcpEPort,
108 mc_directive->tcpOPort);
109 }
110
111 NVSWITCH_PRINT(device, INFO, "EAltPath: %4d OAltPath: %4d",
112 mc_directive->tcpEAltPath,
113 mc_directive->tcpOAltPath);
114 NVSWITCH_PRINT(device, INFO, "EVCHop: %4d OVCHop: %4d",
115 mc_directive->tcpEVCHop,
116 mc_directive->tcpOVCHop);
117 NVSWITCH_PRINT(device, INFO, "portFlag: %4d continueRound: %4d lastRound: %4d ",
118 mc_directive->portFlag,
119 mc_directive->continueRound,
120 mc_directive->lastRound);
121 NVSWITCH_PRINT(device, INFO, "\n");
122 }
123
124 static void
_nvswitch_mc_print_directives(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 * mcp_list,NvU32 entries_used,NvU8 * spray_group_ptrs,NvU32 num_spray_groups)125 _nvswitch_mc_print_directives
126 (
127 nvswitch_device *device,
128 NVSWITCH_TCP_DIRECTIVE_LS10 *mcp_list,
129 NvU32 entries_used,
130 NvU8 *spray_group_ptrs,
131 NvU32 num_spray_groups
132 )
133 {
134 NvU32 i, spray_group_offset, round, spray_group_idx, cur_entry_idx, entries_printed;
135 NvBool spray_group_done = NV_FALSE;
136
137 if (num_spray_groups == 0)
138 {
139 NVSWITCH_PRINT(device, ERROR, "%s: No spray groups specified\n", __FUNCTION__);
140 return;
141 }
142
143 if (num_spray_groups > NVSWITCH_MC_MAX_SPRAY_LS10)
144 {
145 NVSWITCH_PRINT(device, ERROR, "%s: Too many spray groups specified: %d\n",
146 __FUNCTION__, num_spray_groups);
147 return;
148 }
149
150 if (entries_used > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
151 {
152 NVSWITCH_PRINT(device, ERROR, "%s: Too many entries specified: %d\n",
153 __FUNCTION__, entries_used);
154 return;
155 }
156
157
158 NVSWITCH_PRINT(device, INFO, "Total spray groups %d\n", num_spray_groups);
159
160 entries_printed = 0;
161
162 // loop through spray groups
163 for (spray_group_idx = 0; spray_group_idx < num_spray_groups; spray_group_idx++)
164 {
165 spray_group_done = NV_FALSE;
166 spray_group_offset = spray_group_ptrs[spray_group_idx];
167 cur_entry_idx = spray_group_offset;
168 round = 0;
169
170 NVSWITCH_PRINT(device, INFO, "Spray group %d offset %d\n", spray_group_idx,
171 spray_group_offset);
172
173 while (!spray_group_done)
174 {
175 if (entries_printed >= NVSWITCH_MC_TCP_LIST_SIZE_LS10)
176 {
177 NVSWITCH_PRINT(device, ERROR, "%s: Overflow of mcplist. Entries printed: %d\n",
178 __FUNCTION__, entries_printed);
179 return;
180 }
181
182 NVSWITCH_PRINT(device, INFO, "Round %d, First mc_plist Index %d round size %d\n",
183 round, cur_entry_idx, mcp_list[cur_entry_idx].roundSize);
184
185 for (i = 0; i < mcp_list[cur_entry_idx].roundSize; i++)
186 {
187 if ((i + cur_entry_idx) > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
188 {
189 NVSWITCH_PRINT(device, ERROR, "%s: Overflow of mcplist. %d\n",
190 __FUNCTION__, i + cur_entry_idx);
191 }
192
193 _nvswitch_mc_print_directive(device, &mcp_list[i + cur_entry_idx]);
194 entries_printed++;
195
196 if (mcp_list[i + cur_entry_idx].lastRound)
197 {
198 NVSWITCH_PRINT(device, INFO, "Last round of spray group found at offset %d\n",
199 i + cur_entry_idx);
200 spray_group_done = NV_TRUE;
201 }
202 }
203
204 round++;
205 cur_entry_idx += i;
206 }
207
208 }
209 }
210 #endif // defined(NVSWITCH_MC_TRACE)
211
212 //
213 // Build column-port bitmap. Each 32-bit portmap in the array represents a column.
214 // Each bit set in the portmap represents the column-relative port offset.
215 //
216 static NvlStatus
_nvswitch_mc_build_cpb(nvswitch_device * device,NvU32 num_ports,NvU32 * spray_group,NvU32 num_columns,NvU32 * cpb,NvU8 * vchop_array_sg,NvU8 vchop_map[NVSWITCH_MC_NUM_COLUMNS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10])217 _nvswitch_mc_build_cpb
218 (
219 nvswitch_device *device,
220 NvU32 num_ports,
221 NvU32 *spray_group,
222 NvU32 num_columns,
223 NvU32 *cpb,
224 NvU8 *vchop_array_sg,
225 NvU8 vchop_map[NVSWITCH_MC_NUM_COLUMNS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10]
226 )
227 {
228 NvU32 i, ret;
229 NVSWITCH_COLUMN_PORT_OFFSET_LS10 cpo;
230
231 if ((spray_group == NULL) || (cpb == NULL) || (num_ports == 0) ||
232 (num_ports > NVSWITCH_NUM_LINKS_LS10))
233 {
234 NVSWITCH_PRINT(device, ERROR, "%s: invalid arguments\n", __FUNCTION__);
235 return -NVL_BAD_ARGS;
236 }
237
238 nvswitch_os_memset(cpb, 0, sizeof(*cpb) * num_columns);
239 nvswitch_os_memset(vchop_map, 0, sizeof(NvU8) *
240 NVSWITCH_MC_NUM_COLUMNS_LS10 * NVSWITCH_MC_PORTS_PER_COLUMN_LS10);
241
242 for (i = 0; i < num_ports; i++)
243 {
244 ret = _nvswitch_get_column_port_offset_ls10(spray_group[i], &cpo);
245 if (ret != NVL_SUCCESS)
246 {
247 NVSWITCH_PRINT(device, ERROR, "%s: error getting column-port offset\n", __FUNCTION__);
248 return ret;
249 }
250
251 if (nvswitch_test_flags(cpb[cpo.column], NVBIT(cpo.port_offset)))
252 {
253 NVSWITCH_PRINT(device, ERROR, "%s: duplicate port specified: %d\n", __FUNCTION__,
254 spray_group[i]);
255 return -NVL_BAD_ARGS;
256 }
257
258 nvswitch_set_flags(&cpb[cpo.column], NVBIT(cpo.port_offset));
259
260 if (vchop_array_sg[i] > NVSWITCH_MC_VCHOP_FORCE1)
261 {
262 NVSWITCH_PRINT(device, ERROR, "%s: vchop value out of range: %d\n", __FUNCTION__,
263 vchop_array_sg[i]);
264 return -NVL_BAD_ARGS;
265 }
266
267
268 vchop_map[cpo.column][cpo.port_offset] = vchop_array_sg[i];
269 }
270
271 return NVL_SUCCESS;
272 }
273
274 //
275 // Determine whether the given column/offset pair matches the given absolute
276 // primary_replica port number.
277 //
278 static NvBool
_is_primary_replica(NvU32 col,NvU32 offset,NvU32 primary_replica)279 _is_primary_replica
280 (
281 NvU32 col,
282 NvU32 offset,
283 NvU32 primary_replica
284 )
285 {
286 NVSWITCH_COLUMN_PORT_OFFSET_LS10 cpo;
287
288 if (primary_replica == NVSWITCH_MC_INVALID)
289 return NV_FALSE;
290
291 if (_nvswitch_get_column_port_offset_ls10(primary_replica, &cpo) != NVL_SUCCESS)
292 return NV_FALSE;
293
294 if ((cpo.column == col) && (cpo.port_offset == offset))
295 return NV_TRUE;
296
297 return NV_FALSE;
298 }
299
300 //
301 // This function compacts the directive list and updates port_list_size
302 //
303 static NvlStatus
_nvswitch_mc_compact_portlist(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 * port_list,NvU32 * port_list_size)304 _nvswitch_mc_compact_portlist
305 (
306 nvswitch_device *device,
307 NVSWITCH_TCP_DIRECTIVE_LS10 *port_list,
308 NvU32 *port_list_size
309 )
310 {
311 NvU32 cur_portlist_pos, new_portlist_pos;
312 NVSWITCH_TCP_DIRECTIVE_LS10 *cur_dir, *old_list;
313
314 if (port_list_size == NULL)
315 {
316 NVSWITCH_PRINT(device, ERROR, "%s: port list size ptr is null\n", __FUNCTION__);
317 return -NVL_BAD_ARGS;
318 }
319
320 if ((port_list == NULL) || (*port_list_size == 0))
321 return NVL_SUCCESS;
322
323 if ((*port_list_size) > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
324 {
325 NVSWITCH_PRINT(device, ERROR, "%s: port list size out of range\n", __FUNCTION__);
326 return -NVL_BAD_ARGS;
327 }
328
329 #ifdef NVSWITCH_MC_DEBUG
330 NVSWITCH_PRINT(device, INFO, "%s: old size: %d\n", __FUNCTION__, *port_list_size);
331 #endif
332
333 // create temporary directive list
334 old_list = nvswitch_os_malloc(sizeof(NVSWITCH_TCP_DIRECTIVE_LS10) * (*port_list_size));
335
336 if (!old_list)
337 {
338 NVSWITCH_PRINT(device, ERROR, "%s: error allocating temporary portlist\n", __FUNCTION__);
339 return -NVL_NO_MEM;
340 }
341
342 nvswitch_os_memcpy(old_list, port_list, sizeof(NVSWITCH_TCP_DIRECTIVE_LS10) * (*port_list_size));
343
344 // rebuild list using only valid entries
345 new_portlist_pos = 0;
346
347 for (cur_portlist_pos = 0; cur_portlist_pos < (*port_list_size); cur_portlist_pos++)
348 {
349 cur_dir = &old_list[cur_portlist_pos];
350
351 if (cur_dir->tcp != NVSWITCH_MC_INVALID)
352 {
353 #ifdef NVSWITCH_MC_TRACE
354 NVSWITCH_PRINT(device, INFO, "%s: valid directive:\n", __FUNCTION__);
355 _nvswitch_mc_print_directive(device, &old_list[cur_portlist_pos]);
356 #endif
357 nvswitch_os_memcpy(&port_list[new_portlist_pos], &old_list[cur_portlist_pos],
358 sizeof(NVSWITCH_TCP_DIRECTIVE_LS10));
359 new_portlist_pos++;
360 }
361 }
362
363 nvswitch_os_free(old_list);
364
365 #ifdef NVSWITCH_MC_DEBUG
366 NVSWITCH_PRINT(device, INFO, "%s: new size: %d\n", __FUNCTION__, new_portlist_pos);
367 #endif
368
369 *port_list_size = new_portlist_pos;
370
371 return NVL_SUCCESS;
372 }
373
374 //
375 // Set the round flags to indicate the size of each multicast round.
376 // See IAS section "6.12. Consistent MC Semantics" for more info.
377 //
378 static void
_nvswitch_mc_set_round_flags(NVSWITCH_TCP_DIRECTIVE_LS10 * port_list,NvU32 port_list_size)379 _nvswitch_mc_set_round_flags
380 (
381 NVSWITCH_TCP_DIRECTIVE_LS10 *port_list,
382 NvU32 port_list_size
383 )
384 {
385 NvU32 cur_portlist_pos, round_size, round_start, round_end;
386 NVSWITCH_TCP_DIRECTIVE_LS10 *cur_dir, *next_dir;
387
388 if ((port_list == NULL) || (port_list_size == 0))
389 return;
390
391 round_start = 0;
392 round_end = 0;
393
394 for (cur_portlist_pos = 0; cur_portlist_pos < port_list_size; cur_portlist_pos++)
395 {
396 cur_dir = &port_list[cur_portlist_pos];
397
398 // special case: last element: end of round and last round
399 if (cur_portlist_pos == port_list_size - 1)
400 {
401 cur_dir->continueRound = NV_FALSE;
402 cur_dir->lastRound = NV_TRUE;
403
404 round_end = cur_portlist_pos;
405 round_size = round_end - round_start + 1;
406
407 // set the round size in the first directive
408 cur_dir = &port_list[round_start];
409 cur_dir->roundSize = (NvU8)round_size;
410 }
411 else
412 {
413 // if next tcp is less than or equal to the current, then current is end of round
414 next_dir = &port_list[cur_portlist_pos + 1];
415 if (next_dir->tcp <= cur_dir->tcp)
416 {
417 cur_dir->continueRound = NV_FALSE;
418
419 round_end = cur_portlist_pos;
420 round_size = round_end - round_start + 1;
421
422 // set the round size in the first directive
423 cur_dir = &port_list[round_start];
424 cur_dir->roundSize = (NvU8)round_size;
425
426 // advance round_start
427 round_start = cur_portlist_pos + 1;
428 }
429 }
430 }
431 }
432
433 //
434 // Set the port flags to indicate primary replica port location.
435 // See IAS section "6.12. Consistent MC Semantics" for more info.
436 //
437 static void
_nvswitch_mc_set_port_flags(NVSWITCH_TCP_DIRECTIVE_LS10 * port_list,NvU32 port_list_size)438 _nvswitch_mc_set_port_flags
439 (
440 NVSWITCH_TCP_DIRECTIVE_LS10 *port_list,
441 NvU32 port_list_size
442 )
443 {
444 NvU32 cur_portlist_pos;
445 NVSWITCH_TCP_DIRECTIVE_LS10 *cur_dir, *next_dir;
446
447 if ((port_list == NULL) || (port_list_size == 0))
448 return;
449
450 for (cur_portlist_pos = 0; cur_portlist_pos < port_list_size; cur_portlist_pos++)
451 {
452 cur_dir = &port_list[cur_portlist_pos];
453
454 if (cur_dir->primaryReplica != PRIMARY_REPLICA_NONE)
455 {
456 if (cur_dir->lastRound)
457 {
458 cur_dir->continueRound = NV_TRUE;
459
460 if (cur_dir->primaryReplica == PRIMARY_REPLICA_EVEN)
461 cur_dir->portFlag = 0;
462 if (cur_dir->primaryReplica == PRIMARY_REPLICA_ODD)
463 cur_dir->portFlag = 1;
464 }
465 else
466 {
467 // primary replica is in this directive, next directive specifies even or odd
468 cur_dir->portFlag = 1;
469
470 if (cur_portlist_pos + 1 >= port_list_size)
471 {
472 NVSWITCH_ASSERT(0);
473 return;
474 }
475
476 next_dir = &port_list[cur_portlist_pos + 1];
477
478 if (cur_dir->primaryReplica == PRIMARY_REPLICA_EVEN)
479 next_dir->portFlag = 0;
480 if (cur_dir->primaryReplica == PRIMARY_REPLICA_ODD)
481 next_dir->portFlag = 1;
482 }
483 }
484 }
485 }
486
487 //
488 // This function "pops" the next port offset from the portlist bitmap.
489 //
490 static NV_INLINE NvU8
_nvswitch_mc_get_next_port(NvU32 * portmap)491 _nvswitch_mc_get_next_port
492 (
493 NvU32 *portmap
494 )
495 {
496 NvU32 port;
497
498 if (!portmap)
499 {
500 NVSWITCH_ASSERT(0);
501 return NVSWITCH_MC_NULL_PORT_LS10;
502 }
503
504 //
505 // We have to do some gymnastics here because LOWESTBITIDX_32 is
506 // destructive on the input variable, and the result is not assignable.
507 //
508 port = *portmap;
509 LOWESTBITIDX_32(port);
510 nvswitch_clear_flags(portmap, NVBIT(port));
511
512 if (port >= NVSWITCH_MC_PORTS_PER_COLUMN_LS10)
513 {
514 NVSWITCH_ASSERT(0);
515 return NVSWITCH_MC_NULL_PORT_LS10;
516 }
517
518 return (NvU8)port;
519 }
520
521 //
522 // This helper function generates a map of directive list offsets indexed by tile/column pair
523 // port offsets. This is used during construction of the directive list to point to where each
524 // newly constructed directive will be placed in the list. This process has to account for the
525 // fact that the middle two columns contain 10 ports each, while the rest have 11, all mapping
526 // into a 32-entry directive list.
527 //
528 static NV_INLINE void
_nvswitch_mc_build_mcplist_position_map(NvU32 port_offsets_by_tcp[NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10])529 _nvswitch_mc_build_mcplist_position_map
530 (
531 NvU32 port_offsets_by_tcp[NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10]
532 )
533 {
534 NvU32 i, j, tcp;
535
536 if (!port_offsets_by_tcp)
537 {
538 NVSWITCH_ASSERT(0);
539 return;
540 }
541
542 for (tcp = 0; tcp < NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10; tcp++)
543 {
544 if (tcp == 0)
545 {
546 j = 0;
547 for (i = 0; i < NVSWITCH_MC_PORTS_PER_COLUMN_LS10; i++)
548 {
549 port_offsets_by_tcp[tcp][i] = j;
550 j += NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10;
551 }
552 }
553
554 if (tcp == 1)
555 {
556 j = 1;
557 for (i = 0; i < NVSWITCH_MC_PORTS_PER_COLUMN_LS10 - 1; i++)
558 {
559 port_offsets_by_tcp[tcp][i] = j;
560 j += NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10;
561 }
562 }
563
564 if (tcp == 2)
565 {
566 j = 2;
567 for (i = 0; i < NVSWITCH_MC_PORTS_PER_COLUMN_LS10; i++)
568 {
569 port_offsets_by_tcp[tcp][i] = (j == NVSWITCH_MC_TCP_LIST_SIZE_LS10) ?
570 (NVSWITCH_MC_TCP_LIST_SIZE_LS10 - 1) : j;
571 j += NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10;
572 }
573 }
574 }
575 }
576
577 //
578 // Wrapper for the NUMSETBITS_32 macro, which is destructive on input.
579 //
580 static NV_INLINE NvU32
_nvswitch_mc_get_pop_count(NvU32 i)581 _nvswitch_mc_get_pop_count
582 (
583 NvU32 i
584 )
585 {
586 NvU32 tmp = i;
587
588 NUMSETBITS_32(tmp);
589
590 return tmp;
591 }
592
593 //
594 // Build a list of TCP directives. This is the main conversion function which is used to build a
595 // TCP directive list for each spray group from a given column/port bitmap.
596 //
597 // @param device [in] pointer to the nvswitch device struct
598 // @param cpb [in] pointer to the column/port bitmap used to build directive list
599 // @param primary_replica [in] the primary replica port for this spray group, if specified
600 // @param vchop_map [in] array containing per-port vchop values in column/port format
601 // @param port_list [out] array where the newly built directive list is written
602 // @param entries_used [out] pointer to an int where the size of resulting list is written
603 //
604 static NvlStatus
_nvswitch_mc_build_portlist(nvswitch_device * device,NvU32 * cpb,NvU32 primary_replica,NvU8 vchop_map[NVSWITCH_MC_NUM_COLUMNS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10],NVSWITCH_TCP_DIRECTIVE_LS10 * port_list,NvU32 * entries_used)605 _nvswitch_mc_build_portlist
606 (
607 nvswitch_device *device,
608 NvU32 *cpb,
609 NvU32 primary_replica,
610 NvU8 vchop_map[NVSWITCH_MC_NUM_COLUMNS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10],
611 NVSWITCH_TCP_DIRECTIVE_LS10 *port_list,
612 NvU32 *entries_used
613 )
614 {
615 NvU32 ecol_idx, ocol_idx, ecol_portcount, ocol_portcount, ecol_portmap, ocol_portmap;
616 NvU32 cur_portlist_pos, j, cur_portlist_slot, last_portlist_pos;
617 NvU8 cur_eport, cur_oport, i;
618 NvS32 extra_ports;
619 NvU32 port_offsets_by_tcp[NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10];
620 NVSWITCH_TCP_DIRECTIVE_LS10 *cur_dir;
621
622 if ((cpb == NULL) || (port_list == NULL))
623 {
624 NVSWITCH_PRINT(device, ERROR, "%s: Invalid arguments\n", __FUNCTION__);
625 return -NVL_BAD_ARGS;
626 }
627
628 _nvswitch_mc_build_mcplist_position_map(port_offsets_by_tcp);
629
630 //
631 // process columns pairwise. if one column is larger than the other by 2 or more entries,
632 // set the port as alt path
633 //
634
635 cur_portlist_pos = 0;
636 last_portlist_pos = 0;
637 cur_portlist_slot = 0;
638
639 for ( i = 0; i < NVSWITCH_MC_NUM_COLUMN_PAIRS_LS10; i++ )
640 {
641 ecol_idx = 2 * i;
642 ocol_idx = 2 * i + 1;
643
644 ecol_portmap = cpb[ecol_idx];
645 ocol_portmap = cpb[ocol_idx];
646
647 ecol_portcount = _nvswitch_mc_get_pop_count(ecol_portmap);
648 ocol_portcount = _nvswitch_mc_get_pop_count(ocol_portmap);
649
650 extra_ports = ecol_portcount - ocol_portcount;
651
652 // Start current portlist position on column offset of the current column
653 cur_portlist_slot = 0;
654 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
655
656 if ( extra_ports >= 0 )
657 {
658 //
659 // even column has more ports or both columns have an equal number
660 // iterate on odd column port count to go through both columns
661 //
662 for (j = 0; j < ocol_portcount; j++, cur_portlist_slot++)
663 {
664 cur_eport = _nvswitch_mc_get_next_port(&ecol_portmap);
665 cur_oport = _nvswitch_mc_get_next_port(&ocol_portmap);
666 if ((cur_eport == NVSWITCH_MC_NULL_PORT_LS10) ||
667 (cur_oport == NVSWITCH_MC_NULL_PORT_LS10))
668 {
669 return -NVL_ERR_GENERIC;
670 }
671
672 // assign the ports to the current directive
673 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
674 cur_dir = &port_list[cur_portlist_pos];
675 cur_dir->tcpEPort = cur_eport;
676 cur_dir->tcpOPort = cur_oport;
677
678 cur_dir->tcpEVCHop = vchop_map[ecol_idx][cur_eport];
679 cur_dir->tcpOVCHop = vchop_map[ocol_idx][cur_oport];
680
681 cur_dir->tcp = i;
682
683 #ifdef NVSWITCH_MC_TRACE
684 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_eport: %d, cur_oport %d\n",
685 __FUNCTION__, i, extra_ports, cur_eport, cur_oport);
686 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
687 cur_portlist_pos);
688 #endif
689 // set primary replica
690 if (_is_primary_replica(ocol_idx, cur_oport, primary_replica))
691 cur_dir->primaryReplica = PRIMARY_REPLICA_ODD;
692
693 if (_is_primary_replica(ecol_idx, cur_eport, primary_replica))
694 cur_dir->primaryReplica = PRIMARY_REPLICA_EVEN;
695
696 }
697
698 // if both columns had the same number of ports, move on to the next column pair
699 if (!extra_ports)
700 {
701 last_portlist_pos = NV_MAX(last_portlist_pos, cur_portlist_pos);
702 continue;
703 }
704
705 //
706 // otherwise, handle remaining ports in even column
707 // for the first extra port, assign it directly
708 // cur_portlist_slot is incremented by the last iteration, or 0
709 //
710 cur_eport = _nvswitch_mc_get_next_port(&ecol_portmap);
711 if (cur_eport == NVSWITCH_MC_NULL_PORT_LS10)
712 {
713 return -NVL_ERR_GENERIC;
714 }
715
716 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
717 cur_dir = &port_list[cur_portlist_pos];
718 cur_dir->tcpEPort = cur_eport;
719
720 cur_dir->tcpEVCHop = vchop_map[ecol_idx][cur_eport];
721
722 cur_dir->tcp = i;
723
724 #ifdef NVSWITCH_MC_TRACE
725 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_eport: %d\n",
726 __FUNCTION__, i, extra_ports, cur_eport);
727 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
728 cur_portlist_pos);
729 #endif
730
731 // if this is the primary replica port, mark it
732 if (_is_primary_replica(ecol_idx, cur_eport, primary_replica))
733 cur_dir->primaryReplica = PRIMARY_REPLICA_EVEN;
734
735 extra_ports--;
736
737 // if there are more, assign to altpath
738 while (extra_ports)
739 {
740 // get next port from even column
741 cur_eport = _nvswitch_mc_get_next_port(&ecol_portmap);
742 if (cur_eport == NVSWITCH_MC_NULL_PORT_LS10)
743 {
744 return -NVL_ERR_GENERIC;
745 }
746
747 // assign it to odd port in current directive (altpath)
748 cur_dir->tcpOPort = cur_eport;
749 cur_dir->tcpOAltPath = NV_TRUE;
750
751 cur_dir->tcpOVCHop = vchop_map[ecol_idx][cur_eport];
752
753 #ifdef NVSWITCH_MC_TRACE
754 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_eport: %d (alt)\n",
755 __FUNCTION__, i, extra_ports, cur_eport);
756 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
757 cur_portlist_pos);
758 #endif
759 // if this is the primary replica port, mark _ODD due to altpath
760 if (_is_primary_replica(ecol_idx, cur_eport, primary_replica))
761 cur_dir->primaryReplica = PRIMARY_REPLICA_ODD;
762
763 extra_ports--;
764
765 // if there are more ports remaining, start the next entry
766 if (extra_ports)
767 {
768 // advance the portlist entry
769 cur_portlist_slot++;
770 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
771 cur_dir = &port_list[cur_portlist_pos];
772
773 cur_eport = _nvswitch_mc_get_next_port(&ecol_portmap);
774 if (cur_eport == NVSWITCH_MC_NULL_PORT_LS10)
775 {
776 return -NVL_ERR_GENERIC;
777 }
778
779 cur_dir->tcpEPort = cur_eport;
780
781 cur_dir->tcpEVCHop = vchop_map[ecol_idx][cur_eport];
782
783 cur_dir->tcp = i;
784
785 #ifdef NVSWITCH_MC_TRACE
786 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_eport: %d\n",
787 __FUNCTION__, i, extra_ports, cur_eport);
788 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
789 cur_portlist_pos);
790 #endif
791
792 // if this is the primary replica port, mark it
793 if (_is_primary_replica(ecol_idx, cur_eport, primary_replica))
794 cur_dir->primaryReplica = PRIMARY_REPLICA_EVEN;
795
796 extra_ports--;
797 }
798 }
799 }
800 else
801 {
802 // odd column has more ports
803 extra_ports = -extra_ports;
804
805 // iterate over even column to go through port pairs
806 for (j = 0; j < ecol_portcount; j++, cur_portlist_slot++)
807 {
808 cur_eport = _nvswitch_mc_get_next_port(&ecol_portmap);
809 cur_oport = _nvswitch_mc_get_next_port(&ocol_portmap);
810 if ((cur_eport == NVSWITCH_MC_NULL_PORT_LS10) ||
811 (cur_oport == NVSWITCH_MC_NULL_PORT_LS10))
812 {
813 return -NVL_ERR_GENERIC;
814 }
815
816 // assign the ports to the current directive
817 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
818 cur_dir = &port_list[cur_portlist_pos];
819 cur_dir->tcpEPort = cur_eport;
820 cur_dir->tcpOPort = cur_oport;
821
822 cur_dir->tcpEVCHop = vchop_map[ecol_idx][cur_eport];
823 cur_dir->tcpOVCHop = vchop_map[ocol_idx][cur_oport];
824
825 cur_dir->tcp = i;
826
827 #ifdef NVSWITCH_MC_TRACE
828 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_eport: %d, cur_oport %d\n",
829 __FUNCTION__, i, extra_ports, cur_eport, cur_oport);
830 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
831 cur_portlist_pos);
832 #endif
833 if (_is_primary_replica(ocol_idx, cur_oport, primary_replica))
834 cur_dir->primaryReplica = PRIMARY_REPLICA_ODD;
835 if (_is_primary_replica(ecol_idx, cur_eport, primary_replica))
836 cur_dir->primaryReplica = PRIMARY_REPLICA_EVEN;
837
838 }
839
840 // handle the leftover ports in odd column
841 cur_oport = _nvswitch_mc_get_next_port(&ocol_portmap);
842 if (cur_oport == NVSWITCH_MC_NULL_PORT_LS10)
843 {
844 return -NVL_ERR_GENERIC;
845 }
846
847 // cur_portlist_slot is incremented by the last iteration, or 0
848 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
849 cur_dir = &port_list[cur_portlist_pos];
850
851 cur_dir->tcpOPort = cur_oport;
852
853 cur_dir->tcpOVCHop = vchop_map[ocol_idx][cur_oport];
854
855 cur_dir->tcp = i;
856
857 #ifdef NVSWITCH_MC_TRACE
858 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_oport %d\n",
859 __FUNCTION__, i, extra_ports, cur_oport);
860 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
861 cur_portlist_pos);
862 #endif
863
864 if (_is_primary_replica(ocol_idx, cur_oport, primary_replica))
865 cur_dir->primaryReplica = PRIMARY_REPLICA_ODD;
866
867 extra_ports--;
868
869 // process any remaining ports in odd column
870 while (extra_ports)
871 {
872 // get next odd port
873 cur_oport = _nvswitch_mc_get_next_port(&ocol_portmap);
874 if (cur_oport == NVSWITCH_MC_NULL_PORT_LS10)
875 {
876 return -NVL_ERR_GENERIC;
877 }
878
879 // set it as even altpath port in current directive
880 cur_dir->tcpEPort = cur_oport;
881 cur_dir->tcpEAltPath = NV_TRUE;
882
883 cur_dir->tcpEVCHop = vchop_map[ocol_idx][cur_oport];
884
885 #ifdef NVSWITCH_MC_TRACE
886 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_oport %d (alt)\n",
887 __FUNCTION__, i, extra_ports, cur_oport);
888 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
889 cur_portlist_pos);
890 #endif
891 // if this is the primary replica port, mark _EVEN due to altpath
892 if (_is_primary_replica(ocol_idx, cur_oport, primary_replica))
893 cur_dir->primaryReplica = PRIMARY_REPLICA_EVEN;
894
895 extra_ports--;
896
897 // if there is another port, it goes in the next directive
898 if (extra_ports)
899 {
900 cur_portlist_slot++;
901 cur_portlist_pos = port_offsets_by_tcp[i][cur_portlist_slot];
902 cur_dir = &port_list[cur_portlist_pos];
903
904 cur_oport = _nvswitch_mc_get_next_port(&ocol_portmap);
905 if (cur_oport == NVSWITCH_MC_NULL_PORT_LS10)
906 {
907 return -NVL_ERR_GENERIC;
908 }
909
910 cur_dir->tcpOPort = cur_oport;
911
912 cur_dir->tcpOVCHop = vchop_map[ocol_idx][cur_oport];
913
914 cur_dir->tcp = i;
915
916 #ifdef NVSWITCH_MC_TRACE
917 NVSWITCH_PRINT(device, INFO, "%s: tcp: %d, extra: %d, cur_oport %d\n",
918 __FUNCTION__, i, extra_ports, cur_oport);
919 NVSWITCH_PRINT(device, INFO, "%s: cur_portlist_pos: %d\n", __FUNCTION__,
920 cur_portlist_pos);
921 #endif
922
923 if (_is_primary_replica(ocol_idx, cur_oport, primary_replica))
924 cur_dir->primaryReplica = PRIMARY_REPLICA_ODD;
925
926 extra_ports--;
927 }
928 }
929 }
930
931 last_portlist_pos = NV_MAX(last_portlist_pos, cur_portlist_pos);
932 }
933
934 // set the lastRound flag for the last entry in the spray string
935 cur_dir = &port_list[last_portlist_pos];
936 cur_dir->lastRound = NV_TRUE;
937
938 *entries_used = last_portlist_pos + 1;
939
940 #ifdef NVSWITCH_MC_DEBUG
941 NVSWITCH_PRINT(device, INFO,
942 "%s: entries_used: %d, cur_portlist_pos: %d last_portlist_pos: %d\n",
943 __FUNCTION__, *entries_used, cur_portlist_pos, last_portlist_pos);
944 #endif
945
946 return NVL_SUCCESS;
947 }
948
949 //
950 // Helper that initializes a given directive list to some base values.
951 //
952 static NV_INLINE NvlStatus
nvswitch_init_portlist_ls10(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 * mcp_list,NvU32 mcp_list_size)953 nvswitch_init_portlist_ls10
954 (
955 nvswitch_device *device,
956 NVSWITCH_TCP_DIRECTIVE_LS10 *mcp_list,
957 NvU32 mcp_list_size
958 )
959 {
960 NvU32 i;
961
962 if (mcp_list_size > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
963 {
964 NVSWITCH_PRINT(device, ERROR, "%s: mcp_list_size out of range (%d)\n",
965 __FUNCTION__, mcp_list_size);
966 return -NVL_BAD_ARGS;
967 }
968
969 nvswitch_os_memset(mcp_list, 0,
970 sizeof(NVSWITCH_TCP_DIRECTIVE_LS10) * mcp_list_size);
971
972 //
973 // initialize port list with invalid values
974 // continueRound will be fixed up when processing round flags
975 //
976 for ( i = 0; i < mcp_list_size; i ++ )
977 {
978 mcp_list[i].tcp = NVSWITCH_MC_INVALID;
979 mcp_list[i].continueRound = NV_TRUE;
980 mcp_list[i].tcpEPort = NVSWITCH_MC_NULL_PORT_LS10;
981 mcp_list[i].tcpOPort = NVSWITCH_MC_NULL_PORT_LS10;
982 }
983
984 return NVL_SUCCESS;
985 }
986
987
988 //
989 // Helper to traverse list of directives given in src and copy only valid entries to dst starting
990 // at dst_list_offset.
991 //
992 // This is used when building the final directive list from individual per-spray-group lists,
993 // ensuring that no invalid entries sneak in, as well as checking for a nontrivial corner case
994 // where a configuration of input spray groups can result in a directive list larger than the
995 // 32-entry space allowed in the table. This returns -NVL_MORE_PROCESSING_REQUIRED which is
996 // then propagated to the caller to adjust the input parameters and try again.
997 //
998 static NV_INLINE NvlStatus
_nvswitch_mc_copy_valid_entries_ls10(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 * dst,NVSWITCH_TCP_DIRECTIVE_LS10 * src,NvU32 num_valid_entries,NvU32 dst_list_offset)999 _nvswitch_mc_copy_valid_entries_ls10
1000 (
1001 nvswitch_device *device,
1002 NVSWITCH_TCP_DIRECTIVE_LS10 *dst,
1003 NVSWITCH_TCP_DIRECTIVE_LS10 *src,
1004 NvU32 num_valid_entries,
1005 NvU32 dst_list_offset
1006 )
1007 {
1008 NvU32 i;
1009
1010 if (num_valid_entries + dst_list_offset > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
1011 {
1012 NVSWITCH_PRINT(device, ERROR,
1013 "%s: Overflow of mcplist. num_valid_entries: %d, dst_list_offset: %d\n",
1014 __FUNCTION__, num_valid_entries, dst_list_offset);
1015 return -NVL_MORE_PROCESSING_REQUIRED;
1016 }
1017
1018 for (i = 0; i < num_valid_entries; i++)
1019 {
1020 if (src[i].tcp == NVSWITCH_MC_INVALID)
1021 {
1022 NVSWITCH_PRINT(device, ERROR, "%s: invalid entry at offset %d\n", __FUNCTION__, i);
1023 return -NVL_ERR_GENERIC;
1024 }
1025
1026 #ifdef NVSWITCH_MC_TRACE
1027 NVSWITCH_PRINT(device, INFO, "%s: copying entry from src[%d] to dst[%d]\n",
1028 __FUNCTION__, i, dst_list_offset + i);
1029 _nvswitch_mc_print_directive(device, &src[i]);
1030 #endif
1031
1032 nvswitch_os_memcpy(&dst[dst_list_offset + i], &src[i], sizeof(NVSWITCH_TCP_DIRECTIVE_LS10));
1033 }
1034
1035 return NVL_SUCCESS;
1036 }
1037
1038
1039 //
1040 // Build multicast directive list using the inputs given.
1041 //
1042 // @param device [in] pointer to the nvswitch device struct
1043 // @param port_list [in] array of ports for all spray groups
1044 // @param ports_per_spray_group [in] array specifying the size of each spray group
1045 // @param pri_replica_offsets [in] array, offsets of primary replica ports for each spray group
1046 // @param replica_valid_array [in] array, specifies which pri_replica_offsets are valid
1047 // @param vchop_array [in] array of vchop values for each port given in port_list
1048 // @param table_entry [out] pointer to table entry where directive list will be written
1049 // @param entries_used [out] pointer, number of valid entries produced is written here
1050 //
1051 NvlStatus
nvswitch_mc_build_mcp_list_ls10(nvswitch_device * device,NvU32 * port_list,NvU32 * ports_per_spray_group,NvU32 * pri_replica_offsets,NvBool * replica_valid_array,NvU8 * vchop_array,NVSWITCH_MC_RID_ENTRY_LS10 * table_entry,NvU32 * entries_used)1052 nvswitch_mc_build_mcp_list_ls10
1053 (
1054 nvswitch_device *device,
1055 NvU32 *port_list,
1056 NvU32 *ports_per_spray_group,
1057 NvU32 *pri_replica_offsets,
1058 NvBool *replica_valid_array,
1059 NvU8 *vchop_array,
1060 NVSWITCH_MC_RID_ENTRY_LS10 *table_entry,
1061 NvU32 *entries_used
1062 )
1063 {
1064 NvU32 i, spray_group_idx, spray_group_size, num_spray_groups, ret;
1065 NvU8 *spray_group_ptrs;
1066 NvU32 spray_group_offset = 0;
1067 NvU32 primary_replica_port = NVSWITCH_MC_INVALID;
1068 NvU32 dir_entries_used_sg = 0;
1069 NvU32 dir_entries_used = 0;
1070 NvU32 mcplist_offset = 0;
1071 NvU32 cpb[NVSWITCH_MC_NUM_COLUMNS_LS10] = { 0 };
1072 NvU8 vchop_map[NVSWITCH_MC_NUM_COLUMNS_LS10][NVSWITCH_MC_PORTS_PER_COLUMN_LS10];
1073 NVSWITCH_TCP_DIRECTIVE_LS10 tmp_mcp_list[NVSWITCH_MC_TCP_LIST_SIZE_LS10];
1074 NVSWITCH_TCP_DIRECTIVE_LS10 *mcp_list;
1075
1076 NvU32 j;
1077
1078 if ((device == NULL) || (port_list == NULL) || (ports_per_spray_group == NULL) ||
1079 (pri_replica_offsets == NULL) || (replica_valid_array == NULL) || (vchop_array == NULL) ||
1080 (table_entry == NULL) || (entries_used == NULL))
1081 {
1082 return -NVL_BAD_ARGS;
1083 }
1084
1085 num_spray_groups = table_entry->num_spray_groups;
1086 spray_group_ptrs = table_entry->spray_group_ptrs;
1087 mcp_list = table_entry->directives;
1088
1089 if (num_spray_groups == 0)
1090 {
1091 NVSWITCH_PRINT(device, ERROR, "%s: No spray groups specified\n", __FUNCTION__);
1092 return -NVL_BAD_ARGS;
1093 }
1094
1095 if (num_spray_groups > NVSWITCH_MC_MAX_SPRAYGROUPS)
1096 {
1097 NVSWITCH_PRINT(device, ERROR, "%s: Too many spray groups specified: %d\n",
1098 __FUNCTION__, num_spray_groups);
1099 return -NVL_BAD_ARGS;
1100 }
1101
1102 for (i = 0, j = 0; i < num_spray_groups; i++)
1103 {
1104 if (ports_per_spray_group[i] < NVSWITCH_MC_MIN_PORTS_PER_GROUP_LS10)
1105 {
1106 NVSWITCH_PRINT(device, ERROR, "%s: Too few ports in spray group %d\n",
1107 __FUNCTION__, i);
1108 return -NVL_BAD_ARGS;
1109 }
1110
1111 if (ports_per_spray_group[i] > NVSWITCH_NUM_LINKS_LS10)
1112 {
1113 NVSWITCH_PRINT(device, ERROR, "%s: Too many ports in spray group %d\n",
1114 __FUNCTION__, i);
1115 return -NVL_BAD_ARGS;
1116 }
1117
1118 j += ports_per_spray_group[i];
1119 }
1120
1121 if (j > NVSWITCH_NUM_LINKS_LS10)
1122 {
1123 NVSWITCH_PRINT(device, ERROR, "%s: Too many ports specified in total spray groups: %d\n",
1124 __FUNCTION__, j);
1125 return -NVL_BAD_ARGS;
1126 }
1127
1128 ret = nvswitch_init_portlist_ls10(device, mcp_list, NVSWITCH_MC_TCP_LIST_SIZE_LS10);
1129 if (ret != NVL_SUCCESS)
1130 return ret;
1131
1132 // build spray strings for each spray group
1133 for ( spray_group_idx = 0; spray_group_idx < num_spray_groups; spray_group_idx++ )
1134 {
1135 spray_group_size = ports_per_spray_group[spray_group_idx];
1136
1137 #ifdef NVSWITCH_MC_DEBUG
1138 NVSWITCH_PRINT(device, INFO, "%s: processing spray group %d size %d of %d total groups\n",
1139 __FUNCTION__, spray_group_idx, spray_group_size, num_spray_groups);
1140 #endif
1141
1142 ret = nvswitch_init_portlist_ls10(device, tmp_mcp_list, NVSWITCH_MC_TCP_LIST_SIZE_LS10);
1143 if (ret != NVL_SUCCESS)
1144 return ret;
1145
1146 ret = _nvswitch_mc_build_cpb(device, spray_group_size, &port_list[spray_group_offset],
1147 NVSWITCH_MC_NUM_COLUMNS_LS10, cpb,
1148 &vchop_array[spray_group_offset], vchop_map);
1149
1150 if (ret != NVL_SUCCESS)
1151 {
1152 NVSWITCH_PRINT(device, ERROR,
1153 "%s: error building column-port bitmap for spray group %d: %d\n",
1154 __FUNCTION__, spray_group_idx, ret);
1155 return ret;
1156 }
1157
1158 // Set the offset to this spray group in the mcp list.
1159 spray_group_ptrs[spray_group_idx] = (NvU8)dir_entries_used;
1160
1161 #ifdef NVSWITCH_MC_TRACE
1162 NVSWITCH_PRINT(device, INFO, "%s: spray group offset for group %d is %d\n",
1163 __FUNCTION__, spray_group_idx, dir_entries_used);
1164
1165 for (i = 0; i < NVSWITCH_MC_NUM_COLUMNS_LS10; i++)
1166 {
1167 NVSWITCH_PRINT(device, INFO, "%d Relative ports in column %d\n",
1168 _nvswitch_mc_get_pop_count(cpb[i]), i);
1169
1170 for ( j = 0; j < 32; j++ )
1171 {
1172 if (nvswitch_test_flags(cpb[i], NVBIT(j)))
1173 {
1174 NVSWITCH_PRINT(device, INFO, "%4d", j);
1175 }
1176 }
1177 NVSWITCH_PRINT(device, INFO, "\n");
1178 }
1179 #endif
1180 // if primary replica is specified for this spray group, find the port number
1181 if (replica_valid_array[spray_group_idx])
1182 {
1183 if (pri_replica_offsets[spray_group_idx] >= spray_group_size)
1184 {
1185 NVSWITCH_PRINT(device, ERROR,
1186 "%s: primary replica offset %d is out of range for spray group %d\n",
1187 __FUNCTION__, pri_replica_offsets[spray_group_idx], spray_group_idx);
1188 return -NVL_BAD_ARGS;
1189 }
1190
1191 for (i = 0; i < spray_group_size; i++)
1192 {
1193 if (pri_replica_offsets[spray_group_idx] == i)
1194 {
1195 primary_replica_port = port_list[spray_group_offset + i];
1196 #ifdef NVSWITCH_MC_DEBUG
1197 NVSWITCH_PRINT(device, INFO, "Primary replica port in spray group %d is %d\n",
1198 spray_group_idx, primary_replica_port);
1199 #endif
1200 }
1201 }
1202 }
1203 #ifdef NVSWITCH_MC_DEBUG
1204 if (primary_replica_port == NVSWITCH_MC_INVALID)
1205 NVSWITCH_PRINT(device, INFO, "%s: No primary replica specified for spray group %d\n",
1206 __FUNCTION__, spray_group_idx);
1207 #endif
1208
1209 // process columns into spray group of multicast directives
1210 mcplist_offset = dir_entries_used;
1211
1212 if (mcplist_offset >= NVSWITCH_MC_TCP_LIST_SIZE_LS10)
1213 {
1214 NVSWITCH_PRINT(device, ERROR, "%s: Overflow: mcplist_offset is %d\n",
1215 __FUNCTION__, mcplist_offset);
1216 return -NVL_ERR_GENERIC;
1217 }
1218
1219 #ifdef NVSWITCH_MC_DEBUG
1220 NVSWITCH_PRINT(device, INFO, "%s: building tmp mc portlist at mcp offset %d, size %d\n",
1221 __FUNCTION__, mcplist_offset, spray_group_size);
1222 #endif
1223
1224 ret = _nvswitch_mc_build_portlist(device, cpb, primary_replica_port, vchop_map,
1225 tmp_mcp_list, &dir_entries_used_sg);
1226
1227 if (ret != NVL_SUCCESS)
1228 {
1229 NVSWITCH_PRINT(device, ERROR, "%s: error building MC portlist\n", __FUNCTION__);
1230 return ret;
1231 }
1232
1233 #ifdef NVSWITCH_MC_DEBUG
1234 NVSWITCH_PRINT(device, INFO, "%s: entries used after building portlist: %d\n",
1235 __FUNCTION__, dir_entries_used_sg);
1236 #endif
1237
1238 ret = _nvswitch_mc_compact_portlist(device, tmp_mcp_list, &dir_entries_used_sg);
1239 if (ret != NVL_SUCCESS)
1240 {
1241 NVSWITCH_PRINT(device, ERROR, "%s: error compacting MC portlist\n", __FUNCTION__);
1242 return ret;
1243 }
1244
1245 _nvswitch_mc_set_round_flags(tmp_mcp_list, dir_entries_used_sg);
1246
1247 _nvswitch_mc_set_port_flags(tmp_mcp_list, dir_entries_used_sg);
1248
1249 //copy spray group entries into final portlist
1250 ret = _nvswitch_mc_copy_valid_entries_ls10(device, mcp_list, tmp_mcp_list,
1251 dir_entries_used_sg, mcplist_offset);
1252 if (ret != NVL_SUCCESS)
1253 return ret;
1254
1255 dir_entries_used += dir_entries_used_sg;
1256
1257 // increment position in the input port list
1258 spray_group_offset += spray_group_size;
1259 }
1260
1261 *entries_used = dir_entries_used;
1262
1263 #ifdef NVSWITCH_MC_TRACE
1264 _nvswitch_mc_print_directives(device, mcp_list, *entries_used, spray_group_ptrs,
1265 num_spray_groups);
1266 #endif
1267
1268 return NVL_SUCCESS;
1269 }
1270
1271 static NvU32
_nvswitch_col_offset_to_port_ls10(NvU32 col,NvU32 offset)1272 _nvswitch_col_offset_to_port_ls10
1273 (
1274 NvU32 col,
1275 NvU32 offset
1276 )
1277 {
1278 NvU32 i;
1279 NVSWITCH_COLUMN_PORT_OFFSET_LS10 cpo;
1280
1281 if ((col > NVSWITCH_MC_NUM_COLUMNS_LS10) || (offset > NVSWITCH_MC_PORTS_PER_COLUMN_LS10))
1282 return NVSWITCH_MC_INVALID;
1283
1284 for (i = 0; i < NVSWITCH_NUM_LINKS_LS10; i++)
1285 {
1286 cpo = nvswitch_portmap_ls10[i];
1287
1288 if ((cpo.column == col) && (cpo.port_offset == offset))
1289 return i;
1290 }
1291
1292 return NVSWITCH_MC_INVALID;
1293 }
1294
1295 NvlStatus
nvswitch_mc_unwind_directives_ls10(nvswitch_device * device,NVSWITCH_TCP_DIRECTIVE_LS10 directives[NVSWITCH_MC_TCP_LIST_SIZE_LS10],NvU32 ports[NVSWITCH_MC_MAX_PORTS],NvU8 vc_hop[NVSWITCH_MC_MAX_PORTS],NvU32 ports_per_spray_group[NVSWITCH_MC_MAX_SPRAYGROUPS],NvU32 replica_offset[NVSWITCH_MC_MAX_SPRAYGROUPS],NvBool replica_valid[NVSWITCH_MC_MAX_SPRAYGROUPS])1296 nvswitch_mc_unwind_directives_ls10
1297 (
1298 nvswitch_device *device,
1299 NVSWITCH_TCP_DIRECTIVE_LS10 directives[NVSWITCH_MC_TCP_LIST_SIZE_LS10],
1300 NvU32 ports[NVSWITCH_MC_MAX_PORTS],
1301 NvU8 vc_hop[NVSWITCH_MC_MAX_PORTS],
1302 NvU32 ports_per_spray_group[NVSWITCH_MC_MAX_SPRAYGROUPS],
1303 NvU32 replica_offset[NVSWITCH_MC_MAX_SPRAYGROUPS],
1304 NvBool replica_valid[NVSWITCH_MC_MAX_SPRAYGROUPS]
1305 )
1306 {
1307 NvU32 ret = NVL_SUCCESS;
1308 NvU32 i, port_idx, cur_sg, ports_in_cur_sg, port, primary_replica;
1309 NVSWITCH_TCP_DIRECTIVE_LS10 cur_dir, prev_dir;
1310
1311 cur_sg = 0;
1312 port_idx = 0;
1313 ports_in_cur_sg = 0;
1314
1315 for (i = 0; i < NVSWITCH_MC_TCP_LIST_SIZE_LS10; i++)
1316 {
1317 cur_dir = directives[i];
1318 if (cur_dir.tcp == NVSWITCH_MC_INVALID)
1319 {
1320 #ifdef NVSWITCH_MC_DEBUG
1321 NVSWITCH_PRINT(device, INFO, "%s: reached end of directive list (element %d)\n",
1322 __FUNCTION__, i);
1323 #endif
1324 break;
1325 }
1326
1327 //
1328 // Find primary replica.
1329 // For more info, see: IAS 6.12. Consistent MC Semantics
1330 //
1331
1332 primary_replica = PRIMARY_REPLICA_NONE;
1333
1334 //
1335 // If lastRound = 1 and continueRound = 1, primary replica is in
1336 // this TCP directive and portFlag = 0/1 selects even/odd port.
1337 //
1338 if ((cur_dir.lastRound) && (cur_dir.continueRound))
1339 {
1340 if (cur_dir.portFlag)
1341 primary_replica = PRIMARY_REPLICA_ODD;
1342 else
1343 primary_replica = PRIMARY_REPLICA_EVEN;
1344
1345 }
1346 //
1347 // If the previous TCP directive's portFlag = 0, and if it was not
1348 // used to select the even or odd port of its predecessor, and this
1349 // directive's portFlag == 1, this TCP directive contains the
1350 // primary replica, and the next TCP directive's portFlag = 0/1
1351 // selects the even/odd port of this TCP directive.
1352 //
1353
1354 // If we don't have the first or last directive and portFlag == 1
1355 else if ((i < (NVSWITCH_MC_TCP_LIST_SIZE_LS10 - 1)) && (i > 0) && (cur_dir.portFlag == 1))
1356 {
1357 prev_dir = directives[i - 1];
1358
1359 // Is the previous directive in the same sg and is the portFlag == 0?
1360 if ((prev_dir.lastRound == 0) && (prev_dir.portFlag == 0))
1361 {
1362 // Check if there is no predecessor, or if the predecessor's portFlag == 0
1363 if ((i < 2) || (directives[i - 2].portFlag == 0))
1364 {
1365 // The next directive's portFlags specify even or odd
1366 if (directives[i + 1].portFlag)
1367 primary_replica = PRIMARY_REPLICA_ODD;
1368 else
1369 primary_replica = PRIMARY_REPLICA_EVEN;
1370 }
1371 }
1372 }
1373
1374 if (cur_dir.tcpEPort != NVSWITCH_MC_NULL_PORT_LS10)
1375 {
1376 ports_in_cur_sg++;
1377
1378 if (cur_dir.tcpEAltPath)
1379 {
1380 port = _nvswitch_col_offset_to_port_ls10(cur_dir.tcp * 2 + 1, cur_dir.tcpEPort);
1381 }
1382 else
1383 {
1384 port = _nvswitch_col_offset_to_port_ls10(cur_dir.tcp * 2, cur_dir.tcpEPort);
1385 }
1386
1387 if (port == NVSWITCH_MC_INVALID)
1388 {
1389 // if we get here, there's a bug when converting from col/offset to port number
1390 NVSWITCH_ASSERT(0);
1391 return -NVL_ERR_GENERIC;
1392 }
1393
1394 if (port_idx >= NVSWITCH_MC_MAX_PORTS)
1395 {
1396 // if we get here, there's a bug when incrementing the port index
1397 NVSWITCH_ASSERT(0);
1398 return -NVL_ERR_GENERIC;
1399 }
1400
1401 vc_hop[port_idx] = cur_dir.tcpEVCHop;
1402 ports[port_idx] = port;
1403
1404 if (primary_replica == PRIMARY_REPLICA_EVEN)
1405 {
1406 replica_offset[cur_sg] = port_idx;
1407 replica_valid[cur_sg] = NV_TRUE;
1408 #ifdef NVSWITCH_MC_TRACE
1409 NVSWITCH_PRINT(device, INFO, "%s: primary replica is port %d, offset %d in sg %d\n",
1410 __FUNCTION__, port, port_idx, cur_sg);
1411 #endif
1412 }
1413
1414 port_idx++;
1415 }
1416
1417 if (cur_dir.tcpOPort != NVSWITCH_MC_NULL_PORT_LS10)
1418 {
1419 ports_in_cur_sg++;
1420
1421 if (cur_dir.tcpOAltPath)
1422 {
1423 port = _nvswitch_col_offset_to_port_ls10(cur_dir.tcp * 2, cur_dir.tcpOPort);
1424 }
1425 else
1426 {
1427 port = _nvswitch_col_offset_to_port_ls10(cur_dir.tcp * 2 + 1, cur_dir.tcpOPort);
1428 }
1429
1430 if (port == NVSWITCH_MC_INVALID)
1431 {
1432 // if we get here, there's a bug when converting from col/offset to port number
1433 NVSWITCH_ASSERT(0);
1434 return -NVL_ERR_GENERIC;
1435 }
1436
1437 if (port_idx >= NVSWITCH_MC_MAX_PORTS)
1438 {
1439 // if we get here, there's a bug when incrementing the port index
1440 NVSWITCH_ASSERT(0);
1441 return -NVL_ERR_GENERIC;
1442 }
1443
1444 vc_hop[port_idx] = cur_dir.tcpOVCHop;
1445 ports[port_idx] = port;
1446
1447 if (primary_replica == PRIMARY_REPLICA_ODD)
1448 {
1449 replica_offset[cur_sg] = port_idx;
1450 replica_valid[cur_sg] = NV_TRUE;
1451 #ifdef NVSWITCH_MC_TRACE
1452 NVSWITCH_PRINT(device, INFO, "%s: primary replica is port %d, offset %d in sg %d\n",
1453 __FUNCTION__, port, port_idx, cur_sg);
1454 #endif
1455 }
1456
1457 port_idx++;
1458 }
1459
1460 if (cur_dir.lastRound)
1461 {
1462 #ifdef NVSWITCH_MC_TRACE
1463 NVSWITCH_PRINT(device, INFO, "%s: reached end of spray group %d, %d total ports\n",
1464 __FUNCTION__, cur_sg, ports_in_cur_sg);
1465 #endif
1466 ports_per_spray_group[cur_sg] = ports_in_cur_sg;
1467 ports_in_cur_sg = 0;
1468 cur_sg++;
1469 }
1470 }
1471
1472 return ret;
1473 }
1474
1475 //
1476 // Invalidate an MCRID table entry.
1477 //
1478 // @param device [in] pointer to the nvswitch device struct
1479 // @param port [in] port for which to invalidate the table entry
1480 // @param index [in] index into the MCRID table
1481 // @param use_extended_table [in] specifies whether to use the extended table, or main table
1482 // @param zero [in] specifies whether to zero the entry as well as invalidate
1483 //
1484 NvlStatus
nvswitch_mc_invalidate_mc_rid_entry_ls10(nvswitch_device * device,NvU32 port,NvU32 index,NvBool use_extended_table,NvBool zero)1485 nvswitch_mc_invalidate_mc_rid_entry_ls10
1486 (
1487 nvswitch_device *device,
1488 NvU32 port,
1489 NvU32 index,
1490 NvBool use_extended_table,
1491 NvBool zero
1492 )
1493 {
1494 NvU32 reg, i;
1495
1496 if ((device == NULL) || (!nvswitch_is_link_valid(device, port)))
1497 return -NVL_BAD_ARGS;
1498
1499 if (use_extended_table && (index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDEXTTAB_DEPTH))
1500 {
1501 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for extended table\n",
1502 __FUNCTION__, index);
1503 return -NVL_BAD_ARGS;
1504 }
1505
1506 if (index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDTAB_DEPTH)
1507 {
1508 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for main table\n",
1509 __FUNCTION__, index);
1510 return -NVL_BAD_ARGS;
1511 }
1512
1513 if (use_extended_table)
1514 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSEXTMCRIDROUTERAM, 0);
1515 else
1516 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSMCRIDROUTERAM, 0);
1517
1518 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABADDR, _INDEX, index, reg);
1519 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABADDR, reg);
1520
1521 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _VALID, 0, 0);
1522
1523 if (!zero)
1524 {
1525 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA0, reg);
1526 return NVL_SUCCESS;
1527 }
1528
1529 for (i = 0; i < 4; i++)
1530 {
1531 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA2, 0);
1532 }
1533
1534 for (i = 0; i < 32; i++)
1535 {
1536 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA1, 0);
1537 }
1538
1539 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA0, 0);
1540
1541 return NVL_SUCCESS;
1542 }
1543
1544 //
1545 // Program an MCRID table entry.
1546 //
1547 // @param device [in] pointer to the nvswitch device struct
1548 // @param port [in] port for which to write the table entry
1549 // @param table_entry [in] pointer to the table entry to write
1550 // @param directive_list_size [in] size of the directive list contained in table_entry
1551 //
1552 NvlStatus
nvswitch_mc_program_mc_rid_entry_ls10(nvswitch_device * device,NvU32 port,NVSWITCH_MC_RID_ENTRY_LS10 * table_entry,NvU32 directive_list_size)1553 nvswitch_mc_program_mc_rid_entry_ls10
1554 (
1555 nvswitch_device *device,
1556 NvU32 port,
1557 NVSWITCH_MC_RID_ENTRY_LS10 *table_entry,
1558 NvU32 directive_list_size
1559 )
1560 {
1561 NvU32 i, reg;
1562 NVSWITCH_TCP_DIRECTIVE_LS10 *cur_dir;
1563
1564 if ((device == NULL) || (!nvswitch_is_link_valid(device, port)) || (table_entry == NULL))
1565 {
1566 return -NVL_BAD_ARGS;
1567 }
1568
1569 if (table_entry->use_extended_table &&
1570 (table_entry->index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDEXTTAB_DEPTH))
1571 {
1572 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for extended table\n",
1573 __FUNCTION__, table_entry->index);
1574 return -NVL_BAD_ARGS;
1575 }
1576
1577 if (table_entry->index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDTAB_DEPTH)
1578 {
1579 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for main table\n",
1580 __FUNCTION__, table_entry->index);
1581 return -NVL_BAD_ARGS;
1582 }
1583
1584 if (directive_list_size > NVSWITCH_MC_TCP_LIST_SIZE_LS10)
1585 {
1586 NVSWITCH_PRINT(device, ERROR, "%s: directive_list_size out of range\n", __FUNCTION__);
1587 return -NVL_BAD_ARGS;
1588 }
1589
1590 if ((table_entry->num_spray_groups > NVSWITCH_MC_MAX_SPRAY_LS10) ||
1591 (table_entry->num_spray_groups == 0))
1592 {
1593 NVSWITCH_PRINT(device, ERROR, "%s: num_spray_groups out of range\n", __FUNCTION__);
1594 return -NVL_BAD_ARGS;
1595 }
1596
1597 if ((table_entry->mcpl_size > NVSWITCH_NUM_LINKS_LS10) ||
1598 (table_entry->mcpl_size == 0))
1599 {
1600 NVSWITCH_PRINT(device, ERROR, "%s: mcpl_size out of range\n", __FUNCTION__);
1601 return -NVL_BAD_ARGS;
1602 }
1603
1604 if (table_entry->ext_ptr_valid &&
1605 (table_entry->ext_ptr > NV_ROUTE_RIDTABADDR_INDEX_MCRIDEXTTAB_DEPTH))
1606 {
1607 NVSWITCH_PRINT(device, ERROR, "%s: extended_ptr out of range\n", __FUNCTION__);
1608 return -NVL_BAD_ARGS;
1609 }
1610
1611 if (table_entry->use_extended_table)
1612 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSEXTMCRIDROUTERAM, 0);
1613 else
1614 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSMCRIDROUTERAM, 0);
1615
1616 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABADDR, _INDEX, table_entry->index, reg);
1617 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABADDR, reg);
1618
1619 //
1620 // Write register 2. Each time this register is written it causes the
1621 // mcpl_str_ptr index to increment by 4 (4 entries are written at a time).
1622 //
1623 i = 0;
1624 while (i < table_entry->num_spray_groups)
1625 {
1626
1627 #ifdef NVSWITCH_MC_DEBUG
1628 NVSWITCH_PRINT(device, INFO, "%s: writing offset %d for spray group %d\n",
1629 __FUNCTION__, table_entry->spray_group_ptrs[i], i);
1630 #endif
1631 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR0,
1632 table_entry->spray_group_ptrs[i], 0);
1633 i++;
1634
1635 if (i < table_entry->num_spray_groups)
1636 {
1637 #ifdef NVSWITCH_MC_DEBUG
1638 NVSWITCH_PRINT(device, INFO, "%s: writing offset %d for spray group %d\n",
1639 __FUNCTION__, table_entry->spray_group_ptrs[i], i);
1640 #endif
1641 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR1,
1642 table_entry->spray_group_ptrs[i], reg);
1643 i++;
1644 }
1645
1646 if (i < table_entry->num_spray_groups)
1647 {
1648 #ifdef NVSWITCH_MC_DEBUG
1649 NVSWITCH_PRINT(device, INFO, "%s: writing offset %d for spray group %d\n",
1650 __FUNCTION__, table_entry->spray_group_ptrs[i], i);
1651 #endif
1652 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR2,
1653 table_entry->spray_group_ptrs[i], reg);
1654 i++;
1655 }
1656
1657 if (i < table_entry->num_spray_groups)
1658 {
1659 #ifdef NVSWITCH_MC_DEBUG
1660 NVSWITCH_PRINT(device, INFO, "%s: writing offset %d for spray group %d\n",
1661 __FUNCTION__, table_entry->spray_group_ptrs[i], i);
1662 #endif
1663 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR3,
1664 table_entry->spray_group_ptrs[i], reg);
1665 i++;
1666 }
1667
1668 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA2, reg);
1669 }
1670
1671
1672 //
1673 // Write register 1. Each time this register is written it causes the mcpl_directive
1674 // index to increment by 1.
1675 //
1676
1677 for (i = 0; i < directive_list_size; i++)
1678 {
1679 cur_dir = &table_entry->directives[i];
1680
1681 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_E_PORT, cur_dir->tcpEPort, 0);
1682 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_E_ALTPATH, cur_dir->tcpEAltPath, reg);
1683 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_E_REQ_VCHOP, cur_dir->tcpEVCHop, reg);
1684 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_O_PORT, cur_dir->tcpOPort, reg);
1685 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_O_ALTPATH, cur_dir->tcpOAltPath, reg);
1686 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_O_REQ_VCHOP, cur_dir->tcpOVCHop, reg);
1687 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_TCP, cur_dir->tcp, reg);
1688 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_PORT_FLAG, cur_dir->portFlag, reg);
1689 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_RND_CONTINUE, cur_dir->continueRound, reg);
1690 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA1, _MCPL_LAST_RND, cur_dir->lastRound, reg);
1691
1692 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA1, reg);
1693 }
1694
1695 //
1696 // Write register 0.
1697 //
1698 // Due to size limitations in HW, _MCPL_SIZE must be adjusted by one here.
1699 // From the reference manuals:
1700 //
1701 // The number of expected responses at this switch hop for this MCID is MCPL_SIZE+1.
1702 //
1703 // For _MCPL_SPRAY_SIZE the value 0 represents 16. This requires no adjustment
1704 // when writing, since 16 is truncated to 0 due to field width.
1705 //
1706 // The input parameters for both of these are guaranteed to be nonzero values.
1707 //
1708 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _MCPL_SIZE, table_entry->mcpl_size - 1, 0);
1709 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _MCPL_SPRAY_SIZE, table_entry->num_spray_groups,
1710 reg);
1711 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _MCPL_RID_EXT_PTR, table_entry->ext_ptr, reg);
1712 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _MCPL_RID_EXT_PTR_VAL, table_entry->ext_ptr_valid,
1713 reg);
1714 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _VALID, 1, reg);
1715
1716 if (!table_entry->use_extended_table)
1717 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABDATA0, _MCPL_NO_DYN_RSP, table_entry->no_dyn_rsp, reg);
1718
1719 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABDATA0, reg);
1720
1721 return NVL_SUCCESS;
1722 }
1723
1724 NvlStatus
nvswitch_mc_read_mc_rid_entry_ls10(nvswitch_device * device,NvU32 port,NVSWITCH_MC_RID_ENTRY_LS10 * table_entry)1725 nvswitch_mc_read_mc_rid_entry_ls10
1726 (
1727 nvswitch_device *device,
1728 NvU32 port,
1729 NVSWITCH_MC_RID_ENTRY_LS10 *table_entry
1730 )
1731 {
1732 NvU32 i, reg;
1733
1734 if ((device == NULL) || (table_entry == NULL))
1735 return -NVL_BAD_ARGS;
1736
1737 if (table_entry->use_extended_table &&
1738 (table_entry->index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDEXTTAB_DEPTH))
1739 {
1740 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for extended table\n",
1741 __FUNCTION__, table_entry->index);
1742 return -NVL_BAD_ARGS;
1743 }
1744
1745 if (table_entry->index > NV_ROUTE_RIDTABADDR_INDEX_MCRIDTAB_DEPTH)
1746 {
1747 NVSWITCH_PRINT(device, ERROR, "%s: index %d out of range for main table\n",
1748 __FUNCTION__, table_entry->index);
1749 return -NVL_BAD_ARGS;
1750 }
1751
1752 // set the address
1753 if (table_entry->use_extended_table)
1754 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSEXTMCRIDROUTERAM, 0);
1755 else
1756 reg = FLD_SET_DRF(_ROUTE, _RIDTABADDR, _RAM_SEL, _SELECTSMCRIDROUTERAM, 0);
1757
1758 reg = FLD_SET_DRF_NUM(_ROUTE, _RIDTABADDR, _INDEX, table_entry->index, reg);
1759 NVSWITCH_NPORT_WR32_LS10(device, port, _ROUTE, _RIDTABADDR, reg);
1760
1761 // read in the entry
1762 reg = NVSWITCH_NPORT_RD32_LS10(device, port, _ROUTE, _RIDTABDATA0);
1763
1764 // parse DATA0
1765 table_entry->valid = DRF_VAL(_ROUTE, _RIDTABDATA0, _VALID, reg);
1766
1767 // if the entry is invalid, we're done
1768 if (!table_entry->valid)
1769 {
1770 return NVL_SUCCESS;
1771 }
1772
1773 //
1774 // Due to size limitations in HW, _MCPL_SIZE must be adjusted by one here.
1775 // From the reference manuals:
1776 //
1777 // The number of expected responses at this switch hop for this MCID is MCPL_SIZE+1.
1778 //
1779 // For _MCPL_SPRAY_SIZE, the value 0 represents 16, so we need to adjust for that here.
1780 //
1781 table_entry->mcpl_size = DRF_VAL(_ROUTE, _RIDTABDATA0, _MCPL_SIZE, reg) + 1;
1782 table_entry->num_spray_groups = DRF_VAL(_ROUTE, _RIDTABDATA0, _MCPL_SPRAY_SIZE, reg);
1783
1784 if (table_entry->num_spray_groups == 0)
1785 table_entry->num_spray_groups = 16;
1786
1787 if (!table_entry->use_extended_table)
1788 {
1789 table_entry->ext_ptr = DRF_VAL(_ROUTE, _RIDTABDATA0, _MCPL_RID_EXT_PTR, reg);
1790 table_entry->ext_ptr_valid = DRF_VAL(_ROUTE, _RIDTABDATA0, _MCPL_RID_EXT_PTR_VAL, reg);
1791 }
1792
1793 table_entry->no_dyn_rsp = DRF_VAL(_ROUTE, _RIDTABDATA0, _MCPL_NO_DYN_RSP, reg);
1794
1795 // DATA1 contains the directives
1796
1797 for (i = 0; i < NVSWITCH_MC_TCP_LIST_SIZE_LS10; i++)
1798 {
1799 reg = NVSWITCH_NPORT_RD32_LS10(device, port, _ROUTE, _RIDTABDATA1);
1800
1801 table_entry->directives[i].tcpEPort = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_E_PORT, reg);
1802 table_entry->directives[i].tcpEAltPath = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_E_ALTPATH, reg);
1803 table_entry->directives[i].tcpEVCHop = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_E_REQ_VCHOP, reg);
1804 table_entry->directives[i].tcpOPort = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_O_PORT, reg);
1805 table_entry->directives[i].tcpOAltPath = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_O_ALTPATH, reg);
1806 table_entry->directives[i].tcpOVCHop = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_O_REQ_VCHOP, reg);
1807 table_entry->directives[i].tcp = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_TCP, reg);
1808 table_entry->directives[i].portFlag = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_PORT_FLAG, reg);
1809 table_entry->directives[i].continueRound = DRF_VAL(_ROUTE, _RIDTABDATA1,
1810 _MCPL_RND_CONTINUE, reg);
1811 table_entry->directives[i].lastRound = DRF_VAL(_ROUTE, _RIDTABDATA1, _MCPL_LAST_RND, reg);
1812 }
1813
1814 // DATA2 contains the spray group pointers. This register loads the next 4 pointers on each read.
1815 i = 0;
1816 while (i < table_entry->num_spray_groups)
1817 {
1818 reg = NVSWITCH_NPORT_RD32_LS10(device, port, _ROUTE, _RIDTABDATA2);
1819
1820 table_entry->spray_group_ptrs[i] = DRF_VAL(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR0, reg);
1821 i++;
1822
1823 if (i < table_entry->num_spray_groups)
1824 {
1825 table_entry->spray_group_ptrs[i] = DRF_VAL(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR1, reg);
1826 i++;
1827 }
1828
1829 if (i < table_entry->num_spray_groups)
1830 {
1831 table_entry->spray_group_ptrs[i] = DRF_VAL(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR2, reg);
1832 i++;
1833 }
1834
1835 if (i < table_entry->num_spray_groups)
1836 {
1837 table_entry->spray_group_ptrs[i] = DRF_VAL(_ROUTE, _RIDTABDATA2, _MCPL_STR_PTR3, reg);
1838 i++;
1839 }
1840 }
1841
1842 return NVL_SUCCESS;
1843 }
1844
1845