Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  qed_dev.c   Sprache: C

 
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
 * Copyright (c) 2015-2017  QLogic Corporation
 * Copyright (c) 2019-2020 Marvell International Ltd.
 */


#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/etherdevice.h>
#include <linux/qed/qed_chain.h>
#include <linux/qed/qed_if.h>
#include "qed.h"
#include "qed_cxt.h"
#include "qed_dcbx.h"
#include "qed_dev_api.h"
#include "qed_fcoe.h"
#include "qed_hsi.h"
#include "qed_iro_hsi.h"
#include "qed_hw.h"
#include "qed_init_ops.h"
#include "qed_int.h"
#include "qed_iscsi.h"
#include "qed_ll2.h"
#include "qed_mcp.h"
#include "qed_ooo.h"
#include "qed_reg_addr.h"
#include "qed_sp.h"
#include "qed_sriov.h"
#include "qed_vf.h"
#include "qed_rdma.h"
#include "qed_nvmetcp.h"

static DEFINE_SPINLOCK(qm_lock);

/******************** Doorbell Recovery *******************/
/* The doorbell recovery mechanism consists of a list of entries which represent
 * doorbelling entities (l2 queues, roce sq/rq/cqs, the slowpath spq, etc). Each
 * entity needs to register with the mechanism and provide the parameters
 * describing it's doorbell, including a location where last used doorbell data
 * can be found. The doorbell execute function will traverse the list and
 * doorbell all of the registered entries.
 */

struct qed_db_recovery_entry {
 struct list_head list_entry;
 void __iomem *db_addr;
 void *db_data;
 enum qed_db_rec_width db_width;
 enum qed_db_rec_space db_space;
 u8 hwfn_idx;
};

/* Display a single doorbell recovery entry */
static void qed_db_recovery_dp_entry(struct qed_hwfn *p_hwfn,
         struct qed_db_recovery_entry *db_entry,
         char *action)
{
 DP_VERBOSE(p_hwfn,
     QED_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) */
static bool qed_db_rec_sanity(struct qed_dev *cdev,
         void __iomem *db_addr,
         enum qed_db_rec_width db_width,
         void *db_data)
{
 u32 width = (db_width == DB_REC_WIDTH_32B) ? 32 : 64;

 /* Make sure doorbell address is within the doorbell bar */
 if (db_addr < cdev->doorbells ||
     (u8 __iomem *)db_addr + width >
     (u8 __iomem *)cdev->doorbells + cdev->db_size) {
  WARN(true,
       "Illegal doorbell address: %p. Legal range for doorbell addresses is [%p..%p]\n",
       db_addr,
       cdev->doorbells,
       (u8 __iomem *)cdev->doorbells + cdev->db_size);
  return false;
 }

 /* ake sure doorbell data pointer is not null */
 if (!db_data) {
  WARN(true"Illegal doorbell data pointer: %p", db_data);
  return false;
 }

 return true;
}

/* Find hwfn according to the doorbell address */
static struct qed_hwfn *qed_db_rec_find_hwfn(struct qed_dev *cdev,
          void __iomem *db_addr)
{
 struct qed_hwfn *p_hwfn;

 /* In CMT doorbell bar is split down the middle between engine 0 and enigne 1 */
 if (cdev->num_hwfns > 1)
  p_hwfn = db_addr < cdev->hwfns[1].doorbells ?
      &cdev->hwfns[0] : &cdev->hwfns[1];
 else
  p_hwfn = QED_LEADING_HWFN(cdev);

 return p_hwfn;
}

/* Add a new entry to the doorbell recovery mechanism */
int qed_db_recovery_add(struct qed_dev *cdev,
   void __iomem *db_addr,
   void *db_data,
   enum qed_db_rec_width db_width,
   enum qed_db_rec_space db_space)
{
 struct qed_db_recovery_entry *db_entry;
 struct qed_hwfn *p_hwfn;

 /* Shortcircuit VFs, for now */
 if (IS_VF(cdev)) {
  DP_VERBOSE(cdev,
      QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
  return 0;
 }

 /* Sanitize doorbell address */
 if (!qed_db_rec_sanity(cdev, db_addr, db_width, db_data))
  return -EINVAL;

 /* Obtain hwfn from doorbell address */
 p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);

 /* Create entry */
 db_entry = kzalloc(sizeof(*db_entry), GFP_KERNEL);
 if (!db_entry) {
  DP_NOTICE(cdev, "Failed to allocate a db recovery entry\n");
  return -ENOMEM;
 }

 /* 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 */
 qed_db_recovery_dp_entry(p_hwfn, db_entry, "Adding");

 /* Protect the list */
 spin_lock_bh(&p_hwfn->db_recovery_info.lock);
 list_add_tail(&db_entry->list_entry, &p_hwfn->db_recovery_info.list);
 spin_unlock_bh(&p_hwfn->db_recovery_info.lock);

 return 0;
}

/* Remove an entry from the doorbell recovery mechanism */
int qed_db_recovery_del(struct qed_dev *cdev,
   void __iomem *db_addr, void *db_data)
{
 struct qed_db_recovery_entry *db_entry = NULL;
 struct qed_hwfn *p_hwfn;
 int rc = -EINVAL;

 /* Shortcircuit VFs, for now */
 if (IS_VF(cdev)) {
  DP_VERBOSE(cdev,
      QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
  return 0;
 }

 /* Obtain hwfn from doorbell address */
 p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);

 /* Protect the list */
 spin_lock_bh(&p_hwfn->db_recovery_info.lock);
 list_for_each_entry(db_entry,
       &p_hwfn->db_recovery_info.list, list_entry) {
  /* search according to db_data addr since db_addr is not unique (roce) */
  if (db_entry->db_data == db_data) {
   qed_db_recovery_dp_entry(p_hwfn, db_entry, "Deleting");
   list_del(&db_entry->list_entry);
   rc = 0;
   break;
  }
 }

 spin_unlock_bh(&p_hwfn->db_recovery_info.lock);

 if (rc == -EINVAL)

  DP_NOTICE(p_hwfn,
     "Failed to find element in list. Key (db_data addr) was %p. db_addr was %p\n",
     db_data, db_addr);
 else
  kfree(db_entry);

 return rc;
}

/* Initialize the doorbell recovery mechanism */
static int qed_db_recovery_setup(struct qed_hwfn *p_hwfn)
{
 DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Setting up db recovery\n");

 /* Make sure db_size was set in cdev */
 if (!p_hwfn->cdev->db_size) {
  DP_ERR(p_hwfn->cdev, "db_size not set\n");
  return -EINVAL;
 }

 INIT_LIST_HEAD(&p_hwfn->db_recovery_info.list);
 spin_lock_init(&p_hwfn->db_recovery_info.lock);
 p_hwfn->db_recovery_info.db_recovery_counter = 0;

 return 0;
}

/* Destroy the doorbell recovery mechanism */
static void qed_db_recovery_teardown(struct qed_hwfn *p_hwfn)
{
 struct qed_db_recovery_entry *db_entry = NULL;

 DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Tearing down db recovery\n");
 if (!list_empty(&p_hwfn->db_recovery_info.list)) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_SPQ,
      "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 (!list_empty(&p_hwfn->db_recovery_info.list)) {
   db_entry =
       list_first_entry(&p_hwfn->db_recovery_info.list,
          struct qed_db_recovery_entry,
          list_entry);
   qed_db_recovery_dp_entry(p_hwfn, db_entry, "Purging");
   list_del(&db_entry->list_entry);
   kfree(db_entry);
  }
 }
 p_hwfn->db_recovery_info.db_recovery_counter = 0;
}

