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

program.c (30452B)


      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:        program.c
      8  * Purpose:     board stack programming
      9  *
     10  *
     11  *
     12  * Methodology:
     13  *
     14  *   Create a connection list formed by the merger of the board's
     15  *   internal connections and external topology connections from the
     16  *   CPUDB. Create a tree driver from this connection list and the
     17  *   stack vertex and edge programming functions, and use
     18  *   board_tree_connect to program all the devices.
     19  *
     20  *   The edge programmer programs any trunks found on local units, and
     21  *   adds the edge port to the "allowable" stack ports (by default,
     22  *   all stack ports are disallowed).
     23  *
     24  *   The vertex programmer programs the modport map, modid enable, and
     25  *   topology map for every local unit.
     26  *
     27  *   Note that the unit numbers for remote units are synthesized, and
     28  *   must not correspond to any local unit number. bcm_unit_local()
     29  *   must return FALSE for any such remote unit, and the unit number
     30  *   used must be usable by BCM_GPORT_DEVPORT. These remote units
     31  *   numbers are not used as a unit argument except to bcm_unit_local,
     32  *   and are are relatively small, as they are used as an index into
     33  *   board_stk_pgm_t.stk_unit. This requirement is driven by the use
     34  *   of a connection list to drive the tree programming.
     35  *
     36  *   The topology map used for bcm_topo_port_get() is a two
     37  *   dimensional array constructed at stack programming time, and so
     38  *   is common for all systems.
     39  *
     40  *   The methodology used here is a significant departure from that
     41  *   used by stktask, and is more data driven, rather than using hand
     42  *   coded interfaces for each board.
     43  *
     44  *   Note that this subsystem, unlike stktask board programming,
     45  *   expects that internal stack connections between local devices
     46  *   have already been registered with BCM stack during local board
     47  *   initialization, so stack programming only has to deal with modid
     48  *   mapping on internal ports.
     49  */
     50 
     51 #include <shared/bsl.h>
     52 
     53 #include <bcm/error.h>
     54 #include <bcm/port.h>
     55 #include <bcm/stack.h>
     56 #include <bcm/trunk.h>
     57 #include <bcm/topo.h>
     58 #if defined(INCLUDE_LIB_CPUDB)
     59 #include <appl/cpudb/cpudb.h>
     60 #endif
     61 #if defined(INCLUDE_LIB_STKTASK)
     62 #include <appl/stktask/topology.h>
     63 #include <appl/stktask/stktask.h>       /* bcm_st_reserved_modid_enable_get */
     64 #include <appl/cputrans/nh_tx.h>        /* nh_tx_src_get */
     65 #endif
     66 
     67 
     68 #include <board/manager.h>
     69 #include <board/stack.h>
     70 #include <board_int/support.h>
     71 
     72 #if defined(INCLUDE_LIB_CPUDB) && defined(INCLUDE_LIB_STKTASK)
     73 
     74 
     75 #ifndef BOARD_PGM_ABS_MAX_MODID
     76 #define BOARD_PGM_ABS_MAX_MODID 32
     77 #endif
     78 
     79 typedef struct board_stk_unit_s {
     80     int local;                  /* true if a local unit */
     81     int unit_id;                /* unit identifier */
     82     int modid;                  /* modid for this unit */
     83     bcm_pbmp_t disports;        /* inactive stack ports (ext only) */
     84     bcm_pbmp_t enaports;        /* active stack ports (int+ext) */
     85     bcm_pbmp_t ethports;        /* non-stack ports (ext only) */
     86 } board_stk_unit_t;
     87 
     88 /* The topology map is a dynamically allocated 2D array indexed by
     89    unit number and modid containing a port */
     90 
     91 typedef bcm_port_t board_topo_unit[BOARD_PGM_ABS_MAX_MODID];
     92 
     93 
     94 /* Persistant data used by the stacking board programming subsystem */
     95 typedef struct board_stk_info_s {
     96     int topo_valid;             /* true if this entry is valid */
     97     int max_modid;              /* maximum modid in the maps */
     98     int topo_units;             /* number of topo_map entries */
     99     board_topo_unit *topo_map;  /* topo map */
    100 } board_stk_info_t;
    101 
    102 /* Transient programming data */
    103 typedef struct board_stk_pgm_s {
    104     void *alloc;                /* memory buffer */
    105     int unit_last;              /* last unit_id + 1 */
    106     board_stk_unit_t *stk_unit; /* per unit data */
    107     int *ru_offset;             /* indexed by topo_idx */
    108     int max_conn;               /* maximum number of connections managed */
    109     board_tree_driver_t td;     /* tree driver used */
    110 } board_stk_pgm_t;
    111 
    112 STATIC board_stk_info_t _board_stack_info;
    113 
    114 /*
    115  * Function:
    116  *     _board_topo_port_get
    117  * Purpose:
    118  *     Topology mapping function registered with BCM
    119  * Parameters:
    120  *     unit       - (IN)  unit to map
    121  *     dest_modid - (IN)  modid to map
    122  *     exit_port  - (OUT) physical port (unit,modid) egresses
    123  * Returns:
    124  *     BCM_E_NONE - success
    125  *     BCM_E_XXX - failed
    126  */
    127 STATIC int
    128 _board_topo_port_get(int unit, int dest_modid, bcm_port_t *exit_port)
    129 {
    130     int rv = BCM_E_PARAM;
    131 
    132     if (_board_stack_info.topo_map == NULL ||!_board_stack_info.topo_valid) {
    133         rv = BCM_E_UNAVAIL;
    134     } else if (unit >= 0 && unit < _board_stack_info.topo_units &&
    135                dest_modid >= 0 &&  dest_modid < _board_stack_info.max_modid) {
    136         *exit_port = _board_stack_info.topo_map[unit][dest_modid];
    137         rv = BCM_E_NONE;
    138     }
    139 
    140     return rv;
    141 }
    142 
    143 /*
    144  * Function:
    145  *     _board_program_info
    146  * Purpose:
    147  *     Tree Programmer 'info' function.
    148  *     Sets modid/trunk info for given unit
    149  * Parameters:
    150  *     unit      - (IN)  current unit (see notes above)
    151  *     modid     - (OUT) modid for this unit
    152  *     ti        - (OUT) trunk info for this unit (local only)
    153  *     user_data - (IN)  user data registered
    154  * Returns:
    155  *     BCM_E_NONE - success
    156  *     BCM_E_XXX - failed
    157  */
    158 STATIC int
    159 _board_program_info(int unit, int *modid, bcm_trunk_chip_info_t *ti,
    160                     void *user_data)
    161 {
    162     board_stk_pgm_t *pgm = (board_stk_pgm_t *)user_data;
    163     int rv = BCM_E_NONE;
    164 
    165     /* Copy modid out. Modid assignments have already been made
    166        available via the CPUDB, so this function just copies out the
    167        result of that analysis. */
    168     *modid = pgm->stk_unit[unit].modid;
    169     if (pgm->stk_unit[unit].local) {
    170         /* get chip info for local units */
    171         rv = bcm_trunk_chip_info_get(unit, ti);
    172     }
    173 
    174     return rv;
    175 }
    176 
    177 /*
    178  * Function:
    179  *     _board_program_edge
    180  * Purpose:
    181  *     Tree Programmer 'edge' function.
    182  *     Program trunk if needed and add port to 'enable' list.
    183  * Parameters:
    184  *     unit      - (IN)  current unit (see notes above)
    185  *     ti        - (IN)  trunk info for this unit (local only)
    186  *     ta        - (OUT) trunk add info for this unit (local only)
    187  *     user_data - (IN)  user data registered
    188  * Returns:
    189  *     BCM_E_NONE - success
    190  *     BCM_E_XXX - failed
    191  */
    192 STATIC int
    193 _board_program_edge(int unit, bcm_trunk_chip_info_t *ti,
    194                     bcm_trunk_add_info_t *ta, void *user_data)
    195 {
    196     /* program trunk if needed */
    197     int i, rv, tid;
    198     bcm_trunk_member_t *member_array = NULL;
    199     bcm_trunk_info_t tinfo;
    200 
    201     rv = BCM_E_INTERNAL;
    202 
    203     if (ta->num_ports > 1) {
    204         tid = ti->trunk_fabric_id_max;
    205         if (tid >= ti->trunk_fabric_id_min) {
    206             ta->psc = -1; /* Default hash algorithm */
    207             ta->dlf_index = -1;
    208             ta->mc_index = -1;
    209             ta->ipmc_index = -1;
    210             rv = bcm_trunk_create(unit, BCM_TRUNK_FLAG_WITH_ID, &tid);
    211             if (BCM_SUCCESS(rv)) {
    212                 LOG_VERBOSE(BSL_LS_BOARD_COMMON,
    213                             (BSL_META_U(unit,
    214                             "Board trunk u:%d tid:%d\n"),
    215                              unit, tid));
    216                 member_array = sal_alloc(ta->num_ports *
    217                         sizeof(bcm_trunk_member_t), "trunk member array");
    218                 if (NULL == member_array) {
    219                     return BCM_E_MEMORY;
    220                 }
    221                 for (i = 0; i < ta->num_ports; i++) {
    222                     bcm_trunk_member_t_init(&member_array[i]);
    223                     if (BCM_GPORT_IS_SET(ta->tp[i])) {
    224                         member_array[i].gport = ta->tp[i];
    225                     } else {
    226                         BCM_GPORT_LOCAL_SET(member_array[i].gport, ta->tp[i]);
    227                     } 
    228                 }
    229                 bcm_trunk_info_t_init(&tinfo);
    230                 tinfo.flags = ta->flags;
    231                 tinfo.psc = ta->psc;
    232                 tinfo.dlf_index = ta->dlf_index;
    233                 tinfo.mc_index = ta->mc_index;
    234                 tinfo.ipmc_index = ta->ipmc_index;
    235                 rv = bcm_trunk_set(unit, tid, &tinfo, ta->num_ports,
    236                         member_array);
    237                 if (BCM_SUCCESS(rv)) {
    238                     ti->trunk_fabric_id_max--;
    239                 }
    240                 sal_free(member_array);
    241             }
    242         } else {
    243             /* No more trunks */
    244             rv = BCM_E_FULL;
    245         }
    246     }
    247 
    248     if (BCM_SUCCESS(rv)) {
    249         for (i=0; i<ta->num_ports; i++) {
    250             /* Add port to enable list */
    251             
    252             rv = BCM_E_UNAVAIL;
    253         }
    254     }
    255 
    256     return rv;
    257 
    258 }
    259 
    260 /*
    261  * Function:
    262  *     _board_program_vertex
    263  * Purpose:
    264  *     Tree Programmer 'vertex' function.
    265  *     Programs device modmap, modid enables, and topo map.
    266  * Parameters:
    267  *     unit      - (IN) current unit (see notes above)
    268  *     modid     - (IN) modid for this unit
    269  *     port      - (IN) egressing stack port on this vertex
    270  *     user_data - (IN) user data registered
    271  * Returns:
    272  *     BCM_E_NONE - success
    273  *     BCM_E_XXX - failed
    274  */
    275 
    276 STATIC int
    277 _board_program_vertex(int unit, int modid, bcm_gport_t port, void *user_data)
    278 {
    279     board_stk_pgm_t *pgm = (board_stk_pgm_t *)user_data;
    280     int rv;
    281 
    282     rv = BCM_E_NONE;
    283 
    284     do {
    285         if (!pgm->stk_unit[unit].local) {
    286             break;
    287         }
    288 
    289         if (modid < 0) {
    290             break;
    291         }
    292 
    293         /* modmap modid to port */
    294         LOG_VERBOSE(BSL_LS_BOARD_COMMON,
    295                     (BSL_META_U(unit,
    296                     "Board modmap mod=%d u:%d,%d\n"),
    297                      modid, unit, port));
    298         rv = bcm_stk_modport_set(unit, modid, port);
    299         if (BCM_FAILURE(rv)) {
    300             break;
    301         }
    302 
    303         /* allow modid on port */
    304         rv = bcm_port_modid_enable_set(unit, port, modid, TRUE);
    305         if (BCM_FAILURE(rv)) {
    306             break;
    307         }
    308 
    309         /* add port to topo map */
    310         _board_stack_info.topo_map[unit][modid] =
    311             BCM_GPORT_DEVPORT_PORT_GET(port);
    312 
    313     } while (0);
    314 
    315     return rv;
    316 }
    317 
    318 /*
    319  * Function:
    320  *     _stk_gport
    321  * Purpose:
    322  *     Return a DEVPORT for the given stack port.
    323  * Parameters:
    324  *      sp     - (IN) CPUDB stack port structure
    325  *      offset - (IN) synthetic remote unit offset
    326  * Returns:
    327  *      DEVPORT
    328  * Notes:
    329  *     Units for local stack ports are real, and units for remote
    330  *     stack ports are synthetic, and must not correspond to any local
    331  *     unit number. They could land up corresponding to a currently
    332  *     attached remote unit number, but that's OK, because they should
    333  *     never be used as an argument to a BCM API call.
    334  */
    335 STATIC bcm_gport_t
    336 _stk_gport(cpudb_unit_port_t *sp, int offset)
    337 {
    338     return BCM_GPORT_DEVPORT(sp->unit + offset, sp->port);
    339 }
    340 
    341 /*
    342  * Function:
    343  *     _topo_port
    344  * Purpose:
    345  *     Determine the egressing port for packets from src to dst.
    346  * Parameters:
    347  *     db_ref    - (IN)  CPUDB
    348  *     src       - (IN)  source entry
    349  *     dst       - (IN)  destination entry
    350  *     ru_offset - (IN)  remote unit offset array
    351  *     devport   - (OUT) egress port
    352  * Returns:
    353  *     BCM_E_NONE - success
    354  *     BCM_E_XXX - failed
    355  * Notes:
    356  *     This is a deliberate alternative to using the TOPO_CPU construction.
    357  *     This will reduce the coupling to stktask, and eliminate such coupling
    358  *     if and when topology is moved to a separate module.
    359  */
    360 STATIC int
    361 _topo_port(cpudb_ref_t db_ref,
    362            cpudb_entry_t *src,
    363            cpudb_entry_t *dst,
    364            int *ru_offset,
    365            bcm_gport_t *devport)
    366 {
    367     int idx;
    368     int rv;
    369     int offset;
    370 
    371     offset = ru_offset[src->topo_idx];
    372     rv = topo_tx_port_get(db_ref, src, dst, &idx);
    373     if (BCM_SUCCESS(rv)) {
    374         *devport = _stk_gport(&src->base.stk_ports[idx], offset);
    375     }
    376     return rv;
    377 }
    378 
    379 
    380 /*
    381  * Function:
    382  *     _cpudb_connection
    383  * Purpose:
    384  *     Returns a connection list from the CPUDB suitable for the
    385  *     stack Tree programmer.
    386  * Parameters:
    387  *      db_ref         - (IN)  CPUDB
    388  *      max_connection - (IN)  max size of connection list
    389  *      list           - (OUT) connection list
    390  *      actual         - (OUT) actual size of list
    391  *      ru_offset      - (IN)  ru_offset array
    392  * Returns:
    393  *     BCM_E_NONE - success
    394  *     BCM_E_XXX - failed
    395  */
    396 STATIC int
    397 _cpudb_connection(cpudb_ref_t db_ref,
    398                   int max_connection,
    399                   board_connection_t *list,
    400                   int *actual,
    401                   int *ru_offset)
    402 {
    403     cpudb_entry_t *src, *dst;
    404     board_connection_t conn;
    405     int count = 0;
    406 
    407     /* This gets *all* the connections in the topology, not just
    408        the connections from the local system.
    409     */
    410     CPUDB_FOREACH_ENTRY(db_ref, src) {
    411         CPUDB_FOREACH_ENTRY(db_ref, dst) {
    412             if (src == dst) {
    413                 /* only iterate across the lower triangle of the matrix
    414                    below the diagonal. */
    415                 break;
    416             }
    417             BCM_IF_ERROR_RETURN
    418                 (_topo_port(db_ref, src, dst, ru_offset, &conn.from));
    419             BCM_IF_ERROR_RETURN
    420                 (_topo_port(db_ref, dst, src, ru_offset, &conn.to));
    421             if (max_connection > 0) {
    422                 *list++ = conn;
    423                 max_connection--;
    424                 count++;
    425             } else {
    426                 return BCM_E_FULL;
    427             }
    428         }
    429     }
    430 
    431     if (actual) {
    432         *actual = count;
    433     }
    434 
    435     return BCM_E_NONE;
    436 }
    437 
    438 /*
    439  * Function:
    440  *     _pgm_unit_init
    441  * Purpose:
    442  *     Initialize board_stk_pgm_t structure
    443  * Parameters:
    444  *     db_ref  - (IN)  CPUDB
    445  *     pgm     - (OUT) board_stk_pgm_t to initialize
    446  * Returns:
    447  *     BCM_E_NONE - success
    448  *     BCM_E_XXX - failed
    449  */
    450 STATIC int
    451 _pgm_unit_init(cpudb_ref_t db_ref, board_stk_pgm_t *pgm)
    452 {
    453     int offset = board_num_devices();
    454     int i, unit_id_base, local, unit_id_last = 0;
    455     cpudb_entry_t *entry;
    456     board_stk_unit_t *stkunit;
    457 
    458     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    459         /* local or remote */
    460         if (entry == db_ref->local_entry) {
    461             unit_id_base = 0;
    462             local = TRUE;
    463         } else {
    464             unit_id_base = offset;
    465             offset += entry->base.num_units;
    466             local = FALSE;
    467         }
    468         pgm->ru_offset[entry->topo_idx] = unit_id_base;
    469 
    470         for (i=0; i<entry->base.num_units; i++) {
    471             stkunit = &pgm->stk_unit[unit_id_base + i];
    472             stkunit->local = local;
    473             stkunit->unit_id = unit_id_base + i;
    474             stkunit->modid = entry->mod_ids[i];
    475         }
    476         unit_id_last = unit_id_base + entry->base.num_units;
    477     }
    478     pgm->unit_last = unit_id_last;
    479     return BCM_E_NONE;
    480 }
    481 
    482 #define NOT_DISABLED (BCM_STK_ENABLE|CPUDB_SPF_INACTIVE)
    483 
    484 /*
    485  * Function:
    486  *     _bcm_stk_port_change
    487  * Purpose:
    488  *     Change stack port to given flags, avoiding unnecessary callbacks
    489  * Parameters:
    490  *     unit  - (IN) stack port unit
    491  *     port  - (IN) stack port on unit
    492  *     flags - (IN) requested flags
    493  * Returns:
    494  *     BCM_E_NONE - success
    495  *     BCM_E_XXX - failed
    496  */
    497 STATIC int
    498 _bcm_stk_port_change(int unit, int port, uint32 flags)
    499 {
    500     int changing;
    501     uint32 cur_flags;
    502 
    503     /* get the current flags */
    504     BCM_IF_ERROR_RETURN(bcm_stk_port_get(unit, port, &cur_flags));
    505     changing = FALSE;
    506 
    507     /* If the stack port is going to be disabled and is currently
    508     enabled, or the BCM_STK_ENABLE or CPUDB_SPF_INACTIVE flags are
    509     changing state, then set the new stack port state. */
    510 
    511     
    512 
    513     if ((flags == 0 && (cur_flags & BCM_STK_ENABLE)) ||
    514         ((flags & NOT_DISABLED) != (cur_flags & NOT_DISABLED))) {
    515         changing = TRUE;
    516     }
    517 
    518     if (changing) {
    519         BCM_IF_ERROR_RETURN(bcm_stk_port_set(unit, port, flags));
    520     }
    521 
    522     return BCM_E_NONE;
    523 }
    524 
    525 /*
    526  * Function:
    527  *     _pgm_stk_ports
    528  * Purpose:
    529  *     Program stack ports in local CPUDB entry
    530  * Parameters:
    531  *     db_ref - (IN) CPUDB
    532  * Returns:
    533  *     BCM_E_NONE - success
    534  *     BCM_E_XXX - failed
    535  */
    536 STATIC int
    537 _pgm_stk_ports(cpudb_ref_t db_ref)
    538 {
    539     int rv, unit, port, i;
    540     uint32 flags, spflags;
    541     cpudb_entry_t *local;
    542 
    543     local = db_ref->local_entry;
    544     rv = BCM_E_NONE;
    545 
    546     /* TO DO:  Scan for ports that are no longer stack ports
    547      * Foreach local device
    548      *    get current bitmap
    549      *    foreach port in bitmap
    550      *       search local entry stack ports for (unit, port)
    551      *           if found, continue
    552      *           otherwise, call bcm_stk_port_set with flags = 0
    553      */
    554 
    555     /* Update stack ports */
    556     for (i = 0; i < local->base.num_stk_ports; i++) {
    557         unit = local->base.stk_ports[i].unit;
    558         port = local->base.stk_ports[i].port;
    559         flags = BCM_STK_ENABLE;
    560         spflags = local->sp_info[i].flags;
    561 
    562         /* See if port is inactive (explicit, cut or no link) */
    563         if (spflags & CPUDB_SPF_ETHERNET) {
    564             flags = 0; /* Disable - not a stack port anymore */
    565         } else if (spflags & CPUDB_SPF_NO_LINK) {
    566             flags |= BCM_STK_INACTIVE | BCM_STK_NO_LINK;
    567         } else if (spflags & CPUDB_SPF_INACTIVE) {
    568             flags |= BCM_STK_INACTIVE;
    569         } else if (spflags & CPUDB_SPF_CUT_PORT) {
    570             flags |= BCM_STK_CUT;
    571         } else { /* Port is active */
    572             /* Simplex/duplex: Only mark simplex if not duplex and resolved */
    573             if (spflags & CPUDB_SPF_DUPLEX) {
    574                 flags |= BCM_STK_DUPLEX;
    575             } else {
    576                 if ((spflags & CPUDB_SPF_TX_RESOLVED) &&
    577                     (spflags & CPUDB_SPF_RX_RESOLVED)) {
    578                     flags |= BCM_STK_SIMPLEX;
    579                 }
    580             } /* Else, port is not yet resolved */
    581         }
    582 
    583         rv = _bcm_stk_port_change(unit, port, flags);
    584         if (BCM_FAILURE(rv)) {
    585             break;
    586         }
    587     }
    588 
    589     return rv;
    590 }
    591 
    592 /*
    593  * Function:
    594  *     _stk_gport_enable
    595  * Purpose:
    596  *     Adds given stack port to enable list
    597  * Parameters:
    598  *     pgm   - (INOUT) pgm structure
    599  *     gport - (IN)    stack port
    600  * Returns:
    601  *     void
    602  */
    603 STATIC void
    604 _stk_gport_enable(board_stk_pgm_t *pgm, bcm_gport_t gport)
    605 {
    606     int unit;
    607     int port;
    608 
    609     unit = BCM_GPORT_DEVPORT_DEVID_GET(gport);
    610     port = BCM_GPORT_DEVPORT_PORT_GET(gport);
    611 
    612     BCM_PBMP_PORT_ADD(pgm->stk_unit[unit].enaports, port);
    613 }
    614 
    615 /*
    616  * Function:
    617  *     _pgm_internal_sp
    618  * Purpose:
    619  *     Callback function to add connection as internal stack ports.
    620  * Parameters:
    621  *     conn      - (IN)    internal stack port connection
    622  *     user_data - (INOUT) user data from callback registration
    623  * Returns:
    624  *     BCM_E_NONE - success
    625  *     BCM_E_XXX - failed
    626  */
    627 STATIC int
    628 _pgm_internal_sp(board_connection_t *conn, void *user_data)
    629 {
    630     board_stk_pgm_t *pgm = (board_stk_pgm_t *)user_data;
    631 
    632     /* add each port to enable list */
    633     _stk_gport_enable(pgm, conn->from);
    634     _stk_gport_enable(pgm, conn->to);
    635 
    636     return BCM_E_NONE;
    637 }
    638 
    639 /*
    640  * Function:
    641  *     _manage_internal_stack_ports
    642  * Purpose:
    643  *     Add all connection pairs in list as internal stack ports
    644  * Parameters:
    645  *     pgm         - (INOUT) pgm structure
    646  *     count       - (IN)    connection list length
    647  *     connection  - (IN)    connection list
    648  * Returns:
    649  *     BCM_E_NONE - success
    650  *     BCM_E_XXX - failed
    651  */
    652 STATIC int
    653 _manage_internal_stack_ports(board_stk_pgm_t *pgm,
    654                              int count,
    655                              board_connection_t *connection)
    656 {
    657     return
    658         board_connection_connected_stack_port_traverse(count,
    659                                                        connection,
    660                                                        _pgm_internal_sp,
    661                                                        pgm);
    662 }
    663 
    664 /*
    665  * Function:
    666  *     _manage_external_stack_ports
    667  * Purpose:
    668  *     Add CPUDB external ports to appropriate pgm members
    669  * Parameters:
    670  *     pgm     - (INOUT) pgm structure
    671  *     db_ref  - (IN)    CPUDB
    672  * Returns:
    673  *     BCM_E_NONE - success
    674  */
    675 STATIC int
    676 _manage_external_stack_ports(board_stk_pgm_t *pgm, cpudb_ref_t db_ref)
    677 {
    678     cpudb_entry_t *local = db_ref->local_entry;
    679     int unit, port, i;
    680     uint32 spflags;
    681     /* Update stack ports */
    682 
    683     for (i = 0; i < local->base.num_stk_ports; i++) {
    684         unit = local->base.stk_ports[i].unit;
    685         port = local->base.stk_ports[i].port;
    686         spflags = local->sp_info[i].flags;
    687 
    688         if ((spflags & CPUDB_SPF_TX_RESOLVED) &&
    689             (spflags & CPUDB_SPF_RX_RESOLVED)) {
    690             /* rx/tx resolved -> enaports */
    691             BCM_PBMP_PORT_ADD(pgm->stk_unit[unit].enaports, port);
    692         } else if (spflags & CPUDB_SPF_ETHERNET) {
    693             /* not stacking -> ethports */
    694             BCM_PBMP_PORT_ADD(pgm->stk_unit[unit].ethports, port);
    695         } else {
    696             /* otherwise -> disports */
    697             BCM_PBMP_PORT_ADD(pgm->stk_unit[unit].disports, port);
    698         }
    699     }
    700 
    701     return BCM_E_NONE;
    702 }
    703 
    704 /*
    705  * Function:
    706  *     _pgm_conn_init
    707  * Purpose:
    708  *     Initialize pgm structure connection data
    709  * Parameters:
    710  *     pgm    - (INOUT) pgm structure
    711  *     board  - (IN)    board driver
    712  *     db_ref - (IN)    CPUDB
    713  * Returns:
    714  *     BCM_E_NONE - success
    715  *     BCM_E_XXX - failed
    716  */
    717 STATIC int
    718 _pgm_conn_init(board_stk_pgm_t *pgm, board_driver_t *board, cpudb_ref_t db_ref)
    719 {
    720     int rv, added;
    721 
    722     /* Get internal connections */
    723 
    724     rv = BCM_E_FULL;
    725     if (board->num_connection < (pgm->max_conn - pgm->td.num_connection)) {
    726 
    727         /* get the internal connections */
    728         sal_memcpy(pgm->td.connection, board->connection,
    729                    board->num_connection * sizeof(pgm->td.connection[0]));
    730         pgm->td.num_connection += board->num_connection;
    731 
    732         /* manage internal connections */
    733         BCM_IF_ERROR_RETURN
    734             (_manage_internal_stack_ports(pgm,
    735                                           board->num_connection,
    736                                           board->connection));
    737     }
    738 
    739     BCM_IF_ERROR_RETURN(_manage_external_stack_ports(pgm, db_ref));
    740 
    741     BCM_IF_ERROR_RETURN(_pgm_stk_ports(db_ref));
    742 
    743     /* Get external connections, and remote unit offset */
    744     rv = _cpudb_connection(db_ref, pgm->max_conn,
    745                            pgm->td.connection+pgm->td.num_connection,
    746                            &added, pgm->ru_offset);
    747     if (BCM_SUCCESS(rv)) {
    748         pgm->td.num_connection += added;
    749         pgm->td.user_data = pgm;
    750         pgm->td.info = _board_program_info;
    751         pgm->td.edge = _board_program_edge;
    752         pgm->td.vertex = _board_program_vertex;
    753     }
    754 
    755     return rv;
    756 }
    757 
    758 /* byte offset in allocation */
    759 #define OFFSET(m,offset) ((void *)(((uint8 *)(m)) + (int)(offset)))
    760 
    761 /*
    762  * Function:
    763  *     _stk_pgm_init
    764  * Purpose:
    765  *     Initialze pgm structure
    766  * Parameters:
    767  *     pgm    - (OUT) pgm structure
    768  *     board  - (IN)  board driver
    769  *     db_ref - (IN)  CPUDB
    770  * Returns:
    771  *     BCM_E_NONE - success
    772  *     BCM_E_XXX - failed
    773  */
    774 STATIC int
    775 _stk_pgm_init(board_stk_pgm_t *pgm, board_driver_t *board, cpudb_ref_t db_ref)
    776 {
    777     int rv, units, entries, su_size, ru_size, cn_size;
    778     cpudb_entry_t *entry;
    779 
    780     /*
    781       Count how many entries and units are going to be managed.  Units
    782       are in stk_unit by unit id. The unit id is equivalant to the SDK
    783       unit number when the unit is local, and is offset by the number
    784       of units on the board (to force such units to be recognized as
    785       remote) for all remote units, so the total allocation needed is
    786       the total number of units found in the CPUDB plus the number of
    787       units on this board.
    788 
    789     */
    790 
    791     rv = BCM_E_NONE;
    792     units = entries = 0;
    793     CPUDB_FOREACH_ENTRY(db_ref, entry) {
    794         entries++;
    795         units += entry->base.num_units;
    796     }
    797     units += board_num_devices();
    798 
    799     /* init struct */
    800     sal_memset(pgm, 0, sizeof(*pgm));
    801 
    802     /* allocate and init stk_unit */
    803     su_size = units * sizeof(pgm->stk_unit[0]);
    804     ru_size = entries * sizeof(pgm->ru_offset[0]);
    805 
    806     /* cn_size, at most, is the number of internal connections plus
    807        the square of the number of CPUDB entries divided by two, as
    808        there is only one topological connection between any given pair
    809        of systems.
    810     */
    811     pgm->max_conn = board->num_connection + (entries*entries/2);
    812     pgm->td.num_connection = 0;
    813     cn_size = pgm->max_conn * sizeof(pgm->td.connection[0]);
    814     pgm->alloc = ALLOC(su_size + ru_size + cn_size);
    815     if (pgm->alloc != NULL) {
    816         sal_memset(pgm->alloc, 0, su_size + ru_size + cn_size);
    817         pgm->stk_unit = OFFSET(pgm->alloc, 0);
    818         pgm->ru_offset = OFFSET(pgm->alloc, su_size);
    819         pgm->td.connection = OFFSET(pgm->alloc, su_size + ru_size);
    820         rv = _pgm_unit_init(db_ref, pgm);
    821         if (BCM_SUCCESS(rv)) {
    822             rv = _pgm_conn_init(pgm, board, db_ref);
    823         }
    824     }
    825 
    826     if (BCM_FAILURE(rv) && pgm->alloc != NULL) {
    827         FREE(pgm->alloc);
    828     }
    829 
    830     return rv;
    831 }
    832 
    833 /*
    834  * Function:
    835  *     _nh_reserved_modid_set
    836  * Purpose:
    837  *     Gets the reserved Next Hop modid for the given unit and port
    838  * Parameters:
    839  *     unit - (IN) stack unit
    840  *     port - (IN) port on stack unit
    841  * Returns:
    842  *     BCM_E_NONE - success
    843  *     BCM_E_XXX - failed
    844  */
    845 STATIC int
    846 _nh_reserved_modid_set(int unit, bcm_port_t port)
    847 {
    848     int nhmod = -1;
    849     int nhport;
    850     int rv = BCM_E_NONE;
    851 
    852     if (bcm_st_reserved_modid_enable_get()) {
    853         rv = nh_tx_src_mod_port_get(unit, port, &nhmod, &nhport);
    854         if (BCM_FAILURE(rv) || nhmod < 0) {
    855             rv = nh_tx_src_get(&nhmod, &nhport);
    856         }
    857         if (BCM_SUCCESS(rv) && nhmod >= 0) {
    858             rv = bcm_port_modid_enable_set(unit, port, nhmod, TRUE);
    859         }
    860     }
    861 
    862     return rv;
    863 }
    864 
    865 /*
    866  * Function:
    867  *     _stk_program_unit
    868  * Purpose:
    869  *     Program the given unit for stacking
    870  * Parameters:
    871  *     stkunit - (IN) programming data
    872  * Returns:
    873  *     BCM_E_NONE - success
    874  *     BCM_E_XXX - failed
    875  */
    876 STATIC int
    877 _stk_program_unit(board_stk_unit_t *stkunit)
    878 {
    879     bcm_port_t from, to;
    880     int rv = BCM_E_NONE;
    881 
    882     BCM_PBMP_ITER(stkunit->disports, from) {
    883         /* block all disports -> enaports */
    884         BCM_PBMP_ITER(stkunit->enaports, to) {
    885             rv = bcm_port_flood_block_set(stkunit->unit_id, from, to, -1U);
    886             if (BCM_FAILURE(rv)) {
    887                 return rv;
    888             }
    889         }
    890         /* block all disports -> disports */
    891         BCM_PBMP_ITER(stkunit->disports, to) {
    892             if (from == to) {
    893                 continue;
    894             }
    895             rv = bcm_port_flood_block_set(stkunit->unit_id, from, to, -1U);
    896             if (BCM_FAILURE(rv)) {
    897                 return rv;
    898             }
    899         }
    900     }
    901 
    902     BCM_PBMP_ITER(stkunit->enaports, from) {
    903         /* allow all enaports -> enaports */
    904         BCM_PBMP_ITER(stkunit->enaports, to) {
    905             rv = bcm_port_flood_block_set(stkunit->unit_id, from, to, 0);
    906             if (BCM_FAILURE(rv)) {
    907                 return rv;
    908             }
    909         }
    910         /* block all enaports -> disports */
    911         BCM_PBMP_ITER(stkunit->enaports, to) {
    912             rv = bcm_port_flood_block_set(stkunit->unit_id, from, to, -1U);
    913             if (BCM_FAILURE(rv)) {
    914                 return rv;
    915             }
    916         }
    917     }
    918 
    919     /* reserved modid enabled on all enaports */
    920     BCM_PBMP_ITER(stkunit->enaports, from) {
    921         rv = _nh_reserved_modid_set(stkunit->unit_id, from);
    922         if (BCM_FAILURE(rv)) {
    923             return rv;
    924         }
    925     }
    926 
    927     /* all modids enabled on all ethports */
    928     BCM_PBMP_ITER(stkunit->ethports, from) {
    929         rv = bcm_port_modid_enable_set(stkunit->unit_id, from, -1, TRUE);
    930         if (BCM_FAILURE(rv)) {
    931             return rv;
    932         }
    933     }
    934 
    935     return rv;
    936 }
    937 
    938 /*
    939  * Function:
    940  *     _stk_pgm_finish
    941  * Purpose:
    942  *     Finish stack programming for all local devices
    943  * Parameters:
    944  *     pgm    - (IN) pgm structure
    945  * Returns:
    946  *     BCM_E_NONE - success
    947  *     BCM_E_XXX - failed
    948  */
    949 STATIC int
    950 _stk_pgm_finish(board_stk_pgm_t *pgm)
    951 {
    952     int i, rv;
    953 
    954     rv = BCM_E_NONE;
    955 
    956     for (i=0; i<pgm->unit_last; i++) {
    957         if (pgm->stk_unit[i].local) {
    958             rv = _stk_program_unit(&pgm->stk_unit[i]);
    959             if (BCM_FAILURE(rv)) {
    960                 break;
    961             }
    962         }
    963     }
    964     return rv;
    965 }
    966 
    967 /*
    968  * Function:
    969  *     _stk_pgm_free
    970  * Purpose:
    971  *     Free allocate pgm data
    972  * Parameters:
    973  *     pgm    - (IN) pgm structure
    974  * Returns:
    975  *     void
    976  */
    977 STATIC void
    978 _stk_pgm_free(board_stk_pgm_t *pgm)
    979 {
    980     FREE(pgm->alloc);
    981 }
    982 
    983 /*
    984  * Function:
    985  *     _stk_topo_map_init
    986  * Purpose:
    987  *     Initializes board topology data
    988  * Parameters:
    989  *     db_ref  - (IN) CPUDB
    990  * Returns:
    991  *     BCM_E_NONE - success
    992  *     BCM_E_XXX - failed
    993  */
    994 STATIC int
    995 _stk_topo_map_init(cpudb_ref_t db_ref)
    996 {
    997     int units;
    998     int rv = BCM_E_FAIL;
    999     void *map;
   1000 
   1001     _board_stack_info.topo_valid = FALSE;
   1002     map = _board_stack_info.topo_map;
   1003     if (map) {
   1004         /* Force current map to be unavailable */
   1005         _board_stack_info.topo_map = NULL;
   1006 
   1007         /* now free the memory */
   1008         FREE(_board_stack_info.topo_map);
   1009     }
   1010 
   1011     /* Allocate topo map */
   1012     units = db_ref->local_entry->base.num_units;
   1013     _board_stack_info.topo_map =
   1014         ALLOC(units * sizeof(_board_stack_info.topo_map[0]));
   1015 
   1016     if (_board_stack_info.topo_map) {
   1017         _board_stack_info.topo_units = db_ref->local_entry->base.num_units;
   1018         rv = BCM_E_NONE;
   1019     } else {
   1020         rv = BCM_E_MEMORY;
   1021     }
   1022 
   1023     return rv;
   1024 }
   1025 
   1026 /*
   1027  * Function:
   1028  *     board_stack_program
   1029  * Purpose:
   1030  *     Program the current board for stacking operation
   1031  * Parameters:
   1032  *     db_ref - (IN) CPUDB
   1033  *     flags  - (IN) flags
   1034  * Returns:
   1035  *     BCM_E_NONE - success
   1036  *     BCM_E_XXX - failed
   1037  */
   1038 int
   1039 board_stack_program(cpudb_ref_t db_ref, uint32 flags)
   1040 {
   1041     board_stk_pgm_t pgm;
   1042     board_driver_t *board;
   1043 
   1044     board = board_driver();
   1045 
   1046     if (board == NULL) {
   1047         return BCM_E_INIT;
   1048     }
   1049 
   1050     /* init pgm struct */
   1051     BCM_IF_ERROR_RETURN(_stk_pgm_init(&pgm, board, db_ref));
   1052 
   1053     /* init topo map */
   1054     BCM_IF_ERROR_RETURN(_stk_topo_map_init(db_ref));
   1055 
   1056     /* run tree programmer */
   1057     BCM_IF_ERROR_RETURN(board_tree_connect(&pgm.td));
   1058 
   1059     /* deal with enabled/disabled ports and flood blocking */
   1060     BCM_IF_ERROR_RETURN(_stk_pgm_finish(&pgm));
   1061 
   1062     /* set topo map, and make available */
   1063     BCM_IF_ERROR_RETURN(bcm_topo_map_set(_board_topo_port_get));
   1064     _board_stack_info.topo_valid = TRUE;
   1065 
   1066     _stk_pgm_free(&pgm);
   1067 
   1068     return BCM_E_NONE;
   1069 }
   1070 
   1071 #endif