root/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/device/lm_dcbx_mp.c
/*******************************************************************************
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2014 QLogic Corporation
 * The contents of this file are subject to the terms of the
 * QLogic End User License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
 * QLogic_End_User_Software_License.txt
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *
 * Module Description:
 *
 *
 * History:
 *    28/06/11 Shay Haroush    Inception.
 ******************************************************************************/

#include "lm5710.h"
#include "lm_dcbx_mp.h"

/*******************************************************************************
 * Constants.
 ******************************************************************************/
#define MAX_NUM_OF_ACTIVE_ETH_CONS_PER_COS(_pdev)       LM_SB_CNT(_pdev)
#define ETH_CHAIN_COS0_START_OFFSET(_pdev)              (0)
#define ETH_CHAIN_COS1_START_OFFSET(_pdev)              (ETH_CHAIN_COS0_START_OFFSET(_pdev) + LM_SB_CNT(_pdev) + MAX_NON_RSS_CHAINS)
#define ETH_CHAIN_COS2_START_OFFSET(_pdev)              (ETH_CHAIN_COS1_START_OFFSET(_pdev) + LM_SB_CNT(_pdev))

/*******************************************************************************
 * Defines.
 ******************************************************************************/
#define ETH_IS_CHAIN_IN_COS0_RANGE(_pdev, _chain)   ( lm_mp_eth_is_chain_in_cosx_range(_pdev, _chain, ETH_CHAIN_COS0_START_OFFSET(_pdev)))
#define ETH_IS_CHAIN_IN_COS1_RANGE(_pdev, _chain)   ( lm_mp_eth_is_chain_in_cosx_range(_pdev, _chain, ETH_CHAIN_COS1_START_OFFSET(_pdev)))
#define ETH_IS_CHAIN_IN_COS2_RANGE(_pdev, _chain)   ( lm_mp_eth_is_chain_in_cosx_range(_pdev, _chain, ETH_CHAIN_COS2_START_OFFSET(_pdev)))
#define ETH_CID_COSX_END_OFFSET(_pdev, _val)        (_val + MAX_NUM_OF_ACTIVE_ETH_CONS_PER_COS(pdev))

/**
 * @description
 * Check if chain is in COS range based on COS start offset. 
 * @param chain 
 * @param cos_start_offset 
 * 
 * @return u8_t 
 */
STATIC u8_t 
lm_mp_eth_is_chain_in_cosx_range(
    IN lm_device_t  *pdev,
    IN const u32_t  chain, 
    IN const u32_t  cos_start_offset)
{
    if((cos_start_offset <= chain) &&
      (chain < ETH_CID_COSX_END_OFFSET(pdev, cos_start_offset)))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
/**
 * @description
 * Get COS number based on chain. 
 * If the chain doesn't belong to a specific COS (e.g. ISCSI L2 
 * chain) 
 * @param pdev 
 * @param chain 
 * 
 * @return u8_t 
 */
u8_t 
lm_mp_cos_from_chain(IN struct _lm_device_t *pdev, 
                     IN const u32_t         chain)
{
    // Cos 0 is the default
    u8_t cos = ETH_MP_MAX_COS_SUPPORTED;

    if (ETH_IS_CHAIN_IN_COS0_RANGE(pdev, chain) )
    {
        cos = 0;
    }
    else if (ETH_IS_CHAIN_IN_COS1_RANGE(pdev, chain) )
    {
        cos = 1;
    }
    else if (ETH_IS_CHAIN_IN_COS2_RANGE(pdev, chain) )
    {
        cos = 2;
    }
    else
    {
        DbgMessage(pdev, INFORMi|INFORMl2sp, " lm_mp_cos_from_chain:  ");
    }
    return cos;
}
/**
 * @description
 * Return chain type.
 * @param pdev 
 * @param chain 
 * 
 * @return lm_chain_type_t 
 */
lm_chain_type_t
lm_mp_get_chain_type(IN struct _lm_device_t   *pdev, 
                     IN const u32_t           chain)
{
    const u8_t cos = lm_mp_cos_from_chain(pdev, chain);

    if (0 == cos)
    {
        return lm_chain_type_cos_reg;
    }
    else if (cos < ETH_MP_MAX_COS_SUPPORTED)
    {
        return lm_chain_type_cos_tx_only;
    }
    else
    {
        return lm_chain_type_not_cos;
    }
}
/**
 * @description
 * GET L2 chain COS start offset.
 * @param pdev 
 * @param cos 
 * 
 * @return u8_t 
 */
STATIC u8_t
lm_mp_get_eth_chain_cosx_start_offset(IN struct _lm_device_t    *pdev, 
                                      IN const u8_t             cos)
{
    u8_t cosx_start_offset = 0;

    switch(cos)
    {
    case 0:
        cosx_start_offset = ETH_CHAIN_COS0_START_OFFSET(pdev);
        break;
    case 1:
        cosx_start_offset = ETH_CHAIN_COS1_START_OFFSET(pdev);
        break;
    case 2:
        cosx_start_offset = ETH_CHAIN_COS2_START_OFFSET(pdev);
        break;
    default:
        DbgBreakMsg("invalid cos");
        cosx_start_offset = ETH_CHAIN_COS0_START_OFFSET(pdev);
    }

    return cosx_start_offset;
}
/**
 * @description
 * Get regular chain from chain. 
 * If chain isn't a COS chain(e.g. ISCSI L2) than return 
 * original value. 
 * @param pdev 
 * @param chain 
 * 
 * @return u32_t 
 */
u32_t 
lm_mp_get_reg_chain_from_chain(IN struct _lm_device_t   *pdev, 
                               IN u32_t                 chain)
{
    const u8_t cos          = lm_mp_cos_from_chain(pdev, chain);
    u8_t cosx_start_offset  = 0;

    if( cos >= ETH_MP_MAX_COS_SUPPORTED)
    {
        return chain;
    }

    cosx_start_offset = 
        lm_mp_get_eth_chain_cosx_start_offset(pdev,cos);

    chain -= cosx_start_offset;

    return chain;
}
/**
 * @description
 * Get COS chain from regular chain.
 * @param pdev 
 * @param chain 
 * @param cos 
 * 
 * @return u32_t 
 */
u8_t
lm_mp_get_cos_chain_from_reg_chain(
    IN struct _lm_device_t  *pdev, 
    INOUT u8_t              chain,
    INOUT const u8_t        cos)
{
    u8_t cosx_start_offset = 0;

    if( cos >= ETH_MP_MAX_COS_SUPPORTED)
    {
        return chain;
    }

    cosx_start_offset = 
        lm_mp_get_eth_chain_cosx_start_offset(pdev,cos);

    chain += cosx_start_offset;

    return chain;
}
/**
 * @description
 * Get max cos chain used.
 * @param pdev 
 * 
 * @return u32_t 
 */
u8_t
lm_mp_max_cos_chain_used(
    IN struct _lm_device_t  *pdev)
{
    const u8_t max_num_cos = lm_dcbx_cos_max_num(pdev);
    const u8_t cosx_start_offset = 
        lm_mp_get_eth_chain_cosx_start_offset(pdev, max_num_cos -1);
    const u8_t max_chain_used = ETH_CID_COSX_END_OFFSET(pdev, cosx_start_offset);

    DbgBreakIf(FALSE == MM_DCB_MP_L2_IS_ENABLE(pdev));
    DbgBreakIf(1 == max_num_cos);

    return max_chain_used;
}