mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
35b53f8c98
Created new directory "bnxt_en" in /dev/bnxt and /modules/bnxt and moved source files and Makefile into respective directory. ETS support: - Added new files bnxt_dcb.c & bnxt_dcb.h - Added sysctl node 'dcb' and created handlers 'ets' and 'dcbx_cap' - Add logic to validate user input and configure ETS in the firmware - Updated makefile to include bnxt_dcb.c & bnxt_dcb.h PFC support: - Created sysctl handlers 'pfc' under node 'dcb' - Added logic to validate user input and configure PFC in the firmware. App TLV support: - Created 3 new sysctl handlers under node 'dcb' - set_apptlv (write only): Sets a specified TLV - del_apptlv (write only): Deletes a specified TLV - list_apptlv (read only): Lists all APP TLVs configured - Added logic to validate user input and configure APP TLVs in the firmware. Added Below DCB ops for management interface: - Set PFC, Get PFC, Set ETS, Get ETS, Add App_TLV, Del App_TLV Lst App_TLV Reviewed by: imp Approved by: imp Differential revision: https://reviews.freebsd.org/D45005
862 lines
22 KiB
C
862 lines
22 KiB
C
/*-
|
|
* Broadcom NetXtreme-C/E network driver.
|
|
*
|
|
* Copyright (c) 2024 Broadcom, All Rights Reserved.
|
|
* The term Broadcom refers to Broadcom Limited and/or its subsidiaries
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS'
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/endian.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/bitops.h>
|
|
|
|
#include "bnxt.h"
|
|
#include "bnxt_hwrm.h"
|
|
#include "bnxt_dcb.h"
|
|
#include "hsi_struct_def.h"
|
|
|
|
static int
|
|
bnxt_tx_queue_to_tc(struct bnxt_softc *softc, uint8_t queue_id)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < softc->max_tc; i++) {
|
|
if (softc->tx_q_info[i].queue_id == queue_id) {
|
|
for (j = 0; j < softc->max_tc; j++) {
|
|
if (softc->tc_to_qidx[j] == i)
|
|
return j;
|
|
}
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_pri2cos_cfg(struct bnxt_softc *softc,
|
|
struct bnxt_ieee_ets *ets,
|
|
uint32_t path_dir)
|
|
{
|
|
struct hwrm_queue_pri2cos_cfg_input req = {0};
|
|
struct bnxt_queue_info *q_info;
|
|
uint8_t *pri2cos;
|
|
int i;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PRI2COS_CFG);
|
|
|
|
req.flags = htole32(path_dir | HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_IVLAN);
|
|
if (path_dir == HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR ||
|
|
path_dir == HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_TX)
|
|
q_info = softc->tx_q_info;
|
|
else
|
|
q_info = softc->rx_q_info;
|
|
pri2cos = &req.pri0_cos_queue_id;
|
|
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
|
|
uint8_t qidx;
|
|
|
|
req.enables |= htole32(HWRM_QUEUE_PRI2COS_CFG_INPUT_ENABLES_PRI0_COS_QUEUE_ID << i);
|
|
|
|
qidx = softc->tc_to_qidx[ets->prio_tc[i]];
|
|
pri2cos[i] = q_info[qidx].queue_id;
|
|
}
|
|
return _hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_pri2cos_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
|
|
{
|
|
struct hwrm_queue_pri2cos_qcfg_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
struct hwrm_queue_pri2cos_qcfg_input req = {0};
|
|
int rc;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PRI2COS_QCFG);
|
|
|
|
req.flags = htole32(HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_IVLAN);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (!rc) {
|
|
uint8_t *pri2cos = &resp->pri0_cos_queue_id;
|
|
int i;
|
|
|
|
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
|
|
uint8_t queue_id = pri2cos[i];
|
|
int tc;
|
|
|
|
tc = bnxt_tx_queue_to_tc(softc, queue_id);
|
|
if (tc >= 0)
|
|
ets->prio_tc[i] = tc;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_cos2bw_cfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets,
|
|
uint8_t max_tc)
|
|
{
|
|
struct hwrm_queue_cos2bw_cfg_input req = {0};
|
|
struct bnxt_cos2bw_cfg cos2bw;
|
|
void *data;
|
|
int i;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_COS2BW_CFG);
|
|
|
|
for (i = 0; i < max_tc; i++) {
|
|
uint8_t qidx = softc->tc_to_qidx[i];
|
|
|
|
req.enables |=
|
|
htole32(HWRM_QUEUE_COS2BW_CFG_INPUT_ENABLES_COS_QUEUE_ID0_VALID << qidx);
|
|
|
|
memset(&cos2bw, 0, sizeof(cos2bw));
|
|
cos2bw.queue_id = softc->tx_q_info[qidx].queue_id;
|
|
if (ets->tc_tsa[i] == BNXT_IEEE_8021QAZ_TSA_STRICT) {
|
|
cos2bw.tsa =
|
|
HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_SP;
|
|
cos2bw.pri_lvl = i;
|
|
} else {
|
|
cos2bw.tsa =
|
|
HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_ETS;
|
|
cos2bw.bw_weight = ets->tc_tx_bw[i];
|
|
/* older firmware requires min_bw to be set to the
|
|
* same weight value in percent.
|
|
*/
|
|
if (BNXT_FW_MAJ(softc) < 218) {
|
|
cos2bw.min_bw =
|
|
htole32((ets->tc_tx_bw[i] * 100) |
|
|
BW_VALUE_UNIT_PERCENT1_100);
|
|
}
|
|
}
|
|
data = &req.unused_0 + qidx * (sizeof(cos2bw) - 4);
|
|
memcpy(data, &cos2bw.queue_id, sizeof(cos2bw) - 4);
|
|
if (qidx == 0) {
|
|
req.queue_id0 = cos2bw.queue_id;
|
|
req.unused_0 = 0;
|
|
}
|
|
}
|
|
return _hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_cos2bw_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
|
|
{
|
|
struct hwrm_queue_cos2bw_qcfg_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
struct hwrm_queue_cos2bw_qcfg_input req = {0};
|
|
struct bnxt_cos2bw_cfg cos2bw;
|
|
uint8_t *data;
|
|
int rc, i;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_COS2BW_QCFG);
|
|
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
data = &resp->queue_id0 + offsetof(struct bnxt_cos2bw_cfg, queue_id);
|
|
for (i = 0; i < softc->max_tc; i++, data += sizeof(cos2bw.cfg)) {
|
|
int tc;
|
|
|
|
memcpy(&cos2bw.cfg, data, sizeof(cos2bw.cfg));
|
|
if (i == 0)
|
|
cos2bw.queue_id = resp->queue_id0;
|
|
|
|
tc = bnxt_tx_queue_to_tc(softc, cos2bw.queue_id);
|
|
if (tc < 0)
|
|
continue;
|
|
|
|
if (cos2bw.tsa == HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_SP) {
|
|
ets->tc_tsa[tc] = BNXT_IEEE_8021QAZ_TSA_STRICT;
|
|
} else {
|
|
ets->tc_tsa[tc] = BNXT_IEEE_8021QAZ_TSA_ETS;
|
|
ets->tc_tx_bw[tc] = cos2bw.bw_weight;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bnxt_queue_remap(struct bnxt_softc *softc, unsigned int lltc_mask)
|
|
{
|
|
unsigned long qmap = 0;
|
|
int max = softc->max_tc;
|
|
int i, j, rc;
|
|
|
|
/* Assign lossless TCs first */
|
|
for (i = 0, j = 0; i < max; ) {
|
|
if (lltc_mask & (1 << i)) {
|
|
if (BNXT_LLQ(softc->rx_q_info[j].queue_profile)) {
|
|
softc->tc_to_qidx[i] = j;
|
|
__set_bit(j, &qmap);
|
|
i++;
|
|
}
|
|
j++;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < max; i++) {
|
|
if (lltc_mask & (1 << i))
|
|
continue;
|
|
j = find_next_zero_bit(&qmap, max, j);
|
|
softc->tc_to_qidx[i] = j;
|
|
__set_bit(j, &qmap);
|
|
j++;
|
|
}
|
|
|
|
if (softc->ieee_ets) {
|
|
rc = bnxt_hwrm_queue_cos2bw_cfg(softc, softc->ieee_ets, softc->max_tc);
|
|
if (rc) {
|
|
device_printf(softc->dev, "failed to config BW, rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, softc->ieee_ets,
|
|
HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR);
|
|
if (rc) {
|
|
device_printf(softc->dev, "failed to config prio, rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_pfc_cfg(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
|
|
{
|
|
struct hwrm_queue_pfcenable_cfg_input req = {0};
|
|
struct bnxt_ieee_ets *my_ets = softc->ieee_ets;
|
|
unsigned int tc_mask = 0, pri_mask = 0;
|
|
uint8_t i, pri, lltc_count = 0;
|
|
bool need_q_remap = false;
|
|
|
|
if (!my_ets)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < softc->max_tc; i++) {
|
|
for (pri = 0; pri < BNXT_IEEE_8021QAZ_MAX_TCS; pri++) {
|
|
if ((pfc->pfc_en & (1 << pri)) &&
|
|
(my_ets->prio_tc[pri] == i)) {
|
|
pri_mask |= 1 << pri;
|
|
tc_mask |= 1 << i;
|
|
}
|
|
}
|
|
if (tc_mask & (1 << i))
|
|
lltc_count++;
|
|
}
|
|
|
|
if (lltc_count > softc->max_lltc) {
|
|
device_printf(softc->dev,
|
|
"Hardware doesn't support %d lossless queues "
|
|
"to configure PFC (cap %d)\n", lltc_count, softc->max_lltc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < softc->max_tc; i++) {
|
|
if (tc_mask & (1 << i)) {
|
|
uint8_t qidx = softc->tc_to_qidx[i];
|
|
|
|
if (!BNXT_LLQ(softc->rx_q_info[qidx].queue_profile)) {
|
|
need_q_remap = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_q_remap)
|
|
bnxt_queue_remap(softc, tc_mask);
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PFCENABLE_CFG);
|
|
|
|
req.flags = htole32(pri_mask);
|
|
return _hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_pfc_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
|
|
{
|
|
struct hwrm_queue_pfcenable_qcfg_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
struct hwrm_queue_pfcenable_qcfg_input req = {0};
|
|
uint8_t pri_mask;
|
|
int rc;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PFCENABLE_QCFG);
|
|
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
pri_mask = le32toh(resp->flags);
|
|
pfc->pfc_en = pri_mask;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_get_dcbx_app(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
|
|
{
|
|
struct hwrm_fw_get_structured_data_input get = {0};
|
|
struct hwrm_struct_data_dcbx_app *fw_app;
|
|
struct hwrm_struct_hdr *data;
|
|
struct iflib_dma_info dma_data;
|
|
size_t data_len;
|
|
int rc, n, i;
|
|
|
|
if (softc->hwrm_spec_code < 0x10601)
|
|
return 0;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &get, HWRM_FW_GET_STRUCTURED_DATA);
|
|
|
|
n = BNXT_IEEE_8021QAZ_MAX_TCS;
|
|
data_len = sizeof(*data) + sizeof(*fw_app) * n;
|
|
rc = iflib_dma_alloc(softc->ctx, data_len, &dma_data,
|
|
BUS_DMA_NOWAIT);
|
|
if (rc)
|
|
return ENOMEM;
|
|
get.dest_data_addr = htole64(dma_data.idi_paddr);
|
|
get.structure_id = htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP);
|
|
get.subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
|
|
get.count = 0;
|
|
rc = _hwrm_send_message(softc, &get, sizeof(get));
|
|
if (rc)
|
|
goto set_app_exit;
|
|
|
|
data = (void *)dma_data.idi_vaddr;
|
|
fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1);
|
|
|
|
if (data->struct_id != htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP)) {
|
|
rc = -ENODEV;
|
|
goto set_app_exit;
|
|
}
|
|
|
|
n = data->count;
|
|
for (i = 0; i < n; i++, fw_app++) {
|
|
app[*num_inputs].priority = fw_app->priority;
|
|
app[*num_inputs].protocol = htobe16(fw_app->protocol_id);
|
|
app[*num_inputs].selector = fw_app->protocol_selector;
|
|
(*num_inputs)++;
|
|
}
|
|
|
|
set_app_exit:
|
|
iflib_dma_free(&dma_data);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_set_dcbx_app(struct bnxt_softc *softc, struct bnxt_dcb_app *app,
|
|
bool add)
|
|
{
|
|
struct hwrm_fw_set_structured_data_input set = {0};
|
|
struct hwrm_fw_get_structured_data_input get = {0};
|
|
struct hwrm_struct_data_dcbx_app *fw_app;
|
|
struct hwrm_struct_hdr *data;
|
|
struct iflib_dma_info dma_data;
|
|
size_t data_len;
|
|
int rc, n, i;
|
|
|
|
if (softc->hwrm_spec_code < 0x10601)
|
|
return 0;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &get, HWRM_FW_GET_STRUCTURED_DATA);
|
|
|
|
n = BNXT_IEEE_8021QAZ_MAX_TCS;
|
|
data_len = sizeof(*data) + sizeof(*fw_app) * n;
|
|
rc = iflib_dma_alloc(softc->ctx, data_len, &dma_data,
|
|
BUS_DMA_NOWAIT);
|
|
if (rc)
|
|
return ENOMEM;
|
|
get.dest_data_addr = htole64(dma_data.idi_paddr);
|
|
get.structure_id = htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP);
|
|
get.subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
|
|
get.count = 0;
|
|
rc = _hwrm_send_message(softc, &get, sizeof(get));
|
|
if (rc)
|
|
goto set_app_exit;
|
|
|
|
data = (void *)dma_data.idi_vaddr;
|
|
fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1);
|
|
|
|
if (data->struct_id != htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP)) {
|
|
rc = -ENODEV;
|
|
goto set_app_exit;
|
|
}
|
|
|
|
n = data->count;
|
|
for (i = 0; i < n; i++, fw_app++) {
|
|
if (fw_app->protocol_id == htobe16(app->protocol) &&
|
|
fw_app->protocol_selector == app->selector &&
|
|
fw_app->priority == app->priority) {
|
|
if (add)
|
|
goto set_app_exit;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if (add) {
|
|
/* append */
|
|
n++;
|
|
fw_app->protocol_id = htobe16(app->protocol);
|
|
fw_app->protocol_selector = app->selector;
|
|
fw_app->priority = app->priority;
|
|
fw_app->valid = 1;
|
|
} else {
|
|
size_t len = 0;
|
|
|
|
/* not found, nothing to delete */
|
|
if (n == i)
|
|
goto set_app_exit;
|
|
|
|
len = (n - 1 - i) * sizeof(*fw_app);
|
|
if (len)
|
|
memmove(fw_app, fw_app + 1, len);
|
|
n--;
|
|
memset(fw_app + n, 0, sizeof(*fw_app));
|
|
}
|
|
data->count = n;
|
|
data->len = htole16(sizeof(*fw_app) * n);
|
|
data->subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &set, HWRM_FW_SET_STRUCTURED_DATA);
|
|
|
|
set.src_data_addr = htole64(dma_data.idi_paddr);
|
|
set.data_len = htole16(sizeof(*data) + sizeof(*fw_app) * n);
|
|
set.hdr_cnt = 1;
|
|
rc = _hwrm_send_message(softc, &set, sizeof(set));
|
|
|
|
set_app_exit:
|
|
iflib_dma_free(&dma_data);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_dscp_qcaps(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_queue_dscp_qcaps_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
struct hwrm_queue_dscp_qcaps_input req = {0};
|
|
int rc;
|
|
|
|
softc->max_dscp_value = 0;
|
|
if (softc->hwrm_spec_code < 0x10800 || BNXT_VF(softc))
|
|
return 0;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP_QCAPS);
|
|
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (!rc) {
|
|
softc->max_dscp_value = (1 << resp->num_dscp_bits) - 1;
|
|
if (softc->max_dscp_value < 0x3f)
|
|
softc->max_dscp_value = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_dscp2pri_qcfg(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
|
|
{
|
|
struct hwrm_queue_dscp2pri_qcfg_input req = {0};
|
|
struct hwrm_queue_dscp2pri_qcfg_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
struct bnxt_dscp2pri_entry *dscp2pri;
|
|
struct iflib_dma_info dma_data;
|
|
int rc, entry_cnt;
|
|
int i;
|
|
|
|
if (softc->hwrm_spec_code < 0x10800)
|
|
return 0;
|
|
|
|
rc = iflib_dma_alloc(softc->ctx, sizeof(*dscp2pri) * 128, &dma_data,
|
|
BUS_DMA_NOWAIT);
|
|
if (rc)
|
|
return ENOMEM;
|
|
|
|
dscp2pri = (void *)dma_data.idi_vaddr;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP2PRI_QCFG);
|
|
|
|
req.dest_data_addr = htole64(dma_data.idi_paddr);
|
|
req.dest_data_buffer_size = htole16(sizeof(*dscp2pri) * 64);
|
|
req.port_id = htole16(softc->pf.port_id);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
|
|
if (rc)
|
|
goto end;
|
|
|
|
entry_cnt = le16toh(resp->entry_cnt);
|
|
for (i = 0; i < entry_cnt; i++) {
|
|
app[*num_inputs].priority = dscp2pri[i].pri;
|
|
app[*num_inputs].protocol = dscp2pri[i].dscp;
|
|
app[*num_inputs].selector = BNXT_IEEE_8021QAZ_APP_SEL_DSCP;
|
|
(*num_inputs)++;
|
|
}
|
|
|
|
end:
|
|
iflib_dma_free(&dma_data);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_hwrm_queue_dscp2pri_cfg(struct bnxt_softc *softc, struct bnxt_dcb_app *app,
|
|
bool add)
|
|
{
|
|
struct hwrm_queue_dscp2pri_cfg_input req = {0};
|
|
struct bnxt_dscp2pri_entry *dscp2pri;
|
|
struct iflib_dma_info dma_data;
|
|
int rc;
|
|
|
|
if (softc->hwrm_spec_code < 0x10800)
|
|
return 0;
|
|
|
|
rc = iflib_dma_alloc(softc->ctx, sizeof(*dscp2pri), &dma_data,
|
|
BUS_DMA_NOWAIT);
|
|
if (rc)
|
|
return ENOMEM;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP2PRI_CFG);
|
|
|
|
req.src_data_addr = htole64(dma_data.idi_paddr);
|
|
dscp2pri = (void *)dma_data.idi_vaddr;
|
|
dscp2pri->dscp = app->protocol;
|
|
if (add)
|
|
dscp2pri->mask = 0x3f;
|
|
else
|
|
dscp2pri->mask = 0;
|
|
dscp2pri->pri = app->priority;
|
|
req.entry_cnt = htole16(1);
|
|
req.port_id = htole16(softc->pf.port_id);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
|
|
iflib_dma_free(&dma_data);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_ets_validate(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets, uint8_t *tc)
|
|
{
|
|
int total_ets_bw = 0;
|
|
bool zero = false;
|
|
uint8_t max_tc = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
|
|
if (ets->prio_tc[i] > softc->max_tc) {
|
|
device_printf(softc->dev, "priority to TC mapping exceeds TC count %d\n",
|
|
ets->prio_tc[i]);
|
|
return -EINVAL;
|
|
}
|
|
if (ets->prio_tc[i] > max_tc)
|
|
max_tc = ets->prio_tc[i];
|
|
|
|
if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) && i > softc->max_tc)
|
|
return -EINVAL;
|
|
|
|
switch (ets->tc_tsa[i]) {
|
|
case BNXT_IEEE_8021QAZ_TSA_STRICT:
|
|
break;
|
|
case BNXT_IEEE_8021QAZ_TSA_ETS:
|
|
total_ets_bw += ets->tc_tx_bw[i];
|
|
zero = zero || !ets->tc_tx_bw[i];
|
|
break;
|
|
default:
|
|
return -ENOTSUPP;
|
|
}
|
|
}
|
|
if (total_ets_bw > 100) {
|
|
device_printf(softc->dev, "rejecting ETS config exceeding available bandwidth\n");
|
|
return -EINVAL;
|
|
}
|
|
if (zero && total_ets_bw == 100) {
|
|
device_printf(softc->dev, "rejecting ETS config starving a TC\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (max_tc >= softc->max_tc)
|
|
*tc = softc->max_tc;
|
|
else
|
|
*tc = max_tc + 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_getets(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
|
|
{
|
|
struct bnxt_ieee_ets *my_ets = softc->ieee_ets;
|
|
int rc;
|
|
|
|
if (!my_ets)
|
|
return 0;
|
|
|
|
rc = bnxt_hwrm_queue_cos2bw_qcfg(softc, my_ets);
|
|
if (rc)
|
|
goto error;
|
|
rc = bnxt_hwrm_queue_pri2cos_qcfg(softc, my_ets);
|
|
if (rc)
|
|
goto error;
|
|
|
|
if (ets) {
|
|
ets->cbs = my_ets->cbs;
|
|
ets->ets_cap = softc->max_tc;
|
|
memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
|
|
memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
|
|
memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa));
|
|
memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc));
|
|
}
|
|
return 0;
|
|
error:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_setets(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
|
|
{
|
|
uint8_t max_tc = 0;
|
|
int rc;
|
|
|
|
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
|
|
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
|
|
return -EINVAL;
|
|
|
|
rc = bnxt_ets_validate(softc, ets, &max_tc);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = bnxt_hwrm_queue_cos2bw_cfg(softc, ets, max_tc);
|
|
if (rc)
|
|
goto error;
|
|
|
|
if (!softc->is_asym_q) {
|
|
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
|
|
HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR);
|
|
if (rc)
|
|
goto error;
|
|
} else {
|
|
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
|
|
HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_PATH_TX);
|
|
if (rc)
|
|
goto error;
|
|
|
|
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
|
|
HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_PATH_RX);
|
|
if (rc)
|
|
goto error;
|
|
}
|
|
|
|
memcpy(softc->ieee_ets, ets, sizeof(*ets));
|
|
return 0;
|
|
error:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_getpfc(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
|
|
{
|
|
struct bnxt_ieee_pfc *my_pfc = softc->ieee_pfc;
|
|
int rc;
|
|
|
|
if (!my_pfc)
|
|
return -1;
|
|
|
|
pfc->pfc_cap = softc->max_lltc;
|
|
|
|
rc = bnxt_hwrm_queue_pfc_qcfg(softc, my_pfc);
|
|
if (rc)
|
|
return 0;
|
|
|
|
pfc->pfc_en = my_pfc->pfc_en;
|
|
pfc->mbc = my_pfc->mbc;
|
|
pfc->delay = my_pfc->delay;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_setpfc(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
|
|
{
|
|
struct bnxt_ieee_pfc *my_pfc = softc->ieee_pfc;
|
|
int rc;
|
|
|
|
if (!my_pfc)
|
|
return -1;
|
|
|
|
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
|
|
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST) ||
|
|
(softc->phy_flags & BNXT_PHY_FL_NO_PAUSE))
|
|
return -EINVAL;
|
|
|
|
rc = bnxt_hwrm_queue_pfc_cfg(softc, pfc);
|
|
if (!rc)
|
|
memcpy(my_pfc, pfc, sizeof(*my_pfc));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
bnxt_dcb_ieee_dscp_app_prep(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
|
|
{
|
|
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP) {
|
|
if (!softc->max_dscp_value)
|
|
return -ENOTSUPP;
|
|
if (app->protocol > softc->max_dscp_value)
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_setapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
|
|
{
|
|
int rc;
|
|
|
|
|
|
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
|
|
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
|
|
return -EINVAL;
|
|
|
|
rc = bnxt_dcb_ieee_dscp_app_prep(softc, app);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if ((app->selector == BNXT_IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
|
|
app->protocol == ETH_P_ROCE) ||
|
|
(app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DGRAM &&
|
|
app->protocol == ROCE_V2_UDP_DPORT))
|
|
rc = bnxt_hwrm_set_dcbx_app(softc, app, true);
|
|
|
|
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP)
|
|
rc = bnxt_hwrm_queue_dscp2pri_cfg(softc, app, true);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_delapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
|
|
{
|
|
int rc;
|
|
|
|
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
|
|
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
|
|
return -EINVAL;
|
|
|
|
rc = bnxt_dcb_ieee_dscp_app_prep(softc, app);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if ((app->selector == BNXT_IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
|
|
app->protocol == ETH_P_ROCE) ||
|
|
(app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DGRAM &&
|
|
app->protocol == ROCE_V2_UDP_DPORT))
|
|
rc = bnxt_hwrm_set_dcbx_app(softc, app, false);
|
|
|
|
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP)
|
|
rc = bnxt_hwrm_queue_dscp2pri_cfg(softc, app, false);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_dcb_ieee_listapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
|
|
{
|
|
bnxt_hwrm_get_dcbx_app(softc, app, num_inputs);
|
|
bnxt_hwrm_queue_dscp2pri_qcfg(softc, app, num_inputs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t
|
|
bnxt_dcb_getdcbx(struct bnxt_softc *softc)
|
|
{
|
|
return softc->dcbx_cap;
|
|
}
|
|
|
|
uint8_t
|
|
bnxt_dcb_setdcbx(struct bnxt_softc *softc, uint8_t mode)
|
|
{
|
|
/* All firmware DCBX settings are set in NVRAM */
|
|
if (softc->dcbx_cap & BNXT_DCB_CAP_DCBX_LLD_MANAGED)
|
|
return 1;
|
|
|
|
/*
|
|
* Do't allow editing CAP_DCBX_LLD_MANAGED since it is driven
|
|
* based on FUNC_QCFG_OUTPUT_FLAGS_FW_DCBX_AGENT_ENABLED
|
|
*/
|
|
if ((softc->dcbx_cap & BNXT_DCB_CAP_DCBX_LLD_MANAGED) !=
|
|
(mode & BNXT_DCB_CAP_DCBX_LLD_MANAGED))
|
|
return 1;
|
|
|
|
if (mode & BNXT_DCB_CAP_DCBX_HOST) {
|
|
if (BNXT_VF(softc) || (softc->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
|
|
return 1;
|
|
|
|
/* only support BNXT_IEEE */
|
|
if ((mode & BNXT_DCB_CAP_DCBX_VER_CEE) ||
|
|
!(mode & BNXT_DCB_CAP_DCBX_VER_IEEE))
|
|
return 1;
|
|
}
|
|
|
|
if (mode == softc->dcbx_cap)
|
|
return 0;
|
|
|
|
softc->dcbx_cap = mode;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
bnxt_dcb_init(struct bnxt_softc *softc)
|
|
{
|
|
struct bnxt_ieee_ets ets = {0};
|
|
struct bnxt_ieee_pfc pfc = {0};
|
|
|
|
softc->dcbx_cap = 0;
|
|
|
|
if (softc->hwrm_spec_code < 0x10501)
|
|
return;
|
|
|
|
softc->ieee_ets = malloc(sizeof(struct bnxt_ieee_ets), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (!softc->ieee_ets)
|
|
return;
|
|
|
|
softc->ieee_pfc = malloc(sizeof(struct bnxt_ieee_pfc), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (!softc->ieee_pfc)
|
|
return;
|
|
|
|
bnxt_hwrm_queue_dscp_qcaps(softc);
|
|
softc->dcbx_cap = BNXT_DCB_CAP_DCBX_VER_IEEE;
|
|
if (BNXT_PF(softc) && !(softc->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
|
|
softc->dcbx_cap |= BNXT_DCB_CAP_DCBX_HOST;
|
|
else if (softc->fw_cap & BNXT_FW_CAP_DCBX_AGENT)
|
|
softc->dcbx_cap |= BNXT_DCB_CAP_DCBX_LLD_MANAGED;
|
|
|
|
bnxt_dcb_ieee_setets(softc, &ets);
|
|
bnxt_dcb_ieee_setpfc(softc, &pfc);
|
|
|
|
}
|
|
|
|
void
|
|
bnxt_dcb_free(struct bnxt_softc *softc)
|
|
{
|
|
free(softc->ieee_ets, M_DEVBUF);
|
|
softc->ieee_ets = NULL;
|
|
free(softc->ieee_pfc, M_DEVBUF);
|
|
softc->ieee_pfc = NULL;
|
|
}
|