forked from Minki/linux
net: thunderx: Pause frame support
Enable pause frames on both Rx and Tx side, configure pause interval e.t.c. Also support for enable/disable pause frames on Rx/Tx via ethtool has been added. Signed-off-by: Sunil Goutham <sgoutham@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d5b2d7a718
commit
430da20808
@ -149,6 +149,12 @@ struct nicvf_rss_info {
|
||||
u64 key[RSS_HASH_KEY_SIZE];
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
struct nicvf_pfc {
|
||||
u8 autoneg;
|
||||
u8 fc_rx;
|
||||
u8 fc_tx;
|
||||
};
|
||||
|
||||
enum rx_stats_reg_offset {
|
||||
RX_OCTS = 0x0,
|
||||
RX_UCAST = 0x1,
|
||||
@ -298,6 +304,7 @@ struct nicvf {
|
||||
bool tns_mode;
|
||||
bool loopback_supported;
|
||||
struct nicvf_rss_info rss_info;
|
||||
struct nicvf_pfc pfc;
|
||||
struct tasklet_struct qs_err_task;
|
||||
struct work_struct reset_task;
|
||||
|
||||
@ -358,6 +365,7 @@ struct nicvf {
|
||||
#define NIC_MBOX_MSG_SNICVF_PTR 0x15 /* Send sqet nicvf ptr to PVF */
|
||||
#define NIC_MBOX_MSG_LOOPBACK 0x16 /* Set interface in loopback */
|
||||
#define NIC_MBOX_MSG_RESET_STAT_COUNTER 0x17 /* Reset statistics counters */
|
||||
#define NIC_MBOX_MSG_PFC 0x18 /* Pause frame control */
|
||||
#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */
|
||||
#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */
|
||||
|
||||
@ -500,6 +508,14 @@ struct reset_stat_cfg {
|
||||
u16 sq_stat_mask;
|
||||
};
|
||||
|
||||
struct pfc {
|
||||
u8 msg;
|
||||
u8 get; /* Get or set PFC settings */
|
||||
u8 autoneg;
|
||||
u8 fc_rx;
|
||||
u8 fc_tx;
|
||||
};
|
||||
|
||||
/* 128 bit shared memory between PF and each VF */
|
||||
union nic_mbx {
|
||||
struct { u8 msg; } msg;
|
||||
@ -518,6 +534,7 @@ union nic_mbx {
|
||||
struct nicvf_ptr nicvf;
|
||||
struct set_loopback lbk;
|
||||
struct reset_stat_cfg reset_stat;
|
||||
struct pfc pfc;
|
||||
};
|
||||
|
||||
#define NIC_NODE_ID_MASK 0x03
|
||||
|
@ -898,6 +898,30 @@ static void nic_enable_vf(struct nicpf *nic, int vf, bool enable)
|
||||
bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, enable);
|
||||
}
|
||||
|
||||
static void nic_pause_frame(struct nicpf *nic, int vf, struct pfc *cfg)
|
||||
{
|
||||
int bgx, lmac;
|
||||
struct pfc pfc;
|
||||
union nic_mbx mbx = {};
|
||||
|
||||
if (vf >= nic->num_vf_en)
|
||||
return;
|
||||
bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
|
||||
lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
|
||||
|
||||
if (cfg->get) {
|
||||
bgx_lmac_get_pfc(nic->node, bgx, lmac, &pfc);
|
||||
mbx.pfc.msg = NIC_MBOX_MSG_PFC;
|
||||
mbx.pfc.autoneg = pfc.autoneg;
|
||||
mbx.pfc.fc_rx = pfc.fc_rx;
|
||||
mbx.pfc.fc_tx = pfc.fc_tx;
|
||||
nic_send_msg_to_vf(nic, vf, &mbx);
|
||||
} else {
|
||||
bgx_lmac_set_pfc(nic->node, bgx, lmac, cfg);
|
||||
nic_mbx_send_ack(nic, vf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Interrupt handler to handle mailbox messages from VFs */
|
||||
static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
|
||||
{
|
||||
@ -1037,6 +1061,9 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
|
||||
case NIC_MBOX_MSG_RESET_STAT_COUNTER:
|
||||
ret = nic_reset_stat_counters(nic, vf, &mbx.reset_stat);
|
||||
break;
|
||||
case NIC_MBOX_MSG_PFC:
|
||||
nic_pause_frame(nic, vf, &mbx.pfc);
|
||||
goto unlock;
|
||||
default:
|
||||
dev_err(&nic->pdev->dev,
|
||||
"Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg);
|
||||
|
@ -720,6 +720,55 @@ static int nicvf_set_channels(struct net_device *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nicvf_get_pauseparam(struct net_device *dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct nicvf *nic = netdev_priv(dev);
|
||||
union nic_mbx mbx = {};
|
||||
|
||||
/* Supported only for 10G/40G interfaces */
|
||||
if ((nic->mac_type == BGX_MODE_SGMII) ||
|
||||
(nic->mac_type == BGX_MODE_QSGMII) ||
|
||||
(nic->mac_type == BGX_MODE_RGMII))
|
||||
return;
|
||||
|
||||
mbx.pfc.msg = NIC_MBOX_MSG_PFC;
|
||||
mbx.pfc.get = 1;
|
||||
if (!nicvf_send_msg_to_pf(nic, &mbx)) {
|
||||
pause->autoneg = nic->pfc.autoneg;
|
||||
pause->rx_pause = nic->pfc.fc_rx;
|
||||
pause->tx_pause = nic->pfc.fc_tx;
|
||||
}
|
||||
}
|
||||
|
||||
static int nicvf_set_pauseparam(struct net_device *dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct nicvf *nic = netdev_priv(dev);
|
||||
union nic_mbx mbx = {};
|
||||
|
||||
/* Supported only for 10G/40G interfaces */
|
||||
if ((nic->mac_type == BGX_MODE_SGMII) ||
|
||||
(nic->mac_type == BGX_MODE_QSGMII) ||
|
||||
(nic->mac_type == BGX_MODE_RGMII))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (pause->autoneg)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mbx.pfc.msg = NIC_MBOX_MSG_PFC;
|
||||
mbx.pfc.get = 0;
|
||||
mbx.pfc.fc_rx = pause->rx_pause;
|
||||
mbx.pfc.fc_tx = pause->tx_pause;
|
||||
if (nicvf_send_msg_to_pf(nic, &mbx))
|
||||
return -EAGAIN;
|
||||
|
||||
nic->pfc.fc_rx = pause->rx_pause;
|
||||
nic->pfc.fc_tx = pause->tx_pause;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops nicvf_ethtool_ops = {
|
||||
.get_settings = nicvf_get_settings,
|
||||
.get_link = nicvf_get_link,
|
||||
@ -741,6 +790,8 @@ static const struct ethtool_ops nicvf_ethtool_ops = {
|
||||
.set_rxfh = nicvf_set_rxfh,
|
||||
.get_channels = nicvf_get_channels,
|
||||
.set_channels = nicvf_set_channels,
|
||||
.get_pauseparam = nicvf_get_pauseparam,
|
||||
.set_pauseparam = nicvf_set_pauseparam,
|
||||
.get_ts_info = ethtool_op_get_ts_info,
|
||||
};
|
||||
|
||||
|
@ -256,6 +256,12 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic)
|
||||
nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf;
|
||||
nic->pf_acked = true;
|
||||
break;
|
||||
case NIC_MBOX_MSG_PFC:
|
||||
nic->pfc.autoneg = mbx.pfc.autoneg;
|
||||
nic->pfc.fc_rx = mbx.pfc.fc_rx;
|
||||
nic->pfc.fc_tx = mbx.pfc.fc_tx;
|
||||
nic->pf_acked = true;
|
||||
break;
|
||||
default:
|
||||
netdev_err(nic->netdev,
|
||||
"Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
|
||||
|
@ -212,6 +212,47 @@ void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
|
||||
}
|
||||
EXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
|
||||
|
||||
void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause)
|
||||
{
|
||||
struct pfc *pfc = (struct pfc *)pause;
|
||||
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
|
||||
struct lmac *lmac;
|
||||
u64 cfg;
|
||||
|
||||
if (!bgx)
|
||||
return;
|
||||
lmac = &bgx->lmac[lmacid];
|
||||
if (lmac->is_sgmii)
|
||||
return;
|
||||
|
||||
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
|
||||
pfc->fc_rx = cfg & RX_EN;
|
||||
pfc->fc_tx = cfg & TX_EN;
|
||||
pfc->autoneg = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bgx_lmac_get_pfc);
|
||||
|
||||
void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause)
|
||||
{
|
||||
struct pfc *pfc = (struct pfc *)pause;
|
||||
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
|
||||
struct lmac *lmac;
|
||||
u64 cfg;
|
||||
|
||||
if (!bgx)
|
||||
return;
|
||||
lmac = &bgx->lmac[lmacid];
|
||||
if (lmac->is_sgmii)
|
||||
return;
|
||||
|
||||
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
|
||||
cfg &= ~(RX_EN | TX_EN);
|
||||
cfg |= (pfc->fc_rx ? RX_EN : 0x00);
|
||||
cfg |= (pfc->fc_tx ? TX_EN : 0x00);
|
||||
bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg);
|
||||
}
|
||||
EXPORT_SYMBOL(bgx_lmac_set_pfc);
|
||||
|
||||
static void bgx_sgmii_change_link_state(struct lmac *lmac)
|
||||
{
|
||||
struct bgx *bgx = lmac->bgx;
|
||||
@ -525,6 +566,18 @@ static int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac)
|
||||
cfg |= SMU_TX_CTL_DIC_EN;
|
||||
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
|
||||
|
||||
/* Enable receive and transmission of pause frames */
|
||||
bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) |
|
||||
BCK_EN | DRP_EN | TX_EN | RX_EN));
|
||||
/* Configure pause time and interval */
|
||||
bgx_reg_write(bgx, lmacid,
|
||||
BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME);
|
||||
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL);
|
||||
cfg &= ~0xFFFFull;
|
||||
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL,
|
||||
cfg | (DEFAULT_PAUSE_TIME - 0x1000));
|
||||
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01);
|
||||
|
||||
/* take lmac_count into account */
|
||||
bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
|
||||
/* max packet size */
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define MAX_BGX_CHANS_PER_LMAC 16
|
||||
#define MAX_DMAC_PER_LMAC 8
|
||||
#define MAX_FRAME_SIZE 9216
|
||||
#define DEFAULT_PAUSE_TIME 0xFFFF
|
||||
|
||||
#define BGX_ID_MASK 0x3
|
||||
|
||||
@ -126,7 +127,10 @@
|
||||
#define SMU_RX_CTL_STATUS (3ull << 0)
|
||||
#define BGX_SMUX_TX_APPEND 0x20100
|
||||
#define SMU_TX_APPEND_FCS_D BIT_ULL(2)
|
||||
#define BGX_SMUX_TX_PAUSE_PKT_TIME 0x20110
|
||||
#define BGX_SMUX_TX_MIN_PKT 0x20118
|
||||
#define BGX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120
|
||||
#define BGX_SMUX_TX_PAUSE_ZERO 0x20138
|
||||
#define BGX_SMUX_TX_INT 0x20140
|
||||
#define BGX_SMUX_TX_CTL 0x20178
|
||||
#define SMU_TX_CTL_DIC_EN BIT_ULL(0)
|
||||
@ -136,6 +140,11 @@
|
||||
#define BGX_SMUX_CTL 0x20200
|
||||
#define SMU_CTL_RX_IDLE BIT_ULL(0)
|
||||
#define SMU_CTL_TX_IDLE BIT_ULL(1)
|
||||
#define BGX_SMUX_CBFC_CTL 0x20218
|
||||
#define RX_EN BIT_ULL(0)
|
||||
#define TX_EN BIT_ULL(1)
|
||||
#define BCK_EN BIT_ULL(2)
|
||||
#define DRP_EN BIT_ULL(3)
|
||||
|
||||
#define BGX_GMP_PCS_MRX_CTL 0x30000
|
||||
#define PCS_MRX_CTL_RST_AN BIT_ULL(9)
|
||||
@ -207,6 +216,9 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac);
|
||||
void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status);
|
||||
void bgx_lmac_internal_loopback(int node, int bgx_idx,
|
||||
int lmac_idx, bool enable);
|
||||
void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause);
|
||||
void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause);
|
||||
|
||||
void xcv_init_hw(void);
|
||||
void xcv_setup_link(bool link_up, int link_speed);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user