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

topo_brd.c (37593B)


      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:        topo_brd.c
      8  * Purpose:
      9  * Requires:    
     10  */
     11 
     12 #include <shared/bsl.h>
     13 
     14 #include <bcm/error.h>
     15 #include <bcm/types.h>
     16 #include <bcm/topo.h>
     17 #include <bcm/stack.h>
     18 #include <bcm/link.h>
     19 #include <bcm/port.h>
     20 #include <bcm/switch.h>
     21 #ifdef BCM_RPC_SUPPORT
     22 #include <bcm_int/rpc/rpc.h>
     23 #endif /* BCM_RPC_SUPPORT */
     24 
     25 #include <appl/stktask/stktask.h>
     26 #include <appl/stktask/topology.h>
     27 #include <appl/stktask/topo_brd.h>
     28 #include <appl/stktask/topo_pkt.h>  
     29 #include <appl/stktask/attach.h>
     30 #include <appl/cpudb/cpudb.h>
     31 #include <appl/cputrans/nh_tx.h>
     32 #include <appl/cputrans/cpu2cpu.h>
     33 #include <appl/cputrans/atp.h>
     34 #include <appl/discover/disc.h>
     35 
     36 
     37 #include <soc/drv.h>
     38 
     39 #include "topo_int.h"
     40 #include "brd_chassis_int.h"
     41 
     42 int topo_board_op(topo_board_control_t op,
     43                   cpudb_ref_t db_ref, topo_cpu_t *topo_cpu);
     44 
     45 static sal_mutex_t topo_data_lock;   /* Serves as init done indication */
     46 #define TD_INIT (topo_data_lock != NULL)
     47 
     48 #define TOPO_DATA_LOCK sal_mutex_take(topo_data_lock, sal_mutex_FOREVER)
     49 #define TOPO_DATA_UNLOCK sal_mutex_give(topo_data_lock)
     50 
     51 /* Manage the system topology information here */
     52 topo_cpu_t bcm_board_topo_data;
     53 volatile int topo_data_set;
     54 
     55 /* Indicates whether stack ports can be set to HW linkscan mode */
     56 int _topo_hw_linkscan = 1;
     57 
     58 
     59 /*
     60  * Rapid Recovery
     61  *
     62  * The following data structure stores information for local stack ports
     63  * used for rapid recovery.
     64  *
     65  * Note:  The stack port index correspond to the same index used
     66  *        for stack port in databases such as cpudb, topo, and stacking.
     67  *        This convention has been used throughout the stacking code.
     68  */
     69 typedef struct topo_sp_info_s {
     70     int          unit;   /* Stack port unit */
     71     bcm_port_t   port;   /* Stack port */
     72     bcm_trunk_t  tid;    /* Stack port trunk ID */
     73 } topo_sp_info_t;
     74 
     75 static topo_sp_info_t  topo_sp[CPUDB_STK_PORTS_MAX];
     76 
     77 #define TOPO_SP_TID_GET(_idx)          (topo_sp[_idx].tid)
     78 #define TOPO_SP_TID_SET(_idx, _tid)    (topo_sp[_idx].tid = _tid)
     79 
     80 STATIC int topo_sp_idx_find(int unit, bcm_port_t port);
     81 STATIC int topo_sp_init(cpudb_entry_t *entry);
     82 STATIC int topo_sp_info_update(cpudb_entry_t *entry);
     83 
     84 
     85 /*
     86  * Set/get the current topology CPU information.
     87  *
     88  * Should be set to NULL when in transition or invalid
     89  *
     90  * This is exposed inside this library, but shouldn't be used
     91  * above that level.
     92  */
     93 
     94 int
     95 bcm_board_topo_set(topo_cpu_t *tp_cpu)
     96 {
     97     topo_data_set = FALSE;
     98     if (!TD_INIT) {
     99         if ((topo_data_lock = sal_mutex_create("topo_data_lock")) == NULL) {
    100             return BCM_E_MEMORY;
    101         }
    102     }
    103 
    104     TOPO_DATA_LOCK;
    105     if (tp_cpu != NULL) {
    106         if (tp_cpu != &bcm_board_topo_data) {
    107             /* Only copy if not the default */
    108             sal_memcpy(&bcm_board_topo_data, tp_cpu, sizeof(topo_cpu_t));
    109         }
    110         topo_data_set = TRUE;
    111     } else {
    112         topo_data_set = FALSE;
    113     }
    114     TOPO_DATA_UNLOCK;
    115 
    116     return BCM_E_NONE;
    117 }
    118 
    119 int
    120 bcm_board_topo_get(topo_cpu_t **tp_cpu)
    121 {
    122     if (topo_data_set) {
    123         *tp_cpu = &bcm_board_topo_data;
    124     } else {
    125         *tp_cpu = NULL;
    126     }
    127     return BCM_E_NONE;
    128 }
    129 
    130 
    131 /*
    132  * Function:
    133  *     topo_cpu_trans_purge
    134  * Purpose:
    135  *     Clears any pending transactions for given remote destination CPU.
    136  * Parameters:
    137  *     entry    - Entry for remote CPU
    138  * Returns:
    139  *     N/A
    140  */
    141 
    142 STATIC void
    143 topo_cpu_trans_purge(cpudb_entry_t *entry)
    144 {
    145     atp_key_purge(entry->base.key);
    146 #ifdef BCM_RPC_SUPPORT
    147     bcm_rpc_cleanup(entry->base.key);
    148 #endif /* BCM_RPC_SUPPORT */
    149 }
    150 
    151 
    152 /*
    153  * Function:
    154  *     topo_cpu_trans_flush
    155  * Purpose:
    156  *     Checks and clears pending transactions for remote CPUs where
    157  *     a different local stack port is now used to transmit to.
    158  * Parameters:
    159  *     db_ref    - Database of current system configuration
    160  * Returns:
    161  *     BCM_E_NONE  Success
    162  * Notes:
    163  *     This routine should be called before a new topology is
    164  *     set for the system.
    165  */
    166 
    167 STATIC int
    168 topo_cpu_trans_flush(cpudb_ref_t db_ref)
    169 {
    170     cpudb_ref_t    prev_db;
    171     cpudb_entry_t  *entry;
    172     cpudb_entry_t  *l_entry;
    173     cpudb_entry_t  *prev_entry;
    174 
    175     if ((prev_db = db_ref->old_db) == NULL) {
    176         return BCM_E_NONE;
    177     }
    178 
    179     l_entry = db_ref->local_entry;
    180 
    181     /* Check if path to get to this remote CPU has changed */
    182     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    183         if (entry == l_entry) {
    184             continue;
    185         }
    186 
    187         CPUDB_KEY_SEARCH(prev_db, entry->base.key, prev_entry);
    188         if (prev_entry != NULL) {
    189             if ((prev_entry->tx_unit == entry->tx_unit) &&
    190                 (prev_entry->tx_port == entry->tx_port)) {
    191                 continue;
    192             }
    193             LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    194                         (BSL_META("TX path changed to CPU " CPUDB_KEY_FMT_EOLN),
    195                          CPUDB_KEY_DISP(entry->base.key)));            
    196             topo_cpu_trans_purge(entry);
    197         }
    198     }
    199                 
    200     return BCM_E_NONE;
    201 }
    202 
    203 /*
    204  * Function:
    205  *      topo_board_setup
    206  * Purpose:
    207  *      Program a board according to the given CPUDB
    208  * Parameters:
    209  *      db_ref     - Data base with system info
    210  * Returns:
    211  *      BCM_E_XXX
    212  * Notes:
    213  *      Currently supports 24 Gig, 48 Gig and 48 FE stacking
    214  *      reference designs.
    215  *
    216  *      If CPUDB contains one entry, set up the topo CPU structure directly.
    217  *      Otherwise, (for now) call the topo packet generate and parse
    218  *      routines to generate the topo CPU structure.
    219  *
    220  *      This routine clears "topo_data_set" and will only set it back to true
    221  *      if the data is successfully generated.
    222  */
    223 
    224 int
    225 topo_board_setup(cpudb_ref_t db_ref)
    226 {
    227     int rv = BCM_E_NONE;
    228 
    229     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    230                 (BSL_META("Topology board setup\n")));
    231     
    232     topo_data_set = FALSE;
    233     if (!TD_INIT) {
    234         if ((topo_data_lock = sal_mutex_create("topo_data_lock")) == NULL) {
    235             return BCM_E_MEMORY;
    236         }
    237     }
    238 
    239     if (db_ref->local_entry == NULL || db_ref->num_cpus <= 0 ||
    240         db_ref->num_cpus > CPUDB_CPU_MAX) {
    241         LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    242                  (BSL_META("TOPO SETUP: Local entry %p, num cpus %d\n"),
    243                   db_ref->local_entry, db_ref->num_cpus));
    244         return BCM_E_PARAM;
    245     }
    246 
    247     TOPO_DATA_LOCK;
    248     /* Clear current topo data */
    249     sal_memset(&bcm_board_topo_data, 0, sizeof(topo_cpu_t));
    250     if (db_ref->num_cpus == 1) {
    251 
    252         /* All local external stack ports have 0 mod id associations. */
    253         if ((rv = cpudb_entry_copy(&bcm_board_topo_data.local_entry,
    254                 db_ref->local_entry)) < 0) {
    255             LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    256                      (BSL_META("TOPO SETUP: Failed to copy entry\n")));
    257         } else {
    258             bcm_board_topo_data.num_cpus = 1;
    259             topo_data_set = TRUE;
    260 
    261             if ((rv = topo_board_program(db_ref, &bcm_board_topo_data)) < 0) {
    262                 LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    263                          (BSL_META("TOPO SETUP: Failed topo brd program: %s\n"),
    264                           bcm_errmsg(rv)));
    265             }
    266         }
    267     } else {
    268         /* For now, use packet generate/parse */
    269         static uint8 _pkt_buf[TOPO_PKT_BYTES_MAX];
    270         int len;
    271 
    272         /* Generate topo pkt; then parse it to get topo cpu struct */
    273         if ((rv = topo_pkt_gen(db_ref, db_ref->local_entry,
    274                                _pkt_buf, TOPO_PKT_BYTES_MAX, &len)) < 0) {
    275             LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    276                      (BSL_META("TOPO SETUP: Failed pkt gen\n")));
    277         } else if ((rv = topo_pkt_parse(db_ref, db_ref->local_entry, _pkt_buf,
    278                              len, &bcm_board_topo_data, NULL)) < 0) {
    279             LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    280                      (BSL_META("TOPO SETUP: Failed pkt parse\n")));
    281         } else {
    282             bcm_board_topo_data.num_cpus = db_ref->num_cpus;
    283             topo_data_set = TRUE;
    284         }
    285     }
    286     /* Set master seq num if present */
    287     if (topo_data_set && db_ref->master_entry != NULL) {
    288         bcm_board_topo_data.master_seq_num =
    289             db_ref->master_entry->base.dseq_num;
    290     }
    291     TOPO_DATA_UNLOCK;
    292 
    293     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    294                 (BSL_META("TOPO SETUP: Exit rv %d, data set %d\n"),
    295                  rv, topo_data_set));
    296     return rv;
    297 }
    298 
    299 /* Set stack ports */
    300 
    301 STATIC int
    302 _topo_stk_ports_update(cpudb_ref_t db_ref, topo_cpu_t *topo_cpu)
    303 {
    304     int unit, port;
    305     uint32 flags, spflags;
    306     cpudb_entry_t *l_entry;
    307     int i;
    308 
    309     l_entry = db_ref->local_entry;
    310 
    311     /* TO DO:  Scan for ports that are no longer stack ports
    312      * Foreach local device
    313      *    get current bitmap
    314      *    foreach port in bitmap
    315      *       search local entry stack ports for (unit, port)
    316      *           if found, continue
    317      *           otherwise, call bcm_stk_port_set with flags = 0
    318      */
    319 
    320     /* Update stack ports */
    321     for (i = 0; i < l_entry->base.num_stk_ports; i++) {
    322         unit = l_entry->base.stk_ports[i].unit;
    323         port = l_entry->base.stk_ports[i].port;
    324         flags = BCM_STK_ENABLE;
    325         spflags = l_entry->sp_info[i].flags;
    326 
    327 
    328 #if !defined(TOPO_BRD_UPDATE_ALL_PORTS)
    329         /* See if stack port flags have changed since the old db.
    330            This is an optimization that depends on the old CPUDB being
    331            propagated. Applications that require pre SDK-5.6.3
    332            behavior can define TOPO_BRD_UPDATE_ALL_PORTS.
    333         */
    334         if ((cpudb_valid(db_ref->old_db)) &&
    335             (db_ref->old_db->local_entry->base.num_stk_ports ==
    336              l_entry->base.num_stk_ports) &&
    337             (db_ref->old_db->local_entry->base.stk_ports[i].unit == unit) &&
    338             (db_ref->old_db->local_entry->base.stk_ports[i].port == port) &&
    339             (db_ref->old_db->local_entry->sp_info[i].flags == spflags)) {
    340             continue;
    341         }
    342 #endif
    343 
    344         /* See if port is inactive (explicit, cut or no link) */
    345         if (spflags & CPUDB_SPF_ETHERNET) {
    346             flags = 0; /* Disable - not a stack port anymore */
    347         } else if (spflags & CPUDB_SPF_NO_LINK) {
    348             flags |= BCM_STK_INACTIVE | BCM_STK_NO_LINK;
    349         } else if (spflags & CPUDB_SPF_INACTIVE) {
    350             flags |= BCM_STK_INACTIVE;
    351         } else if (spflags & CPUDB_SPF_CUT_PORT) {
    352             flags |= BCM_STK_CUT;
    353         } else { /* Port is active */
    354             /* Simplex/duplex: Only mark simplex if not duplex and resolved */
    355             if (spflags & CPUDB_SPF_DUPLEX) {
    356                 flags |= BCM_STK_DUPLEX;
    357             } else {
    358                 if ((spflags & CPUDB_SPF_TX_RESOLVED) &&
    359                     (spflags & CPUDB_SPF_RX_RESOLVED)) {
    360                     flags |= BCM_STK_SIMPLEX;
    361                 }
    362             } /* Else, port is not yet resolved */
    363         }
    364         BCM_IF_ERROR_RETURN(bcm_stk_port_set(unit, port, flags));
    365     }
    366 
    367     return BCM_E_NONE;
    368 }
    369 
    370 
    371 /*
    372  * Function:
    373  *      topo_board_program
    374  * Purpose:
    375  *      Program a board given local topology info
    376  * Parameters:
    377  *      db_ref    - Data base of current configuration
    378  *      topo_cpu  - Info for local board programming
    379  * Returns:
    380  *      BCM_E_XXX
    381  * Notes:
    382  *      Currently supports 24 Gig, 48 Gig and 48 FE stacking
    383  *      reference designs.
    384  */
    385 
    386 STATIC int
    387 _topo_board_default_program(cpudb_ref_t db_ref, topo_cpu_t *topo_cpu)
    388 {
    389     int i, j;
    390     int done;
    391     cpudb_entry_t *entry;
    392     cpudb_entry_t *l_entry;
    393     int rv_tmp, rv = BCM_E_FAIL;
    394     cpudb_unit_port_t *sp;
    395     uint32 flags;
    396             
    397     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    398                 (BSL_META("Topology board programming db_ref %p. old_db %p\n"),
    399                  db_ref, db_ref->old_db));
    400                
    401     l_entry = db_ref->local_entry;
    402 
    403     /* First, handle some things for the local entry */
    404     l_entry->flags |= CPUDB_F_TX_KNOWN;
    405 
    406     /* If not using the reserved modid feature, set the NH source
    407        modid on each resolved stack port */
    408     if (!bcm_st_reserved_modid_enable_get()) {
    409         int loc_mod_id = -1;
    410 
    411         /*
    412          * Program NH TX source module to a local mod ID;
    413          * First, find a local mod ID, if present
    414          */
    415         for (i = 0; i < l_entry->base.num_units; i++) {
    416             if (l_entry->mod_ids[i] >= 0) {
    417                 loc_mod_id = l_entry->mod_ids[i];
    418                 break;  /* Found a local mod ID */
    419             }
    420         } /* for each unit */
    421 
    422         /* Even if loc_mod_id < 0, that's what we want to use */
    423         for (i = 0; i < l_entry->base.num_stk_ports; i++) {
    424             sp = &l_entry->base.stk_ports[i];
    425             BCM_IF_ERROR_RETURN(bcm_stk_port_get(sp->unit, sp->port, &flags));
    426             if (!(flags & BCM_STK_SL) &&
    427                 l_entry->sp_info[i].flags & CPUDB_SPF_RX_RESOLVED) {
    428                 rv_tmp = nh_tx_src_mod_port_set(sp->unit, sp->port,
    429                                                 loc_mod_id, -1);
    430                 if (rv_tmp < 0) {
    431                     LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    432                              (BSL_META("TOPOLOGY WARNING: "
    433                               "Could not set NH mod/port for (%d, %d): %s\n"),
    434                               sp->unit, sp->port, bcm_errmsg(rv_tmp)));
    435                 }
    436             } else {
    437                 /* Allow default NH source mod id */
    438                 rv_tmp = nh_tx_src_mod_port_set(sp->unit, sp->port, -1, -1);
    439                 if (rv_tmp < 0) {
    440                     LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    441                              (BSL_META("TOPOLOGY WARNING: "
    442                               "Could not clear NH mod/port for (%d, %d): "
    443                               "%s\n"),
    444                               sp->unit, sp->port, bcm_errmsg(rv_tmp)));
    445                 }
    446             }
    447         }
    448     }
    449 
    450     /* Set up the tx unit/port for each other destination in the system */
    451 
    452     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    453         if (entry == l_entry) {
    454             continue;
    455         }
    456 
    457         done = FALSE;
    458         /* Find the stack port to get to this CPU */
    459         for (i = 0; i < l_entry->base.num_stk_ports && !done; i++) {
    460             for (j = 0; j < topo_cpu->tp_stk_port[i].tx_mod_num && !done; j++) {
    461                 if (entry->dest_mod == topo_cpu->tp_stk_port[i].tx_mods[j]) {
    462                     entry->tx_unit = l_entry->base.stk_ports[i].unit;
    463                     entry->tx_port = l_entry->base.stk_ports[i].port;
    464                     done = TRUE;
    465                 }
    466             }
    467         }
    468         if (!done) {
    469             LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    470                      (BSL_META("TOPOLOGY WARNING: Did not find tx stack port for "
    471                       CPUDB_KEY_FMT " mod %d\n"), CPUDB_KEY_DISP(entry->base.key),
    472                       entry->dest_mod));
    473         }
    474         /* This amounts to ignoring the warning */
    475         entry->flags |= CPUDB_F_TX_KNOWN;
    476     }
    477 
    478 
    479     /* Check and clear transactions for remote CPUs with a different path */
    480     topo_cpu_trans_flush(db_ref);
    481     
    482     /* Set new topology information */
    483     bcm_board_topo_set(topo_cpu);
    484 
    485     /* Initialize topology stack port info */
    486     topo_sp_init(l_entry);
    487 
    488 #if defined(TOPO_BRD_UPDATE_BEFORE_PROGRAM)
    489     /* Currently, stack port status is updated after the rest of board
    490        programming. The behavior before SDK-5.6.3 was to update the
    491        stack ports before board programming. In principle, the order
    492        should not matter. Define TOPO_BRD_UPDATE_BEFORE_PROGRAM to
    493        restore the older behavior if it is needed. */
    494     BCM_IF_ERROR_RETURN(_topo_stk_ports_update(db_ref, topo_cpu));
    495 #endif
    496 
    497     /* Set up the board based on device detection. */
    498     if (soc_ndev == 1 &&
    499                SOC_IS_HERCULES15(0)) {
    500         rv = bcm_board_topo_cfm_xgs2(topo_cpu, db_ref);
    501     } else if (soc_ndev == 1 &&
    502                SOC_IS_XGS12_FABRIC(0)) {
    503         rv = bcm_board_topo_8h(topo_cpu, db_ref);
    504     } else if (soc_ndev == 1 &&
    505                SOC_IS_RAPTOR(0)) {
    506         rv = bcm_board_topo_xgs3_48g5g(topo_cpu, db_ref);
    507     } else if (soc_ndev == 1 &&
    508                SOC_IS_FB_FX_HX(0)) {
    509         rv = bcm_board_topo_sdk_xgs3_24g(topo_cpu, db_ref);
    510     } else {
    511         
    512         switch (db_ref->local_entry->base.board_id) {
    513         case cpudb_board_id_sdk_xgs3_24g:
    514             rv = bcm_board_topo_sdk_xgs3_24g(topo_cpu, db_ref);
    515             break;
    516         case cpudb_board_id_sdk_xgs3_20x:
    517             rv = bcm_board_topo_xgs3_20x(topo_cpu, db_ref);
    518             break;
    519         case cpudb_board_id_cfm_xgs3:
    520             rv = bcm_board_topo_cfm_xgs3(topo_cpu, db_ref);
    521             break;
    522         case cpudb_board_id_lm_56800_12x:
    523             rv = bcm_board_topo_lm_56800_12x(topo_cpu, db_ref);
    524             break;
    525         case cpudb_board_id_sdk_xgs3_48g:
    526             rv = bcm_board_topo_xgs3_48g(topo_cpu, db_ref);
    527             break;
    528         case cpudb_board_id_sdk_xgs3_48f:
    529             rv = bcm_board_topo_xgs3_48f(topo_cpu, db_ref);
    530             break;
    531         case cpudb_board_id_lm_xgs3_48g:
    532             rv = bcm_board_topo_lm_xgs3_48g(topo_cpu, db_ref);
    533             break;
    534         case cpudb_board_id_sdk_xgs3_48g5g:
    535             rv = bcm_board_topo_xgs3_48g5g(topo_cpu, db_ref);
    536             break;
    537         case cpudb_board_id_sdk_xgs3_48g2:
    538             rv = bcm_board_topo_xgs3_48g2(topo_cpu, db_ref);
    539             break;
    540         default:
    541             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    542                       (BSL_META("TOPOLOGY ERROR: Unknown board\n")));
    543             rv = BCM_E_NOT_FOUND;
    544             break;
    545         }
    546     }
    547     return rv;
    548 }
    549 
    550 int
    551 topo_board_program(cpudb_ref_t db_ref, topo_cpu_t *topo_cpu)
    552 {
    553     int i, j;
    554     int done;
    555     cpudb_entry_t *entry;
    556     cpudb_entry_t *l_entry;
    557     int rv_tmp, rv = BCM_E_FAIL;
    558     cpudb_unit_port_t *sp;
    559     uint32 flags;
    560     int nh_vlan;
    561             
    562     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    563                 (BSL_META("Topology board programming db_ref %p. old_db %p\n"),
    564                  db_ref, db_ref->old_db));
    565                
    566     l_entry = db_ref->local_entry;
    567 
    568     /* First, handle some things for the local entry */
    569     l_entry->flags |= CPUDB_F_TX_KNOWN;
    570 
    571     /* If not using the reserved modid feature, set the NH source
    572        modid on each resolved stack port */
    573     if (!bcm_st_reserved_modid_enable_get()) {
    574         int loc_mod_id = -1;
    575 
    576         /*
    577          * Program NH TX source module to a local mod ID;
    578          * First, find a local mod ID, if present
    579          */
    580         for (i = 0; i < l_entry->base.num_units; i++) {
    581             if (l_entry->mod_ids[i] >= 0) {
    582                 loc_mod_id = l_entry->mod_ids[i];
    583                 break;  /* Found a local mod ID */
    584             }
    585         } /* for each unit */
    586 
    587         /* Even if loc_mod_id < 0, that's what we want to use */
    588         for (i = 0; i < l_entry->base.num_stk_ports; i++) {
    589             sp = &l_entry->base.stk_ports[i];
    590             BCM_IF_ERROR_RETURN(bcm_stk_port_get(sp->unit, sp->port, &flags));
    591             if (!(flags & BCM_STK_SL) &&
    592                 l_entry->sp_info[i].flags & CPUDB_SPF_RX_RESOLVED) {
    593                 rv_tmp = nh_tx_src_mod_port_set(sp->unit, sp->port,
    594                                                 loc_mod_id, -1);
    595                 if (rv_tmp < 0) {
    596                     LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    597                              (BSL_META("TOPOLOGY WARNING: "
    598                               "Could not set NH mod/port for (%d, %d): %s\n"),
    599                               sp->unit, sp->port, bcm_errmsg(rv_tmp)));
    600                 }
    601             } else {
    602                 /* Allow default NH source mod id */
    603                 rv_tmp = nh_tx_src_mod_port_set(sp->unit, sp->port, -1, -1);
    604                 if (rv_tmp < 0) {
    605                     LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    606                              (BSL_META("TOPOLOGY WARNING: "
    607                               "Could not clear NH mod/port for (%d, %d): "
    608                               "%s\n"),
    609                               sp->unit, sp->port, bcm_errmsg(rv_tmp)));
    610                 }
    611             }
    612         }
    613     }
    614 
    615     /* Set up the tx unit/port for each other destination in the system */
    616 
    617     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    618         if (entry == l_entry) {
    619             continue;
    620         }
    621 
    622         done = FALSE;
    623         /* Find the stack port to get to this CPU */
    624         for (i = 0; i < l_entry->base.num_stk_ports && !done; i++) {
    625             for (j = 0; j < topo_cpu->tp_stk_port[i].tx_mod_num && !done; j++) {
    626                 if (entry->dest_mod == topo_cpu->tp_stk_port[i].tx_mods[j]) {
    627                     entry->tx_unit = l_entry->base.stk_ports[i].unit;
    628                     entry->tx_port = l_entry->base.stk_ports[i].port;
    629                     done = TRUE;
    630                 }
    631             }
    632         }
    633         if (!done) {
    634             LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    635                      (BSL_META("TOPOLOGY WARNING: Did not find tx stack port for "
    636                       CPUDB_KEY_FMT " mod %d\n"), CPUDB_KEY_DISP(entry->base.key),
    637                       entry->dest_mod));
    638         }
    639         /* This amounts to ignoring the warning */
    640         entry->flags |= CPUDB_F_TX_KNOWN;
    641     }
    642 
    643 
    644     /* Check and clear transactions for remote CPUs with a different path */
    645     topo_cpu_trans_flush(db_ref);
    646     
    647     /* Set new topology information */
    648     bcm_board_topo_set(topo_cpu);
    649 
    650     /* Initialize topology stack port info */
    651     topo_sp_init(l_entry);
    652 
    653     rv = topo_board_op(topoBoardProgram, db_ref, topo_cpu);
    654 
    655 #if !defined(TOPO_BRD_UPDATE_BEFORE_PROGRAM)
    656     BCM_IF_ERROR_RETURN(_topo_stk_ports_update(db_ref, topo_cpu));
    657 #endif
    658     /*
    659      * Re-install NH TX destination mac address since mod ID
    660      * for local CPU could be different after a topology run
    661      */
    662     next_hop_vlan_get(&nh_vlan);
    663     nh_tx_dest_install(TRUE, nh_vlan);
    664      
    665     if (rv < 0) {
    666         LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    667                  (BSL_META("TOPO board prg: Board programming failed: %s\n"),
    668                   bcm_errmsg(rv)));
    669         bcm_board_topo_set(NULL);
    670     } else {
    671         rv = topo_sp_info_update(l_entry);
    672     }
    673 
    674     return rv;
    675 }
    676 
    677 /*
    678  * Set HW linkscan when this is allowed by flag '_topo_hw_linkscan'
    679  * and Stack Trunk Failover is supported by device.
    680  */
    681 STATIC int
    682 _topo_linkscan_mode_set(cpudb_entry_t *entry)
    683 {
    684     int  i;
    685     int  unit;
    686     int  port;
    687     int  trunk_failover;
    688     
    689     if (!_topo_hw_linkscan) {
    690         return BCM_E_NONE;
    691     }
    692 
    693     /* Using CPUDB same stack port index */
    694     for (i = 0; i < entry->base.num_stk_ports; i++) {
    695         unit = entry->base.stk_ports[i].unit;
    696         port = entry->base.stk_ports[i].port;
    697 
    698         if (BCM_SUCCESS(bcm_switch_control_get(unit,
    699                                                bcmSwitchFailoverStackTrunk,
    700                                                &trunk_failover))) {
    701             if (trunk_failover) {
    702                 BCM_IF_ERROR_RETURN
    703                     (bcm_linkscan_mode_set(unit, port, BCM_LINKSCAN_MODE_HW));
    704             }
    705         }
    706     }
    707 
    708     return BCM_E_NONE;
    709 }
    710 
    711 /*
    712  * Some devices have restrictions on what the allowable modid
    713  * may be, such as the modid must be even when the device requires
    714  * two modids. This isn't true for all such devices, so we just test
    715  * for that property
    716  */
    717 
    718 STATIC int
    719 _topo_set_unit_modid_preference(int unit, int num_modid, int *preference)
    720 {
    721     int current_modid, new_modid;
    722     
    723     if (num_modid == 2) {
    724 
    725         /* If the device requires two modids and the modid is even,
    726            see if the modid can be odd. */
    727         
    728         BCM_IF_ERROR_RETURN(bcm_stk_my_modid_get(unit, &current_modid));
    729         if ((current_modid & 1) == 0) {
    730             int rv;
    731 
    732             new_modid = current_modid+1;
    733             rv = bcm_stk_my_modid_set(unit, new_modid);
    734             if (rv == BCM_E_BADID) {
    735                 *preference = CPUDB_TOPO_MODID_EVEN;
    736                 LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    737                             (BSL_META_U(unit,
    738                             "unit %d modid preference is even\n"),
    739                              unit));
    740             }
    741             BCM_IF_ERROR_RETURN(bcm_stk_my_modid_set(unit, current_modid));
    742         }
    743     }
    744     return BCM_E_NONE;
    745 }
    746 
    747 
    748 /*
    749  * Update the CPU database module ID parameters based on actual device
    750  * configuration.
    751  */
    752 
    753 STATIC int
    754 _topo_set_modid_parameters(cpudb_entry_t *entry)
    755 {
    756     int unit;
    757     int *preference, *required, num_modid;
    758 
    759     for ( unit=0; unit<entry->base.num_units; unit++ ) {
    760         required = &entry->base.mod_ids_req[unit];
    761 
    762         if (*required > 0) {
    763 
    764             preference = &entry->base.pref_mod_id[unit];
    765             
    766             if (*preference == CPUDB_TOPO_MODID_ANY) {
    767                 
    768                 BCM_IF_ERROR_RETURN(bcm_stk_modid_count(unit, &num_modid));
    769 
    770                 /*
    771                   If the number of modids the device needs is more
    772                   than what is requested, then update the number of
    773                   required modids to what the device supports.
    774 
    775                   This is intended to support devices that are
    776                   configurable to use one or more modids.
    777                 */
    778                 
    779                 if (num_modid > *required) {
    780                     *required = num_modid;
    781                 }
    782                 
    783                 BCM_IF_ERROR_RETURN
    784                     (_topo_set_unit_modid_preference(unit,
    785                                                      num_modid, preference));
    786             }
    787         }
    788     }
    789     return BCM_E_NONE;
    790 }
    791 
    792 static int
    793 _topo_board_default_initialize(cpudb_ref_t db_ref)
    794 {
    795     switch (db_ref->local_entry->base.board_id) {
    796     case cpudb_board_id_cfm_xgs2:
    797     case cpudb_board_id_cfm_xgs3:
    798     case cpudb_board_id_lm_xgs2_6x:
    799     case cpudb_board_id_lm_xgs2_48g:
    800     case cpudb_board_id_lm_xgs3_12x:
    801     case cpudb_board_id_lm_xgs3_48g:
    802     case cpudb_board_id_lm_56800_12x:
    803 #if defined(INCLUDE_CHASSIS)
    804 
    805 #define CHASSIS_C2C_SRC_MODID     31    /* Src mod ID for c2c */
    806 
    807         db_ref->local_entry->base.flags |= CPUDB_BASE_F_CHASSIS;
    808         if (db_ref->local_entry->base.board_id == cpudb_board_id_cfm_xgs3) {
    809             db_ref->local_entry->base.flags |= CPUDB_BASE_F_CHASSIS_10SLOT;
    810         }
    811         if (CHASSIS_SM(db_ref->local_entry->base.flags)) {
    812             /* Set c2c src modid for for SM load balancing only */
    813             c2c_src_mod_set(CHASSIS_C2C_SRC_MODID);
    814             /* Use special modid assignment */
    815             topology_mod_id_assign_function(topo_chassis_modids_assign);
    816         }
    817         /* Don't touch discovery minimum TTL; set maximum TTL to 2 */
    818         disc_ttl_max_set(2);
    819         bcm_board_chassis_setup(db_ref);
    820 #endif
    821         break;
    822     case cpudb_board_id_sdk_xgs3_48f:
    823     case cpudb_board_id_sdk_xgs3_48g:
    824     case cpudb_board_id_sdk_xgs3_24g:
    825     case cpudb_board_id_sdk_xgs3_20x:
    826     case cpudb_board_id_sdk_xgs3_48g5g:
    827     case cpudb_board_id_sdk_xgs3_48g2:
    828         disc_ttl_max_set(2);
    829         break;
    830     default:
    831         break;
    832     }
    833 
    834     return BCM_E_NONE;
    835 }
    836 
    837 int
    838 topo_board_initialize(cpudb_ref_t db_ref)
    839 {
    840     BCM_IF_ERROR_RETURN(topo_board_op(topoBoardInit, db_ref, NULL));
    841     
    842     /* Set stack ports linkscan mode */
    843     _topo_linkscan_mode_set(db_ref->local_entry);
    844 
    845     _topo_set_modid_parameters(db_ref->local_entry);
    846 
    847     return BCM_E_NONE;
    848 }
    849 
    850 
    851 /*
    852  * Rapid Recovery
    853  */
    854 
    855 /*
    856  * Function:
    857  *     topo_sp_idx_find
    858  * Purpose:
    859  *     Looks up given stack unit/port in local topology stack information
    860  *     and returns database index.
    861  * Parameters:
    862  *     unit  - Unit of stack port
    863  *     port  - Stack port
    864  * Returns:
    865  *     >= 0  Stack port index
    866  *     < 0   Cannot find given stack port in local topology information
    867  * Note:
    868  *     Caller must apply LOCK.
    869  */
    870 
    871 STATIC int
    872 topo_sp_idx_find(int unit, bcm_port_t port)
    873 {
    874     int i;
    875 
    876     for (i = 0; i < COUNTOF(topo_sp); i++) {
    877         if ((topo_sp[i].unit == unit) && (topo_sp[i].port == port)) {
    878             return i;
    879         }
    880     }
    881 
    882     return BCM_E_NOT_FOUND;
    883 }
    884 
    885 
    886 /*
    887  * Function:
    888  *     topo_sp_init
    889  * Purpose:
    890  *     Initializes topology stack port information and
    891  *     set HW linkscan mode for stack ports on devices with
    892  *     stack port trunk failover support.
    893  * Parameters:
    894  *     entry  - Local CPUDB entry
    895  * Returns:
    896  *     BCM_E_NONE  - Success
    897  *     BCM_E_XXX   - Number of given stack ports is larger than local db
    898  */
    899 
    900 STATIC int
    901 topo_sp_init(cpudb_entry_t *entry)
    902 {
    903     int  i;
    904     int  topo_sp_max = COUNTOF(topo_sp);
    905         
    906         
    907     TOPO_DATA_LOCK;
    908     for (i = 0; i < topo_sp_max; i++) {
    909         topo_sp[i].unit = -1;
    910         topo_sp[i].port = -1;
    911         topo_sp[i].tid  = -1;
    912     }
    913     
    914     if (entry->base.num_stk_ports > topo_sp_max) {
    915         TOPO_DATA_UNLOCK;
    916         return BCM_E_FAIL;
    917     }
    918 
    919     /* Using CPUDB same stack port index */
    920     for (i = 0; i < entry->base.num_stk_ports; i++) {
    921         topo_sp[i].unit = entry->base.stk_ports[i].unit;
    922         topo_sp[i].port = entry->base.stk_ports[i].port;
    923     }
    924 
    925     TOPO_DATA_UNLOCK;
    926     return BCM_E_NONE;
    927 }
    928 
    929 
    930 /*
    931  * Function:
    932  *     topo_sp_info_update
    933  * Purpose:
    934  *     Updates the topology stack port database with the system's
    935  *     current configuration.  Currently, only trunk information
    936  *     is being stored.
    937  * Parameters:
    938  *     entry  - Local CPUDB entry
    939  * Returns:
    940  *     BCM_E_NONE  - Success
    941  *     BCM_E_XXX   - Error obtaining system's information
    942  */
    943 
    944 STATIC int
    945 topo_sp_info_update(cpudb_entry_t *entry)
    946 {
    947     int           i;
    948     int           unit;
    949     bcm_port_t    port;
    950     bcm_trunk_t   tid;
    951     bcm_module_t  modid = -1;
    952     int           topo_sp_max = COUNTOF(topo_sp);
    953     
    954     /* Load information from current system configuration */
    955     TOPO_DATA_LOCK;
    956     for (i = 0; i < topo_sp_max; i++) {
    957         unit = topo_sp[i].unit;
    958         port = topo_sp[i].port;
    959         
    960         if ((unit < 0) || (port < 0)) {
    961             continue;
    962         }
    963         
    964         if (BCM_SUCCESS(bcm_trunk_find(unit, modid,
    965                                        BCM_GPORT_DEVPORT(unit, port), &tid))) {
    966             topo_sp[i].tid = tid;
    967         } else {
    968             topo_sp[i].tid = -1;
    969         }
    970         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    971                     (BSL_META_U(unit,
    972                     "Stack port %d: unit %d, port %d, tid %d\n"),
    973                      i, unit, port, topo_sp[i].tid));
    974     }
    975     TOPO_DATA_UNLOCK;
    976 
    977     return BCM_E_NONE;
    978 }
    979 
    980 
    981 /*
    982  * Function:
    983  *     topo_sp_tx_cpu_replace
    984  * Purpose:
    985  *     Replaces the TX stack port if the failed port is used to
    986  *     reach a destination in the system.  The routine will:
    987  *         - replace port in CPU database,
    988  *         - update C2C database,
    989  *         - clear any pending transaction to destination CPU
    990  * Parameters:
    991  *     db_ref    - (IN/OUT) Current CPU database
    992  *     unit      - Unit of failed stack port
    993  *     fail_sp   - Failed stack port to replace
    994  *     repl_sp   - Replacing stack port
    995  * Returns:
    996  *     N/A
    997  * NOTE:
    998  *     Assumes that the failed and replacing ports are in the same unit.
    999  */
   1000 
   1001 STATIC void
   1002 topo_sp_tx_cpu_replace(cpudb_ref_t db_ref, int unit,
   1003                        bcm_port_t fail_sp, bcm_port_t repl_sp)
   1004 {
   1005     cpudb_entry_t  *entry;
   1006 
   1007     CPUDB_FOREACH_ENTRY(db_ref, entry) {
   1008         if (entry == db_ref->local_entry) {
   1009             continue;
   1010         }
   1011         /* Replace if failed port is being used */
   1012         if ((entry->tx_unit == unit) && (entry->tx_port == fail_sp)) {
   1013             entry->tx_port = repl_sp;
   1014             topo_cpu_trans_purge(entry);
   1015             c2c_cpu_add(entry);
   1016             /*
   1017              * Inform ATP that the db entry has been modified
   1018              * so that in-flight tx pkts are handled gracefully
   1019              */
   1020             atp_db_update_notify();
   1021         }
   1022     }
   1023     
   1024 }
   1025 
   1026 
   1027 /*
   1028  * Function:
   1029  *     topo_board_rapid_recovery
   1030  * Purpose:
   1031  *     Performs a rapid recovery of the local system for
   1032  *     the following defined cases:
   1033  *
   1034  *     a) Trunk link failure 
   1035  *        When the failure occurs on a stack port that is a member
   1036  *        of a trunk.  Trunk should contain at least one more additional
   1037  *        stack port.
   1038  *
   1039  *     Any other cases not defined above will return failure.
   1040  *
   1041  * Parameters:
   1042  *     db_ref    - (IN/OUT) Current CPU database
   1043  *     unit      - Unit of failed stack port
   1044  *     fail_sp   - Failed stack port
   1045  * Returns:
   1046  *     BCM_E_NONE  - Success
   1047  *     BCM_E_XXX   - System cannot perform rapid recovery
   1048  */
   1049 
   1050 int
   1051 topo_board_rapid_recovery(cpudb_ref_t db_ref, int unit, bcm_port_t fail_sp)
   1052 {
   1053     topo_cpu_t   *topo_cpu;    /* Current topology */
   1054     int          fail_sp_idx;  /* Failed stack port index */
   1055     bcm_trunk_t  tid;          /* Trunk ID of failed stack port */
   1056     int          rv = BCM_E_UNAVAIL;
   1057 
   1058     TOPO_DATA_LOCK;
   1059 
   1060     /* Get current topology */
   1061     bcm_board_topo_get(&topo_cpu);
   1062     if (topo_cpu == NULL) {
   1063         TOPO_DATA_UNLOCK;
   1064         LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1065                   (BSL_META_U(unit,
   1066                   "Current topology is null\n")));
   1067         return BCM_E_FAIL;
   1068     } 
   1069 
   1070     
   1071     /*
   1072      * Trunk Link Failure
   1073      *
   1074      * Note:  The topology database is currently NOT being updated for now.
   1075      *        The original information created by topology after
   1076      *        a full discovery cycle remains unchanged.
   1077      */
   1078     if ((fail_sp_idx = topo_sp_idx_find(unit, fail_sp)) < 0) {
   1079         TOPO_DATA_UNLOCK;
   1080         LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1081                   (BSL_META_U(unit,
   1082                   "Stack port unit %d, port %d, not found\n"),
   1083                    unit, fail_sp));
   1084         return BCM_E_NOT_FOUND;
   1085     }
   1086 
   1087     tid = TOPO_SP_TID_GET(fail_sp_idx);
   1088     if (tid >= 0) {
   1089         int                   i, j;
   1090         bcm_trunk_info_t      t_info;
   1091         bcm_port_t            repl_sp;  /* Replacing port */
   1092         int                   member_count;
   1093         bcm_trunk_member_t    member_array[BCM_TRUNK_FABRIC_MAX_PORTCNT];
   1094         bcm_port_t            local_port;
   1095         
   1096         /* Get current trunk information and remove failed port */
   1097         rv = bcm_trunk_get(unit, tid, &t_info, BCM_TRUNK_FABRIC_MAX_PORTCNT,
   1098                            member_array, &member_count);
   1099         if (BCM_FAILURE(rv)) {
   1100             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1101                       (BSL_META_U(unit,
   1102                       "Cannot get trunk information for unit %d, tid %d\n"),
   1103                        unit, tid));
   1104             TOPO_DATA_UNLOCK;
   1105             return rv;
   1106         }
   1107         
   1108         /* Setup new trunk information with failed port removed */
   1109         j = 0;
   1110         for (i = 0; i < member_count; i++) {
   1111             BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, member_array[i].gport,
   1112                                                    &local_port));
   1113             if (local_port == fail_sp) {
   1114                 continue;
   1115             }
   1116             sal_memcpy(&member_array[j], &member_array[i],
   1117                        sizeof(bcm_trunk_member_t));
   1118             j++;
   1119         }
   1120         member_count = j;
   1121         
   1122         /* Trunk failover requires at least one port left in trunk */
   1123         if (member_count < 1) {
   1124             LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1125                         (BSL_META_U(unit,
   1126                         "Link failure on unit %d, port %d, is not a "
   1127                          "trunk failover case\n"), unit, fail_sp));
   1128             TOPO_DATA_UNLOCK;
   1129             return BCM_E_FAIL;
   1130         }
   1131 
   1132         /* Get first active port of trunk to replace failed port */
   1133         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, member_array[0].gport,
   1134                                                &repl_sp));
   1135 
   1136         /* Board programming */
   1137         rv = bcm_board_topo_trunk_failover(topo_cpu, unit,
   1138                                            fail_sp, fail_sp_idx,
   1139                                            tid, &t_info, member_count,
   1140                                            member_array);
   1141         if (BCM_FAILURE(rv)) {
   1142             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1143                       (BSL_META_U(unit,
   1144                       "Fail to reconfigure system for trunk failover\n")));
   1145             TOPO_DATA_UNLOCK;
   1146             return BCM_E_FAIL;
   1147         }
   1148 
   1149         /* Update CPU transport */
   1150         topo_sp_tx_cpu_replace(db_ref, unit, fail_sp, repl_sp);
   1151 
   1152         /* Update internal information */
   1153         TOPO_SP_TID_SET(fail_sp_idx, -1);
   1154     } 
   1155 
   1156     TOPO_DATA_UNLOCK;
   1157 
   1158     return rv;
   1159 }
   1160 
   1161 /* Registration interface */
   1162 int
   1163 topo_board_default_board_program(topo_board_control_t op,
   1164                                  cpudb_ref_t db_ref,
   1165                                  topo_cpu_t *topo_cpu,
   1166                                  void *user_data)
   1167 {
   1168     int rv = BCM_E_PARAM;
   1169 
   1170     switch (op) {
   1171     case topoBoardInit:
   1172         rv = _topo_board_default_initialize(db_ref);
   1173         break;
   1174 
   1175     case topoBoardProgram:
   1176         rv = _topo_board_default_program(db_ref, topo_cpu);
   1177         break;
   1178 
   1179     default:
   1180         break;
   1181         
   1182     }
   1183 
   1184     return rv;
   1185 }
   1186 
   1187 
   1188 
   1189 static topo_board_callback_t _topo_board_handler;
   1190 static void *_topo_board_user_data;
   1191 
   1192 int
   1193 topo_board_register(topo_board_callback_t func, void *user_data)
   1194 {
   1195     _topo_board_handler = func;
   1196     _topo_board_user_data = user_data;
   1197 
   1198     return BCM_E_NONE;
   1199 }
   1200 
   1201 int
   1202 topo_board_unregister(topo_board_callback_t func, void *user_data)
   1203 {
   1204     int rv = BCM_E_NOT_FOUND;
   1205 
   1206     if (_topo_board_handler == func && _topo_board_user_data == user_data) {
   1207         _topo_board_handler = NULL;
   1208         _topo_board_user_data = NULL;
   1209         rv = BCM_E_NONE;
   1210     }
   1211 
   1212     return rv;
   1213 }
   1214 
   1215 int
   1216 topo_board_op(topo_board_control_t op,
   1217               cpudb_ref_t db_ref, topo_cpu_t *topo_cpu)
   1218 {
   1219     return _topo_board_handler ?
   1220         _topo_board_handler(op, db_ref, topo_cpu, _topo_board_user_data)
   1221         : BCM_E_UNAVAIL;
   1222 }