261 lines
6.8 KiB
C
261 lines
6.8 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Marvell RVU Admin Function driver
|
||
|
*
|
||
|
* Copyright (C) 2021 Marvell.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/bitfield.h>
|
||
|
#include "rvu.h"
|
||
|
|
||
|
static int rvu_switch_install_rx_rule(struct rvu *rvu, u16 pcifunc,
|
||
|
u16 chan_mask)
|
||
|
{
|
||
|
struct npc_install_flow_req req = { 0 };
|
||
|
struct npc_install_flow_rsp rsp = { 0 };
|
||
|
struct rvu_pfvf *pfvf;
|
||
|
|
||
|
pfvf = rvu_get_pfvf(rvu, pcifunc);
|
||
|
/* If the pcifunc is not initialized then nothing to do.
|
||
|
* This same function will be called again via rvu_switch_update_rules
|
||
|
* after pcifunc is initialized.
|
||
|
*/
|
||
|
if (!test_bit(NIXLF_INITIALIZED, &pfvf->flags))
|
||
|
return 0;
|
||
|
|
||
|
ether_addr_copy(req.packet.dmac, pfvf->mac_addr);
|
||
|
eth_broadcast_addr((u8 *)&req.mask.dmac);
|
||
|
req.hdr.pcifunc = 0; /* AF is requester */
|
||
|
req.vf = pcifunc;
|
||
|
req.features = BIT_ULL(NPC_DMAC);
|
||
|
req.channel = pfvf->rx_chan_base;
|
||
|
req.chan_mask = chan_mask;
|
||
|
req.intf = pfvf->nix_rx_intf;
|
||
|
req.op = NIX_RX_ACTION_DEFAULT;
|
||
|
req.default_rule = 1;
|
||
|
|
||
|
return rvu_mbox_handler_npc_install_flow(rvu, &req, &rsp);
|
||
|
}
|
||
|
|
||
|
static int rvu_switch_install_tx_rule(struct rvu *rvu, u16 pcifunc, u16 entry)
|
||
|
{
|
||
|
struct npc_install_flow_req req = { 0 };
|
||
|
struct npc_install_flow_rsp rsp = { 0 };
|
||
|
struct rvu_pfvf *pfvf;
|
||
|
u8 lbkid;
|
||
|
|
||
|
pfvf = rvu_get_pfvf(rvu, pcifunc);
|
||
|
/* If the pcifunc is not initialized then nothing to do.
|
||
|
* This same function will be called again via rvu_switch_update_rules
|
||
|
* after pcifunc is initialized.
|
||
|
*/
|
||
|
if (!test_bit(NIXLF_INITIALIZED, &pfvf->flags))
|
||
|
return 0;
|
||
|
|
||
|
lbkid = pfvf->nix_blkaddr == BLKADDR_NIX0 ? 0 : 1;
|
||
|
ether_addr_copy(req.packet.dmac, pfvf->mac_addr);
|
||
|
eth_broadcast_addr((u8 *)&req.mask.dmac);
|
||
|
req.hdr.pcifunc = 0; /* AF is requester */
|
||
|
req.vf = pcifunc;
|
||
|
req.entry = entry;
|
||
|
req.features = BIT_ULL(NPC_DMAC);
|
||
|
req.intf = pfvf->nix_tx_intf;
|
||
|
req.op = NIX_TX_ACTIONOP_UCAST_CHAN;
|
||
|
req.index = (lbkid << 8) | RVU_SWITCH_LBK_CHAN;
|
||
|
req.set_cntr = 1;
|
||
|
|
||
|
return rvu_mbox_handler_npc_install_flow(rvu, &req, &rsp);
|
||
|
}
|
||
|
|
||
|
static int rvu_switch_install_rules(struct rvu *rvu)
|
||
|
{
|
||
|
struct rvu_switch *rswitch = &rvu->rswitch;
|
||
|
u16 start = rswitch->start_entry;
|
||
|
struct rvu_hwinfo *hw = rvu->hw;
|
||
|
u16 pcifunc, entry = 0;
|
||
|
int pf, vf, numvfs;
|
||
|
int err;
|
||
|
|
||
|
for (pf = 1; pf < hw->total_pfs; pf++) {
|
||
|
if (!is_pf_cgxmapped(rvu, pf))
|
||
|
continue;
|
||
|
|
||
|
pcifunc = pf << 10;
|
||
|
/* rvu_get_nix_blkaddr sets up the corresponding NIX block
|
||
|
* address and NIX RX and TX interfaces for a pcifunc.
|
||
|
* Generally it is called during attach call of a pcifunc but it
|
||
|
* is called here since we are pre-installing rules before
|
||
|
* nixlfs are attached
|
||
|
*/
|
||
|
rvu_get_nix_blkaddr(rvu, pcifunc);
|
||
|
|
||
|
/* MCAM RX rule for a PF/VF already exists as default unicast
|
||
|
* rules installed by AF. Hence change the channel in those
|
||
|
* rules to ignore channel so that packets with the required
|
||
|
* DMAC received from LBK(by other PF/VFs in system) or from
|
||
|
* external world (from wire) are accepted.
|
||
|
*/
|
||
|
err = rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
|
||
|
if (err) {
|
||
|
dev_err(rvu->dev, "RX rule for PF%d failed(%d)\n",
|
||
|
pf, err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
err = rvu_switch_install_tx_rule(rvu, pcifunc, start + entry);
|
||
|
if (err) {
|
||
|
dev_err(rvu->dev, "TX rule for PF%d failed(%d)\n",
|
||
|
pf, err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rswitch->entry2pcifunc[entry++] = pcifunc;
|
||
|
|
||
|
rvu_get_pf_numvfs(rvu, pf, &numvfs, NULL);
|
||
|
for (vf = 0; vf < numvfs; vf++) {
|
||
|
pcifunc = pf << 10 | ((vf + 1) & 0x3FF);
|
||
|
rvu_get_nix_blkaddr(rvu, pcifunc);
|
||
|
|
||
|
err = rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
|
||
|
if (err) {
|
||
|
dev_err(rvu->dev,
|
||
|
"RX rule for PF%dVF%d failed(%d)\n",
|
||
|
pf, vf, err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
err = rvu_switch_install_tx_rule(rvu, pcifunc,
|
||
|
start + entry);
|
||
|
if (err) {
|
||
|
dev_err(rvu->dev,
|
||
|
"TX rule for PF%dVF%d failed(%d)\n",
|
||
|
pf, vf, err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rswitch->entry2pcifunc[entry++] = pcifunc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void rvu_switch_enable(struct rvu *rvu)
|
||
|
{
|
||
|
struct npc_mcam_alloc_entry_req alloc_req = { 0 };
|
||
|
struct npc_mcam_alloc_entry_rsp alloc_rsp = { 0 };
|
||
|
struct npc_delete_flow_req uninstall_req = { 0 };
|
||
|
struct npc_mcam_free_entry_req free_req = { 0 };
|
||
|
struct rvu_switch *rswitch = &rvu->rswitch;
|
||
|
struct msg_rsp rsp;
|
||
|
int ret;
|
||
|
|
||
|
alloc_req.contig = true;
|
||
|
alloc_req.count = rvu->cgx_mapped_pfs + rvu->cgx_mapped_vfs;
|
||
|
ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &alloc_req,
|
||
|
&alloc_rsp);
|
||
|
if (ret) {
|
||
|
dev_err(rvu->dev,
|
||
|
"Unable to allocate MCAM entries\n");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (alloc_rsp.count != alloc_req.count) {
|
||
|
dev_err(rvu->dev,
|
||
|
"Unable to allocate %d MCAM entries, got %d\n",
|
||
|
alloc_req.count, alloc_rsp.count);
|
||
|
goto free_entries;
|
||
|
}
|
||
|
|
||
|
rswitch->entry2pcifunc = kcalloc(alloc_req.count, sizeof(u16),
|
||
|
GFP_KERNEL);
|
||
|
if (!rswitch->entry2pcifunc)
|
||
|
goto free_entries;
|
||
|
|
||
|
rswitch->used_entries = alloc_rsp.count;
|
||
|
rswitch->start_entry = alloc_rsp.entry;
|
||
|
|
||
|
ret = rvu_switch_install_rules(rvu);
|
||
|
if (ret)
|
||
|
goto uninstall_rules;
|
||
|
|
||
|
return;
|
||
|
|
||
|
uninstall_rules:
|
||
|
uninstall_req.start = rswitch->start_entry;
|
||
|
uninstall_req.end = rswitch->start_entry + rswitch->used_entries - 1;
|
||
|
rvu_mbox_handler_npc_delete_flow(rvu, &uninstall_req, &rsp);
|
||
|
kfree(rswitch->entry2pcifunc);
|
||
|
free_entries:
|
||
|
free_req.all = 1;
|
||
|
rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp);
|
||
|
exit:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void rvu_switch_disable(struct rvu *rvu)
|
||
|
{
|
||
|
struct npc_delete_flow_req uninstall_req = { 0 };
|
||
|
struct npc_mcam_free_entry_req free_req = { 0 };
|
||
|
struct rvu_switch *rswitch = &rvu->rswitch;
|
||
|
struct rvu_hwinfo *hw = rvu->hw;
|
||
|
int pf, vf, numvfs;
|
||
|
struct msg_rsp rsp;
|
||
|
u16 pcifunc;
|
||
|
int err;
|
||
|
|
||
|
if (!rswitch->used_entries)
|
||
|
return;
|
||
|
|
||
|
for (pf = 1; pf < hw->total_pfs; pf++) {
|
||
|
if (!is_pf_cgxmapped(rvu, pf))
|
||
|
continue;
|
||
|
|
||
|
pcifunc = pf << 10;
|
||
|
err = rvu_switch_install_rx_rule(rvu, pcifunc, 0xFFF);
|
||
|
if (err)
|
||
|
dev_err(rvu->dev,
|
||
|
"Reverting RX rule for PF%d failed(%d)\n",
|
||
|
pf, err);
|
||
|
|
||
|
rvu_get_pf_numvfs(rvu, pf, &numvfs, NULL);
|
||
|
for (vf = 0; vf < numvfs; vf++) {
|
||
|
pcifunc = pf << 10 | ((vf + 1) & 0x3FF);
|
||
|
err = rvu_switch_install_rx_rule(rvu, pcifunc, 0xFFF);
|
||
|
if (err)
|
||
|
dev_err(rvu->dev,
|
||
|
"Reverting RX rule for PF%dVF%d failed(%d)\n",
|
||
|
pf, vf, err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uninstall_req.start = rswitch->start_entry;
|
||
|
uninstall_req.end = rswitch->start_entry + rswitch->used_entries - 1;
|
||
|
free_req.all = 1;
|
||
|
rvu_mbox_handler_npc_delete_flow(rvu, &uninstall_req, &rsp);
|
||
|
rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp);
|
||
|
rswitch->used_entries = 0;
|
||
|
kfree(rswitch->entry2pcifunc);
|
||
|
}
|
||
|
|
||
|
void rvu_switch_update_rules(struct rvu *rvu, u16 pcifunc)
|
||
|
{
|
||
|
struct rvu_switch *rswitch = &rvu->rswitch;
|
||
|
u32 max = rswitch->used_entries;
|
||
|
u16 entry;
|
||
|
|
||
|
if (!rswitch->used_entries)
|
||
|
return;
|
||
|
|
||
|
for (entry = 0; entry < max; entry++) {
|
||
|
if (rswitch->entry2pcifunc[entry] == pcifunc)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (entry >= max)
|
||
|
return;
|
||
|
|
||
|
rvu_switch_install_tx_rule(rvu, pcifunc, rswitch->start_entry + entry);
|
||
|
rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
|
||
|
}
|