root/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c
// SPDX-License-Identifier: GPL-2.0
/* Marvell Octeon EP (EndPoint) VF Ethernet Driver
 *
 * Copyright (C) 2020 Marvell.
 *
 */
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include "octep_vf_config.h"
#include "octep_vf_main.h"

/* When a new command is implemented, the below table should be updated
 * with new command and it's version info.
 */
static u32 pfvf_cmd_versions[OCTEP_PFVF_MBOX_CMD_MAX] = {
        [0 ... OCTEP_PFVF_MBOX_CMD_DEV_REMOVE] = OCTEP_PFVF_MBOX_VERSION_V1,
        [OCTEP_PFVF_MBOX_CMD_GET_FW_INFO ... OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS] =
                OCTEP_PFVF_MBOX_VERSION_V2
};

int octep_vf_setup_mbox(struct octep_vf_device *oct)
{
        int ring = 0;

        oct->mbox = vzalloc(sizeof(*oct->mbox));
        if (!oct->mbox)
                return -1;

        mutex_init(&oct->mbox->lock);

        oct->hw_ops.setup_mbox_regs(oct, ring);
        INIT_WORK(&oct->mbox->wk.work, octep_vf_mbox_work);
        oct->mbox->wk.ctxptr = oct;
        oct->mbox_neg_ver = OCTEP_PFVF_MBOX_VERSION_CURRENT;
        dev_info(&oct->pdev->dev, "setup vf mbox successfully\n");
        return 0;
}

void octep_vf_delete_mbox(struct octep_vf_device *oct)
{
        if (oct->mbox) {
                if (work_pending(&oct->mbox->wk.work))
                        cancel_work_sync(&oct->mbox->wk.work);

                mutex_destroy(&oct->mbox->lock);
                vfree(oct->mbox);
                oct->mbox = NULL;
                dev_info(&oct->pdev->dev, "Deleted vf mbox successfully\n");
        }
}

int octep_vf_mbox_version_check(struct octep_vf_device *oct)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_version.opcode = OCTEP_PFVF_MBOX_CMD_VERSION;
        cmd.s_version.version = OCTEP_PFVF_MBOX_VERSION_CURRENT;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret == OCTEP_PFVF_MBOX_CMD_STATUS_NACK) {
                dev_err(&oct->pdev->dev,
                        "VF Mbox version is incompatible with PF\n");
                return -EINVAL;
        }
        oct->mbox_neg_ver = (u32)rsp.s_version.version;
        dev_dbg(&oct->pdev->dev,
                "VF Mbox version:%u Negotiated VF version with PF:%u\n",
                 (u32)cmd.s_version.version,
                 (u32)rsp.s_version.version);
        return 0;
}

void octep_vf_mbox_work(struct work_struct *work)
{
        struct octep_vf_mbox_wk *wk = container_of(work, struct octep_vf_mbox_wk, work);
        struct octep_vf_iface_link_info *link_info;
        struct octep_vf_device *oct = NULL;
        struct octep_vf_mbox *mbox = NULL;
        union octep_pfvf_mbox_word *notif;
        u64 pf_vf_data;

        oct = (struct octep_vf_device *)wk->ctxptr;
        link_info = &oct->link_info;
        mbox = oct->mbox;
        pf_vf_data = readq(mbox->mbox_read_reg);

        notif = (union octep_pfvf_mbox_word *)&pf_vf_data;

        switch (notif->s.opcode) {
        case OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS:
                if (notif->s_link_status.status) {
                        link_info->oper_up = OCTEP_PFVF_LINK_STATUS_UP;
                        netif_carrier_on(oct->netdev);
                        dev_info(&oct->pdev->dev, "netif_carrier_on\n");
                } else {
                        link_info->oper_up = OCTEP_PFVF_LINK_STATUS_DOWN;
                        netif_carrier_off(oct->netdev);
                        dev_info(&oct->pdev->dev, "netif_carrier_off\n");
                }
                break;
        default:
                dev_err(&oct->pdev->dev,
                        "Received unsupported notif %d\n", notif->s.opcode);
                break;
        }
}

static int __octep_vf_mbox_send_cmd(struct octep_vf_device *oct,
                                    union octep_pfvf_mbox_word cmd,
                                    union octep_pfvf_mbox_word *rsp)
{
        struct octep_vf_mbox *mbox = oct->mbox;
        u64 reg_val = 0ull;
        int count;

        if (!mbox)
                return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP;

        cmd.s.type = OCTEP_PFVF_MBOX_TYPE_CMD;
        writeq(cmd.u64, mbox->mbox_write_reg);

        /* No response for notification messages */
        if (!rsp)
                return 0;

