openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

safc.c (55970B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File:        safc.c
      8  * Purpose:     SAFC board programming implementation
      9  */
     10 
     11 #include <shared/bsl.h>
     12 
     13 #include <sal/core/alloc.h>
     14 #include <bcm/types.h>
     15 #include <bcm/error.h>
     16 #include <bcm/field.h>
     17 #include <bcm/stack.h>
     18 #include <appl/stktask/topology.h>
     19 #include <appl/stktask/topo_pkt.h>
     20 #include <appl/stktask/topo_brd.h>
     21 #include <appl/stktask/stktask.h>
     22 #include <appl/cputrans/nh_tx.h>
     23 #include <appl/stktask/safc.h>
     24 #include "topo_int.h"
     25 
     26 /*
     27   Generic topology matrix handling.
     28 */
     29 
     30 
     31 #define TX_CXN(topo_info, src_idx, dest_idx) \
     32     (topo_info->tp_tx_cxns[db_ref->num_cpus * src_idx + dest_idx])
     33 
     34 #define RX_CXN(topo_info, src_idx, dest_idx) \
     35     (topo_info->tp_rx_cxns[db_ref->num_cpus * src_idx + dest_idx])
     36 
     37 /* modid, unit, stack port index set types */
     38 typedef SHR_BITDCLNAME(modset_t, TOPO_MODIDS_MAX);
     39 typedef SHR_BITDCLNAME(uset_t, BCM_MAX_NUM_UNITS);
     40 typedef SHR_BITDCLNAME(stkset_t, CPUDB_CXN_MAX);
     41 
     42 /* SAFC programming data
     43 
     44    'normal' means a port that handles non-SAFC traffic only
     45    'safc'   means a port than handles SAFC traffic and non-SAFC traffic
     46 
     47       This is because a SAFC ports are a subset of all stack ports.
     48 
     49    'all' is the union of normal and safc ports
     50 
     51 */
     52 typedef struct safc_data_s {
     53     /* topology inputs */
     54     topo_cpu_t *tp_cpu;
     55     cpudb_ref_t db_ref;
     56     topo_info_t *safc_topo;
     57 
     58     /* BCM programming inputs */
     59     int group_priority;
     60     bcm_field_group_t group[BCM_MAX_NUM_UNITS];
     61     bcm_field_qset_t qset;
     62     uint32 safc_cos_map;
     63     uint32 safc_vlan_mask;
     64     bcm_port_config_t port_config[BCM_MAX_NUM_UNITS];
     65 
     66     /* derived */
     67     uset_t      units;                      /* set of units with stack ports */
     68     int         s_idx[TOPO_MODIDS_MAX];     /* modid to safc sp index */
     69     int         n_idx[TOPO_MODIDS_MAX];     /* modid to normal sp index */
     70     modset_t    modid[CPUDB_CXN_MAX];       /* sp index to modid set */
     71     modset_t    blocked[CPUDB_CXN_MAX];     /* sp index to blocked modid set */
     72     modset_t    all_modid;                  /* all modids in stack */
     73     bcm_pbmp_t  trunk[CPUDB_CXN_MAX];       /* sp index to trunk ports */
     74     int         t_idx[CPUDB_CXN_MAX];       /* sp/trunk mapping */
     75     bcm_pbmp_t  none_pbm_mask;              /* empty PBM */
     76     bcm_pbmp_t  all_pbm_mask;               /* full PBM */
     77 
     78     bcm_pbmp_t  safc_pbm[BCM_MAX_NUM_UNITS];   /* SAFC stack PBM */
     79     bcm_pbmp_t  normal_pbm[BCM_MAX_NUM_UNITS]; /* normal *only* stack PBM */
     80     stkset_t    safc;                       /* set of SAFC sp idxs */
     81     stkset_t    normal;                     /* set of normal sp idxs */
     82     stkset_t    trunked;                    /* set of trunked sp idxs */
     83     stkset_t    s_visit;                    /* visited flags for collect */
     84     /* true if this system has *only* SAFC ports*/
     85     int         safc_only;
     86 
     87 } safc_data_t;
     88 
     89 /* iterator function pointers */
     90 typedef int (*_ufunc_t)(safc_data_t *info, int unit);
     91 typedef int (*_pfunc_t)(safc_data_t *info, int spidx);
     92 typedef int (*_cfunc_t)(safc_data_t *info, int unit, bcm_cos_t cos);
     93 
     94 /* unit iterator */
     95 STATIC int
     96 _bcm_board_safc_unit_iterate(safc_data_t *info, _ufunc_t func)
     97 {
     98     int unit;
     99     int rv;
    100 
    101     rv = BCM_E_NONE;
    102 
    103     for (unit = 0; unit < BCM_MAX_NUM_UNITS; unit++) {
    104         if (SHR_BITGET(info->units, unit)) {
    105             rv = func(info, unit);
    106             if (BCM_FAILURE(rv)) {
    107                 break;
    108             }
    109         }
    110     }
    111 
    112     return rv;
    113 }
    114 
    115 /* stack port iterator */
    116 STATIC int
    117 _bcm_board_safc_stkport_iterate(safc_data_t *info, _pfunc_t func)
    118 {
    119     int rv;
    120     int i;
    121 
    122     rv = BCM_E_NONE;
    123 
    124     for (i = 0; i < info->db_ref->local_entry->base.num_stk_ports; i++) {
    125         rv = func(info, i);
    126         if (BCM_FAILURE(rv)) {
    127             break;
    128         }
    129     }
    130 
    131     return rv;
    132 }
    133 
    134 /* cos iterator */
    135 STATIC int
    136 _bcm_board_safc_cos_iterate(safc_data_t *info, int unit, _cfunc_t func)
    137 {
    138     bcm_cos_t cos;
    139     int rv;
    140 
    141     for (cos = BCM_COS_MAX; cos >= BCM_COS_MIN; cos--) {
    142         if ((info->safc_cos_map & (1<<cos)) == 0) {
    143             continue;
    144         }
    145         rv = func(info, unit, cos);
    146         BCM_IF_ERROR_RETURN(rv);
    147     }
    148     return BCM_E_NONE;
    149 }
    150 
    151 
    152 
    153 /*
    154   Allocate a TX/RX topology matrix.
    155  */
    156 topo_info_t *_topo_info_t_create(cpudb_ref_t db_ref)
    157 {
    158     topo_info_t *topo;
    159     uint8 *rx, *tx;
    160     int topo_len, cpu_mtx_len;    /* CPUxCPU matrix */
    161 
    162     topo_len = sizeof(*topo);
    163     cpu_mtx_len = db_ref->num_cpus * db_ref->num_cpus;
    164 
    165     topo = sal_alloc(topo_len, "topo_info_t_create");
    166     rx = sal_alloc(cpu_mtx_len, "topo_info_t_create");
    167     tx = sal_alloc(cpu_mtx_len, "topo_info_t_create");
    168     if (topo != NULL && rx != NULL && tx != NULL) {
    169         sal_memset(topo, 0,  topo_len);
    170         sal_memset(rx, TOPO_CXN_UNKNOWN,  cpu_mtx_len);
    171         sal_memset(tx, TOPO_CXN_UNKNOWN,  cpu_mtx_len);
    172         topo->tp_rx_cxns = rx;
    173         topo->tp_tx_cxns = tx;
    174     } else {
    175         if (rx) {
    176             sal_free(rx);
    177         }
    178         if (tx) {
    179             sal_free(tx);
    180         }
    181         if (topo) {
    182             sal_free(topo);
    183 
    184         }
    185         topo = NULL;
    186     }
    187 
    188     return topo;
    189 
    190 }
    191 
    192 /* Mark all cut ports disabled */
    193 STATIC void
    194 _bcm_topo_disable_cut_ports(cpudb_ref_t db_ref)
    195 {
    196     cpudb_entry_t *entry;
    197     int i;
    198 
    199     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    200         for (i = 0; i < entry->base.num_stk_ports; i++) {
    201             if (entry->sp_info[i].flags & CPUDB_SPF_CUT_PORT) {
    202                 entry->sp_info[i].flags |= CPUDB_SPF_TX_DISABLE_FORCE;
    203             }
    204         }
    205     }
    206 }
    207 
    208 /* copy out topo info from cpudb */
    209 STATIC void
    210 _bcm_topo_info_copy(topo_info_t *connection, cpudb_ref_t db_ref)
    211 {
    212     topo_info_t *from;
    213     int cpu_mtx_len;
    214 
    215     from = (topo_info_t *)db_ref->topo_cookie;
    216     cpu_mtx_len = db_ref->num_cpus * db_ref->num_cpus;
    217 
    218     if (from) {
    219         sal_memcpy(connection->tp_rx_cxns, from->tp_rx_cxns, cpu_mtx_len);
    220         sal_memcpy(connection->tp_tx_cxns, from->tp_tx_cxns, cpu_mtx_len);
    221     }
    222 }
    223 
    224 /* Create a spanning tree topology from the existing CPUDB */
    225 STATIC int
    226 _bcm_topo_use_cut_port_spanning_tree(cpudb_ref_t db_ref,
    227                                      topo_info_t *connection)
    228 {
    229     cpudb_ref_t newdb;
    230     topo_cpu_t *tp_cpu;
    231     int rv;
    232 
    233     rv = BCM_E_INTERNAL;
    234 
    235     /* topo_cpu_t is pretty big, so alloc from heap, not stack */
    236     tp_cpu = sal_alloc(sizeof(*tp_cpu), "_bcm_topo_spt");
    237 
    238     if (tp_cpu != NULL) {
    239         newdb = cpudb_copy(db_ref);
    240         if (newdb != NULL) {
    241             _bcm_topo_disable_cut_ports(newdb);
    242             rv = bcm_stack_topo_create(newdb, tp_cpu);
    243             if (BCM_SUCCESS(rv)) {
    244                 _bcm_topo_info_copy(connection, newdb);
    245             }
    246             cpudb_destroy(newdb);
    247         } else {
    248             rv = BCM_E_MEMORY;
    249         }
    250         sal_free(tp_cpu);
    251     } else {
    252         rv = BCM_E_MEMORY;
    253     }
    254 
    255     return rv;
    256 }
    257 
    258 /* Generic spanning tree calculator - not implemented yet */
    259 STATIC int
    260 _bcm_topo_spanning_tree_create(cpudb_ref_t db_ref,
    261                                cpudb_entry_t *root,
    262                                topo_info_t *connection)
    263 {
    264     COMPILER_REFERENCE(db_ref);
    265     COMPILER_REFERENCE(root);
    266     COMPILER_REFERENCE(connection);
    267 
    268     return BCM_E_UNAVAIL;
    269 }
    270 
    271 
    272 /*
    273  * Function:
    274  *     bcm_stack_spanning_tree_create(cpudb_ref_t db_ref,
    275  *                                    cpudb_entry_t *root_entry,
    276  *                                    topo_info_t **connection)
    277  * Purpose:
    278  *     Calculate a local spanning tree topology from db_ref rooted at
    279  *     root_entry, returning a connection matrix 'connection'.
    280  * Parameters:
    281  *     db_ref     - (IN)  CPU database
    282  *     root_entry - (IN)  Root of spanning tree
    283  *     connection - (OUT) Calculated topology
    284  * Returns:
    285  *     BCM_E_NONE     - no errors
    286  *     BCM_E_PARAM    - db_ref NULL, db_ref not fully constructed
    287  *     BCM_E_MEMORY   - not enough memory or system resources
    288  *     BCM_E_FAIL     - topology calculation failed (CPUDB incomplete)
    289  * Notes:
    290  *     Does not perform any device programming.
    291  *
    292  *     If root_entry is NULL, then the spanning tree is recovered from
    293  *     the existing 'CUT Ports' calculated from the default topology.
    294  */
    295 
    296 int
    297 bcm_stack_spanning_tree_create(cpudb_ref_t db_ref,
    298                                cpudb_entry_t *root,
    299                                topo_info_t **connection)
    300 {
    301     int rv;
    302     topo_info_t *conn = NULL;
    303 
    304     rv = BCM_E_PARAM;
    305 
    306     if (!cpudb_valid(db_ref)) {
    307         return BCM_E_PARAM;
    308     }
    309 
    310     if (db_ref->num_cpus > 1) {
    311         conn = _topo_info_t_create(db_ref);
    312         if (conn == NULL) {
    313             return BCM_E_MEMORY;
    314         }
    315 
    316         if (root == NULL) {
    317             /* As a shortcut, use existing cut port topology as the
    318                spanning tree */
    319             rv = _bcm_topo_use_cut_port_spanning_tree(db_ref, conn);
    320         } else {
    321             /* (re)calculate a spanning tree */
    322             rv = _bcm_topo_spanning_tree_create(db_ref, root, conn);
    323         }
    324     } else {
    325         /* More than one CPU is required for a useful topology */
    326         rv = BCM_E_NONE;
    327     }
    328 
    329     if (BCM_SUCCESS(rv)) {
    330         *connection = conn;
    331     } else {
    332         (void)bcm_stack_connection_destroy(conn);
    333     }
    334 
    335     return rv;
    336 }
    337 
    338 
    339 /*
    340  * Function:
    341  *     int bcm_stack_connection_destroy(topo_info_t *connection)
    342  * Purpose:
    343  *     Free a connection data structure
    344  * Parameters:
    345  *     connection - (IN) connection structure
    346  * Returns:
    347  *     BCM_E_NONE     - no errors
    348  *     BCM_E_FAIL     - error
    349  */
    350 
    351 int
    352 bcm_stack_connection_destroy(topo_info_t *connection)
    353 {
    354     if (connection) {
    355         sal_free((void *)connection->tp_tx_cxns);
    356         sal_free((void *)connection->tp_rx_cxns);
    357         sal_free((void *)connection);
    358     }
    359 
    360     return BCM_E_NONE;
    361 }
    362 
    363 
    364 
    365 /*
    366   Problem - When SAFC is enabled in a ring configuration (or Stack)
    367   the flow control among XGS devices create a deadlock in the ring.
    368 
    369   Solution - Break the loop structure of the ring configuration among
    370   XGS devices "just" for the SAFC enabled traffic.  To break the loop
    371   structure of the ring, SAFC traffic is redirected to a new Higig
    372   distribution configuration using IFP.  Basically the current
    373   MODPORT_MAP Table would generate two HiGIG Output ports for an
    374   outgoing Higig packet (one for SAFC Traffic, and one for non-SAFC)
    375   and IFP, based on Traffic class, shall mask one of the outgoing
    376   Higig Ports (by an IFP rule to "and" the Port Bit Map).
    377 
    378   For SAFC-traffic: One pre-designated Higig link in the ring shall be
    379   disabled using IFP for SAFC traffic.  XGS devices that are connected
    380   to the disabled link will not allow SAFC-traffic to be forwarded to
    381   the this link using an IFP rule. All other XGS devices (which are
    382   not connected to the disabled link) will forward SAFC traffic
    383   without applying the regular "SRC MODID based block" .
    384 
    385   For Non-SAFC-Traffic: IFP will apply the SRC_MODID based mask
    386   (instead of the traditional table driven Mask) so that non-SAFC
    387   traffic is forwarded as today. Non-SAFC traffic is allowed to flow
    388   through the disabled link also.
    389 
    390   The following IFP rules are required - (assuming 16 nodes in a ring,
    391   and one SAFC traffic class and rest are non-SAFC)
    392 
    393   Packets from Higig Ports - (2 rules for every ingress Higig Port)
    394 
    395     o Either IFP higher precedence rule for "SAFC allow" for devices
    396     that don't have a SAFC-disabled link (Traffic with a specific TC
    397     value will be unconditionally allowed to go to the outgoing
    398     Higig-port), Or- IFP higher precedence rule for "SAFC block" for
    399     devices that have a SAFC-disabled link. (Traffic with a specific
    400     TC value will be unconditionally not allowed to go to the outgoing
    401     Higig-port).
    402 
    403     o IFP lower precedence rule for not allowing packet from a
    404     specific "SRC_MOD_ID" to go to the outgoing Higig port. This rule
    405     is to replace the current table driven SRC_MOD_ID block. The
    406     specific SRC_MOD_ID that each node is blocking is dependent on the
    407     relative position of the device in the ring.
    408     NOTE: this rule is not used in this implementation. Instead, egress
    409     masking is used.
    410 
    411   For Front Panel Packets - (16 DST_MOD_ID based rules SAFC Unicast,
    412   16 DST-MODID based rules for non-SAFC Unicast, 1 rule for SAFC
    413   Non-Unicast, 1 rule for non-SAFC Non-Unicast)
    414 
    415     o For SAFC- Unicast Traffic, block the local Higig Port that is
    416     used for the Non-SAFC Distribution Tree. If, for the current node,
    417     both SAFC and non-SAFC trees use the same local port, then this
    418     rule has no effect. (For a specific TC, destined to a specific
    419     MOD_ID, if the packet is Unicast, apply an "and-pbm" to block the
    420     non-SAFC local port)
    421 
    422     o For SAFC-Non-Unicast Traffic, allow the packet to be forwarded
    423     to only SAFC -local Higig ports.
    424 
    425     o For Non-SAFC- Unicast Traffic, block the local Higig Port that
    426     is used for the SAFC Distribution Tree. If, for the current node,
    427     both SAFC and non-SAFC trees use the same local port, then this
    428     rule has no effect. (For a specific TC, destined to a specific
    429     MOD_ID, if the packet is Unicast, apply an "and-pbm" to block the
    430     SAFC local port)
    431 
    432     o For Non-SAFC-Non-Unicast Traffic, allow the packet to be
    433     forwarded to only non SAFC- local Higig ports.
    434 
    435   For packets that are mirrored, SAFC distribution tree is selected by
    436   mapping the DST_MODID to a local Port using the IM/EM_MOD_PORT_MAP
    437   Table. Since IFP rules are not applied to mirror-copy the packet will
    438   be forwarded through the ring back to the originating device where
    439   it shall be dropped (as done today using the non-SAFC tree).
    440 
    441  */
    442 
    443 typedef struct {
    444     int                 found;          /* true if group found */
    445     int                 priority;       /* priority to search for */
    446     bcm_field_group_t   group;          /* group which matches priority */
    447 } group_info_t;
    448 
    449 /* traverse function to find group matching a priority */
    450 STATIC int
    451 _bcm_board_safc_find_group(int unit, bcm_field_group_t group, void *user)
    452 {
    453     group_info_t *gi;
    454     int priority;
    455     int rv;
    456 
    457     gi = (group_info_t *)user;
    458     rv = bcm_field_group_priority_get(unit, group, &priority);
    459     if (BCM_SUCCESS(rv) && priority == gi->priority) {
    460         gi->found = TRUE;
    461         gi->group = group;
    462     }
    463 
    464     return BCM_E_NONE;
    465 }
    466 
    467 #define SAFC_DEBUG 1
    468 
    469 #ifdef SAFC_DEBUG
    470 
    471 /* create a debug counter */
    472 STATIC int
    473 _bcm_board_safc_count_create(int unit,
    474                              bcm_field_group_t group,
    475                              bcm_field_entry_t entry,
    476                              const char *locus)
    477 {
    478     bcm_field_stat_t stat;
    479     int id;
    480     int rv;
    481 
    482     stat = bcmFieldStatPackets;
    483 
    484     rv = bcm_field_stat_create(unit, group, 1, &stat, &id);
    485     BCM_IF_ERROR_RETURN(rv);
    486 
    487     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    488                 (BSL_META("counter %d@%s\n"),
    489                  id, locus));
    490     return
    491         (bcm_field_entry_stat_attach(unit, entry, id));
    492 
    493 }
    494 
    495 
    496 /* destroy a debug counter */
    497 STATIC int
    498 _bcm_board_safc_count_destroy(int unit, bcm_field_entry_t entry)
    499 {
    500     int id;
    501     int rv;
    502 
    503     rv = bcm_field_entry_stat_get(unit, entry, &id);
    504     BCM_IF_ERROR_RETURN(rv);
    505 
    506     rv = bcm_field_entry_stat_detach(unit, entry, id);
    507     BCM_IF_ERROR_RETURN(rv);
    508 
    509     return
    510         (bcm_field_stat_destroy(unit, id));
    511 
    512 }
    513 
    514 #else
    515 #define _bcm_board_safc_count_create(unit, group, entry, locus) (BCM_E_NONE)
    516 #define _bcm_board_safc_count_destroy(unit, entry) (BCM_E_NONE)
    517 #endif
    518 
    519 /* Deallocate field group and entries */
    520 STATIC int
    521 _bcm_board_safc_fp_destroy(int unit, bcm_field_group_t group)
    522 {
    523     int rv;
    524     int i;
    525     bcm_field_entry_t *entry;
    526     int entry_count;
    527     int entry_size;
    528 
    529     rv = bcm_field_entry_multi_get(unit, group, 0, NULL, &entry_size);
    530     if (BCM_SUCCESS(rv) && entry_size > 0) {
    531         entry = sal_alloc(entry_size * sizeof(*entry), "safc_fp_destroy");
    532         if (entry) {
    533             rv = bcm_field_entry_multi_get(unit, group,
    534                                            entry_size, entry, &entry_count);
    535             if (BCM_SUCCESS(rv)) {
    536                 for (i = 0; i < entry_count; i++) {
    537                     rv = _bcm_board_safc_count_destroy(unit, entry[i]);
    538                     if (BCM_FAILURE(rv)) {
    539                         break;
    540                     }
    541                     rv = bcm_field_entry_remove(unit, entry[i]);
    542                     if (BCM_FAILURE(rv)) {
    543                         break;
    544                     }
    545                     rv = bcm_field_entry_destroy(unit, entry[i]);
    546                     if (BCM_FAILURE(rv)) {
    547                         break;
    548                     }
    549                 }
    550             }
    551             sal_free(entry);
    552         } else {
    553             return BCM_E_MEMORY;
    554         }
    555     }
    556     if (BCM_SUCCESS(rv)) {
    557             rv = bcm_field_group_remove(unit, group);
    558     }
    559     if (BCM_SUCCESS(rv)) {
    560             rv = bcm_field_group_destroy(unit, group);
    561     }
    562 
    563     return rv;
    564 }
    565 
    566 /* unconfigure SAFC field programming */
    567 STATIC int
    568 _bcm_board_safc_unconfigure(int group_priority)
    569 {
    570     int rv;
    571     int unit;
    572     group_info_t gi;
    573 
    574     gi.priority = group_priority;
    575     gi.found = FALSE;
    576     BCM_FOREACH_LOCAL_UNIT(unit) {
    577         (void)bcm_field_group_traverse(unit,
    578                                        _bcm_board_safc_find_group,
    579                                        &gi);
    580         if (gi.found) {
    581             rv = _bcm_board_safc_fp_destroy(unit, gi.group);
    582             if (BCM_FAILURE(rv)) {
    583                 break;
    584             }
    585         }
    586     }
    587 
    588     return BCM_E_NONE;
    589 }
    590 
    591 /* per-unit SAFC data initializer function */
    592 STATIC int
    593 _bcm_board_safc_data_unit_init(safc_data_t *info, int unit)
    594 {
    595     int rv;
    596 
    597     rv = bcm_port_config_get(unit, info->port_config + unit);
    598 
    599     return rv;
    600 }
    601 
    602 /*
    603   Collect the modids for SAFC topology. This is called recursively
    604   starting from the links of the root node.
    605 
    606   The 'visited flag' is used to prevent an infinite loop in case the
    607   topology was calculated incorrectly.
    608 
    609  */
    610 STATIC int
    611 _bcm_board_safc_collect_modid(safc_data_t *info,
    612                               int src_spidx,
    613                               cpudb_entry_t *dst_entry,
    614                               int dst_idx)
    615 {
    616     int              i, rx_idx;
    617     cpudb_entry_t    *rx_entry;
    618     cpudb_key_t      rx_key;
    619     int              rv;
    620 
    621     /* already visited? */
    622     if (SHR_BITGET(info->s_visit, dst_entry->topo_idx)) {
    623         /* If the visit bit is set, then we've already been here,
    624            which should not happen because if this was really a
    625            spanning tree we are traversing, then there should be no
    626            loops. */
    627         return BCM_E_INTERNAL;
    628     }
    629 
    630     /* mark visited */
    631     SHR_BITSET(info->s_visit, dst_entry->topo_idx);
    632 
    633     /* descend on any remaining edges */
    634     for (i = 0; i < dst_entry->base.num_stk_ports; i++) {
    635         /* skip dst_idx because it points back to the source */
    636         if (i == dst_idx) {
    637             continue;
    638         }
    639 
    640         /* skip obvious non-stack ports, or stacking cut ports */
    641         if ((dst_entry->sp_info[i].flags &
    642              (CPUDB_SPF_ETHERNET|CPUDB_SPF_NO_LINK|
    643               CPUDB_SPF_INACTIVE|CPUDB_SPF_CUT_PORT))) {
    644             continue;
    645         }
    646 
    647         /* skip ports with no obvious partner */
    648         if (!(dst_entry->sp_info[i].flags & CPUDB_SPF_RX_RESOLVED)) {
    649             continue;
    650         }
    651 
    652         /* Get the remote RX CPUDB entry and and stack port index */
    653         CPUDB_KEY_COPY(rx_key, dst_entry->sp_info[i].rx_cpu_key);
    654         rx_entry = NULL;
    655         CPUDB_KEY_SEARCH(info->db_ref, rx_key, rx_entry);
    656         if (!rx_entry) {
    657             return BCM_E_NOT_FOUND;
    658         }
    659         rx_idx = dst_entry->sp_info[i].rx_stk_idx;
    660         if (!((rx_idx >= 0) && (rx_idx < CPUDB_CXN_MAX))) {
    661             return BCM_E_NOT_FOUND;
    662         }
    663         rv = _bcm_board_safc_collect_modid(info, src_spidx, rx_entry, rx_idx);
    664         BCM_IF_ERROR_RETURN(rv);
    665     }
    666 
    667     /* mark modids found in this entry as being reachable via
    668        the base port related to src_spidx */
    669 
    670     for (i=0; i<dst_entry->base.num_units; i++) {
    671         int j;
    672 
    673         for (j=0; j<dst_entry->base.mod_ids_req[i]; j++) {
    674             info->s_idx[dst_entry->mod_ids[i] + j] = info->t_idx[src_spidx];
    675         }
    676     }
    677     return BCM_E_NONE;
    678 }
    679 
    680 /* return true if the stack ports a and b are sources and sunk to
    681    identical units
    682  */
    683 STATIC int
    684 _bcm_board_safc_same_link(cpudb_ref_t db_ref, int a, int b)
    685 {
    686     cpudb_entry_t       *entry, *tx_entry;
    687     cpudb_stk_port_t    *sp_a, *sp_b;
    688     cpudb_unit_port_t   *txup;
    689     int                 tx_a, tx_b;
    690 
    691     entry = db_ref->local_entry;
    692     sp_a = entry->sp_info + a;
    693     sp_b = entry->sp_info + b;
    694     tx_a = sp_a->tx_stk_idx;
    695     tx_b = sp_b->tx_stk_idx;
    696     tx_entry = NULL;
    697 
    698     if (entry->base.stk_ports[a].unit != entry->base.stk_ports[b].unit) {
    699         /* not on the same local unit */
    700         return FALSE;
    701     }
    702 
    703     if ((tx_a < 0) || (tx_a >= CPUDB_CXN_MAX)) {
    704         /* index out of range */
    705         return FALSE;
    706     }
    707 
    708     if ((tx_b < 0) || (tx_b >= CPUDB_CXN_MAX)) {
    709         /* index out of range */
    710         return FALSE;
    711     }
    712 
    713     if (!CPUDB_KEY_EQUAL(sp_a->tx_cpu_key, sp_b->tx_cpu_key)) {
    714         /* not connected to the same remote system */
    715         return FALSE;
    716     }
    717 
    718     CPUDB_KEY_SEARCH(db_ref, sp_a->tx_cpu_key, tx_entry);
    719     if (tx_entry == NULL) {
    720         /* can't even find the key */
    721         return FALSE;
    722     }
    723 
    724     txup = tx_entry->base.stk_ports;
    725 
    726     if (txup[tx_a].unit != txup[tx_b].unit) {
    727         /* units on the far end are different */
    728         return FALSE;
    729     }
    730 
    731     return TRUE;
    732 }
    733 
    734 /* determin stack ports that may be a member of a trunk group */
    735 STATIC int
    736 _bcm_board_safc_trunk_idx_add(safc_data_t *info, int idx)
    737 {
    738     int i, unit, port;
    739     cpudb_entry_t    *entry;
    740 
    741     if (SHR_BITGET(info->trunked, idx)) {
    742         /* already added */
    743         return BCM_E_NONE;
    744     }
    745 
    746     BCM_PBMP_CLEAR(info->trunk[idx]);
    747 
    748     /* check if this stack port idx is associated with other stack
    749        ports connected to the same remote system and same unit. If so,
    750        assume it is a member of a trunk and add it to the given stack
    751        set. Do not assume that trunks are formed at this point, so do
    752        not use the trunk API to make this determination. */
    753 
    754     entry = info->db_ref->local_entry;
    755 
    756     /*
    757       get TX key and remote unit of this port
    758     */
    759 
    760     for (i = 0; i < entry->base.num_stk_ports; i++) {
    761 
    762         if ((entry->sp_info[i].flags &
    763              (CPUDB_SPF_ETHERNET|CPUDB_SPF_NO_LINK|CPUDB_SPF_INACTIVE))) {
    764             continue;
    765         }
    766 
    767         if (idx == i) {
    768             /* looking at this port, so skip */
    769             continue;
    770         }
    771 
    772         /*
    773           get TX key and remote unit of this port
    774         */
    775         if (_bcm_board_safc_same_link(info->db_ref, idx, i)) {
    776             unit = entry->base.stk_ports[i].unit;
    777             port = entry->base.stk_ports[i].port;
    778             LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    779                         (BSL_META("Trunk related spidx %d (%d,%d)\n"),
    780                          i, unit, port));
    781             SHR_BITSET(info->trunked, i);
    782             BCM_PBMP_PORT_ADD(info->trunk[idx], port);
    783             if (info->t_idx[i] >= 0) {
    784                 return BCM_E_INTERNAL;
    785             }
    786             info->t_idx[i] = idx;
    787         }
    788     }
    789 
    790     unit = entry->base.stk_ports[idx].unit;
    791     port = entry->base.stk_ports[idx].port;
    792     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    793                 (BSL_META("Trunk base spidx %d (%d,%d)\n"),
    794                  idx, unit, port));
    795     BCM_PBMP_PORT_ADD(info->trunk[idx], port);
    796     SHR_BITSET(info->trunked, idx);
    797     if (info->t_idx[i] >= 0) {
    798         return BCM_E_INTERNAL;
    799     }
    800     info->t_idx[idx] = idx;
    801 
    802     return BCM_E_NONE;
    803 }
    804 
    805 
    806 
    807 /* add stack port idx and any related ports to the stack set  */
    808 STATIC int
    809 _bcm_board_safc_stack_idx_add(safc_data_t *info, stkset_t stkset, int idx)
    810 {
    811     int i, related;
    812 
    813     if (SHR_BITGET(stkset, idx)) {
    814         /* already added */
    815         return BCM_E_NONE;
    816     }
    817 
    818     SHR_BITSET(stkset, idx);
    819 
    820     /* now see if there are any related ports that need to be added */
    821 
    822     /* stack port index of the 'base' stack port */
    823     related = info->t_idx[idx];
    824     for (i = 0; i < CPUDB_CXN_MAX; i++) {
    825         /* if this stack port index is related, then add it in */
    826         if (info->t_idx[i] == related) {
    827             SHR_BITSET(stkset, i);
    828         }
    829     }
    830 
    831     return BCM_E_NONE;
    832 }
    833 
    834 /* SAFC data initializer */
    835 STATIC int
    836 _bcm_board_safc_data_set(safc_data_t *info,
    837                 topo_cpu_t *tp_cpu,
    838                 cpudb_ref_t db_ref,
    839                 topo_info_t *safc)
    840 {
    841     int              num_cpu;
    842     topo_info_t      *normal;
    843     int              src;
    844     int              i, j, m, dst, idx, rx_idx;
    845     cpudb_entry_t    *entry;
    846     int              num_modids;
    847     bcm_port_t       port;
    848     int              unit;
    849     int              nhmod, nhport;
    850     cpudb_entry_t    *rx_entry;
    851     cpudb_key_t      rx_key;   /* RX key, unit on master port to .. */
    852     int              rx_unit;  /* .. compare against */
    853     cpudb_key_t      pkey;     /* RX key, unit on other ports being ..*/
    854     int              punit;    /* .. compare to master port */
    855     topo_stk_port_t  *tsp;
    856     int              rv;
    857 
    858     if(NULL == db_ref)
    859     {
    860         return BCM_E_PARAM;
    861     }
    862 
    863     num_cpu = db_ref->num_cpus;
    864     normal = (topo_info_t *)db_ref->topo_cookie;
    865     src = db_ref->local_entry->topo_idx;
    866     nhmod = -1;
    867     entry = db_ref->local_entry;
    868     info->tp_cpu = tp_cpu;
    869     info->db_ref = db_ref;
    870     info->safc_topo = safc;
    871 
    872     BCM_PBMP_CLEAR(info->none_pbm_mask);
    873     BCM_PBMP_NEGATE(info->all_pbm_mask, info->none_pbm_mask);
    874 
    875     /* init t_idx to catch errors */
    876     for (i = 0; i < CPUDB_CXN_MAX; i++) {
    877         info->t_idx[i] = -1;
    878     }
    879 
    880     /* collect information about stack ports that may be trunked */
    881     for (i = 0; i < entry->base.num_stk_ports; i++) {
    882 
    883         if ((entry->sp_info[i].flags &
    884              (CPUDB_SPF_ETHERNET|CPUDB_SPF_NO_LINK|CPUDB_SPF_INACTIVE))) {
    885             continue;
    886         }
    887         rv = _bcm_board_safc_trunk_idx_add(info, i);
    888         BCM_IF_ERROR_RETURN(rv);
    889     }
    890 
    891     /* Set which stack ports are in use based on the topology data */
    892     for (dst = 0; dst < num_cpu; dst++ ) {
    893         if (src != dst) {
    894             idx = TX_CXN(normal, src, dst);
    895             if (idx != TOPO_CXN_UNKNOWN) {
    896                 rv = _bcm_board_safc_stack_idx_add(info, info->normal, idx);
    897                 BCM_IF_ERROR_RETURN(rv);
    898             }
    899             idx = TX_CXN(safc, src, dst);
    900             if (idx != TOPO_CXN_UNKNOWN) {
    901                 rv = _bcm_board_safc_stack_idx_add(info, info->safc, idx);
    902                 BCM_IF_ERROR_RETURN(rv);
    903             }
    904         }
    905     }
    906 
    907     /* mixed system or safc only? */
    908     if (shr_bitop_range_eq(info->normal, info->safc, 0, CPUDB_CXN_MAX)) {
    909         info->safc_only = TRUE;
    910     }
    911 
    912     /* init portidx */
    913     for (i = 0; i < TOPO_MODIDS_MAX; i++) {
    914         info->n_idx[i] = -1;
    915         info->s_idx[i] = -1;
    916     }
    917 
    918     /* logic from bcm_board_module_filter */
    919     num_modids = 0;
    920     for (i = 0; i < entry->base.num_units; i++) {
    921         num_modids += entry->base.mod_ids_req[i];
    922     }
    923 
    924     for (i = 0; i < entry->base.num_stk_ports; i++) {
    925 
    926         if ((entry->sp_info[i].flags &
    927              (CPUDB_SPF_ETHERNET|CPUDB_SPF_NO_LINK|CPUDB_SPF_INACTIVE))) {
    928             continue;
    929         }
    930 
    931         port = entry->base.stk_ports[i].port;
    932         unit = entry->base.stk_ports[i].unit;
    933 
    934         /* Every active port should be a member of the SAFC port set
    935            or the normal port set. If it is not, then there is some
    936            logic error. */
    937         if (SHR_BITGET(info->safc, i)) {
    938             BCM_PBMP_PORT_ADD(info->safc_pbm[unit], port);
    939         } else if (SHR_BITGET(info->normal, i)) {
    940             BCM_PBMP_PORT_ADD(info->normal_pbm[unit], port);
    941         } else {
    942             return BCM_E_INTERNAL;
    943         }
    944 
    945         SHR_BITSET(info->units, unit);
    946         /* If we're using a reserved NH modid, or there's no
    947            nhmod defined for this stack port, or there was a nhmod
    948            for this stack port but it was was less than zero, then get the
    949            default */
    950 
    951         if (bcm_st_reserved_modid_enable_get()) {
    952             nh_tx_src_mod_port_get(unit, port, &nhmod, &nhport);
    953         }
    954 
    955         if (nhmod >= 0 && nhmod < num_modids) {
    956             /* add nexthop module */
    957             SHR_BITSET(info->modid[i], nhmod);
    958             SHR_BITSET(info->all_modid, nhmod);
    959         }
    960 
    961         /*
    962          * Get the remote RX CPU key and unit for the master port
    963          */
    964         CPUDB_KEY_COPY(rx_key, entry->sp_info[i].rx_cpu_key);
    965         rx_entry = NULL;
    966         rx_unit = -1;
    967         rx_idx  = -1;
    968         CPUDB_KEY_SEARCH(db_ref, rx_key, rx_entry);
    969         if (rx_entry) {
    970             rx_idx = entry->sp_info[i].rx_stk_idx;
    971             if ((rx_idx >= 0) && (rx_idx < CPUDB_CXN_MAX)) {
    972                 rx_unit = rx_entry->base.stk_ports[rx_idx].unit;
    973             } else {
    974                 return BCM_E_NOT_FOUND;
    975             }
    976         } else {
    977             return BCM_E_NOT_FOUND;
    978         }
    979 
    980         /*
    981          * Allow only mod IDs of those stack ports with same RX CPU key
    982          * and same remote stack-port unit.
    983          *
    984          * Note: If stack-port information for remote CPU entries
    985          *       is not available, then check for CPU key only
    986          *       (old behavior).
    987          */
    988         for (j = 0; j < entry->base.num_stk_ports; j++) {
    989             if (entry->base.stk_ports[j].unit != unit) {
    990                 continue;
    991             }
    992             if (!(entry->sp_info[j].flags & CPUDB_SPF_RX_RESOLVED)) {
    993                 continue;
    994             }
    995 
    996             /*
    997              * Get other stack port RX CPU key and unit to
    998              * compare against master port
    999              */
   1000             CPUDB_KEY_COPY(pkey, entry->sp_info[j].rx_cpu_key);
   1001             punit = -1;
   1002             if (rx_unit >= 0) {
   1003                 idx = entry->sp_info[j].rx_stk_idx;
   1004                 if ((idx >= 0) && (idx < CPUDB_CXN_MAX) && rx_entry) {
   1005                     punit = rx_entry->base.stk_ports[idx].unit;
   1006                 }
   1007             }
   1008 
   1009             /* Skip port if RX CPU key or unit are different */
   1010             if (!CPUDB_KEY_EQUAL(rx_key, pkey) || (rx_unit != punit)) {
   1011                 continue;
   1012             }
   1013 
   1014             /* Add modules that are rxable on this stack port.
   1015                This is for normal topology.
   1016              */
   1017             tsp = &tp_cpu->tp_stk_port[j];
   1018             for (m = 0; m < tsp->rx_mod_num; m++) {
   1019                 int modid = tsp->rx_mods[m];
   1020                 /* modid on this port */
   1021                 SHR_BITSET(info->modid[i], modid);
   1022                 /* all modids */
   1023                 SHR_BITSET(info->all_modid, modid);
   1024                 /* modid to base stack port index */
   1025                 info->n_idx[modid] = info->t_idx[i];
   1026             }
   1027         }
   1028         /* Now if this is not a cut port, that means this port will be
   1029            used for SAFC traffic. Gather all the modids reachable in
   1030            the SAFC topology. */
   1031         if (!(entry->sp_info[i].flags & (CPUDB_SPF_CUT_PORT))) {
   1032             sal_memset(info->s_visit, 0, sizeof(stkset_t));
   1033             SHR_BITSET(info->s_visit, entry->topo_idx);
   1034             rv = _bcm_board_safc_collect_modid(info, i, rx_entry, rx_idx);
   1035             BCM_IF_ERROR_RETURN(rv);
   1036         }
   1037     }
   1038 
   1039     /* set blocked modid map. This is a list of modids for each *normal*
   1040        stack port that is not expected to be received on that port. */
   1041     for (i = 0; i < entry->base.num_stk_ports; i++) {
   1042         for (j = 0; j < TOPO_MODIDS_MAX; j++) {
   1043             if (SHR_BITGET(info->all_modid, j) &&
   1044                 (!SHR_BITGET(info->modid[i], j))) {
   1045                 /* valid modid not on this port */
   1046                 SHR_BITSET(info->blocked[i], j);
   1047             }
   1048         }
   1049     }
   1050 
   1051     rv = _bcm_board_safc_unit_iterate(info, _bcm_board_safc_data_unit_init);
   1052     BCM_IF_ERROR_RETURN(rv);
   1053 
   1054     return BCM_E_NONE;
   1055 }
   1056 
   1057 /* SAFC data allocator and initializer */
   1058 STATIC int
   1059 _bcm_board_safc_data(safc_data_t **info_p,
   1060                      topo_cpu_t *tp_cpu, cpudb_ref_t db_ref, topo_info_t *safc)
   1061 {
   1062     safc_data_t *info;
   1063     int rv;
   1064 
   1065     info = sal_alloc(sizeof(*info), "info");
   1066     if (info) {
   1067         sal_memset(info, 0, sizeof(*info));
   1068         rv = _bcm_board_safc_data_set(info, tp_cpu, db_ref, safc);
   1069         if (BCM_FAILURE(rv)) {
   1070             sal_free(info);
   1071             info = NULL;
   1072         }
   1073     } else {
   1074         rv = BCM_E_MEMORY;
   1075     }
   1076     if (info) {
   1077         *info_p = info;
   1078     }
   1079     return rv;
   1080 }
   1081 
   1082 STATIC int
   1083 _bcm_board_safc_only_higig_u_cos(safc_data_t *info, int unit, bcm_cos_t cos)
   1084 {
   1085     bcm_field_entry_t e1;
   1086     int rv;
   1087 
   1088     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1089     BCM_IF_ERROR_RETURN(rv);
   1090 
   1091     rv = bcm_field_qualify_InPorts(unit, e1,
   1092                                    info->safc_pbm[unit],
   1093                                    info->all_pbm_mask);
   1094     BCM_IF_ERROR_RETURN(rv);
   1095 
   1096     rv = bcm_field_qualify_OuterVlan(unit, e1,
   1097                                      BCM_VLAN_CTRL(cos, 0, 0),
   1098                                      info->safc_vlan_mask);
   1099     BCM_IF_ERROR_RETURN(rv);
   1100 
   1101     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1102                                       FUNCTION_NAME());
   1103     BCM_IF_ERROR_RETURN(rv);
   1104 
   1105     rv = bcm_field_entry_install(unit, e1);
   1106     BCM_IF_ERROR_RETURN(rv);
   1107 
   1108     return BCM_E_NONE;
   1109 }
   1110 
   1111 /* SAFC-only Higig unit programming */
   1112 STATIC int
   1113 _bcm_board_safc_only_higig_u(safc_data_t *info, int unit)
   1114 {
   1115     return _bcm_board_safc_cos_iterate(info, unit,
   1116                                        _bcm_board_safc_only_higig_u_cos);
   1117 }
   1118 
   1119 STATIC int
   1120 _bcm_board_safc_drop_ingress_u_cos(safc_data_t *info, int unit, bcm_cos_t cos)
   1121 {
   1122     bcm_field_entry_t e1;
   1123     int rv;
   1124 
   1125     /* Drop SAFC traffic ingressing on normal HG ports */
   1126     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1127     BCM_IF_ERROR_RETURN(rv);
   1128 
   1129     rv = bcm_field_qualify_InPorts(unit, e1,
   1130                                    info->normal_pbm[unit], info->all_pbm_mask);
   1131     BCM_IF_ERROR_RETURN(rv);
   1132 
   1133     rv = bcm_field_qualify_OuterVlan(unit, e1,
   1134                                      BCM_VLAN_CTRL(cos, 0, 0),
   1135                                      info->safc_vlan_mask);
   1136     BCM_IF_ERROR_RETURN(rv);
   1137 
   1138     rv = bcm_field_action_add(unit, e1, bcmFieldActionDrop, 0, 0);
   1139     BCM_IF_ERROR_RETURN(rv);
   1140 
   1141     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1142                                       FUNCTION_NAME());
   1143     BCM_IF_ERROR_RETURN(rv);
   1144 
   1145     rv = bcm_field_entry_install(unit, e1);
   1146     BCM_IF_ERROR_RETURN(rv);
   1147 
   1148     return BCM_E_NONE;
   1149 }
   1150 
   1151 /* Mixed unit programming */
   1152 STATIC int
   1153 _bcm_board_safc_drop_ingress_u(safc_data_t *info, int unit)
   1154 {
   1155     return _bcm_board_safc_cos_iterate(info, unit,
   1156                                        _bcm_board_safc_drop_ingress_u_cos);
   1157 }
   1158 
   1159 STATIC int
   1160 _bcm_board_safc_block_egress_u_cos(safc_data_t *info, int unit, bcm_cos_t cos)
   1161 {
   1162     bcm_field_entry_t e1;
   1163     int rv;
   1164 
   1165     /* Block SAFC traffic from egressing normal HG ports */
   1166 
   1167     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1168     BCM_IF_ERROR_RETURN(rv);
   1169 
   1170     rv = bcm_field_qualify_InPorts(unit, e1,
   1171                                    info->safc_pbm[unit], info->all_pbm_mask);
   1172     BCM_IF_ERROR_RETURN(rv);
   1173 
   1174     rv = bcm_field_qualify_OuterVlan(unit, e1,
   1175                                      BCM_VLAN_CTRL(cos, 0, 0),
   1176                                      info->safc_vlan_mask);
   1177     BCM_IF_ERROR_RETURN(rv);
   1178 
   1179     rv = bcm_field_action_ports_add(unit, e1,
   1180                                     bcmFieldActionEgressMask,
   1181                                     info->normal_pbm[unit]);
   1182     BCM_IF_ERROR_RETURN(rv);
   1183 
   1184     rv =  _bcm_board_safc_count_create(unit, info->group[unit],
   1185                                       e1, FUNCTION_NAME());
   1186     BCM_IF_ERROR_RETURN(rv);
   1187 
   1188     rv = bcm_field_entry_install(unit, e1);
   1189     BCM_IF_ERROR_RETURN(rv);
   1190 
   1191     return BCM_E_NONE;
   1192 }
   1193 
   1194 STATIC int
   1195 _bcm_board_safc_block_egress_u(safc_data_t *info, int unit)
   1196 {
   1197     return _bcm_board_safc_cos_iterate(info, unit,
   1198                                        _bcm_board_safc_block_egress_u_cos);
   1199 }
   1200 
   1201 STATIC int
   1202 _bcm_board_mixed_higig_u(safc_data_t *info, int unit)
   1203 {
   1204     int rv;
   1205 
   1206     rv = _bcm_board_safc_drop_ingress_u(info, unit);
   1207     BCM_IF_ERROR_RETURN(rv);
   1208 
   1209     rv = _bcm_board_safc_block_egress_u(info, unit);
   1210     BCM_IF_ERROR_RETURN(rv);
   1211 
   1212     return BCM_E_NONE;
   1213 }
   1214 
   1215 /* Per-port, per modid, UNDO src modid blocking */
   1216 
   1217 STATIC int
   1218 _bcm_board_safc_undo_source_modid_block_p(safc_data_t *info, int spidx)
   1219 {
   1220     int m, unit, port;
   1221     int rv;
   1222 
   1223     unit = info->db_ref->local_entry->base.stk_ports[spidx].unit;
   1224     port = info->db_ref->local_entry->base.stk_ports[spidx].port;
   1225 
   1226     /* No ports on this unit may be actively participating in
   1227        any topology. If so, then skip. */
   1228     if (!SHR_BITGET(info->units, unit)) {
   1229         return BCM_E_NONE;
   1230     }
   1231 
   1232     for (m=0; m < TOPO_MODIDS_MAX; m++) {
   1233         if (SHR_BITGET(info->blocked[spidx], m)) {
   1234             /* this modid should be blocked */
   1235 
   1236             /* undo device table block */
   1237             rv = bcm_port_modid_enable_set(unit, port, m, TRUE);
   1238             BCM_IF_ERROR_RETURN(rv);
   1239         }
   1240     }
   1241 
   1242     return BCM_E_NONE;
   1243 }
   1244 
   1245 
   1246 
   1247 /* SAFC/normal unicast forwarding */
   1248 
   1249 STATIC int
   1250 _bcm_board_safc_unicast_forward_cos(safc_data_t *info,
   1251                                     int unit, int port, int modid, int dest,
   1252                                     bcm_pbmp_t egress_mask, bcm_cos_t cos)
   1253 {
   1254     bcm_field_entry_t e1;
   1255     int rv;
   1256 
   1257     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1258     BCM_IF_ERROR_RETURN(rv);
   1259 
   1260     rv = bcm_field_qualify_InPorts(unit, e1,
   1261                                    info->port_config[unit].e,
   1262                                    info->all_pbm_mask);
   1263     BCM_IF_ERROR_RETURN(rv);
   1264 
   1265     rv = bcm_field_qualify_L2DestHit(unit, e1, 1, 1);
   1266     BCM_IF_ERROR_RETURN(rv);
   1267 
   1268     if (dest) {
   1269         rv = bcm_field_qualify_OuterVlan(unit, e1,
   1270                                          BCM_VLAN_CTRL(cos, 0, 0),
   1271                                          info->safc_vlan_mask);
   1272         BCM_IF_ERROR_RETURN(rv);
   1273     }
   1274 
   1275     rv = bcm_field_qualify_DstPort(unit, e1, modid,
   1276                                     BCM_FIELD_EXACT_MATCH_MASK, 0, 0);
   1277     BCM_IF_ERROR_RETURN(rv);
   1278 
   1279     rv = bcm_field_action_ports_add(unit, e1,
   1280                                     bcmFieldActionEgressMask, egress_mask);
   1281     BCM_IF_ERROR_RETURN(rv);
   1282 
   1283     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1284                                       FUNCTION_NAME());
   1285     BCM_IF_ERROR_RETURN(rv);
   1286 
   1287     rv = bcm_field_entry_install(unit, e1);
   1288     BCM_IF_ERROR_RETURN(rv);
   1289 
   1290     return BCM_E_NONE;
   1291 
   1292 }
   1293 
   1294 /* per-unit, per-dest-type unicast forwarding
   1295 
   1296    dest=1 SAFC
   1297    dest=0 normal
   1298 
   1299    Standard board programming should already add the normal topology
   1300    port to the modport map, so this function just adds the SAFC port.
   1301 */
   1302 
   1303 #define TRUNK(n) (((n)>1) ? "trunk " : "")
   1304 
   1305 /* Check if a stack unit has multi stack ports mapped to the same modid. */
   1306 STATIC int
   1307 _bcm_board_safc_multi_modmap(safc_data_t *info, int unit, int *multi_map)
   1308 {
   1309     int modid;
   1310     int rv, i, j;
   1311     int port_max = 16, port_count = 0;
   1312     bcm_port_t port_array[16];
   1313 
   1314     for (modid = 0; modid < TOPO_MODIDS_MAX; modid++) {
   1315         if (SHR_BITGET(info->all_modid, modid)) {
   1316             rv = bcm_stk_modport_get_all(unit, modid, port_max,
   1317                                          port_array, &port_count);
   1318             BCM_IF_ERROR_RETURN(rv);
   1319             if (port_count > 1) {
   1320                 for (i = 0; i < (port_count - 1); i++) {
   1321                     for (j = (i + 1); j < port_count; j++) {
   1322                         if (port_array[i] != port_array[j]) {
   1323                             *multi_map = TRUE;
   1324                             return BCM_E_NONE;
   1325                         }
   1326                     }
   1327                 }
   1328             }
   1329         }
   1330     }
   1331 
   1332     *multi_map = FALSE;
   1333     return BCM_E_NONE;
   1334 }
   1335 
   1336 STATIC int
   1337 _bcm_board_safc_unicast_forward_port(safc_data_t *info,
   1338                                      int unit, int modid, int dest)
   1339 {
   1340     int spidx, otidx;
   1341     int spunit, otunit;
   1342     int s_port, num_port;
   1343     bcm_pbmp_t s_pbmp, o_pbmp;
   1344     bcm_pbmp_t egress_mask;
   1345     int rv;
   1346     bcm_cos_t cos;
   1347 
   1348     /* get stack port index appropriate for destination */
   1349     spidx = dest ? info->s_idx[modid] : info->n_idx[modid];
   1350     if (spidx < 0) {
   1351         return BCM_E_NONE;
   1352     }
   1353     spunit = info->db_ref->local_entry->base.stk_ports[spidx].unit;
   1354 
   1355     BCM_PBMP_ASSIGN(s_pbmp, info->trunk[spidx]);
   1356     if (BCM_PBMP_IS_NULL(s_pbmp)) {
   1357         return BCM_E_INTERNAL;
   1358     }
   1359 
   1360     /* get the other frame format stack port index */
   1361     otidx = dest ? info->n_idx[modid] : info->s_idx[modid];
   1362     if (otidx < 0) {
   1363         return BCM_E_NONE;
   1364     }
   1365     otunit = info->db_ref->local_entry->base.stk_ports[otidx].unit;
   1366 
   1367     BCM_PBMP_ASSIGN(o_pbmp, info->trunk[otidx]);
   1368     if (BCM_PBMP_IS_NULL(o_pbmp)) {
   1369         return BCM_E_INTERNAL;
   1370     }
   1371 
   1372     if (unit != spunit && unit != otunit) {
   1373         /* modid is not forwarded off this unit, so skip */
   1374         return BCM_E_NONE;
   1375     }
   1376 
   1377 
   1378     /* set s_port to first port */
   1379     BCM_PBMP_ITER(s_pbmp, s_port) {
   1380         break;
   1381     }
   1382 
   1383     BCM_PBMP_COUNT(s_pbmp, num_port);
   1384 
   1385     /* mask out all ports for egress except for the ports appropriate
   1386        for this packet type. */
   1387     BCM_PBMP_CLEAR(egress_mask);
   1388     if (BCM_PBMP_NEQ(o_pbmp, s_pbmp)) {
   1389         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1390                     (BSL_META_U(unit,
   1391                     "%s: masking for modid %d egress to %s%d\n"),
   1392                      FUNCTION_NAME(), modid, TRUNK(num_port), s_port));
   1393         BCM_PBMP_ASSIGN(egress_mask, o_pbmp);
   1394     } else {
   1395         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1396                     (BSL_META_U(unit,
   1397                     "%s: no mask for modid %d egress to %s%d\n"),
   1398                      FUNCTION_NAME(), modid, TRUNK(num_port), s_port));
   1399     }
   1400 
   1401     if (dest) {
   1402         /* if SAFC, add the first port to modport */
   1403         rv = bcm_stk_modport_add(unit, modid, s_port);
   1404         BCM_IF_ERROR_RETURN(rv);
   1405     }
   1406 
   1407     /* forwarding rules for each port */
   1408     BCM_PBMP_ITER(s_pbmp, s_port) {
   1409         for (cos = BCM_COS_MAX; cos >= BCM_COS_MIN; cos--) {
   1410 
   1411             /* check each COS if SAFC dest */
   1412             if ((dest != 0) && ((info->safc_cos_map & (1<<cos)) == 0)) {
   1413                 continue;
   1414             }
   1415             rv = _bcm_board_safc_unicast_forward_cos(info, spunit, s_port,
   1416                                                      modid, dest,
   1417                                                      egress_mask, cos);
   1418             BCM_IF_ERROR_RETURN(rv);
   1419 
   1420             /* COS ignored if !SAFC, so quit */
   1421             if (dest == 0) {
   1422                 break;
   1423             }
   1424         }
   1425     }
   1426 
   1427     return BCM_E_NONE;
   1428 }
   1429 
   1430 
   1431 STATIC int
   1432 _bcm_board_safc_unicast_ingress_lag(safc_data_t *info, int unit)
   1433 {
   1434     bcm_field_entry_t e1;
   1435     bcm_pbmp_t egress_mask;
   1436     int s_port, num_port;
   1437     int rv, multi_map;
   1438 
   1439     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1440                 (BSL_META_U(unit,
   1441                 "%s(<>,%d)\n"),
   1442                  FUNCTION_NAME(), unit));
   1443 
   1444     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1445     BCM_IF_ERROR_RETURN(rv);
   1446 
   1447     rv = bcm_field_qualify_L2DestHit(unit, e1, 1, 1);
   1448     BCM_IF_ERROR_RETURN(rv);
   1449 
   1450     /* It just needs to be a trunk; it doesn't matter what the trunk ID is */
   1451     rv = bcm_field_qualify_DstTrunk(unit, e1, 0, 0);
   1452     BCM_IF_ERROR_RETURN(rv);
   1453 
   1454     BCM_PBMP_COUNT(info->normal_pbm[unit], num_port);
   1455     if (num_port != 0) {
   1456         rv = bcm_field_action_ports_add(unit, e1, bcmFieldActionEgressMask,
   1457                                         info->normal_pbm[unit]);
   1458     } else {
   1459         BCM_PBMP_CLEAR(egress_mask);
   1460         BCM_PBMP_COUNT(info->safc_pbm[unit], num_port);
   1461         rv = _bcm_board_safc_multi_modmap(info, unit, &multi_map);
   1462         BCM_IF_ERROR_RETURN(rv);
   1463 
   1464         if ((num_port > 1) && multi_map) {
   1465             /* Simply pick a port from the safc_pbm and mask out egress */
   1466             BCM_PBMP_ITER(info->safc_pbm[unit], s_port) {
   1467                 break;
   1468             }
   1469             BCM_PBMP_PORT_ADD(egress_mask, s_port);
   1470         }
   1471         rv = bcm_field_action_ports_add(unit, e1, bcmFieldActionEgressMask,
   1472                                         egress_mask);
   1473     }
   1474     BCM_IF_ERROR_RETURN(rv);
   1475 
   1476     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1477                                       FUNCTION_NAME());
   1478     BCM_IF_ERROR_RETURN(rv);
   1479 
   1480     rv = bcm_field_entry_install(unit, e1);
   1481     BCM_IF_ERROR_RETURN(rv);
   1482 
   1483     return BCM_E_NONE;
   1484 }
   1485 
   1486 STATIC int
   1487 _bcm_board_safc_unicast_forwarding_u_d(safc_data_t *info, int unit, int dest)
   1488 {
   1489     int modid;
   1490     int rv;
   1491 
   1492     if (dest) {
   1493         /* LAG handling */
   1494         rv = _bcm_board_safc_unicast_ingress_lag(info, unit);
   1495         BCM_IF_ERROR_RETURN(rv);
   1496     }
   1497 
   1498     for (modid = 0; modid < TOPO_MODIDS_MAX; modid++) {
   1499         if (SHR_BITGET(info->all_modid, modid)) {
   1500             rv = _bcm_board_safc_unicast_forward_port(info, unit,
   1501                                                       modid, dest);
   1502             BCM_IF_ERROR_RETURN(rv);
   1503         }
   1504     }
   1505 
   1506     return BCM_E_NONE;
   1507 }
   1508 
   1509 /* per-unit SAFC unicast forwarding */
   1510 STATIC int
   1511 _bcm_board_safc_unicast_forwarding_u(safc_data_t *info, int unit)
   1512 {
   1513     return _bcm_board_safc_unicast_forwarding_u_d(info, unit, 1);
   1514 }
   1515 
   1516 /* per-unit Normal unicast forwarding */
   1517 STATIC int
   1518 _bcm_board_normal_unicast_forwarding_u(safc_data_t *info, int unit)
   1519 {
   1520     return _bcm_board_safc_unicast_forwarding_u_d(info, unit, 0);
   1521 }
   1522 
   1523 /* per-unit non-unicast egress blocking from Ethernet ports */
   1524 STATIC int
   1525 _bcm_board_safc_nonunicast_egress_blocking_u_cos(safc_data_t *info,
   1526                                                  int unit, bcm_cos_t cos)
   1527 {
   1528     bcm_field_entry_t e1;
   1529     int rv;
   1530 
   1531     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1532     BCM_IF_ERROR_RETURN(rv);
   1533 
   1534     rv = bcm_field_qualify_InPorts(unit, e1, info->port_config[unit].e,
   1535                                    info->all_pbm_mask);
   1536     BCM_IF_ERROR_RETURN(rv);
   1537 
   1538     rv = bcm_field_qualify_L2DestHit(unit, e1, 0, 1);
   1539     BCM_IF_ERROR_RETURN(rv);
   1540 
   1541     /* Block all L2 misses instead of source modid blocking. */
   1542     rv = bcm_field_action_ports_add(unit, e1, bcmFieldActionEgressMask,
   1543                                     info->normal_pbm[unit]);
   1544     BCM_IF_ERROR_RETURN(rv);
   1545 
   1546     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1547                                       FUNCTION_NAME());
   1548     BCM_IF_ERROR_RETURN(rv);
   1549 
   1550     rv = bcm_field_entry_install(unit, e1);
   1551     BCM_IF_ERROR_RETURN(rv);
   1552 
   1553     return BCM_E_NONE;
   1554 }
   1555 
   1556 /* per-unit MH header egress blocking from SAFC Higig ports */
   1557 STATIC int
   1558 _bcm_board_safc_hg_op_egress_blocking_u(safc_data_t *info, int unit, int op)
   1559 {
   1560     bcm_field_entry_t e1;
   1561     int rv;
   1562 
   1563     rv = bcm_field_entry_create(unit, info->group[unit], &e1);
   1564     BCM_IF_ERROR_RETURN(rv);
   1565 
   1566     rv = bcm_field_qualify_InPorts(unit, e1,
   1567                                    info->safc_pbm[unit],
   1568                                    info->all_pbm_mask);
   1569     BCM_IF_ERROR_RETURN(rv);
   1570 
   1571     rv = bcm_field_qualify_MHOpcode(unit, e1, op, 0x7);
   1572     BCM_IF_ERROR_RETURN(rv);
   1573 
   1574     /* Block all L2 misses instead of source modid blocking. */
   1575     rv = bcm_field_action_ports_add(unit, e1, bcmFieldActionEgressMask,
   1576                                     info->normal_pbm[unit]);
   1577     BCM_IF_ERROR_RETURN(rv);
   1578 
   1579     rv = _bcm_board_safc_count_create(unit, info->group[unit], e1,
   1580                                       FUNCTION_NAME());
   1581     BCM_IF_ERROR_RETURN(rv);
   1582 
   1583     rv = bcm_field_entry_install(unit, e1);
   1584     BCM_IF_ERROR_RETURN(rv);
   1585 
   1586     return BCM_E_NONE;
   1587 }
   1588 
   1589 
   1590 STATIC int
   1591 _bcm_board_safc_nonunicast_egress_blocking_u(safc_data_t *info, int unit)
   1592 {
   1593     int rv;
   1594 
   1595     rv = _bcm_board_safc_cos_iterate(info, unit,
   1596              _bcm_board_safc_nonunicast_egress_blocking_u_cos);
   1597     BCM_IF_ERROR_RETURN(rv);
   1598 
   1599     rv = _bcm_board_safc_hg_op_egress_blocking_u(info, unit,
   1600              BCM_FIELD_MHOPCODE_BROADCAST_DLF);
   1601     BCM_IF_ERROR_RETURN(rv);
   1602 
   1603     rv = _bcm_board_safc_hg_op_egress_blocking_u(info, unit,
   1604              BCM_FIELD_MHOPCODE_MULTICAST);
   1605     BCM_IF_ERROR_RETURN(rv);
   1606 
   1607     rv = _bcm_board_safc_hg_op_egress_blocking_u(info, unit,
   1608              BCM_FIELD_MHOPCODE_IPMULTICAST);
   1609     BCM_IF_ERROR_RETURN(rv);
   1610 
   1611     return BCM_E_NONE;
   1612 }
   1613 
   1614 /*
   1615   System level programming using iterators and per-unit or per-port
   1616   programming functions.
   1617 */
   1618 
   1619 /* SAFC only higig programming */
   1620 STATIC int
   1621 _bcm_board_safc_only_higig(safc_data_t *info)
   1622 {
   1623     int rv;
   1624 
   1625     rv =_bcm_board_safc_unit_iterate(info, _bcm_board_safc_only_higig_u);
   1626     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1627                 (BSL_META("%s()=%d\n"),
   1628                  FUNCTION_NAME(), rv));
   1629 
   1630     return rv;
   1631 }
   1632 
   1633 /* Mixed SAFC/normal higig programming */
   1634 STATIC int
   1635 _bcm_board_mixed_higig(safc_data_t *info)
   1636 {
   1637     int rv;
   1638 
   1639     rv = _bcm_board_safc_unit_iterate(info, _bcm_board_mixed_higig_u);
   1640     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1641                 (BSL_META("%s()=%d\n"),
   1642                  FUNCTION_NAME(), rv));
   1643 
   1644     return rv;
   1645 }
   1646 
   1647 /* UNDO Source modid blocking */
   1648 STATIC int
   1649 _bcm_board_safc_undo_source_modid_block(safc_data_t *info)
   1650 {
   1651     int rv;
   1652 
   1653     rv = _bcm_board_safc_stkport_iterate
   1654         (info, _bcm_board_safc_undo_source_modid_block_p);
   1655     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1656                 (BSL_META("%s()=%d\n"),
   1657                  FUNCTION_NAME(), rv));
   1658 
   1659     return rv;
   1660 }
   1661 
   1662 /* SAFC unicast forwarding */
   1663 STATIC int
   1664 _bcm_board_safc_unicast_forwarding(safc_data_t *info)
   1665 {
   1666     int rv;
   1667 
   1668     rv = _bcm_board_safc_unit_iterate
   1669         (info, _bcm_board_safc_unicast_forwarding_u);
   1670     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1671                 (BSL_META("%s()=%d\n"),
   1672                  FUNCTION_NAME(), rv));
   1673 
   1674     return rv;
   1675 }
   1676 
   1677 /* Normal unicast forwarding */
   1678 STATIC int
   1679 _bcm_board_normal_unicast_forwarding(safc_data_t *info)
   1680 {
   1681     int rv;
   1682 
   1683     rv = _bcm_board_safc_unit_iterate
   1684         (info, _bcm_board_normal_unicast_forwarding_u);
   1685     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1686                 (BSL_META("%s()=%d\n"),
   1687                  FUNCTION_NAME(), rv));
   1688 
   1689     return rv;
   1690 }
   1691 
   1692 /* non-unicast egress blocking */
   1693 STATIC int
   1694 _bcm_board_safc_nonunicast_egress_blocking(safc_data_t *info)
   1695 {
   1696     int rv;
   1697 
   1698     rv = _bcm_board_safc_unit_iterate
   1699         (info, _bcm_board_safc_nonunicast_egress_blocking_u);
   1700     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1701                 (BSL_META("%s()=%d\n"),
   1702                  FUNCTION_NAME(), rv));
   1703 
   1704     return rv;
   1705 }
   1706 
   1707 /* field programming group initialization */
   1708 STATIC int
   1709 _bcm_board_safc_unit_init(safc_data_t *info, int unit)
   1710 {
   1711     info->group[unit] = -1;
   1712 
   1713     return
   1714         bcm_field_group_create(unit,
   1715                                info->qset,
   1716                                info->group_priority, info->group + unit);
   1717 }
   1718 
   1719 
   1720 /* field programming initialization */
   1721 STATIC int
   1722 _bcm_board_safc_init(safc_data_t *info,
   1723                      uint32 safc_cos_map,
   1724                      int group_priority)
   1725 {
   1726     int rv;
   1727 
   1728     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1729                 (BSL_META("%s\n"),
   1730                  FUNCTION_NAME()));
   1731 
   1732     info->safc_cos_map = safc_cos_map;
   1733     info->safc_vlan_mask = BCM_VLAN_CTRL(BCM_COS_MAX,0,0);
   1734     info->group_priority = group_priority;
   1735 
   1736     BCM_FIELD_QSET_INIT(info->qset);
   1737     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyInPorts);
   1738     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyDstTrunk);
   1739     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyDstPort);
   1740     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyOuterVlan);
   1741     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyStageIngress);
   1742     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyL2DestHit);
   1743     BCM_FIELD_QSET_ADD(info->qset,bcmFieldQualifyMHOpcode);
   1744 
   1745     rv = _bcm_board_safc_unit_iterate(info, _bcm_board_safc_unit_init);
   1746 
   1747     return rv;
   1748 }
   1749 
   1750 /* SAFC configuration */
   1751 STATIC int
   1752 _bcm_board_safc_configure(topo_cpu_t *tp_cpu,
   1753                           cpudb_ref_t db_ref,
   1754                           uint32 safc_cos_map,
   1755                           int group_priority,
   1756                           topo_info_t *safc)
   1757 {
   1758     int rv;
   1759     safc_data_t *info;
   1760 
   1761     rv = BCM_E_PARAM;
   1762     info = NULL;
   1763 
   1764     if ((safc_cos_map & ~((1<<BCM_COS_COUNT)-1)) != 0) {
   1765         /* should be no bits set from bit BCM_COS_COUNT and higher */
   1766         goto done;
   1767     }
   1768 
   1769     rv = _bcm_board_safc_data(&info, tp_cpu, db_ref, safc);
   1770     if (BCM_FAILURE(rv)) {
   1771         goto done;
   1772     }
   1773 
   1774     if (!info) {
   1775         rv = BCM_E_MEMORY;
   1776         goto done;
   1777     }
   1778 
   1779     rv = _bcm_board_safc_init(info, safc_cos_map, group_priority);
   1780     if (BCM_FAILURE(rv)) {
   1781         goto done;
   1782     }
   1783 
   1784     if (info->safc_only) {
   1785         rv = _bcm_board_safc_only_higig(info);
   1786         if (BCM_FAILURE(rv)) {
   1787             goto done;
   1788         }
   1789     } else {
   1790         rv = _bcm_board_mixed_higig(info);
   1791         if (BCM_FAILURE(rv)) {
   1792             goto done;
   1793         }
   1794     }
   1795 
   1796     rv = _bcm_board_safc_unicast_forwarding(info);
   1797     if (BCM_FAILURE(rv)) {
   1798         goto done;
   1799     }
   1800 
   1801     rv = _bcm_board_normal_unicast_forwarding(info);
   1802     if (BCM_FAILURE(rv)) {
   1803         goto done;
   1804     }
   1805 
   1806     if (!info->safc_only) {
   1807         rv = _bcm_board_safc_nonunicast_egress_blocking(info);
   1808         if (BCM_FAILURE(rv)) {
   1809             goto done;
   1810         }
   1811     }
   1812 
   1813     rv = _bcm_board_safc_undo_source_modid_block(info);
   1814     if (BCM_FAILURE(rv)) {
   1815         goto done;
   1816     }
   1817 
   1818  done:
   1819     if (info) {
   1820         sal_free(info);
   1821     }
   1822 
   1823     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1824                 (BSL_META("%s=%d\n"),
   1825                  FUNCTION_NAME(), rv));
   1826     return rv;
   1827 }
   1828 
   1829 /*
   1830  * Function:
   1831  * int bcm_board_safc_configure(topo_cpu_t *tp_cpu,
   1832  *                              cpudb_ref_t db_ref,
   1833  *                              uint32 safc_cos_map,
   1834  *                              int group_priority,
   1835  *                              topo_info_t *connection)
   1836  * Purpose:
   1837  *     Program SAFC forwarding based on provided connection info
   1838  * Parameters:
   1839  *     tp_cpu         - (IN)  CPU topology information
   1840  *     db_ref         - (IN)  CPU database
   1841  *     safc_cos_map   - (IN)  SAFC COS bitmap
   1842  *     group_priority - (IN)  field group priority
   1843  *     connection     - (IN)  Connection matrix
   1844  * Returns:
   1845  *     BCM_E_NONE     - no errors
   1846  *     BCM_E_FAIL     - topology calculation failed (CPUDB incomplete)
   1847  *     BCM_E_RESOURCE - not enough programming resources
   1848  * Notes:
   1849  *
   1850  *     Performs device programming. Assumes any necessary non-SAFC
   1851  *     programming is complete.
   1852  *
   1853  *     Connection matrix is from bcm_stack_spanning_tree (for SAFC)
   1854  *     *and* db_ref (for non SAFC).
   1855  *
   1856  *     This function will only use SDK APIs. Any register manipulation
   1857  *     required by the application must be managed outside this
   1858  *     function.
   1859  *
   1860  *     Group priority must be higher than any application or system
   1861  *     field group priority, and must be unique for each different
   1862  *     SAFC topology.
   1863  *
   1864  *     SAFC is unconfigured when connection is NULL. In this case,
   1865  *     group_priority is the only parameter that must be valid; tp_cpu,
   1866  *     db_ref, and safc_cos_map is ignored.
   1867  */
   1868 
   1869 int
   1870 bcm_board_safc_configure(topo_cpu_t *tp_cpu,
   1871                          cpudb_ref_t db_ref,
   1872                          uint32 safc_cos_map,
   1873                          int group_priority,
   1874                          topo_info_t *connection)
   1875 {
   1876     int rv;
   1877 
   1878     rv = BCM_E_PARAM;
   1879 
   1880     if (cpudb_valid(db_ref) && tp_cpu && connection) {
   1881         (void) _bcm_board_safc_unconfigure(group_priority);
   1882         rv = _bcm_board_safc_configure(tp_cpu, db_ref,
   1883                                        safc_cos_map, group_priority,
   1884                                        connection);
   1885         if (BCM_FAILURE(rv)) {
   1886             (void) _bcm_board_safc_unconfigure(group_priority);
   1887         }
   1888     } else if (connection == NULL) {
   1889         rv = _bcm_board_safc_unconfigure(group_priority);
   1890     }
   1891 
   1892     return rv;
   1893 }
   1894 
   1895