root/drivers/net/ethernet/wangxun/libwx/wx_vf.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */

#include <linux/etherdevice.h>
#include <linux/pci.h>

#include "wx_type.h"
#include "wx_hw.h"
#include "wx_mbx.h"
#include "wx_vf.h"

static void wx_virt_clr_reg(struct wx *wx)
{
        u32 vfsrrctl, i;

        /* VRSRRCTL default values (BSIZEPACKET = 2048, BSIZEHEADER = 256) */
        vfsrrctl = WX_VXRXDCTL_HDRSZ(wx_hdr_sz(WX_RX_HDR_SIZE));
        vfsrrctl |= WX_VXRXDCTL_BUFSZ(wx_buf_sz(WX_RX_BUF_SIZE));

        /* clear all rxd ctl */
        for (i = 0; i < WX_VF_MAX_RING_NUMS; i++)
                wr32m(wx, WX_VXRXDCTL(i),
                      WX_VXRXDCTL_HDRSZ_MASK | WX_VXRXDCTL_BUFSZ_MASK,
                      vfsrrctl);

        rd32(wx, WX_VXSTATUS);
}

/**
 *  wx_init_hw_vf - virtual function hardware initialization
 *  @wx: pointer to hardware structure
 *
 *  Initialize the mac address
 **/
void wx_init_hw_vf(struct wx *wx)
{
        wx_get_mac_addr_vf(wx, wx->mac.addr);
}
EXPORT_SYMBOL(wx_init_hw_vf);

static int wx_mbx_write_and_read_reply(struct wx *wx, u32 *req_buf,
                                       u32 *resp_buf, u16 size)
{
        int ret;

        ret = wx_write_posted_mbx(wx, req_buf, size);
        if (ret)
                return ret;

        return wx_read_posted_mbx(wx, resp_buf, size);
}

/**
 *  wx_reset_hw_vf - Performs hardware reset
 *  @wx: pointer to hardware structure
 *
 *  Resets the hardware by resetting the transmit and receive units, masks and
 *  clears all interrupts.
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_reset_hw_vf(struct wx *wx)
{
        struct wx_mbx_info *mbx = &wx->mbx;
        u32 msgbuf[4] = {WX_VF_RESET};
        u8 *addr = (u8 *)(&msgbuf[1]);
        u32 b4_buf[16] = {0};
        u32 timeout = 200;
        int ret;
        u32 i;

        /* Call wx stop to disable tx/rx and clear interrupts */
        wx_stop_adapter_vf(wx);

        /* reset the api version */
        wx->vfinfo->vf_api = wx_mbox_api_null;

        /* backup msix vectors */
        if (wx->b4_addr) {
                for (i = 0; i < 16; i++)
                        b4_buf[i] = readl(wx->b4_addr + i * 4);
        }

        wr32m(wx, WX_VXCTRL, WX_VXCTRL_RST, WX_VXCTRL_RST);
        rd32(wx, WX_VXSTATUS);

        /* we cannot reset while the RSTI / RSTD bits are asserted */
        while (!wx_check_for_rst_vf(wx) && timeout) {
                timeout--;
                udelay(5);
        }

        /* restore msix vectors */
        if (wx->b4_addr) {
                for (i = 0; i < 16; i++)
                        writel(b4_buf[i], wx->b4_addr + i * 4);
        }

        /* amlite: bme */
        if (wx->mac.type == wx_mac_aml || wx->mac.type == wx_mac_aml40)
                wr32(wx, WX_VX_PF_BME, WX_VF_BME_ENABLE);

        if (!timeout)
                return -EBUSY;

        /* Reset VF registers to initial values */
        wx_virt_clr_reg(wx);

        /* mailbox timeout can now become active */
        mbx->timeout = 2000;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        if (msgbuf[0] != (WX_VF_RESET | WX_VT_MSGTYPE_ACK) &&
            msgbuf[0] != (WX_VF_RESET | WX_VT_MSGTYPE_NACK))
                return -EINVAL;

        if (msgbuf[0] == (WX_VF_RESET | WX_VT_MSGTYPE_ACK))
                ether_addr_copy(wx->mac.perm_addr, addr);

        wx->mac.mc_filter_type = msgbuf[3];

        return 0;
}
EXPORT_SYMBOL(wx_reset_hw_vf);

/**
 *  wx_stop_adapter_vf - Generic stop Tx/Rx units
 *  @wx: pointer to hardware structure
 *
 *  Clears interrupts, disables transmit and receive units.
 **/