        for (count = 0; count < OCTEP_PFVF_MBOX_TIMEOUT_WAIT_COUNT; count++) {
                usleep_range(1000, 1500);
                reg_val = readq(mbox->mbox_write_reg);
                if (reg_val != cmd.u64) {
                        rsp->u64 = reg_val;
                        break;
                }
        }
        if (count == OCTEP_PFVF_MBOX_TIMEOUT_WAIT_COUNT) {
                dev_err(&oct->pdev->dev, "mbox send command timed out\n");
                return OCTEP_PFVF_MBOX_CMD_STATUS_TIMEDOUT;
        }
        if (rsp->s.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "mbox_send: Received NACK\n");
                return OCTEP_PFVF_MBOX_CMD_STATUS_NACK;
        }
        rsp->u64 = reg_val;
        return 0;
}

int octep_vf_mbox_send_cmd(struct octep_vf_device *oct, union octep_pfvf_mbox_word cmd,
                           union octep_pfvf_mbox_word *rsp)
{
        struct octep_vf_mbox *mbox = oct->mbox;
        int ret;

        if (!mbox)
                return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP;
        mutex_lock(&mbox->lock);
        if (pfvf_cmd_versions[cmd.s.opcode] > oct->mbox_neg_ver) {
                dev_dbg(&oct->pdev->dev, "CMD:%d not supported in Version:%d\n",
                        cmd.s.opcode, oct->mbox_neg_ver);
                mutex_unlock(&mbox->lock);
                return -EOPNOTSUPP;
        }
        ret = __octep_vf_mbox_send_cmd(oct, cmd, rsp);
        mutex_unlock(&mbox->lock);
        return ret;
}

int octep_vf_mbox_bulk_read(struct octep_vf_device *oct, enum octep_pfvf_mbox_opcode opcode,
                            u8 *data, int *size)
{
        struct octep_vf_mbox *mbox = oct->mbox;
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int data_len = 0, tmp_len = 0;
        int read_cnt, i = 0, ret;

        if (!mbox)
                return OCTEP_PFVF_MBOX_CMD_STATUS_NOT_SETUP;

        mutex_lock(&mbox->lock);
        cmd.u64 = 0;
        cmd.s_data.opcode = opcode;
        cmd.s_data.frag = 0;
        /* Send cmd to read data from PF */
        ret = __octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "send mbox cmd fail for data request\n");
                mutex_unlock(&mbox->lock);
                return ret;
        }
        /*  PF sends the data length of requested CMD
         *  in  ACK
         */
        data_len = *((int32_t *)rsp.s_data.data);
        tmp_len = data_len;
        cmd.u64 = 0;
        rsp.u64 = 0;
        cmd.s_data.opcode = opcode;
        cmd.s_data.frag = 1;
        while (data_len) {
                ret = __octep_vf_mbox_send_cmd(oct, cmd, &rsp);
                if (ret) {
                        dev_err(&oct->pdev->dev, "send mbox cmd fail for data request\n");
                        mutex_unlock(&mbox->lock);
                        mbox->mbox_data.data_index = 0;
                        memset(mbox->mbox_data.recv_data, 0, OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE);
                        return ret;
                }
                if (data_len > OCTEP_PFVF_MBOX_MAX_DATA_SIZE) {
                        data_len -= OCTEP_PFVF_MBOX_MAX_DATA_SIZE;
                        read_cnt = OCTEP_PFVF_MBOX_MAX_DATA_SIZE;
                } else {
                        read_cnt = data_len;
                        data_len = 0;
                }
                for (i = 0; i < read_cnt; i++) {
                        mbox->mbox_data.recv_data[mbox->mbox_data.data_index] =
                                rsp.s_data.data[i];
                        mbox->mbox_data.data_index++;
                }
                cmd.u64 = 0;
                rsp.u64 = 0;
                cmd.s_data.opcode = opcode;
                cmd.s_data.frag = 1;
        }
        memcpy(data, mbox->mbox_data.recv_data, tmp_len);
        *size = tmp_len;
        mbox->mbox_data.data_index = 0;
        memset(mbox->mbox_data.recv_data, 0, OCTEP_PFVF_MBOX_MAX_DATA_BUF_SIZE);
        mutex_unlock(&mbox->lock);
        return 0;
}

int octep_vf_mbox_set_mtu(struct octep_vf_device *oct, int mtu)
{
        int frame_size = mtu + ETH_HLEN + ETH_FCS_LEN;
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret = 0;

        if (mtu < ETH_MIN_MTU || frame_size > ETH_MAX_MTU) {
                dev_err(&oct->pdev->dev,
                        "Failed to set MTU to %d MIN MTU:%d MAX MTU:%d\n",
                        mtu, ETH_MIN_MTU, ETH_MAX_MTU);
                return -EINVAL;
        }

        cmd.u64 = 0;
        cmd.s_set_mtu.opcode = OCTEP_PFVF_MBOX_CMD_SET_MTU;
        cmd.s_set_mtu.mtu = mtu;

        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Mbox send failed; err=%d\n", ret);
                return ret;
        }
        if (rsp.s_set_mtu.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Received Mbox NACK from PF for MTU:%d\n", mtu);
                return -EINVAL;
        }

        return 0;
}

