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