+ *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);
+}
+
+void ecore_llh_clear_ppfid_filters(struct ecore_dev *p_dev, u8 ppfid)
+{
+ 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;
+ enum _ecore_status_t rc = ECORE_SUCCESS;
+
+ if (p_ptt == OSAL_NULL)
+ return;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_PROTO_CLSS, &p_dev->mf_bits) &&
+ !OSAL_TEST_BIT(ECORE_MF_LLH_MAC_CLSS, &p_dev->mf_bits))
+ goto out;
+
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ rc = ecore_llh_shadow_remove_all_filters(p_dev, ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ for (filter_idx = 0; filter_idx < NIG_REG_LLH_FUNC_FILTER_EN_SIZE;
+ filter_idx++) {
+ rc = ecore_llh_remove_filter(p_hwfn, p_ptt,
+ abs_ppfid, filter_idx);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+ }
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+}
+
+void ecore_llh_clear_all_filters(struct ecore_dev *p_dev)
+{
+ u8 ppfid;
+
+ if (!OSAL_TEST_BIT(ECORE_MF_LLH_PROTO_CLSS, &p_dev->mf_bits) &&
+ !OSAL_TEST_BIT(ECORE_MF_LLH_MAC_CLSS, &p_dev->mf_bits))
+ return;
+
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++)
+ ecore_llh_clear_ppfid_filters(p_dev, ppfid);
+}
+
+enum _ecore_status_t ecore_all_ppfids_wr(struct ecore_hwfn *p_hwfn,
+ struct ecore_ptt *p_ptt, u32 addr,
+ u32 val)
+{
+ 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++) {
+ rc = ecore_abs_ppfid(p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+
+ ecore_ppfid_wr(p_hwfn, p_ptt, abs_ppfid, addr, val);
+ }
+
+ return ECORE_SUCCESS;
+}
+
+enum _ecore_status_t
+ecore_llh_dump_ppfid(struct ecore_dev *p_dev, u8 ppfid)
+{
+ struct ecore_hwfn *p_hwfn = ECORE_LEADING_HWFN(p_dev);
+ struct ecore_ptt *p_ptt = ecore_ptt_acquire(p_hwfn);
+ struct ecore_llh_filter_details filter_details;
+ u8 abs_ppfid, filter_idx;
+ u32 addr;
+ enum _ecore_status_t rc;
+
+ if (!p_ptt)
+ return ECORE_AGAIN;
+
+ rc = ecore_abs_ppfid(p_hwfn->p_dev, ppfid, &abs_ppfid);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ addr = NIG_REG_PPF_TO_ENGINE_SEL + abs_ppfid * 0x4;
+ DP_NOTICE(p_hwfn, false,
+ "[rel_pf_id %hhd, ppfid={rel %hhd, abs %hhd}, engine_sel 0x%x]\n",
+ p_hwfn->rel_pf_id, ppfid, abs_ppfid,
+ ecore_rd(p_hwfn, p_ptt, addr));
+
+ for (filter_idx = 0; filter_idx < NIG_REG_LLH_FUNC_FILTER_EN_SIZE;
+ filter_idx++) {
+ OSAL_MEMSET(&filter_details, 0, sizeof(filter_details));
+ rc = ecore_llh_access_filter(p_hwfn, p_ptt, abs_ppfid,
+ filter_idx, &filter_details,
+ false /* read access */);
+ if (rc != ECORE_SUCCESS)
+ goto out;
+
+ DP_NOTICE(p_hwfn, false,
+ "filter %2hhd: enable %d, value 0x%016lx, mode %d, protocol_type 0x%x, hdr_sel 0x%x\n",
+ filter_idx, filter_details.enable,
+ (unsigned long)filter_details.value,
+ filter_details.mode,
+ filter_details.protocol_type, filter_details.hdr_sel);
+ }
+
+
+out:
+ ecore_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+enum _ecore_status_t ecore_llh_dump_all(struct ecore_dev *p_dev)
+{
+ u8 ppfid;
+ enum _ecore_status_t rc;
+
+ for (ppfid = 0; ppfid < p_dev->p_llh_info->num_ppfid; ppfid++) {
+ rc = ecore_llh_dump_ppfid(p_dev, ppfid);
+ if (rc != ECORE_SUCCESS)
+ return rc;
+ }
+
+ return ECORE_SUCCESS;
+}
+
+/******************************* NIG LLH - End ********************************/
+
+/* Configurable */
+#define ECORE_MIN_DPIS (4) /* The minimal num of DPIs required to
+ * load the driver. The number was
+ * arbitrarily set.
+ */
+
+/* Derived */
+#define ECORE_MIN_PWM_REGION (ECORE_WID_SIZE * ECORE_MIN_DPIS)
+
+static u32 ecore_hw_bar_size(struct ecore_hwfn *p_hwfn,
+ struct ecore_ptt *p_ptt,
+ enum BAR_ID bar_id)
+{
+ u32 bar_reg = (bar_id == BAR_ID_0 ?
+ PGLUE_B_REG_PF_BAR0_SIZE : PGLUE_B_REG_PF_BAR1_SIZE);
+ u32 val;
+
+ if (IS_VF(p_hwfn->p_dev))
+ return ecore_vf_hw_bar_size(p_hwfn, bar_id);
+
+ val = ecore_rd(p_hwfn, p_ptt, bar_reg);
+ if (val)
+ return 1 << (val + 15);
+
+ /* The above registers were updated in the past only in CMT mode. Since
+ * they were found to be useful MFW started updating them from 8.7.7.0.
+ * In older MFW versions they are set to 0 which means disabled.
+ */
+ if (ECORE_IS_CMT(p_hwfn->p_dev)) {
+ DP_INFO(p_hwfn,
+ "BAR size not configured. Assuming BAR size of 256kB for GRC and 512kB for DB\n");
+ val = BAR_ID_0 ? 256 * 1024 : 512 * 1024;
+ } else {
+ DP_INFO(p_hwfn,
+ "BAR size not configured. Assuming BAR size of 512kB for GRC and 512kB for DB\n");
+ val = 512 * 1024;
+ }
+
+ return val;
+}
+
+void ecore_init_dp(struct ecore_dev *p_dev,
+ u32 dp_module, u8 dp_level, void *dp_ctx)
+{
+ u32 i;
+
+ p_dev->dp_level = dp_level;
+ p_dev->dp_module = dp_module;
+ p_dev->dp_ctx = dp_ctx;
+ for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
+ struct ecore_hwfn *p_hwfn = &p_dev->hwfns[i];
+
+ p_hwfn->dp_level = dp_level;
+ p_hwfn->dp_module = dp_module;
+ p_hwfn->dp_ctx = dp_ctx;
+ }
+}
+
+enum _ecore_status_t ecore_init_struct(struct ecore_dev *p_dev)
+{
+ u8 i;
+
+ for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
+ struct ecore_hwfn *p_hwfn = &p_dev->hwfns[i];
+
+ p_hwfn->p_dev = p_dev;
+ p_hwfn->my_id = i;
+ p_hwfn->b_active = false;
+
+#ifdef CONFIG_ECORE_LOCK_ALLOC
+ if (OSAL_SPIN_LOCK_ALLOC(p_hwfn, &p_hwfn->dmae_info.lock))
+ goto handle_err;
+#endif
+ OSAL_SPIN_LOCK_INIT(&p_hwfn->dmae_info.lock);
+ }
+
+ /* hwfn 0 is always active */
+ p_dev->hwfns[0].b_active = true;
+
+ /* set the default cache alignment to 128 (may be overridden later) */
+ p_dev->cache_shift = 7;
+ return ECORE_SUCCESS;
+#ifdef CONFIG_ECORE_LOCK_ALLOC
+handle_err:
+ while (--i) {
+ struct ecore_hwfn *p_hwfn = OSAL_NULL;
+
+ p_hwfn = &p_dev->hwfns[i];
+ OSAL_SPIN_LOCK_DEALLOC(&p_hwfn->dmae_info.lock);
+ }
+ return ECORE_NOMEM;
+#endif
+}
+
+static void ecore_qm_info_free(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+
+ OSAL_FREE(p_hwfn->p_dev, qm_info->qm_pq_params);
+ OSAL_FREE(p_hwfn->p_dev, qm_info->qm_vport_params);
+ OSAL_FREE(p_hwfn->p_dev, qm_info->qm_port_params);
+ OSAL_FREE(p_hwfn->p_dev, qm_info->wfq_data);
+}
+
+static void ecore_dbg_user_data_free(struct ecore_hwfn *p_hwfn)
+{
+ OSAL_FREE(p_hwfn->p_dev, p_hwfn->dbg_user_info);
+ p_hwfn->dbg_user_info = OSAL_NULL;
+}
+
+void ecore_resc_free(struct ecore_dev *p_dev)
+{
+ int i;
+
+ if (IS_VF(p_dev)) {
+ for_each_hwfn(p_dev, i)
+ ecore_l2_free(&p_dev->hwfns[i]);
+ return;
+ }
+
+ OSAL_FREE(p_dev, p_dev->fw_data);
+
+ OSAL_FREE(p_dev, p_dev->reset_stats);
+
+ ecore_llh_free(p_dev);
+
+ for_each_hwfn(p_dev, i) {
+ struct ecore_hwfn *p_hwfn = &p_dev->hwfns[i];
+
+ ecore_cxt_mngr_free(p_hwfn);
+ ecore_qm_info_free(p_hwfn);
+ ecore_spq_free(p_hwfn);
+ ecore_eq_free(p_hwfn);
+ ecore_consq_free(p_hwfn);
+ ecore_int_free(p_hwfn);
+ ecore_iov_free(p_hwfn);
+ ecore_l2_free(p_hwfn);
+ ecore_dmae_info_free(p_hwfn);
+ ecore_dcbx_info_free(p_hwfn);
+ ecore_dbg_user_data_free(p_hwfn);
+ /* @@@TBD Flush work-queue ? */
+
+ /* destroy doorbell recovery mechanism */
+ ecore_db_recovery_teardown(p_hwfn);
+ }
+}
+
+/******************** QM initialization *******************/
+
+/* bitmaps for indicating active traffic classes.
+ * Special case for Arrowhead 4 port
+ */
+/* 0..3 actualy used, 4 serves OOO, 7 serves high priority stuff (e.g. DCQCN) */
+#define ACTIVE_TCS_BMAP 0x9f
+/* 0..3 actually used, OOO and high priority stuff all use 3 */
+#define ACTIVE_TCS_BMAP_4PORT_K2 0xf
+
+/* determines the physical queue flags for a given PF. */
+static u32 ecore_get_pq_flags(struct ecore_hwfn *p_hwfn)
+{
+ u32 flags;
+
+ /* common flags */
+ flags = PQ_FLAGS_LB;
+
+ /* feature flags */
+ if (IS_ECORE_SRIOV(p_hwfn->p_dev))
+ flags |= PQ_FLAGS_VFS;
+ if (IS_ECORE_PACING(p_hwfn))
+ flags |= PQ_FLAGS_RLS;
+
+ /* protocol flags */
+ switch (p_hwfn->hw_info.personality) {
+ case ECORE_PCI_ETH:
+ if (!IS_ECORE_PACING(p_hwfn))
+ flags |= PQ_FLAGS_MCOS;
+ break;
+ case ECORE_PCI_FCOE:
+ flags |= PQ_FLAGS_OFLD;
+ break;
+ case ECORE_PCI_ISCSI:
+ flags |= PQ_FLAGS_ACK | PQ_FLAGS_OOO | PQ_FLAGS_OFLD;
+ break;
+ case ECORE_PCI_ETH_ROCE:
+ flags |= PQ_FLAGS_OFLD | PQ_FLAGS_LLT;
+ if (!IS_ECORE_PACING(p_hwfn))
+ flags |= PQ_FLAGS_MCOS;
+ break;
+ case ECORE_PCI_ETH_IWARP:
+ flags |= PQ_FLAGS_ACK | PQ_FLAGS_OOO | PQ_FLAGS_OFLD;
+ if (!IS_ECORE_PACING(p_hwfn))
+ flags |= PQ_FLAGS_MCOS;
+ break;
+ default:
+ DP_ERR(p_hwfn, "unknown personality %d\n",
+ p_hwfn->hw_info.personality);
+ return 0;
+ }
+ return flags;
+}
+
+/* Getters for resource amounts necessary for qm initialization */
+u8 ecore_init_qm_get_num_tcs(struct ecore_hwfn *p_hwfn)
+{
+ return p_hwfn->hw_info.num_hw_tc;
+}
+
+u16 ecore_init_qm_get_num_vfs(struct ecore_hwfn *p_hwfn)
+{
+ return IS_ECORE_SRIOV(p_hwfn->p_dev) ?
+ p_hwfn->p_dev->p_iov_info->total_vfs : 0;
+}
+
+#define NUM_DEFAULT_RLS 1
+
+u16 ecore_init_qm_get_num_pf_rls(struct ecore_hwfn *p_hwfn)
+{
+ u16 num_pf_rls, num_vfs = ecore_init_qm_get_num_vfs(p_hwfn);
+
+ /* num RLs can't exceed resource amount of rls or vports or the
+ * dcqcn qps
+ */
+ num_pf_rls = (u16)OSAL_MIN_T(u32, RESC_NUM(p_hwfn, ECORE_RL),
+ RESC_NUM(p_hwfn, ECORE_VPORT));
+
+ /* make sure after we reserve the default and VF rls we'll have
+ * something left
+ */
+ if (num_pf_rls < num_vfs + NUM_DEFAULT_RLS) {
+ DP_NOTICE(p_hwfn, false,
+ "no rate limiters left for PF rate limiting"
+ " [num_pf_rls %d num_vfs %d]\n", num_pf_rls, num_vfs);
+ return 0;
+ }
+
+ /* subtract rls necessary for VFs and one default one for the PF */
+ num_pf_rls -= num_vfs + NUM_DEFAULT_RLS;
+
+ return num_pf_rls;
+}
+
+u16 ecore_init_qm_get_num_vports(struct ecore_hwfn *p_hwfn)
+{
+ u32 pq_flags = ecore_get_pq_flags(p_hwfn);
+
+ /* all pqs share the same vport (hence the 1 below), except for vfs
+ * and pf_rl pqs
+ */
+ return (!!(PQ_FLAGS_RLS & pq_flags)) *
+ ecore_init_qm_get_num_pf_rls(p_hwfn) +
+ (!!(PQ_FLAGS_VFS & pq_flags)) *
+ ecore_init_qm_get_num_vfs(p_hwfn) + 1;
+}
+
+/* calc amount of PQs according to the requested flags */
+u16 ecore_init_qm_get_num_pqs(struct ecore_hwfn *p_hwfn)
+{
+ u32 pq_flags = ecore_get_pq_flags(p_hwfn);
+
+ return (!!(PQ_FLAGS_RLS & pq_flags)) *
+ ecore_init_qm_get_num_pf_rls(p_hwfn) +
+ (!!(PQ_FLAGS_MCOS & pq_flags)) *
+ ecore_init_qm_get_num_tcs(p_hwfn) +
+ (!!(PQ_FLAGS_LB & pq_flags)) +
+ (!!(PQ_FLAGS_OOO & pq_flags)) +
+ (!!(PQ_FLAGS_ACK & pq_flags)) +
+ (!!(PQ_FLAGS_OFLD & pq_flags)) +
+ (!!(PQ_FLAGS_VFS & pq_flags)) *
+ ecore_init_qm_get_num_vfs(p_hwfn);
+}
+
+/* initialize the top level QM params */
+static void ecore_init_qm_params(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+ bool four_port;
+
+ /* pq and vport bases for this PF */
+ qm_info->start_pq = (u16)RESC_START(p_hwfn, ECORE_PQ);
+ qm_info->start_vport = (u8)RESC_START(p_hwfn, ECORE_VPORT);
+
+ /* rate limiting and weighted fair queueing are always enabled */
+ qm_info->vport_rl_en = 1;
+ qm_info->vport_wfq_en = 1;
+
+ /* TC config is different for AH 4 port */
+ four_port = p_hwfn->p_dev->num_ports_in_engine == MAX_NUM_PORTS_K2;
+
+ /* in AH 4 port we have fewer TCs per port */
+ qm_info->max_phys_tcs_per_port = four_port ? NUM_PHYS_TCS_4PORT_K2 :
+ NUM_OF_PHYS_TCS;
+
+ /* unless MFW indicated otherwise, ooo_tc should be 3 for AH 4 port and
+ * 4 otherwise
+ */
+ if (!qm_info->ooo_tc)
+ qm_info->ooo_tc = four_port ? DCBX_TCP_OOO_K2_4PORT_TC :
+ DCBX_TCP_OOO_TC;
+}
+
+/* initialize qm vport params */
+static void ecore_init_qm_vport_params(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+ u8 i;
+
+ /* all vports participate in weighted fair queueing */
+ for (i = 0; i < ecore_init_qm_get_num_vports(p_hwfn); i++)
+ qm_info->qm_vport_params[i].wfq = 1;
+}
+
+/* initialize qm port params */
+static void ecore_init_qm_port_params(struct ecore_hwfn *p_hwfn)
+{
+ /* Initialize qm port parameters */
+ u8 i, active_phys_tcs, num_ports = p_hwfn->p_dev->num_ports_in_engine;
+ struct ecore_dev *p_dev = p_hwfn->p_dev;
+
+ /* indicate how ooo and high pri traffic is dealt with */
+ active_phys_tcs = num_ports == MAX_NUM_PORTS_K2 ?
+ ACTIVE_TCS_BMAP_4PORT_K2 : ACTIVE_TCS_BMAP;
+
+ for (i = 0; i < num_ports; i++) {
+ struct init_qm_port_params *p_qm_port =
+ &p_hwfn->qm_info.qm_port_params[i];
+ u16 pbf_max_cmd_lines;
+
+ p_qm_port->active = 1;
+ p_qm_port->active_phys_tcs = active_phys_tcs;
+ pbf_max_cmd_lines = (u16)NUM_OF_PBF_CMD_LINES(p_dev);
+ p_qm_port->num_pbf_cmd_lines = pbf_max_cmd_lines / num_ports;
+ p_qm_port->num_btb_blocks =
+ NUM_OF_BTB_BLOCKS(p_dev) / num_ports;
+ }
+}
+
+/* Reset the params which must be reset for qm init. QM init may be called as
+ * a result of flows other than driver load (e.g. dcbx renegotiation). Other
+ * params may be affected by the init but would simply recalculate to the same
+ * values. The allocations made for QM init, ports, vports, pqs and vfqs are not
+ * affected as these amounts stay the same.
+ */
+static void ecore_init_qm_reset_params(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+
+ qm_info->num_pqs = 0;
+ qm_info->num_vports = 0;
+ qm_info->num_pf_rls = 0;
+ qm_info->num_vf_pqs = 0;
+ qm_info->first_vf_pq = 0;
+ qm_info->first_mcos_pq = 0;
+ qm_info->first_rl_pq = 0;
+}
+
+static void ecore_init_qm_advance_vport(struct ecore_hwfn *p_hwfn)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+
+ qm_info->num_vports++;
+
+ if (qm_info->num_vports > ecore_init_qm_get_num_vports(p_hwfn))
+ DP_ERR(p_hwfn,
+ "vport overflow! qm_info->num_vports %d,"
+ " qm_init_get_num_vports() %d\n",
+ qm_info->num_vports,
+ ecore_init_qm_get_num_vports(p_hwfn));
+}
+
+/* initialize a single pq and manage qm_info resources accounting.
+ * The pq_init_flags param determines whether the PQ is rate limited
+ * (for VF or PF)
+ * and whether a new vport is allocated to the pq or not (i.e. vport will be
+ * shared)
+ */
+
+/* flags for pq init */
+#define PQ_INIT_SHARE_VPORT (1 << 0)
+#define PQ_INIT_PF_RL (1 << 1)
+#define PQ_INIT_VF_RL (1 << 2)
+
+/* defines for pq init */
+#define PQ_INIT_DEFAULT_WRR_GROUP 1
+#define PQ_INIT_DEFAULT_TC 0
+#define PQ_INIT_OFLD_TC (p_hwfn->hw_info.offload_tc)
+
+static void ecore_init_qm_pq(struct ecore_hwfn *p_hwfn,
+ struct ecore_qm_info *qm_info,
+ u8 tc, u32 pq_init_flags)
+{
+ u16 pq_idx = qm_info->num_pqs, max_pq =
+ ecore_init_qm_get_num_pqs(p_hwfn);
+
+ if (pq_idx > max_pq)
+ DP_ERR(p_hwfn,
+ "pq overflow! pq %d, max pq %d\n", pq_idx, max_pq);
+
+ /* init pq params */
+ qm_info->qm_pq_params[pq_idx].port_id = p_hwfn->port_id;
+ qm_info->qm_pq_params[pq_idx].vport_id = qm_info->start_vport +
+ qm_info->num_vports;
+ qm_info->qm_pq_params[pq_idx].tc_id = tc;
+ qm_info->qm_pq_params[pq_idx].wrr_group = PQ_INIT_DEFAULT_WRR_GROUP;
+ qm_info->qm_pq_params[pq_idx].rl_valid =
+ (pq_init_flags & PQ_INIT_PF_RL ||
+ pq_init_flags & PQ_INIT_VF_RL);
+
+ /* The "rl_id" is set as the "vport_id" */
+ qm_info->qm_pq_params[pq_idx].rl_id =
+ qm_info->qm_pq_params[pq_idx].vport_id;
+
+ /* qm params accounting */
+ qm_info->num_pqs++;
+ if (!(pq_init_flags & PQ_INIT_SHARE_VPORT))
+ qm_info->num_vports++;
+
+ if (pq_init_flags & PQ_INIT_PF_RL)
+ qm_info->num_pf_rls++;
+
+ if (qm_info->num_vports > ecore_init_qm_get_num_vports(p_hwfn))
+ DP_ERR(p_hwfn,
+ "vport overflow! qm_info->num_vports %d,"
+ " qm_init_get_num_vports() %d\n",
+ qm_info->num_vports,
+ ecore_init_qm_get_num_vports(p_hwfn));
+
+ if (qm_info->num_pf_rls > ecore_init_qm_get_num_pf_rls(p_hwfn))
+ DP_ERR(p_hwfn, "rl overflow! qm_info->num_pf_rls %d,"
+ " qm_init_get_num_pf_rls() %d\n",
+ qm_info->num_pf_rls,
+ ecore_init_qm_get_num_pf_rls(p_hwfn));
+}
+
+/* get pq index according to PQ_FLAGS */
+static u16 *ecore_init_qm_get_idx_from_flags(struct ecore_hwfn *p_hwfn,
+ u32 pq_flags)
+{
+ struct ecore_qm_info *qm_info = &p_hwfn->qm_info;
+
+ /* Can't have multiple flags set here */
+ if (OSAL_BITMAP_WEIGHT((unsigned long *)&pq_flags,
+ sizeof(pq_flags)) > 1)
+ goto err;
+
+ switch (pq_flags) {
+ case PQ_FLAGS_RLS:
+ return &qm_info->first_rl_pq;
+ case PQ_FLAGS_MCOS:
+ return &qm_info->first_mcos_pq;
+ case PQ_FLAGS_LB:
+ return &qm_info->pure_lb_pq;
+ case PQ_FLAGS_OOO:
+ return &qm_info->ooo_pq;
+ case PQ_FLAGS_ACK:
+ return &qm_info->pure_ack_pq;
+ case PQ_FLAGS_OFLD:
+ return &qm_info->offload_pq;
+ case PQ_FLAGS_VFS:
+ return &qm_info->first_vf_pq;
+ default:
+ goto err;
+ }
+
+err:
+ DP_ERR(p_hwfn, "BAD pq flags %d\n", pq_flags);
+ return OSAL_NULL;
+}
+
+/* save pq index in qm info */
+static void ecore_init_qm_set_idx(struct ecore_hwfn *p_hwfn,
+ u32 pq_flags, u16 pq_val)
+{
+ u16 *base_pq_idx = ecore_init_qm_get_idx_from_flags(p_hwfn, pq_flags);
+
+ *base_pq_idx = p_hwfn->qm_info.start_pq + pq_val;
+}
+
+/* get tx pq index, with the PQ TX base already set (ready for context init) */
+u16 ecore_get_cm_pq_idx(struct ecore_hwfn *p_hwfn, u32 pq_flags)
+{
+ u16 *base_pq_idx = ecore_init_qm_get_idx_from_flags(p_hwfn, pq_flags);
+
+ return *base_pq_idx + CM_TX_PQ_BASE;