void wx_stop_adapter_vf(struct wx *wx)
{
        u32 reg_val;
        u16 i;

        /* Clear interrupt mask to stop from interrupts being generated */
        wr32(wx, WX_VXIMS, WX_VF_IRQ_CLEAR_MASK);

        /* Clear any pending interrupts, flush previous writes */
        wr32(wx, WX_VXICR, U32_MAX);

        /* Disable the transmit unit.  Each queue must be disabled. */
        for (i = 0; i < wx->mac.max_tx_queues; i++)
                wr32(wx, WX_VXTXDCTL(i), WX_VXTXDCTL_FLUSH);

        /* Disable the receive unit by stopping each queue */
        for (i = 0; i < wx->mac.max_rx_queues; i++) {
                reg_val = rd32(wx, WX_VXRXDCTL(i));
                reg_val &= ~WX_VXRXDCTL_ENABLE;
                wr32(wx, WX_VXRXDCTL(i), reg_val);
        }
        /* Clear packet split and pool config */
        wr32(wx, WX_VXMRQC, 0);

        /* flush all queues disables */
        rd32(wx, WX_VXSTATUS);
}
EXPORT_SYMBOL(wx_stop_adapter_vf);

/**
 *  wx_set_rar_vf - set device MAC address
 *  @wx: pointer to hardware structure
 *  @index: Receive address register to write
 *  @addr: Address to put into receive address register
 *  @enable_addr: set flag that address is active
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_set_rar_vf(struct wx *wx, u32 index, u8 *addr, u32 enable_addr)
{
        u32 msgbuf[3] = {WX_VF_SET_MAC_ADDR};
        u8 *msg_addr = (u8 *)(&msgbuf[1]);
        int ret;

        memcpy(msg_addr, addr, ETH_ALEN);

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;
        msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;

        /* if nacked the address was rejected, use "perm_addr" */
        if (msgbuf[0] == (WX_VF_SET_MAC_ADDR | WX_VT_MSGTYPE_NACK)) {
                wx_get_mac_addr_vf(wx, wx->mac.addr);
                return -EINVAL;
        }

        return 0;
}
EXPORT_SYMBOL(wx_set_rar_vf);

/**
 *  wx_update_mc_addr_list_vf - Update Multicast addresses
 *  @wx: pointer to the HW structure
 *  @netdev: pointer to the net device structure
 *
 *  Updates the Multicast Table Array.
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_update_mc_addr_list_vf(struct wx *wx, struct net_device *netdev)
{
        u32 msgbuf[WX_VXMAILBOX_SIZE] = {WX_VF_SET_MULTICAST};
        u16 *vector_l = (u16 *)&msgbuf[1];
        struct netdev_hw_addr *ha;
        u32 cnt, i;

        cnt = netdev_mc_count(netdev);
        if (cnt > 28)
                cnt = 28;
        msgbuf[0] |= cnt << WX_VT_MSGINFO_SHIFT;

        i = 0;
        netdev_for_each_mc_addr(ha, netdev) {
                if (i == cnt)
                        break;
                if (is_link_local_ether_addr(ha->addr))
                        continue;

                vector_l[i++] = wx_mta_vector(wx, ha->addr);
        }

        return wx_write_posted_mbx(wx, msgbuf, ARRAY_SIZE(msgbuf));
}
EXPORT_SYMBOL(wx_update_mc_addr_list_vf);

/**
 *  wx_update_xcast_mode_vf - Update Multicast mode
 *  @wx: pointer to the HW structure
 *  @xcast_mode: new multicast mode
 *
 *  Updates the Multicast Mode of VF.
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_update_xcast_mode_vf(struct wx *wx, int xcast_mode)
{
        u32 msgbuf[2] = {WX_VF_UPDATE_XCAST_MODE, xcast_mode};
        int ret = 0;

        if (wx->vfinfo->vf_api < wx_mbox_api_13)
                return -EINVAL;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
        if (msgbuf[0] == (WX_VF_UPDATE_XCAST_MODE | WX_VT_MSGTYPE_NACK))
                return -EINVAL;

        return 0;
}
EXPORT_SYMBOL(wx_update_xcast_mode_vf);

/**
 * wx_get_link_state_vf - Get VF link state from PF
 * @wx: pointer to the HW structure
 * @link_state: link state storage
 *
 * Return: return state of the operation error or success.
 **/
int wx_get_link_state_vf(struct wx *wx, u16 *link_state)
{
        u32 msgbuf[2] = {WX_VF_GET_LINK_STATE};
        int ret;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
                return -EINVAL;

        *link_state = msgbuf[1];

        return 0;
}
EXPORT_SYMBOL(wx_get_link_state_vf);

