--- /dev/null
+// 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);
+}