/* Ring the doorbell of a single doorbell recovery entry */
static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
     struct qed_db_recovery_entry *db_entry)
{
 /* Print according to width */
 if (db_entry->db_width == DB_REC_WIDTH_32B) {
  DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
      "ringing doorbell address %p data %x\n",
      db_entry->db_addr,
      *(u32 *)db_entry->db_data);
 } else {
  DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
      "ringing doorbell address %p data %llx\n",
      db_entry->db_addr,
      *(u64 *)(db_entry->db_data));
 }

 /* Sanity */
 if (!qed_db_rec_sanity(p_hwfn->cdev, db_entry->db_addr,
          db_entry->db_width, 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.
 */

 wmb();

 /* Ring the doorbell */
 if (db_entry->db_width == DB_REC_WIDTH_32B)
  DIRECT_REG_WR(db_entry->db_addr,
         *(u32 *)(db_entry->db_data));
 else
  DIRECT_REG_WR64(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...
 */

 wmb();
}

/* Traverse the doorbell recovery entry list and ring all the doorbells */
void qed_db_recovery_execute(struct qed_hwfn *p_hwfn)
{
 struct qed_db_recovery_entry *db_entry = NULL;

 DP_NOTICE(p_hwfn, "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 */
 spin_lock_bh(&p_hwfn->db_recovery_info.lock);
 list_for_each_entry(db_entry,
       &p_hwfn->db_recovery_info.list, list_entry)
  qed_db_recovery_ring(p_hwfn, db_entry);
 spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
}

/******************** Doorbell Recovery end ****************/

/********************************** NIG LLH ***********************************/

enum qed_llh_filter_type {
 QED_LLH_FILTER_TYPE_MAC,
 QED_LLH_FILTER_TYPE_PROTOCOL,
};

struct qed_llh_mac_filter {
 u8 addr[ETH_ALEN];
};

struct qed_llh_protocol_filter {
 enum qed_llh_prot_filter_type_t type;
 u16 source_port_or_eth_type;
 u16 dest_port;
};

union qed_llh_filter {
 struct qed_llh_mac_filter mac;
 struct qed_llh_protocol_filter protocol;
};

struct qed_llh_filter_info {
 bool b_enabled;
 u32 ref_cnt;
 enum qed_llh_filter_type type;
 union qed_llh_filter filter;
};

struct qed_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 qed_llh_filter_info **pp_filters;
};

static void qed_llh_free(struct qed_dev *cdev)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;
 u32 i;

 if (p_llh_info) {
  if (p_llh_info->pp_filters)
   for (i = 0; i < p_llh_info->num_ppfid; i++)
    kfree(p_llh_info->pp_filters[i]);

  kfree(p_llh_info->pp_filters);
 }

 kfree(p_llh_info);
 cdev->p_llh_info = NULL;
}

static int qed_llh_alloc(struct qed_dev *cdev)
{
 struct qed_llh_info *p_llh_info;
 u32 size, i;

 p_llh_info = kzalloc(sizeof(*p_llh_info), GFP_KERNEL);
 if (!p_llh_info)
  return -ENOMEM;
 cdev->p_llh_info = p_llh_info;

 for (i = 0; i < MAX_NUM_PPFID; i++) {
  if (!(cdev->ppfid_bitmap & (0x1 << i)))
   continue;

  p_llh_info->ppfid_array[p_llh_info->num_ppfid] = i;
  DP_VERBOSE(cdev, QED_MSG_SP, "ppfid_array[%d] = %u\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 = kzalloc(size, GFP_KERNEL);
 if (!p_llh_info->pp_filters)
  return -ENOMEM;

 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] = kzalloc(size, GFP_KERNEL);
  if (!p_llh_info->pp_filters[i])
   return -ENOMEM;
 }

 return 0;
}

static int qed_llh_shadow_sanity(struct qed_dev *cdev,
     u8 ppfid, u8 filter_idx, const char *action)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;

 if (ppfid >= p_llh_info->num_ppfid) {
  DP_NOTICE(cdev,
     "LLH shadow [%s]: using ppfid %d while only %d ppfids are available\n",
     action, ppfid, p_llh_info->num_ppfid);
  return -EINVAL;
 }

 if (filter_idx >= NIG_REG_LLH_FUNC_FILTER_EN_SIZE) {
  DP_NOTICE(cdev,
     "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 -EINVAL;
 }

 return 0;
}

#define QED_LLH_INVALID_FILTER_IDX      0xff

static int
qed_llh_shadow_search_filter(struct qed_dev *cdev,
        u8 ppfid,
        union qed_llh_filter *p_filter, u8 *p_filter_idx)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;
 struct qed_llh_filter_info *p_filters;
 int rc;
 u8 i;

 rc = qed_llh_shadow_sanity(cdev, ppfid, 0, "search");
 if (rc)
  return rc;

 *p_filter_idx = QED_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 (!memcmp(p_filter, &p_filters[i].filter,
       sizeof(*p_filter))) {
   *p_filter_idx = i;
   break;
  }
 }

 return 0;
}

static int
qed_llh_shadow_get_free_idx(struct qed_dev *cdev, u8 ppfid, u8 *p_filter_idx)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;
 struct qed_llh_filter_info *p_filters;
 int rc;
 u8 i;

 rc = qed_llh_shadow_sanity(cdev, ppfid, 0, "get_free_idx");
 if (rc)
  return rc;

 *p_filter_idx = QED_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 0;
}

static int
__qed_llh_shadow_add_filter(struct qed_dev *cdev,
       u8 ppfid,
       u8 filter_idx,
       enum qed_llh_filter_type type,
       union qed_llh_filter *p_filter, u32 *p_ref_cnt)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;
 struct qed_llh_filter_info *p_filters;
 int rc;

 rc = qed_llh_shadow_sanity(cdev, ppfid, filter_idx, "add");
 if (rc)
  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;
  memcpy(&p_filters[filter_idx].filter, p_filter,
         sizeof(p_filters[filter_idx].filter));
 }

 *p_ref_cnt = ++p_filters[filter_idx].ref_cnt;

 return 0;
}

static int
qed_llh_shadow_add_filter(struct qed_dev *cdev,
     u8 ppfid,
     enum qed_llh_filter_type type,
     union qed_llh_filter *p_filter,
     u8 *p_filter_idx, u32 *p_ref_cnt)
{
 int rc;

 /* Check if the same filter already exist */
 rc = qed_llh_shadow_search_filter(cdev, ppfid, p_filter, p_filter_idx);
 if (rc)
  return rc;

 /* Find a new entry in case of a new filter */
 if (*p_filter_idx == QED_LLH_INVALID_FILTER_IDX) {
  rc = qed_llh_shadow_get_free_idx(cdev, ppfid, p_filter_idx);
  if (rc)
   return rc;
 }

 /* No free entry was found */
 if (*p_filter_idx == QED_LLH_INVALID_FILTER_IDX) {
  DP_NOTICE(cdev,
     "Failed to find an empty LLH filter to utilize [ppfid %d]\n",
     ppfid);
  return -EINVAL;
 }

 return __qed_llh_shadow_add_filter(cdev, ppfid, *p_filter_idx, type,
        p_filter, p_ref_cnt);
}

static int
__qed_llh_shadow_remove_filter(struct qed_dev *cdev,
          u8 ppfid, u8 filter_idx, u32 *p_ref_cnt)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;
 struct qed_llh_filter_info *p_filters;
 int rc;

 rc = qed_llh_shadow_sanity(cdev, ppfid, filter_idx, "remove");
 if (rc)
  return rc;

 p_filters = p_llh_info->pp_filters[ppfid];
 if (!p_filters[filter_idx].ref_cnt) {
  DP_NOTICE(cdev,
     "LLH shadow: trying to remove a filter with ref_cnt=0\n");
  return -EINVAL;
 }

 *p_ref_cnt = --p_filters[filter_idx].ref_cnt;
 if (!p_filters[filter_idx].ref_cnt)
  memset(&p_filters[filter_idx],
         0, sizeof(p_filters[filter_idx]));

 return 0;
}

static int
qed_llh_shadow_remove_filter(struct qed_dev *cdev,
        u8 ppfid,
        union qed_llh_filter *p_filter,
        u8 *p_filter_idx, u32 *p_ref_cnt)
{
 int rc;

 rc = qed_llh_shadow_search_filter(cdev, ppfid, p_filter, p_filter_idx);
 if (rc)
  return rc;

 /* No matching filter was found */
 if (*p_filter_idx == QED_LLH_INVALID_FILTER_IDX) {
  DP_NOTICE(cdev, "Failed to find a filter in the LLH shadow\n");
  return -EINVAL;
 }

 return __qed_llh_shadow_remove_filter(cdev, ppfid, *p_filter_idx,
           p_ref_cnt);
}

static int qed_llh_abs_ppfid(struct qed_dev *cdev, u8 ppfid, u8 *p_abs_ppfid)
{
 struct qed_llh_info *p_llh_info = cdev->p_llh_info;

 if (ppfid >= p_llh_info->num_ppfid) {
  DP_NOTICE(cdev,
     "ppfid %d is not valid, available indices are 0..%d\n",
     ppfid, p_llh_info->num_ppfid - 1);
  *p_abs_ppfid = 0;
  return -EINVAL;
 }

 *p_abs_ppfid = p_llh_info->ppfid_array[ppfid];

 return 0;
}

