// SPDX-License-Identifier: GPL-2.0 /* Marvell Octeon EP (EndPoint) Ethernet Driver * * Copyright (C) 2020 Marvell. * */ #include #include #include #include #include #include #include #include #include #include #include #include "octep_config.h" #include "octep_main.h" #include "octep_pfvf_mbox.h" #include "octep_ctrl_net.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 }; static void octep_pfvf_validate_version(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { u32 vf_version = (u32)cmd.s_version.version; dev_dbg(&oct->pdev->dev, "VF id:%d VF version:%d PF version:%d\n", vf_id, vf_version, OCTEP_PFVF_MBOX_VERSION_CURRENT); if (vf_version < OCTEP_PFVF_MBOX_VERSION_CURRENT) rsp->s_version.version = vf_version; else rsp->s_version.version = OCTEP_PFVF_MBOX_VERSION_CURRENT; oct->vf_info[vf_id].mbox_version = rsp->s_version.version; dev_dbg(&oct->pdev->dev, "VF id:%d negotiated VF version:%d\n", vf_id, oct->vf_info[vf_id].mbox_version); rsp->s_version.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_get_link_status(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int status; status = octep_ctrl_net_get_link_status(oct, vf_id); if (status < 0) { rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Get VF link status failed via host control Mbox\n"); return; } rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; rsp->s_link_status.status = status; } static void octep_pfvf_set_link_status(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_set_link_status(oct, vf_id, cmd.s_link_status.status, true); if (err) { rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Set VF link status failed via host control Mbox\n"); return; } rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_set_rx_state(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_set_rx_state(oct, vf_id, cmd.s_link_state.state, true); if (err) { rsp->s_link_state.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Set VF Rx link state failed via host control Mbox\n"); return; } rsp->s_link_state.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static int octep_send_notification(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd) { u32 max_rings_per_vf, vf_mbox_queue; struct octep_mbox *mbox; /* check if VF PF Mailbox is compatible for this notification */ if (pfvf_cmd_versions[cmd.s.opcode] > oct->vf_info[vf_id].mbox_version) { dev_dbg(&oct->pdev->dev, "VF Mbox doesn't support Notification:%d on VF ver:%d\n", cmd.s.opcode, oct->vf_info[vf_id].mbox_version); return -EOPNOTSUPP; } max_rings_per_vf = CFG_GET_MAX_RPVF(oct->conf); vf_mbox_queue = vf_id * max_rings_per_vf; if (!oct->mbox[vf_mbox_queue]) { dev_err(&oct->pdev->dev, "Notif obtained for bad mbox vf %d\n", vf_id); return -EINVAL; } mbox = oct->mbox[vf_mbox_queue]; mutex_lock(&mbox->lock); writeq(cmd.u64, mbox->pf_vf_data_reg); mutex_unlock(&mbox->lock); return 0; } static void octep_pfvf_set_mtu(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_set_mtu(oct, vf_id, cmd.s_set_mtu.mtu, true); if (err) { rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Set VF MTU failed via host control Mbox\n"); return; } rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_get_mtu(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int max_rx_pktlen = oct->netdev->max_mtu + (ETH_HLEN + ETH_FCS_LEN); rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; rsp->s_get_mtu.mtu = max_rx_pktlen; } static void octep_pfvf_set_mac_addr(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_set_mac_addr(oct, vf_id, cmd.s_set_mac.mac_addr, true); if (err) { rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Set VF MAC address failed via host control Mbox\n"); return; } rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_get_mac_addr(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_get_mac_addr(oct, vf_id, rsp->s_set_mac.mac_addr); if (err) { rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Get VF MAC address failed via host control Mbox\n"); return; } rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_dev_remove(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int err; err = octep_ctrl_net_dev_remove(oct, vf_id); if (err) { rsp->s.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Failed to acknowledge fw of vf %d removal\n", vf_id); return; } rsp->s.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_get_fw_info(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { struct octep_fw_info fw_info; int err; err = octep_ctrl_net_get_info(oct, vf_id, &fw_info); if (err) { rsp->s_fw_info.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Get VF info failed via host control Mbox\n"); return; } rsp->s_fw_info.pkind = fw_info.pkind; rsp->s_fw_info.fsz = fw_info.fsz; rsp->s_fw_info.rx_ol_flags = fw_info.rx_ol_flags; rsp->s_fw_info.tx_ol_flags = fw_info.tx_ol_flags; rsp->s_fw_info.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } static void octep_pfvf_set_offloads(struct octep_device *oct, u32 vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { struct octep_ctrl_net_offloads offloads = { .rx_offloads = cmd.s_offloads.rx_ol_flags, .tx_offloads = cmd.s_offloads.tx_ol_flags }; int err; err = octep_ctrl_net_set_offloads(oct, vf_id, &offloads, true); if (err) { rsp->s_offloads.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; dev_err(&oct->pdev->dev, "Set VF offloads failed via host control Mbox\n"); return; } rsp->s_offloads.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; } int octep_setup_pfvf_mbox(struct octep_device *oct) { int i = 0, num_vfs = 0, rings_per_vf = 0; int ring = 0; num_vfs = oct->conf->sriov_cfg.active_vfs; rings_per_vf = oct->conf->sriov_cfg.max_rings_per_vf; for (i = 0; i < num_vfs; i++) { ring = rings_per_vf * i; oct->mbox[ring] = vzalloc(sizeof(*oct->mbox[ring])); if (!oct->mbox[ring]) goto free_mbox; memset(oct->mbox[ring], 0, sizeof(struct octep_mbox)); memset(&oct->vf_info[i], 0, sizeof(struct octep_pfvf_info)); mutex_init(&oct->mbox[ring]->lock); INIT_WORK(&oct->mbox[ring]->wk.work, octep_pfvf_mbox_work); oct->mbox[ring]->wk.ctxptr = oct->mbox[ring]; oct->mbox[ring]->oct = oct; oct->mbox[ring]->vf_id = i; oct->hw_ops.setup_mbox_regs(oct, ring); } return 0; free_mbox: while (i) { i--; ring = rings_per_vf * i; cancel_work_sync(&oct->mbox[ring]->wk.work); mutex_destroy(&oct->mbox[ring]->lock); vfree(oct->mbox[ring]); oct->mbox[ring] = NULL; } return -ENOMEM; } void octep_delete_pfvf_mbox(struct octep_device *oct) { int rings_per_vf = oct->conf->sriov_cfg.max_rings_per_vf; int num_vfs = oct->conf->sriov_cfg.active_vfs; int i = 0, ring = 0, vf_srn = 0; for (i = 0; i < num_vfs; i++) { ring = vf_srn + rings_per_vf * i; if (!oct->mbox[ring]) continue; if (work_pending(&oct->mbox[ring]->wk.work)) cancel_work_sync(&oct->mbox[ring]->wk.work); mutex_destroy(&oct->mbox[ring]->lock); vfree(oct->mbox[ring]); oct->mbox[ring] = NULL; } } static void octep_pfvf_pf_get_data(struct octep_device *oct, struct octep_mbox *mbox, int vf_id, union octep_pfvf_mbox_word cmd, union octep_pfvf_mbox_word *rsp) { int length = 0; int i = 0; int err; struct octep_iface_link_info link_info; struct octep_iface_rx_stats rx_stats; struct octep_iface_tx_stats tx_stats; rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; if (cmd.s_data.frag != OCTEP_PFVF_MBOX_MORE_FRAG_FLAG) { mbox->config_data_index = 0; memset(mbox->config_data, 0, MAX_VF_PF_MBOX_DATA_SIZE); /* Based on the OPCODE CMD the PF driver * specific API should be called to fetch * the requested data */ switch (cmd.s.opcode) { case OCTEP_PFVF_MBOX_CMD_GET_LINK_INFO: memset(&link_info, 0, sizeof(link_info)); err = octep_ctrl_net_get_link_info(oct, vf_id, &link_info); if (!err) { mbox->message_len = sizeof(link_info); *((int32_t *)rsp->s_data.data) = mbox->message_len; memcpy(mbox->config_data, (u8 *)&link_info, sizeof(link_info)); } else { rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; return; } break; case OCTEP_PFVF_MBOX_CMD_GET_STATS: memset(&rx_stats, 0, sizeof(rx_stats)); memset(&tx_stats, 0, sizeof(tx_stats)); err = octep_ctrl_net_get_if_stats(oct, vf_id, &rx_stats, &tx_stats); if (!err) { mbox->message_len = sizeof(rx_stats) + sizeof(tx_stats); *((int32_t *)rsp->s_data.data) = mbox->message_len; memcpy(mbox->config_data, (u8 *)&rx_stats, sizeof(rx_stats)); memcpy(mbox->config_data + sizeof(rx_stats), (u8 *)&tx_stats, sizeof(tx_stats)); } else { rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; return; } break; } *((int32_t *)rsp->s_data.data) = mbox->message_len; return; } if (mbox->message_len > OCTEP_PFVF_MBOX_MAX_DATA_SIZE) length = OCTEP_PFVF_MBOX_MAX_DATA_SIZE; else length = mbox->message_len; mbox->message_len -= length; for (i = 0; i < length; i++) { rsp->s_data.data[i] = mbox->config_data[mbox->config_data_index]; mbox->config_data_index++; } } void octep_pfvf_notify(struct octep_device *oct, struct octep_ctrl_mbox_msg *msg) { union octep_pfvf_mbox_word notif = { 0 }; struct octep_ctrl_net_f2h_req *req; req = (struct octep_ctrl_net_f2h_req *)msg->sg_list[0].msg; switch (req->hdr.s.cmd) { case OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS: notif.s_link_status.opcode = OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS; notif.s_link_status.status = req->link.state; break; default: pr_info("Unknown mbox notif for vf: %u\n", req->hdr.s.cmd); return; } notif.s.type = OCTEP_PFVF_MBOX_TYPE_CMD; octep_send_notification(oct, msg->hdr.s.vf_idx, notif); } void octep_pfvf_mbox_work(struct work_struct *work) { struct octep_pfvf_mbox_wk *wk = container_of(work, struct octep_pfvf_mbox_wk, work); union octep_pfvf_mbox_word cmd = { 0 }; union octep_pfvf_mbox_word rsp = { 0 }; struct octep_mbox *mbox = NULL; struct octep_device *oct = NULL; int vf_id; mbox = (struct octep_mbox *)wk->ctxptr; oct = (struct octep_device *)mbox->oct; vf_id = mbox->vf_id; mutex_lock(&mbox->lock); cmd.u64 = readq(mbox->vf_pf_data_reg); rsp.u64 = 0; switch (cmd.s.opcode) { case OCTEP_PFVF_MBOX_CMD_VERSION: octep_pfvf_validate_version(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_GET_LINK_STATUS: octep_pfvf_get_link_status(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_SET_LINK_STATUS: octep_pfvf_set_link_status(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_SET_RX_STATE: octep_pfvf_set_rx_state(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_SET_MTU: octep_pfvf_set_mtu(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_SET_MAC_ADDR: octep_pfvf_set_mac_addr(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_GET_MAC_ADDR: octep_pfvf_get_mac_addr(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_GET_LINK_INFO: case OCTEP_PFVF_MBOX_CMD_GET_STATS: octep_pfvf_pf_get_data(oct, mbox, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_GET_MTU: octep_pfvf_get_mtu(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_DEV_REMOVE: octep_pfvf_dev_remove(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_GET_FW_INFO: octep_pfvf_get_fw_info(oct, vf_id, cmd, &rsp); break; case OCTEP_PFVF_MBOX_CMD_SET_OFFLOADS: octep_pfvf_set_offloads(oct, vf_id, cmd, &rsp); break; default: dev_err(&oct->pdev->dev, "PF-VF mailbox: invalid opcode %d\n", cmd.s.opcode); rsp.s.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; break; } writeq(rsp.u64, mbox->vf_pf_data_reg); mutex_unlock(&mbox->lock); }