// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2023 Corigine, Inc. */ #include #include #include #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); }