static int
qed_llh_set_engine_affin(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
 struct qed_dev *cdev = p_hwfn->cdev;
 enum qed_eng eng;
 u8 ppfid;
 int rc;

 rc = qed_mcp_get_engine_config(p_hwfn, p_ptt);
 if (rc != 0 && rc != -EOPNOTSUPP) {
  DP_NOTICE(p_hwfn,
     "Failed to get the engine affinity configuration\n");
  return rc;
 }

 /* RoCE PF is bound to a single engine */
 if (QED_IS_ROCE_PERSONALITY(p_hwfn)) {
  eng = cdev->fir_affin ? QED_ENG1 : QED_ENG0;
  rc = qed_llh_set_roce_affinity(cdev, eng);
  if (rc) {
   DP_NOTICE(cdev,
      "Failed to set the RoCE engine affinity\n");
   return rc;
  }

  DP_VERBOSE(cdev,
      QED_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 (QED_IS_FCOE_PERSONALITY(p_hwfn) || QED_IS_ISCSI_PERSONALITY(p_hwfn) ||
     QED_IS_NVMETCP_PERSONALITY(p_hwfn))
  eng = cdev->fir_affin ? QED_ENG1 : QED_ENG0;
 else   /* L2_PERSONALITY */
  eng = QED_BOTH_ENG;

 for (ppfid = 0; ppfid < cdev->p_llh_info->num_ppfid; ppfid++) {
  rc = qed_llh_set_ppfid_affinity(cdev, ppfid, eng);
  if (rc) {
   DP_NOTICE(cdev,
      "Failed to set the engine affinity of ppfid %d\n",
      ppfid);
   return rc;
  }
 }

 DP_VERBOSE(cdev, QED_MSG_SP,
     "LLH: Set the engine affinity of non-RoCE packets as %d\n",
     eng);

 return 0;
}

static int qed_llh_hw_init_pf(struct qed_hwfn *p_hwfn,
         struct qed_ptt *p_ptt)
{
 struct qed_dev *cdev = p_hwfn->cdev;
 u8 ppfid, abs_ppfid;
 int rc;

 for (ppfid = 0; ppfid < cdev->p_llh_info->num_ppfid; ppfid++) {
  u32 addr;

  rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
  if (rc)
   return rc;

  addr = NIG_REG_LLH_PPFID2PFID_TBL_0 + abs_ppfid * 0x4;
  qed_wr(p_hwfn, p_ptt, addr, p_hwfn->rel_pf_id);
 }

 if (test_bit(QED_MF_LLH_MAC_CLSS, &cdev->mf_bits) &&
     !QED_IS_FCOE_PERSONALITY(p_hwfn)) {
  rc = qed_llh_add_mac_filter(cdev, 0,
         p_hwfn->hw_info.hw_mac_addr);
  if (rc)
   DP_NOTICE(cdev,
      "Failed to add an LLH filter with the primary MAC\n");
 }

 if (QED_IS_CMT(cdev)) {
  rc = qed_llh_set_engine_affin(p_hwfn, p_ptt);
  if (rc)
   return rc;
 }

 return 0;
}

u8 qed_llh_get_num_ppfid(struct qed_dev *cdev)
{
 return cdev->p_llh_info->num_ppfid;
}

#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

int qed_llh_set_ppfid_affinity(struct qed_dev *cdev, u8 ppfid, enum qed_eng eng)
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 u32 addr, val, eng_sel;
 u8 abs_ppfid;
 int rc = 0;

 if (!p_ptt)
  return -EAGAIN;

 if (!QED_IS_CMT(cdev))
  goto out;

 rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
 if (rc)
  goto out;

 switch (eng) {
 case QED_ENG0:
  eng_sel = 0;
  break;
 case QED_ENG1:
  eng_sel = 1;
  break;
 case QED_BOTH_ENG:
  eng_sel = 2;
  break;
 default:
  DP_NOTICE(cdev, "Invalid affinity value for ppfid [%d]\n", eng);
  rc = -EINVAL;
  goto out;
 }

 addr = NIG_REG_PPF_TO_ENGINE_SEL + abs_ppfid * 0x4;
 val = qed_rd(p_hwfn, p_ptt, addr);
 SET_FIELD(val, NIG_REG_PPF_TO_ENGINE_SEL_NON_ROCE, eng_sel);
 qed_wr(p_hwfn, p_ptt, addr, val);

 /* The iWARP affinity is set as the affinity of ppfid 0 */
 if (!ppfid && QED_IS_IWARP_PERSONALITY(p_hwfn))
  cdev->iwarp_affin = (eng == QED_ENG1) ? 1 : 0;
out:
 qed_ptt_release(p_hwfn, p_ptt);

 return rc;
}

int qed_llh_set_roce_affinity(struct qed_dev *cdev, enum qed_eng eng)
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 u32 addr, val, eng_sel;
 u8 ppfid, abs_ppfid;
 int rc = 0;

 if (!p_ptt)
  return -EAGAIN;

 if (!QED_IS_CMT(cdev))
  goto out;

 switch (eng) {
 case QED_ENG0:
  eng_sel = 0;
  break;
 case QED_ENG1:
  eng_sel = 1;
  break;
 case QED_BOTH_ENG:
  eng_sel = 2;
  qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_ENG_CLS_ROCE_QP_SEL,
         0xf);  /* QP bit 15 */
  break;
 default:
  DP_NOTICE(cdev, "Invalid affinity value for RoCE [%d]\n", eng);
  rc = -EINVAL;
  goto out;
 }

 for (ppfid = 0; ppfid < cdev->p_llh_info->num_ppfid; ppfid++) {
  rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
  if (rc)
   goto out;

  addr = NIG_REG_PPF_TO_ENGINE_SEL + abs_ppfid * 0x4;
  val = qed_rd(p_hwfn, p_ptt, addr);
  SET_FIELD(val, NIG_REG_PPF_TO_ENGINE_SEL_ROCE, eng_sel);
  qed_wr(p_hwfn, p_ptt, addr, val);
 }
out:
 qed_ptt_release(p_hwfn, p_ptt);

 return rc;
}

struct qed_llh_filter_details {
 u64 value;
 u32 mode;
 u32 protocol_type;
 u32 hdr_sel;
 u32 enable;
};

static int
qed_llh_access_filter(struct qed_hwfn *p_hwfn,
        struct qed_ptt *p_ptt,
        u8 abs_ppfid,
        u8 filter_idx,
        struct qed_llh_filter_details *p_details)
{
 struct qed_dmae_params params = {0};
 u32 addr;
 u8 pfid;
 int rc;

 /* 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 another PFs.
 * The calculation of PPFID->PFID in AH is based on the relative index
 * of a PF on its port.
 * For BB the pfid is actually the abs_ppfid.
 */

 if (QED_IS_BB(p_hwfn->cdev))
  pfid = abs_ppfid;
 else
  pfid = abs_ppfid * p_hwfn->cdev->num_ports_in_engine +
      MFW_PORT(p_hwfn);

 /* Filter enable - should be done first when removing a filter */
 if (!p_details->enable) {
  qed_fid_pretend(p_hwfn, p_ptt,
    pfid << PXP_PRETEND_CONCRETE_FID_PFID_SHIFT);

  addr = NIG_REG_LLH_FUNC_FILTER_EN + filter_idx * 0x4;
  qed_wr(p_hwfn, p_ptt, addr, p_details->enable);

  qed_fid_pretend(p_hwfn, p_ptt,
    p_hwfn->rel_pf_id <<
    PXP_PRETEND_CONCRETE_FID_PFID_SHIFT);
 }

 /* Filter value */
 addr = NIG_REG_LLH_FUNC_FILTER_VALUE + 2 * filter_idx * 0x4;

 SET_FIELD(params.flags, QED_DMAE_PARAMS_DST_PF_VALID, 0x1);
 params.dst_pfid = pfid;
 rc = qed_dmae_host2grc(p_hwfn,
          p_ptt,
          (u64)(uintptr_t)&p_details->value,
          addr, 2 /* size_in_dwords */,
          ¶ms);
 if (rc)
  return rc;

 qed_fid_pretend(p_hwfn, p_ptt,
   pfid << PXP_PRETEND_CONCRETE_FID_PFID_SHIFT);

 /* Filter mode */
 addr = NIG_REG_LLH_FUNC_FILTER_MODE + filter_idx * 0x4;
 qed_wr(p_hwfn, p_ptt, addr, p_details->mode);

 /* Filter protocol type */
 addr = NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE + filter_idx * 0x4;
 qed_wr(p_hwfn, p_ptt, addr, p_details->protocol_type);

 /* Filter header select */
 addr = NIG_REG_LLH_FUNC_FILTER_HDR_SEL + filter_idx * 0x4;
 qed_wr(p_hwfn, p_ptt, addr, p_details->hdr_sel);

 /* Filter enable - should be done last when adding a filter */
 if (p_details->enable) {
  addr = NIG_REG_LLH_FUNC_FILTER_EN + filter_idx * 0x4;
  qed_wr(p_hwfn, p_ptt, addr, p_details->enable);
 }

 qed_fid_pretend(p_hwfn, p_ptt,
   p_hwfn->rel_pf_id <<
   PXP_PRETEND_CONCRETE_FID_PFID_SHIFT);

 return 0;
}

static int
qed_llh_add_filter(struct qed_hwfn *p_hwfn,
     struct qed_ptt *p_ptt,
     u8 abs_ppfid,
     u8 filter_idx, u8 filter_prot_type, u32 high, u32 low)
{
 struct qed_llh_filter_details filter_details;

 filter_details.enable = 1;
 filter_details.value = ((u64)high << 32) | low;
 filter_details.hdr_sel = 0;
 filter_details.protocol_type = filter_prot_type;
 /* Mode: 0: MAC-address classification 1: protocol classification */
 filter_details.mode = filter_prot_type ? 1 : 0;

 return qed_llh_access_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
         &filter_details);
}

static int
qed_llh_remove_filter(struct qed_hwfn *p_hwfn,
        struct qed_ptt *p_ptt, u8 abs_ppfid, u8 filter_idx)
{
 struct qed_llh_filter_details filter_details = {0};

 return qed_llh_access_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
         &filter_details);
}

int qed_llh_add_mac_filter(struct qed_dev *cdev,
      u8 ppfid, const u8 mac_addr[ETH_ALEN])
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 union qed_llh_filter filter = {};
 u8 filter_idx, abs_ppfid = 0;
 u32 high, low, ref_cnt;
 int rc = 0;

 if (!p_ptt)
  return -EAGAIN;

 if (!test_bit(QED_MF_LLH_MAC_CLSS, &cdev->mf_bits))
  goto out;

 memcpy(filter.mac.addr, mac_addr, ETH_ALEN);
 rc = qed_llh_shadow_add_filter(cdev, ppfid,
           QED_LLH_FILTER_TYPE_MAC,
           &filter, &filter_idx, &ref_cnt);
 if (rc)
  goto err;

 /* Configure the LLH only in case of a new the filter */
 if (ref_cnt == 1) {
  rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
  if (rc)
   goto err;

  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 = qed_llh_add_filter(p_hwfn, p_ptt, abs_ppfid, filter_idx,
     0, high, low);
  if (rc)
   goto err;
 }

 DP_VERBOSE(cdev,
     QED_MSG_SP,
     "LLH: Added MAC filter [%pM] to ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
     mac_addr, ppfid, abs_ppfid, filter_idx, ref_cnt);

 goto out;

