diff options
Diffstat (limited to 'drivers/net/ethernet/netronome/nfp/nic/dcb.c')
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nic/dcb.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/nic/dcb.c b/drivers/net/ethernet/netronome/nfp/nic/dcb.c new file mode 100644 index 000000000000..bb498ac6bd7d --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nic/dcb.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2023 Corigine, Inc. */ + +#include <linux/device.h> +#include <linux/netdevice.h> +#include <net/dcbnl.h> + +#include "../nfp_app.h" +#include "../nfp_net.h" +#include "../nfp_main.h" +#include "../nfpcore/nfp_cpp.h" +#include "../nfpcore/nfp_nffw.h" +#include "../nfp_net_sriov.h" + +#include "main.h" + +#define NFP_DCB_TRUST_PCP 1 +#define NFP_DCB_TRUST_DSCP 2 +#define NFP_DCB_TRUST_INVALID 0xff + +#define NFP_DCB_TSA_VENDOR 1 +#define NFP_DCB_TSA_STRICT 2 +#define NFP_DCB_TSA_ETS 3 + +#define NFP_DCB_GBL_ENABLE BIT(0) +#define NFP_DCB_QOS_ENABLE BIT(1) +#define NFP_DCB_DISABLE 0 +#define NFP_DCB_ALL_QOS_ENABLE (NFP_DCB_GBL_ENABLE | NFP_DCB_QOS_ENABLE) + +#define NFP_DCB_UPDATE_MSK_SZ 4 +#define NFP_DCB_TC_RATE_MAX 0xffff + +#define NFP_DCB_DATA_OFF_DSCP2IDX 0 +#define NFP_DCB_DATA_OFF_PCP2IDX 64 +#define NFP_DCB_DATA_OFF_TSA 80 +#define NFP_DCB_DATA_OFF_IDX_BW_PCT 88 +#define NFP_DCB_DATA_OFF_RATE 96 +#define NFP_DCB_DATA_OFF_CAP 112 +#define NFP_DCB_DATA_OFF_ENABLE 116 +#define NFP_DCB_DATA_OFF_TRUST 120 + +#define NFP_DCB_MSG_MSK_ENABLE BIT(31) +#define NFP_DCB_MSG_MSK_TRUST BIT(30) +#define NFP_DCB_MSG_MSK_TSA BIT(29) +#define NFP_DCB_MSG_MSK_DSCP BIT(28) +#define NFP_DCB_MSG_MSK_PCP BIT(27) +#define NFP_DCB_MSG_MSK_RATE BIT(26) +#define NFP_DCB_MSG_MSK_PCT BIT(25) + +static struct nfp_dcb *get_dcb_priv(struct nfp_net *nn) +{ + struct nfp_dcb *dcb = &((struct nfp_app_nic_private *)nn->app_priv)->dcb; + + return dcb; +} + +static u8 nfp_tsa_ieee2nfp(u8 tsa) +{ + switch (tsa) { + case IEEE_8021QAZ_TSA_STRICT: + return NFP_DCB_TSA_STRICT; + case IEEE_8021QAZ_TSA_ETS: + return NFP_DCB_TSA_ETS; + default: + return NFP_DCB_TSA_VENDOR; + } +} + +static int nfp_nic_dcbnl_ieee_getets(struct net_device *dev, + struct ieee_ets *ets) +{ + struct nfp_net *nn = netdev_priv(dev); + struct nfp_dcb *dcb; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + ets->prio_tc[i] = dcb->prio2tc[i]; + ets->tc_tx_bw[i] = dcb->tc_tx_pct[i]; + ets->tc_tsa[i] = dcb->tc_tsa[i]; + } + + return 0; +} + +static bool nfp_refresh_tc2idx(struct nfp_net *nn) +{ + u8 tc2idx[IEEE_8021QAZ_MAX_TCS]; + bool change = false; + struct nfp_dcb *dcb; + int maxstrict = 0; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + tc2idx[i] = i; + if (dcb->tc_tsa[i] == IEEE_8021QAZ_TSA_STRICT) + maxstrict = i; + } + + if (maxstrict > 0 && dcb->tc_tsa[0] != IEEE_8021QAZ_TSA_STRICT) { + tc2idx[0] = maxstrict; + tc2idx[maxstrict] = 0; + } + + for (unsigned int j = 0; j < IEEE_8021QAZ_MAX_TCS; j++) { + if (dcb->tc2idx[j] != tc2idx[j]) { + change = true; + dcb->tc2idx[j] = tc2idx[j]; + } + } + + return change; +} + +static int nfp_fill_maxrate(struct nfp_net *nn, u64 *max_rate_array) +{ + struct nfp_app *app = nn->app; + struct nfp_dcb *dcb; + u32 ratembps; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + /* Convert bandwidth from kbps to mbps. */ + ratembps = max_rate_array[i] / 1024; + + /* Reject input values >= NFP_DCB_TC_RATE_MAX */ + if (ratembps >= NFP_DCB_TC_RATE_MAX) { + nfp_warn(app->cpp, "ratembps(%d) must less than %d.", + ratembps, NFP_DCB_TC_RATE_MAX); + return -EINVAL; + } + /* Input value 0 mapped to NFP_DCB_TC_RATE_MAX for firmware. */ + if (ratembps == 0) + ratembps = NFP_DCB_TC_RATE_MAX; + + writew((u16)ratembps, dcb->dcbcfg_tbl + + dcb->cfg_offset + NFP_DCB_DATA_OFF_RATE + dcb->tc2idx[i] * 2); + /* for rate value from user space, need to sync to dcb structure */ + if (dcb->tc_maxrate != max_rate_array) + dcb->tc_maxrate[i] = max_rate_array[i]; + } + + return 0; +} + +static int update_dscp_maxrate(struct net_device *dev, u32 *update) +{ + struct nfp_net *nn = netdev_priv(dev); + struct nfp_dcb *dcb; + int err; + + dcb = get_dcb_priv(nn); + + err = nfp_fill_maxrate(nn, dcb->tc_maxrate); + if (err) + return err; + + *update |= NFP_DCB_MSG_MSK_RATE; + + /* We only refresh dscp in dscp trust mode. */ + if (dcb->dscp_cnt > 0) { + for (unsigned int i = 0; i < NFP_NET_MAX_DSCP; i++) { + writeb(dcb->tc2idx[dcb->prio2tc[dcb->dscp2prio[i]]], + dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_DSCP2IDX + i); + } + *update |= NFP_DCB_MSG_MSK_DSCP; + } + + return 0; +} + +static void nfp_nic_set_trust(struct nfp_net *nn, u32 *update) +{ + struct nfp_dcb *dcb; + u8 trust; + + dcb = get_dcb_priv(nn); + + if (dcb->trust_status != NFP_DCB_TRUST_INVALID) + return; + + trust = dcb->dscp_cnt > 0 ? NFP_DCB_TRUST_DSCP : NFP_DCB_TRUST_PCP; + writeb(trust, dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_TRUST); + + dcb->trust_status = trust; + *update |= NFP_DCB_MSG_MSK_TRUST; +} + +static void nfp_nic_set_enable(struct nfp_net *nn, u32 enable, u32 *update) +{ + struct nfp_dcb *dcb; + u32 value = 0; + + dcb = get_dcb_priv(nn); + + value = readl(dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_ENABLE); + if (value != enable) { + writel(enable, dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_ENABLE); + *update |= NFP_DCB_MSG_MSK_ENABLE; + } +} + +static int dcb_ets_check(struct net_device *dev, struct ieee_ets *ets) +{ + struct nfp_net *nn = netdev_priv(dev); + struct nfp_app *app = nn->app; + bool ets_exists = false; + int sum = 0; + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + /* For ets mode, check bw percentage sum. */ + if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { + ets_exists = true; + sum += ets->tc_tx_bw[i]; + } else if (ets->tc_tx_bw[i]) { + nfp_warn(app->cpp, "ETS BW for strict/vendor TC must be 0."); + return -EINVAL; + } + } + + if (ets_exists && sum != 100) { + nfp_warn(app->cpp, "Failed to validate ETS BW: sum must be 100."); + return -EINVAL; + } + + return 0; +} + +static void nfp_nic_fill_ets(struct nfp_net *nn) +{ + struct nfp_dcb *dcb; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + writeb(dcb->tc2idx[dcb->prio2tc[i]], + dcb->dcbcfg_tbl + dcb->cfg_offset + NFP_DCB_DATA_OFF_PCP2IDX + i); + writeb(dcb->tc_tx_pct[i], dcb->dcbcfg_tbl + + dcb->cfg_offset + NFP_DCB_DATA_OFF_IDX_BW_PCT + dcb->tc2idx[i]); + writeb(nfp_tsa_ieee2nfp(dcb->tc_tsa[i]), dcb->dcbcfg_tbl + + dcb->cfg_offset + NFP_DCB_DATA_OFF_TSA + dcb->tc2idx[i]); + } +} + +static void nfp_nic_ets_init(struct nfp_net *nn, u32 *update) +{ + struct nfp_dcb *dcb = get_dcb_priv(nn); + + if (dcb->ets_init) + return; + + nfp_nic_fill_ets(nn); + dcb->ets_init = true; + *update |= NFP_DCB_MSG_MSK_TSA | NFP_DCB_MSG_MSK_PCT | NFP_DCB_MSG_MSK_PCP; +} + +static int nfp_nic_dcbnl_ieee_setets(struct net_device *dev, + struct ieee_ets *ets) +{ + const u32 cmd = NFP_NET_CFG_MBOX_CMD_DCB_UPDATE; + struct nfp_net *nn = netdev_priv(dev); + struct nfp_app *app = nn->app; + struct nfp_dcb *dcb; + u32 update = 0; + bool change; + int err; + + err = dcb_ets_check(dev, ets); + if (err) + return err; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + dcb->prio2tc[i] = ets->prio_tc[i]; + dcb->tc_tx_pct[i] = ets->tc_tx_bw[i]; + dcb->tc_tsa[i] = ets->tc_tsa[i]; + } + + change = nfp_refresh_tc2idx(nn); + nfp_nic_fill_ets(nn); + dcb->ets_init = true; + if (change || !dcb->rate_init) { + err = update_dscp_maxrate(dev, &update); + if (err) { + nfp_warn(app->cpp, + "nfp dcbnl ieee setets ERROR:%d.", + err); + return err; + } + + dcb->rate_init = true; + } + nfp_nic_set_enable(nn, NFP_DCB_ALL_QOS_ENABLE, &update); + nfp_nic_set_trust(nn, &update); + err = nfp_net_mbox_lock(nn, NFP_DCB_UPDATE_MSK_SZ); + if (err) + return err; + + nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL, + update | NFP_DCB_MSG_MSK_TSA | NFP_DCB_MSG_MSK_PCT | + NFP_DCB_MSG_MSK_PCP); + + return nfp_net_mbox_reconfig_and_unlock(nn, cmd); +} + +static int nfp_nic_dcbnl_ieee_getmaxrate(struct net_device *dev, + struct ieee_maxrate *maxrate) +{ + struct nfp_net *nn = netdev_priv(dev); + struct nfp_dcb *dcb; + + dcb = get_dcb_priv(nn); + + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) + maxrate->tc_maxrate[i] = dcb->tc_maxrate[i]; + + return 0; +} + +static int nfp_nic_dcbnl_ieee_setmaxrate(struct net_device *dev, + struct ieee_maxrate *maxrate) +{ + const u32 cmd = NFP_NET_CFG_MBOX_CMD_DCB_UPDATE; + struct nfp_net *nn = netdev_priv(dev); + struct nfp_app *app = nn->app; + struct nfp_dcb *dcb; + u32 update = 0; + int err; + + err = nfp_fill_maxrate(nn, maxrate->tc_maxrate); + if (err) { + nfp_warn(app->cpp, + "nfp dcbnl ieee setmaxrate ERROR:%d.", + err); + return err; + } + + dcb = get_dcb_priv(nn); + + dcb->rate_init = true; + nfp_nic_set_enable(nn, NFP_DCB_ALL_QOS_ENABLE, &update); + nfp_nic_set_trust(nn, &update); + nfp_nic_ets_init(nn, &update); + + err = nfp_net_mbox_lock(nn, NFP_DCB_UPDATE_MSK_SZ); + if (err) + return err; + + nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL, + update | NFP_DCB_MSG_MSK_RATE); + + return nfp_net_mbox_reconfig_and_unlock(nn, cmd); +} + +static int nfp_nic_set_trust_status(struct nfp_net *nn, u8 status) +{ + const u32 cmd = NFP_NET_CFG_MBOX_CMD_DCB_UPDATE; + struct nfp_dcb *dcb; + u32 update = 0; + int err; + + dcb = get_dcb_priv(nn); + if (!dcb->rate_init) { + err = nfp_fill_maxrate(nn, dcb->tc_maxrate); + if (err) + return err; + + update |= NFP_DCB_MSG_MSK_RATE; + dcb->rate_init = true; + } + + err = nfp_net_mbox_lock(nn, NFP_DCB_UPDATE_MSK_SZ); + if (err) + return err; + + nfp_nic_ets_init(nn, &update); + writeb(status, dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_TRUST); + nfp_nic_set_enable(nn, NFP_DCB_ALL_QOS_ENABLE, &update); + nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL, + update | NFP_DCB_MSG_MSK_TRUST); + + err = nfp_net_mbox_reconfig_and_unlock(nn, cmd); + if (err) + return err; + + dcb->trust_status = status; + + return 0; +} + +static int nfp_nic_set_dscp2prio(struct nfp_net *nn, u8 dscp, u8 prio) +{ + const u32 cmd = NFP_NET_CFG_MBOX_CMD_DCB_UPDATE; + struct nfp_dcb *dcb; + u8 idx, tc; + int err; + + err = nfp_net_mbox_lock(nn, NFP_DCB_UPDATE_MSK_SZ); + if (err) + return err; + + dcb = get_dcb_priv(nn); + + tc = dcb->prio2tc[prio]; + idx = dcb->tc2idx[tc]; + + writeb(idx, dcb->dcbcfg_tbl + dcb->cfg_offset + + NFP_DCB_DATA_OFF_DSCP2IDX + dscp); + + nn_writel(nn, nn->tlv_caps.mbox_off + + NFP_NET_CFG_MBOX_SIMPLE_VAL, NFP_DCB_MSG_MSK_DSCP); + + err = nfp_net_mbox_reconfig_and_unlock(nn, cmd); + if (err) + return err; + + dcb->dscp2prio[dscp] = prio; + + return 0; +} + +static int nfp_nic_dcbnl_ieee_setapp(struct net_device *dev, + struct dcb_app *app) +{ + struct nfp_net *nn = netdev_priv(dev); + struct dcb_app old_app; + struct nfp_dcb *dcb; + bool is_new; + int err; + + if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP) + return -EINVAL; + + dcb = get_dcb_priv(nn); + + /* Save the old entry info */ + old_app.selector = IEEE_8021QAZ_APP_SEL_DSCP; + old_app.protocol = app->protocol; + old_app.priority = dcb->dscp2prio[app->protocol]; + + /* Check trust status */ + if (!dcb->dscp_cnt) { + err = nfp_nic_set_trust_status(nn, NFP_DCB_TRUST_DSCP); + if (err) + return err; + } + + /* Check if the new mapping is same as old or in init stage */ + if (app->priority != old_app.priority || app->priority == 0) { + err = nfp_nic_set_dscp2prio(nn, app->protocol, app->priority); + if (err) + return err; + } + + /* Delete the old entry if exists */ + is_new = !!dcb_ieee_delapp(dev, &old_app); + + /* Add new entry and update counter */ + err = dcb_ieee_setapp(dev, app); + if (err) + return err; + + if (is_new) + dcb->dscp_cnt++; + + return 0; +} + +static int nfp_nic_dcbnl_ieee_delapp(struct net_device *dev, + struct dcb_app *app) +{ + struct nfp_net *nn = netdev_priv(dev); + struct nfp_dcb *dcb; + int err; + + if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP) + return -EINVAL; + + dcb = get_dcb_priv(nn); + + /* Check if the dcb_app param match fw */ + if (app->priority != dcb->dscp2prio[app->protocol]) + return -ENOENT; + + /* Set fw dscp mapping to 0 */ + err = nfp_nic_set_dscp2prio(nn, app->protocol, 0); + if (err) + return err; + + /* Delete app from dcb list */ + err = dcb_ieee_delapp(dev, app); + if (err) + return err; + + /* Decrease dscp counter */ + dcb->dscp_cnt--; + + /* If no dscp mapping is configured, trust pcp */ + if (dcb->dscp_cnt == 0) + return nfp_nic_set_trust_status(nn, NFP_DCB_TRUST_PCP); + + return 0; +} + +static const struct dcbnl_rtnl_ops nfp_nic_dcbnl_ops = { + /* ieee 802.1Qaz std */ + .ieee_getets = nfp_nic_dcbnl_ieee_getets, + .ieee_setets = nfp_nic_dcbnl_ieee_setets, + .ieee_getmaxrate = nfp_nic_dcbnl_ieee_getmaxrate, + .ieee_setmaxrate = nfp_nic_dcbnl_ieee_setmaxrate, + .ieee_setapp = nfp_nic_dcbnl_ieee_setapp, + .ieee_delapp = nfp_nic_dcbnl_ieee_delapp, +}; + +int nfp_nic_dcb_init(struct nfp_net *nn) +{ + struct nfp_app *app = nn->app; + struct nfp_dcb *dcb; + int err; + + dcb = get_dcb_priv(nn); + dcb->cfg_offset = NFP_DCB_CFG_STRIDE * nn->id; + dcb->dcbcfg_tbl = nfp_pf_map_rtsym(app->pf, "net.dcbcfg_tbl", + "_abi_dcb_cfg", + dcb->cfg_offset, &dcb->dcbcfg_tbl_area); + if (IS_ERR(dcb->dcbcfg_tbl)) { + if (PTR_ERR(dcb->dcbcfg_tbl) != -ENOENT) { + err = PTR_ERR(dcb->dcbcfg_tbl); + dcb->dcbcfg_tbl = NULL; + nfp_err(app->cpp, + "Failed to map dcbcfg_tbl area, min_size %u.\n", + dcb->cfg_offset); + return err; + } + dcb->dcbcfg_tbl = NULL; + } + + if (dcb->dcbcfg_tbl) { + for (unsigned int i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + dcb->prio2tc[i] = i; + dcb->tc2idx[i] = i; + dcb->tc_tx_pct[i] = 0; + dcb->tc_maxrate[i] = 0; + dcb->tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; + } + dcb->trust_status = NFP_DCB_TRUST_INVALID; + dcb->rate_init = false; + dcb->ets_init = false; + + nn->dp.netdev->dcbnl_ops = &nfp_nic_dcbnl_ops; + } + + return 0; +} + +void nfp_nic_dcb_clean(struct nfp_net *nn) +{ + struct nfp_dcb *dcb; + + dcb = get_dcb_priv(nn); + if (dcb->dcbcfg_tbl_area) + nfp_cpp_area_release_free(dcb->dcbcfg_tbl_area); +} |