/**
 *  wx_set_vfta_vf - Set/Unset vlan filter table address
 *  @wx: pointer to the HW structure
 *  @vlan: 12 bit VLAN ID
 *  @vind: unused by VF drivers
 *  @vlan_on: if true then set bit, else clear bit
 *  @vlvf_bypass: boolean flag indicating updating default pool is okay
 *
 *  Turn on/off specified VLAN in the VLAN filter table.
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_set_vfta_vf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on,
                   bool vlvf_bypass)
{
        u32 msgbuf[2] = {WX_VF_SET_VLAN, vlan};
        bool vlan_offload = false;
        int ret;

        /* Setting the 8 bit field MSG INFO to TRUE indicates "add" */
        msgbuf[0] |= vlan_on << WX_VT_MSGINFO_SHIFT;
        /* if vf vlan offload is disabled, allow to create vlan under pf port vlan */
        msgbuf[0] |= BIT(vlan_offload);

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        if (msgbuf[0] & WX_VT_MSGTYPE_ACK)
                return 0;

        return msgbuf[0] & WX_VT_MSGTYPE_NACK;
}
EXPORT_SYMBOL(wx_set_vfta_vf);

void wx_get_mac_addr_vf(struct wx *wx, u8 *mac_addr)
{
        ether_addr_copy(mac_addr, wx->mac.perm_addr);
}
EXPORT_SYMBOL(wx_get_mac_addr_vf);

int wx_get_fw_version_vf(struct wx *wx)
{
        u32 msgbuf[2] = {WX_VF_GET_FW_VERSION};
        int ret;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
                return -EINVAL;
        snprintf(wx->eeprom_id, 32, "0x%08x", msgbuf[1]);

        return 0;
}
EXPORT_SYMBOL(wx_get_fw_version_vf);

int wx_set_uc_addr_vf(struct wx *wx, u32 index, u8 *addr)
{
        u32 msgbuf[3] = {WX_VF_SET_MACVLAN};
        u8 *msg_addr = (u8 *)(&msgbuf[1]);
        int ret;

        /* If index is one then this is the start of a new list and needs
         * indication to the PF so it can do it's own list management.
         * If it is zero then that tells the PF to just clear all of
         * this VF's macvlans and there is no new list.
         */
        msgbuf[0] |= index << WX_VT_MSGINFO_SHIFT;
        if (addr)
                memcpy(msg_addr, addr, 6);
        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;

        if (msgbuf[0] == (WX_VF_SET_MACVLAN | WX_VT_MSGTYPE_NACK))
                return -EINVAL;

        return 0;
}
EXPORT_SYMBOL(wx_set_uc_addr_vf);

/**
 *  wx_rlpml_set_vf - Set the maximum receive packet length
 *  @wx: pointer to the HW structure
 *  @max_size: value to assign to max frame size
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_rlpml_set_vf(struct wx *wx, u16 max_size)
{
        u32 msgbuf[2] = {WX_VF_SET_LPE, max_size};
        int ret;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;
        if ((msgbuf[0] & WX_VF_SET_LPE) &&
            (msgbuf[0] & WX_VT_MSGTYPE_NACK))
                return -EINVAL;

        return 0;
}
EXPORT_SYMBOL(wx_rlpml_set_vf);

/**
 *  wx_negotiate_api_version - Negotiate supported API version
 *  @wx: pointer to the HW structure
 *  @api: integer containing requested API version
 *
 *  Return: returns 0 on success, negative error code on failure
 **/
int wx_negotiate_api_version(struct wx *wx, int api)
{
        u32 msgbuf[2] = {WX_VF_API_NEGOTIATE, api};
        int ret;

        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;

        msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;

        /* Store value and return 0 on success */
        if (msgbuf[0] == (WX_VF_API_NEGOTIATE | WX_VT_MSGTYPE_NACK))
                return -EINVAL;
        wx->vfinfo->vf_api = api;

        return 0;
}
EXPORT_SYMBOL(wx_negotiate_api_version);

int wx_get_queues_vf(struct wx *wx, u32 *num_tcs, u32 *default_tc)
{
        u32 msgbuf[5] = {WX_VF_GET_QUEUES};
        int ret;

        /* do nothing if API doesn't support wx_get_queues */
        if (wx->vfinfo->vf_api < wx_mbox_api_13)
                return -EINVAL;

        /* Fetch queue configuration from the PF */
        ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
                                          ARRAY_SIZE(msgbuf));
        if (ret)
                return ret;
        msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;

        /* if we didn't get an ACK there must have been
         * some sort of mailbox error so we should treat it
         * as such
         */
        if (msgbuf[0] != (WX_VF_GET_QUEUES | WX_VT_MSGTYPE_ACK))
                return -EINVAL;
        /* record and validate values from message */
        wx->mac.max_tx_queues = msgbuf[WX_VF_TX_QUEUES];
        if (wx->mac.max_tx_queues == 0 ||
            wx->mac.max_tx_queues > WX_VF_MAX_TX_QUEUES)
                wx->mac.max_tx_queues = WX_VF_MAX_TX_QUEUES;

        wx->mac.max_rx_queues = msgbuf[WX_VF_RX_QUEUES];
        if (wx->mac.max_rx_queues == 0 ||
            wx->mac.max_rx_queues > WX_VF_MAX_RX_QUEUES)
                wx->mac.max_rx_queues = WX_VF_MAX_RX_QUEUES;

        *num_tcs = msgbuf[WX_VF_TRANS_VLAN];
        /* in case of unknown state assume we cannot tag frames */
        if (*num_tcs > wx->mac.max_rx_queues)
                *num_tcs = 1;
        *default_tc = msgbuf[WX_VF_DEF_QUEUE];
        /* default to queue 0 on out-of-bounds queue number */
        if (*default_tc >= wx->mac.max_tx_queues)
                *default_tc = 0;

        return 0;
}
EXPORT_SYMBOL(wx_get_queues_vf);