err: DP_NOTICE(cdev,
    "LLH: Failed to add MAC filter [%pM] to ppfid %hhd\n",
    mac_addr, ppfid);
out:
 qed_ptt_release(p_hwfn, p_ptt);

 return rc;
}

static int
qed_llh_protocol_filter_stringify(struct qed_dev *cdev,
      enum qed_llh_prot_filter_type_t type,
      u16 source_port_or_eth_type,
      u16 dest_port, u8 *str, size_t str_len)
{
 switch (type) {
 case QED_LLH_FILTER_ETHERTYPE:
  snprintf(str, str_len, "Ethertype 0x%04x",
    source_port_or_eth_type);
  break;
 case QED_LLH_FILTER_TCP_SRC_PORT:
  snprintf(str, str_len, "TCP src port 0x%04x",
    source_port_or_eth_type);
  break;
 case QED_LLH_FILTER_UDP_SRC_PORT:
  snprintf(str, str_len, "UDP src port 0x%04x",
    source_port_or_eth_type);
  break;
 case QED_LLH_FILTER_TCP_DEST_PORT:
  snprintf(str, str_len, "TCP dst port 0x%04x", dest_port);
  break;
 case QED_LLH_FILTER_UDP_DEST_PORT:
  snprintf(str, str_len, "UDP dst port 0x%04x", dest_port);
  break;
 case QED_LLH_FILTER_TCP_SRC_AND_DEST_PORT:
  snprintf(str, str_len, "TCP src/dst ports 0x%04x/0x%04x",
    source_port_or_eth_type, dest_port);
  break;
 case QED_LLH_FILTER_UDP_SRC_AND_DEST_PORT:
  snprintf(str, str_len, "UDP src/dst ports 0x%04x/0x%04x",
    source_port_or_eth_type, dest_port);
  break;
 default:
  DP_NOTICE(cdev,
     "Non valid LLH protocol filter type %d\n", type);
  return -EINVAL;
 }

 return 0;
}

static int
qed_llh_protocol_filter_to_hilo(struct qed_dev *cdev,
    enum qed_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 QED_LLH_FILTER_ETHERTYPE:
  *p_high = source_port_or_eth_type;
  break;
 case QED_LLH_FILTER_TCP_SRC_PORT:
 case QED_LLH_FILTER_UDP_SRC_PORT:
  *p_low = source_port_or_eth_type << 16;
  break;
 case QED_LLH_FILTER_TCP_DEST_PORT:
 case QED_LLH_FILTER_UDP_DEST_PORT:
  *p_low = dest_port;
  break;
 case QED_LLH_FILTER_TCP_SRC_AND_DEST_PORT:
 case QED_LLH_FILTER_UDP_SRC_AND_DEST_PORT:
  *p_low = (source_port_or_eth_type << 16) | dest_port;
  break;
 default:
  DP_NOTICE(cdev,
     "Non valid LLH protocol filter type %d\n", type);
  return -EINVAL;
 }

 return 0;
}

int
qed_llh_add_protocol_filter(struct qed_dev *cdev,
       u8 ppfid,
       enum qed_llh_prot_filter_type_t type,
       u16 source_port_or_eth_type, u16 dest_port)
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 u8 filter_idx, abs_ppfid, str[32], type_bitmap;
 union qed_llh_filter filter = {};
 u32 high, low, ref_cnt;
 int rc = 0;

 if (!p_ptt)
  return -EAGAIN;

 if (!test_bit(QED_MF_LLH_PROTO_CLSS, &cdev->mf_bits))
  goto out;

 rc = qed_llh_protocol_filter_stringify(cdev, type,
            source_port_or_eth_type,
            dest_port, str, sizeof(str));
 if (rc)
  goto err;

 filter.protocol.type = type;
 filter.protocol.source_port_or_eth_type = source_port_or_eth_type;
 filter.protocol.dest_port = dest_port;
 rc = qed_llh_shadow_add_filter(cdev,
           ppfid,
           QED_LLH_FILTER_TYPE_PROTOCOL,
           &filter, &filter_idx, &ref_cnt);
 if (rc)
  goto err;

 rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
 if (rc)
  goto err;

 /* Configure the LLH only in case of a new the filter */
 if (ref_cnt == 1) {
  rc = qed_llh_protocol_filter_to_hilo(cdev, type,
           source_port_or_eth_type,
           dest_port, &high, &low);
  if (rc)
   goto err;

  type_bitmap = 0x1 << type;
  rc = qed_llh_add_filter(p_hwfn, p_ptt, abs_ppfid,
     filter_idx, type_bitmap, high, low);
  if (rc)
   goto err;
 }

 DP_VERBOSE(cdev,
     QED_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,
    "LLH: Failed to add protocol filter [%s] to ppfid %hhd\n",
    str, ppfid);
out:
 qed_ptt_release(p_hwfn, p_ptt);

 return rc;
}

void qed_llh_remove_mac_filter(struct qed_dev *cdev,
          u8 ppfid, u8 mac_addr[ETH_ALEN])
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 union qed_llh_filter filter = {};
 u8 filter_idx, abs_ppfid;
 int rc = 0;
 u32 ref_cnt;

 if (!p_ptt)
  return;

 if (!test_bit(QED_MF_LLH_MAC_CLSS, &cdev->mf_bits))
  goto out;

 if (QED_IS_NVMETCP_PERSONALITY(p_hwfn))
  return;

 ether_addr_copy(filter.mac.addr, mac_addr);
 rc = qed_llh_shadow_remove_filter(cdev, ppfid, &filter, &filter_idx,
       &ref_cnt);
 if (rc)
  goto err;

 rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
 if (rc)
  goto err;

 /* Remove from the LLH in case the filter is not in use */
 if (!ref_cnt) {
  rc = qed_llh_remove_filter(p_hwfn, p_ptt, abs_ppfid,
        filter_idx);
  if (rc)
   goto err;
 }

 DP_VERBOSE(cdev,
     QED_MSG_SP,
     "LLH: Removed MAC filter [%pM] from ppfid %hhd [abs %hhd] at idx %hhd [ref_cnt %d]\n",
     mac_addr, ppfid, abs_ppfid, filter_idx, ref_cnt);

 goto out;

err: DP_NOTICE(cdev,
    "LLH: Failed to remove MAC filter [%pM] from ppfid %hhd\n",
    mac_addr, ppfid);
out:
 qed_ptt_release(p_hwfn, p_ptt);
}

void qed_llh_remove_protocol_filter(struct qed_dev *cdev,
        u8 ppfid,
        enum qed_llh_prot_filter_type_t type,
        u16 source_port_or_eth_type, u16 dest_port)
{
 struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
 struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
 u8 filter_idx, abs_ppfid, str[32];
 union qed_llh_filter filter = {};
 int rc = 0;
 u32 ref_cnt;

 if (!p_ptt)
  return;

 if (!test_bit(QED_MF_LLH_PROTO_CLSS, &cdev->mf_bits))
  goto out;

 rc = qed_llh_protocol_filter_stringify(cdev, type,
            source_port_or_eth_type,
            dest_port, str, sizeof(str));
 if (rc)
  goto err;

 filter.protocol.type = type;
 filter.protocol.source_port_or_eth_type = source_port_or_eth_type;
 filter.protocol.dest_port = dest_port;
 rc = qed_llh_shadow_remove_filter(cdev, ppfid, &filter, &filter_idx,
       &ref_cnt);
 if (rc)
  goto err;

 rc = qed_llh_abs_ppfid(cdev, ppfid, &abs_ppfid);
 if (rc)
  goto err;

 /* Remove from the LLH in case the filter is not in use */
 if (!ref_cnt) {
  rc = qed_llh_remove_filter(p_hwfn, p_ptt, abs_ppfid,
        filter_idx);
  if (rc)
   goto err;
 }

 DP_VERBOSE(cdev,
     QED_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(cdev,
    "LLH: Failed to remove protocol filter [%s] from ppfid %hhd\n",
    str, ppfid);
out:
 qed_ptt_release(p_hwfn, p_ptt);
}

/******************************* NIG LLH - End ********************************/

#define QED_MIN_DPIS            (4)
#define QED_MIN_PWM_REGION      (QED_WID_SIZE * QED_MIN_DPIS)

static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn,
      struct qed_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->cdev))
  return qed_vf_hw_bar_size(p_hwfn, bar_id);

 val = qed_rd(p_hwfn, p_ptt, bar_reg);
 if (val)
  return 1 << (val + 15);

 /* Old MFW initialized above registered only conditionally */
 if (p_hwfn->cdev->num_hwfns > 1) {
  DP_INFO(p_hwfn,
   "BAR size not configured. Assuming BAR size of 256kB for GRC and 512kB for DB\n");
   return 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");
   return 512 * 1024;
 }
}

void qed_init_dp(struct qed_dev *cdev, u32 dp_module, u8 dp_level)
{
 u32 i;

 cdev->dp_level = dp_level;
 cdev->dp_module = dp_module;
 for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
  struct qed_hwfn *p_hwfn = &cdev->hwfns[i];

  p_hwfn->dp_level = dp_level;
  p_hwfn->dp_module = dp_module;
 }
}

void qed_init_struct(struct qed_dev *cdev)
{
 u8 i;

 for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
  struct qed_hwfn *p_hwfn = &cdev->hwfns[i];

  p_hwfn->cdev = cdev;
  p_hwfn->my_id = i;
  p_hwfn->b_active = false;

  mutex_init(&p_hwfn->dmae_info.mutex);
 }

 /* hwfn 0 is always active */
 cdev->hwfns[0].b_active = true;

 /* set the default cache alignment to 128 */
 cdev->cache_shift = 7;
}

