2026-01-29 22:25:33 +08:00

333 lines
7.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2022 - 2023 Mucse Corporation. */
#include <linux/dcbnl.h>
#ifdef CONFIG_MXGBE_DCB
#include "rnp.h"
#include "rnp_dcb.h"
#include "rnp_sriov.h"
#include "rnp_common.h"
static void rnp_config_prio_map(struct rnp_adapter *adapter, u8 pfc_map)
{
int i, j;
u32 prio_map = 0;
u8 port = adapter->port;
u8 *prio_tc = adapter->prio_tc_map;
void __iomem *ioaddr = adapter->hw.hw_addr;
u8 num_tc = adapter->num_tc;
for (i = 0; i < num_tc; i++) {
if (i > RNP_MAX_TCS_NUM)
break;
for (j = 0; j < RNP_MAX_USER_PRIO; j++) {
dbg("prio_tc[%d]==%d tc_num[%d] pfc_map 0x%.2x\n",
j, prio_tc[j], i, pfc_map);
if ((prio_tc[j] == i) && (pfc_map & BIT(j))) {
dbg("match rule tc_num %d prio_%d\n", i,
j);
prio_map |= (i << (2 * j));
dbg("match prio_tc change to 0x%.2x\n",
prio_map);
}
}
}
/* config untage pkt fifo */
/* we just have four tc fifo and one fifo is must belong to untage-pkt
* so untage need map to the remain tc fifio
*/
prio_map |= i << RNP_FC_UNCTAGS_MAP_OFFSET;
prio_map |= (1 << 30) | (1 << 31);
rnp_wr_reg(ioaddr + RNP_FC_PORT_PRIO_MAP(port), prio_map);
dbg("tc_prio_map[%d] 0x%.2x\n", i, prio_map);
/* enable port prio_map config */
rnp_wr_reg(ioaddr + RNP_FC_EN_CONF_AVAILABLE, 1);
}
static int rnp_dcb_hw_pfc_config(struct rnp_adapter *adapter, u8 pfc_map)
{
struct rnp_dcb_cfg *dcb = &adapter->dcb_cfg;
void __iomem *ioaddr = adapter->hw.hw_addr;
u8 i = 0, j = 0;
u32 reg = 0;
u8 num_tc = adapter->num_tc;
if (!(adapter->flags & RNP_FLAG_DCB_ENABLED) ||
adapter->num_rx_queues <= 1) {
dev_warn(&adapter->pdev->dev,
"don't support pfc when rx quene less than 1 or disable dcb feature\n");
return 0;
}
/* 1.Enable Receive Priority Flow Control */
reg = RNP_RX_RFE | RNP_PFCE;
rnp_wr_reg(ioaddr + RNP_MAC_RX_FLOW_CTRL, reg);
/* 2.Configure which port will in pfc mode*/
reg = rnp_rd_reg(ioaddr + RNP_FC_PORT_ENABLE);
/* 3.For Now just support two port Version So just enabled
* PF port 0 to enable flow control
*/
reg |= 1 << adapter->port;
rnp_wr_reg(ioaddr + RNP_FC_PORT_ENABLE, reg);
for (i = 0; i < num_tc; i++) {
int enabled = 0;
for (j = 0; j < RNP_MAX_USER_PRIO; j++) {
if ((adapter->prio_tc_map[j] == i) &&
(pfc_map & BIT(j))) {
enabled = 1;
dcb->pfc_cfg.hw_pfc_map |= BIT(j);
dcb->pfc_cfg.pfc_num++;
break;
}
}
if (enabled) {
/* 4.Enable Transmit Priority Flow Control */
reg = RNP_TX_TFE |
(RNP_PAUSE_28_SLOT_TIME
<< RNP_FC_TX_PLTH_OFFSET) |
(RNP_DEFAULT_PAUSE_TIME
<< RNP_FC_TX_PT_OFFSET);
rnp_wr_reg(ioaddr + RNP_MAC_Q0_TX_FLOW_CTRL(j),
reg);
}
}
/* the below configure can just use default config */
/* 5.config for pri_map */
rnp_config_prio_map(adapter, pfc_map);
/* 6.Configure PFC Rx high/low thresholds per TC */
/* 7.Configure Rx full/empty thresholds per tc*/
/* 8.Configure pause time (3 TCs per register) */
/* 9.Configure flow control pause low threshold value */
return 0;
}
static int rnp_dcbnl_getpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct rnp_adapter *adapter = netdev_priv(dev);
struct rnp_dcb_cfg *dcb = &adapter->dcb_cfg;
u8 i = 0, j = 0;
memset(pfc, 0, sizeof(*pfc));
pfc->pfc_cap = dcb->pfc_cfg.pfc_max;
/* Pfc setting is based on TC */
for (i = 0; i < adapter->num_tc; i++) {
for (j = 0; j < RNP_MAX_USER_PRIO; j++) {
if ((adapter->prio_tc_map[j] == i) &&
(dcb->pfc_cfg.hw_pfc_map & BIT(i)))
pfc->pfc_en |= BIT(j);
}
}
/* do we need to get the pfc statistic*/
/* 1. get the tc channel send and recv pfc pkts*/
/*
*/
return 0;
}
/* rnp Support IEEE 802.3 flow-control and
* Priority base flow control (PFC)
*/
static u8 rnp_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap)
{
struct rnp_adapter *priv = netdev_priv(net_dev);
switch (capid) {
case DCB_CAP_ATTR_PFC:
*cap = true;
break;
case DCB_CAP_ATTR_PFC_TCS:
*cap = 0x80;
break;
case DCB_CAP_ATTR_DCBX:
*cap = priv->dcb_cfg.dcbx_mode;
break;
default:
*cap = false;
break;
}
return 0;
}
static u8 rnp_dcbnl_getstate(struct net_device *netdev)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
return !!(adapter->flags & RNP_FLAG_DCB_ENABLED);
}
static u8 rnp_dcbnl_setstate(struct net_device *netdev, u8 state)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
int err = 0;
/* verify there is something to do, if not then exit */
if (!state == !(adapter->flags & RNP_FLAG_DCB_ENABLED))
goto out;
err = rnp_setup_tc(netdev,
state ? adapter->dcb_cfg.num_tcs.pfc_tcs : 0);
out:
return !!err;
}
static u8 rnp_dcbnl_getdcbx(struct net_device *net_dev)
{
struct rnp_adapter *adapter = netdev_priv(net_dev);
return adapter->dcb_cfg.dcbx_mode;
}
static u8 rnp_dcbnl_setdcbx(struct net_device *net_dev, u8 mode)
{
struct rnp_adapter *adapter = netdev_priv(net_dev);
adapter->dcb_cfg.dcbx_mode = mode;
return 0;
}
static int rnp_dcbnl_getnumtcs(struct net_device *netdev, int tcid,
u8 *num)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
u8 rval = 0;
if (adapter->flags & RNP_FLAG_DCB_ENABLED) {
switch (tcid) {
case DCB_NUMTCS_ATTR_PFC:
if (adapter->dcb_cfg.num_tcs.pfc_tcs >
RNP_MAX_TCS_NUM) {
rval = -EINVAL;
break;
}
*num = adapter->dcb_cfg.num_tcs.pfc_tcs;
break;
default:
rval = -EINVAL;
break;
}
} else {
rval = -EINVAL;
}
return rval;
}
static int rnp_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
u8 rval = 0;
if (adapter->flags & RNP_FLAG_DCB_ENABLED) {
switch (tcid) {
case DCB_NUMTCS_ATTR_PFC:
adapter->dcb_cfg.num_tcs.pfc_tcs = num;
break;
default:
rval = -EINVAL;
break;
}
} else {
rval = -EINVAL;
}
return rval;
}
static int rnp_dcb_parse_config(struct rnp_dcb_cfg *dcb,
struct ieee_pfc *pfc)
{
u8 j = 0, pfc_en_num = 0, pfc_map = 0;
for (j = 0; j < RNP_MAX_USER_PRIO; j++) {
if ((pfc->pfc_en & BIT(j))) {
pfc_map |= BIT(j);
pfc_en_num++;
}
}
dcb->pfc_cfg.pfc_num = pfc_en_num;
dcb->pfc_cfg.hw_pfc_map = pfc_map;
dbg("pfc_map 0x%.2x pfc->pfc_en 0x%.2x\n", pfc_map, pfc->pfc_en);
/* tc resource rebuild */
/* we need to decide tx_ring bind to tc 4 fifo-mac*/
return pfc_map;
}
static int rnp_dcbnl_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct rnp_adapter *adapter = netdev_priv(dev);
struct rnp_dcb_cfg *dcb = &adapter->dcb_cfg;
u8 pfc_map = 0;
dbg("%s:%d pfc enabled %d\n", __func__, __LINE__, pfc->pfc_en);
if (pfc->pfc_en) {
/*set PFC Priority mask */
pfc_map = rnp_dcb_parse_config(dcb, pfc);
rnp_dcb_hw_pfc_config(adapter, pfc_map);
}
return 0;
}
static u8 rnp_dcbnl_getpfcstate(struct net_device *netdev)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
struct rnp_pfc_cfg *pfc_cfg = &adapter->dcb_cfg.pfc_cfg;
return pfc_cfg->pfc_en;
}
static void rnp_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
{
struct rnp_adapter *adapter = netdev_priv(netdev);
adapter->dcb_cfg.pfc_cfg.pfc_en = state;
}
const struct dcbnl_rtnl_ops rnp_dcbnl_ops = {
/*IEEE*/
.ieee_getpfc = rnp_dcbnl_getpfc,
.ieee_setpfc = rnp_dcbnl_setpfc,
.getcap = rnp_dcbnl_getcap,
.setdcbx = rnp_dcbnl_setdcbx,
.getdcbx = rnp_dcbnl_getdcbx,
.getnumtcs = rnp_dcbnl_getnumtcs,
.setnumtcs = rnp_dcbnl_setnumtcs,
/*CEE*/
.getstate = rnp_dcbnl_getstate,
.setstate = rnp_dcbnl_setstate,
.getpfcstate = rnp_dcbnl_getpfcstate,
.setpfcstate = rnp_dcbnl_setpfcstate,
};
int rnp_dcb_init(struct net_device *dev, struct rnp_adapter *adapter)
{
struct rnp_dcb_cfg *dcb = &adapter->dcb_cfg;
struct rnp_hw *hw = &adapter->hw;
if ((hw->hw_type != rnp_hw_n10) &&
(hw->hw_type != rnp_hw_n400))
return 0;
dcb->dcb_en = false;
dcb->pfc_cfg.pfc_max = RNP_MAX_TCS_NUM;
dcb->num_tcs.pfc_tcs = RNP_MAX_TCS_NUM;
dcb->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
dev->dcbnl_ops = &rnp_dcbnl_ops;
return 0;
}
#endif