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

cmicx_link.c (25501B)


      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  * Purpose: CMICx link scan module
      8  */
      9 #include <sal/core/boot.h>
     10 #include <sal/core/libc.h>
     11 #include <sal/core/sync.h>
     12 #include <shared/alloc.h>
     13 #include <shared/bsl.h>
     14 
     15 #if defined (BCM_ESW_SUPPORT) || defined(BCM_DNXF_SUPPORT) || defined(BCM_DNX_SUPPORT)
     16 #include <soc/drv.h>
     17 #include <soc/cm.h>
     18 #include <soc/cmic.h>
     19 #ifdef BCM_CMICX_SUPPORT
     20 #include <shared/cmicfw/iproc_mbox.h>
     21 #include <shared/cmicfw/iproc_m0ssq.h>
     22 #include <shared/cmicfw/iproc_fwconfig.h>
     23 #include <shared/cmicfw/cmicx_link_defs.h>
     24 #include <shared/cmicfw/cmicx_link.h>
     25 #include <shared/cmicfw/m0_ver.h>
     26 
     27 static soc_iproc_m0ssq_shmem_t *ls_shmem = NULL;
     28 
     29 static uint32 soc_iproc_sysreg_base_get(int unit)
     30 {
     31    if (SOC_IS_HELIX5(unit)) {
     32       return IPROC_MDIO_SYS_BASE_HX5;
     33    } else {
     34       return IPROC_MDIO_SYS_BASE;
     35    }
     36 }
     37 
     38 uint32 soc_cmicx_miim_phy_reg_read(int unit, int index)
     39 {
     40     uint32 base, val;
     41     uint32 offset = MIIM_PHY_LINK_STATUS0_OFFSET + (index * 4);
     42 
     43     base = soc_iproc_sysreg_base_get(unit);
     44     soc_iproc_getreg(unit, (base + offset), &val);
     45 
     46     return val;
     47 }
     48 
     49 void soc_cmicx_miim_phy_reg_write(int unit, int index, uint32 data)
     50 {
     51     uint32 base;
     52     uint32 offset = MIIM_PHY_LINK_STATUS0_OFFSET + (index * 4);
     53 
     54     base = soc_iproc_sysreg_base_get(unit);
     55     soc_iproc_setreg(unit, (base + offset), data);
     56 }
     57 
     58 
     59 int soc_iproc_linkscan_process_message(soc_iproc_mbox_info_t *chan, soc_iproc_mbox_msg_t *msg);
     60 
     61 /*
     62  * Function:
     63  *      _soc_iproc_l2p_pbmp_update
     64  * Purpose:
     65  *      Convert given port bitmap from logical to physical port numbering
     66  * Parameters:
     67  *      unit number
     68  *      port bitmap based on logical port numbering
     69  *      port bitmap converted to physical port numbering
     70  * Returns:
     71  *      none
     72  */
     73 STATIC
     74 void _soc_iproc_l2p_pbmp_update(int unit, soc_pbmp_t logical_pbmp, ls_phy_pbmp_t *phy_pbmp)
     75 {
     76     soc_port_t port;
     77     int phy_port = 0;
     78     soc_info_t *si;
     79 
     80     si = &SOC_INFO(unit);
     81 
     82     SOC_PBMP_ITER(logical_pbmp, port) {
     83         phy_port = si->port_l2p_mapping[port];
     84         LS_PHY_PBMP_PORT_ADD(*phy_pbmp, phy_port);
     85     }
     86 
     87     return;
     88 }
     89 
     90 /*
     91  * Function:
     92  *      _soc_iproc_p2l_pbmp_update
     93  * Purpose:
     94  *      Convert given port bitmap from physical to logical port numbering
     95  * Parameters:
     96  *      unit number
     97  *      port bitmap based on physical port numbering
     98  *      port bitmap converted to logical port numbering
     99  * Returns:
    100  *      none
    101  */
    102 STATIC
    103 void _soc_iproc_p2l_pbmp_update(int unit, ls_phy_pbmp_t phy_pbmp, soc_pbmp_t *logical_pbmp)
    104 {
    105     soc_port_t phy_port;
    106     int port = 0;
    107     soc_info_t *si;
    108 
    109     si = &SOC_INFO(unit);
    110 
    111     LS_PHY_PBMP_ITER(phy_pbmp, phy_port) {
    112         port = si->port_p2l_mapping[phy_port];
    113         SOC_PBMP_PORT_ADD(*logical_pbmp, port);
    114     }
    115 
    116     return;
    117 }
    118 
    119 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))
    120 /*
    121  * Function:
    122  *      _soc_iproc_add_cpu_port_to_pbmp
    123  * Purpose:
    124  *      Add cpu port(bit 0)to given port bitmap and adjust rest of ports accordingly
    125  *      This function is needed since port bitmap returned by FW taking into account
    126  *      cpu port at bit 0 which is required by XGS SDK code, but not required by
    127  *      DNX SDK code. So for DNX device, the function must be needed.
    128  * Parameters:
    129  *      unit number
    130  *      pointer to port bitmap containing incoming port bitmap
    131  *      port bitmap to be modified to contain cpu port(bit 0)
    132  *
    133  * Returns:
    134  *      none
    135  */
    136 STATIC
    137 void _soc_iproc_add_cpu_port_to_pbmp(int unit, uint32 *data, ls_phy_pbmp_t *phy_pbmp)
    138 {
    139     uint32 tmp1 = 0, tmp2 = 0, idx = 0;
    140 
    141     /* For 1st word, save bit 31 before left shift to add cpu port */
    142     tmp1 = (data[idx] & 0x80000000) >> 31;
    143     LS_PHY_PBMP_WORD_SET(*phy_pbmp, 0, (data[idx] << 1));
    144 
    145     for(idx = 1; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    146         /* Set saved bit 31 from previous to bit 0 of next word */
    147         tmp2 = (data[idx] << 1) | tmp1;
    148         /* Add updated word to pbmp to be returned */
    149         LS_PHY_PBMP_WORD_SET(*phy_pbmp, idx, tmp2);
    150         /* Save bit 31 from current word to be used in next iteration */
    151         tmp1 = (data[idx] & 0x80000000) >> 31;
    152     }
    153 
    154     return;
    155 }
    156 
    157 /*
    158  * Function:
    159  *      _soc_iproc_remove_cpu_port_from_pbmp
    160  * Purpose:
    161  *      Add cpu port(bit 0)to given port bitmap and adjust rest of ports accordingly
    162  *      This function is needed since port bitmap returned by FW taking into account
    163  *      cpu port at bit 0 which is required by XGS SDK code, but not required by
    164  *      DNX SDK code. So for DNX device, the function must be needed.
    165  * Parameters:
    166  *      unit number
    167  *      pointer to message data containing incoming port bitmap
    168  *      port bitmap to be modified to contain cpu port
    169  * Returns:
    170  *      none
    171  */
    172 STATIC
    173 void _soc_iproc_remove_cpu_port_from_pbmp(int unit, ls_phy_pbmp_t logical_pbmp, uint32 *data)
    174 {
    175     uint32 tmp = 0, idx = 0;
    176 
    177     for(idx = 0; idx < (LS_PHY_PBMP_WORD_MAX - 1); idx++) {
    178         /* Shift out last bit in current word */
    179         data[idx] = (LS_PHY_PBMP_WORD_GET(logical_pbmp, idx) >> 1);
    180         /* Get bit 0 from next word and move it to bit 31 of previous */
    181         tmp = LS_PHY_PBMP_WORD_GET(logical_pbmp, (idx + 1));
    182         tmp &= 0x1;
    183         tmp <<= 31;
    184         data[idx] |= tmp;
    185     }
    186 
    187     data[idx] = (LS_PHY_PBMP_WORD_GET(logical_pbmp, idx) >> 1);
    188 
    189     return;
    190 }
    191 #endif
    192 
    193 /*
    194  * Function:
    195  *      _soc_iproc_optimized_cpu_port_to_pbmp_add
    196  * Purpose:
    197  *      Add cpu port to given port bitmap and adjust rest of ports accordingly
    198  *      This function is needed since port bitmap returned by FW does not take
    199  *      into account cpu port at bit 0 which is required by rest of SDK code
    200  *      CPU port is not tracked by HW linkscan FW.
    201  * Parameters:
    202  *      unit number
    203  *      port bitmap to be modified to contain cpu port
    204  * Returns:
    205  *      none
    206  */
    207 STATIC
    208 void _soc_iproc_optimized_cpu_port_to_pbmp_add(int unit, ls_phy_pbmp_t *dev_pbmp)
    209 {
    210     uint32 tmp1 = 0, tmp2 = 0, idx = 0;
    211     uint32 reg1, reg2;
    212 
    213     /* DNX doesn't use CPU port at bit0, no need to shift. */
    214     if (SOC_IS_DNX(unit) || SOC_IS_DNXF(unit)) {
    215         for(idx = 0; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    216             reg1 = soc_cmicx_miim_phy_reg_read(unit, idx);
    217             LS_PHY_PBMP_WORD_SET(*dev_pbmp, idx, reg1);
    218         }
    219     } else {
    220         reg1 = soc_cmicx_miim_phy_reg_read(unit, idx);
    221 
    222         /* For 1st word, save bit 31 before left shift to add cpu port */
    223         tmp1 = (reg1 & 0x80000000) >> 31;
    224         LS_PHY_PBMP_WORD_SET(*dev_pbmp, 0, (reg1 << 1));
    225 
    226         for(idx = 1; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    227             /* Set saved bit 31 from previous to bit 0 of next word */
    228             reg2 = soc_cmicx_miim_phy_reg_read(unit, idx);
    229             tmp2 = (reg2 << 1) | tmp1;
    230             /* Add updated word to pbmp to be returned */
    231             LS_PHY_PBMP_WORD_SET(*dev_pbmp, idx, tmp2);
    232             /* Save bit 31 from current word to be used in next iteration */
    233             reg1 = soc_cmicx_miim_phy_reg_read(unit, idx);
    234             tmp1 = (reg1 & 0x80000000) >> 31;
    235         }
    236     }
    237     return;
    238 }
    239 /*
    240  * Function:
    241  *     soc_hx5_war_is_enable
    242  * Purpose:
    243  *     This function is only for HX5. It will check
    244  * if HX5HW-2279 is applicable for a given TSC core.
    245  *     Assume that port flexing won't change PMQ mode,
    246  * since PMQ qsgmii mode is not support flexing.
    247  * Parameters:
    248  *     unit number
    249  *     core Core number
    250  *          > 0, Initial WAR enable cache.
    251  * Returns:
    252  *     1 if core need WAR.
    253  */
    254 static int
    255 soc_hx5_war_is_enable(int unit, int core) {
    256 
    257    uint32 qmode = 0;
    258    uint32 otp_qmode = 0;
    259    static uint32 hx5_war_en[SOC_MAX_NUM_DEVICES] = {0};
    260    static uint32 hx5_war_en_valid[SOC_MAX_NUM_DEVICES] = {0};
    261 
    262    if (core < 0) {
    263        hx5_war_en_valid[unit] = 0;
    264    } else if (core > 2) {
    265        return 0;
    266    }
    267 
    268    if (hx5_war_en_valid[unit]) {
    269        return ((hx5_war_en[unit] & (1 << core)) != 0);
    270    }
    271 
    272    /* Read PMQ current mode */
    273    if (READ_TOP_MISC_GENERIC_CONTROLr(unit, &qmode) < 0) {
    274        return 0;
    275    };
    276 
    277    /* Read OTP (QSGMII_ENABLE) */
    278    READ_DMU_PCU_OTP_CONFIG_8r(unit, &otp_qmode);
    279 
    280    otp_qmode = (otp_qmode >> 25);
    281    otp_qmode = (otp_qmode & 0x7);
    282    qmode = qmode & 0x7;
    283 
    284    /*
    285     * if OTP (QSGMII_ENABLE) is not equal to PMQ current mode.
    286     * Then set "war enable" as 1
    287     */
    288    hx5_war_en[unit] = (otp_qmode ^ qmode);
    289    hx5_war_en_valid[unit] = 1;
    290 
    291    if (core < 0) {
    292        return (hx5_war_en[unit] != 0);
    293    } else {
    294        return ((hx5_war_en[unit] & (1 << core)) != 0);
    295    }
    296 
    297 }
    298 
    299 
    300 /*
    301  * Function:
    302  *      _soc_hx5_optimized_cpu_port_to_pbmp_add
    303  * Purpose:
    304  *      This function is only for HELIX5 with the same purpose with
    305  *      This function is needed since port bitmap returned by FW does not take
    306  *      into account cpu port at bit 0 which is required by rest of SDK code
    307  *      CPU port is not tracked by HW linkscan FW.
    308  * Parameters:
    309  *      unit number
    310  *      port bitmap to be modified to contain cpu port
    311  * Returns:
    312  *      none
    313  */
    314 STATIC
    315 void _soc_hx5_optimized_cpu_port_to_pbmp_add(int unit, ls_phy_pbmp_t *dev_pbmp)
    316 {
    317     uint32 tmp1 = 0, tmp2 = 0, idx = 0;
    318     uint32 reg1, reg2;
    319     pbmp_t lpbmp;
    320     soc_port_t phy_port;
    321     int port = 0;
    322     soc_info_t *si;
    323     link_status_entry_t link_status;
    324 
    325     si = &SOC_INFO(unit);
    326 
    327     READ_MIIM_INT_PHY_LINK_RAW_STATUSr(unit, idx, &reg1);
    328 
    329     /* For 1st word, save bit 31 before left shift to add cpu port */
    330     tmp1 = (reg1 & 0x80000000) >> 31;
    331     LS_PHY_PBMP_WORD_SET(*dev_pbmp, 0, (reg1 << 1));
    332 
    333     for(idx = 1; idx < SOC_REG_NUMELS(unit, MIIM_INT_PHY_LINK_RAW_STATUSr); idx++) {
    334 
    335         /* Set saved bit 31 from previous to bit 0 of next word */
    336         READ_MIIM_INT_PHY_LINK_RAW_STATUSr(unit, idx, &reg2);
    337         tmp2 = (reg2 << 1) | tmp1;
    338 
    339         /* Add updated word to pbmp to be returned */
    340         LS_PHY_PBMP_WORD_SET(*dev_pbmp, idx, tmp2);
    341 
    342         /* Save bit 31 from current word to be used in next iteration */
    343         READ_MIIM_INT_PHY_LINK_RAW_STATUSr(unit, idx, &reg1);
    344         tmp1 = (reg1 & 0x80000000) >> 31;
    345     }
    346 
    347     SOC_PBMP_CLEAR(lpbmp);
    348     if (SOC_FAILURE(READ_LINK_STATUSm(unit, IPIPE_BLOCK(unit), 0, &link_status))) {
    349         return;
    350     };
    351     sal_memcpy(&lpbmp, &link_status, sizeof(link_status));
    352 
    353     /* WAR for HX5 */
    354     for (phy_port = 1; phy_port <= 48; phy_port++)
    355     {
    356         if (soc_hx5_war_is_enable(unit, (phy_port-1)/16))
    357         {
    358            port = si->port_p2l_mapping[phy_port];
    359            if (port > 0) {
    360                if (PBMP_MEMBER(lpbmp, port))
    361                {
    362                   LS_PHY_PBMP_PORT_ADD(*dev_pbmp, phy_port);
    363                } else {
    364                   LS_PHY_PBMP_PORT_REMOVE(*dev_pbmp, phy_port);
    365                }
    366            }
    367         }
    368     }
    369 
    370     return;
    371 }
    372 /*
    373  * Function:
    374  *      soc_cmicx_linkscan_hw_link_cache_get
    375  * Purpose:
    376  *      Get the current HW link status from linkscan cached copy
    377  * Parameters:
    378  *      unit number
    379  * Returns:
    380  *      SOC_E_NONE
    381  */
    382 int soc_cmicx_linkscan_hw_link_cache_get(int unit, soc_pbmp_t *hw_link)
    383 {
    384     soc_control_t   *soc = SOC_CONTROL(unit);
    385     int idx;
    386 
    387     for(idx = 0; idx < _SHR_PBMP_WORD_MAX; idx++) {
    388         SOC_PBMP_WORD_SET(*hw_link, idx, SOC_PBMP_WORD_GET(soc->cmicx_link_stat, idx));
    389     }
    390 
    391     return SOC_E_NONE;
    392 }
    393 
    394 /*
    395  * Function:
    396  *      soc_iproc_link_status_update
    397  * Purpose:
    398  *      Notify the link status change update from Cortex-M0 to linkscan thread
    399  * Parameters:
    400  *      unit number
    401  *      Linkscan message from cortex-M0
    402  *      contain_cpu_bit  indicate the message include cpu port bit.
    403  * Returns:
    404  *      SOC_E_NONE
    405  */
    406 int soc_iproc_link_status_update(int unit, soc_iproc_mbox_msg_t *msg, int contain_cpu_bit)
    407 {
    408     soc_control_t   *soc = SOC_CONTROL(unit);
    409     ls_phy_pbmp_t phy_pbmp;
    410 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))
    411     ls_phy_pbmp_t updated_phy_pbm;
    412 #endif
    413     int idx = 0;
    414 
    415     /* Clear phy pbmp */
    416     LS_PHY_PBMP_CLEAR(phy_pbmp);
    417 
    418     /* Copy message payload */
    419     for (idx = 0; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    420         LS_PHY_PBMP_WORD_SET(phy_pbmp, idx, msg->data[idx]);
    421     }
    422 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))
    423     if (SOC_IS_DNX(unit) || SOC_IS_DNXF(unit)) {
    424        /*
    425         * DNX need to remove CPU port at bit0.
    426         * If it is in hw-config response,
    427         * CPU port bit0 are not included in msg
    428         */
    429         
    430         if (contain_cpu_bit)
    431         {
    432             LS_PHY_PBMP_CLEAR(updated_phy_pbm);
    433             _soc_iproc_remove_cpu_port_from_pbmp(unit, phy_pbmp, updated_phy_pbm.pbits);
    434             for(idx = 0; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    435                 LS_PHY_PBMP_WORD_SET(phy_pbmp, idx, LS_PHY_PBMP_WORD_GET(updated_phy_pbm, idx));
    436             }
    437         }
    438         LS_PHY_PBMP_CLEAR(soc->cmicx_link_stat);
    439     }
    440 #endif
    441     /* Convert physical pbmp to logical pbmp */
    442     _soc_iproc_p2l_pbmp_update(unit, phy_pbmp, &soc->cmicx_link_stat);
    443 
    444 #if defined (BCM_ESW_SUPPORT)
    445     if (SOC_IS_ESW(unit)) {
    446         /* Inform linkscan thread */
    447         cmicx_esw_linkscan_hw_interrupt(unit);
    448     }
    449 #endif
    450 
    451 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))
    452     if (SOC_IS_DNXF(unit) || SOC_IS_DNX(unit)) {
    453         /* Inform linkscan thread */
    454         cmicx_common_linkscan_hw_interrupt(unit);
    455     }
    456 #endif
    457 
    458     return SOC_E_NONE;
    459 }
    460 
    461 /*
    462  * Function:
    463  *      soc_iproc_linkscan_process_message
    464  * Purpose:
    465  *      Linkscan application handler to handle messages in running state.
    466  * Parameters:
    467  *      unit number
    468  *      Iproc mbox info
    469  *      Linkscan message from cortex-M0
    470  * Returns:
    471  *      SOC_E_xxx
    472  */
    473 int soc_iproc_linkscan_process_message(soc_iproc_mbox_info_t *chan, soc_iproc_mbox_msg_t *msg)
    474 {
    475     int rv = 0;
    476 
    477     if (NULL == msg) {
    478         return SOC_E_PARAM;
    479     }
    480 
    481     switch(msg->id) {
    482         case LS_LINK_STATUS_CHANGE:
    483             
    484             return soc_iproc_link_status_update(chan->unit, msg, 1);
    485         default:
    486             LOG_ERROR(BSL_LS_SOC_M0, (BSL_META_U(chan->unit, "Invalid M0 message id\n")));
    487             rv = IPROC_MBOX_ERR_INVCMD;
    488             break;
    489     }
    490 
    491     return rv;
    492 }
    493 
    494 /*
    495  * Function:
    496  *      soc_iproc_linkscan_msg_handler
    497  * Purpose:
    498  *      Linkscan message handler to handle messages from Cortex-M0 linkscan application.
    499  * Parameters:
    500  *      Registered parameter
    501  * Returns:
    502  *      SOC_E_xxx
    503  */
    504 int soc_iproc_linkscan_msg_handler(void *param)
    505 {
    506     int rv = 0, respidx;
    507     soc_iproc_mbox_msg_t *msg;
    508     soc_iproc_mbox_info_t *rxchan = (soc_iproc_mbox_info_t *)param;
    509 
    510     if (rxchan == NULL)
    511         return SOC_E_PARAM;
    512 
    513     while(!rv) {
    514         respidx = soc_iproc_data_recv(rxchan, &msg);
    515         if (respidx < 0) {
    516             rv = respidx;
    517             break;
    518         }
    519 
    520         if ((!IS_IPROC_RESP_READY(msg)) & (!IS_ASYNC_IPROC_STATUS(msg))) {
    521             soc_iproc_free(msg);
    522             break;
    523         } else {
    524             rv = soc_iproc_linkscan_process_message(rxchan, msg);
    525             LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(rxchan->unit, " Msg processed %d\n"), msg->id));
    526             soc_iproc_free(msg);
    527         }
    528     }
    529 
    530     return rv;
    531 }
    532 
    533 
    534 /*
    535  * Function:
    536  *      soc_cmicx_link_fw_config_init
    537  * Purpose:
    538  *      Initialize linkscan firmware config.
    539  * Parameters:
    540  *      unit number
    541  * Returns:
    542  *      SOC_E_xxx
    543  */
    544 int
    545 soc_cmicx_link_fw_config_init(int unit) {
    546 
    547     FWLINKSCAN_CTRLr_t ctrl;
    548 
    549     /* Get shared memory of firmware linkscan. */
    550     SOC_IF_ERROR_RETURN(soc_iproc_m0ssq_shmem_get(unit, "linkscan", &ls_shmem));
    551 
    552     /* Clear shared memory of firmware linkscan as zero. */
    553     SOC_IF_ERROR_RETURN(soc_iproc_m0ssq_shmem_clear(ls_shmem));
    554 
    555     /* Pause firmware linkscan. */
    556     FWLINKSCAN_CTRLr_CLR(ctrl);
    557     FWLINKSCAN_CTRLr_PAUSEf_SET(ctrl, 1);
    558     SOC_IF_ERROR_RETURN(WRITE_FWLINKSCAN_CTRLr(ls_shmem, ctrl));
    559 
    560     return SOC_E_NONE;
    561 }
    562 
    563 /*
    564  * Function:
    565  *      soc_cmicx_linkscan_hw_init
    566  * Purpose:
    567  *      Initialize linkscan mbox and handler.
    568  * Parameters:
    569  *      unit number
    570  * Returns:
    571  *      SOC_E_xxx
    572  */
    573 int soc_cmicx_linkscan_hw_init(int unit)
    574 {
    575     int rv = 0;
    576     soc_control_t   *soc = SOC_CONTROL(unit);
    577 
    578     if (!soc->iproc_m0ls_init_done) {
    579 
    580         /* Allocate mailbox. */
    581         SOC_IF_ERROR_RETURN(soc->ls_mbox_id = soc_iproc_mbox_alloc(unit, U0_LINKSCAN_APP));
    582         soc->ls_txmbox= &soc->iproc_mbox_info[soc->ls_mbox_id][IPROC_MBOX_TYPE_TX];
    583         soc->ls_rxmbox= &soc->iproc_mbox_info[soc->ls_mbox_id][IPROC_MBOX_TYPE_RX];
    584         rv = soc_iproc_mbox_handler_register(unit, soc->ls_mbox_id, soc_iproc_linkscan_msg_handler, (void *) soc->ls_rxmbox);
    585         if (rv < 0) {
    586             LOG_ERROR(BSL_LS_SOC_M0, (BSL_META_U(unit, "Linkscan Mbox handler register failed\n")));
    587             soc_iproc_mbox_free(unit, soc->ls_mbox_id);
    588             return rv;
    589         }
    590 
    591         soc->iproc_m0ls_init_done = 1;
    592     }
    593 
    594     return rv;
    595 }
    596 
    597 /*
    598  * Function:
    599  *      soc_cmicx_linkscan_hw_deinit
    600  * Purpose:
    601  *      Cleanup linkscan mbox and handler.
    602  * Parameters:
    603  *      unit number
    604  * Returns:
    605  *      SOC_E_xxx
    606  */
    607 int soc_cmicx_linkscan_hw_deinit(int unit)
    608 {
    609     int rv = 0;
    610     soc_control_t   *soc = SOC_CONTROL(unit);
    611 
    612     if (soc->iproc_m0ls_init_done) {
    613         soc_iproc_mbox_free(unit, soc->ls_mbox_id);
    614         soc->iproc_m0ls_init_done = 0;
    615     }
    616 
    617     return rv;
    618 }
    619 
    620 /* Max time for firmware to complete linkscan one time. (200ms) */
    621 #define LINKSCAN_TIME_MAX        (200000)
    622 
    623 /*
    624  * Function:
    625  *      soc_cmicx_linkscan_pause
    626  * Purpose:
    627  *      Pause firmware linkscan.
    628  * Parameters:
    629  *      unit number
    630  * Returns:
    631  *      SOC_E_xxx
    632  */
    633 int soc_cmicx_linkscan_pause(int unit)
    634 {
    635     FWLINKSCAN_STATUSr_t status;
    636     FWLINKSCAN_CTRLr_t ctrl;
    637     int run;
    638     soc_timeout_t to;
    639 
    640     LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "M0 Linkscan pause\n")));
    641 
    642     SOC_IF_ERROR_RETURN(READ_FWLINKSCAN_CTRLr(ls_shmem, &ctrl));
    643 
    644     /* if firmware linkscan is paused then return. */
    645     if (FWLINKSCAN_CTRLr_PAUSEf_GET(ctrl) != 0) {
    646         return SOC_E_NONE;
    647     }
    648 
    649     /* Pause firmware linkscan. */
    650     FWLINKSCAN_CTRLr_PAUSEf_SET(ctrl, 1);
    651     SOC_IF_ERROR_RETURN(WRITE_FWLINKSCAN_CTRLr(ls_shmem, ctrl));
    652 
    653     /* Init timeout object with LINKSCAN_TIME_MAX, polling period 100usec. */
    654     soc_timeout_init(&to, LINKSCAN_TIME_MAX, 10);
    655 
    656     /* Ensure firmware being paused. */
    657     do {
    658 
    659         /* Check if firmware linkscan is running. */
    660         SOC_IF_ERROR_RETURN(READ_FWLINKSCAN_STATUSr(ls_shmem, &status));
    661         run = FWLINKSCAN_STATUSr_RUNf_GET(status);
    662         if (run == 0) {
    663             return SOC_E_NONE;
    664         }
    665 
    666     } while (!soc_timeout_check(&to));
    667 
    668     return SOC_E_TIMEOUT;
    669 }
    670 
    671 /*
    672  * Function:
    673  *      soc_cmicx_linkscan_continue
    674  * Purpose:
    675  *      Continue/start firmware linkscan.
    676  * Parameters:
    677  *      unit number
    678  * Returns:
    679  *      SOC_E_xxx
    680  */
    681 int soc_cmicx_linkscan_continue(int unit)
    682 {
    683     FWLINKSCAN_CTRLr_t ctrl;
    684 
    685     LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "M0 Linkscan continue\n")));
    686 
    687     SOC_IF_ERROR_RETURN(READ_FWLINKSCAN_CTRLr(ls_shmem, &ctrl));
    688 
    689     /* if firmware linkscan is not paused then return. */
    690     if (FWLINKSCAN_CTRLr_PAUSEf_GET(ctrl) == 0) {
    691         return SOC_E_NONE;
    692     }
    693 
    694     /* Continue/clear pause of firmware linkscan. */
    695     FWLINKSCAN_CTRLr_PAUSEf_SET(ctrl, 0);
    696     SOC_IF_ERROR_RETURN(WRITE_FWLINKSCAN_CTRLr(ls_shmem, ctrl));
    697 
    698     return SOC_E_NONE;
    699 
    700 }
    701 
    702 /*
    703  * Function:
    704  *       soc_cmicx_linkscan_heartbeat
    705  * Purpose:
    706  *       Send LS_HW_HEARTBEAT message to Cortex-M0 linkscan application.
    707  * Parameters:
    708  *       unit number
    709  * Returns:
    710  *       SOC_E_XXX
    711  */
    712 int soc_cmicx_linkscan_heartbeat(int unit, soc_ls_heartbeat_t *ls_heartbeat)
    713 {
    714     soc_iproc_mbox_msg_t *msg, *resp;
    715     int rv;
    716 
    717     if (!SOC_CONTROL(unit)->iproc_mbox_init_done) {
    718         LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "Mbox init not done\n")));
    719         return SOC_E_NONE;
    720     }
    721 
    722     LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "M0 Linkscan heartbeat\n")));
    723 
    724     sal_memset(ls_heartbeat, 0, sizeof(soc_ls_heartbeat_t));
    725 
    726     ls_heartbeat->host_fw_version = (HOST_FW_VER_MAJOR << 16 | HOST_FW_VER_MINOR);
    727 
    728     msg = soc_iproc_alloc(sizeof(soc_iproc_mbox_msg_t) + sizeof(soc_ls_heartbeat_t));
    729     if (msg == NULL)
    730         return SOC_E_MEMORY;
    731 
    732     resp = soc_iproc_alloc(sizeof(soc_iproc_mbox_msg_t) + sizeof(soc_ls_heartbeat_t));
    733     if (resp == NULL) {
    734         soc_iproc_free(msg);
    735         return SOC_E_MEMORY;
    736     }
    737 
    738     msg->id = LS_HW_HEARTBEAT;
    739     msg->flags = IPROC_SYNC_MSG | IPROC_RESP_REQUIRED;
    740     msg->size = sizeof(soc_ls_heartbeat_t) / sizeof(uint32);
    741     if (sizeof(soc_ls_heartbeat_t) % sizeof(uint32)) {
    742         msg->size++;
    743     }
    744 
    745     /* Copy message data */
    746     sal_memcpy((void *)msg->data, (void *)ls_heartbeat, sizeof(soc_ls_heartbeat_t));
    747 
    748     rv = soc_iproc_data_send_wait(SOC_CONTROL(unit)->ls_txmbox, msg, resp);
    749     if (rv == IPROC_MSG_SUCCESS) {
    750         if (IS_IPROC_RESP_READY(resp)) {
    751             if (IS_IPROC_RESP_SUCCESS(resp)) {
    752                 /* Copy response data */
    753                 sal_memcpy((void *)ls_heartbeat, (void *)resp->data, sizeof(soc_ls_heartbeat_t));
    754                 rv = SOC_E_NONE;
    755             } else {
    756                 rv = resp->data[0];
    757             }
    758         }
    759     }
    760 
    761     soc_iproc_free(msg);
    762     soc_iproc_free(resp);
    763 
    764     return rv;
    765 }
    766 
    767 /*
    768  * Function:
    769  *      soc_cmicx_linkscan_hw_link_get
    770  * Purpose:
    771  *      Obtain actual device link status from miim registers
    772  * Parameters:
    773  *      unit number
    774  * Returns:
    775  *      SOC_E_xxx
    776  */
    777 int soc_cmicx_linkscan_hw_link_get(int unit, soc_pbmp_t *hw_link)
    778 {
    779     int rv;
    780     ls_phy_pbmp_t phy_hw_pbmp;
    781 
    782     LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "M0 Linkscan hw link get\n")));
    783 
    784     if (SOC_IS_HELIX5(unit)) {
    785         /* Add CPU port to port bitmap since M0 FW responds without it */
    786         _soc_hx5_optimized_cpu_port_to_pbmp_add(unit, &phy_hw_pbmp);
    787     } else {
    788         /* Add CPU port to port bitmap since M0 FW responds without it */
    789         _soc_iproc_optimized_cpu_port_to_pbmp_add(unit, &phy_hw_pbmp);
    790     }
    791 
    792     /* Convert physical to logical port numbering */
    793     _soc_iproc_p2l_pbmp_update(unit, phy_hw_pbmp, hw_link);
    794 
    795     rv = SOC_E_NONE;
    796     return rv;
    797 }
    798 
    799 /*
    800  * Function:
    801  *       soc_cmicx_linkscan_config
    802  * Purpose:
    803  *       Send LS_HW_CONFIG message to Cortex-M0 linkscan application.
    804  * Parameters:
    805  *       unit number
    806  *       hw_mii_pbm - Port bit map of ports to scan with MIIM registers
    807  * Returns:
    808  *       SOC_E_XXX
    809  */
    810 int soc_cmicx_linkscan_config(int unit, soc_pbmp_t *hw_mii_pbm)
    811 {
    812     soc_iproc_mbox_msg_t *msg, *resp;
    813     int rv;
    814     soc_pbmp_t empty_pbmp;
    815     ls_phy_pbmp_t phy_mii_pbm;
    816 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))  
    817     ls_phy_pbmp_t updated_phy_pbm;
    818 #endif
    819     int idx = 0;
    820 
    821     LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "M0 Linkscan config\n")));
    822 
    823     if (SOC_IS_HELIX5(unit)) {
    824         /* Check HX5 WAR enable then cache the result. */
    825         soc_hx5_war_is_enable(unit, -1);
    826     }
    827 
    828     if (!SOC_CONTROL(unit)->iproc_mbox_init_done) {
    829         LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "Mbox init not done\n")));
    830         return SOC_E_NONE;
    831     }
    832 
    833     SOC_PBMP_CLEAR(empty_pbmp);
    834     if (SOC_PBMP_EQ(*hw_mii_pbm, empty_pbmp)) {
    835         LOG_VERBOSE(BSL_LS_SOC_M0, (BSL_META_U(unit, "HW pbmp is empty..skip\n")));
    836         return SOC_E_NONE;
    837     }
    838 
    839     msg = soc_iproc_alloc(sizeof(soc_iproc_mbox_msg_t) + sizeof(ls_phy_pbmp_t));
    840     if (msg == NULL) {
    841         LOG_ERROR(BSL_LS_SOC_M0, (BSL_META_U(unit, "HW Linkscan Mbox msg alloc failed\n")));
    842         return SOC_E_MEMORY;
    843     }
    844 
    845     resp = soc_iproc_alloc(sizeof(soc_iproc_mbox_msg_t) + sizeof(ls_phy_pbmp_t));
    846     if (resp == NULL) {
    847         LOG_ERROR(BSL_LS_SOC_M0, (BSL_META_U(unit, "HW Linkscan Mbox resp alloc failed\n")));
    848         soc_iproc_free(msg);
    849         return SOC_E_MEMORY;
    850     }
    851 
    852     msg->id = LS_HW_CONFIG;
    853     msg->flags = IPROC_SYNC_MSG | IPROC_RESP_REQUIRED;
    854     msg->size = sizeof(ls_phy_pbmp_t) / sizeof(uint32);
    855     if (sizeof(ls_phy_pbmp_t) % sizeof(uint32)) {
    856         msg->size++;
    857     }
    858 
    859     LS_PHY_PBMP_CLEAR(phy_mii_pbm);
    860 
    861     /* Convert incoming logical pbmp to physical pbmp */
    862     _soc_iproc_l2p_pbmp_update(unit, *hw_mii_pbm, &phy_mii_pbm);
    863 
    864 #if (defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT))
    865     /* DNX need to add CPU port at bit0. */
    866     if (SOC_IS_DNX(unit) || SOC_IS_DNXF(unit)) {
    867          LS_PHY_PBMP_CLEAR(updated_phy_pbm);
    868         _soc_iproc_add_cpu_port_to_pbmp(unit, phy_mii_pbm.pbits, &updated_phy_pbm);
    869         for(idx = 0; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    870             LS_PHY_PBMP_WORD_SET(phy_mii_pbm, idx, LS_PHY_PBMP_WORD_GET(updated_phy_pbm, idx));
    871         }
    872     }
    873 #endif
    874     /* Copy message payload */
    875     for (idx = 0; idx < LS_PHY_PBMP_WORD_MAX; idx++) {
    876         msg->data[idx] = LS_PHY_PBMP_WORD_GET(phy_mii_pbm, idx);
    877     }
    878 
    879     /* Send message to M0 */
    880     rv = soc_iproc_data_send_wait(SOC_CONTROL(unit)->ls_txmbox, msg, resp);
    881     if (rv == IPROC_MSG_SUCCESS) {
    882         if (IS_IPROC_RESP_READY(resp)) {
    883             if (IS_IPROC_RESP_SUCCESS(resp)) {
    884                 rv = SOC_E_NONE;
    885                 /* Update linkscan thread with link status for configured port */
    886                 
    887                 soc_iproc_link_status_update(unit, resp, 0);
    888             } else {
    889                 rv = resp->data[0];
    890             }
    891         }
    892     }
    893 
    894     soc_iproc_free(msg);
    895     soc_iproc_free(resp);
    896 
    897     return rv;
    898 }
    899 #endif /*BCM_CMICX_SUPPORT*/
    900 #endif /*BCM_ESW_SUPPORT*/