static void qed_qm_info_free(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 kfree(qm_info->qm_pq_params);
 qm_info->qm_pq_params = NULL;
 kfree(qm_info->qm_vport_params);
 qm_info->qm_vport_params = NULL;
 kfree(qm_info->qm_port_params);
 qm_info->qm_port_params = NULL;
 kfree(qm_info->wfq_data);
 qm_info->wfq_data = NULL;
}

static void qed_dbg_user_data_free(struct qed_hwfn *p_hwfn)
{
 kfree(p_hwfn->dbg_user_info);
 p_hwfn->dbg_user_info = NULL;
}

void qed_resc_free(struct qed_dev *cdev)
{
 struct qed_rdma_info *rdma_info;
 struct qed_hwfn *p_hwfn;
 int i;

 if (IS_VF(cdev)) {
  for_each_hwfn(cdev, i)
   qed_l2_free(&cdev->hwfns[i]);
  return;
 }

 kfree(cdev->fw_data);
 cdev->fw_data = NULL;

 kfree(cdev->reset_stats);
 cdev->reset_stats = NULL;

 qed_llh_free(cdev);

 for_each_hwfn(cdev, i) {
  p_hwfn = cdev->hwfns + i;
  rdma_info = p_hwfn->p_rdma_info;

  qed_cxt_mngr_free(p_hwfn);
  qed_qm_info_free(p_hwfn);
  qed_spq_free(p_hwfn);
  qed_eq_free(p_hwfn);
  qed_consq_free(p_hwfn);
  qed_int_free(p_hwfn);
#ifdef CONFIG_QED_LL2
  qed_ll2_free(p_hwfn);
#endif
  if (p_hwfn->hw_info.personality == QED_PCI_FCOE)
   qed_fcoe_free(p_hwfn);

  if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
   qed_iscsi_free(p_hwfn);
   qed_ooo_free(p_hwfn);
  }

  if (p_hwfn->hw_info.personality == QED_PCI_NVMETCP) {
   qed_nvmetcp_free(p_hwfn);
   qed_ooo_free(p_hwfn);
  }

  if (QED_IS_RDMA_PERSONALITY(p_hwfn) && rdma_info) {
   qed_spq_unregister_async_cb(p_hwfn, rdma_info->proto);
   qed_rdma_info_free(p_hwfn);
  }

  qed_spq_unregister_async_cb(p_hwfn, PROTOCOLID_COMMON);
  qed_iov_free(p_hwfn);
  qed_l2_free(p_hwfn);
  qed_dmae_info_free(p_hwfn);
  qed_dcbx_info_free(p_hwfn);
  qed_dbg_user_data_free(p_hwfn);
  qed_fw_overlay_mem_free(p_hwfn, &p_hwfn->fw_overlay_mem);

  /* Destroy doorbell recovery mechanism */
  qed_db_recovery_teardown(p_hwfn);
 }
}

/******************** QM initialization *******************/
#define ACTIVE_TCS_BMAP 0x9f
#define ACTIVE_TCS_BMAP_4PORT_K2 0xf

/* determines the physical queue flags for a given PF. */
static u32 qed_get_pq_flags(struct qed_hwfn *p_hwfn)
{
 u32 flags;

 /* common flags */
 flags = PQ_FLAGS_LB;

 /* feature flags */
 if (IS_QED_SRIOV(p_hwfn->cdev))
  flags |= PQ_FLAGS_VFS;

 /* protocol flags */
 switch (p_hwfn->hw_info.personality) {
 case QED_PCI_ETH:
  flags |= PQ_FLAGS_MCOS;
  break;
 case QED_PCI_FCOE:
  flags |= PQ_FLAGS_OFLD;
  break;
 case QED_PCI_ISCSI:
 case QED_PCI_NVMETCP:
  flags |= PQ_FLAGS_ACK | PQ_FLAGS_OOO | PQ_FLAGS_OFLD;
  break;
 case QED_PCI_ETH_ROCE:
  flags |= PQ_FLAGS_MCOS | PQ_FLAGS_OFLD | PQ_FLAGS_LLT;
  if (IS_QED_MULTI_TC_ROCE(p_hwfn))
   flags |= PQ_FLAGS_MTC;
  break;
 case QED_PCI_ETH_IWARP:
  flags |= PQ_FLAGS_MCOS | PQ_FLAGS_ACK | PQ_FLAGS_OOO |
      PQ_FLAGS_OFLD;
  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 */
static u8 qed_init_qm_get_num_tcs(struct qed_hwfn *p_hwfn)
{
 return p_hwfn->hw_info.num_hw_tc;
}

static u16 qed_init_qm_get_num_vfs(struct qed_hwfn *p_hwfn)
{
 return IS_QED_SRIOV(p_hwfn->cdev) ?
        p_hwfn->cdev->p_iov_info->total_vfs : 0;
}

static u8 qed_init_qm_get_num_mtc_tcs(struct qed_hwfn *p_hwfn)
{
 u32 pq_flags = qed_get_pq_flags(p_hwfn);

 if (!(PQ_FLAGS_MTC & pq_flags))
  return 1;

 return qed_init_qm_get_num_tcs(p_hwfn);
}

#define NUM_DEFAULT_RLS 1

static u16 qed_init_qm_get_num_pf_rls(struct qed_hwfn *p_hwfn)
{
 u16 num_pf_rls, num_vfs = qed_init_qm_get_num_vfs(p_hwfn);

 /* num RLs can't exceed resource amount of rls or vports */
 num_pf_rls = (u16)min_t(u32, RESC_NUM(p_hwfn, QED_RL),
    RESC_NUM(p_hwfn, QED_VPORT));

 /* Make sure after we reserve there's something left */
 if (num_pf_rls < num_vfs + NUM_DEFAULT_RLS)
  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;
}

static u16 qed_init_qm_get_num_vports(struct qed_hwfn *p_hwfn)
{
 u32 pq_flags = qed_get_pq_flags(p_hwfn);

 /* all pqs share the same vport, except for vfs and pf_rl pqs */
 return (!!(PQ_FLAGS_RLS & pq_flags)) *
        qed_init_qm_get_num_pf_rls(p_hwfn) +
        (!!(PQ_FLAGS_VFS & pq_flags)) *
        qed_init_qm_get_num_vfs(p_hwfn) + 1;
}

/* calc amount of PQs according to the requested flags */
static u16 qed_init_qm_get_num_pqs(struct qed_hwfn *p_hwfn)
{
 u32 pq_flags = qed_get_pq_flags(p_hwfn);

 return (!!(PQ_FLAGS_RLS & pq_flags)) *
        qed_init_qm_get_num_pf_rls(p_hwfn) +
        (!!(PQ_FLAGS_MCOS & pq_flags)) *
        qed_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)) *
        qed_init_qm_get_num_mtc_tcs(p_hwfn) +
        (!!(PQ_FLAGS_LLT & pq_flags)) *
        qed_init_qm_get_num_mtc_tcs(p_hwfn) +
        (!!(PQ_FLAGS_VFS & pq_flags)) * qed_init_qm_get_num_vfs(p_hwfn);
}

/* initialize the top level QM params */
static void qed_init_qm_params(struct qed_hwfn *p_hwfn)
{
 struct qed_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, QED_PQ);
 qm_info->start_vport = (u8)RESC_START(p_hwfn, QED_VPORT);

 /* rate limiting and weighted fair queueing are always enabled */
 qm_info->vport_rl_en = true;
 qm_info->vport_wfq_en = true;

 /* TC config is different for AH 4 port */
 four_port = p_hwfn->cdev->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 == 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 qed_init_qm_vport_params(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 u8 i;

 /* all vports participate in weighted fair queueing */
 for (i = 0; i < qed_init_qm_get_num_vports(p_hwfn); i++)
  qm_info->qm_vport_params[i].wfq = 1;
}

/* initialize qm port params */
static void qed_init_qm_port_params(struct qed_hwfn *p_hwfn)
{
 /* Initialize qm port parameters */
 u8 i, active_phys_tcs, num_ports = p_hwfn->cdev->num_ports_in_engine;
 struct qed_dev *cdev = p_hwfn->cdev;

 /* 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(cdev);
  p_qm_port->num_pbf_cmd_lines = pbf_max_cmd_lines / num_ports;
  p_qm_port->num_btb_blocks = NUM_OF_BTB_BLOCKS(cdev) / 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 qed_init_qm_reset_params(struct qed_hwfn *p_hwfn)
{
 struct qed_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 qed_init_qm_advance_vport(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 qm_info->num_vports++;

 if (qm_info->num_vports > qed_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, qed_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     BIT(0)
#define PQ_INIT_PF_RL           BIT(1)
#define PQ_INIT_VF_RL           BIT(2)

/* defines for pq init */
#define PQ_INIT_DEFAULT_WRR_GROUP       1
#define PQ_INIT_DEFAULT_TC              0

void qed_hw_info_set_offload_tc(struct qed_hw_info *p_info, u8 tc)
{
 p_info->offload_tc = tc;
 p_info->offload_tc_set = true;
}

static bool qed_is_offload_tc_set(struct qed_hwfn *p_hwfn)
{
 return p_hwfn->hw_info.offload_tc_set;
}

static u32 qed_get_offload_tc(struct qed_hwfn *p_hwfn)
{
 if (qed_is_offload_tc_set(p_hwfn))
  return p_hwfn->hw_info.offload_tc;

 return PQ_INIT_DEFAULT_TC;
}

static void qed_init_qm_pq(struct qed_hwfn *p_hwfn,
      struct qed_qm_info *qm_info,
      u8 tc, u32 pq_init_flags)
{
 u16 pq_idx = qm_info->num_pqs, max_pq = qed_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);

 /* 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 > qed_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, qed_init_qm_get_num_vports(p_hwfn));

 if (qm_info->num_pf_rls > qed_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, qed_init_qm_get_num_pf_rls(p_hwfn));
}

/* get pq index according to PQ_FLAGS */
static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn,
        unsigned long pq_flags)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 /* Can't have multiple flags set here */
 if (bitmap_weight(&pq_flags,
     sizeof(pq_flags) * BITS_PER_BYTE) > 1) {
  DP_ERR(p_hwfn, "requested multiple pq flags 0x%lx\n", pq_flags);
  goto err;
 }

 if (!(qed_get_pq_flags(p_hwfn) & pq_flags)) {
  DP_ERR(p_hwfn, "pq flag 0x%lx is not set\n", pq_flags);
  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->first_ofld_pq;
 case PQ_FLAGS_LLT:
  return &qm_info->first_llt_pq;
 case PQ_FLAGS_VFS:
  return &qm_info->first_vf_pq;
 default:
  goto err;
 }