int octep_vf_mbox_set_mac_addr(struct octep_vf_device *oct, char *mac_addr)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int i, ret;

        cmd.u64 = 0;
        cmd.s_set_mac.opcode = OCTEP_PFVF_MBOX_CMD_SET_MAC_ADDR;
        for (i = 0; i < ETH_ALEN; i++)
                cmd.s_set_mac.mac_addr[i] = mac_addr[i];
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Mbox send failed; err = %d\n", ret);
                return ret;
        }
        if (rsp.s_set_mac.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "received NACK\n");
                return -EINVAL;
        }
        return 0;
}

int octep_vf_mbox_get_mac_addr(struct octep_vf_device *oct, char *mac_addr)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int i, ret;

        cmd.u64 = 0;
        cmd.s_set_mac.opcode = OCTEP_PFVF_MBOX_CMD_GET_MAC_ADDR;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "get_mac: mbox send failed; err = %d\n", ret);
                return ret;
        }
        if (rsp.s_set_mac.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "get_mac: received NACK\n");
                return -EINVAL;
        }
        for (i = 0; i < ETH_ALEN; i++)
                mac_addr[i] = rsp.s_set_mac.mac_addr[i];
        return 0;
}

int octep_vf_mbox_set_rx_state(struct octep_vf_device *oct, bool state)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_link_state.opcode = OCTEP_PFVF_MBOX_CMD_SET_RX_STATE;
        cmd.s_link_state.state = state;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Set Rx state via VF Mbox send failed\n");
                return ret;
        }
        if (rsp.s_link_state.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Set Rx state received NACK\n");
                return -EINVAL;
        }
        return 0;
}

int octep_vf_mbox_set_link_status(struct octep_vf_device *oct, bool status)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_link_status.opcode = OCTEP_PFVF_MBOX_CMD_SET_LINK_STATUS;
        cmd.s_link_status.status = status;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Set link status via VF Mbox send failed\n");
                return ret;
        }
        if (rsp.s_link_status.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Set link status received NACK\n");
                return -EINVAL;
        }
        return 0;
}

int octep_vf_mbox_get_link_status(struct octep_vf_device *oct, u8 *oper_up)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_link_status.opcode = OCTEP_PFVF_MBOX_CMD_GET_LINK_STATUS;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Get link status via VF Mbox send failed\n");
                return ret;
        }
        if (rsp.s_link_status.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Get link status received NACK\n");
                return -EINVAL;
        }
        *oper_up = rsp.s_link_status.status;
        return 0;
}

int octep_vf_mbox_dev_remove(struct octep_vf_device *oct)
{
        union octep_pfvf_mbox_word cmd;
        int ret;

        cmd.u64 = 0;
        cmd.s.opcode = OCTEP_PFVF_MBOX_CMD_DEV_REMOVE;
        ret = octep_vf_mbox_send_cmd(oct, cmd, NULL);
        return ret;
}

int octep_vf_mbox_get_fw_info(struct octep_vf_device *oct)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_fw_info.opcode = OCTEP_PFVF_MBOX_CMD_GET_FW_INFO;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Get link status via VF Mbox send failed\n");
                return ret;
        }
        if (rsp.s_fw_info.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Get link status received NACK\n");
                return -EINVAL;
        }
        oct->fw_info.pkind = rsp.s_fw_info.pkind;
        oct->fw_info.fsz = rsp.s_fw_info.fsz;
        oct->fw_info.rx_ol_flags = rsp.s_fw_info.rx_ol_flags;
        oct->fw_info.tx_ol_flags = rsp.s_fw_info.tx_ol_flags;

        return 0;
}

int octep_vf_mbox_set_offloads(struct octep_vf_device *oct, u16 tx_offloads,
                               u16 rx_offloads)
{
        union octep_pfvf_mbox_word cmd;
        union octep_pfvf_mbox_word rsp;
        int ret;

        cmd.u64 = 0;
        cmd.s_offloads.opcode = OCTEP_PFVF_MBOX_CMD_SET_OFFLOADS;
        cmd.s_offloads.rx_ol_flags = rx_offloads;
        cmd.s_offloads.tx_ol_flags = tx_offloads;
        ret = octep_vf_mbox_send_cmd(oct, cmd, &rsp);
        if (ret) {
                dev_err(&oct->pdev->dev, "Set offloads via VF Mbox send failed\n");
                return ret;
        }
        if (rsp.s_link_state.type != OCTEP_PFVF_MBOX_TYPE_RSP_ACK) {
                dev_err(&oct->pdev->dev, "Set offloads received NACK\n");
                return -EINVAL;
        }
        return 0;
}