#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
#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_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
#define NTMP_GEN_UA_CFGEU BIT(0)
#define NTMP_GEN_UA_STSEU BIT(1)
#define FDBT_UA_ACTEU BIT(1)
#define ECT_UA_STSEU BIT(0)
#define BPT_UA_BPSEU BIT(1)
#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_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;
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;
netc_write(cbdr->regs.bar0, lower_32_bits(cbdr->dma_base_align));
netc_write(cbdr->regs.bar1, upper_32_bits(cbdr->dma_base_align));
netc_write(cbdr->regs.lenr, cbdr->bd_num);
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)
{
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;
}
*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();
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();
*cbd = *cur_cbd;
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);
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;
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;
}
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;
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)
return -EINVAL;
swcbd.size = struct_size(req, groups, count);
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
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)
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;
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);
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;
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);
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);
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;
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));
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);
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;
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;
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);
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);
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;
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));
req->ak.search.cfge_mc = FDBT_CFGE_MC_PORT_BITMAP;
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);
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;
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;
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_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);
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;
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;
req->ak.search.acte_mc = FDBT_ACTE_MC;
req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC;
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);
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;
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);
req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP;
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);
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;
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;
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;
}
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);
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);
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;
ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, 0);
req->ak.exact.vid = cpu_to_le16(vid);
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);
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;
ntmp_fill_crd_eid(&req->rbe, user->tbl.ett_ver, 0,
NTMP_GEN_UA_CFGEU, entry_id);
req->cfge = *cfge;
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;
}
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);
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);
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);
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;
ntmp_fill_crd_eid(req, user->tbl.ect_ver, 0, ECT_UA_STSEU, entry_id);
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;
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");