err:
 return &qm_info->start_pq;
}

/* save pq index in qm info */
static void qed_init_qm_set_idx(struct qed_hwfn *p_hwfn,
    u32 pq_flags, u16 pq_val)
{
 u16 *base_pq_idx = qed_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 qed_get_cm_pq_idx(struct qed_hwfn *p_hwfn, u32 pq_flags)
{
 u16 *base_pq_idx = qed_init_qm_get_idx_from_flags(p_hwfn, pq_flags);

 return *base_pq_idx + CM_TX_PQ_BASE;
}

u16 qed_get_cm_pq_idx_mcos(struct qed_hwfn *p_hwfn, u8 tc)
{
 u8 max_tc = qed_init_qm_get_num_tcs(p_hwfn);

 if (max_tc == 0) {
  DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n",
         PQ_FLAGS_MCOS);
  return p_hwfn->qm_info.start_pq;
 }

 if (tc > max_tc)
  DP_ERR(p_hwfn, "tc %d must be smaller than %d\n", tc, max_tc);

 return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + (tc % max_tc);
}

u16 qed_get_cm_pq_idx_vf(struct qed_hwfn *p_hwfn, u16 vf)
{
 u16 max_vf = qed_init_qm_get_num_vfs(p_hwfn);

 if (max_vf == 0) {
  DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n",
         PQ_FLAGS_VFS);
  return p_hwfn->qm_info.start_pq;
 }

 if (vf > max_vf)
  DP_ERR(p_hwfn, "vf %d must be smaller than %d\n", vf, max_vf);

 return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + (vf % max_vf);
}

u16 qed_get_cm_pq_idx_ofld_mtc(struct qed_hwfn *p_hwfn, u8 tc)
{
 u16 first_ofld_pq, pq_offset;

 first_ofld_pq = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD);
 pq_offset = (tc < qed_init_qm_get_num_mtc_tcs(p_hwfn)) ?
      tc : PQ_INIT_DEFAULT_TC;

 return first_ofld_pq + pq_offset;
}

u16 qed_get_cm_pq_idx_llt_mtc(struct qed_hwfn *p_hwfn, u8 tc)
{
 u16 first_llt_pq, pq_offset;

 first_llt_pq = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LLT);
 pq_offset = (tc < qed_init_qm_get_num_mtc_tcs(p_hwfn)) ?
      tc : PQ_INIT_DEFAULT_TC;

 return first_llt_pq + pq_offset;
}

/* Functions for creating specific types of pqs */
static void qed_init_qm_lb_pq(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_LB))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_LB, qm_info->num_pqs);
 qed_init_qm_pq(p_hwfn, qm_info, PURE_LB_TC, PQ_INIT_SHARE_VPORT);
}

static void qed_init_qm_ooo_pq(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_OOO))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_OOO, qm_info->num_pqs);
 qed_init_qm_pq(p_hwfn, qm_info, qm_info->ooo_tc, PQ_INIT_SHARE_VPORT);
}

static void qed_init_qm_pure_ack_pq(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_ACK))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_ACK, qm_info->num_pqs);
 qed_init_qm_pq(p_hwfn, qm_info, qed_get_offload_tc(p_hwfn),
         PQ_INIT_SHARE_VPORT);
}

static void qed_init_qm_mtc_pqs(struct qed_hwfn *p_hwfn)
{
 u8 num_tcs = qed_init_qm_get_num_mtc_tcs(p_hwfn);
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 u8 tc;

 /* override pq's TC if offload TC is set */
 for (tc = 0; tc < num_tcs; tc++)
  qed_init_qm_pq(p_hwfn, qm_info,
          qed_is_offload_tc_set(p_hwfn) ?
          p_hwfn->hw_info.offload_tc : tc,
          PQ_INIT_SHARE_VPORT);
}

static void qed_init_qm_offload_pq(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_OFLD))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_OFLD, qm_info->num_pqs);
 qed_init_qm_mtc_pqs(p_hwfn);
}

static void qed_init_qm_low_latency_pq(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_LLT))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_LLT, qm_info->num_pqs);
 qed_init_qm_mtc_pqs(p_hwfn);
}

static void qed_init_qm_mcos_pqs(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 u8 tc_idx;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_MCOS))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_MCOS, qm_info->num_pqs);
 for (tc_idx = 0; tc_idx < qed_init_qm_get_num_tcs(p_hwfn); tc_idx++)
  qed_init_qm_pq(p_hwfn, qm_info, tc_idx, PQ_INIT_SHARE_VPORT);
}

static void qed_init_qm_vf_pqs(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 u16 vf_idx, num_vfs = qed_init_qm_get_num_vfs(p_hwfn);

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_VFS))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_VFS, qm_info->num_pqs);
 qm_info->num_vf_pqs = num_vfs;
 for (vf_idx = 0; vf_idx < num_vfs; vf_idx++)
  qed_init_qm_pq(p_hwfn,
          qm_info, PQ_INIT_DEFAULT_TC, PQ_INIT_VF_RL);
}

static void qed_init_qm_rl_pqs(struct qed_hwfn *p_hwfn)
{
 u16 pf_rls_idx, num_pf_rls = qed_init_qm_get_num_pf_rls(p_hwfn);
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;

 if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_RLS))
  return;

 qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_RLS, qm_info->num_pqs);
 for (pf_rls_idx = 0; pf_rls_idx < num_pf_rls; pf_rls_idx++)
  qed_init_qm_pq(p_hwfn, qm_info, qed_get_offload_tc(p_hwfn),
          PQ_INIT_PF_RL);
}

static void qed_init_qm_pq_params(struct qed_hwfn *p_hwfn)
{
 /* rate limited pqs, must come first (FW assumption) */
 qed_init_qm_rl_pqs(p_hwfn);

 /* pqs for multi cos */
 qed_init_qm_mcos_pqs(p_hwfn);

 /* pure loopback pq */
 qed_init_qm_lb_pq(p_hwfn);

 /* out of order pq */
 qed_init_qm_ooo_pq(p_hwfn);

 /* pure ack pq */
 qed_init_qm_pure_ack_pq(p_hwfn);

 /* pq for offloaded protocol */
 qed_init_qm_offload_pq(p_hwfn);

 /* low latency pq */
 qed_init_qm_low_latency_pq(p_hwfn);

 /* done sharing vports */
 qed_init_qm_advance_vport(p_hwfn);

 /* pqs for vfs */
 qed_init_qm_vf_pqs(p_hwfn);
}

/* compare values of getters against resources amounts */
static int qed_init_qm_sanity(struct qed_hwfn *p_hwfn)
{
 if (qed_init_qm_get_num_vports(p_hwfn) > RESC_NUM(p_hwfn, QED_VPORT)) {
  DP_ERR(p_hwfn, "requested amount of vports exceeds resource\n");
  return -EINVAL;
 }

 if (qed_init_qm_get_num_pqs(p_hwfn) <= RESC_NUM(p_hwfn, QED_PQ))
  return 0;

 if (QED_IS_ROCE_PERSONALITY(p_hwfn)) {
  p_hwfn->hw_info.multi_tc_roce_en = false;
  DP_NOTICE(p_hwfn,
     "multi-tc roce was disabled to reduce requested amount of pqs\n");
  if (qed_init_qm_get_num_pqs(p_hwfn) <= RESC_NUM(p_hwfn, QED_PQ))
   return 0;
 }

 DP_ERR(p_hwfn, "requested amount of pqs exceeds resource\n");
 return -EINVAL;
}

