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

l2x.c (64810B)


      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  */
      8 
      9 #include <shared/bsl.h>
     10 
     11 #include <sal/core/libc.h>
     12 #include <sal/types.h>
     13 #include <shared/bsl.h>
     14 #include <soc/drv.h>
     15 #include <soc/l2x.h>
     16 #include <soc/ptable.h>
     17 #include <soc/debug.h>
     18 #include <soc/util.h>
     19 #include <soc/mem.h>
     20 #include <soc/iproc.h>
     21 #include <soc/mcm/intr_iproc.h>
     22 #include <soc/tomahawk3.h>
     23 
     24 #if defined(BCM_TOMAHAWK3_SUPPORT)
     25 #ifdef BCM_XGS_SWITCH_SUPPORT
     26 
     27 /* Size of AVL table used for learning entries */
     28 #define _SOC_TH3_L2_LRN_TBL_SIZE 8192
     29 
     30 #define SOC_MEM_COMPARE_RETURN(a, b) {		\
     31         if ((a) < (b)) { return -1; }		\
     32         if ((a) > (b)) { return  1; }		\
     33 }
     34 
     35 typedef struct soc_l2_lrn_avl_info_s {
     36     vlan_id_t       vlan;
     37     soc_module_t    mod;
     38     int             dest_type; /* 0=dest. is port, 1=dest. is trunk */
     39     int             port_tgid; /* Holds port num if dest_type is 0.
     40                                   Holds TGID if dest_type is 1 */
     41     sal_mac_addr_t  mac;
     42     int             in_hw; /* Entry  programmed in h/w. Used to avoid hits due
     43                               to duplicate pkts */
     44 } soc_l2_lrn_avl_info_t, *soc_l2_lrn_avl_info_p;
     45 
     46 static int _soc_th3_l2_bulk_age_iter[SOC_MAX_NUM_DEVICES] = {0};
     47 
     48 static uint8 rev_id = 0;
     49 static uint16 dev_id = 0;
     50 
     51 /*
     52  * Function:
     53  *	soc_th3_l2x_shadow_callback
     54  * Purpose:
     55  *	Internal callback routine for updating an AVL tree shadow table
     56  * Parameters:
     57  *	unit - StrataSwitch unit number.
     58  *	entry_del - Entry to be deleted or updated, NULL if none.
     59  *	entry_add - Entry to be inserted or updated, NULL if none.
     60  *	fn_data - unused.
     61  * Notes:
     62  *	Used only if L2X shadow table is enabled.
     63  */
     64 
     65 STATIC void
     66 soc_th3_l2x_shadow_callback(int unit,
     67                         int flags,
     68                         l2x_entry_t *entry_del,
     69                         l2x_entry_t *entry_add,
     70                         void *fn_data)
     71 {
     72     soc_control_t	*soc = SOC_CONTROL(unit);
     73 
     74     if (flags & (SOC_L2X_ENTRY_DUMMY | SOC_L2X_ENTRY_NO_ACTION |
     75                  SOC_L2X_ENTRY_OVERFLOW)) {
     76         return;
     77     }
     78 
     79     /* Since sync thread (bcmL2X) updates both its own database and learn
     80      * shadow database, we make sure both threads are running, and are synced
     81      * together
     82      */  
     83     if ((soc->l2x_pid != SAL_THREAD_ERROR) &&
     84         (soc->arlShadowMutex != NULL) &&
     85         (soc->arlShadow != NULL) &&
     86         (soc->l2x_learn_pid != SAL_THREAD_ERROR) &&
     87         (soc->l2x_lrn_shadow_mutex != NULL)) {
     88         int rv;
     89 
     90         sal_mutex_take(soc->arlShadowMutex, sal_mutex_FOREVER);
     91 
     92         if (entry_del != NULL) {
     93             rv = shr_avl_delete(soc->arlShadow, soc_l2x_entry_compare_key,
     94                            (shr_avl_datum_t *)entry_del);
     95             if (rv == 0) {
     96                 sal_mac_addr_t mac;
     97                 vlan_id_t vlan;
     98                 int dest;
     99 
    100                 soc_mem_mac_addr_get(unit, L2Xm, entry_del, MAC_ADDRf, mac);
    101                 vlan = soc_mem_field32_get(unit, L2Xm, entry_del, VLAN_IDf);
    102                 dest = soc_mem_field32_get(unit, L2Xm, entry_del, DESTINATIONf);
    103 
    104                 LOG_INFO(BSL_LS_SOC_L2,
    105                          (BSL_META_U(unit,
    106                           "AVL delete: datum not found:\n dest %d, vlan %d,"
    107                           " mac(hex) %02X:%02X:%02X:%02X:%02X:%02X\n"), dest, vlan,
    108                           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
    109             }
    110         }
    111 
    112         if (entry_add != NULL) {
    113             shr_avl_insert(soc->arlShadow, soc_l2x_entry_compare_key,
    114                            (shr_avl_datum_t *)entry_add);
    115         }
    116 
    117         sal_mutex_give(soc->arlShadowMutex);
    118 
    119         /* Update shadow learn table after successful h/w update(s) */
    120         /* Note the order of delete and insert below is important, since for
    121          * station move condition in _soc_th3_learn_cache_entry_process, we
    122          * delete an entry first and then insert it with the new port info
    123          */
    124         sal_mutex_take(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex,
    125                                    sal_mutex_FOREVER);
    126         if (entry_del != NULL) {
    127             soc_th3_lrn_shadow_delete(unit, entry_del);
    128         }
    129 
    130         if (entry_add != NULL) {
    131             soc_th3_lrn_shadow_insert(unit, entry_add);
    132         }
    133         sal_mutex_give(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex);
    134     }
    135 }
    136 
    137 /*
    138  * Function:
    139  *	soc_th3_l2x_detach
    140  * Purpose:
    141  *	Deallocate L2X subsystem resources
    142  * Parameters:
    143  *	unit - StrataSwitch unit number.
    144  * Returns:
    145  * SOC_E_XXX
    146  * Notes:
    147  *  Learn cache interrupt is disabled. Learn cache is cleared, learn cache
    148  *  status bits are cleared	
    149  */
    150 int
    151 soc_th3_l2x_detach(int unit)
    152 {
    153     soc_control_t *soc = SOC_CONTROL(unit);
    154 
    155     
    156 
    157     /* Free reources allocated for shadow table */
    158 
    159     soc_l2x_unregister(unit, soc_th3_l2x_shadow_callback, NULL);
    160 
    161     /* Free cml_freeze structure  */
    162     _soc_l2x_cml_struct_free(unit);
    163 
    164     if (soc->arlShadow != NULL) {
    165         shr_avl_destroy(soc->arlShadow);
    166         soc->arlShadow = NULL;
    167     }
    168 
    169     if (soc->arlShadowMutex != NULL) {
    170         sal_mutex_destroy(soc->arlShadowMutex);
    171         soc->arlShadowMutex = NULL;
    172     }
    173 
    174     
    175 
    176     return SOC_E_NONE;
    177 }
    178 
    179 /*
    180  * Function:
    181  *	soc_th3_l2x_attach
    182  * Purpose:
    183  *	Allocate L2X subsystem resources
    184  * Parameters:
    185  *	unit - StrataSwitch unit number.
    186  * Returns:
    187  *	SOC_E_XXX
    188  * Notes:
    189  *	The L2X tree shadow table is always allocated, since it will be used in
    190  *  learning, aging and table management. Value of spn_L2XMSG_AVL will be
    191  *  ignored
    192  */
    193 
    194 int
    195 soc_th3_l2x_attach(int unit)
    196 {
    197     soc_control_t	*soc = SOC_CONTROL(unit);
    198     int datum_bytes, datum_max;
    199 
    200     (void)soc_th3_l2x_detach(unit);
    201 
    202     datum_bytes = sizeof (l2x_entry_t);
    203     datum_max = soc_mem_index_count(unit, L2Xm);
    204 
    205     if (shr_avl_create(&soc->arlShadow,
    206                        INT_TO_PTR(unit),
    207                        datum_bytes,
    208                        datum_max) < 0) {
    209         return SOC_E_MEMORY;
    210     }
    211 
    212     if ((soc->arlShadowMutex = sal_mutex_create("asMutex")) == NULL) {
    213         (void)soc_l2x_detach(unit);
    214         return SOC_E_MEMORY;
    215     }
    216 
    217     soc_l2x_register(unit, soc_th3_l2x_shadow_callback, NULL);
    218 
    219     /* Reset l2 freeze structure */
    220     soc_th3_l2x_reset_freeze_state(unit);
    221 
    222     /* Allocate cml freeze structure */
    223     SOC_IF_ERROR_RETURN(_soc_l2x_cml_struct_alloc(unit));
    224 
    225     return SOC_E_NONE;
    226 }
    227 
    228 /*
    229  * Function:
    230  *  _soc_th3_l2_age_entries_process
    231  * Purpose:
    232  *  This function is invoked as part of aging mechanism to check for hit bits
    233  *  and take appropriate action (either clear the hit bits, or delete the entry)
    234  *  Since there is no h/w aging support, nor do we have bulk operations block in
    235  *  hardware's L2 implementation, this function reads L2X entries, and takes
    236  *  decision one entry at a time
    237  * Parameters:
    238  *	unit - unit number
    239  * Returns:
    240  *	SOC_E_NONE on success, or other SOC_E_* error code on failure
    241  * Notes:
    242  *  This function will execute much slower than other devices that have
    243  *  hardware support for aging. Entries are processed one at a time
    244  */
    245 STATIC int
    246 _soc_th3_l2_age_entries_process(int unit, l2x_entry_t *l2x_entries)
    247 {
    248     uint32 index_min, index_max, count;
    249     int i;
    250     int rv;
    251 
    252     index_min = soc_mem_index_min(unit, L2Xm);
    253     index_max = soc_mem_index_max(unit, L2Xm);
    254     count = soc_mem_index_count(unit, L2Xm);
    255 
    256     sal_memset((void *)l2x_entries, 0, sizeof(l2x_entry_t) * count);
    257 
    258     /* Read L2 table */
    259     soc_mem_lock(unit, L2Xm);
    260     rv = soc_mem_read_range(unit, L2Xm, MEM_BLOCK_ANY,
    261                             index_min, index_max, l2x_entries);
    262     soc_mem_unlock(unit, L2Xm);
    263 
    264     if (SOC_FAILURE(rv)) {
    265         LOG_ERROR(BSL_LS_SOC_L2,
    266                   (BSL_META_U(unit,
    267                    "%s:DMA read failed: %s\n"), __FUNCTION__, soc_errmsg(rv)));
    268         /* We do not return error, otherwise thread will be killed. If thread
    269          * is alive it can be debugged
    270          */
    271         return SOC_E_NONE;
    272     }
    273 
    274     for (i = index_min; i <= index_max; i++) {
    275         l2x_entry_t *l2x_entry;
    276         uint32 hit_da, hit_sa, local_sa;
    277 
    278         l2x_entry = soc_mem_table_idx_to_pointer(unit, L2Xm, l2x_entry_t*,
    279                                                  l2x_entries, i);
    280 
    281         /* Skip invalid entry */
    282         if (!soc_L2Xm_field32_get(unit, l2x_entry, BASE_VALIDf)) {
    283             continue;
    284         }
    285 
    286         /* Skip static entry */
    287         if (soc_L2Xm_field32_get(unit, l2x_entry, STATIC_BITf)) {
    288             continue;
    289         }
    290 
    291         hit_da = soc_L2Xm_field32_get(unit, l2x_entry, HITDAf);
    292         hit_sa = soc_L2Xm_field32_get(unit, l2x_entry, HITSAf);
    293         local_sa = soc_L2Xm_field32_get(unit, l2x_entry, LOCAL_SAf);
    294 
    295         /* If no hot bits are set, delete the entry */
    296         if (!(hit_da || hit_sa || local_sa)) {
    297             /* Delete entry */
    298             soc_mem_lock(unit, L2Xm);
    299             rv = soc_mem_delete(unit, L2Xm, MEM_BLOCK_ALL,
    300                                 (void *)l2x_entry);
    301             soc_mem_unlock(unit, L2Xm);
    302             if (SOC_FAILURE(rv)) {
    303                 /* If entry is not found, it has been deleted by other source,
    304                  * e.g. address delete from application. So we ignore not found
    305                  * condition here
    306                  */
    307                 if (rv != SOC_E_NOT_FOUND) {
    308                     LOG_WARN(BSL_LS_SOC_L2,
    309                              (BSL_META_U(unit,
    310                              "%s:soc mem delete failed: %s\n"), __FUNCTION__,
    311                              soc_errmsg(rv)));
    312                 }
    313             }
    314         } else {
    315             /* Clear hit bits */
    316             soc_L2Xm_field32_set(unit, l2x_entry, HITDAf, 0);
    317             soc_L2Xm_field32_set(unit, l2x_entry, HITSAf, 0);
    318             soc_L2Xm_field32_set(unit, l2x_entry, LOCAL_SAf, 0);
    319 
    320             soc_mem_lock(unit, L2Xm);
    321             rv = soc_mem_write(unit, L2Xm, MEM_BLOCK_ALL, i, (void *)l2x_entry);
    322             soc_mem_unlock(unit, L2Xm);
    323             if (SOC_FAILURE(rv)) {
    324                 /* We do not return error, otherwise thread will be killed. If
    325                  * thread is alive it can be debugged
    326                  */
    327                 LOG_WARN(BSL_LS_SOC_L2,
    328                          (BSL_META_U(unit,
    329                          "%s:soc mem write failed: %s\n"), __FUNCTION__,
    330                          soc_errmsg(rv)));
    331             }
    332         }
    333 
    334         /* When an entry is deleted, soc_th3_l2x_shadow_callback will call
    335          * learn shadow upadate function. So we don't call learn shadow update
    336          * function here
    337          */
    338     }
    339 
    340     return SOC_E_NONE;
    341 }
    342 
    343 /*
    344  * Function:
    345  * 	_soc_th3_l2_age
    346  * Purpose:
    347  *   	Handler function for L2 entry aging thread
    348  * Parameters:
    349  *	unit - unit number
    350  * Returns:
    351  *	none
    352  */
    353 STATIC void
    354 _soc_th3_l2_age(void *unit_ptr)
    355 {
    356     int unit = PTR_TO_INT(unit_ptr);
    357     int c, m, r, rv, iter = 0;
    358     soc_control_t *soc = SOC_CONTROL(unit);
    359     sal_usecs_t interval;
    360     sal_usecs_t stime, etime;
    361     l2x_entry_t *buffer;
    362 
    363     /* Allocate memory to accomodate L2X table */
    364     buffer = soc_cm_salloc(unit,
    365                            sizeof(l2x_entry_t) *
    366                            soc_mem_index_count(unit, L2Xm),
    367                            "L2Xm_age");
    368 
    369     if (buffer == NULL) {
    370         LOG_ERROR(BSL_LS_SOC_L2, (BSL_META_U(unit, "_soc_th3_l2_age: "
    371                                              "Memory alloc failed, size %d\n"),
    372                                              (int)(sizeof(l2x_entry_t) *
    373                                              soc_mem_index_count(unit, L2Xm))));
    374 
    375         goto cleanup_exit;
    376     }
    377 
    378     while((interval = soc->l2x_age_interval) != 0) {
    379         if (!iter) {
    380             goto age_delay;
    381         }
    382 
    383         LOG_VERBOSE(BSL_LS_SOC_ARL,
    384                     (BSL_META_U(unit,
    385                                 "l2_age_thread: "
    386                                 "Process iters(total:%d, this run:%d\n"),
    387                      ++_soc_th3_l2_bulk_age_iter[unit], iter));
    388 
    389         stime = sal_time_usecs();
    390 
    391         if (!soc->l2x_age_enable) {
    392             goto age_delay;
    393         }
    394 
    395         if (soc_mem_index_count(unit, L2Xm) == 0) {
    396             goto cleanup_exit;
    397         }
    398 
    399         rv = _soc_th3_l2_age_entries_process(unit, buffer);
    400 
    401         if (!SOC_SUCCESS(rv)) {
    402             goto cleanup_exit;
    403         }
    404 
    405         etime = sal_time_usecs();
    406         LOG_VERBOSE(BSL_LS_SOC_ARL,
    407                     (BSL_META_U(unit,
    408                                 "l2_bulk_age_thread: unit=%d: done in %d usec\n"),
    409                      unit, SAL_USECS_SUB(etime, stime)));
    410 age_delay:
    411         rv = -1; /* timeout */
    412         if (interval > 2147) {
    413             m = (interval / 2147) * 1000;
    414             r = (interval % 2147) * 1000000;
    415             for (c = 0; c < m; c++) {
    416                 rv = sal_sem_take(soc->l2x_age_notify, 2147000);
    417                 /* age interval is changed */
    418                 if (rv == 0 || interval != soc->l2x_age_interval) {
    419                     break;
    420                 }
    421             }
    422             /* age interval is changed */
    423             if (soc->l2x_age_interval &&
    424                 (rv == 0 || interval != soc->l2x_age_interval)) {
    425                 interval = soc->l2x_age_interval;
    426                 goto age_delay;
    427             } else if (r) {
    428                  /* age interval is not changed */
    429                 (void)sal_sem_take(soc->l2x_age_notify, r);
    430             }
    431         } else {
    432             rv = sal_sem_take(soc->l2x_age_notify, interval * 1000000);
    433             /* age interval is changed */
    434             if (soc->l2x_age_interval &&
    435                 (rv == 0 || interval != soc->l2x_age_interval)) {
    436                 interval = soc->l2x_age_interval;
    437                 goto age_delay;
    438             }
    439         }
    440         iter++;
    441     }
    442 
    443 cleanup_exit:
    444     if (buffer != NULL) {
    445         soc_cm_sfree(unit, buffer);
    446     }
    447 
    448     LOG_VERBOSE(BSL_LS_SOC_COMMON,
    449                 (BSL_META_U(unit,
    450                             "l2_age_thread: exiting\n")));
    451     soc->l2x_age_pid = SAL_THREAD_ERROR;
    452     sal_thread_exit(0);
    453 
    454  /*   return; */
    455 }
    456 
    457 /*
    458  * Function:
    459  * 	soc_th3_l2_age_start
    460  * Purpose:
    461  *   	Start L2 aging thread
    462  * Parameters:
    463  *	unit - unit number
    464  * Returns:
    465  *	SOC_E_XXX
    466  */
    467 int
    468 soc_th3_l2_age_start(int unit, int interval)
    469 {
    470     int cfg_interval;
    471     soc_control_t *soc = SOC_CONTROL(unit);
    472 
    473     cfg_interval = soc_property_get(unit, spn_L2_SW_AGING_INTERVAL, 
    474                                     SAL_BOOT_QUICKTURN ? 30 : 10);
    475     SOC_CONTROL_LOCK(unit);
    476 
    477     soc->l2x_age_interval = interval ? interval : cfg_interval;
    478     sal_snprintf(soc->l2x_age_name, sizeof (soc->l2x_age_name),
    479                  "bcmL2age.%d", unit);
    480 
    481     soc->l2x_age_pid = sal_thread_create(soc->l2x_age_name, SAL_THREAD_STKSZ,
    482                                          soc_property_get(unit,
    483                                              spn_L2AGE_THREAD_PRI, 50),
    484                                          _soc_th3_l2_age, INT_TO_PTR(unit));
    485 
    486     if (soc->l2x_age_pid == SAL_THREAD_ERROR) {
    487         LOG_ERROR(BSL_LS_SOC_COMMON,
    488                   (BSL_META_U(unit, "soc_th3_l2_age_start: Could not start"
    489                               " L2 aging thread\n")));
    490 
    491         SOC_CONTROL_UNLOCK(unit);
    492 
    493         return SOC_E_MEMORY;
    494     }
    495 
    496     SOC_CONTROL_UNLOCK(unit);
    497 
    498     return SOC_E_NONE;
    499 }
    500 
    501 /*
    502  * Function:
    503  * 	soc_th3_l2_age_stop
    504  * Purpose:
    505  *   	Stop l2 aging thread
    506  * Parameters:
    507  *	unit - unit number
    508  * Returns:
    509  *	SOC_E_XXX
    510  */
    511 int
    512 soc_th3_l2_age_stop(int unit)
    513 {
    514     soc_control_t *soc = SOC_CONTROL(unit);
    515     int           rv = SOC_E_NONE;
    516     soc_timeout_t to;
    517 
    518     SOC_CONTROL_LOCK(unit);
    519     soc->l2x_age_interval = 0;  /* Request exit */
    520     SOC_CONTROL_UNLOCK(unit);
    521 
    522     if (soc->l2x_age_pid && (soc->l2x_age_pid != SAL_THREAD_ERROR)) {
    523         /* Wake up thread so it will check the exit flag */
    524         sal_sem_give(soc->l2x_age_notify);
    525 
    526         /* Give thread a few seconds to wake up and exit */
    527         if (SAL_BOOT_SIMULATION) {
    528             soc_timeout_init(&to, 300 * 1000000, 0);
    529         } else {
    530             soc_timeout_init(&to, 60 * 1000000, 0);
    531         }
    532 
    533         while (soc->l2x_age_pid != SAL_THREAD_ERROR) {
    534             if (soc_timeout_check(&to)) {
    535                 LOG_ERROR(BSL_LS_SOC_COMMON,
    536                           (BSL_META_U(unit,
    537                                       "thread will not exit\n")));
    538                 rv = SOC_E_INTERNAL;
    539                 break;
    540             }
    541         }
    542     }
    543 
    544     return rv;
    545 }
    546 
    547 /*
    548  * Function:
    549  * 	soc_th3_l2_lrn_cache_entry_invalidate
    550  * Purpose:
    551  *  This function clears a specific L2 learn cache entry from a given pipe.
    552  *  It will be called during learning process to clear entries after they are 
    553  *  learned
    554  * Parameters:
    555  *	unit - unit number
    556  *  pipe - pipe number (0 based)
    557  *  entry - entry index with learn cache (0 based)
    558  * Returns:
    559  *	SOC_E_XXX
    560  */
    561 STATIC
    562 int soc_th3_l2_lrn_cache_entry_invalidate(int unit, int pipe, int entry)
    563 {
    564     soc_mem_t mem;
    565  
    566     if ((pipe < 0) || (pipe > (NUM_PIPE(unit) - 1))) {
    567         return SOC_E_PARAM;
    568     }
    569 
    570     mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe];
    571 
    572     if ((entry < soc_mem_index_min(unit, mem)) ||
    573         (entry > soc_mem_index_max(unit, mem))) {
    574         return SOC_E_PARAM;
    575     }
    576 
    577     soc_mem_lock(unit, mem);
    578     SOC_IF_ERROR_RETURN(soc_mem_write(unit, mem, MEM_BLOCK_ALL, entry,
    579                                       soc_mem_entry_zeroes(unit, mem)));
    580     soc_mem_unlock(unit, mem);
    581 
    582     return SOC_E_NONE;
    583 }
    584 
    585 /*
    586  * Function:
    587  * 	soc_th3_l2_learn_cache_clear
    588  * Purpose:
    589  *  This function clears L2 learn cache. All copies of learn cache (in all
    590  *  pipes) are cleared  
    591  * Parameters:
    592  *	unit - unit number
    593  * Returns:
    594  *	SOC_E_XXX
    595  */
    596 STATIC
    597 int soc_th3_l2_learn_cache_clear(int unit)
    598 {
    599     int  pipe;
    600     soc_info_t *si;
    601     soc_mem_t mem;
    602 
    603     si = &SOC_INFO(unit);
    604 
    605     for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) {
    606 
    607         if (SOC_PBMP_IS_NULL(si->pipe_pbm[pipe])) {
    608             continue;
    609         }
    610 
    611         mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe];
    612 
    613         soc_mem_lock(unit, mem);
    614 
    615         SOC_IF_ERROR_RETURN(soc_mem_clear(unit, mem, MEM_BLOCK_ALL, TRUE));
    616 
    617         soc_mem_unlock(unit, mem);
    618     }
    619 
    620     return SOC_E_NONE;
    621 }
    622 
    623 /*
    624  * Function:
    625  * 	soc_th3_l2_learn_cache_status_clear
    626  * Purpose:
    627  *  This function clears L2 learn cache status registers. All copies of learn
    628  *  cache (in all pipes) are cleared. These are sticky bits and need to be
    629  *  explicitly cleared by software during init or shutdown of L2 module
    630  * Parameters:
    631  *	unit - unit number
    632  * Returns:
    633  *	SOC_E_XXX
    634  */
    635 STATIC
    636 int soc_th3_l2_learn_cache_status_clear(int unit)
    637 {
    638     int  pipe;
    639     soc_reg_t reg = INVALIDr;
    640     uint32 rval = 0;
    641 
    642     reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[0];
    643     SOC_IF_ERROR_RETURN(soc_reg32_get(unit, reg, REG_PORT_ANY, 0, &rval));
    644     soc_reg_field_set(unit, reg, &rval, L2_LEARN_CACHE_FULLf, 0x0);
    645     soc_reg_field_set(unit, reg, &rval, L2_LEARN_CACHE_THRESHOLD_EXCEEDEDf,
    646                       0x0);
    647     for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) {
    648         reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[pipe];
    649         SOC_IF_ERROR_RETURN(soc_reg32_set(unit, reg, REG_PORT_ANY, 0, rval));
    650     }
    651 
    652     return SOC_E_NONE;
    653 }
    654 
    655 /*
    656  * Function:
    657  * 	_soc_th3_l2_learn_cache_status_check_clear
    658  * Purpose:
    659  *  This function checks status of one or more status bits in a pipe's
    660  *  L2 learn cache status register for the specified pipe. If any bit is set,
    661  *  it clears the bit(s). These are sticky bits and need to be explicitly
    662  *  cleared by software
    663  * Parameters:
    664  *	unit     - Unit number
    665  *	pipe     - Pipe whose status bit needs to be cleared
    666  *	fld_ptr  - Pointer to an array of one or more fields which are to be cleared
    667  *	num_flds - Size of fld_ptr array
    668  * Returns:
    669  *	SOC_E_XXX
    670  *	Notes:
    671  *	Caller must provide correct pipe number and correct field value(s)
    672  */
    673 STATIC
    674 int _soc_th3_l2_learn_cache_status_check_clear(int unit, int pipe,
    675                                                soc_field_t *fld_ptr,
    676                                                int num_flds)
    677 {
    678     soc_reg_t reg = INVALIDr;
    679     uint32 rval = 0;
    680     uint32 bit_val;
    681     int i;
    682     int clear;
    683 
    684     reg = SOC_REG_UNIQUE_ACC(unit, L2_LEARN_CACHE_STATUSr)[pipe];
    685 
    686     SOC_IF_ERROR_RETURN(soc_reg32_get(unit, reg, REG_PORT_ANY, 0, &rval));
    687 
    688     clear = FALSE;
    689 
    690     for (i = 0; i < num_flds; i++) {
    691 
    692         /* Check if a status bit is set. If so clear it, else check next bit */
    693         bit_val = soc_reg_field_get(unit, reg, rval, fld_ptr[i]);
    694 
    695         if (bit_val) {
    696             soc_reg_field_set(unit, reg, &rval, fld_ptr[i], 0x0);
    697             clear = TRUE;
    698         }
    699     }
    700 
    701     /* Program register only if atleast one bit was modified */
    702     if (clear == TRUE) {
    703         SOC_IF_ERROR_RETURN(soc_reg32_set(unit, reg, REG_PORT_ANY, 0, rval));
    704     }
    705 
    706     return SOC_E_NONE;
    707 }
    708 
    709 /*
    710  * Function:
    711  * 	soc_th3_l2_learn_cache_read
    712  * Purpose:
    713  *  This function reads all L2 learn cache entries for a given pipe
    714  * Parameters:
    715  *	unit   - unit number
    716  *	pipe   - pipe to read from (range: 0-7)
    717  *	buffer - Buffer filled by memory read operation
    718  * Returns:
    719  *	SOC_E_XXX
    720  * Notes:
    721  *	Caller must do range check and provide correct pipe number
    722  */
    723 STATIC
    724 int soc_th3_l2_learn_cache_read(int unit, int pipe, uint32 *buffer)
    725 {
    726     soc_mem_t mem;
    727     uint32 index_min, index_max;
    728     int rv;
    729 
    730     rv = SOC_E_NONE;
    731 
    732     mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe];
    733 
    734     index_min = soc_mem_index_min(unit, mem);
    735     index_max = soc_mem_index_max(unit, mem);
    736 
    737     soc_mem_lock(unit, mem);
    738 
    739     /* Read learn cache entries from the specified pipe */
    740     rv = soc_mem_read_range(unit, mem, MEM_BLOCK_ANY,
    741                             index_min, index_max, buffer);
    742 
    743     /* Explicitly clear learn cache for rev A0 */
    744     if (SOC_CONTROL(unit)->lrn_cache_clr_on_rd &&
    745         (rev_id == BCM56980_A0_REV_ID)) {
    746         if (rv == SOC_E_NONE) {
    747             rv = soc_mem_clear(unit, mem, MEM_BLOCK_ALL, FALSE);
    748         }
    749     }
    750 
    751     soc_mem_unlock(unit, mem);
    752 
    753     if (SOC_FAILURE(rv)) {
    754         LOG_ERROR(BSL_LS_SOC_L2,
    755                   (BSL_META_U(unit,
    756                    "%s:DMA read failed: %s\n"), __FUNCTION__, soc_errmsg(rv)));
    757     }
    758 
    759     return rv;
    760 }
    761 
    762 /*
    763  * Function:
    764  * 	_soc_th3_learn_avl_compare_key
    765  * Purpose:
    766  *  Comparison function for AVL shadow table operations
    767  * Parameters:
    768  *	user_data - User supplied data reuired by AVL library
    769  *	datum1    - First data item to compare
    770  *	datum2    - Second data item to compare
    771  * Returns:
    772  *	SOC_E_XXX
    773  * Notes:
    774  *	None
    775  */
    776 STATIC int
    777 _soc_th3_learn_avl_compare_key(void *user_data,
    778                                shr_avl_datum_t *datum1,
    779                                shr_avl_datum_t *datum2)
    780 {
    781      soc_l2_lrn_avl_info_p k1, k2;
    782 
    783     /* COMPILER_REFERENCE(user_data);*/
    784     k1 = (soc_l2_lrn_avl_info_p)datum1;
    785     k2 = (soc_l2_lrn_avl_info_p)datum2;
    786 
    787     SOC_MEM_COMPARE_RETURN(k1->vlan, k2->vlan);
    788 
    789     return ENET_CMP_MACADDR(k1->mac, k2->mac);
    790 }
    791 
    792 /*
    793  * Function:
    794  * 	soc_th3_lrn_shadow_insert
    795  * Purpose:
    796  *  This function is used to insert an entry in to learn shadow table,
    797  *  corresponding to the hardware L2 entry added before calling this function.
    798  *  Since there is no relevance of L2 multicast, static entries and
    799  *  vlan cross connect entries for learning process we ignore these entry types.
    800  *  See Notes
    801  * Parameters:
    802  *	unit - Switch unit #
    803  *	l2x_entry_t - Entry to be inserted in to AVL tree
    804  * Returns:
    805  *	SOC_E_XXX
    806  * Notes:
    807  *	Caller _must_ lock mutex l2x_lrn_shadow_mutex before calling this function
    808  */
    809 int
    810 soc_th3_lrn_shadow_insert(int unit, l2x_entry_t *entry)
    811 {
    812     soc_l2_lrn_avl_info_t k;
    813     int rv;
    814 
    815     /* If shadow memory is freed, or not set up, do nothing */
    816     if (SOC_CONTROL(unit)->l2x_lrn_shadow == NULL) {
    817         return SOC_E_NONE;
    818     }
    819 
    820     /* If entry is not valid, do not insert in to shadow table */
    821     if (!soc_mem_field32_get(unit, L2Xm, entry, BASE_VALIDf)) {
    822         return SOC_E_NONE;
    823     }
    824 
    825     /* Ignore static entry */
    826     if (soc_L2Xm_field32_get(unit, entry, STATIC_BITf)) {
    827         return SOC_E_NONE;
    828     }
    829 
    830     /* Do not add single cross connect entries, since they are niether learned,
    831      * nor aged
    832      */
    833     if (soc_mem_field32_get(unit, L2Xm, entry, KEY_TYPEf) !=
    834                             TH3_L2_HASH_KEY_TYPE_BRIDGE) {
    835         return SOC_E_NONE;
    836     }
    837 
    838     sal_memset(&k, 0x0, sizeof(k));
    839     soc_mem_mac_addr_get(unit, L2Xm, entry, MAC_ADDRf, k.mac);
    840 
    841     /* Do not add multicast entries */
    842     if (SOC_TH3_MAC_IS_MCAST(k.mac)) {
    843         return SOC_E_NONE;
    844     }
    845 
    846     k.vlan = soc_mem_field32_get(unit, L2Xm, entry, VLAN_IDf);
    847     k.dest_type = soc_mem_field32_get(unit, L2Xm, entry, Tf);
    848     k.port_tgid = soc_mem_field32_get(unit, L2Xm, entry, DESTINATIONf);
    849     /* Entry has already been added to h/w, so we set in_hw to 'true' */
    850     k.in_hw = TRUE;
    851 
    852     rv = shr_avl_insert(SOC_CONTROL(unit)->l2x_lrn_shadow,
    853                         _soc_th3_learn_avl_compare_key,
    854                         (shr_avl_datum_t *)&k);
    855 
    856     /* We do not return error since normally there will always be space for a
    857      * new entry to add in the tree. If this were not the case, mem insert/write
    858      * called before invoking this function will fail. Also, the full condition
    859      * may be cleared by software replace mechanism, or aging, or application
    860      * deleting L2 entries. Also hardware h/w has already been updated at this
    861      * point
    862      */
    863     if (rv == -1) {
    864         LOG_WARN(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    865                      "shr_avl_insert - tree full\n")));
    866     }
    867 
    868     return SOC_E_NONE;
    869 }
    870 
    871 /*
    872  * Function:
    873  * 	soc_th3_lrn_shadow_delete
    874  * Purpose:
    875  *  This function deletes an entry from learn shadow table,
    876  *  after the hardware L2 entry is deleted. Since there is no relevance of L2
    877  *  multicast, static entries and vlan cross connect entries for learning
    878  *  process we ignore these entry types. See Notes
    879  * Parameters:
    880  *	unit - Switch unit #
    881  *	l2x_entry_t - Entry to be inserted in to AVL tree
    882  * Returns:
    883  *	SOC_E_XXX
    884  * Notes:
    885  *	Caller _must_ lock mutex l2x_lrn_shadow_mutex before calling this function
    886  */
    887 int
    888 soc_th3_lrn_shadow_delete(int unit, l2x_entry_t *entry)
    889 {
    890     soc_l2_lrn_avl_info_t k;
    891     int rv;
    892 
    893     /* If shadow memory is freed, or not set up, don't do anything */
    894     if (SOC_CONTROL(unit)->l2x_lrn_shadow == NULL) {
    895         return SOC_E_NONE;
    896     }
    897 
    898 #if 0
    899     /* If entry is not valid, do not insert in to shadow table */
    900     if (!soc_mem_field32_get(unit, L2Xm, entry, BASE_VALIDf)) {
    901         return SOC_E_NONE;
    902     }
    903 #endif
    904 
    905     /* Ignore static entry */
    906     if (soc_L2Xm_field32_get(unit, entry, STATIC_BITf)) {
    907         return SOC_E_NONE;
    908     }
    909 
    910     /* Ignore single cross connect entries, since they are niether learned,
    911      * nor aged
    912      */
    913     if (soc_mem_field32_get(unit, L2Xm, entry, KEY_TYPEf) !=
    914                             TH3_L2_HASH_KEY_TYPE_BRIDGE) {
    915         return SOC_E_NONE;
    916     }
    917 
    918     sal_memset(&k, 0x0, sizeof(k));
    919     soc_mem_mac_addr_get(unit, L2Xm, entry, MAC_ADDRf, k.mac);
    920 
    921     /* Ignore multicast entries */
    922     if (SOC_TH3_MAC_IS_MCAST(k.mac)) {
    923         return SOC_E_NONE;
    924     }
    925 
    926     k.vlan = soc_mem_field32_get(unit, L2Xm, entry, VLAN_IDf);
    927     k.dest_type = soc_mem_field32_get(unit, L2Xm, entry, Tf);
    928     k.port_tgid = soc_mem_field32_get(unit, L2Xm, entry, DESTINATIONf);
    929     /* Entry has already been deleted from h/w, so we set in_hw to 'false' */
    930     k.in_hw = FALSE;
    931 
    932     rv = shr_avl_delete(SOC_CONTROL(unit)->l2x_lrn_shadow,
    933                             _soc_th3_learn_avl_compare_key,
    934                             (shr_avl_datum_t *)&k);
    935     if (rv == 0) {
    936         LOG_INFO(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
    937             "shr_avl_delete: Did not find datum\n")));
    938     }
    939 
    940     return SOC_E_NONE;
    941 }
    942 
    943 /*
    944  * Function:
    945  *	soc_th3_lrn_shadow_show
    946  * Purpose:
    947  *	Debug display function for AVL learn shadow table
    948  * Parameters:
    949  *	user_data - Used to pass StrataSwitch unit #
    950  *	datum - AVL node to display
    951  *	extra_data - Unused
    952  * Returns:
    953  *	SOC_E_XXX
    954  */
    955 
    956 int
    957 soc_th3_lrn_shadow_show(void *user_data, shr_avl_datum_t *datum, void *extra_data)
    958 {
    959     int		unit = PTR_TO_INT(user_data);
    960     soc_l2_lrn_avl_info_p k = (soc_l2_lrn_avl_info_p)datum;
    961 
    962     COMPILER_REFERENCE(extra_data);
    963 
    964             BSL_LOG(BSL_LSS_CLI, (BSL_META_U(unit, "dest_type: %d, port_tgid: %d, mod: %d, in_hw: %d \n"),k->dest_type, k->port_tgid, k->mod, k->in_hw));
    965             BSL_LOG(BSL_LSS_CLI, (BSL_META_U(unit, "mac(in hex) %02X:%02X:%02X:%02X:%02X:%02X, vlan: %d\n"),k->mac[0], k->mac[1], k->mac[2], k->mac[3], k->mac[4], k->mac[5], k->vlan));
    966     LOG_CLI((BSL_META_U(unit,
    967                         "----------------------------------------\n")));
    968 
    969     return SOC_E_NONE;
    970 }
    971 
    972 /*
    973  * Function:
    974  * 	_soc_th3_learn_do_lookup
    975  * Purpose:
    976  *  This function searches shadow table for matching key, and sets passed
    977  *  arguments accordingly
    978  * Parameters:
    979  *	unit(IN)      - Device unit number
    980  *	k(IN/OUT)     - key items to search for. The AVL library updates other
    981  *                  fields of this structure, if key is found
    982  *	found(OUT)    - Set to true if item is found, else set to false
    983  *	stn_move(OUT) - Set to true if station move condition is detected,
    984  *                  else set to false
    985  * Returns:
    986  *	SOC_E_XXX
    987  * Notes:
    988  *	None
    989  */
    990 STATIC int
    991 _soc_th3_learn_do_lookup(int unit,
    992                          soc_l2_lrn_avl_info_p k,
    993                          int *found,
    994                          int *stn_move)
    995 {
    996 
    997     int result;
    998     int port_tgid;
    999     int dest_type;
   1000 
   1001     /* Save port number obtained from hardware
   1002      * Note AVL lookup overwrites 'k' completely, if the entry was found
   1003      */
   1004     port_tgid = k->port_tgid;
   1005     dest_type = k->dest_type;
   1006 
   1007     sal_mutex_take(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex, sal_mutex_FOREVER);
   1008 
   1009     result = shr_avl_lookup(SOC_CONTROL(unit)->l2x_lrn_shadow,
   1010                  _soc_th3_learn_avl_compare_key, (shr_avl_datum_t *)k);
   1011 
   1012     sal_mutex_give(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex);
   1013 
   1014     /* Check if a matching node was found in AVL tree. If so, check if the
   1015      * entry has been added to L2 table in h/w. If so, then do nothing.
   1016      * If not, the entry needs to be flagged for programming in h/w
   1017      */
   1018     *found = FALSE;
   1019     *stn_move = FALSE;
   1020     if (result) {
   1021 
   1022         /* Entry found */
   1023         *found = TRUE;
   1024 
   1025         /* If entry is found, check for station move */
   1026         *stn_move = ((k->dest_type == dest_type) && (k->port_tgid == port_tgid)) ? FALSE : TRUE;
   1027     }
   1028 
   1029     return SOC_E_NONE;
   1030 }
   1031 
   1032 /*
   1033  * Function:
   1034  * 	_soc_th3_learn_cache_entry_process
   1035  * Purpose:
   1036  *  This function processes each entry from the learn cache. It check if the
   1037  *  if the entry is present in learn shadow table. If entry is not found, it is
   1038  *  a new L2 flow, so its added programmed in to main L2 table. Learn cache
   1039  *  is updated after addition.
   1040  * Parameters:
   1041  *	unit  - Unit number of device
   1042  *	pipe  - Pipe in which the entry was detected (range: 0-7)
   1043  *	entry - Learn cache entry from the pipe
   1044  *  index - Location of entry within the learn cache (range 0-15)
   1045  * Returns:
   1046  *	SOC_E_XXX
   1047  * Notes:
   1048  *	None
   1049  */
   1050 STATIC int
   1051 _soc_th3_learn_cache_entry_process(int unit,
   1052                                    int pipe,
   1053                                    l2_learn_cache_entry_t *entry,
   1054                                    int entry_idx)
   1055 {
   1056     int rv;
   1057     int found;
   1058     int stn_move;
   1059     soc_mem_t mem;
   1060     soc_l2_lrn_avl_info_t k;
   1061     int invalidated = FALSE;
   1062     int curr_l2_table_entries;
   1063     int max_l2_table_entries;
   1064     int l2copyno;
   1065 
   1066     max_l2_table_entries = soc_mem_index_count(unit, L2Xm);
   1067     l2copyno = SOC_MEM_BLOCK_ANY(unit, L2Xm);
   1068 
   1069     mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe];
   1070 
   1071     sal_memset(&k, 0x0, sizeof(k));
   1072 
   1073     soc_mem_mac_addr_get(unit, mem, entry, MAC_ADDRf, k.mac);
   1074     k.vlan = soc_mem_field32_get(unit, mem, entry, VLAN_IDf);
   1075     k.dest_type = soc_mem_field32_get(unit, mem, entry, DEST_TYPEf);
   1076     k.port_tgid = soc_mem_field32_get(unit, mem, entry, DESTINATIONf);
   1077     k.in_hw = FALSE;
   1078 
   1079     rv = _soc_th3_learn_do_lookup(unit, &k, &found, &stn_move);
   1080 
   1081     /* Check if learn interrupt needs to be disabled during processing below */
   1082     if (rv == SOC_E_NONE) {
   1083         l2x_entry_t l2x_entry;
   1084         soc_port_t port_tgid = 0;
   1085         soc_field_t field = INVALIDf;
   1086 
   1087         if (SOC_CONTROL(unit)->lrn_cache_clr_on_rd) {
   1088             /* Check if entry was added to h/w by previous learn cache entry */
   1089             /* Duplicate entries can result only in clear on read mode */
   1090             if (found == TRUE) {
   1091                 if (k.in_hw == TRUE) {
   1092                     /* In case of station move, L2X entry is already present in
   1093                      * h/w (in_hw is true); we should not invalidate the first
   1094                      * learn cache entry here (for station move), without
   1095                      * checking station move condition (station move is handled
   1096                      * later in this function)
   1097                      */
   1098                     if (stn_move == FALSE) {
   1099                         /* Entry cleared by h/w in clr-on-rd mode, so we do not
   1100                          * explicitly invalidate the entry here
   1101                          */
   1102                         /* SOC_IF_ERROR_RETURN(
   1103                             soc_th3_l2_lrn_cache_entry_invalidate(unit, pipe,
   1104                                                                   entry_idx)); */
   1105                         LOG_INFO(BSL_LS_SOC_L2,
   1106                             (BSL_META_U(unit, "Duplicate lrn cache entry:"
   1107                             " pipe %d, index %d\n"), pipe, entry_idx));
   1108 
   1109                         return rv;
   1110                     }
   1111                 } else {
   1112                     /* If k.in_hw is FALSE, it means h/w was
   1113                      * updated, but software table entry was not, which should
   1114                      * never happen. It may point to software table corruption,
   1115                      * or a problem arising out of table write sequence.
   1116                      * Tables should always be in sync
   1117                      */
   1118                     LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit, "%s: S/w"
   1119                                   " entry %d, in pipe %d out of sync with"
   1120                                   " h/w\n"), __FUNCTION__, entry_idx, pipe));
   1121                     return SOC_E_INTERNAL;
   1122                 }
   1123             }
   1124         }
   1125 
   1126         sal_memset(&l2x_entry, 0, sizeof(l2x_entry));
   1127         soc_L2Xm_field32_set(unit, &l2x_entry, BASE_VALIDf, 0x1);
   1128         soc_L2Xm_field32_set(unit, &l2x_entry, VLAN_IDf, k.vlan);
   1129         soc_L2Xm_mac_addr_set(unit, &l2x_entry, MAC_ADDRf, k.mac);
   1130         soc_L2Xm_field32_set(unit, &l2x_entry, KEY_TYPEf,
   1131                                  TH3_L2_HASH_KEY_TYPE_BRIDGE);
   1132         if (k.dest_type) {
   1133             soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x1);
   1134         }
   1135 
   1136         if (found == FALSE) {
   1137             /* Replace original port with new port */
   1138             field = k.dest_type ? TGIDf : PORT_NUMf;
   1139             soc_L2Xm_field32_set(unit, &l2x_entry, field, k.port_tgid);
   1140             soc_L2Xm_field32_set(unit, &l2x_entry, HITSAf, 1);
   1141 
   1142             /* Insert L2 entry in h/w */
   1143             soc_mem_lock(unit, L2Xm);
   1144             curr_l2_table_entries = SOP_MEM_STATE(unit, L2Xm).count[l2copyno];
   1145 
   1146             /* If there is no space in the L2 table, do not issue insert. Note
   1147              * that current L2 table size is dynamically changing; entries can
   1148              * be added/deleted though other sources like application thread
   1149              * (using L2 APIs), cmd shell, other internal SDK modules and so on.
   1150              * So the current table enttries is only a tentative (but closer to
   1151              * accurate) value
   1152              */
   1153             rv = SOC_E_NONE;
   1154             if ((curr_l2_table_entries >= 0) &&
   1155                 (curr_l2_table_entries < max_l2_table_entries)) {
   1156                 rv = soc_mem_insert(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry);
   1157             }
   1158             soc_mem_unlock(unit, L2Xm);
   1159 
   1160             /* AVL tree will be updated through soc_th3_l2x_shadow_callback */
   1161 
   1162             if ((rv == SOC_E_FULL) || (rv == SOC_E_EXISTS) ||
   1163                 (rv == SOC_E_NOT_FOUND)) {
   1164                 /* If full, exist or not found conditions are encountered, we
   1165                  * simply log an error. If we return error, learn thread will
   1166                  * exit. We don't want the thread to exit based on certain
   1167                  * 'conditions', or search result, since it will stop learning
   1168                  * altogether. Full condition can get cleared later on by aging,
   1169                  * deletions by application(s), or by s/w replace operation.
   1170                  */
   1171                 LOG_INFO(BSL_LS_SOC_L2,
   1172                             (BSL_META_U(unit, "%s: soc_mem_insert retval %d\n"),
   1173                             __FUNCTION__, rv));
   1174                 rv = SOC_E_NONE;
   1175             } else {
   1176                 if (SOC_FAILURE(rv)) {
   1177                     return rv;
   1178                 }
   1179             }
   1180 
   1181             /* Clear entry in learn cache only if clr on rd is not enabled */
   1182             if (!(SOC_CONTROL(unit)->lrn_cache_clr_on_rd)) {
   1183                 SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit,
   1184                                         pipe, entry_idx));
   1185             }
   1186 
   1187             invalidated = TRUE;
   1188         }
   1189 
   1190         /* Handle station move */
   1191         if ((stn_move == TRUE) && (found == TRUE)) {
   1192             soc_mem_lock(unit, L2Xm);
   1193             rv = soc_mem_delete(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry);
   1194             if (SOC_FAILURE(rv)) {
   1195                 soc_mem_unlock(unit, L2Xm);
   1196                 if (rv == SOC_E_NOT_FOUND) {
   1197                     /* If entry is not found, log an error. Do not return this
   1198                      * error code since it is not a critical/fatal error. If
   1199                      * same error code is returned, the learn thread will exit
   1200                      * and learning will stop
   1201                      */
   1202                     LOG_INFO(BSL_LS_SOC_L2,
   1203                                 (BSL_META_U(unit, "%s: soc_mem_delete"
   1204                                  " retval %d\n"), __FUNCTION__, rv));
   1205                     rv = SOC_E_NONE;
   1206                 }
   1207 
   1208                 return rv;
   1209             }
   1210 
   1211             /* Replace original port with new port */
   1212             port_tgid = soc_mem_field32_get(unit, mem, entry, DESTINATIONf);
   1213             /* k.dest_type, k.port_tgid are overwritten by
   1214              * _soc_th3_learn_do_lookup, so we get k.dest_type from learn cache
   1215              * again (k.port_tgid is not used here though)
   1216              */
   1217             k.dest_type = soc_mem_field32_get(unit, mem, entry, DEST_TYPEf);
   1218             field = k.dest_type ? TGIDf : PORT_NUMf;
   1219             if (k.dest_type) {
   1220                 soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x1);
   1221             } else {
   1222                 soc_L2Xm_field32_set(unit, &l2x_entry, Tf, 0x0);
   1223             }
   1224             soc_L2Xm_field32_set(unit, &l2x_entry, field, port_tgid);
   1225             soc_L2Xm_field32_set(unit, &l2x_entry, HITSAf, 1);
   1226 
   1227             rv = soc_mem_insert(unit, L2Xm, MEM_BLOCK_ALL, &l2x_entry);
   1228             soc_mem_unlock(unit, L2Xm);
   1229 
   1230             if ((rv == SOC_E_FULL) || (rv == SOC_E_EXISTS) ||
   1231                 (rv == SOC_E_NOT_FOUND)) {
   1232                 /* If full, exist or not found conditions are encountered, we
   1233                  * simply log an error. If we return error, learn thread will
   1234                  * exit. We don't want the thread to exit based on certain
   1235                  * 'conditions', or search result, since it will stop learning
   1236                  * altogether. Full condition can get cleared later on by aging,
   1237                  * deletions by application(s), or by s/w replace operation.
   1238                  */
   1239                 LOG_INFO(BSL_LS_SOC_L2,
   1240                             (BSL_META_U(unit, "%s: soc_mem_insert retval %d\n"),
   1241                             __FUNCTION__, rv));
   1242                 rv = SOC_E_NONE;
   1243             } else {
   1244                 if (SOC_FAILURE(rv)) {
   1245                     return rv;
   1246                 }
   1247             }
   1248 
   1249 
   1250             /* AVL tree will be updated through soc_th3_l2x_shadow_callback */
   1251 
   1252             /* Clear entry in learn cache only if clr on rd is not enabled */
   1253             if (!(SOC_CONTROL(unit)->lrn_cache_clr_on_rd)) {
   1254                 SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit,
   1255                                         pipe, entry_idx));
   1256             }
   1257             invalidated = TRUE;
   1258         }
   1259     }
   1260 
   1261     /* This case should not happen, added here as a precaution to avoid
   1262      * full condition for learn cache due to unprocessed entries (if
   1263      * this condition is reached, it means the entry was not processed
   1264      * earlier)
   1265      */
   1266     if (invalidated == FALSE) {
   1267         LOG_INFO(BSL_LS_SOC_L2,
   1268                     (BSL_META_U(unit, "%s: Entry %d in pipe %d not processed,"
   1269                      " removing it from lrn cache\n"), __FUNCTION__,
   1270                      entry_idx, pipe));
   1271 
   1272         SOC_IF_ERROR_RETURN(soc_th3_l2_lrn_cache_entry_invalidate(unit,
   1273                                 pipe, entry_idx));
   1274     }
   1275 
   1276     return rv;
   1277 }
   1278 
   1279 /*
   1280  * Function:
   1281  *      _soc_th3_lrn_cache_intr_configure
   1282  * Purpose:
   1283  *      This function is used to enable or disable L2 learn cache interrupt
   1284  *      generation
   1285  * Parameters:
   1286  *      unit   - SOC unit #
   1287  *      bit    - Bit number corresponding to a pipe. Bit 8 is for pipe 0 and
   1288  *               bit 15 is for pipe 7. See Notes.
   1289  *      enable - To enable interrupt, use 1 or a non-zero value.
   1290  *               To disable interrupt, use 0.
   1291  * Returns:
   1292  *     SOC_E_XXX
   1293  * Notes:
   1294  *     This function uses hard-coded values for interrupt numbers/bit positions.
   1295  *     Also it assumes fixed number of bits (1 per pipe), contiguous numbering,
   1296  *     and fixed bit positions for each pipe; so it is not portable
   1297  */
   1298 STATIC int
   1299 _soc_th3_lrn_cache_intr_configure(int unit, int bit, int enable)
   1300 {
   1301     int rv = SOC_E_NONE;
   1302     uint32 regval = 0;
   1303     soc_reg_t reg = ICFG_CHIP_LP_INTR_ENABLE_REG1r;
   1304 
   1305     /* Accept only bits 8-15 (both numbers included) */
   1306     if ((bit < 8) || (bit > 15)) {
   1307         return SOC_E_INTERNAL;
   1308     }
   1309 
   1310     rv = soc_iproc_getreg(unit, soc_reg_addr(unit, reg, REG_PORT_ANY, 0),
   1311                           &regval);
   1312     if (rv == SOC_E_NONE) {
   1313         if (enable) {
   1314             regval |= 1 << bit;
   1315         } else {
   1316             regval &= ~(1 << bit);
   1317         }
   1318 
   1319         rv = soc_iproc_setreg(unit, soc_reg_addr(unit, reg, REG_PORT_ANY, 0),
   1320                               regval);
   1321     }
   1322 
   1323     return rv;
   1324 }
   1325 
   1326 /*
   1327  * Function:
   1328  *      soc_th3_lrn_cache_intr_handler
   1329  * Purpose:
   1330  *      Interrupt handler for (per-pipe) learn cache (fifo) interrupt
   1331  * Parameters:
   1332  *      unit  - SOC unit #
   1333  *      data - Data used by the isr, initialized during interrupt registration
   1334  * Returns:
   1335  *     Nothing
   1336  */
   1337 void
   1338 soc_th3_lrn_cache_intr_handler(int unit, void *data)
   1339 {
   1340     soc_control_t *soc = SOC_CONTROL(unit);
   1341     int i;
   1342 
   1343     if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1344         /* Disable learn cache interrupts from all pipes */
   1345         for (i = 8; i <= 15; i++) {
   1346             (void)_soc_th3_lrn_cache_intr_configure(unit, i, 0);
   1347         }
   1348 
   1349         /* Signal lrn thread of learn event(s) */
   1350         sal_sem_give(soc->arl_notify);
   1351     }
   1352 
   1353     /* If we see interrupt in polled mode, then there is some misconfiguration.
   1354      * In this case, check L2_LEARN_COPY_CACHE_CTRL's interrupt control bit
   1355      */
   1356 
   1357     return;
   1358 }
   1359 
   1360 /*
   1361  * Function:
   1362  *     soc_th3_l2_learn_alloc_resources
   1363  * Purpose:
   1364  *     This function is used to create learn shadow table memory and mutex for
   1365  *     safe access to the shadow table. It is called during L2 initialization
   1366  * Parameters:
   1367  *     u - Pointer to unit number
   1368  * Returns:
   1369  *     SOC_E_NONE on success
   1370  *     Other SOC_E_* codes on error
   1371  */
   1372 int
   1373 soc_th3_l2_learn_alloc_resources(int unit)
   1374 {
   1375     /* Create AVL table and semaphore used for L2 learning */
   1376     if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) {
   1377 
   1378         if (shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow) < 0) {
   1379             LOG_ERROR(BSL_LS_SOC_COMMON,
   1380                 (BSL_META_U(unit, "%d: Error calling shr_avl_destroy\n"), unit));
   1381 
   1382             return SOC_E_INTERNAL;
   1383         }
   1384 
   1385         SOC_CONTROL(unit)->l2x_lrn_shadow = NULL;
   1386     }
   1387 
   1388     if (shr_avl_create(&SOC_CONTROL(unit)->l2x_lrn_shadow, INT_TO_PTR(unit),
   1389            sizeof(soc_l2_lrn_avl_info_t), _SOC_TH3_L2_LRN_TBL_SIZE) < 0) {
   1390 
   1391             LOG_ERROR(BSL_LS_SOC_COMMON,
   1392                       (BSL_META_U(unit, "%d: Error calling shr_avl_create\n"), unit));
   1393 
   1394             return SOC_E_MEMORY;
   1395     }
   1396 
   1397     if ((SOC_CONTROL(unit)->l2x_lrn_shadow_mutex =
   1398             sal_mutex_create("L2AvlMutex")) == NULL) {
   1399 
   1400         if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) {
   1401             shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow);
   1402             SOC_CONTROL(unit)->l2x_lrn_shadow = NULL;
   1403         }
   1404 
   1405         LOG_ERROR(BSL_LS_SOC_COMMON,
   1406             (BSL_META_U(unit, "%d: Error calling sal_mutex_create for"
   1407             " L2 AVL Mutex\n"), unit));
   1408 
   1409         return SOC_E_MEMORY;
   1410     }
   1411 
   1412     LOG_INFO(BSL_LS_SOC_L2,
   1413                 (BSL_META_U(unit, "%d: %s: Created"
   1414                      " shadow table and mutex\n"), unit, __FUNCTION__));
   1415 
   1416     return SOC_E_NONE;
   1417 }
   1418 
   1419 /*
   1420  * Function:
   1421  *     _soc_th3_l2_learn_process
   1422  * Purpose:
   1423  *     This function is the main handler for learn thread. It will be used by
   1424  *     learn thread during learning. It will read learn cache entries, perform
   1425  *     look-ups in shadow (AVL) table during station move, and write to it
   1426  *     after an L2 entry is learned
   1427  * Parameters:
   1428  *     u - Pointer to unit number
   1429  * Returns:
   1430  *     Nothing
   1431  */
   1432 STATIC void
   1433 _soc_th3_l2_learn_process(void *u)
   1434 {
   1435     int unit = PTR_TO_INT(u);
   1436     soc_control_t *soc = SOC_CONTROL(unit);
   1437     int interval;
   1438     void *buffer;
   1439     uint32 index_min;
   1440     int count;
   1441     int num_bytes;
   1442     int rv;
   1443     int pipe;
   1444     soc_mem_t mem;
   1445     int i;
   1446     int valid;
   1447 
   1448     LOG_INFO(BSL_LS_SOC_L2,
   1449                 (BSL_META_U(unit, "%d: In _soc_th3_l2_learn_process\n"), unit));
   1450 
   1451     mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[0];
   1452     num_bytes = soc_mem_entry_bytes(unit, mem);
   1453     count = soc_mem_index_count(unit, mem);
   1454     index_min = soc_mem_index_min(unit, mem);
   1455 
   1456     buffer = soc_cm_salloc(unit, num_bytes * count, "L2_LEARN_CACHEm");
   1457 
   1458     if (buffer == NULL) {
   1459         soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR,
   1460                            SOC_SWITCH_EVENT_THREAD_L2X_LEARN, __LINE__,
   1461                            SOC_E_MEMORY);
   1462         goto cleanup_exit;
   1463     }
   1464 
   1465     if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1466         /* Enable learn cache interrupts for all pipes */
   1467         for (i = 8; i <= 15; i++) {
   1468             (void)_soc_th3_lrn_cache_intr_configure(unit, i, 1);
   1469         }
   1470     }
   1471 
   1472     while((interval = soc->l2x_learn_interval)) {
   1473 
   1474         uint32 sts_reg = 0;
   1475         uint32 en_reg = 0;
   1476         uint32 mask = 0;
   1477         uint32 poll_all_pipes = 1;
   1478 
   1479         if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1480             soc_reg_t reg = INVALIDr;
   1481             uint32 shift = 0;
   1482 
   1483             sal_sem_take(soc->arl_notify, interval);
   1484 
   1485             /*
   1486              * We read the interrupt status here, and process the pipes whose
   1487              * learn cache has reached or exceeded threshold. While processing,
   1488              * if other pipes raise interrupts, we will process them in the
   1489              * next cycle
   1490              */
   1491             reg = ICFG_CHIP_LP_INTR_RAW_STATUS_REG1r;
   1492 
   1493             rv = soc_iproc_getreg(unit,
   1494                                   soc_reg_addr(unit, reg, REG_PORT_ANY, 0),
   1495                                   &sts_reg);
   1496 
   1497             if (SOC_SUCCESS(rv)) {
   1498                 reg = ICFG_CHIP_LP_INTR_ENABLE_REG1r;
   1499 
   1500                 rv = soc_iproc_getreg(unit,
   1501                                       soc_reg_addr(unit, reg, REG_PORT_ANY, 0),
   1502                                       &en_reg);
   1503             }
   1504 
   1505             if (SOC_FAILURE(rv)) {
   1506                 LOG_ERROR(BSL_LS_SOC_COMMON,
   1507                     (BSL_META_U(unit, "Failed to read register"
   1508                     " %s, rv = %d\n"), SOC_REG_NAME(unit, reg), rv));
   1509 
   1510                 goto cleanup_exit;
   1511             }
   1512 
   1513             shift = 8; /* L2 learn interrupt bits start at bit 8 */
   1514 
   1515             /* Create mask to pick the set of learning bits */
   1516             mask = (1U << NUM_PIPE(unit)) - 1;
   1517             mask <<= shift;
   1518 
   1519             /* Clear out intr bits other than those that are for learning */
   1520             en_reg &= mask;
   1521             sts_reg &= mask;
   1522 
   1523             /* Select intr bits which are currently disabled by the isr
   1524              * (soc_th3_lrn_cache_intr_handler), since those are the ones that
   1525              *  need to be serviced. (Note currently we reset all bits in the
   1526              * isr to simplify interrupt processing)
   1527              */
   1528             sts_reg &= ~en_reg;
   1529             sts_reg >>= shift;
   1530 
   1531             /* Process all pipes if there was no interrupt during learn
   1532              * interval. This way we handle pipes which have entries, but the
   1533              * threshold has not reached, so the interrupt is not generated
   1534              * (for those pipes)
   1535              */
   1536             poll_all_pipes = !sts_reg ? 1 : 0;
   1537         }
   1538 
   1539         /* The system is in warmboot phase. We do not do any learn processing
   1540          * until we are out of warmboot
   1541          */
   1542         if (SOC_WARM_BOOT(unit)) {
   1543             goto skip_processing;
   1544         }
   1545 
   1546         for (pipe = 0; pipe < NUM_PIPE(unit); pipe++) {
   1547             int full_cleared;
   1548             int thr_cleared;
   1549             /* Number of valid learn cache entries processed successfully */
   1550             int processed_cnt;
   1551             soc_info_t *si = &SOC_INFO(unit);
   1552 
   1553             /* For half-pipe configuration, this check has been added */
   1554             if (SOC_PBMP_IS_NULL(si->pipe_pbm[pipe])) {
   1555                 continue;
   1556             }
   1557 
   1558             /* The system is in warmboot phase. We do not do any learn
   1559              * processing until we are out of warmboot
   1560              */
   1561             if (SOC_WARM_BOOT(unit)) {
   1562                 goto skip_processing;
   1563             }
   1564 
   1565             full_cleared = FALSE;
   1566             thr_cleared = FALSE;
   1567 
   1568             /* Count the number of valid entries processed, and use it to
   1569              * compare to threshold value
   1570              */
   1571             processed_cnt = 0;
   1572 
   1573             if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1574                 /* sts_reg has bits set for pipes whose caches need
   1575                  * to be processed
   1576                  * If we are not polling all pipes, then we should check
   1577                  * individual bits in the sts_reg bitmap (corresponding to each
   1578                  * pipe), and process entries in that pipe (interrupt was
   1579                  * asserted for pipes in sts_reg bitmap)
   1580                  */
   1581                 /* If we shouldn't process all pipes, then specific pipes in the
   1582                  * bitmap are the ones which need to be serviced
   1583                  */
   1584                 if ((!poll_all_pipes) && (!(sts_reg & (1U << pipe)))) {
   1585                     continue;
   1586                 }
   1587             }
   1588 
   1589             /* Read cache entries for the specified pipe */
   1590             rv = soc_th3_l2_learn_cache_read(unit, pipe, buffer);
   1591 
   1592             if (SOC_FAILURE(rv)) {
   1593                 soc_event_generate(unit, SOC_SWITCH_EVENT_THREAD_ERROR,
   1594                                    SOC_SWITCH_EVENT_THREAD_L2X_LEARN,
   1595                                    __LINE__, rv);
   1596                 goto cleanup_exit;
   1597             }
   1598 
   1599             /* Process each learn cache entry */
   1600             for (i = index_min; i < (index_min + count); i++) {
   1601                 l2_learn_cache_entry_t *entry;
   1602 
   1603                 if (!soc->l2x_learn_interval) {
   1604                     goto cleanup_exit;
   1605                 }
   1606 
   1607                 /* The system is in warmboot phase. We do not do any learn
   1608                  * processing until we are out of warmboot
   1609                  */
   1610                 if (SOC_WARM_BOOT(unit)) {
   1611                     goto skip_processing;
   1612                 }
   1613 
   1614                 /* Point to the next entry in the buffer */
   1615                 entry = (l2_learn_cache_entry_t *)((uint8 *)buffer +
   1616                                                    i * num_bytes);
   1617 
   1618                 mem = SOC_MEM_UNIQUE_ACC(unit, L2_LEARN_CACHEm)[pipe];
   1619 
   1620                 valid = soc_mem_field32_get(unit, mem, entry, VALIDf);
   1621 
   1622                 /* Process valid entries only */
   1623                 if (valid) {
   1624                     LOG_DEBUG(BSL_LS_SOC_L2,
   1625                         (BSL_META_U(unit, "%s: Valid entry in pipe %d, index %d\n"), __FUNCTION__, pipe, i));
   1626 
   1627                     rv = _soc_th3_learn_cache_entry_process(unit, pipe, entry, i);
   1628                     if (SOC_FAILURE(rv)) {
   1629                         /* In case of failure, we do not exit the thread, since
   1630                          * learning will stop altogether. Assumption is that
   1631                          * the error may be transitory in nature
   1632                          */
   1633                         LOG_INFO(BSL_LS_SOC_COMMON,
   1634                             (BSL_META_U(unit, "Failed to add entry"
   1635                             " in pipe %d, index %d, rv = %d\n"),
   1636                             pipe, i, rv));
   1637                         continue;
   1638                     }
   1639 
   1640                     if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1641                         processed_cnt++;
   1642 
   1643                         /* Check if cache full condition exists. If so, clear
   1644                          * it only once in this processing cycle, for each pipe,
   1645                          * after 1st entry is processed.  (If cache fills again,
   1646                          * we will handle it in the next interrupt handling
   1647                          * cycle)
   1648                          */
   1649                         if (full_cleared == FALSE) {
   1650                             soc_field_t fld = L2_LEARN_CACHE_FULLf;
   1651 
   1652                             rv = _soc_th3_l2_learn_cache_status_check_clear(
   1653                                      unit, pipe, &fld, 1);
   1654 
   1655                             if (SOC_FAILURE(rv)) {
   1656                                 LOG_ERROR(BSL_LS_SOC_COMMON,
   1657                                     (BSL_META_U(unit, "Cache full bit"
   1658                                     " could not be cleared in pipe %d,"
   1659                                     " index %d, rv = %d\n"),
   1660                                     pipe, i, rv));
   1661                                 goto cleanup_exit;
   1662                             }
   1663 
   1664                             full_cleared = TRUE;
   1665                         }
   1666 
   1667                         if (thr_cleared == FALSE) {
   1668                             /* Clear thresold exceeded bit if number of valid
   1669                              * entries processed crossed the programmed
   1670                              * threshold value. If the threshold is set to 1,
   1671                              * we have already processed the entry, so we
   1672                              * immediately clear threshold bit
   1673                              */
   1674                             if ((processed_cnt > soc->lrn_cache_threshold) ||
   1675                                 (soc->lrn_cache_threshold == 1)) {
   1676 
   1677                                 soc_field_t fld =
   1678                                             L2_LEARN_CACHE_THRESHOLD_EXCEEDEDf;
   1679 
   1680                                 rv = _soc_th3_l2_learn_cache_status_check_clear(
   1681                                          unit, pipe, &fld, 1);
   1682 
   1683                                 if (SOC_FAILURE(rv)) {
   1684                                     LOG_ERROR(BSL_LS_SOC_COMMON,
   1685                                         (BSL_META_U(unit, "Threshold exc. bit"
   1686                                         " could not be cleared in pipe %d,"
   1687                                         " index %d, rv = %d\n"),
   1688                                         pipe, i, rv));
   1689                                     goto cleanup_exit;
   1690                                 }
   1691 
   1692                                 thr_cleared = TRUE;
   1693                             }
   1694                         }
   1695                     }
   1696                 }
   1697             }
   1698         }
   1699 
   1700 skip_processing:
   1701         if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1702             /* Enable learn cache interrupts for all pipes */
   1703             for (i = 8; i <= 15; i++) {
   1704                 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 1);
   1705             }
   1706         } else {
   1707             sal_usleep(interval);
   1708         }
   1709     }
   1710 
   1711 cleanup_exit:
   1712 /*
   1713     Check if this is required
   1714     if (SOC_CONTROL(unit)->l2x_lrn_shadow != NULL) {
   1715         shr_avl_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow);
   1716         SOC_CONTROL(unit)->l2x_lrn_shadow = NULL;
   1717     }
   1718 
   1719     if (SOC_CONTROL(unit)->l2x_lrn_shadow_mutex != NULL) {
   1720         sal_mutex_destroy(SOC_CONTROL(unit)->l2x_lrn_shadow_mutex);
   1721         SOC_CONTROL(unit)->l2x_lrn_shadow_mutex = NULL;
   1722     }
   1723 */
   1724 
   1725     if (buffer != NULL) {
   1726         soc_cm_sfree(unit, buffer);
   1727     }
   1728     soc->l2x_learn_pid = SAL_THREAD_ERROR;
   1729     sal_thread_exit(0);
   1730 }
   1731 
   1732 /*
   1733  * Function:
   1734  * 	soc_th3_l2_learn_start
   1735  * Purpose:
   1736  *   	Start l2 learn thread
   1737  * Parameters:
   1738  *	unit - unit number.
   1739  * Returns:
   1740  *	SOC_E_XXX
   1741  * Notes:
   1742  * soc_th3_l2_learn_alloc_resources must be called before calling this function
   1743  * for the _first_ time
   1744  */
   1745 int
   1746 soc_th3_l2_learn_thread_start(int unit, int interval)
   1747 {
   1748     soc_control_t *soc = SOC_CONTROL(unit);
   1749     uint32 reg_val = 0;
   1750     int pri = SOC_TH3_LRN_THREAD_PRI_DEFAULT;
   1751 
   1752     if (soc->l2x_learn_interval != 0) {
   1753         SOC_IF_ERROR_RETURN(soc_th3_l2_learn_thread_stop(unit));
   1754     }
   1755 
   1756     SOC_CONTROL_LOCK(unit);
   1757     sal_snprintf(soc->l2x_learn_name, sizeof (soc->l2x_age_name), "L2Lrn.%d",
   1758                  unit);
   1759 
   1760     if (soc->l2x_learn_pid == SAL_THREAD_ERROR) {
   1761         soc_th3_l2x_lrn_mode_t mode;
   1762 
   1763         soc_cm_get_id(unit, &dev_id, &rev_id);
   1764 
   1765         if (soc_property_get(unit, spn_L2XLRN_INTR_EN,
   1766                              SOC_TH3_LRN_CACHE_INTR_CTL_DEFAULT)) {
   1767             mode = L2_LRN_MODE_INTR;
   1768         } else {
   1769             mode = L2_LRN_MODE_POLL;
   1770         }
   1771 
   1772         /* Always polled mode for simulation */
   1773         if (SAL_BOOT_BCMSIM) {
   1774             mode = L2_LRN_MODE_POLL;
   1775         }
   1776 
   1777         soc->l2x_lrn_mode = mode;
   1778 
   1779         /* Do not use clear-on-read by default, unless user wants it */
   1780         soc->lrn_cache_clr_on_rd = soc_property_get(unit,
   1781                                        spn_L2XLRN_CLEAR_ON_READ,
   1782                                        SOC_TH3_LRN_CACHE_CLR_ON_RD_DEFAULT);
   1783 
   1784         soc->l2x_learn_interval = interval;
   1785 
   1786         if (interval == 0) {
   1787             SOC_CONTROL_UNLOCK(unit);
   1788             return SOC_E_NONE;
   1789         }
   1790 
   1791         /* Set initial values for learn cache operation */
   1792         SOC_IF_ERROR_RETURN(READ_L2_LEARN_COPY_CACHE_CTRLr(unit, &reg_val));
   1793 
   1794         soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1795                           L2_LEARN_CACHE_ENf, 0x1);
   1796         soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1797                           CLEAR_ON_READ_ENf,
   1798                           (uint32)(soc->lrn_cache_clr_on_rd));
   1799 
   1800         if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1801             /*
   1802              * 1. Set default interrupt threshold (from soc property, or soc)
   1803              * 2. Mark all cache entries as 'invalid' - done below this 'if'
   1804              * 3. Enable l2 learn cache interrupt.
   1805              */
   1806             soc->lrn_cache_threshold = soc_property_get(unit,
   1807                                            spn_L2XLRN_INTR_THRESHOLD,
   1808                                            SOC_TH3_LRN_CACHE_THRESHOLD_DEFAULT);
   1809 
   1810             /* A value of 0 or less is illegal. Also a value above 16 is
   1811              * illegal, flag error
   1812              */
   1813             if ((soc->lrn_cache_threshold <= 0) ||
   1814                 (soc->lrn_cache_threshold > 16)) {
   1815                 LOG_ERROR(BSL_LS_SOC_COMMON, (BSL_META_U(unit,
   1816                           "soc_th3_l2_learn_start: Illegal value of intr"
   1817                           " threshold: %d\n"), soc->lrn_cache_threshold));
   1818                 return SOC_E_CONFIG;
   1819             }
   1820 
   1821             /* By default, generate interrupt only when # cache entries equals
   1822              * the programmed threshold value. For generating interrupt on each
   1823              * learn event, user may set spn_L2XLRN_INTR_THRESHOLD to 1 
   1824              */
   1825             soc->lrn_cache_intr_ctl = 0x0;
   1826 
   1827             soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1828                               CACHE_INTERRUPT_CTRLf,
   1829                               (uint32)(soc->lrn_cache_intr_ctl));
   1830 
   1831             soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1832                               CACHE_INTERRUPT_THRESHOLDf,
   1833                               (uint32)(soc->lrn_cache_threshold));
   1834 
   1835         } else {
   1836            soc->lrn_cache_intr_ctl = 0x0;
   1837 
   1838            /* In polled mode, set these fields to reset values */
   1839            soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1840                              CACHE_INTERRUPT_CTRLf,
   1841                              (uint32)(soc->lrn_cache_intr_ctl));
   1842 
   1843            soc_reg_field_set(unit, L2_LEARN_COPY_CACHE_CTRLr, &reg_val,
   1844                              CACHE_INTERRUPT_THRESHOLDf, 0x8);
   1845 
   1846         }
   1847 
   1848         SOC_IF_ERROR_RETURN(WRITE_L2_LEARN_COPY_CACHE_CTRLr(unit, reg_val));
   1849 
   1850         /* Clear all entries of L2 learn cache */
   1851         SOC_IF_ERROR_RETURN(soc_th3_l2_learn_cache_clear(unit));
   1852 
   1853         /* Reset cache status bits */
   1854         SOC_IF_ERROR_RETURN(soc_th3_l2_learn_cache_status_clear(unit));
   1855 
   1856         pri = soc_property_get(unit, spn_L2XLRN_THREAD_PRI,
   1857                                SOC_TH3_LRN_THREAD_PRI_DEFAULT);
   1858 
   1859         /* Make sure that learn cache interrupt is enabled in CMICx,
   1860          * later in the main initialization sequence
   1861          */
   1862         soc->l2x_learn_pid = sal_thread_create(soc->l2x_learn_name,
   1863                                                SAL_THREAD_STKSZ, pri,
   1864                                                _soc_th3_l2_learn_process,
   1865                                                INT_TO_PTR(unit));
   1866 
   1867         if (soc->l2x_learn_pid == SAL_THREAD_ERROR) {
   1868             LOG_ERROR(BSL_LS_SOC_COMMON,
   1869                       (BSL_META_U(unit,
   1870                            "soc_th3_l2_learn_start: Could not start L2 learn"
   1871                            " thread\n")));
   1872             SOC_CONTROL_UNLOCK(unit);
   1873             return SOC_E_MEMORY;
   1874         }
   1875     }
   1876 
   1877     SOC_CONTROL_UNLOCK(unit);
   1878 
   1879     /* More programming might be reqd depending on learn cache en/dis setting */
   1880 
   1881     return SOC_E_NONE;
   1882 }
   1883 
   1884 
   1885 int
   1886 soc_th3_l2_learn_thread_stop(int unit)
   1887 {
   1888     soc_control_t   *soc = SOC_CONTROL(unit);
   1889     int rv = SOC_E_NONE;
   1890     soc_timeout_t   to;
   1891     sal_usecs_t interval;
   1892 
   1893     LOG_INFO(BSL_LS_SOC_ARL, (BSL_META_U(unit, "Stopping learn"
   1894                                          " thread: unit=%d\n"), unit));
   1895 
   1896    /* Save interval to wait for thread to wake up again */
   1897    if (SAL_BOOT_SIMULATION) {
   1898         /* Allow more time on simulation, similar to other devices */
   1899         interval = 30 * 1000000;
   1900     } else {
   1901         interval = 10 * 1000000;
   1902     }
   1903 
   1904     SOC_CONTROL_LOCK(unit);
   1905     soc->l2x_learn_interval = 0;  /* Request exit */
   1906     SOC_CONTROL_UNLOCK(unit);
   1907 
   1908     if (soc->l2x_learn_pid != SAL_THREAD_ERROR) {
   1909 
   1910         if (soc->l2x_lrn_mode == L2_LRN_MODE_INTR) {
   1911             int i;
   1912 
   1913             /* Disable learn cache interrupts from all pipes */
   1914             for (i = 8; i <= 15; i++) {
   1915                 (void)_soc_th3_lrn_cache_intr_configure(unit, i, 0);
   1916             }
   1917         }
   1918 
   1919         /* Wake up thread so it will check the exit flag */
   1920         /*sal_sem_give(soc->arl_notify); Check if notification to learn thread
   1921           is required */
   1922 
   1923         /* Give thread a few seconds to wake up and exit */
   1924         soc_timeout_init(&to, interval, 0);
   1925 
   1926         LOG_INFO(BSL_LS_SOC_COMMON,
   1927                  (BSL_META_U(unit, "Learn thread stop: Wait may be longer if"
   1928                  " cfg polling interval is high, cfg interval = %u\n"),
   1929                  interval));
   1930 
   1931         while (soc->l2x_learn_pid != SAL_THREAD_ERROR) {
   1932             if (soc_timeout_check(&to)) {
   1933                 LOG_ERROR(BSL_LS_SOC_L2,
   1934                           (BSL_META_U(unit, "Learn thread did not stop\n")));
   1935                 rv = SOC_E_INTERNAL;
   1936                 break;
   1937             }
   1938         }
   1939     }
   1940 
   1941     return (rv);
   1942 }
   1943 
   1944 int
   1945 soc_th3_l2_learn_thread_running(int unit, sal_usecs_t* interval)
   1946 {
   1947     soc_control_t *soc = SOC_CONTROL(unit);
   1948 
   1949     if (soc->l2x_learn_pid != SAL_THREAD_ERROR) {
   1950         if (interval != NULL) {
   1951             *interval = soc->l2x_learn_interval;
   1952         }
   1953     }
   1954 
   1955     return(soc->l2x_learn_pid != SAL_THREAD_ERROR);
   1956 }
   1957 #endif /* BCM_XGS_SWITCH_SUPPORT */
   1958 #endif /* BCM_TOMAHAWK3_SUPPORT */