/* bnx2x_dcb.c: Broadcom Everest network driver.
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available
* at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*
*
*
*/
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/errno.h>
#ifdef BCM_DCBNL
#include <linux/dcbnl.h>
#endif
#include "bnx2x.h"
#include "bnx2x_cmn.h"
#include "bnx2x_dcb.h"
/* forward declarations of dcbx related functions */
static void bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
static void bnx2x_pfc_set_pfc(struct bnx2x *bp);
static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp);
static void bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp,
u32 *set_configuration_ets_pg,
u32 *pri_pg_tbl);
static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp,
u32 *pg_pri_orginal_spread,
struct pg_help_data *help_data);
static void bnx2x_dcbx_fill_cos_params(struct bnx2x *bp,
struct pg_help_data *help_data,
struct dcbx_ets_feature *ets,
u32 *pg_pri_orginal_spread);
static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
struct cos_help_data *cos_data,
u32 *pg_pri_orginal_spread,
struct dcbx_ets_feature *ets);
static void bnx2x_pfc_fw_struct_e2(struct bnx2x *bp);
static void bnx2x_pfc_set(struct bnx2x *bp)
{
struct bnx2x_nig_brb_pfc_port_params pfc_params = {0};
u32 pri_bit, val = 0;
u8 pri;
/* Tx COS configuration */
if (bp->dcbx_port_params.ets.cos_params[0].pauseable)
pfc_params.rx_cos0_priority_mask =
bp->dcbx_port_params.ets.cos_params[0].pri_bitmask;
if (bp->dcbx_port_params.ets.cos_params[1].pauseable)
pfc_params.rx_cos1_priority_mask =
bp->dcbx_port_params.ets.cos_params[1].pri_bitmask;
/**
* Rx COS configuration
* Changing PFC RX configuration .
* In RX COS0 will always be configured to lossy and COS1 to lossless
*/
for (pri = 0 ; pri < MAX_PFC_PRIORITIES ; pri++) {
pri_bit = 1 << pri;
if (pri_bit & DCBX_PFC_PRI_PAUSE_MASK(bp))
val |= 1 << (pri * 4);
}
pfc_params.pkt_priority_to_cos = val;
/* RX COS0 */
pfc_params.llfc_low_priority_classes = 0;
/* RX COS1 */
pfc_params.llfc_high_priority_classes = DCBX_PFC_PRI_PAUSE_MASK(bp);
/* BRB configuration */
pfc_params.cos0_pauseable = false;
pfc_params.cos1_pauseable = true;
bnx2x_acquire_phy_lock(bp);
bp->link_params.feature_config_flags |= FEATURE_CONFIG_PFC_ENABLED;
bnx2x_update_pfc(&bp->link_params, &bp->link_vars, &pfc_params);
bnx2x_release_phy_lock(bp);
}
static void bnx2x_pfc_clear(struct bnx2x *bp)
{
struct bnx2x_nig_brb_pfc_port_params nig_params = {0};
nig_params.pause_enable = 1;
#ifdef BNX2X_SAFC
if (bp->flags & SAFC_TX_FLAG) {
u32 high = 0, low = 0;
int i;
for (i = 0; i < BNX2X_MAX_PRIORITY; i++) {
if (bp->pri_map[i] == 1)
high |= (1 << i);
if (bp->pri_map[i] == 0)
low |= (1 << i);
}
nig_params.llfc_low_priority_classes = high;
nig_params.llfc_low_priority_classes = low;
nig_params.pause_enable = 0;
nig_params.llfc_enable = 1;
nig_params.llfc_out_en = 1;
}
#endif /* BNX2X_SAFC */
bnx2x_acquire_phy_lock(bp);
bp->link_params.feature_config_flags &= ~FEATURE_CONFIG_PFC_ENABLED;
bnx2x_update_pfc(&bp->link_params, &bp->link_vars, &nig_params);
bnx2x_release_phy_lock(bp);
}
static void bnx2x_dump_dcbx_drv_param(struct bnx2x *bp,
struct dcbx_features *features,
u32 error)
{
u8 i = 0;
DP(NETIF_MSG_LINK, "local_mib.error %x\n", error);
/* PG */
DP(NETIF_MSG_LINK,
"local_mib.features.ets.enabled %x\n", features->ets.enabled);
for (i = 0; i < DCBX_MAX_NUM_PG_BW_ENTRIES; i++)
DP(NETIF_MSG_LINK,
"local_mib.features.ets.pg_bw_tbl[%d] %d\n", i,
DCBX_PG_BW_GET(features->ets.pg_bw_tbl, i));
for (i = 0; i < DCBX_MAX_NUM_PRI_PG_ENTRIES; i++)
DP(NETIF_MSG_LINK,
"local_mib.features.ets.pri_pg_tbl[%d] %d\n", i,
DCBX_PRI_PG_GET(features->ets.pri_pg_tbl, i));
/* pfc */
DP(NETIF_MSG_LINK, "dcbx_features.pfc.pri_en_bitmap %x\n",
features->pfc.pri_en_bitmap);
DP(NETIF_MSG_LINK, "dcbx_features.pfc.pfc_caps %x\n",
features->pfc.pfc_caps);
DP(NETIF_MSG_LINK, "dcbx_features.pfc.enabled %x\n",
features->pfc.enabled);
DP(NETIF_MSG_LINK, "dcbx_features.app.default_pri %x\n",
features->app.default_pri);
DP(NETIF_MSG_LINK, "dcbx_features.app.tc_supported %x\n",
features->app.tc_supported);
DP(NETIF_MSG_LINK, "dcbx_features.app.enabled %x\n",
features->app.enabled);
for (i = 0; i < DCBX_MAX_APP_PROTOCOL; i++) {
DP(NETIF_MSG_LINK,
"dcbx_features.app.app_pri_tbl[%x].app_id %x\n",
i, features->app.app_pri_tbl[i].app_id);
DP(NETIF_MSG_LINK,
"dcbx_features.app.app_pri_tbl[%x].pri_bitmap %x\n",
i, features->app.app_pri_tbl[i].pri_bitmap);
DP(NETIF_MSG_LINK,
"dcbx_features.app.app_pri_tbl[%x].appBitfield %x\n",
i, features->app.app_pri_tbl[i].appBitfield);
}
}
static void bnx2x_dcbx_get_ap_priority(struct bnx2x *bp,
u8 pri_bitmap,
u8 llfc_traf_type)
{
u32 pri = MAX_PFC_PRIORITIES;
u32 index = MAX_PFC_PRIORITIES - 1;
u32 pri_mask;
u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority;
/* Choose the highest priority */
while ((MAX_PFC_PRIORITIES == pri) && (0 != index)) {
pri_mask = 1 << index;
if (GET_FLAGS(pri_bitmap, pri_mask))
pri = index ;
index--;
}
if (pri < MAX_PFC_PRIORITIES)
ttp[llfc_traf_type] = max_t(u32, ttp[llfc_traf_type], pri);
}
static void bnx2x_dcbx_get_ap_feature(struct bnx2x *bp,
struct dcbx_app_priority_feature *app,
u32 error) {
u8 index;
u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority;
if (GET_FLAGS(error, DCBX_LOCAL_APP_ERROR))
DP(NETIF_MSG_LINK, "DCBX_LOCAL_APP_ERROR\n");
if (app->enabled && !GET_FLAGS(error, DCBX_LOCAL_APP_ERROR)) {
bp->dcbx_port_params.app.enabled = true;
for (index = 0 ; index < LLFC_DRIVER_TRAFFIC_TYPE_MAX; index++)
ttp[index] = 0;
if (app->default_pri < MAX_PFC_PRIORITIES)
ttp[LLFC_TRAFFIC_TYPE_NW] = app->default_pri;
for (index = 0 ; index < DCBX_MAX_APP_PROTOCOL; index++) {
struct dcbx_app_priority_entry *entry =
app->app_pri_tbl;
if (GET_FLAGS(entry[index].appBitfield,
DCBX_APP_SF_ETH_TYPE) &&
ETH_TYPE_FCOE == entry[index].app_id)
bnx2x_dcbx_get_ap_priority(bp,
entry[index].pri_bitmap,
LLFC_TRAFFIC_TYPE_FCOE);
if (GET_FLAGS(entry[index].appBitfield,
DCBX_APP_SF_PORT) &&
TCP_PORT_ISCSI == entry[index].app_id)
bnx2x_dcbx_get_ap_priority(bp,
entry[index].pri_bitmap,
LLFC_TRAFFIC_TYPE_ISCSI);
}
} else {
DP(NETIF_MSG_LINK, "DCBX_LOCAL_APP_DISABLED\n");
bp->dcbx_port_params.app.enabled = false;
for (index = 0 ; index < LLFC_DRIVER_TRAFFIC_TYPE_MAX; index++)
ttp[index] = INVALID_TRAFFIC_TYPE_PRIORITY;
}
}
static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp,
struct dcbx_ets_feature *ets,
u32 error) {
int i = 0;
u32 pg_pri_orginal_spread[DCBX_MAX_NUM_PG_BW_ENTRIES] = {0};
struct pg_help_data pg_help_data;
struct bnx2x_dcbx_cos_params *cos_params =
bp->dcbx_port_params.ets.cos_params;
memset(&pg_help_data, 0, sizeof(struct pg_help_data));
if (GET_FLAGS(error, DCBX_LOCAL_ETS_ERROR))
DP(NETIF_MSG_LINK, "DCBX_LOCAL_ETS_ERROR\n");
/* Clean up old settings of ets on COS */
for (i = 0; i < E2_NUM_OF_COS ; i++) {
cos_params[i].pauseable = false;
cos_params[i].strict = BNX2X_DCBX_COS_NOT_STRICT;
cos_params[i].bw_tbl = DCBX_INVALID_COS_BW;
cos_params[i].pri_bitmask = DCBX_PFC_PRI_GET_N