static void qed_dp_init_qm_params(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 struct init_qm_vport_params *vport;
 struct init_qm_port_params *port;
 struct init_qm_pq_params *pq;
 int i, tc;

 /* top level params */
 DP_VERBOSE(p_hwfn,
     NETIF_MSG_HW,
     "qm init top level params: start_pq %d, start_vport %d, pure_lb_pq %d, offload_pq %d, llt_pq %d, pure_ack_pq %d\n",
     qm_info->start_pq,
     qm_info->start_vport,
     qm_info->pure_lb_pq,
     qm_info->first_ofld_pq,
     qm_info->first_llt_pq,
     qm_info->pure_ack_pq);
 DP_VERBOSE(p_hwfn,
     NETIF_MSG_HW,
     "ooo_pq %d, first_vf_pq %d, num_pqs %d, num_vf_pqs %d, num_vports %d, max_phys_tcs_per_port %d\n",
     qm_info->ooo_pq,
     qm_info->first_vf_pq,
     qm_info->num_pqs,
     qm_info->num_vf_pqs,
     qm_info->num_vports, qm_info->max_phys_tcs_per_port);
 DP_VERBOSE(p_hwfn,
     NETIF_MSG_HW,
     "pf_rl_en %d, pf_wfq_en %d, vport_rl_en %d, vport_wfq_en %d, pf_wfq %d, pf_rl %d, num_pf_rls %d, pq_flags %x\n",
     qm_info->pf_rl_en,
     qm_info->pf_wfq_en,
     qm_info->vport_rl_en,
     qm_info->vport_wfq_en,
     qm_info->pf_wfq,
     qm_info->pf_rl,
     qm_info->num_pf_rls, qed_get_pq_flags(p_hwfn));

 /* port table */
 for (i = 0; i < p_hwfn->cdev->num_ports_in_engine; i++) {
  port = &(qm_info->qm_port_params[i]);
  DP_VERBOSE(p_hwfn,
      NETIF_MSG_HW,
      "port idx %d, active %d, active_phys_tcs %d, num_pbf_cmd_lines %d, num_btb_blocks %d, reserved %d\n",
      i,
      port->active,
      port->active_phys_tcs,
      port->num_pbf_cmd_lines,
      port->num_btb_blocks, port->reserved);
 }

 /* vport table */
 for (i = 0; i < qm_info->num_vports; i++) {
  vport = &(qm_info->qm_vport_params[i]);
  DP_VERBOSE(p_hwfn,
      NETIF_MSG_HW,
      "vport idx %d, wfq %d, first_tx_pq_id [ ",
      qm_info->start_vport + i, vport->wfq);
  for (tc = 0; tc < NUM_OF_TCS; tc++)
   DP_VERBOSE(p_hwfn,
       NETIF_MSG_HW,
       "%d ", vport->first_tx_pq_id[tc]);
  DP_VERBOSE(p_hwfn, NETIF_MSG_HW, "]\n");
 }

 /* pq table */
 for (i = 0; i < qm_info->num_pqs; i++) {
  pq = &(qm_info->qm_pq_params[i]);
  DP_VERBOSE(p_hwfn,
      NETIF_MSG_HW,
      "pq idx %d, port %d, vport_id %d, tc %d, wrr_grp %d, rl_valid %d rl_id %d\n",
      qm_info->start_pq + i,
      pq->port_id,
      pq->vport_id,
      pq->tc_id, pq->wrr_group, pq->rl_valid, pq->rl_id);
 }
}

static void qed_init_qm_info(struct qed_hwfn *p_hwfn)
{
 /* reset params required for init run */
 qed_init_qm_reset_params(p_hwfn);

 /* init QM top level params */
 qed_init_qm_params(p_hwfn);

 /* init QM port params */
 qed_init_qm_port_params(p_hwfn);

 /* init QM vport params */
 qed_init_qm_vport_params(p_hwfn);

 /* init QM physical queue params */
 qed_init_qm_pq_params(p_hwfn);

 /* display all that init */
 qed_dp_init_qm_params(p_hwfn);
}

/* This function reconfigures the QM pf on the fly.
 * For this purpose we:
 * 1. reconfigure the QM database
 * 2. set new values to runtime array
 * 3. send an sdm_qm_cmd through the rbc interface to stop the QM
 * 4. activate init tool in QM_PF stage
 * 5. send an sdm_qm_cmd through rbc interface to release the QM
 */

int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 bool b_rc;
 int rc;

 /* initialize qed's qm data structure */
 qed_init_qm_info(p_hwfn);

 /* stop PF's qm queues */
 spin_lock_bh(&qm_lock);
 b_rc = qed_send_qm_stop_cmd(p_hwfn, p_ptt, falsetrue,
        qm_info->start_pq, qm_info->num_pqs);
 spin_unlock_bh(&qm_lock);
 if (!b_rc)
  return -EINVAL;

 /* prepare QM portion of runtime array */
 qed_qm_init_pf(p_hwfn, p_ptt, false);

 /* activate init tool on runtime array */
 rc = qed_init_run(p_hwfn, p_ptt, PHASE_QM_PF, p_hwfn->rel_pf_id,
     p_hwfn->hw_info.hw_mode);
 if (rc)
  return rc;

 /* start PF's qm queues */
 spin_lock_bh(&qm_lock);
 b_rc = qed_send_qm_stop_cmd(p_hwfn, p_ptt, truetrue,
        qm_info->start_pq, qm_info->num_pqs);
 spin_unlock_bh(&qm_lock);
 if (!b_rc)
  return -EINVAL;

 return 0;
}

static int qed_alloc_qm_data(struct qed_hwfn *p_hwfn)
{
 struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 int rc;

 rc = qed_init_qm_sanity(p_hwfn);
 if (rc)
  goto alloc_err;

 qm_info->qm_pq_params = kcalloc(qed_init_qm_get_num_pqs(p_hwfn),
     sizeof(*qm_info->qm_pq_params),
     GFP_KERNEL);
 if (!qm_info->qm_pq_params)
  goto alloc_err;

 qm_info->qm_vport_params = kcalloc(qed_init_qm_get_num_vports(p_hwfn),
        sizeof(*qm_info->qm_vport_params),
        GFP_KERNEL);
 if (!qm_info->qm_vport_params)
  goto alloc_err;

 qm_info->qm_port_params = kcalloc(p_hwfn->cdev->num_ports_in_engine,
       sizeof(*qm_info->qm_port_params),
       GFP_KERNEL);
 if (!qm_info->qm_port_params)
  goto alloc_err;

 qm_info->wfq_data = kcalloc(qed_init_qm_get_num_vports(p_hwfn),
        sizeof(*qm_info->wfq_data),
        GFP_KERNEL);
 if (!qm_info->wfq_data)
  goto alloc_err;

 return 0;

alloc_err:
 DP_NOTICE(p_hwfn, "Failed to allocate memory for QM params\n");
 qed_qm_info_free(p_hwfn);
 return -ENOMEM;
}

int qed_resc_alloc(struct qed_dev *cdev)
{
 u32 rdma_tasks, excess_tasks;
 u32 line_count;
 int i, rc = 0;

 if (IS_VF(cdev)) {
  for_each_hwfn(cdev, i) {
   rc = qed_l2_alloc(&cdev->hwfns[i]);
   if (rc)
    return rc;
  }
  return rc;
 }

 cdev->fw_data = kzalloc(sizeof(*cdev->fw_data), GFP_KERNEL);
 if (!cdev->fw_data)
  return -ENOMEM;

 for_each_hwfn(cdev, i) {
  struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
  u32 n_eqes, num_cons;

  /* Initialize the doorbell recovery mechanism */
  rc = qed_db_recovery_setup(p_hwfn);
  if (rc)
   goto alloc_err;

  /* First allocate the context manager structure */
  rc = qed_cxt_mngr_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  /* Set the HW cid/tid numbers (in the contest manager)
 * Must be done prior to any further computations.
 */

  rc = qed_cxt_set_pf_params(p_hwfn, RDMA_MAX_TIDS);
  if (rc)
   goto alloc_err;

  rc = qed_alloc_qm_data(p_hwfn);
  if (rc)
   goto alloc_err;

  /* init qm info */
  qed_init_qm_info(p_hwfn);

  /* Compute the ILT client partition */
  rc = qed_cxt_cfg_ilt_compute(p_hwfn, &line_count);
  if (rc) {
   DP_NOTICE(p_hwfn,
      "too many ILT lines; re-computing with less lines\n");
   /* In case there are not enough ILT lines we reduce the
 * number of RDMA tasks and re-compute.
 */

   excess_tasks =
       qed_cxt_cfg_ilt_compute_excess(p_hwfn, line_count);
   if (!excess_tasks)
    goto alloc_err;

   rdma_tasks = RDMA_MAX_TIDS - excess_tasks;
   rc = qed_cxt_set_pf_params(p_hwfn, rdma_tasks);
   if (rc)
    goto alloc_err;

   rc = qed_cxt_cfg_ilt_compute(p_hwfn, &line_count);
   if (rc) {
    DP_ERR(p_hwfn,
           "failed ILT compute. Requested too many lines: %u\n",
           line_count);

    goto alloc_err;
   }
  }

  /* CID map / ILT shadow table / T2
 * The table sizes are determined by the computations above
 */

  rc = qed_cxt_tables_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  /* SPQ, must follow ILT because initializes SPQ context */
  rc = qed_spq_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  /* SP status block allocation */
  p_hwfn->p_dpc_ptt = qed_get_reserved_ptt(p_hwfn,
        RESERVED_PTT_DPC);

  rc = qed_int_alloc(p_hwfn, p_hwfn->p_main_ptt);
  if (rc)
   goto alloc_err;

  rc = qed_iov_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  /* EQ */
  n_eqes = qed_chain_get_capacity(&p_hwfn->p_spq->chain);
  if (QED_IS_RDMA_PERSONALITY(p_hwfn)) {
   u32 n_srq = qed_cxt_get_total_srq_count(p_hwfn);
   enum protocol_type rdma_proto;

   if (QED_IS_ROCE_PERSONALITY(p_hwfn))
    rdma_proto = PROTOCOLID_ROCE;
   else
    rdma_proto = PROTOCOLID_IWARP;

   num_cons = qed_cxt_get_proto_cid_count(p_hwfn,
              rdma_proto,
              NULL) * 2;
   /* EQ should be able to get events from all SRQ's
 * at the same time
 */

   n_eqes += num_cons + 2 * MAX_NUM_VFS_BB + n_srq;
  } else if (p_hwfn->hw_info.personality == QED_PCI_ISCSI ||
      p_hwfn->hw_info.personality == QED_PCI_NVMETCP) {
   num_cons =
       qed_cxt_get_proto_cid_count(p_hwfn,
       PROTOCOLID_TCP_ULP,
       NULL);
   n_eqes += 2 * num_cons;
  }

  if (n_eqes > 0xFFFF) {
   DP_ERR(p_hwfn,
          "Cannot allocate 0x%x EQ elements. The maximum of a u16 chain is 0x%x\n",
          n_eqes, 0xFFFF);
   goto alloc_no_mem;
  }

  rc = qed_eq_alloc(p_hwfn, (u16)n_eqes);
  if (rc)
   goto alloc_err;

  rc = qed_consq_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  rc = qed_l2_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

#ifdef CONFIG_QED_LL2
  if (p_hwfn->using_ll2) {
   rc = qed_ll2_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
  }
#endif

  if (p_hwfn->hw_info.personality == QED_PCI_FCOE) {
   rc = qed_fcoe_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
  }

  if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
   rc = qed_iscsi_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
   rc = qed_ooo_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
  }

  if (p_hwfn->hw_info.personality == QED_PCI_NVMETCP) {
   rc = qed_nvmetcp_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
   rc = qed_ooo_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
  }

  if (QED_IS_RDMA_PERSONALITY(p_hwfn)) {
   rc = qed_rdma_info_alloc(p_hwfn);
   if (rc)
    goto alloc_err;
  }

  /* DMA info initialization */
  rc = qed_dmae_info_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  /* DCBX initialization */
  rc = qed_dcbx_info_alloc(p_hwfn);
  if (rc)
   goto alloc_err;

  rc = qed_dbg_alloc_user_data(p_hwfn, &p_hwfn->dbg_user_info);
  if (rc)
   goto alloc_err;
 }

 rc = qed_llh_alloc(cdev);
 if (rc) {
  DP_NOTICE(cdev,
     "Failed to allocate memory for the llh_info structure\n");
  goto alloc_err;
 }

 cdev->reset_stats = kzalloc(sizeof(*cdev->reset_stats), GFP_KERNEL);
 if (!cdev->reset_stats)
  goto alloc_no_mem;

 return 0;

