| // 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/list.h> | 
| #include <linux/pci.h> | 
| #include <linux/slab.h> | 
| #include <linux/spinlock.h> | 
| #include <linux/string.h> | 
| #include "qed.h" | 
| #include "qed_cxt.h" | 
| #include "qed_dev_api.h" | 
| #include "qed_hsi.h" | 
| #include "qed_hw.h" | 
| #include "qed_int.h" | 
| #include "qed_iscsi.h" | 
| #include "qed_mcp.h" | 
| #include "qed_ooo.h" | 
| #include "qed_reg_addr.h" | 
| #include "qed_sp.h" | 
| #include "qed_sriov.h" | 
| #include "qed_rdma.h" | 
|   | 
| /*************************************************************************** | 
| * Structures & Definitions | 
| ***************************************************************************/ | 
|   | 
| #define SPQ_HIGH_PRI_RESERVE_DEFAULT    (1) | 
|   | 
| #define SPQ_BLOCK_DELAY_MAX_ITER        (10) | 
| #define SPQ_BLOCK_DELAY_US              (10) | 
| #define SPQ_BLOCK_SLEEP_MAX_ITER        (1000) | 
| #define SPQ_BLOCK_SLEEP_MS              (5) | 
|   | 
| /*************************************************************************** | 
| * Blocking Imp. (BLOCK/EBLOCK mode) | 
| ***************************************************************************/ | 
| static void qed_spq_blocking_cb(struct qed_hwfn *p_hwfn, | 
|                 void *cookie, | 
|                 union event_ring_data *data, u8 fw_return_code) | 
| { | 
|     struct qed_spq_comp_done *comp_done; | 
|   | 
|     comp_done = (struct qed_spq_comp_done *)cookie; | 
|   | 
|     comp_done->fw_return_code = fw_return_code; | 
|   | 
|     /* Make sure completion done is visible on waiting thread */ | 
|     smp_store_release(&comp_done->done, 0x1); | 
| } | 
|   | 
| static int __qed_spq_block(struct qed_hwfn *p_hwfn, | 
|                struct qed_spq_entry *p_ent, | 
|                u8 *p_fw_ret, bool sleep_between_iter) | 
| { | 
|     struct qed_spq_comp_done *comp_done; | 
|     u32 iter_cnt; | 
|   | 
|     comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie; | 
|     iter_cnt = sleep_between_iter ? SPQ_BLOCK_SLEEP_MAX_ITER | 
|                       : SPQ_BLOCK_DELAY_MAX_ITER; | 
|   | 
|     while (iter_cnt--) { | 
|         /* Validate we receive completion update */ | 
|         if (smp_load_acquire(&comp_done->done) == 1) { /* ^^^ */ | 
|             if (p_fw_ret) | 
|                 *p_fw_ret = comp_done->fw_return_code; | 
|             return 0; | 
|         } | 
|   | 
|         if (sleep_between_iter) | 
|             msleep(SPQ_BLOCK_SLEEP_MS); | 
|         else | 
|             udelay(SPQ_BLOCK_DELAY_US); | 
|     } | 
|   | 
|     return -EBUSY; | 
| } | 
|   | 
| static int qed_spq_block(struct qed_hwfn *p_hwfn, | 
|              struct qed_spq_entry *p_ent, | 
|              u8 *p_fw_ret, bool skip_quick_poll) | 
| { | 
|     struct qed_spq_comp_done *comp_done; | 
|     struct qed_ptt *p_ptt; | 
|     int rc; | 
|   | 
|     /* A relatively short polling period w/o sleeping, to allow the FW to | 
|      * complete the ramrod and thus possibly to avoid the following sleeps. | 
|      */ | 
|     if (!skip_quick_poll) { | 
|         rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, false); | 
|         if (!rc) | 
|             return 0; | 
|     } | 
|   | 
|     /* Move to polling with a sleeping period between iterations */ | 
|     rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true); | 
|     if (!rc) | 
|         return 0; | 
|   | 
|     p_ptt = qed_ptt_acquire(p_hwfn); | 
|     if (!p_ptt) { | 
|         DP_NOTICE(p_hwfn, "ptt, failed to acquire\n"); | 
|         return -EAGAIN; | 
|     } | 
|   | 
|     DP_INFO(p_hwfn, "Ramrod is stuck, requesting MCP drain\n"); | 
|     rc = qed_mcp_drain(p_hwfn, p_ptt); | 
|     qed_ptt_release(p_hwfn, p_ptt); | 
|     if (rc) { | 
|         DP_NOTICE(p_hwfn, "MCP drain failed\n"); | 
|         goto err; | 
|     } | 
|   | 
|     /* Retry after drain */ | 
|     rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true); | 
|     if (!rc) | 
|         return 0; | 
|   | 
|     comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie; | 
|     if (comp_done->done == 1) { | 
|         if (p_fw_ret) | 
|             *p_fw_ret = comp_done->fw_return_code; | 
|         return 0; | 
|     } | 
| err: | 
|     p_ptt = qed_ptt_acquire(p_hwfn); | 
|     if (!p_ptt) | 
|         return -EBUSY; | 
|     qed_hw_err_notify(p_hwfn, p_ptt, QED_HW_ERR_RAMROD_FAIL, | 
|               "Ramrod is stuck [CID %08x cmd %02x protocol %02x echo %04x]\n", | 
|               le32_to_cpu(p_ent->elem.hdr.cid), | 
|               p_ent->elem.hdr.cmd_id, | 
|               p_ent->elem.hdr.protocol_id, | 
|               le16_to_cpu(p_ent->elem.hdr.echo)); | 
|     qed_ptt_release(p_hwfn, p_ptt); | 
|   | 
|     return -EBUSY; | 
| } | 
|   | 
| /*************************************************************************** | 
| * SPQ entries inner API | 
| ***************************************************************************/ | 
| static int qed_spq_fill_entry(struct qed_hwfn *p_hwfn, | 
|                   struct qed_spq_entry *p_ent) | 
| { | 
|     p_ent->flags = 0; | 
|   | 
|     switch (p_ent->comp_mode) { | 
|     case QED_SPQ_MODE_EBLOCK: | 
|     case QED_SPQ_MODE_BLOCK: | 
|         p_ent->comp_cb.function = qed_spq_blocking_cb; | 
|         break; | 
|     case QED_SPQ_MODE_CB: | 
|         break; | 
|     default: | 
|         DP_NOTICE(p_hwfn, "Unknown SPQE completion mode %d\n", | 
|               p_ent->comp_mode); | 
|         return -EINVAL; | 
|     } | 
|   | 
|     DP_VERBOSE(p_hwfn, QED_MSG_SPQ, | 
|            "Ramrod header: [CID 0x%08x CMD 0x%02x protocol 0x%02x] Data pointer: [%08x:%08x] Completion Mode: %s\n", | 
|            p_ent->elem.hdr.cid, | 
|            p_ent->elem.hdr.cmd_id, | 
|            p_ent->elem.hdr.protocol_id, | 
|            p_ent->elem.data_ptr.hi, | 
|            p_ent->elem.data_ptr.lo, | 
|            D_TRINE(p_ent->comp_mode, QED_SPQ_MODE_EBLOCK, | 
|                QED_SPQ_MODE_BLOCK, "MODE_EBLOCK", "MODE_BLOCK", | 
|                "MODE_CB")); | 
|   | 
|     return 0; | 
| } | 
|   | 
| /*************************************************************************** | 
| * HSI access | 
| ***************************************************************************/ | 
| static void qed_spq_hw_initialize(struct qed_hwfn *p_hwfn, | 
|                   struct qed_spq *p_spq) | 
| { | 
|     struct e4_core_conn_context *p_cxt; | 
|     struct qed_cxt_info cxt_info; | 
|     u16 physical_q; | 
|     int rc; | 
|   | 
|     cxt_info.iid = p_spq->cid; | 
|   | 
|     rc = qed_cxt_get_cid_info(p_hwfn, &cxt_info); | 
|   | 
|     if (rc < 0) { | 
|         DP_NOTICE(p_hwfn, "Cannot find context info for cid=%d\n", | 
|               p_spq->cid); | 
|         return; | 
|     } | 
|   | 
|     p_cxt = cxt_info.p_cxt; | 
|   | 
|     SET_FIELD(p_cxt->xstorm_ag_context.flags10, | 
|           E4_XSTORM_CORE_CONN_AG_CTX_DQ_CF_EN, 1); | 
|     SET_FIELD(p_cxt->xstorm_ag_context.flags1, | 
|           E4_XSTORM_CORE_CONN_AG_CTX_DQ_CF_ACTIVE, 1); | 
|     SET_FIELD(p_cxt->xstorm_ag_context.flags9, | 
|           E4_XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_EN, 1); | 
|   | 
|     /* QM physical queue */ | 
|     physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LB); | 
|     p_cxt->xstorm_ag_context.physical_q0 = cpu_to_le16(physical_q); | 
|   | 
|     p_cxt->xstorm_st_context.spq_base_lo = | 
|         DMA_LO_LE(p_spq->chain.p_phys_addr); | 
|     p_cxt->xstorm_st_context.spq_base_hi = | 
|         DMA_HI_LE(p_spq->chain.p_phys_addr); | 
|   | 
|     DMA_REGPAIR_LE(p_cxt->xstorm_st_context.consolid_base_addr, | 
|                p_hwfn->p_consq->chain.p_phys_addr); | 
| } | 
|   | 
| static int qed_spq_hw_post(struct qed_hwfn *p_hwfn, | 
|                struct qed_spq *p_spq, struct qed_spq_entry *p_ent) | 
| { | 
|     struct qed_chain *p_chain = &p_hwfn->p_spq->chain; | 
|     struct core_db_data *p_db_data = &p_spq->db_data; | 
|     u16 echo = qed_chain_get_prod_idx(p_chain); | 
|     struct slow_path_element    *elem; | 
|   | 
|     p_ent->elem.hdr.echo    = cpu_to_le16(echo); | 
|     elem = qed_chain_produce(p_chain); | 
|     if (!elem) { | 
|         DP_NOTICE(p_hwfn, "Failed to produce from SPQ chain\n"); | 
|         return -EINVAL; | 
|     } | 
|   | 
|     *elem = p_ent->elem; /* struct assignment */ | 
|   | 
|     /* send a doorbell on the slow hwfn session */ | 
|     p_db_data->spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain)); | 
|   | 
|     /* make sure the SPQE is updated before the doorbell */ | 
|     wmb(); | 
|   | 
|     DOORBELL(p_hwfn, p_spq->db_addr_offset, *(u32 *)p_db_data); | 
|   | 
|     /* make sure doorbell is rang */ | 
|     wmb(); | 
|   | 
|     DP_VERBOSE(p_hwfn, QED_MSG_SPQ, | 
|            "Doorbelled [0x%08x, CID 0x%08x] with Flags: %02x agg_params: %02x, prod: %04x\n", | 
|            p_spq->db_addr_offset, | 
|            p_spq->cid, | 
|            p_db_data->params, | 
|            p_db_data->agg_flags, qed_chain_get_prod_idx(p_chain)); | 
|   | 
|     return 0; | 
| } | 
|   | 
| /*************************************************************************** | 
| * Asynchronous events | 
| ***************************************************************************/ | 
| static int | 
| qed_async_event_completion(struct qed_hwfn *p_hwfn, | 
|                struct event_ring_entry *p_eqe) | 
| { | 
|     qed_spq_async_comp_cb cb; | 
|   | 
|     if (!p_hwfn->p_spq || (p_eqe->protocol_id >= MAX_PROTOCOL_TYPE)) | 
|         return -EINVAL; | 
|   | 
|     cb = p_hwfn->p_spq->async_comp_cb[p_eqe->protocol_id]; | 
|     if (cb) { | 
|         return cb(p_hwfn, p_eqe->opcode, p_eqe->echo, | 
|               &p_eqe->data, p_eqe->fw_return_code); | 
|     } else { | 
|         DP_NOTICE(p_hwfn, | 
|               "Unknown Async completion for protocol: %d\n", | 
|               p_eqe->protocol_id); | 
|         return -EINVAL; | 
|     } | 
| } | 
|   | 
| int | 
| qed_spq_register_async_cb(struct qed_hwfn *p_hwfn, | 
|               enum protocol_type protocol_id, | 
|               qed_spq_async_comp_cb cb) | 
| { | 
|     if (!p_hwfn->p_spq || (protocol_id >= MAX_PROTOCOL_TYPE)) | 
|         return -EINVAL; | 
|   | 
|     p_hwfn->p_spq->async_comp_cb[protocol_id] = cb; | 
|     return 0; | 
| } | 
|   | 
| void | 
| qed_spq_unregister_async_cb(struct qed_hwfn *p_hwfn, | 
|                 enum protocol_type protocol_id) | 
| { | 
|     if (!p_hwfn->p_spq || (protocol_id >= MAX_PROTOCOL_TYPE)) | 
|         return; | 
|   | 
|     p_hwfn->p_spq->async_comp_cb[protocol_id] = NULL; | 
| } | 
|   | 
| /*************************************************************************** | 
| * EQ API | 
| ***************************************************************************/ | 
| void qed_eq_prod_update(struct qed_hwfn *p_hwfn, u16 prod) | 
| { | 
|     u32 addr = GTT_BAR0_MAP_REG_USDM_RAM + | 
|            USTORM_EQE_CONS_OFFSET(p_hwfn->rel_pf_id); | 
|   | 
|     REG_WR16(p_hwfn, addr, prod); | 
| } | 
|   | 
| int qed_eq_completion(struct qed_hwfn *p_hwfn, void *cookie) | 
| { | 
|     struct qed_eq *p_eq = cookie; | 
|     struct qed_chain *p_chain = &p_eq->chain; | 
|     int rc = 0; | 
|   | 
|     /* take a snapshot of the FW consumer */ | 
|     u16 fw_cons_idx = le16_to_cpu(*p_eq->p_fw_cons); | 
|   | 
|     DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "fw_cons_idx %x\n", fw_cons_idx); | 
|   | 
|     /* Need to guarantee the fw_cons index we use points to a usuable | 
|      * element (to comply with our chain), so our macros would comply | 
|      */ | 
|     if ((fw_cons_idx & qed_chain_get_usable_per_page(p_chain)) == | 
|         qed_chain_get_usable_per_page(p_chain)) | 
|         fw_cons_idx += qed_chain_get_unusable_per_page(p_chain); | 
|   | 
|     /* Complete current segment of eq entries */ | 
|     while (fw_cons_idx != qed_chain_get_cons_idx(p_chain)) { | 
|         struct event_ring_entry *p_eqe = qed_chain_consume(p_chain); | 
|   | 
|         if (!p_eqe) { | 
|             rc = -EINVAL; | 
|             break; | 
|         } | 
|   | 
|         DP_VERBOSE(p_hwfn, QED_MSG_SPQ, | 
|                "op %x prot %x res0 %x echo %x fwret %x flags %x\n", | 
|                p_eqe->opcode, | 
|                p_eqe->protocol_id, | 
|                p_eqe->reserved0, | 
|                le16_to_cpu(p_eqe->echo), | 
|                p_eqe->fw_return_code, | 
|                p_eqe->flags); | 
|   | 
|         if (GET_FIELD(p_eqe->flags, EVENT_RING_ENTRY_ASYNC)) { | 
|             if (qed_async_event_completion(p_hwfn, p_eqe)) | 
|                 rc = -EINVAL; | 
|         } else if (qed_spq_completion(p_hwfn, | 
|                           p_eqe->echo, | 
|                           p_eqe->fw_return_code, | 
|                           &p_eqe->data)) { | 
|             rc = -EINVAL; | 
|         } | 
|   | 
|         qed_chain_recycle_consumed(p_chain); | 
|     } | 
|   | 
|     qed_eq_prod_update(p_hwfn, qed_chain_get_prod_idx(p_chain)); | 
|   | 
|     /* Attempt to post pending requests */ | 
|     spin_lock_bh(&p_hwfn->p_spq->lock); | 
|     rc = qed_spq_pend_post(p_hwfn); | 
|     spin_unlock_bh(&p_hwfn->p_spq->lock); | 
|   | 
|     return rc; | 
| } | 
|   | 
| int qed_eq_alloc(struct qed_hwfn *p_hwfn, u16 num_elem) | 
| { | 
|     struct qed_chain_init_params params = { | 
|         .mode        = QED_CHAIN_MODE_PBL, | 
|         .intended_use    = QED_CHAIN_USE_TO_PRODUCE, | 
|         .cnt_type    = QED_CHAIN_CNT_TYPE_U16, | 
|         .num_elems    = num_elem, | 
|         .elem_size    = sizeof(union event_ring_element), | 
|     }; | 
|     struct qed_eq *p_eq; | 
|     int ret; | 
|   | 
|     /* Allocate EQ struct */ | 
|     p_eq = kzalloc(sizeof(*p_eq), GFP_KERNEL); | 
|     if (!p_eq) | 
|         return -ENOMEM; | 
|   | 
|     ret = qed_chain_alloc(p_hwfn->cdev, &p_eq->chain, ¶ms); | 
|     if (ret) { | 
|         DP_NOTICE(p_hwfn, "Failed to allocate EQ chain\n"); | 
|         goto eq_allocate_fail; | 
|     } | 
|   | 
|     /* register EQ completion on the SP SB */ | 
|     qed_int_register_cb(p_hwfn, qed_eq_completion, | 
|                 p_eq, &p_eq->eq_sb_index, &p_eq->p_fw_cons); | 
|   | 
|     p_hwfn->p_eq = p_eq; | 
|     return 0; | 
|   | 
| eq_allocate_fail: | 
|     kfree(p_eq); | 
|   | 
|     return ret; | 
| } | 
|   | 
| void qed_eq_setup(struct qed_hwfn *p_hwfn) | 
| { | 
|     qed_chain_reset(&p_hwfn->p_eq->chain); | 
| } | 
|   | 
| void qed_eq_free(struct qed_hwfn *p_hwfn) | 
| { | 
|     if (!p_hwfn->p_eq) | 
|         return; | 
|   | 
|     qed_chain_free(p_hwfn->cdev, &p_hwfn->p_eq->chain); | 
|   | 
|     kfree(p_hwfn->p_eq); | 
|     p_hwfn->p_eq = NULL; | 
| } | 
|   | 
| /*************************************************************************** | 
| * CQE API - manipulate EQ functionality | 
| ***************************************************************************/ | 
| static int qed_cqe_completion(struct qed_hwfn *p_hwfn, | 
|                   struct eth_slow_path_rx_cqe *cqe, | 
|                   enum protocol_type protocol) | 
| { | 
|     if (IS_VF(p_hwfn->cdev)) | 
|         return 0; | 
|   | 
|     /* @@@tmp - it's possible we'll eventually want to handle some | 
|      * actual commands that can arrive here, but for now this is only | 
|      * used to complete the ramrod using the echo value on the cqe | 
|      */ | 
|     return qed_spq_completion(p_hwfn, cqe->echo, 0, NULL); | 
| } | 
|   | 
| int qed_eth_cqe_completion(struct qed_hwfn *p_hwfn, | 
|                struct eth_slow_path_rx_cqe *cqe) | 
| { | 
|     int rc; | 
|   | 
|     rc = qed_cqe_completion(p_hwfn, cqe, PROTOCOLID_ETH); | 
|     if (rc) | 
|         DP_NOTICE(p_hwfn, | 
|               "Failed to handle RXQ CQE [cmd 0x%02x]\n", | 
|               cqe->ramrod_cmd_id); | 
|   | 
|     return rc; | 
| } | 
|   | 
| /*************************************************************************** | 
| * Slow hwfn Queue (spq) | 
| ***************************************************************************/ | 
| void qed_spq_setup(struct qed_hwfn *p_hwfn) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|     struct qed_spq_entry *p_virt = NULL; | 
|     struct core_db_data *p_db_data; | 
|     void __iomem *db_addr; | 
|     dma_addr_t p_phys = 0; | 
|     u32 i, capacity; | 
|     int rc; | 
|   | 
|     INIT_LIST_HEAD(&p_spq->pending); | 
|     INIT_LIST_HEAD(&p_spq->completion_pending); | 
|     INIT_LIST_HEAD(&p_spq->free_pool); | 
|     INIT_LIST_HEAD(&p_spq->unlimited_pending); | 
|     spin_lock_init(&p_spq->lock); | 
|   | 
|     /* SPQ empty pool */ | 
|     p_phys    = p_spq->p_phys + offsetof(struct qed_spq_entry, ramrod); | 
|     p_virt    = p_spq->p_virt; | 
|   | 
|     capacity = qed_chain_get_capacity(&p_spq->chain); | 
|     for (i = 0; i < capacity; i++) { | 
|         DMA_REGPAIR_LE(p_virt->elem.data_ptr, p_phys); | 
|   | 
|         list_add_tail(&p_virt->list, &p_spq->free_pool); | 
|   | 
|         p_virt++; | 
|         p_phys += sizeof(struct qed_spq_entry); | 
|     } | 
|   | 
|     /* Statistics */ | 
|     p_spq->normal_count        = 0; | 
|     p_spq->comp_count        = 0; | 
|     p_spq->comp_sent_count        = 0; | 
|     p_spq->unlimited_pending_count    = 0; | 
|   | 
|     bitmap_zero(p_spq->p_comp_bitmap, SPQ_RING_SIZE); | 
|     p_spq->comp_bitmap_idx = 0; | 
|   | 
|     /* SPQ cid, cannot fail */ | 
|     qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_spq->cid); | 
|     qed_spq_hw_initialize(p_hwfn, p_spq); | 
|   | 
|     /* reset the chain itself */ | 
|     qed_chain_reset(&p_spq->chain); | 
|   | 
|     /* Initialize the address/data of the SPQ doorbell */ | 
|     p_spq->db_addr_offset = qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY); | 
|     p_db_data = &p_spq->db_data; | 
|     memset(p_db_data, 0, sizeof(*p_db_data)); | 
|     SET_FIELD(p_db_data->params, CORE_DB_DATA_DEST, DB_DEST_XCM); | 
|     SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_MAX); | 
|     SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_VAL_SEL, | 
|           DQ_XCM_CORE_SPQ_PROD_CMD); | 
|     p_db_data->agg_flags = DQ_XCM_CORE_DQ_CF_CMD; | 
|   | 
|     /* Register the SPQ doorbell with the doorbell recovery mechanism */ | 
|     db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells + | 
|                    p_spq->db_addr_offset); | 
|     rc = qed_db_recovery_add(p_hwfn->cdev, db_addr, &p_spq->db_data, | 
|                  DB_REC_WIDTH_32B, DB_REC_KERNEL); | 
|     if (rc) | 
|         DP_INFO(p_hwfn, | 
|             "Failed to register the SPQ doorbell with the doorbell recovery mechanism\n"); | 
| } | 
|   | 
| int qed_spq_alloc(struct qed_hwfn *p_hwfn) | 
| { | 
|     struct qed_chain_init_params params = { | 
|         .mode        = QED_CHAIN_MODE_SINGLE, | 
|         .intended_use    = QED_CHAIN_USE_TO_PRODUCE, | 
|         .cnt_type    = QED_CHAIN_CNT_TYPE_U16, | 
|         .elem_size    = sizeof(struct slow_path_element), | 
|     }; | 
|     struct qed_dev *cdev = p_hwfn->cdev; | 
|     struct qed_spq_entry *p_virt = NULL; | 
|     struct qed_spq *p_spq = NULL; | 
|     dma_addr_t p_phys = 0; | 
|     u32 capacity; | 
|     int ret; | 
|   | 
|     /* SPQ struct */ | 
|     p_spq = kzalloc(sizeof(struct qed_spq), GFP_KERNEL); | 
|     if (!p_spq) | 
|         return -ENOMEM; | 
|   | 
|     /* SPQ ring */ | 
|     ret = qed_chain_alloc(cdev, &p_spq->chain, ¶ms); | 
|     if (ret) { | 
|         DP_NOTICE(p_hwfn, "Failed to allocate SPQ chain\n"); | 
|         goto spq_chain_alloc_fail; | 
|     } | 
|   | 
|     /* allocate and fill the SPQ elements (incl. ramrod data list) */ | 
|     capacity = qed_chain_get_capacity(&p_spq->chain); | 
|     ret = -ENOMEM; | 
|   | 
|     p_virt = dma_alloc_coherent(&cdev->pdev->dev, | 
|                     capacity * sizeof(struct qed_spq_entry), | 
|                     &p_phys, GFP_KERNEL); | 
|     if (!p_virt) | 
|         goto spq_alloc_fail; | 
|   | 
|     p_spq->p_virt = p_virt; | 
|     p_spq->p_phys = p_phys; | 
|     p_hwfn->p_spq = p_spq; | 
|   | 
|     return 0; | 
|   | 
| spq_alloc_fail: | 
|     qed_chain_free(cdev, &p_spq->chain); | 
| spq_chain_alloc_fail: | 
|     kfree(p_spq); | 
|   | 
|     return ret; | 
| } | 
|   | 
| void qed_spq_free(struct qed_hwfn *p_hwfn) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|     void __iomem *db_addr; | 
|     u32 capacity; | 
|   | 
|     if (!p_spq) | 
|         return; | 
|   | 
|     /* Delete the SPQ doorbell from the doorbell recovery mechanism */ | 
|     db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells + | 
|                    p_spq->db_addr_offset); | 
|     qed_db_recovery_del(p_hwfn->cdev, db_addr, &p_spq->db_data); | 
|   | 
|     if (p_spq->p_virt) { | 
|         capacity = qed_chain_get_capacity(&p_spq->chain); | 
|         dma_free_coherent(&p_hwfn->cdev->pdev->dev, | 
|                   capacity * | 
|                   sizeof(struct qed_spq_entry), | 
|                   p_spq->p_virt, p_spq->p_phys); | 
|     } | 
|   | 
|     qed_chain_free(p_hwfn->cdev, &p_spq->chain); | 
|     kfree(p_spq); | 
|     p_hwfn->p_spq = NULL; | 
| } | 
|   | 
| int qed_spq_get_entry(struct qed_hwfn *p_hwfn, struct qed_spq_entry **pp_ent) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|     struct qed_spq_entry *p_ent = NULL; | 
|     int rc = 0; | 
|   | 
|     spin_lock_bh(&p_spq->lock); | 
|   | 
|     if (list_empty(&p_spq->free_pool)) { | 
|         p_ent = kzalloc(sizeof(*p_ent), GFP_ATOMIC); | 
|         if (!p_ent) { | 
|             DP_NOTICE(p_hwfn, | 
|                   "Failed to allocate an SPQ entry for a pending ramrod\n"); | 
|             rc = -ENOMEM; | 
|             goto out_unlock; | 
|         } | 
|         p_ent->queue = &p_spq->unlimited_pending; | 
|     } else { | 
|         p_ent = list_first_entry(&p_spq->free_pool, | 
|                      struct qed_spq_entry, list); | 
|         list_del(&p_ent->list); | 
|         p_ent->queue = &p_spq->pending; | 
|     } | 
|   | 
|     *pp_ent = p_ent; | 
|   | 
| out_unlock: | 
|     spin_unlock_bh(&p_spq->lock); | 
|     return rc; | 
| } | 
|   | 
| /* Locked variant; Should be called while the SPQ lock is taken */ | 
| static void __qed_spq_return_entry(struct qed_hwfn *p_hwfn, | 
|                    struct qed_spq_entry *p_ent) | 
| { | 
|     list_add_tail(&p_ent->list, &p_hwfn->p_spq->free_pool); | 
| } | 
|   | 
| void qed_spq_return_entry(struct qed_hwfn *p_hwfn, struct qed_spq_entry *p_ent) | 
| { | 
|     spin_lock_bh(&p_hwfn->p_spq->lock); | 
|     __qed_spq_return_entry(p_hwfn, p_ent); | 
|     spin_unlock_bh(&p_hwfn->p_spq->lock); | 
| } | 
|   | 
| /** | 
|  * qed_spq_add_entry() - Add a new entry to the pending list. | 
|  *                       Should be used while lock is being held. | 
|  * | 
|  * @p_hwfn: HW device data. | 
|  * @p_ent: An entry to add. | 
|  * @priority: Desired priority. | 
|  * | 
|  * Adds an entry to the pending list is there is room (an empty | 
|  * element is available in the free_pool), or else places the | 
|  * entry in the unlimited_pending pool. | 
|  * | 
|  * Return: zero on success, -EINVAL on invalid @priority. | 
|  */ | 
| static int qed_spq_add_entry(struct qed_hwfn *p_hwfn, | 
|                  struct qed_spq_entry *p_ent, | 
|                  enum spq_priority priority) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|   | 
|     if (p_ent->queue == &p_spq->unlimited_pending) { | 
|   | 
|         if (list_empty(&p_spq->free_pool)) { | 
|             list_add_tail(&p_ent->list, &p_spq->unlimited_pending); | 
|             p_spq->unlimited_pending_count++; | 
|   | 
|             return 0; | 
|         } else { | 
|             struct qed_spq_entry *p_en2; | 
|   | 
|             p_en2 = list_first_entry(&p_spq->free_pool, | 
|                          struct qed_spq_entry, list); | 
|             list_del(&p_en2->list); | 
|   | 
|             /* Copy the ring element physical pointer to the new | 
|              * entry, since we are about to override the entire ring | 
|              * entry and don't want to lose the pointer. | 
|              */ | 
|             p_ent->elem.data_ptr = p_en2->elem.data_ptr; | 
|   | 
|             *p_en2 = *p_ent; | 
|   | 
|             /* EBLOCK responsible to free the allocated p_ent */ | 
|             if (p_ent->comp_mode != QED_SPQ_MODE_EBLOCK) | 
|                 kfree(p_ent); | 
|             else | 
|                 p_ent->post_ent = p_en2; | 
|   | 
|             p_ent = p_en2; | 
|         } | 
|     } | 
|   | 
|     /* entry is to be placed in 'pending' queue */ | 
|     switch (priority) { | 
|     case QED_SPQ_PRIORITY_NORMAL: | 
|         list_add_tail(&p_ent->list, &p_spq->pending); | 
|         p_spq->normal_count++; | 
|         break; | 
|     case QED_SPQ_PRIORITY_HIGH: | 
|         list_add(&p_ent->list, &p_spq->pending); | 
|         p_spq->high_count++; | 
|         break; | 
|     default: | 
|         return -EINVAL; | 
|     } | 
|   | 
|     return 0; | 
| } | 
|   | 
| /*************************************************************************** | 
| * Accessor | 
| ***************************************************************************/ | 
| u32 qed_spq_get_cid(struct qed_hwfn *p_hwfn) | 
| { | 
|     if (!p_hwfn->p_spq) | 
|         return 0xffffffff;      /* illegal */ | 
|     return p_hwfn->p_spq->cid; | 
| } | 
|   | 
| /*************************************************************************** | 
| * Posting new Ramrods | 
| ***************************************************************************/ | 
| static int qed_spq_post_list(struct qed_hwfn *p_hwfn, | 
|                  struct list_head *head, u32 keep_reserve) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|     int rc; | 
|   | 
|     while (qed_chain_get_elem_left(&p_spq->chain) > keep_reserve && | 
|            !list_empty(head)) { | 
|         struct qed_spq_entry *p_ent = | 
|             list_first_entry(head, struct qed_spq_entry, list); | 
|         list_move_tail(&p_ent->list, &p_spq->completion_pending); | 
|         p_spq->comp_sent_count++; | 
|   | 
|         rc = qed_spq_hw_post(p_hwfn, p_spq, p_ent); | 
|         if (rc) { | 
|             list_del(&p_ent->list); | 
|             __qed_spq_return_entry(p_hwfn, p_ent); | 
|             return rc; | 
|         } | 
|     } | 
|   | 
|     return 0; | 
| } | 
|   | 
| int qed_spq_pend_post(struct qed_hwfn *p_hwfn) | 
| { | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|     struct qed_spq_entry *p_ent = NULL; | 
|   | 
|     while (!list_empty(&p_spq->free_pool)) { | 
|         if (list_empty(&p_spq->unlimited_pending)) | 
|             break; | 
|   | 
|         p_ent = list_first_entry(&p_spq->unlimited_pending, | 
|                      struct qed_spq_entry, list); | 
|         if (!p_ent) | 
|             return -EINVAL; | 
|   | 
|         list_del(&p_ent->list); | 
|   | 
|         qed_spq_add_entry(p_hwfn, p_ent, p_ent->priority); | 
|     } | 
|   | 
|     return qed_spq_post_list(p_hwfn, &p_spq->pending, | 
|                  SPQ_HIGH_PRI_RESERVE_DEFAULT); | 
| } | 
|   | 
| static void qed_spq_recov_set_ret_code(struct qed_spq_entry *p_ent, | 
|                        u8 *fw_return_code) | 
| { | 
|     if (!fw_return_code) | 
|         return; | 
|   | 
|     if (p_ent->elem.hdr.protocol_id == PROTOCOLID_ROCE || | 
|         p_ent->elem.hdr.protocol_id == PROTOCOLID_IWARP) | 
|         *fw_return_code = RDMA_RETURN_OK; | 
| } | 
|   | 
| /* Avoid overriding of SPQ entries when getting out-of-order completions, by | 
|  * marking the completions in a bitmap and increasing the chain consumer only | 
|  * for the first successive completed entries. | 
|  */ | 
| static void qed_spq_comp_bmap_update(struct qed_hwfn *p_hwfn, __le16 echo) | 
| { | 
|     u16 pos = le16_to_cpu(echo) % SPQ_RING_SIZE; | 
|     struct qed_spq *p_spq = p_hwfn->p_spq; | 
|   | 
|     __set_bit(pos, p_spq->p_comp_bitmap); | 
|     while (test_bit(p_spq->comp_bitmap_idx, | 
|             p_spq->p_comp_bitmap)) { | 
|         __clear_bit(p_spq->comp_bitmap_idx, | 
|                 p_spq->p_comp_bitmap); | 
|         p_spq->comp_bitmap_idx++; | 
|         qed_chain_return_produced(&p_spq->chain); | 
|     } | 
| } | 
|   | 
| int qed_spq_post(struct qed_hwfn *p_hwfn, | 
|          struct qed_spq_entry *p_ent, u8 *fw_return_code) | 
| { | 
|     int rc = 0; | 
|     struct qed_spq *p_spq = p_hwfn ? p_hwfn->p_spq : NULL; | 
|     bool b_ret_ent = true; | 
|     bool eblock; | 
|   | 
|     if (!p_hwfn) | 
|         return -EINVAL; | 
|   | 
|     if (!p_ent) { | 
|         DP_NOTICE(p_hwfn, "Got a NULL pointer\n"); | 
|         return -EINVAL; | 
|     } | 
|   | 
|     if (p_hwfn->cdev->recov_in_prog) { | 
|         DP_VERBOSE(p_hwfn, | 
|                QED_MSG_SPQ, | 
|                "Recovery is in progress. Skip spq post [cmd %02x protocol %02x]\n", | 
|                p_ent->elem.hdr.cmd_id, p_ent->elem.hdr.protocol_id); | 
|   | 
|         /* Let the flow complete w/o any error handling */ | 
|         qed_spq_recov_set_ret_code(p_ent, fw_return_code); | 
|         return 0; | 
|     } | 
|   | 
|     /* Complete the entry */ | 
|     rc = qed_spq_fill_entry(p_hwfn, p_ent); | 
|   | 
|     spin_lock_bh(&p_spq->lock); | 
|   | 
|     /* Check return value after LOCK is taken for cleaner error flow */ | 
|     if (rc) | 
|         goto spq_post_fail; | 
|   | 
|     /* Check if entry is in block mode before qed_spq_add_entry, | 
|      * which might kfree p_ent. | 
|      */ | 
|     eblock = (p_ent->comp_mode == QED_SPQ_MODE_EBLOCK); | 
|   | 
|     /* Add the request to the pending queue */ | 
|     rc = qed_spq_add_entry(p_hwfn, p_ent, p_ent->priority); | 
|     if (rc) | 
|         goto spq_post_fail; | 
|   | 
|     rc = qed_spq_pend_post(p_hwfn); | 
|     if (rc) { | 
|         /* Since it's possible that pending failed for a different | 
|          * entry [although unlikely], the failed entry was already | 
|          * dealt with; No need to return it here. | 
|          */ | 
|         b_ret_ent = false; | 
|         goto spq_post_fail; | 
|     } | 
|   | 
|     spin_unlock_bh(&p_spq->lock); | 
|   | 
|     if (eblock) { | 
|         /* For entries in QED BLOCK mode, the completion code cannot | 
|          * perform the necessary cleanup - if it did, we couldn't | 
|          * access p_ent here to see whether it's successful or not. | 
|          * Thus, after gaining the answer perform the cleanup here. | 
|          */ | 
|         rc = qed_spq_block(p_hwfn, p_ent, fw_return_code, | 
|                    p_ent->queue == &p_spq->unlimited_pending); | 
|   | 
|         if (p_ent->queue == &p_spq->unlimited_pending) { | 
|             struct qed_spq_entry *p_post_ent = p_ent->post_ent; | 
|   | 
|             kfree(p_ent); | 
|   | 
|             /* Return the entry which was actually posted */ | 
|             p_ent = p_post_ent; | 
|         } | 
|   | 
|         if (rc) | 
|             goto spq_post_fail2; | 
|   | 
|         /* return to pool */ | 
|         qed_spq_return_entry(p_hwfn, p_ent); | 
|     } | 
|     return rc; | 
|   | 
| spq_post_fail2: | 
|     spin_lock_bh(&p_spq->lock); | 
|     list_del(&p_ent->list); | 
|     qed_spq_comp_bmap_update(p_hwfn, p_ent->elem.hdr.echo); | 
|   | 
| spq_post_fail: | 
|     /* return to the free pool */ | 
|     if (b_ret_ent) | 
|         __qed_spq_return_entry(p_hwfn, p_ent); | 
|     spin_unlock_bh(&p_spq->lock); | 
|   | 
|     return rc; | 
| } | 
|   | 
| int qed_spq_completion(struct qed_hwfn *p_hwfn, | 
|                __le16 echo, | 
|                u8 fw_return_code, | 
|                union event_ring_data *p_data) | 
| { | 
|     struct qed_spq        *p_spq; | 
|     struct qed_spq_entry    *p_ent = NULL; | 
|     struct qed_spq_entry    *tmp; | 
|     struct qed_spq_entry    *found = NULL; | 
|   | 
|     if (!p_hwfn) | 
|         return -EINVAL; | 
|   | 
|     p_spq = p_hwfn->p_spq; | 
|     if (!p_spq) | 
|         return -EINVAL; | 
|   | 
|     spin_lock_bh(&p_spq->lock); | 
|     list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending, list) { | 
|         if (p_ent->elem.hdr.echo == echo) { | 
|             list_del(&p_ent->list); | 
|             qed_spq_comp_bmap_update(p_hwfn, echo); | 
|             p_spq->comp_count++; | 
|             found = p_ent; | 
|             break; | 
|         } | 
|   | 
|         /* This is relatively uncommon - depends on scenarios | 
|          * which have mutliple per-PF sent ramrods. | 
|          */ | 
|         DP_VERBOSE(p_hwfn, QED_MSG_SPQ, | 
|                "Got completion for echo %04x - doesn't match echo %04x in completion pending list\n", | 
|                le16_to_cpu(echo), | 
|                le16_to_cpu(p_ent->elem.hdr.echo)); | 
|     } | 
|   | 
|     /* Release lock before callback, as callback may post | 
|      * an additional ramrod. | 
|      */ | 
|     spin_unlock_bh(&p_spq->lock); | 
|   | 
|     if (!found) { | 
|         DP_NOTICE(p_hwfn, | 
|               "Failed to find an entry this EQE [echo %04x] completes\n", | 
|               le16_to_cpu(echo)); | 
|         return -EEXIST; | 
|     } | 
|   | 
|     DP_VERBOSE(p_hwfn, QED_MSG_SPQ, | 
|            "Complete EQE [echo %04x]: func %p cookie %p)\n", | 
|            le16_to_cpu(echo), | 
|            p_ent->comp_cb.function, p_ent->comp_cb.cookie); | 
|     if (found->comp_cb.function) | 
|         found->comp_cb.function(p_hwfn, found->comp_cb.cookie, p_data, | 
|                     fw_return_code); | 
|     else | 
|         DP_VERBOSE(p_hwfn, | 
|                QED_MSG_SPQ, | 
|                "Got a completion without a callback function\n"); | 
|   | 
|     if (found->comp_mode != QED_SPQ_MODE_EBLOCK) | 
|         /* EBLOCK  is responsible for returning its own entry into the | 
|          * free list. | 
|          */ | 
|         qed_spq_return_entry(p_hwfn, found); | 
|   | 
|     return 0; | 
| } | 
|   | 
| #define QED_SPQ_CONSQ_ELEM_SIZE        0x80 | 
|   | 
| int qed_consq_alloc(struct qed_hwfn *p_hwfn) | 
| { | 
|     struct qed_chain_init_params params = { | 
|         .mode        = QED_CHAIN_MODE_PBL, | 
|         .intended_use    = QED_CHAIN_USE_TO_PRODUCE, | 
|         .cnt_type    = QED_CHAIN_CNT_TYPE_U16, | 
|         .num_elems    = QED_CHAIN_PAGE_SIZE / QED_SPQ_CONSQ_ELEM_SIZE, | 
|         .elem_size    = QED_SPQ_CONSQ_ELEM_SIZE, | 
|     }; | 
|     struct qed_consq *p_consq; | 
|     int ret; | 
|   | 
|     /* Allocate ConsQ struct */ | 
|     p_consq = kzalloc(sizeof(*p_consq), GFP_KERNEL); | 
|     if (!p_consq) | 
|         return -ENOMEM; | 
|   | 
|     /* Allocate and initialize ConsQ chain */ | 
|     ret = qed_chain_alloc(p_hwfn->cdev, &p_consq->chain, ¶ms); | 
|     if (ret) { | 
|         DP_NOTICE(p_hwfn, "Failed to allocate ConsQ chain"); | 
|         goto consq_alloc_fail; | 
|     } | 
|   | 
|     p_hwfn->p_consq = p_consq; | 
|   | 
|     return 0; | 
|   | 
| consq_alloc_fail: | 
|     kfree(p_consq); | 
|   | 
|     return ret; | 
| } | 
|   | 
| void qed_consq_setup(struct qed_hwfn *p_hwfn) | 
| { | 
|     qed_chain_reset(&p_hwfn->p_consq->chain); | 
| } | 
|   | 
| void qed_consq_free(struct qed_hwfn *p_hwfn) | 
| { | 
|     if (!p_hwfn->p_consq) | 
|         return; | 
|   | 
|     qed_chain_free(p_hwfn->cdev, &p_hwfn->p_consq->chain); | 
|   | 
|     kfree(p_hwfn->p_consq); | 
|     p_hwfn->p_consq = NULL; | 
| } |