| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Wei Fang | 5874 | 100.00% | 14 | 100.00% |
| Total | 5874 | 14 |
// 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1