alloc_no_mem:
 rc = -ENOMEM;
alloc_err:
 qed_resc_free(cdev);
 return rc;
}

static int qed_fw_err_handler(struct qed_hwfn *p_hwfn,
         u8 opcode,
         u16 echo,
         union event_ring_data *data, u8 fw_return_code)
{
 if (fw_return_code != COMMON_ERR_CODE_ERROR)
  goto eqe_unexpected;

 if (data->err_data.recovery_scope == ERR_SCOPE_FUNC &&
     le16_to_cpu(data->err_data.entity_id) >= MAX_NUM_PFS) {
  qed_sriov_vfpf_malicious(p_hwfn, &data->err_data);
  return 0;
 }

eqe_unexpected:
 DP_ERR(p_hwfn,
        "Skipping unexpected eqe 0x%02x, FW return code 0x%x, echo 0x%x\n",
        opcode, fw_return_code, echo);
 return -EINVAL;
}

static int qed_common_eqe_event(struct qed_hwfn *p_hwfn,
    u8 opcode,
    __le16 echo,
    union event_ring_data *data,
    u8 fw_return_code)
{
 switch (opcode) {
 case COMMON_EVENT_VF_PF_CHANNEL:
 case COMMON_EVENT_VF_FLR:
  return qed_sriov_eqe_event(p_hwfn, opcode, echo, data,
        fw_return_code);
 case COMMON_EVENT_FW_ERROR:
  return qed_fw_err_handler(p_hwfn, opcode,
       le16_to_cpu(echo), data,
       fw_return_code);
 default:
  DP_INFO(p_hwfn->cdev, "Unknown eqe event 0x%02x, echo 0x%x\n",
   opcode, echo);
  return -EINVAL;
 }
}

void qed_resc_setup(struct qed_dev *cdev)
{
 int i;

 if (IS_VF(cdev)) {
  for_each_hwfn(cdev, i)
   qed_l2_setup(&cdev->hwfns[i]);
  return;
 }

 for_each_hwfn(cdev, i) {
  struct qed_hwfn *p_hwfn = &cdev->hwfns[i];

  qed_cxt_mngr_setup(p_hwfn);
  qed_spq_setup(p_hwfn);
  qed_eq_setup(p_hwfn);
  qed_consq_setup(p_hwfn);

  /* Read shadow of current MFW mailbox */
  qed_mcp_read_mb(p_hwfn, p_hwfn->p_main_ptt);
  memcpy(p_hwfn->mcp_info->mfw_mb_shadow,
         p_hwfn->mcp_info->mfw_mb_cur,
         p_hwfn->mcp_info->mfw_mb_length);

  qed_int_setup(p_hwfn, p_hwfn->p_main_ptt);

  qed_l2_setup(p_hwfn);
  qed_iov_setup(p_hwfn);
  qed_spq_register_async_cb(p_hwfn, PROTOCOLID_COMMON,
       qed_common_eqe_event);
#ifdef CONFIG_QED_LL2
  if (p_hwfn->using_ll2)
   qed_ll2_setup(p_hwfn);
#endif
  if (p_hwfn->hw_info.personality == QED_PCI_FCOE)
   qed_fcoe_setup(p_hwfn);

  if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
   qed_iscsi_setup(p_hwfn);
   qed_ooo_setup(p_hwfn);
  }

  if (p_hwfn->hw_info.personality == QED_PCI_NVMETCP) {
   qed_nvmetcp_setup(p_hwfn);
   qed_ooo_setup(p_hwfn);
  }
 }
}

#define FINAL_CLEANUP_POLL_CNT          (100)
#define FINAL_CLEANUP_POLL_TIME         (10)
int qed_final_cleanup(struct qed_hwfn *p_hwfn,
        struct qed_ptt *p_ptt, u16 id, bool is_vf)
{
 u32 command = 0, addr, count = FINAL_CLEANUP_POLL_CNT;
 int rc = -EBUSY;

 addr = GET_GTT_REG_ADDR(GTT_BAR0_MAP_REG_USDM_RAM,
    USTORM_FLR_FINAL_ACK, p_hwfn->rel_pf_id);
 if (is_vf)
  id += 0x10;

 command |= X_FINAL_CLEANUP_AGG_INT <<
  SDM_AGG_INT_COMP_PARAMS_AGG_INT_INDEX_SHIFT;
 command |= 1 << SDM_AGG_INT_COMP_PARAMS_AGG_VECTOR_ENABLE_SHIFT;
 command |= id << SDM_AGG_INT_COMP_PARAMS_AGG_VECTOR_BIT_SHIFT;
 command |= SDM_COMP_TYPE_AGG_INT << SDM_OP_GEN_COMP_TYPE_SHIFT;

 /* Make sure notification is not set before initiating final cleanup */
 if (REG_RD(p_hwfn, addr)) {
  DP_NOTICE(p_hwfn,
     "Unexpected; Found final cleanup notification before initiating final cleanup\n");
  REG_WR(p_hwfn, addr, 0);
 }

 DP_VERBOSE(p_hwfn, QED_MSG_IOV,
     "Sending final cleanup for PFVF[%d] [Command %08x]\n",
     id, command);

 qed_wr(p_hwfn, p_ptt, XSDM_REG_OPERATION_GEN, command);

 /* Poll until completion */
 while (!REG_RD(p_hwfn, addr) && count--)
  msleep(FINAL_CLEANUP_POLL_TIME);

 if (REG_RD(p_hwfn, addr))
  rc = 0;
 else
  DP_NOTICE(p_hwfn,
     "Failed to receive FW final cleanup notification\n");

 /* Cleanup afterwards */
 REG_WR(p_hwfn, addr, 0);

 return rc;
}

static int qed_calc_hw_mode(struct qed_hwfn *p_hwfn)
{
 int hw_mode = 0;

 if (QED_IS_BB_B0(p_hwfn->cdev)) {
  hw_mode |= 1 << MODE_BB;
 } else if (QED_IS_AH(p_hwfn->cdev)) {
  hw_mode |= 1 << MODE_K2;
 } else {
  DP_NOTICE(p_hwfn, "Unknown chip type %#x\n",
     p_hwfn->cdev->type);
  return -EINVAL;
 }

 switch (p_hwfn->cdev->num_ports_in_engine) {
 case 1:
  hw_mode |= 1 << MODE_PORTS_PER_ENG_1;
  break;
 case 2:
  hw_mode |= 1 << MODE_PORTS_PER_ENG_2;
  break;
 case 4:
  hw_mode |= 1 << MODE_PORTS_PER_ENG_4;
  break;
 default:
  DP_NOTICE(p_hwfn, "num_ports_in_engine = %d not supported\n",
     p_hwfn->cdev->num_ports_in_engine);
  return -EINVAL;
 }

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=90 G=91

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge