1308 lines
35 KiB
C
1308 lines
35 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
/*
|
|
* NETC NTMP (NETC Table Management Protocol) 2.0 Library
|
|
* Copyright 2025-2026 NXP
|
|
*/
|
|
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/fsl/netc_global.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "ntmp_private.h"
|
|
|
|
#define NETC_CBDR_TIMEOUT 1000 /* us */
|
|
#define NETC_CBDR_DELAY_US 10
|
|
#define NETC_CBDR_MR_EN BIT(31)
|
|
|
|
#define NTMP_BASE_ADDR_ALIGN 128
|
|
#define NTMP_DATA_ADDR_ALIGN 32
|
|
|
|
/* Define NTMP Table ID */
|
|
#define NTMP_MAFT_ID 1
|
|
#define NTMP_RSST_ID 3
|
|
#define NTMP_IPFT_ID 13
|
|
#define NTMP_FDBT_ID 15
|
|
#define NTMP_VFT_ID 18
|
|
#define NTMP_ETT_ID 33
|
|
#define NTMP_ECT_ID 39
|
|
#define NTMP_BPT_ID 41
|
|
|
|
/* Generic Update Actions for most tables */
|
|
#define NTMP_GEN_UA_CFGEU BIT(0)
|
|
#define NTMP_GEN_UA_STSEU BIT(1)
|
|
|
|
/* Specific Update Actions for some tables */
|
|
#define FDBT_UA_ACTEU BIT(1)
|
|
#define ECT_UA_STSEU BIT(0)
|
|
#define BPT_UA_BPSEU BIT(1)
|
|
|
|
/* Query Action: 0: Full query. 1: Query entry ID, the fields after entry
|
|
* ID are not returned.
|
|
*/
|
|
#define NTMP_QA_ENTRY_ID 1
|
|
|
|
#define NTMP_ENTRY_ID_SIZE 4
|
|
#define RSST_ENTRY_NUM 64
|
|
#define RSST_STSE_DATA_SIZE(n) ((n) * 8)
|
|
#define RSST_CFGE_DATA_SIZE(n) (n)
|
|
|
|
u32 ntmp_lookup_free_eid(unsigned long *bitmap, u32 size)
|
|
{
|
|
u32 entry_id;
|
|
|
|
entry_id = find_first_zero_bit(bitmap, size);
|
|
if (entry_id == size)
|
|
return NTMP_NULL_ENTRY_ID;
|
|
|
|
/* Set the bit once we found it */
|
|
__set_bit(entry_id, bitmap);
|
|
|
|
return entry_id;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_lookup_free_eid);
|
|
|
|
void ntmp_clear_eid_bitmap(unsigned long *bitmap, u32 entry_id)
|
|
{
|
|
if (entry_id == NTMP_NULL_ENTRY_ID)
|
|
return;
|
|
|
|
__clear_bit(entry_id, bitmap);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_clear_eid_bitmap);
|
|
|
|
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
|
|
const struct netc_cbdr_regs *regs)
|
|
{
|
|
int cbd_num = NETC_CBDR_BD_NUM;
|
|
size_t size;
|
|
|
|
size = cbd_num * sizeof(union netc_cbd) + NTMP_BASE_ADDR_ALIGN;
|
|
cbdr->addr_base = dma_alloc_coherent(dev, size, &cbdr->dma_base,
|
|
GFP_KERNEL);
|
|
if (!cbdr->addr_base)
|
|
return -ENOMEM;
|
|
|
|
cbdr->swcbd = vcalloc(cbd_num, sizeof(struct netc_swcbd));
|
|
if (!cbdr->swcbd) {
|
|
dma_free_coherent(dev, size, cbdr->addr_base, cbdr->dma_base);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
cbdr->dma_size = size;
|
|
cbdr->bd_num = cbd_num;
|
|
cbdr->regs = *regs;
|
|
cbdr->dev = dev;
|
|
|
|
/* The base address of the Control BD Ring must be 128 bytes aligned */
|
|
cbdr->dma_base_align = ALIGN(cbdr->dma_base, NTMP_BASE_ADDR_ALIGN);
|
|
cbdr->addr_base_align = PTR_ALIGN(cbdr->addr_base,
|
|
NTMP_BASE_ADDR_ALIGN);
|
|
|
|
mutex_init(&cbdr->ring_lock);
|
|
|
|
cbdr->next_to_use = netc_read(cbdr->regs.pir);
|
|
cbdr->next_to_clean = netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX;
|
|
|
|
/* Step 1: Configure the base address of the Control BD Ring */
|
|
netc_write(cbdr->regs.bar0, lower_32_bits(cbdr->dma_base_align));
|
|
netc_write(cbdr->regs.bar1, upper_32_bits(cbdr->dma_base_align));
|
|
|
|
/* Step 2: Configure the number of BDs of the Control BD Ring */
|
|
netc_write(cbdr->regs.lenr, cbdr->bd_num);
|
|
|
|
/* Step 3: Enable the Control BD Ring */
|
|
netc_write(cbdr->regs.mr, NETC_CBDR_MR_EN);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_init_cbdr);
|
|
|
|
static void ntmp_free_data_mem(struct device *dev, struct netc_swcbd *swcbd)
|
|
{
|
|
if (unlikely(!swcbd->buf))
|
|
return;
|
|
|
|
dma_free_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN,
|
|
swcbd->buf, swcbd->dma);
|
|
}
|
|
|
|
void ntmp_free_cbdr(struct netc_cbdr *cbdr)
|
|
{
|
|
/* Disable the Control BD Ring */
|
|
netc_write(cbdr->regs.mr, 0);
|
|
|
|
for (int i = 0; i < cbdr->bd_num; i++)
|
|
ntmp_free_data_mem(cbdr->dev, &cbdr->swcbd[i]);
|
|
|
|
vfree(cbdr->swcbd);
|
|
dma_free_coherent(cbdr->dev, cbdr->dma_size, cbdr->addr_base,
|
|
cbdr->dma_base);
|
|
memset(cbdr, 0, sizeof(*cbdr));
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_free_cbdr);
|
|
|
|
static int ntmp_get_free_cbd_num(struct netc_cbdr *cbdr)
|
|
{
|
|
return (cbdr->next_to_clean - cbdr->next_to_use - 1 +
|
|
cbdr->bd_num) % cbdr->bd_num;
|
|
}
|
|
|
|
static union netc_cbd *ntmp_get_cbd(struct netc_cbdr *cbdr, int index)
|
|
{
|
|
return &((union netc_cbd *)(cbdr->addr_base_align))[index];
|
|
}
|
|
|
|
static void ntmp_clean_cbdr(struct netc_cbdr *cbdr)
|
|
{
|
|
int i = cbdr->next_to_clean;
|
|
|
|
while ((netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX) != i) {
|
|
union netc_cbd *cbd = ntmp_get_cbd(cbdr, i);
|
|
struct netc_swcbd *swcbd = &cbdr->swcbd[i];
|
|
|
|
ntmp_free_data_mem(cbdr->dev, swcbd);
|
|
memset(swcbd, 0, sizeof(*swcbd));
|
|
memset(cbd, 0, sizeof(*cbd));
|
|
i = (i + 1) % cbdr->bd_num;
|
|
}
|
|
|
|
dma_wmb();
|
|
cbdr->next_to_clean = i;
|
|
}
|
|
|
|
static void ntmp_select_and_lock_cbdr(struct ntmp_user *user,
|
|
struct netc_cbdr **cbdr)
|
|
{
|
|
for (int i = 0; i < user->cbdr_num; i++) {
|
|
*cbdr = &user->ring[i];
|
|
if (mutex_trylock(&(*cbdr)->ring_lock))
|
|
return;
|
|
}
|
|
|
|
/* If all command BD rings are locked, we need to select one of
|
|
* them and wait for it.
|
|
*/
|
|
*cbdr = &user->ring[raw_smp_processor_id() % user->cbdr_num];
|
|
mutex_lock(&(*cbdr)->ring_lock);
|
|
}
|
|
|
|
static void ntmp_unlock_cbdr(struct netc_cbdr *cbdr)
|
|
{
|
|
mutex_unlock(&cbdr->ring_lock);
|
|
}
|
|
|
|
static int netc_xmit_ntmp_cmd(struct netc_cbdr *cbdr, union netc_cbd *cbd,
|
|
struct netc_swcbd *swcbd)
|
|
{
|
|
union netc_cbd *cur_cbd;
|
|
int i, err, used_bds;
|
|
u16 status;
|
|
u32 val;
|
|
|
|
used_bds = cbdr->bd_num - ntmp_get_free_cbd_num(cbdr);
|
|
if (unlikely(used_bds >= NETC_CBDR_CLEAN_WORK)) {
|
|
ntmp_clean_cbdr(cbdr);
|
|
if (unlikely(!ntmp_get_free_cbd_num(cbdr))) {
|
|
ntmp_free_data_mem(cbdr->dev, swcbd);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
i = cbdr->next_to_use;
|
|
cur_cbd = ntmp_get_cbd(cbdr, i);
|
|
*cur_cbd = *cbd;
|
|
cbdr->swcbd[i] = *swcbd;
|
|
dma_wmb();
|
|
|
|
/* Update producer index of both software and hardware */
|
|
i = (i + 1) % cbdr->bd_num;
|
|
cbdr->next_to_use = i;
|
|
netc_write(cbdr->regs.pir, i);
|
|
|
|
err = read_poll_timeout(netc_read, val,
|
|
(val & NETC_CBDRCIR_INDEX) == i,
|
|
NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT,
|
|
true, cbdr->regs.cir);
|
|
if (unlikely(err))
|
|
return err;
|
|
|
|
if (unlikely(val & NETC_CBDRCIR_SBE)) {
|
|
dev_err(cbdr->dev, "Command BD system bus error\n");
|
|
return -EIO;
|
|
}
|
|
|
|
dma_rmb();
|
|
/* Get the writeback command BD, because the caller may need
|
|
* to check some other fields of the response header.
|
|
*/
|
|
*cbd = *cur_cbd;
|
|
|
|
/* Check the writeback error status */
|
|
status = le16_to_cpu(cbd->resp_hdr.error_rr) & NTMP_RESP_ERROR;
|
|
if (unlikely(status)) {
|
|
dev_err(cbdr->dev, "Command BD error: 0x%04x\n", status);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ntmp_alloc_data_mem(struct device *dev, struct netc_swcbd *swcbd,
|
|
void **buf_align)
|
|
{
|
|
void *buf;
|
|
|
|
buf = dma_alloc_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN,
|
|
&swcbd->dma, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
swcbd->buf = buf;
|
|
*buf_align = PTR_ALIGN(buf, NTMP_DATA_ADDR_ALIGN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ntmp_fill_request_hdr(union netc_cbd *cbd, dma_addr_t dma,
|
|
int len, int table_id, int cmd,
|
|
int access_method)
|
|
{
|
|
dma_addr_t dma_align;
|
|
|
|
memset(cbd, 0, sizeof(*cbd));
|
|
dma_align = ALIGN(dma, NTMP_DATA_ADDR_ALIGN);
|
|
cbd->req_hdr.addr = cpu_to_le64(dma_align);
|
|
cbd->req_hdr.len = cpu_to_le32(len);
|
|
cbd->req_hdr.cmd = cmd;
|
|
cbd->req_hdr.access_method = FIELD_PREP(NTMP_ACCESS_METHOD,
|
|
access_method);
|
|
cbd->req_hdr.table_id = table_id;
|
|
cbd->req_hdr.ver_cci_rr = FIELD_PREP(NTMP_HDR_VERSION,
|
|
NTMP_HDR_VER2);
|
|
/* For NTMP version 2.0 or later version */
|
|
cbd->req_hdr.npf = cpu_to_le32(NTMP_NPF);
|
|
}
|
|
|
|
static void ntmp_fill_crd(struct ntmp_cmn_req_data *crd, u8 tblv,
|
|
u8 qa, u16 ua)
|
|
{
|
|
crd->update_act = cpu_to_le16(ua);
|
|
crd->tblv_qact = NTMP_TBLV_QACT(tblv, qa);
|
|
}
|
|
|
|
static void ntmp_fill_crd_eid(struct ntmp_req_by_eid *rbe, u8 tblv,
|
|
u8 qa, u16 ua, u32 entry_id)
|
|
{
|
|
ntmp_fill_crd(&rbe->crd, tblv, qa, ua);
|
|
rbe->entry_id = cpu_to_le32(entry_id);
|
|
}
|
|
|
|
static const char *ntmp_table_name(int tbl_id)
|
|
{
|
|
switch (tbl_id) {
|
|
case NTMP_MAFT_ID:
|
|
return "MAC Address Filter Table";
|
|
case NTMP_RSST_ID:
|
|
return "RSS Table";
|
|
case NTMP_IPFT_ID:
|
|
return "Ingress Port Filter Table";
|
|
case NTMP_FDBT_ID:
|
|
return "FDB Table";
|
|
case NTMP_VFT_ID:
|
|
return "VLAN Filter Table";
|
|
case NTMP_ETT_ID:
|
|
return "Egress Treatment Table";
|
|
case NTMP_ECT_ID:
|
|
return "Egress Count Table";
|
|
case NTMP_BPT_ID:
|
|
return "Buffer Pool Table";
|
|
default:
|
|
return "Unknown Table";
|
|
}
|
|
}
|
|
|
|
static int ntmp_delete_entry_by_id(struct ntmp_user *user, int tbl_id,
|
|
u8 tbl_ver, u32 entry_id, u32 req_len,
|
|
u32 resp_len)
|
|
{
|
|
struct netc_swcbd swcbd = {
|
|
.size = max(req_len, resp_len),
|
|
};
|
|
struct ntmp_req_by_eid *req;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err;
|
|
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
ntmp_fill_crd_eid(req, tbl_ver, 0, 0, entry_id);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(req_len, resp_len),
|
|
tbl_id, NTMP_CMD_DELETE, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to delete entry 0x%x of %s, err: %pe",
|
|
entry_id, ntmp_table_name(tbl_id), ERR_PTR(err));
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ntmp_query_entry_by_id(struct netc_cbdr *cbdr, int tbl_id,
|
|
struct ntmp_req_by_eid *req,
|
|
struct netc_swcbd *swcbd,
|
|
bool compare_eid)
|
|
{
|
|
u32 len = NTMP_LEN(sizeof(*req), swcbd->size);
|
|
struct ntmp_cmn_resp_query *resp;
|
|
int cmd = NTMP_CMD_QUERY;
|
|
union netc_cbd cbd;
|
|
u32 entry_id;
|
|
int err;
|
|
|
|
entry_id = le32_to_cpu(req->entry_id);
|
|
if (le16_to_cpu(req->crd.update_act))
|
|
cmd = NTMP_CMD_QU;
|
|
|
|
/* Request header */
|
|
ntmp_fill_request_hdr(&cbd, swcbd->dma, len, tbl_id, cmd,
|
|
NTMP_AM_ENTRY_ID);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, swcbd);
|
|
if (err) {
|
|
dev_err(cbdr->dev,
|
|
"Failed to query entry 0x%x of %s, err: %pe\n",
|
|
entry_id, ntmp_table_name(tbl_id), ERR_PTR(err));
|
|
return err;
|
|
}
|
|
|
|
/* For a few tables, the first field of their response data is not
|
|
* entry_id, so directly return success.
|
|
*/
|
|
if (!compare_eid)
|
|
return 0;
|
|
|
|
resp = (struct ntmp_cmn_resp_query *)req;
|
|
if (unlikely(le32_to_cpu(resp->entry_id) != entry_id)) {
|
|
dev_err(cbdr->dev,
|
|
"%s: query EID 0x%x doesn't match response EID 0x%x\n",
|
|
ntmp_table_name(tbl_id), entry_id, le32_to_cpu(resp->entry_id));
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id,
|
|
struct maft_entry_data *maft)
|
|
{
|
|
struct netc_swcbd swcbd = {
|
|
.size = sizeof(struct maft_req_add),
|
|
};
|
|
struct maft_req_add *req;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err;
|
|
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Set mac address filter table request data buffer */
|
|
ntmp_fill_crd_eid(&req->rbe, user->tbl.maft_ver, 0, 0, entry_id);
|
|
req->keye = maft->keye;
|
|
req->cfge = maft->cfge;
|
|
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
|
NTMP_MAFT_ID, NTMP_CMD_ADD, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev, "Failed to add MAFT entry 0x%x, err: %pe\n",
|
|
entry_id, ERR_PTR(err));
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_maft_add_entry);
|
|
|
|
int ntmp_maft_query_entry(struct ntmp_user *user, u32 entry_id,
|
|
struct maft_entry_data *maft)
|
|
{
|
|
struct netc_swcbd swcbd = {
|
|
.size = sizeof(struct maft_resp_query),
|
|
};
|
|
struct maft_resp_query *resp;
|
|
struct ntmp_req_by_eid *req;
|
|
struct netc_cbdr *cbdr;
|
|
int err;
|
|
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
ntmp_fill_crd_eid(req, user->tbl.maft_ver, 0, 0, entry_id);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = ntmp_query_entry_by_id(cbdr, NTMP_MAFT_ID, req, &swcbd, true);
|
|
if (err)
|
|
goto unlock_cbdr;
|
|
|
|
resp = (struct maft_resp_query *)req;
|
|
maft->keye = resp->keye;
|
|
maft->cfge = resp->cfge;
|
|
|
|
unlock_cbdr:
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_maft_query_entry);
|
|
|
|
int ntmp_maft_delete_entry(struct ntmp_user *user, u32 entry_id)
|
|
{
|
|
return ntmp_delete_entry_by_id(user, NTMP_MAFT_ID, user->tbl.maft_ver,
|
|
entry_id, NTMP_EID_REQ_LEN, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_maft_delete_entry);
|
|
|
|
int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
|
|
int count)
|
|
{
|
|
struct rsst_req_update *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err, i;
|
|
|
|
if (count != RSST_ENTRY_NUM)
|
|
/* HW only takes in a full 64 entry table */
|
|
return -EINVAL;
|
|
|
|
swcbd.size = struct_size(req, groups, count);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Set the request data buffer */
|
|
ntmp_fill_crd_eid(&req->rbe, user->tbl.rsst_ver, 0,
|
|
NTMP_GEN_UA_CFGEU | NTMP_GEN_UA_STSEU, 0);
|
|
for (i = 0; i < count; i++)
|
|
req->groups[i] = (u8)(table[i]);
|
|
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
|
NTMP_RSST_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev, "Failed to update RSST entry, err: %pe\n",
|
|
ERR_PTR(err));
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_rsst_update_entry);
|
|
|
|
int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
|
|
{
|
|
struct ntmp_req_by_eid *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err, i;
|
|
u8 *group;
|
|
|
|
if (count != RSST_ENTRY_NUM)
|
|
/* HW only takes in a full 64 entry table */
|
|
return -EINVAL;
|
|
|
|
swcbd.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) +
|
|
RSST_CFGE_DATA_SIZE(count);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Set the request data buffer */
|
|
ntmp_fill_crd_eid(req, user->tbl.rsst_ver, 0, 0, 0);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(sizeof(*req), swcbd.size),
|
|
NTMP_RSST_ID, NTMP_CMD_QUERY, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err) {
|
|
dev_err(user->dev, "Failed to query RSST entry, err: %pe\n",
|
|
ERR_PTR(err));
|
|
goto unlock_cbdr;
|
|
}
|
|
|
|
group = (u8 *)req;
|
|
group += NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count);
|
|
for (i = 0; i < count; i++)
|
|
table[i] = group[i];
|
|
|
|
unlock_cbdr:
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_rsst_query_entry);
|
|
|
|
/**
|
|
* ntmp_ipft_add_entry - add an entry into the ingress port filter table
|
|
* @user: target ntmp_user struct
|
|
* @entry: the entry data, entry->cfge (configuration element data) and
|
|
* entry->keye (key element data) are used as input. Since the entry ID
|
|
* is assigned by the hardware, so entry->entry_id is a returned value
|
|
* for the driver to use, the driver can update/delete/query the entry
|
|
* based on the entry_id.
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ipft_add_entry(struct ntmp_user *user,
|
|
struct ipft_entry_data *entry)
|
|
{
|
|
struct ipft_resp_query *resp;
|
|
struct ipft_req_ua *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*resp);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Note that NTMP_GEN_UA_STSEU is used to reset the statistics of
|
|
* the entry. The STSE_DATA is not present in the request data for
|
|
* 'Add' operation.
|
|
*/
|
|
ntmp_fill_crd(&req->crd, user->tbl.ipft_ver, NTMP_QA_ENTRY_ID,
|
|
NTMP_GEN_UA_CFGEU | NTMP_GEN_UA_STSEU);
|
|
req->ak.keye = entry->keye;
|
|
req->cfge = entry->cfge;
|
|
|
|
len = NTMP_LEN(sizeof(*req), swcbd.size);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_IPFT_ID,
|
|
NTMP_CMD_AQ, NTMP_AM_TERNARY_KEY);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err) {
|
|
dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
|
|
ntmp_table_name(NTMP_IPFT_ID), ERR_PTR(err));
|
|
|
|
goto unlock_cbdr;
|
|
}
|
|
|
|
resp = (struct ipft_resp_query *)req;
|
|
entry->entry_id = le32_to_cpu(resp->entry_id);
|
|
|
|
unlock_cbdr:
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ipft_add_entry);
|
|
|
|
/**
|
|
* ntmp_ipft_delete_entry - delete a specified ingress port filter table entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: the specified ID of the ingress port filter table entry
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id)
|
|
{
|
|
u32 req_len = sizeof(struct ipft_req_qd);
|
|
|
|
return ntmp_delete_entry_by_id(user, NTMP_IPFT_ID,
|
|
user->tbl.ipft_ver,
|
|
entry_id, req_len,
|
|
NTMP_STATUS_RESP_LEN);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ipft_delete_entry);
|
|
|
|
/**
|
|
* ntmp_fdbt_add_entry - add an entry into the FDB table
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: returned value, the entry ID of the new added entry
|
|
* @keye: key element data
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
|
|
const struct fdbt_keye_data *keye,
|
|
const struct fdbt_cfge_data *cfge)
|
|
{
|
|
struct fdbt_resp_query *resp;
|
|
struct fdbt_req_ua *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, NTMP_QA_ENTRY_ID,
|
|
NTMP_GEN_UA_CFGEU);
|
|
req->ak.exact.keye = *keye;
|
|
req->cfge = *cfge;
|
|
|
|
len = NTMP_LEN(swcbd.size, sizeof(*resp));
|
|
/* The entry ID is allotted by hardware, so we need to perform
|
|
* a query action after the add action to get the entry ID from
|
|
* hardware.
|
|
*/
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_AQ, NTMP_AM_EXACT_KEY);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err) {
|
|
dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
|
goto unlock_cbdr;
|
|
}
|
|
|
|
if (entry_id) {
|
|
resp = (struct fdbt_resp_query *)req;
|
|
*entry_id = le32_to_cpu(resp->entry_id);
|
|
}
|
|
|
|
unlock_cbdr:
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_add_entry);
|
|
|
|
/**
|
|
* ntmp_fdbt_update_entry - update the configuration element data of the
|
|
* specified FDB entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: the specified entry ID of the FDB table
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
|
|
const struct fdbt_cfge_data *cfge)
|
|
{
|
|
struct fdbt_req_ua *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, NTMP_GEN_UA_CFGEU);
|
|
req->ak.eid.entry_id = cpu_to_le32(entry_id);
|
|
req->cfge = *cfge;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev, "Failed to update %s entry, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_update_entry);
|
|
|
|
/**
|
|
* ntmp_fdbt_delete_entry - delete the specified FDB entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: the specified ID of the FDB entry
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id)
|
|
{
|
|
u32 req_len = sizeof(struct fdbt_req_qd);
|
|
|
|
return ntmp_delete_entry_by_id(user, NTMP_FDBT_ID,
|
|
user->tbl.fdbt_ver,
|
|
entry_id, req_len,
|
|
NTMP_STATUS_RESP_LEN);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_entry);
|
|
|
|
/**
|
|
* ntmp_fdbt_search_port_entry - Search the FDB entry on the specified
|
|
* port based on RESUME_ENTRY_ID
|
|
* @user: target ntmp_user struct
|
|
* @port: the specified switch port ID
|
|
* @resume_entry_id: it is both an input and an output. As an input, it
|
|
* represents the FDB entry ID to be searched. If it is a NULL entry ID,
|
|
* it indicates that the first FDB entry for that port is being searched.
|
|
* As an output, it represents the next FDB entry ID to be searched.
|
|
* @entry: returned value, the response data of the searched FDB entry
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
|
|
u32 *resume_entry_id,
|
|
struct fdbt_entry_data *entry)
|
|
{
|
|
struct fdbt_resp_query *resp;
|
|
struct fdbt_req_qd *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
|
|
req->ak.search.resume_eid = cpu_to_le32(*resume_entry_id);
|
|
req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port));
|
|
/* Match CFGE_DATA[PORT_BITMAP] field */
|
|
req->ak.search.cfge_mc = FDBT_CFGE_MC_PORT_BITMAP;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, sizeof(*resp));
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_QUERY, NTMP_AM_SEARCH);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err) {
|
|
dev_err(user->dev,
|
|
"Failed to search %s entry on port %d, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err));
|
|
goto unlock_cbdr;
|
|
}
|
|
|
|
if (!cbd.resp_hdr.num_matched) {
|
|
entry->entry_id = NTMP_NULL_ENTRY_ID;
|
|
*resume_entry_id = NTMP_NULL_ENTRY_ID;
|
|
goto unlock_cbdr;
|
|
}
|
|
|
|
resp = (struct fdbt_resp_query *)req;
|
|
*resume_entry_id = le32_to_cpu(resp->status);
|
|
entry->entry_id = le32_to_cpu(resp->entry_id);
|
|
entry->keye = resp->keye;
|
|
entry->cfge = resp->cfge;
|
|
entry->acte = resp->acte;
|
|
|
|
unlock_cbdr:
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry);
|
|
|
|
/**
|
|
* ntmp_fdbt_update_activity_element - update the activity element of all
|
|
* the dynamic entries in the FDB table.
|
|
* @user: target ntmp_user struct
|
|
*
|
|
* A single activity update management could be used to process all the
|
|
* dynamic entries in the FDB table. When hardware process an activity
|
|
* update management command for an entry in the FDB table and the entry
|
|
* does not have its activity flag set, the activity counter is incremented.
|
|
* However, if the activity flag is set, then both the activity flag and
|
|
* activity counter are reset. Software can issue the activity update
|
|
* management commands at predefined times and the value of the activity
|
|
* counter can then be used to estimate the period of how long an FDB
|
|
* entry has been inactive.
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_update_activity_element(struct ntmp_user *user)
|
|
{
|
|
struct fdbt_req_ua *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, FDBT_UA_ACTEU);
|
|
req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
|
|
req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
|
|
req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
/* For activity update, the access method must be search */
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_UPDATE, NTMP_AM_SEARCH);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to update activity of %s, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_update_activity_element);
|
|
|
|
/**
|
|
* ntmp_fdbt_delete_ageing_entries - delete all the ageing dynamic entries
|
|
* in the FDB table
|
|
* @user: target ntmp_user struct
|
|
* @act_cnt: the target value of the activity counter
|
|
*
|
|
* The matching rule is that the activity flag is not set and the activity
|
|
* counter is greater than or equal to act_cnt
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt)
|
|
{
|
|
struct fdbt_req_qd *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
if (act_cnt > FDBT_ACT_CNT)
|
|
return -EINVAL;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
|
|
req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
|
|
req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
|
|
req->ak.search.acte = act_cnt;
|
|
/* Exact match with ACTE_DATA[ACT_FLAG] AND
|
|
* match >= ACTE_DATA[ACT_CNT]
|
|
*/
|
|
req->ak.search.acte_mc = FDBT_ACTE_MC;
|
|
req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_DELETE, NTMP_AM_SEARCH);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to delete ageing entries of %s, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_ageing_entries);
|
|
|
|
/**
|
|
* ntmp_fdbt_delete_port_dynamic_entries - delete all dynamic FDB entries
|
|
* associated with the specified switch port
|
|
* @user: target ntmp_user struct
|
|
* @port: the specified switch port ID
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port)
|
|
{
|
|
struct fdbt_req_qd *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
|
|
req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
|
|
req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port));
|
|
req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
|
|
/* Match CFGE_DATA[DYNAMIC & PORT_BITMAP] field */
|
|
req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
|
NTMP_CMD_DELETE, NTMP_AM_SEARCH);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to delete dynamic %s entries on port %d, err: %pe\n",
|
|
ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_port_dynamic_entries);
|
|
|
|
/**
|
|
* ntmp_vft_set_entry - add an entry into the VLAN filter table or update
|
|
* the configuration element data of the specified VLAN filter entry
|
|
* @user: target ntmp_user struct
|
|
* @vid: VLAN ID
|
|
* @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
static int ntmp_vft_set_entry(struct ntmp_user *user, u16 vid, int cmd,
|
|
const struct vft_cfge_data *cfge)
|
|
{
|
|
struct netc_swcbd swcbd;
|
|
struct vft_req_ua *req;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE)
|
|
return -EINVAL;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, NTMP_GEN_UA_CFGEU);
|
|
req->ak.exact.vid = cpu_to_le16(vid);
|
|
req->cfge = *cfge;
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID,
|
|
cmd, NTMP_AM_EXACT_KEY);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ntmp_vft_add_entry - add an entry into the VLAN filter table
|
|
* @user: target ntmp_user struct
|
|
* @vid: VLAN ID
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
|
|
const struct vft_cfge_data *cfge)
|
|
{
|
|
int err;
|
|
|
|
err = ntmp_vft_set_entry(user, vid, NTMP_CMD_ADD, cfge);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to add %s entry, vid: %u, err: %pe\n",
|
|
ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
|
|
|
|
/**
|
|
* ntmp_vft_update_entry - update the configuration element data of the
|
|
* specified VLAN filter entry
|
|
* @user: target ntmp_user struct
|
|
* @vid: VLAN ID
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid,
|
|
const struct vft_cfge_data *cfge)
|
|
{
|
|
int err;
|
|
|
|
err = ntmp_vft_set_entry(user, vid, NTMP_CMD_UPDATE, cfge);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to update %s entry, vid: %u, err: %pe\n",
|
|
ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_vft_update_entry);
|
|
|
|
/**
|
|
* ntmp_vft_delete_entry - delete the VLAN filter entry based on the
|
|
* specified VLAN ID
|
|
* @user: target ntmp_user struct
|
|
* @vid: VLAN ID
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid)
|
|
{
|
|
struct netc_swcbd swcbd;
|
|
struct vft_req_qd *req;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
u32 len;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, 0);
|
|
req->ak.exact.vid = cpu_to_le16(vid);
|
|
|
|
/* Request header */
|
|
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID,
|
|
NTMP_CMD_DELETE, NTMP_AM_EXACT_KEY);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to delete %s entry, vid: %u, err: %pe\n",
|
|
ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_vft_delete_entry);
|
|
|
|
/**
|
|
* ntmp_ett_set_entry - add a new entry to the egress treatment table or
|
|
* update the configuration element data of the specified entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: entry ID
|
|
* @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
static int ntmp_ett_set_entry(struct ntmp_user *user, u32 entry_id,
|
|
int cmd, const struct ett_cfge_data *cfge)
|
|
{
|
|
struct netc_swcbd swcbd;
|
|
struct ett_req_ua *req;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err;
|
|
|
|
if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE)
|
|
return -EINVAL;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd_eid(&req->rbe, user->tbl.ett_ver, 0,
|
|
NTMP_GEN_UA_CFGEU, entry_id);
|
|
req->cfge = *cfge;
|
|
|
|
/* Request header */
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
|
NTMP_ETT_ID, cmd, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ntmp_ett_add_entry - add a new entry to the egress treatment table
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: entry ID
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id,
|
|
const struct ett_cfge_data *cfge)
|
|
{
|
|
int err;
|
|
|
|
err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_ADD, cfge);
|
|
if (err)
|
|
dev_err(user->dev, "Failed to add %s entry 0x%x, err: %pe\n",
|
|
ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err));
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ett_add_entry);
|
|
|
|
/**
|
|
* ntmp_ett_update_entry - update the configuration element data of the
|
|
* specified entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: entry ID
|
|
* @cfge: configuration element data
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id,
|
|
const struct ett_cfge_data *cfge)
|
|
{
|
|
int err;
|
|
|
|
err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_UPDATE, cfge);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to update %s entry 0x%x, err: %pe\n",
|
|
ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err));
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ett_update_entry);
|
|
|
|
/**
|
|
* ntmp_ett_delete_entry - delete the specified egress treatment table entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: entry ID
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id)
|
|
{
|
|
return ntmp_delete_entry_by_id(user, NTMP_ETT_ID, user->tbl.ett_ver,
|
|
entry_id, NTMP_EID_REQ_LEN, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ett_delete_entry);
|
|
|
|
/**
|
|
* ntmp_ect_update_entry - reset the statistics element data of the
|
|
* specified egress counter table entry
|
|
* @user: target ntmp_user struct
|
|
* @entry_id: entry ID
|
|
*
|
|
* Return: 0 on success, otherwise a negative error code
|
|
*/
|
|
int ntmp_ect_update_entry(struct ntmp_user *user, u32 entry_id)
|
|
{
|
|
struct ntmp_req_by_eid *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Request data */
|
|
ntmp_fill_crd_eid(req, user->tbl.ect_ver, 0, ECT_UA_STSEU, entry_id);
|
|
|
|
/* Request header */
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
|
NTMP_ECT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to update %s entry 0x%x, err: %pe\n",
|
|
ntmp_table_name(NTMP_ECT_ID), entry_id, ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_ect_update_entry);
|
|
|
|
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
|
|
const struct bpt_cfge_data *cfge)
|
|
{
|
|
struct bpt_req_update *req;
|
|
struct netc_swcbd swcbd;
|
|
struct netc_cbdr *cbdr;
|
|
union netc_cbd cbd;
|
|
int err;
|
|
|
|
swcbd.size = sizeof(*req);
|
|
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Note that BPT_UA_BPSEU is used to update the BPSE_DATA of the entry,
|
|
* which is maintained by the hardware. The BPSE_DATA is not present in
|
|
* the request data for 'Update' operation.
|
|
*/
|
|
ntmp_fill_crd_eid(&req->rbe, user->tbl.bpt_ver, 0,
|
|
NTMP_GEN_UA_CFGEU | BPT_UA_BPSEU, entry_id);
|
|
req->cfge = *cfge;
|
|
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
|
NTMP_BPT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
|
|
|
ntmp_select_and_lock_cbdr(user, &cbdr);
|
|
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
|
if (err)
|
|
dev_err(user->dev,
|
|
"Failed to update %s entry 0x%x, err: %pe\n",
|
|
ntmp_table_name(NTMP_BPT_ID), entry_id, ERR_PTR(err));
|
|
|
|
ntmp_unlock_cbdr(cbdr);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ntmp_bpt_update_entry);
|
|
|
|
MODULE_DESCRIPTION("NXP NETC Library");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|