static int wx_get_link_status_from_pf(struct wx *wx, u32 *msgbuf)
{
        u32 links_reg = msgbuf[1];

        if (msgbuf[1] & WX_PF_NOFITY_VF_NET_NOT_RUNNING)
                wx->notify_down = true;
        else
                wx->notify_down = false;

        if (wx->notify_down) {
                wx->link = false;
                wx->speed = SPEED_UNKNOWN;
                return 0;
        }

        wx->link = WX_PFLINK_STATUS(links_reg);
        wx->speed = WX_PFLINK_SPEED(links_reg);

        return 0;
}

static int wx_pf_ping_vf(struct wx *wx, u32 *msgbuf)
{
        if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS))
                /* msg is not CTS, we need to do reset */
                return -EINVAL;

        return 0;
}

static struct wx_link_reg_fields wx_speed_lookup_vf[] = {
        {wx_mac_unknown},
        {wx_mac_sp, SPEED_10000, SPEED_1000, SPEED_100, SPEED_UNKNOWN, SPEED_UNKNOWN},
        {wx_mac_em, SPEED_1000,  SPEED_100, SPEED_10, SPEED_UNKNOWN, SPEED_UNKNOWN},
        {wx_mac_aml, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
        {wx_mac_aml40, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
};

static void wx_check_physical_link(struct wx *wx)
{
        u32 val, link_val;
        int ret;

        /* get link status from hw status reg
         * for SFP+ modules and DA cables, it can take up to 500usecs
         * before the link status is correct
         */
        if (wx->mac.type == wx_mac_em)
                ret = read_poll_timeout_atomic(rd32, val, val & GENMASK(4, 1),
                                               100, 500, false, wx, WX_VXSTATUS);
        else
                ret = read_poll_timeout_atomic(rd32, val, val & BIT(0), 100,
                                               500, false, wx, WX_VXSTATUS);
        if (ret) {
                wx->speed = SPEED_UNKNOWN;
                wx->link = false;
                return;
        }

        wx->link = true;
        link_val = WX_VXSTATUS_SPEED(val);

        if (link_val & BIT(0))
                wx->speed = wx_speed_lookup_vf[wx->mac.type].bit0_f;
        else if (link_val & BIT(1))
                wx->speed = wx_speed_lookup_vf[wx->mac.type].bit1_f;
        else if (link_val & BIT(2))
                wx->speed = wx_speed_lookup_vf[wx->mac.type].bit2_f;
        else if (link_val & BIT(3))
                wx->speed = wx_speed_lookup_vf[wx->mac.type].bit3_f;
        else
                wx->speed = SPEED_UNKNOWN;
}

int wx_check_mac_link_vf(struct wx *wx)
{
        struct wx_mbx_info *mbx = &wx->mbx;
        u32 msgbuf[2] = {0};
        int ret = 0;

        if (!mbx->timeout)
                goto out;

        wx_check_for_rst_vf(wx);
        if (!wx_check_for_msg_vf(wx))
                ret = wx_read_mbx_vf(wx, msgbuf, 2);
        if (ret)
                goto out;

        switch (msgbuf[0] & GENMASK(8, 0)) {
        case WX_PF_NOFITY_VF_LINK_STATUS | WX_PF_CONTROL_MSG:
                ret = wx_get_link_status_from_pf(wx, msgbuf);
                goto out;
        case WX_PF_CONTROL_MSG:
                ret = wx_pf_ping_vf(wx, msgbuf);
                goto out;
        case 0:
                if (msgbuf[0] & WX_VT_MSGTYPE_NACK) {
                        /* msg is NACK, we must have lost CTS status */
                        ret = -EBUSY;
                        goto out;
                }
                /* no message, check link status */
                wx_check_physical_link(wx);
                goto out;
        default:
                break;
        }

        if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS)) {
                /* msg is not CTS and is NACK we must have lost CTS status */
                if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
                        ret = -EBUSY;
                goto out;
        }

        /* the pf is talking, if we timed out in the past we reinit */
        if (!mbx->timeout) {
                ret = -EBUSY;
                goto out;
        }

out:
        return ret;
}