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

596 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2022 - 2024 Mucse Corporation. */
#include "rnpgbevf_mbx.h"
#include "rnpgbevf.h"
static s32 rnpgbevf_poll_for_msg(struct rnpgbevf_hw *hw, bool to_cm3);
static s32 rnpgbevf_poll_for_ack(struct rnpgbevf_hw *hw, bool to_cm3);
/**
* rnpgbevf_read_posted_mbx - Wait for message notification and receive message
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully received a message notification and
* copied it into the receive buffer.
**/
static s32 rnpgbevf_read_posted_mbx(struct rnpgbevf_hw *hw, u32 *msg, u16 size,
bool to_cm3)
{
struct rnp_mbx_info *mbx = &hw->mbx;
s32 ret_val = -RNPGBE_ERR_MBX;
if (!mbx->ops.read)
goto out;
ret_val = rnpgbevf_poll_for_msg(hw, to_cm3);
/* if ack received read message, otherwise we timed out */
if (!ret_val)
ret_val = mbx->ops.read(hw, msg, size, to_cm3);
out:
return ret_val;
}
/**
* rnpgbevf_write_posted_mbx - Write a message to the mailbox, wait for ack
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully copied message into the buffer and
* received an ack to that message within delay * timeout period
**/
static s32 rnpgbevf_write_posted_mbx(struct rnpgbevf_hw *hw, u32 *msg, u16 size,
bool to_cm3)
{
struct rnp_mbx_info *mbx = &hw->mbx;
s32 ret_val = -RNPGBE_ERR_MBX;
/* exit if either we can't write or there isn't a defined timeout */
if (!mbx->ops.write || !mbx->timeout)
goto out;
/* send msg */
ret_val = mbx->ops.write(hw, msg, size, to_cm3);
/* if msg sent wait until we receive an ack */
if (!ret_val)
ret_val = rnpgbevf_poll_for_ack(hw, to_cm3);
out:
return ret_val;
}
static inline u16 rnpgbevf_mbx_get_req(struct rnpgbevf_hw *hw, int reg)
{
/* memory barrier */
mb();
return mbx_rd32(hw, reg) & 0xffff;
}
static inline u16 rnpgbevf_mbx_get_ack(struct rnpgbevf_hw *hw, int reg)
{
/* memory barrier */
mb();
return (mbx_rd32(hw, reg) >> 16) & 0xffff;
}
static inline void rnpgbevf_mbx_inc_vfreq(struct rnpgbevf_hw *hw, bool to_cm3)
{
u16 req;
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
int reg =
to_cm3 ? VF2CPU_COUNTER(mbx, vfnum) : VF2PF_COUNTER(mbx, vfnum);
u32 v = mbx_rd32(hw, reg);
req = (v & 0xffff);
req++;
v &= ~(0x0000ffff);
v |= req;
/* memory barrier */
mb();
mbx_wr32(hw, reg, v);
/* update stats */
hw->mbx.stats.msgs_tx++;
}
static inline void rnpgbevf_mbx_inc_vfack(struct rnpgbevf_hw *hw, bool to_cm3)
{
u16 ack;
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
int reg =
to_cm3 ? VF2CPU_COUNTER(mbx, vfnum) : VF2PF_COUNTER(mbx, vfnum);
u32 v = mbx_rd32(hw, reg);
ack = (v >> 16) & 0xffff;
ack++;
v &= ~(0xffff0000);
v |= (ack << 16);
/* memory barrier */
mb();
mbx_wr32(hw, reg, v);
/* update stats */
hw->mbx.stats.msgs_rx++;
}
/**
* rnpgbevf_check_for_msg_vf - checks to see if the PF has sent mail
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* returns 0 if the PF has set the Status bit or else ERR_MBX
**/
static s32 rnpgbevf_check_for_msg_vf(struct rnpgbevf_hw *hw, bool to_cm3)
{
s32 ret_val = RNPGBE_ERR_MBX;
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
if (to_cm3) {
if (rnpgbevf_mbx_get_req(hw, CPU2VF_COUNTER(mbx, vfnum)) !=
hw->mbx.cpu_req) {
ret_val = 0;
hw->mbx.stats.reqs++;
}
} else {
if (rnpgbevf_mbx_get_req(hw, PF2VF_COUNTER(mbx, vfnum)) !=
hw->mbx.pf_req) {
ret_val = 0;
hw->mbx.stats.reqs++;
}
}
return ret_val;
}
/**
* rnpgbevf_poll_for_msg - Wait for message notification
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully received a message notification
**/
static s32 rnpgbevf_poll_for_msg(struct rnpgbevf_hw *hw, bool to_cm3)
{
struct rnp_mbx_info *mbx = &hw->mbx;
int countdown = mbx->timeout;
while (countdown && mbx->ops.check_for_msg(hw, to_cm3)) {
countdown--;
udelay(mbx->udelay);
}
return countdown ? 0 : RNPGBE_ERR_MBX;
}
/**
* rnpgbevf_poll_for_ack - Wait for message acknowledgment
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully received a message acknowledgment
**/
static s32 rnpgbevf_poll_for_ack(struct rnpgbevf_hw *hw, bool to_cm3)
{
struct rnp_mbx_info *mbx = &hw->mbx;
int countdown = mbx->timeout;
while (countdown && mbx->ops.check_for_ack(hw, to_cm3)) {
countdown--;
udelay(mbx->udelay);
}
/* if we failed, all future posted messages fail until reset */
if (!countdown) {
mbx->timeout = 0;
dbg("%s timeout\n", __func__);
}
return countdown ? 0 : RNPGBE_ERR_MBX;
}
/**
* rnpgbevf_check_for_rst_msg_vf - checks to see if the PF has ACK'd
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* returns 0 if the PF has set the ACK bit or else ERR_MBX
**/
static s32 rnpgbevf_check_for_rst_msg_vf(struct rnpgbevf_hw *hw, bool to_cm3)
{
struct rnpgbevf_adapter *adapter = hw->back;
struct rnp_mbx_info *mbx = &hw->mbx;
s32 ret_val = RNPGBE_ERR_MBX;
u8 vfnum = VFNUM(mbx, hw->vfnum);
u32 DATA_REG = (to_cm3) ? CPU_VF_SHM_DATA(mbx, vfnum) :
PF_VF_SHM_DATA(mbx, vfnum);
u32 data;
int ret = 1;
ret_val = rnpgbevf_check_for_msg_vf(hw, to_cm3);
if (!ret_val) {
mbx->ops.read(hw, &data, 1, 0);
data &= ~RNPGBE_PF_VFNUM_MASK;
/* add other mailbox setup */
if (((data) & (~RNPGBE_VT_MSGTYPE_CTS)) ==
RNPGBE_PF_CONTROL_PRING_MSG) {
} else if ((data) == RNPGBE_PF_SET_FCS) {
// to-do
data = mbx_rd32(hw, DATA_REG + 4);
if (data) {
adapter->priv_flags |= RNPVF_PRIV_FLAG_FCS_ON;
adapter->netdev->features |= NETIF_F_RXFCS;
} else {
adapter->priv_flags &=
(~RNPVF_PRIV_FLAG_FCS_ON);
adapter->netdev->features &= (~NETIF_F_RXFCS);
}
if ((adapter->priv_flags & RNPVF_PRIV_FLAG_FCS_ON) &&
(adapter->netdev->features & NETIF_F_RXCSUM)) {
adapter->netdev->features &= (~NETIF_F_RXCSUM);
} else {
/* set back rx-chksum status */
if (adapter->flags &
RNPVF_FLAG_RX_CHKSUM_ENABLED)
adapter->netdev->features |=
NETIF_F_RXCSUM;
else
adapter->netdev->features &=
(~NETIF_F_RXCSUM);
}
} else if ((data) == RNPGBE_PF_SET_PAUSE) {
hw->fc.current_mode = mbx_rd32(hw, DATA_REG + 4);
} else if ((data) == RNPGBE_PF_SET_FT_PADDING) {
data = mbx_rd32(hw, DATA_REG + 4);
if (data) {
adapter->priv_flags |=
RNPVF_PRIV_FLAG_FT_PADDING;
} else {
adapter->priv_flags &=
(~RNPVF_PRIV_FLAG_FT_PADDING);
}
} else if ((data) == RNPGBE_PF_SET_VLAN_FILTER) {
data = mbx_rd32(hw, DATA_REG + 4);
if (data) {
if (hw->feature_flags &
RNPVF_NET_FEATURE_VLAN_OFFLOAD) {
adapter->netdev->features |=
NETIF_F_HW_VLAN_CTAG_FILTER;
}
if (hw->feature_flags &
RNPVF_NET_FEATURE_STAG_OFFLOAD) {
adapter->netdev->features |=
NETIF_F_HW_VLAN_STAG_FILTER;
}
} else {
if (hw->feature_flags &
RNPVF_NET_FEATURE_VLAN_OFFLOAD) {
adapter->netdev->features &=
~NETIF_F_HW_VLAN_CTAG_FILTER;
}
if (hw->feature_flags &
RNPVF_NET_FEATURE_STAG_OFFLOAD) {
adapter->netdev->features &=
~NETIF_F_HW_VLAN_STAG_FILTER;
}
}
} else if ((data) == RNPGBE_PF_SET_VLAN) {
struct rnp_mbx_info *mbx = &hw->mbx;
data = mbx_rd32(hw, DATA_REG + 4);
adapter->flags |= RNPVF_FLAG_PF_UPDATE_VLAN;
if (data) {
adapter->flags |= RNPVF_FLAG_PF_SET_VLAN;
adapter->vf_vlan = data;
if (adapter->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
adapter->priv_flags |= RNPVF_FLAG_RX_CVLAN_OFFLOAD;
else
adapter->priv_flags &= ~RNPVF_FLAG_RX_CVLAN_OFFLOAD;
adapter->netdev->features |= NETIF_F_HW_VLAN_CTAG_RX;
if (adapter->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)
adapter->priv_flags |= RNPVF_FLAG_TX_CVLAN_OFFLOAD;
else
adapter->priv_flags &= ~RNPVF_FLAG_TX_CVLAN_OFFLOAD;
adapter->netdev->features &=
~NETIF_F_HW_VLAN_CTAG_TX;
if (adapter->netdev->features & NETIF_F_HW_VLAN_STAG_RX)
adapter->priv_flags |= RNPVF_FLAG_RX_SVLAN_OFFLOAD;
else
adapter->priv_flags &= ~RNPVF_FLAG_RX_SVLAN_OFFLOAD;
adapter->netdev->features |= NETIF_F_HW_VLAN_STAG_RX;
if (adapter->netdev->features & NETIF_F_HW_VLAN_STAG_TX)
adapter->priv_flags |= RNPVF_FLAG_TX_SVLAN_OFFLOAD;
else
adapter->priv_flags &= ~RNPVF_FLAG_TX_SVLAN_OFFLOAD;
adapter->netdev->features &= ~NETIF_F_HW_VLAN_STAG_TX;
} else {
adapter->flags &= (~RNPVF_FLAG_PF_SET_VLAN);
adapter->vf_vlan = 0;
if (adapter->priv_flags & RNPVF_FLAG_RX_CVLAN_OFFLOAD)
adapter->netdev->features |= NETIF_F_HW_VLAN_CTAG_RX;
else
adapter->netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
if (adapter->priv_flags & RNPVF_FLAG_TX_CVLAN_OFFLOAD)
adapter->netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
else
adapter->netdev->features &= ~NETIF_F_HW_VLAN_CTAG_TX;
if (adapter->priv_flags & RNPVF_FLAG_RX_SVLAN_OFFLOAD)
adapter->netdev->features |= NETIF_F_HW_VLAN_STAG_RX;
else
adapter->netdev->features &= ~NETIF_F_HW_VLAN_STAG_RX;
if (adapter->priv_flags & RNPVF_FLAG_TX_SVLAN_OFFLOAD)
adapter->netdev->features |= NETIF_F_HW_VLAN_STAG_TX;
else
adapter->netdev->features &= ~NETIF_F_HW_VLAN_STAG_TX;
}
hw->ops.set_veb_vlan(hw, data, VFNUM(mbx, hw->vfnum));
} else if ((data) == RNPGBE_PF_SET_LINK) {
data = mbx_rd32(hw, DATA_REG + 4);
if (data & RNPGBE_PF_LINK_UP) {
hw->link = true;
hw->speed = data & 0xffff;
} else {
hw->link = false;
hw->speed = 0;
}
} else if ((data) == RNPGBE_PF_SET_MTU) {
data = mbx_rd32(hw, DATA_REG + 4);
hw->mtu = data;
adapter->flags |= RNPVF_FLAG_PF_UPDATE_MTU;
} else if ((data) == RNPGBE_PF_SET_RESET) {
adapter->flags |= RNPVF_FLAG_PF_RESET;
} else {
return RNPGBE_ERR_MBX;
}
}
return ret;
}
/**
* rnpgbevf_check_for_ack_vf - checks to see if the PF has ACK'd
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* returns 0 if the PF has set the ACK bit or else ERR_MBX
**/
static s32 rnpgbevf_check_for_ack_vf(struct rnpgbevf_hw *hw, bool to_cm3)
{
s32 ret_val = RNPGBE_ERR_MBX;
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
if (to_cm3) {
if (rnpgbevf_mbx_get_ack(hw, CPU2VF_COUNTER(mbx, vfnum)) !=
hw->mbx.cpu_ack) {
ret_val = 0;
hw->mbx.stats.acks++;
}
} else {
if (rnpgbevf_mbx_get_ack(hw, PF2VF_COUNTER(mbx, vfnum)) !=
hw->mbx.pf_ack) {
ret_val = 0;
hw->mbx.stats.acks++;
}
}
return ret_val;
}
/**
* rnpgbevf_obtain_mbx_lock_vf - obtain mailbox lock
* @hw: pointer to the HW structure
* @to_cm3: to cm3 or not
*
* return 0 if we obtained the mailbox lock
**/
static s32 rnpgbevf_obtain_mbx_lock_vf(struct rnpgbevf_hw *hw, bool to_cm3)
{
int try_cnt = 2 * 1000; // 1s
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
u32 CTRL_REG = (to_cm3) ? VF2CPU_MBOX_CTRL(mbx, vfnum) :
VF2PF_MBOX_CTRL(mbx, vfnum);
while (try_cnt-- > 0) {
/* Take ownership of the buffer */
mbx_wr32(hw, CTRL_REG, MBOX_CTRL_VF_HOLD_SHM);
/* memory barrier */
mb();
/* reserve mailbox for vf use */
if (mbx_rd32(hw, CTRL_REG) & MBOX_CTRL_VF_HOLD_SHM)
return 0;
udelay(500);
}
return RNPGBE_ERR_MBX;
}
/**
* rnpgbevf_write_mbx_vf - Write a message to the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully copied message into the buffer
**/
static s32 rnpgbevf_write_mbx_vf(struct rnpgbevf_hw *hw, u32 *msg, u16 size,
bool to_cm3)
{
s32 ret_val;
struct rnp_mbx_info *mbx = &hw->mbx;
u32 i;
u8 vfnum = VFNUM(mbx, hw->vfnum);
u32 DATA_REG = (to_cm3) ? CPU_VF_SHM_DATA(mbx, vfnum) :
PF_VF_SHM_DATA(mbx, vfnum);
u32 CTRL_REG = (to_cm3) ? VF2CPU_MBOX_CTRL(mbx, vfnum) :
VF2PF_MBOX_CTRL(mbx, vfnum);
/* lock the mailbox to prevent pf/vf race condition */
ret_val = rnpgbevf_obtain_mbx_lock_vf(hw, to_cm3);
if (ret_val)
goto out_no_write;
/* add mailbox_id [27:21] */
#define VF_NUM_OFFSET (21)
if (!to_cm3)
msg[0] |= ((hw->vfnum & 0x3f) << VF_NUM_OFFSET);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
mbx_wr32(hw, DATA_REG + i * 4, msg[i]);
/* update acks. used by rnpgbevf_check_for_ack_vf */
if (to_cm3)
hw->mbx.cpu_ack =
rnpgbevf_mbx_get_ack(hw, CPU2VF_COUNTER(mbx, vfnum));
else
hw->mbx.pf_ack =
rnpgbevf_mbx_get_ack(hw, PF2VF_COUNTER(mbx, vfnum));
rnpgbevf_mbx_inc_vfreq(hw, to_cm3);
/* Drop VFU and interrupt the PF/CM3 to
* tell it a message has been sent
*/
mbx_wr32(hw, CTRL_REG, MBOX_CTRL_REQ);
out_no_write:
return ret_val;
}
/**
* rnpgbevf_read_mbx_vf - Reads a message from the inbox intended for vf
* @hw: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @to_cm3: to cm3 or not
*
* returns 0 if it successfully read message from buffer
**/
static s32 rnpgbevf_read_mbx_vf(struct rnpgbevf_hw *hw, u32 *msg, u16 size,
bool to_cm3)
{
s32 ret_val = 0;
struct rnp_mbx_info *mbx = &hw->mbx;
u32 i;
u8 vfnum = VFNUM(mbx, hw->vfnum);
u32 BUF_REG = (to_cm3) ? CPU_VF_SHM_DATA(mbx, vfnum) :
PF_VF_SHM_DATA(mbx, vfnum);
u32 CTRL_REG = (to_cm3) ? VF2CPU_MBOX_CTRL(mbx, vfnum) :
VF2PF_MBOX_CTRL(mbx, vfnum);
/* lock the mailbox to prevent pf/vf race condition */
ret_val = rnpgbevf_obtain_mbx_lock_vf(hw, to_cm3);
if (ret_val)
goto out_no_read;
/* we need this */
mb();
/* copy the message from the mailbox memory buffer */
for (i = 0; i < size; i++)
msg[i] = mbx_rd32(hw, BUF_REG + 4 * i);
/* clear vf_num */
#define RNPGBE_VF_NUM_MASK (0x7f << 21)
msg[0] &= (~RNPGBE_VF_NUM_MASK);
/* update req. used by rnpgbevf_check_for_msg_vf */
if (to_cm3)
hw->mbx.cpu_req =
rnpgbevf_mbx_get_req(hw, CPU2VF_COUNTER(mbx, vfnum));
else
hw->mbx.pf_req =
rnpgbevf_mbx_get_req(hw, PF2VF_COUNTER(mbx, vfnum));
/* Acknowledge receipt and release mailbox, then we're done */
rnpgbevf_mbx_inc_vfack(hw, to_cm3);
/* free ownership of the buffer */
mbx_wr32(hw, CTRL_REG, 0);
out_no_read:
return ret_val;
}
static void rnpgbevf_reset_mbx(struct rnpgbevf_hw *hw)
{
u32 v;
struct rnp_mbx_info *mbx = &hw->mbx;
u8 vfnum = VFNUM(mbx, hw->vfnum);
mbx_wr32(hw, VF2CPU_MBOX_CTRL(mbx, vfnum), 0);
mbx_wr32(hw, VF2PF_MBOX_CTRL(mbx, vfnum), 0);
v = mbx_rd32(hw, PF2VF_COUNTER(mbx, vfnum));
hw->mbx.pf_req = v & 0xffff;
hw->mbx.pf_ack = (v >> 16) & 0xffff;
v = mbx_rd32(hw, CPU2VF_COUNTER(mbx, vfnum));
hw->mbx.cpu_req = v & 0xffff;
hw->mbx.cpu_ack = (v >> 16) & 0xffff;
}
static s32 rnpgbevf_mbx_configure_vf(struct rnpgbevf_hw *hw, int nr_vec,
bool enable)
{
struct rnp_mbx_info *mbx = &hw->mbx;
int mbx_vec_reg, vfnum = VFNUM(mbx, hw->vfnum);
mbx_vec_reg = PF2VF_MBOX_VEC(mbx, vfnum);
mbx_wr32(hw, mbx_vec_reg, nr_vec);
return 0;
}
/**
* rnpgbevf_init_mbx_params_vf - set initial values for vf mailbox
* @hw: pointer to the HW structure
*
* Initializes the hw->mbx struct to correct values for vf mailbox
*/
static s32 rnpgbevf_init_mbx_params_vf(struct rnpgbevf_hw *hw)
{
struct rnp_mbx_info *mbx = &hw->mbx;
/* start mailbox as timed out and let the reset_hw call set the timeout
* value to begin communications
*/
mbx->timeout = 0;
mbx->udelay = RNPGBE_VF_MBX_INIT_DELAY;
mbx->stats.msgs_tx = 0;
mbx->stats.msgs_rx = 0;
mbx->stats.reqs = 0;
mbx->stats.acks = 0;
mbx->stats.rsts = 0;
mbx->size = RNPGBE_VFMAILBOX_SIZE;
rnpgbevf_reset_mbx(hw);
return 0;
}
const struct rnp_mbx_operations rnpgbevf_mbx_ops = {
.init_params = rnpgbevf_init_mbx_params_vf,
.read = rnpgbevf_read_mbx_vf,
.write = rnpgbevf_write_mbx_vf,
.read_posted = rnpgbevf_read_posted_mbx,
.write_posted = rnpgbevf_write_posted_mbx,
.check_for_msg = rnpgbevf_check_for_msg_vf,
.check_for_ack = rnpgbevf_check_for_ack_vf,
.check_for_rst = rnpgbevf_check_for_rst_msg_vf,
.configure = rnpgbevf_mbx_configure_vf,
};