+struct ecore_db_recovery_entry {
+ osal_list_entry_t list_entry;
+ void OSAL_IOMEM *db_addr;
+ void *db_data;
+ enum ecore_db_rec_width db_width;
+ enum ecore_db_rec_space db_space;
+ u8 hwfn_idx;
+};
+
+/* display a single doorbell recovery entry */
+void ecore_db_recovery_dp_entry(struct ecore_hwfn *p_hwfn,
+ struct ecore_db_recovery_entry *db_entry,
+ const char *action)
+{
+ DP_VERBOSE(p_hwfn, ECORE_MSG_SPQ, "(%s: db_entry %p, addr %p, data %p, width %s, %s space, hwfn %d)\n",
+ action, db_entry, db_entry->db_addr, db_entry->db_data,
+ db_entry->db_width == DB_REC_WIDTH_32B ? "32b" : "64b",
+ db_entry->db_space == DB_REC_USER ? "user" : "kernel",
+ db_entry->hwfn_idx);
+}
+
+/* doorbell address sanity (address within doorbell bar range) */
+bool ecore_db_rec_sanity(struct ecore_dev *p_dev, void OSAL_IOMEM *db_addr,
+ void *db_data)
+{
+ /* make sure doorbell address is within the doorbell bar */
+ if (db_addr < p_dev->doorbells || (u8 *)db_addr >
+ (u8 *)p_dev->doorbells + p_dev->db_size) {
+ OSAL_WARN(true,
+ "Illegal doorbell address: %p. Legal range for doorbell addresses is [%p..%p]\n",
+ db_addr, p_dev->doorbells,
+ (u8 *)p_dev->doorbells + p_dev->db_size);
+ return false;
+ }
+
+ /* make sure doorbell data pointer is not null */
+ if (!db_data) {
+ OSAL_WARN(true, "Illegal doorbell data pointer: %p", db_data);
+ return false;
+ }
+
+ return true;
+}
+
+/* find hwfn according to the doorbell address */
+struct ecore_hwfn *ecore_db_rec_find_hwfn(struct ecore_dev *p_dev,
+ void OSAL_IOMEM *db_addr)
+{
+ struct ecore_hwfn *p_hwfn;
+
+ /* In CMT doorbell bar is split down the middle between engine 0 and
+ * enigne 1
+ */
+ if (ECORE_IS_CMT(p_dev))
+ p_hwfn = db_addr < p_dev->hwfns[1].doorbells ?
+ &p_dev->hwfns[0] : &p_dev->hwfns[1];
+ else
+ p_hwfn = ECORE_LEADING_HWFN(p_dev);
+
+ return p_hwfn;
+}
+
+/* add a new entry to the doorbell recovery mechanism */
+enum _ecore_status_t ecore_db_recovery_add(struct ecore_dev *p_dev,
+ void OSAL_IOMEM *db_addr,
+ void *db_data,
+ enum ecore_db_rec_width db_width,
+ enum ecore_db_rec_space db_space)
+{
+ struct ecore_db_recovery_entry *db_entry;
+ struct ecore_hwfn *p_hwfn;
+
+ /* shortcircuit VFs, for now */
+ if (IS_VF(p_dev)) {
+ DP_VERBOSE(p_dev, ECORE_MSG_IOV, "db recovery - skipping VF doorbell\n");
+ return ECORE_SUCCESS;
+ }
+
+ /* sanitize doorbell address */
+ if (!ecore_db_rec_sanity(p_dev, db_addr, db_data))
+ return ECORE_INVAL;
+
+ /* obtain hwfn from doorbell address */
+ p_hwfn = ecore_db_rec_find_hwfn(p_dev, db_addr);
+
+ /* create entry */
+ db_entry = OSAL_ZALLOC(p_hwfn->p_dev, GFP_KERNEL, sizeof(*db_entry));
+ if (!db_entry) {
+ DP_NOTICE(p_dev, false, "Failed to allocate a db recovery entry\n");
+ return ECORE_NOMEM;
+ }
+
+ /* populate entry */
+ db_entry->db_addr = db_addr;
+ db_entry->db_data = db_data;
+ db_entry->db_width = db_width;
+ db_entry->db_space = db_space;
+ db_entry->hwfn_idx = p_hwfn->my_id;
+
+ /* display */
+ ecore_db_recovery_dp_entry(p_hwfn, db_entry, "Adding");
+
+ /* protect the list */
+ OSAL_SPIN_LOCK(&p_hwfn->db_recovery_info.lock);
+ OSAL_LIST_PUSH_TAIL(&db_entry->list_entry,
+ &p_hwfn->db_recovery_info.list);
+ OSAL_SPIN_UNLOCK(&p_hwfn->db_recovery_info.lock);
+
+ return ECORE_SUCCESS;
+}
+
+/* remove an entry from the doorbell recovery mechanism */
+enum _ecore_status_t ecore_db_recovery_del(struct ecore_dev *p_dev,
+ void OSAL_IOMEM *db_addr,
+ void *db_data)
+{
+ struct ecore_db_recovery_entry *db_entry = OSAL_NULL;
+ enum _ecore_status_t rc = ECORE_INVAL;
+ struct ecore_hwfn *p_hwfn;
+
+ /* shortcircuit VFs, for now */
+ if (IS_VF(p_dev)) {
+ DP_VERBOSE(p_dev, ECORE_MSG_IOV, "db recovery - skipping VF doorbell\n");
+ return ECORE_SUCCESS;
+ }
+
+ /* sanitize doorbell address */
+ if (!ecore_db_rec_sanity(p_dev, db_addr, db_data))
+ return ECORE_INVAL;
+
+ /* obtain hwfn from doorbell address */
+ p_hwfn = ecore_db_rec_find_hwfn(p_dev, db_addr);
+
+ /* protect the list */
+ OSAL_SPIN_LOCK(&p_hwfn->db_recovery_info.lock);
+ OSAL_LIST_FOR_EACH_ENTRY(db_entry,
+ &p_hwfn->db_recovery_info.list,
+ list_entry,
+ struct ecore_db_recovery_entry) {
+ /* search according to db_data addr since db_addr is not unique
+ * (roce)
+ */
+ if (db_entry->db_data == db_data) {
+ ecore_db_recovery_dp_entry(p_hwfn, db_entry,
+ "Deleting");
+ OSAL_LIST_REMOVE_ENTRY(&db_entry->list_entry,
+ &p_hwfn->db_recovery_info.list);
+ rc = ECORE_SUCCESS;
+ break;
+ }
+ }
+
+ OSAL_SPIN_UNLOCK(&p_hwfn->db_recovery_info.lock);
+
+ if (rc == ECORE_INVAL)
+ /*OSAL_WARN(true,*/
+ DP_NOTICE(p_hwfn, false,
+ "Failed to find element in list. Key (db_data addr) was %p. db_addr was %p\n",
+ db_data, db_addr);
+ else
+ OSAL_FREE(p_dev, db_entry);
+
+ return rc;
+}
+
+/* initialize the doorbell recovery mechanism */
+enum _ecore_status_t ecore_db_recovery_setup(struct ecore_hwfn *p_hwfn)
+{
+ DP_VERBOSE(p_hwfn, ECORE_MSG_SPQ, "Setting up db recovery\n");
+
+ /* make sure db_size was set in p_dev */
+ if (!p_hwfn->p_dev->db_size) {
+ DP_ERR(p_hwfn->p_dev, "db_size not set\n");
+ return ECORE_INVAL;
+ }
+
+ OSAL_LIST_INIT(&p_hwfn->db_recovery_info.list);
+#ifdef CONFIG_ECORE_LOCK_ALLOC
+ if (OSAL_SPIN_LOCK_ALLOC(p_hwfn, &p_hwfn->db_recovery_info.lock))
+ return ECORE_NOMEM;
+#endif
+ OSAL_SPIN_LOCK_INIT(&p_hwfn->db_recovery_info.lock);
+ p_hwfn->db_recovery_info.db_recovery_counter = 0;
+
+ return ECORE_SUCCESS;
+}
+
+/* destroy the doorbell recovery mechanism */
+void ecore_db_recovery_teardown(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_db_recovery_entry *db_entry = OSAL_NULL;
+
+ DP_VERBOSE(p_hwfn, ECORE_MSG_SPQ, "Tearing down db recovery\n");
+ if (!OSAL_LIST_IS_EMPTY(&p_hwfn->db_recovery_info.list)) {
+ DP_VERBOSE(p_hwfn, false, "Doorbell Recovery teardown found the doorbell recovery list was not empty (Expected in disorderly driver unload (e.g. recovery) otherwise this probably means some flow forgot to db_recovery_del). Prepare to purge doorbell recovery list...\n");
+ while (!OSAL_LIST_IS_EMPTY(&p_hwfn->db_recovery_info.list)) {
+ db_entry = OSAL_LIST_FIRST_ENTRY(
+ &p_hwfn->db_recovery_info.list,
+ struct ecore_db_recovery_entry,
+ list_entry);
+ ecore_db_recovery_dp_entry(p_hwfn, db_entry, "Purging");
+ OSAL_LIST_REMOVE_ENTRY(&db_entry->list_entry,
+ &p_hwfn->db_recovery_info.list);
+ OSAL_FREE(p_hwfn->p_dev, db_entry);
+ }
+ }
+#ifdef CONFIG_ECORE_LOCK_ALLOC
+ OSAL_SPIN_LOCK_DEALLOC(&p_hwfn->db_recovery_info.lock);
+#endif
+ p_hwfn->db_recovery_info.db_recovery_counter = 0;
+}
+
+/* print the content of the doorbell recovery mechanism */
+void ecore_db_recovery_dp(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_db_recovery_entry *db_entry = OSAL_NULL;
+
+ DP_NOTICE(p_hwfn, false,
+ "Dispalying doorbell recovery database. Counter was %d\n",
+ p_hwfn->db_recovery_info.db_recovery_counter);
+
+ /* protect the list */
+ OSAL_SPIN_LOCK(&p_hwfn->db_recovery_info.lock);
+ OSAL_LIST_FOR_EACH_ENTRY(db_entry,
+ &p_hwfn->db_recovery_info.list,
+ list_entry,
+ struct ecore_db_recovery_entry) {
+ ecore_db_recovery_dp_entry(p_hwfn, db_entry, "Printing");
+ }
+
+ OSAL_SPIN_UNLOCK(&p_hwfn->db_recovery_info.lock);
+}
+
+/* ring the doorbell of a single doorbell recovery entry */
+void ecore_db_recovery_ring(struct ecore_hwfn *p_hwfn,
+ struct ecore_db_recovery_entry *db_entry,
+ enum ecore_db_rec_exec db_exec)
+{
+ /* Print according to width */
+ if (db_entry->db_width == DB_REC_WIDTH_32B)
+ DP_VERBOSE(p_hwfn, ECORE_MSG_SPQ, "%s doorbell address %p data %x\n",
+ db_exec == DB_REC_DRY_RUN ? "would have rung" : "ringing",
+ db_entry->db_addr, *(u32 *)db_entry->db_data);
+ else
+ DP_VERBOSE(p_hwfn, ECORE_MSG_SPQ, "%s doorbell address %p data %lx\n",
+ db_exec == DB_REC_DRY_RUN ? "would have rung" : "ringing",
+ db_entry->db_addr,
+ *(unsigned long *)(db_entry->db_data));
+
+ /* Sanity */
+ if (!ecore_db_rec_sanity(p_hwfn->p_dev, db_entry->db_addr,
+ db_entry->db_data))
+ return;
+
+ /* Flush the write combined buffer. Since there are multiple doorbelling
+ * entities using the same address, if we don't flush, a transaction
+ * could be lost.
+ */
+ OSAL_WMB(p_hwfn->p_dev);
+
+ /* Ring the doorbell */
+ if (db_exec == DB_REC_REAL_DEAL || db_exec == DB_REC_ONCE) {
+ if (db_entry->db_width == DB_REC_WIDTH_32B)
+ DIRECT_REG_WR(p_hwfn, db_entry->db_addr,
+ *(u32 *)(db_entry->db_data));
+ else
+ DIRECT_REG_WR64(p_hwfn, db_entry->db_addr,
+ *(u64 *)(db_entry->db_data));
+ }
+
+ /* Flush the write combined buffer. Next doorbell may come from a
+ * different entity to the same address...
+ */
+ OSAL_WMB(p_hwfn->p_dev);
+}
+
+/* traverse the doorbell recovery entry list and ring all the doorbells */
+void ecore_db_recovery_execute(struct ecore_hwfn *p_hwfn,
+ enum ecore_db_rec_exec db_exec)
+{
+ struct ecore_db_recovery_entry *db_entry = OSAL_NULL;
+
+ if (db_exec != DB_REC_ONCE) {
+ DP_NOTICE(p_hwfn, false, "Executing doorbell recovery. Counter was %d\n",
+ p_hwfn->db_recovery_info.db_recovery_counter);
+
+ /* track amount of times recovery was executed */
+ p_hwfn->db_recovery_info.db_recovery_counter++;
+ }
+
+ /* protect the list */
+ OSAL_SPIN_LOCK(&p_hwfn->db_recovery_info.lock);
+ OSAL_LIST_FOR_EACH_ENTRY(db_entry,
+ &p_hwfn->db_recovery_info.list,
+ list_entry,
+ struct ecore_db_recovery_entry) {
+ ecore_db_recovery_ring(p_hwfn, db_entry, db_exec);
+ if (db_exec == DB_REC_ONCE)
+ break;
+ }
+
+ OSAL_SPIN_UNLOCK(&p_hwfn->db_recovery_info.lock);
+}
+/******************** Doorbell Recovery end ****************/
+
+/********************************** NIG LLH ***********************************/
+
+enum ecore_llh_filter_type {
+ ECORE_LLH_FILTER_TYPE_MAC,
+ ECORE_LLH_FILTER_TYPE_PROTOCOL,
+};
+
+struct ecore_llh_mac_filter {
+ u8 addr[ETH_ALEN];
+};
+
+struct ecore_llh_protocol_filter {
+ enum ecore_llh_prot_filter_type_t type;
+ u16 source_port_or_eth_type;
+ u16 dest_port;
+};
+
+union ecore_llh_filter {
+ struct ecore_llh_mac_filter mac;
+ struct ecore_llh_protocol_filter protocol;
+};
+
+struct ecore_llh_filter_info {
+ bool b_enabled;
+ u32 ref_cnt;
+ enum ecore_llh_filter_type type;
+ union ecore_llh_filter filter;
+};
+
+struct ecore_llh_info {
+ /* Number of LLH filters banks */
+ u8 num_ppfid;
+
+#define MAX_NUM_PPFID 8
+ u8 ppfid_array[MAX_NUM_PPFID];
+
+ /* Array of filters arrays:
+ * "num_ppfid" elements of filters banks, where each is an array of
+ * "NIG_REG_LLH_FUNC_FILTER_EN_SIZE" filters.
+ */
+ struct ecore_llh_filter_info **pp_filters;
+};
+
+static void ecore_llh_free(struct ecore_dev *p_dev)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ u32 i;
+
+ if (p_llh_info != OSAL_NULL) {
+ if (p_llh_info->pp_filters != OSAL_NULL) {
+ for (i = 0; i < p_llh_info->num_ppfid; i++)
+ OSAL_FREE(p_dev, p_llh_info->pp_filters[i]);
+ }
+
+ OSAL_FREE(p_dev, p_llh_info->pp_filters);
+ }
+
+ OSAL_FREE(p_dev, p_llh_info);
+ p_dev->p_llh_info = OSAL_NULL;
+}
+
+static enum _ecore_status_t ecore_llh_alloc(struct ecore_dev *p_dev)
+{
+ struct ecore_llh_info *p_llh_info;
+ u32 size;
+ u8 i;
+
+ p_llh_info = OSAL_ZALLOC(p_dev, GFP_KERNEL, sizeof(*p_llh_info));
+ if (!p_llh_info)
+ return ECORE_NOMEM;
+ p_dev->p_llh_info = p_llh_info;
+
+ for (i = 0; i < MAX_NUM_PPFID; i++) {
+ if (!(p_dev->ppfid_bitmap & (0x1 << i)))
+ continue;
+
+ p_llh_info->ppfid_array[p_llh_info->num_ppfid] = i;
+ DP_VERBOSE(p_dev, ECORE_MSG_SP, "ppfid_array[%d] = %hhd\n",
+ p_llh_info->num_ppfid, i);
+ p_llh_info->num_ppfid++;
+ }
+
+ size = p_llh_info->num_ppfid * sizeof(*p_llh_info->pp_filters);
+ p_llh_info->pp_filters = OSAL_ZALLOC(p_dev, GFP_KERNEL, size);
+ if (!p_llh_info->pp_filters)
+ return ECORE_NOMEM;
+
+ size = NIG_REG_LLH_FUNC_FILTER_EN_SIZE *
+ sizeof(**p_llh_info->pp_filters);
+ for (i = 0; i < p_llh_info->num_ppfid; i++) {
+ p_llh_info->pp_filters[i] = OSAL_ZALLOC(p_dev, GFP_KERNEL,
+ size);
+ if (!p_llh_info->pp_filters[i])
+ return ECORE_NOMEM;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t ecore_llh_shadow_sanity(struct ecore_dev *p_dev,
+ u8 ppfid, u8 filter_idx,
+ const char *action)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+
+ if (ppfid >= p_llh_info->num_ppfid) {
+ DP_NOTICE(p_dev, false,
+ "LLH shadow [%s]: using ppfid %d while only %d ppfids are available\n",
+ action, ppfid, p_llh_info->num_ppfid);
+ return ECORE_INVAL;
+ }
+
+ if (filter_idx >= NIG_REG_LLH_FUNC_FILTER_EN_SIZE) {
+ DP_NOTICE(p_dev, false,
+ "LLH shadow [%s]: using filter_idx %d while only %d filters are available\n",
+ action, filter_idx, NIG_REG_LLH_FUNC_FILTER_EN_SIZE);
+ return ECORE_INVAL;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+#define ECORE_LLH_INVALID_FILTER_IDX 0xff
+
+static enum _ecore_status_t
+ecore_llh_shadow_search_filter(struct ecore_dev *p_dev, u8 ppfid,
+ union ecore_llh_filter *p_filter,
+ u8 *p_filter_idx)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ struct ecore_llh_filter_info *p_filters;
+ enum _ecore_status_t rc;
+ u8 i;
+
+ rc = ecore_llh_shadow_sanity(p_dev, ppfid, 0, "search");
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ *p_filter_idx = ECORE_LLH_INVALID_FILTER_IDX;
+
+ p_filters = p_llh_info->pp_filters[ppfid];
+ for (i = 0; i < NIG_REG_LLH_FUNC_FILTER_EN_SIZE; i++) {
+ if (!OSAL_MEMCMP(p_filter, &p_filters[i].filter,
+ sizeof(*p_filter))) {
+ *p_filter_idx = i;
+ break;
+ }
+ }
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_shadow_get_free_idx(struct ecore_dev *p_dev, u8 ppfid,
+ u8 *p_filter_idx)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ struct ecore_llh_filter_info *p_filters;
+ enum _ecore_status_t rc;
+ u8 i;
+
+ rc = ecore_llh_shadow_sanity(p_dev, ppfid, 0, "get_free_idx");
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ *p_filter_idx = ECORE_LLH_INVALID_FILTER_IDX;
+
+ p_filters = p_llh_info->pp_filters[ppfid];
+ for (i = 0; i < NIG_REG_LLH_FUNC_FILTER_EN_SIZE; i++) {
+ if (!p_filters[i].b_enabled) {
+ *p_filter_idx = i;
+ break;
+ }
+ }
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+__ecore_llh_shadow_add_filter(struct ecore_dev *p_dev, u8 ppfid, u8 filter_idx,
+ enum ecore_llh_filter_type type,
+ union ecore_llh_filter *p_filter, u32 *p_ref_cnt)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ struct ecore_llh_filter_info *p_filters;
+ enum _ecore_status_t rc;
+
+ rc = ecore_llh_shadow_sanity(p_dev, ppfid, filter_idx, "add");
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ p_filters = p_llh_info->pp_filters[ppfid];
+ if (!p_filters[filter_idx].ref_cnt) {
+ p_filters[filter_idx].b_enabled = true;
+ p_filters[filter_idx].type = type;
+ OSAL_MEMCPY(&p_filters[filter_idx].filter, p_filter,
+ sizeof(p_filters[filter_idx].filter));
+ }
+
+ *p_ref_cnt = ++p_filters[filter_idx].ref_cnt;
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_shadow_add_filter(struct ecore_dev *p_dev, u8 ppfid,
+ enum ecore_llh_filter_type type,
+ union ecore_llh_filter *p_filter,
+ u8 *p_filter_idx, u32 *p_ref_cnt)
+{
+ enum _ecore_status_t rc;
+
+ /* Check if the same filter already exist */
+ rc = ecore_llh_shadow_search_filter(p_dev, ppfid, p_filter,
+ p_filter_idx);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ /* Find a new entry in case of a new filter */
+ if (*p_filter_idx == ECORE_LLH_INVALID_FILTER_IDX) {
+ rc = ecore_llh_shadow_get_free_idx(p_dev, ppfid, p_filter_idx);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+ }
+
+ /* No free entry was found */
+ if (*p_filter_idx == ECORE_LLH_INVALID_FILTER_IDX) {
+ DP_NOTICE(p_dev, false,
+ "Failed to find an empty LLH filter to utilize [ppfid %d]\n",
+ ppfid);
+ return ECORE_NORESOURCES;
+ }
+
+ return __ecore_llh_shadow_add_filter(p_dev, ppfid, *p_filter_idx, type,
+ p_filter, p_ref_cnt);
+}
+
+static enum _ecore_status_t
+__ecore_llh_shadow_remove_filter(struct ecore_dev *p_dev, u8 ppfid,
+ u8 filter_idx, u32 *p_ref_cnt)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ struct ecore_llh_filter_info *p_filters;
+ enum _ecore_status_t rc;
+
+ rc = ecore_llh_shadow_sanity(p_dev, ppfid, filter_idx, "remove");
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ p_filters = p_llh_info->pp_filters[ppfid];
+ if (!p_filters[filter_idx].ref_cnt) {
+ DP_NOTICE(p_dev, false,
+ "LLH shadow: trying to remove a filter with ref_cnt=0\n");
+ return ECORE_INVAL;
+ }
+
+ *p_ref_cnt = --p_filters[filter_idx].ref_cnt;
+ if (!p_filters[filter_idx].ref_cnt)
+ OSAL_MEM_ZERO(&p_filters[filter_idx],
+ sizeof(p_filters[filter_idx]));
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_shadow_remove_filter(struct ecore_dev *p_dev, u8 ppfid,
+ union ecore_llh_filter *p_filter,
+ u8 *p_filter_idx, u32 *p_ref_cnt)
+{
+ enum _ecore_status_t rc;
+
+ rc = ecore_llh_shadow_search_filter(p_dev, ppfid, p_filter,
+ p_filter_idx);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ /* No matching filter was found */
+ if (*p_filter_idx == ECORE_LLH_INVALID_FILTER_IDX) {
+ DP_NOTICE(p_dev, false,
+ "Failed to find a filter in the LLH shadow\n");
+ return ECORE_INVAL;
+ }
+
+ return __ecore_llh_shadow_remove_filter(p_dev, ppfid, *p_filter_idx,
+ p_ref_cnt);
+}
+
+static enum _ecore_status_t
+ecore_llh_shadow_remove_all_filters(struct ecore_dev *p_dev, u8 ppfid)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ struct ecore_llh_filter_info *p_filters;
+ enum _ecore_status_t rc;
+
+ rc = ecore_llh_shadow_sanity(p_dev, ppfid, 0, "remove_all");
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ p_filters = p_llh_info->pp_filters[ppfid];
+ OSAL_MEM_ZERO(p_filters,
+ NIG_REG_LLH_FUNC_FILTER_EN_SIZE * sizeof(*p_filters));
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t ecore_abs_ppfid(struct ecore_dev *p_dev,
+ u8 rel_ppfid, u8 *p_abs_ppfid)
+{
+ struct ecore_llh_info *p_llh_info = p_dev->p_llh_info;
+ u8 ppfids = p_llh_info->num_ppfid - 1;
+
+ if (rel_ppfid >= p_llh_info->num_ppfid) {
+ DP_NOTICE(p_dev, false,
+ "rel_ppfid %d is not valid, available indices are 0..%hhd\n",
+ rel_ppfid, ppfids);
+ return ECORE_INVAL;
+ }
+
+ *p_abs_ppfid = p_llh_info->ppfid_array[rel_ppfid];
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+__ecore_llh_set_engine_affin(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt)
+{
+ struct ecore_dev *p_dev = p_hwfn->p_dev;
+ enum ecore_eng eng;
+ u8 ppfid;
+ enum _ecore_status_t rc;
+
+ rc = ecore_mcp_get_engine_config(p_hwfn, p_ptt);
+ if (rc != ECORE_SUCCESS && rc != ECORE_NOTIMPL) {
+ DP_NOTICE(p_hwfn, false,
+ "Failed to get the engine affinity configuration\n");
+ return rc;
+ }
+
+ /* RoCE PF is bound to a single engine */
+ if (ECORE_IS_ROCE_PERSONALITY(p_hwfn)) {
+ eng = p_dev->fir_affin ? ECORE_ENG1 : ECORE_ENG0;
+ rc = ecore_llh_set_roce_affinity(p_dev, eng);
+ if (rc != ECORE_SUCCESS) {
+ DP_NOTICE(p_dev, false,
+ "Failed to set the RoCE engine affinity\n");
+ return rc;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Set the engine affinity of RoCE packets as %d\n",
+ eng);
+ }
+
+ /* Storage PF is bound to a single engine while L2 PF uses both */
+ if (ECORE_IS_FCOE_PERSONALITY(p_hwfn) ||
+ ECORE_IS_ISCSI_PERSONALITY(p_hwfn))
+ eng = p_dev->fir_affin ? ECORE_ENG1 : ECORE_ENG0;
+ else /* L2_PERSONALITY */
+ eng = ECORE_BOTH_ENG;
+
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++) {
+ rc = ecore_llh_set_ppfid_affinity(p_dev, ppfid, eng);
+ if (rc != ECORE_SUCCESS) {
+ DP_NOTICE(p_dev, false,
+ "Failed to set the engine affinity of ppfid %d\n",
+ ppfid);
+ return rc;
+ }
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Set the engine affinity of non-RoCE packets as %d\n",
+ eng);
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_set_engine_affin(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt,
+ bool avoid_eng_affin)
+{
+ struct ecore_dev *p_dev = p_hwfn->p_dev;
+ enum _ecore_status_t rc;
+
+ /* Backwards compatible mode:
+ * - RoCE packets - Use engine 0.
+ * - Non-RoCE packets - Use connection based classification for L2 PFs,
+ * and engine 0 otherwise.
+ */
+ if (avoid_eng_affin) {
+ enum ecore_eng eng;
+ u8 ppfid;
+
+ if (ECORE_IS_ROCE_PERSONALITY(p_hwfn)) {
+ eng = ECORE_ENG0;
+ rc = ecore_llh_set_roce_affinity(p_dev, eng);
+ if (rc != ECORE_SUCCESS) {
+ DP_NOTICE(p_dev, false,
+ "Failed to set the RoCE engine affinity\n");
+ return rc;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH [backwards compatible mode]: Set the engine affinity of RoCE packets as %d\n",
+ eng);
+ }
+
+ eng = (ECORE_IS_FCOE_PERSONALITY(p_hwfn) ||
+ ECORE_IS_ISCSI_PERSONALITY(p_hwfn)) ? ECORE_ENG0
+ : ECORE_BOTH_ENG;
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++) {
+ rc = ecore_llh_set_ppfid_affinity(p_dev, ppfid, eng);
+ if (rc != ECORE_SUCCESS) {
+ DP_NOTICE(p_dev, false,
+ "Failed to set the engine affinity of ppfid %d\n",
+ ppfid);
+ return rc;
+ }
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH [backwards compatible mode]: Set the engine affinity of non-RoCE packets as %d\n",
+ eng);
+
+ return ECORE_SUCCESS;
+ }
+
+ return __ecore_llh_set_engine_affin(p_hwfn, p_ptt);
+}
+
+static enum _ecore_status_t ecore_llh_hw_init_pf(struct ecore_hwfn *p_hwfn,
+ struct ecore_ptt *p_ptt,
+ bool avoid_eng_affin)
+{
+ struct ecore_dev *p_dev = p_hwfn->p_dev;
+ u8 ppfid, abs_ppfid;
+ enum _ecore_status_t rc;
+
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++) {
+ u32 addr;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ addr = NIG_REG_LLH_PPFID2PFID_TBL_0 + abs_ppfid * 0x4;
+ ecore_wr(p_hwfn, p_ptt, addr, p_hwfn->rel_pf_id);
+ }
+
+ if (OSAL_TEST_BIT(ECORE_MF_LLH_MAC_CLSS, &p_dev->mf_bits) &&
+ !ECORE_IS_FCOE_PERSONALITY(p_hwfn)) {
+ rc = ecore_llh_add_mac_filter(p_dev, 0,
+ p_hwfn->hw_info.hw_mac_addr);
+ if (rc != ECORE_SUCCESS)
+ DP_NOTICE(p_dev, false,
+ "Failed to add an LLH filter with the primary MAC\n");
+ }
+
+ if (ECORE_IS_CMT(p_dev)) {
+ rc = ecore_llh_set_engine_affin(p_hwfn, p_ptt, avoid_eng_affin);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+u8 ecore_llh_get_num_ppfid(struct ecore_dev *p_dev)
+{
+ return p_dev->p_llh_info->num_ppfid;
+}
+
+enum ecore_eng ecore_llh_get_l2_affinity_hint(struct ecore_dev *p_dev)
+{
+ return p_dev->l2_affin_hint ? ECORE_ENG1 : ECORE_ENG0;
+}
+
+/* TBD - should be removed when these definitions are available in reg_addr.h */
+#define NIG_REG_PPF_TO_ENGINE_SEL_ROCE_MASK 0x3
+#define NIG_REG_PPF_TO_ENGINE_SEL_ROCE_SHIFT 0
+#define NIG_REG_PPF_TO_ENGINE_SEL_NON_ROCE_MASK 0x3
+#define NIG_REG_PPF_TO_ENGINE_SEL_NON_ROCE_SHIFT 2
+
+enum _ecore_status_t ecore_llh_set_ppfid_affinity(struct ecore_dev *p_dev,
+ u8 ppfid, enum ecore_eng eng)
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ u32 addr, val, eng_sel;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+ u8 abs_ppfid;
+
+ if (p_ptt == OSAL_NULL)
+ return ECORE_AGAIN;
+
+ if (!ECORE_IS_CMT(p_dev))
+ goto out;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ switch (eng) {
+ case ECORE_ENG0:
+ eng_sel = 0;
+ break;
+ case ECORE_ENG1:
+ eng_sel = 1;
+ break;
+ case ECORE_BOTH_ENG:
+ eng_sel = 2;
+ break;
+ default:
+ DP_NOTICE(p_dev, false,
+ "Invalid affinity value for ppfid [%d]\n", eng);
+ rc = ECORE_INVAL;
+ goto out;
+ }
+
+ addr = NIG_REG_PPF_TO_ENGINE_SEL + abs_ppfid * 0x4;
+ val = ecore_rd(p_hwfn, p_ptt, addr);
+ SET_FIELD(val, NIG_REG_PPF_TO_ENGINE_SEL_NON_ROCE, eng_sel);
+ ecore_wr(p_hwfn, p_ptt, addr, val);
+
+ /* The iWARP affinity is set as the affinity of ppfid 0 */
+ if (!ppfid && ECORE_IS_IWARP_PERSONALITY(p_hwfn))
+ p_dev->iwarp_affin = (eng == ECORE_ENG1) ? 1 : 0;
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+enum _ecore_status_t ecore_llh_set_roce_affinity(struct ecore_dev *p_dev,
+ enum ecore_eng eng)
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ u32 addr, val, eng_sel;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+ u8 ppfid, abs_ppfid;
+
+ if (p_ptt == OSAL_NULL)
+ return ECORE_AGAIN;
+
+ if (!ECORE_IS_CMT(p_dev))
+ goto out;
+
+ switch (eng) {
+ case ECORE_ENG0:
+ eng_sel = 0;
+ break;
+ case ECORE_ENG1:
+ eng_sel = 1;
+ break;
+ case ECORE_BOTH_ENG:
+ eng_sel = 2;
+ ecore_wr(p_hwfn, p_ptt, NIG_REG_LLH_ENG_CLS_ROCE_QP_SEL,
+ 0xf /* QP bit 15 */);
+ break;
+ default:
+ DP_NOTICE(p_dev, false,
+ "Invalid affinity value for RoCE [%d]\n", eng);
+ rc = ECORE_INVAL;
+ goto out;
+ }
+
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++) {
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ addr = NIG_REG_PPF_TO_ENGINE_SEL + abs_ppfid * 0x4;
+ val = ecore_rd(p_hwfn, p_ptt, addr);
+ SET_FIELD(val, NIG_REG_PPF_TO_ENGINE_SEL_ROCE, eng_sel);
+ ecore_wr(p_hwfn, p_ptt, addr, val);
+ }
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+struct ecore_llh_filter_details {
+ u64 value;
+ u32 mode;
+ u32 protocol_type;
+ u32 hdr_sel;
+ u32 enable;
+};
+
+static enum _ecore_status_t
+ecore_llh_access_filter(struct ecore_hwfn *p_hwfn,
+ struct ecore_ptt *p_ptt, u8 abs_ppfid, u8 filter_idx,
+ struct ecore_llh_filter_details *p_details,
+ bool b_write_access)
+{
+ u8 pfid = ECORE_PFID_BY_PPFID(p_hwfn, abs_ppfid);
+ struct dmae_params params;
+ enum _ecore_status_t rc;
+ u32 addr;
+
+ /* The NIG/LLH registers that are accessed in this function have only 16
+ * rows which are exposed to a PF. I.e. only the 16 filters of its
+ * default ppfid
+ * Accessing filters of other ppfids requires pretending to other PFs,
+ * and thus the usage of the ecore_ppfid_rd/wr() functions.
+ */
+
+ /* Filter enable - should be done first when removing a filter */
+ if (b_write_access && !p_details->enable) {
+ addr = NIG_REG_LLH_FUNC_FILTER_EN + filter_idx * 0x4;
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr,
+ p_details->enable);
+ }
+
+ /* Filter value */
+ addr = NIG_REG_LLH_FUNC_FILTER_VALUE + 2 * filter_idx * 0x4;
+ OSAL_MEMSET(¶ms, 0, sizeof(params));
+
+ if (b_write_access) {
+ SET_FIELD(params.flags, DMAE_PARAMS_DST_PF_VALID, 0x1);
+ params.dst_pf_id = pfid;
+ rc = ecore_dmae_host2grc(p_hwfn, p_ptt,
+ (u64)(osal_uintptr_t)&p_details->value,
+ addr, 2 /* size_in_dwords */, ¶ms);
+ } else {
+ SET_FIELD(params.flags, DMAE_PARAMS_SRC_PF_VALID, 0x1);
+ SET_FIELD(params.flags, DMAE_PARAMS_COMPLETION_DST, 0x1);
+ params.src_pf_id = pfid;
+ rc = ecore_dmae_grc2host(p_hwfn, p_ptt, addr,
+ (u64)(osal_uintptr_t)&p_details->value,
+ 2 /* size_in_dwords */, ¶ms);
+ }
+
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ /* Filter mode */
+ addr = NIG_REG_LLH_FUNC_FILTER_MODE + filter_idx * 0x4;
+ if (b_write_access)
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr, p_details->mode);
+ else
+ p_details->mode = ecore_ppfid_rd(p_hwfn, p_ptt, abs_ppfid,
+ addr);
+
+ /* Filter protocol type */
+ addr = NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE + filter_idx * 0x4;
+ if (b_write_access)
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr,
+ p_details->protocol_type);
+ else
+ p_details->protocol_type = ecore_ppfid_rd(p_hwfn, p_ptt,
+ abs_ppfid, addr);
+
+ /* Filter header select */
+ addr = NIG_REG_LLH_FUNC_FILTER_HDR_SEL + filter_idx * 0x4;
+ if (b_write_access)
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr,
+ p_details->hdr_sel);
+ else
+ p_details->hdr_sel = ecore_ppfid_rd(p_hwfn, p_ptt, abs_ppfid,
+ addr);
+
+ /* Filter enable - should be done last when adding a filter */
+ if (!b_write_access || p_details->enable) {
+ addr = NIG_REG_LLH_FUNC_FILTER_EN + filter_idx * 0x4;
+ if (b_write_access)
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr,
+ p_details->enable);
+ else
+ p_details->enable = ecore_ppfid_rd(p_hwfn, p_ptt,
+ abs_ppfid, addr);
+ }
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_add_filter(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt,
+ u8 abs_ppfid, u8 filter_idx, u8 filter_prot_type,
+ u32 high, u32 low)
+{
+ struct ecore_llh_filter_details filter_details;
+
+ filter_details.enable = 1;
+ filter_details.value = ((u64)high << 32) | low;
+ filter_details.hdr_sel =
+ OSAL_TEST_BIT(ECORE_MF_OVLAN_CLSS, &p_hwfn->p_dev->mf_bits) ?
+ 1 : /* inner/encapsulated header */
+ 0; /* outer/tunnel header */
+ filter_details.protocol_type = filter_prot_type;
+ filter_details.mode = filter_prot_type ?
+ 1 : /* protocol-based classification */
+ 0; /* MAC-address based classification */
+
+ return ecore_llh_access_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
+ &filter_details,
+ true /* write access */);
+}
+
+static enum _ecore_status_t
+ecore_llh_remove_filter(struct ecore_hwfn *p_hwfn,
+ struct ecore_ptt *p_ptt, u8 abs_ppfid, u8 filter_idx)
+{
+ struct ecore_llh_filter_details filter_details;
+
+ OSAL_MEMSET(&filter_details, 0, sizeof(filter_details));
+
+ return ecore_llh_access_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
+ &filter_details,
+ true /* write access */);
+}
+
+enum _ecore_status_t ecore_llh_add_mac_filter(struct ecore_dev *p_dev, u8 ppfid,
+ u8 mac_addr[ETH_ALEN])
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ union ecore_llh_filter filter;
+ u8 filter_idx, abs_ppfid;
+ u32 high, low, ref_cnt;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+
+ if (p_ptt == OSAL_NULL)
+ return ECORE_AGAIN;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_MAC_CLSS, &p_dev->mf_bits))
+ goto out;
+
+ OSAL_MEM_ZERO(&filter, sizeof(filter));
+ OSAL_MEMCPY(filter.mac.addr, mac_addr, ETH_ALEN);
+ rc = ecore_llh_shadow_add_filter(p_dev, ppfid,
+ ECORE_LLH_FILTER_TYPE_MAC,
+ &filter, &filter_idx, &ref_cnt);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ /* Configure the LLH only in case of a new the filter */
+ if (ref_cnt == 1) {
+ high = mac_addr[1] | (mac_addr[0] << 8);
+ low = mac_addr[5] | (mac_addr[4] << 8) | (mac_addr[3] << 16) |
+ (mac_addr[2] << 24);
+ rc = ecore_llh_add_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
+ 0, high, low);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Added MAC filter [%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx] to ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5], ppfid, abs_ppfid, filter_idx,
+ ref_cnt);
+
+ goto out;
+
+err:
+ DP_NOTICE(p_dev, false,
+ "LLH: Failed to add MAC filter [%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx] to ppfid %hhd\n",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5], ppfid);
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+static enum _ecore_status_t
+ecore_llh_protocol_filter_stringify(struct ecore_dev *p_dev,
+ enum ecore_llh_prot_filter_type_t type,
+ u16 source_port_or_eth_type, u16 dest_port,
+ char *str, osal_size_t str_len)
+{
+ switch (type) {
+ case ECORE_LLH_FILTER_ETHERTYPE:
+ OSAL_SNPRINTF(str, str_len, "Ethertype 0x%04x",
+ source_port_or_eth_type);
+ break;
+ case ECORE_LLH_FILTER_TCP_SRC_PORT:
+ OSAL_SNPRINTF(str, str_len, "TCP src port 0x%04x",
+ source_port_or_eth_type);
+ break;
+ case ECORE_LLH_FILTER_UDP_SRC_PORT:
+ OSAL_SNPRINTF(str, str_len, "UDP src port 0x%04x",
+ source_port_or_eth_type);
+ break;
+ case ECORE_LLH_FILTER_TCP_DEST_PORT:
+ OSAL_SNPRINTF(str, str_len, "TCP dst port 0x%04x", dest_port);
+ break;
+ case ECORE_LLH_FILTER_UDP_DEST_PORT:
+ OSAL_SNPRINTF(str, str_len, "UDP dst port 0x%04x", dest_port);
+ break;
+ case ECORE_LLH_FILTER_TCP_SRC_AND_DEST_PORT:
+ OSAL_SNPRINTF(str, str_len, "TCP src/dst ports 0x%04x/0x%04x",
+ source_port_or_eth_type, dest_port);
+ break;
+ case ECORE_LLH_FILTER_UDP_SRC_AND_DEST_PORT:
+ OSAL_SNPRINTF(str, str_len, "UDP src/dst ports 0x%04x/0x%04x",
+ source_port_or_eth_type, dest_port);
+ break;
+ default:
+ DP_NOTICE(p_dev, true,
+ "Non valid LLH protocol filter type %d\n", type);
+ return ECORE_INVAL;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+static enum _ecore_status_t
+ecore_llh_protocol_filter_to_hilo(struct ecore_dev *p_dev,
+ enum ecore_llh_prot_filter_type_t type,
+ u16 source_port_or_eth_type, u16 dest_port,
+ u32 *p_high, u32 *p_low)
+{
+ *p_high = 0;
+ *p_low = 0;
+
+ switch (type) {
+ case ECORE_LLH_FILTER_ETHERTYPE:
+ *p_high = source_port_or_eth_type;
+ break;
+ case ECORE_LLH_FILTER_TCP_SRC_PORT:
+ case ECORE_LLH_FILTER_UDP_SRC_PORT:
+ *p_low = source_port_or_eth_type << 16;
+ break;
+ case ECORE_LLH_FILTER_TCP_DEST_PORT:
+ case ECORE_LLH_FILTER_UDP_DEST_PORT:
+ *p_low = dest_port;
+ break;
+ case ECORE_LLH_FILTER_TCP_SRC_AND_DEST_PORT:
+ case ECORE_LLH_FILTER_UDP_SRC_AND_DEST_PORT:
+ *p_low = (source_port_or_eth_type << 16) | dest_port;
+ break;
+ default:
+ DP_NOTICE(p_dev, true,
+ "Non valid LLH protocol filter type %d\n", type);
+ return ECORE_INVAL;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+enum _ecore_status_t
+ecore_llh_add_protocol_filter(struct ecore_dev *p_dev, u8 ppfid,
+ enum ecore_llh_prot_filter_type_t type,
+ u16 source_port_or_eth_type, u16 dest_port)
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ u8 filter_idx, abs_ppfid, type_bitmap;
+ char str[32];
+ union ecore_llh_filter filter;
+ u32 high, low, ref_cnt;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+
+ if (p_ptt == OSAL_NULL)
+ return ECORE_AGAIN;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_PROTO_CLSS, &p_dev->mf_bits))
+ goto out;
+
+ rc = ecore_llh_protocol_filter_stringify(p_dev, type,
+ source_port_or_eth_type,
+ dest_port, str, sizeof(str));
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ OSAL_MEM_ZERO(&filter, sizeof(filter));
+ filter.protocol.type = type;
+ filter.protocol.source_port_or_eth_type = source_port_or_eth_type;
+ filter.protocol.dest_port = dest_port;
+ rc = ecore_llh_shadow_add_filter(p_dev, ppfid,
+ ECORE_LLH_FILTER_TYPE_PROTOCOL,
+ &filter, &filter_idx, &ref_cnt);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ /* Configure the LLH only in case of a new the filter */
+ if (ref_cnt == 1) {
+ rc = ecore_llh_protocol_filter_to_hilo(p_dev, type,
+ source_port_or_eth_type,
+ dest_port, &high, &low);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ type_bitmap = 0x1 << type;
+ rc = ecore_llh_add_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
+ type_bitmap, high, low);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Added protocol filter [%s] to ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
+ str, ppfid, abs_ppfid, filter_idx, ref_cnt);
+
+ goto out;
+
+err:
+ DP_NOTICE(p_hwfn, false,
+ "LLH: Failed to add protocol filter [%s] to ppfid %hhd\n",
+ str, ppfid);
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+void ecore_llh_remove_mac_filter(struct ecore_dev *p_dev, u8 ppfid,
+ u8 mac_addr[ETH_ALEN])
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ union ecore_llh_filter filter;
+ u8 filter_idx, abs_ppfid;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+ u32 ref_cnt;
+
+ if (p_ptt == OSAL_NULL)
+ return;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_MAC_CLSS, &p_dev->mf_bits))
+ goto out;
+
+ OSAL_MEM_ZERO(&filter, sizeof(filter));
+ OSAL_MEMCPY(filter.mac.addr, mac_addr, ETH_ALEN);
+ rc = ecore_llh_shadow_remove_filter(p_dev, ppfid, &filter, &filter_idx,
+ &ref_cnt);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ /* Remove from the LLH in case the filter is not in use */
+ if (!ref_cnt) {
+ rc = ecore_llh_remove_filter(p_hwfn, p_ptt, abs_ppfid,
+ filter_idx);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Removed MAC filter [%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx] from ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5], ppfid, abs_ppfid, filter_idx,
+ ref_cnt);
+
+ goto out;
+
+err:
+ DP_NOTICE(p_dev, false,
+ "LLH: Failed to remove MAC filter [%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx] from ppfid %hhd\n",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5], ppfid);
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+}
+
+void ecore_llh_remove_protocol_filter(struct ecore_dev *p_dev, u8 ppfid,
+ enum ecore_llh_prot_filter_type_t type,
+ u16 source_port_or_eth_type,
+ u16 dest_port)
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ u8 filter_idx, abs_ppfid;
+ char str[32];
+ union ecore_llh_filter filter;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+ u32 ref_cnt;
+
+ if (p_ptt == OSAL_NULL)
+ return;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_PROTO_CLSS, &p_dev->mf_bits))
+ goto out;
+
+ rc = ecore_llh_protocol_filter_stringify(p_dev, type,
+ source_port_or_eth_type,
+ dest_port, str, sizeof(str));
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ OSAL_MEM_ZERO(&filter, sizeof(filter));
+ filter.protocol.type = type;
+ filter.protocol.source_port_or_eth_type = source_port_or_eth_type;
+ filter.protocol.dest_port = dest_port;
+ rc = ecore_llh_shadow_remove_filter(p_dev, ppfid, &filter, &filter_idx,
+ &ref_cnt);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+
+ /* Remove from the LLH in case the filter is not in use */
+ if (!ref_cnt) {
+ rc = ecore_llh_remove_filter(p_hwfn, p_ptt, abs_ppfid,
+ filter_idx);
+ if (rc != ECORE_SUCCESS)
+ goto err;
+ }
+
+ DP_VERBOSE(p_dev, ECORE_MSG_SP,
+ "LLH: Removed protocol filter [%s] from ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
+ str, ppfid, abs_ppfid, filter_idx, ref_cnt);
+
+ goto out;
+
+err:
+ DP_NOTICE(p_dev, false,
+ "LLH: Failed to remove protocol filter [%s] from ppfid %hhd\n",
+ str, ppfid);
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+}