|
@@ -13,17 +13,74 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
+#include <linux/kernel.h>
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/errno.h>
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/device.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/dma-mapping.h>
|
|
|
+#include <linux/log2.h>
|
|
|
+#include <asm/byteorder.h>
|
|
|
+#include <asm/barrier.h>
|
|
|
|
|
|
+#include "hinic_hw_csr.h"
|
|
|
#include "hinic_hw_if.h"
|
|
|
#include "hinic_hw_eqs.h"
|
|
|
|
|
|
#define HINIC_EQS_WQ_NAME "hinic_eqs"
|
|
|
|
|
|
+#define GET_EQ_NUM_PAGES(eq, pg_size) \
|
|
|
+ (ALIGN((eq)->q_len * (eq)->elem_size, pg_size) / (pg_size))
|
|
|
+
|
|
|
+#define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size)
|
|
|
+
|
|
|
+#define EQ_CONS_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id)
|
|
|
+#define EQ_PROD_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id)
|
|
|
+
|
|
|
+#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) \
|
|
|
+ HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num)
|
|
|
+
|
|
|
+#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) \
|
|
|
+ HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num)
|
|
|
+
|
|
|
+#define GET_EQ_ELEMENT(eq, idx) \
|
|
|
+ ((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \
|
|
|
+ (((idx) & ((eq)->num_elem_in_pg - 1)) * (eq)->elem_size))
|
|
|
+
|
|
|
+#define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \
|
|
|
+ GET_EQ_ELEMENT(eq, idx))
|
|
|
+
|
|
|
+#define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx)
|
|
|
+
|
|
|
+#define PAGE_IN_4K(page_size) ((page_size) >> 12)
|
|
|
+#define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size)))
|
|
|
+
|
|
|
+#define ELEMENT_SIZE_IN_32B(eq) (((eq)->elem_size) >> 5)
|
|
|
+#define EQ_SET_HW_ELEM_SIZE_VAL(eq) (ilog2(ELEMENT_SIZE_IN_32B(eq)))
|
|
|
+
|
|
|
+#define EQ_MAX_PAGES 8
|
|
|
+
|
|
|
+#define aeq_to_aeqs(eq) \
|
|
|
+ container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0])
|
|
|
+
|
|
|
+#define work_to_aeq_work(work) \
|
|
|
+ container_of(work, struct hinic_eq_work, work)
|
|
|
+
|
|
|
+#define DMA_ATTR_AEQ_DEFAULT 0
|
|
|
+
|
|
|
+enum eq_int_mode {
|
|
|
+ EQ_INT_MODE_ARMED,
|
|
|
+ EQ_INT_MODE_ALWAYS
|
|
|
+};
|
|
|
+
|
|
|
+enum eq_arm_state {
|
|
|
+ EQ_NOT_ARMED,
|
|
|
+ EQ_ARMED
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* hinic_aeq_register_hw_cb - register AEQ callback for specific event
|
|
|
* @aeqs: pointer to Async eqs of the chip
|
|
@@ -61,6 +118,325 @@ void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs,
|
|
|
hwe_cb->hwe_handler = NULL;
|
|
|
}
|
|
|
|
|
|
+static u8 eq_cons_idx_checksum_set(u32 val)
|
|
|
+{
|
|
|
+ u8 checksum = 0;
|
|
|
+ int idx;
|
|
|
+
|
|
|
+ for (idx = 0; idx < 32; idx += 4)
|
|
|
+ checksum ^= ((val >> idx) & 0xF);
|
|
|
+
|
|
|
+ return (checksum & 0xF);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * eq_update_ci - update the HW cons idx of event queue
|
|
|
+ * @eq: the event queue to update the cons idx for
|
|
|
+ **/
|
|
|
+static void eq_update_ci(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ u32 val, addr = EQ_CONS_IDX_REG_ADDR(eq);
|
|
|
+
|
|
|
+ /* Read Modify Write */
|
|
|
+ val = hinic_hwif_read_reg(eq->hwif, addr);
|
|
|
+
|
|
|
+ val = HINIC_EQ_CI_CLEAR(val, IDX) &
|
|
|
+ HINIC_EQ_CI_CLEAR(val, WRAPPED) &
|
|
|
+ HINIC_EQ_CI_CLEAR(val, INT_ARMED) &
|
|
|
+ HINIC_EQ_CI_CLEAR(val, XOR_CHKSUM);
|
|
|
+
|
|
|
+ val |= HINIC_EQ_CI_SET(eq->cons_idx, IDX) |
|
|
|
+ HINIC_EQ_CI_SET(eq->wrapped, WRAPPED) |
|
|
|
+ HINIC_EQ_CI_SET(EQ_ARMED, INT_ARMED);
|
|
|
+
|
|
|
+ val |= HINIC_EQ_CI_SET(eq_cons_idx_checksum_set(val), XOR_CHKSUM);
|
|
|
+
|
|
|
+ hinic_hwif_write_reg(eq->hwif, addr, val);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * aeq_irq_handler - handler for the AEQ event
|
|
|
+ * @eq: the Async Event Queue that received the event
|
|
|
+ **/
|
|
|
+static void aeq_irq_handler(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ struct hinic_aeqs *aeqs = aeq_to_aeqs(eq);
|
|
|
+ struct hinic_hwif *hwif = aeqs->hwif;
|
|
|
+ struct pci_dev *pdev = hwif->pdev;
|
|
|
+ struct hinic_aeq_elem *aeqe_curr;
|
|
|
+ struct hinic_hw_event_cb *hwe_cb;
|
|
|
+ enum hinic_aeq_type event;
|
|
|
+ unsigned long eqe_state;
|
|
|
+ u32 aeqe_desc;
|
|
|
+ int i, size;
|
|
|
+
|
|
|
+ for (i = 0; i < eq->q_len; i++) {
|
|
|
+ aeqe_curr = GET_CURR_AEQ_ELEM(eq);
|
|
|
+
|
|
|
+ /* Data in HW is in Big endian Format */
|
|
|
+ aeqe_desc = be32_to_cpu(aeqe_curr->desc);
|
|
|
+
|
|
|
+ /* HW toggles the wrapped bit, when it adds eq element */
|
|
|
+ if (HINIC_EQ_ELEM_DESC_GET(aeqe_desc, WRAPPED) == eq->wrapped)
|
|
|
+ break;
|
|
|
+
|
|
|
+ event = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, TYPE);
|
|
|
+ if (event >= HINIC_MAX_AEQ_EVENTS) {
|
|
|
+ dev_err(&pdev->dev, "Unknown AEQ Event %d\n", event);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SRC)) {
|
|
|
+ hwe_cb = &aeqs->hwe_cb[event];
|
|
|
+
|
|
|
+ size = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SIZE);
|
|
|
+
|
|
|
+ eqe_state = cmpxchg(&hwe_cb->hwe_state,
|
|
|
+ HINIC_EQE_ENABLED,
|
|
|
+ HINIC_EQE_ENABLED |
|
|
|
+ HINIC_EQE_RUNNING);
|
|
|
+ if ((eqe_state == HINIC_EQE_ENABLED) &&
|
|
|
+ (hwe_cb->hwe_handler))
|
|
|
+ hwe_cb->hwe_handler(hwe_cb->handle,
|
|
|
+ aeqe_curr->data, size);
|
|
|
+ else
|
|
|
+ dev_err(&pdev->dev, "Unhandled AEQ Event %d\n",
|
|
|
+ event);
|
|
|
+
|
|
|
+ hwe_cb->hwe_state &= ~HINIC_EQE_RUNNING;
|
|
|
+ }
|
|
|
+
|
|
|
+ eq->cons_idx++;
|
|
|
+
|
|
|
+ if (eq->cons_idx == eq->q_len) {
|
|
|
+ eq->cons_idx = 0;
|
|
|
+ eq->wrapped = !eq->wrapped;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * eq_irq_handler - handler for the EQ event
|
|
|
+ * @data: the Event Queue that received the event
|
|
|
+ **/
|
|
|
+static void eq_irq_handler(void *data)
|
|
|
+{
|
|
|
+ struct hinic_eq *eq = data;
|
|
|
+
|
|
|
+ if (eq->type == HINIC_AEQ)
|
|
|
+ aeq_irq_handler(eq);
|
|
|
+
|
|
|
+ eq_update_ci(eq);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * eq_irq_work - the work of the EQ that received the event
|
|
|
+ * @work: the work struct that is associated with the EQ
|
|
|
+ **/
|
|
|
+static void eq_irq_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct hinic_eq_work *aeq_work = work_to_aeq_work(work);
|
|
|
+ struct hinic_eq *aeq;
|
|
|
+
|
|
|
+ aeq = aeq_work->data;
|
|
|
+ eq_irq_handler(aeq);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * aeq_interrupt - aeq interrupt handler
|
|
|
+ * @irq: irq number
|
|
|
+ * @data: the Async Event Queue that collected the event
|
|
|
+ **/
|
|
|
+static irqreturn_t aeq_interrupt(int irq, void *data)
|
|
|
+{
|
|
|
+ struct hinic_eq_work *aeq_work;
|
|
|
+ struct hinic_eq *aeq = data;
|
|
|
+ struct hinic_aeqs *aeqs;
|
|
|
+
|
|
|
+ /* clear resend timer cnt register */
|
|
|
+ hinic_msix_attr_cnt_clear(aeq->hwif, aeq->msix_entry.entry);
|
|
|
+
|
|
|
+ aeq_work = &aeq->aeq_work;
|
|
|
+ aeq_work->data = aeq;
|
|
|
+
|
|
|
+ aeqs = aeq_to_aeqs(aeq);
|
|
|
+ queue_work(aeqs->workq, &aeq_work->work);
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+void set_ctrl0(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ struct msix_entry *msix_entry = &eq->msix_entry;
|
|
|
+ enum hinic_eq_type type = eq->type;
|
|
|
+ u32 addr, val, ctrl0;
|
|
|
+
|
|
|
+ if (type == HINIC_AEQ) {
|
|
|
+ /* RMW Ctrl0 */
|
|
|
+ addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id);
|
|
|
+
|
|
|
+ val = hinic_hwif_read_reg(eq->hwif, addr);
|
|
|
+
|
|
|
+ val = HINIC_AEQ_CTRL_0_CLEAR(val, INT_IDX) &
|
|
|
+ HINIC_AEQ_CTRL_0_CLEAR(val, DMA_ATTR) &
|
|
|
+ HINIC_AEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) &
|
|
|
+ HINIC_AEQ_CTRL_0_CLEAR(val, INT_MODE);
|
|
|
+
|
|
|
+ ctrl0 = HINIC_AEQ_CTRL_0_SET(msix_entry->entry, INT_IDX) |
|
|
|
+ HINIC_AEQ_CTRL_0_SET(DMA_ATTR_AEQ_DEFAULT, DMA_ATTR) |
|
|
|
+ HINIC_AEQ_CTRL_0_SET(HINIC_HWIF_PCI_INTF(eq->hwif),
|
|
|
+ PCI_INTF_IDX) |
|
|
|
+ HINIC_AEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INT_MODE);
|
|
|
+
|
|
|
+ val |= ctrl0;
|
|
|
+
|
|
|
+ hinic_hwif_write_reg(eq->hwif, addr, val);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void set_ctrl1(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ enum hinic_eq_type type = eq->type;
|
|
|
+ u32 page_size_val, elem_size;
|
|
|
+ u32 addr, val, ctrl1;
|
|
|
+
|
|
|
+ if (type == HINIC_AEQ) {
|
|
|
+ /* RMW Ctrl1 */
|
|
|
+ addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id);
|
|
|
+
|
|
|
+ page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq);
|
|
|
+ elem_size = EQ_SET_HW_ELEM_SIZE_VAL(eq);
|
|
|
+
|
|
|
+ val = hinic_hwif_read_reg(eq->hwif, addr);
|
|
|
+
|
|
|
+ val = HINIC_AEQ_CTRL_1_CLEAR(val, LEN) &
|
|
|
+ HINIC_AEQ_CTRL_1_CLEAR(val, ELEM_SIZE) &
|
|
|
+ HINIC_AEQ_CTRL_1_CLEAR(val, PAGE_SIZE);
|
|
|
+
|
|
|
+ ctrl1 = HINIC_AEQ_CTRL_1_SET(eq->q_len, LEN) |
|
|
|
+ HINIC_AEQ_CTRL_1_SET(elem_size, ELEM_SIZE) |
|
|
|
+ HINIC_AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE);
|
|
|
+
|
|
|
+ val |= ctrl1;
|
|
|
+
|
|
|
+ hinic_hwif_write_reg(eq->hwif, addr, val);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * set_eq_ctrls - setting eq's ctrl registers
|
|
|
+ * @eq: the Event Queue for setting
|
|
|
+ **/
|
|
|
+static void set_eq_ctrls(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ set_ctrl0(eq);
|
|
|
+ set_ctrl1(eq);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * aeq_elements_init - initialize all the elements in the aeq
|
|
|
+ * @eq: the Async Event Queue
|
|
|
+ * @init_val: value to initialize the elements with it
|
|
|
+ **/
|
|
|
+static void aeq_elements_init(struct hinic_eq *eq, u32 init_val)
|
|
|
+{
|
|
|
+ struct hinic_aeq_elem *aeqe;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < eq->q_len; i++) {
|
|
|
+ aeqe = GET_AEQ_ELEM(eq, i);
|
|
|
+ aeqe->desc = cpu_to_be32(init_val);
|
|
|
+ }
|
|
|
+
|
|
|
+ wmb(); /* Write the initilzation values */
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * alloc_eq_pages - allocate the pages for the queue
|
|
|
+ * @eq: the event queue
|
|
|
+ *
|
|
|
+ * Return 0 - Success, Negative - Failure
|
|
|
+ **/
|
|
|
+static int alloc_eq_pages(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ struct hinic_hwif *hwif = eq->hwif;
|
|
|
+ struct pci_dev *pdev = hwif->pdev;
|
|
|
+ u32 init_val, addr, val;
|
|
|
+ size_t addr_size;
|
|
|
+ int err, pg;
|
|
|
+
|
|
|
+ addr_size = eq->num_pages * sizeof(*eq->dma_addr);
|
|
|
+ eq->dma_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL);
|
|
|
+ if (!eq->dma_addr)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ addr_size = eq->num_pages * sizeof(*eq->virt_addr);
|
|
|
+ eq->virt_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL);
|
|
|
+ if (!eq->virt_addr) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_virt_addr_alloc;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (pg = 0; pg < eq->num_pages; pg++) {
|
|
|
+ eq->virt_addr[pg] = dma_zalloc_coherent(&pdev->dev,
|
|
|
+ eq->page_size,
|
|
|
+ &eq->dma_addr[pg],
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!eq->virt_addr[pg]) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_dma_alloc;
|
|
|
+ }
|
|
|
+
|
|
|
+ addr = EQ_HI_PHYS_ADDR_REG(eq, pg);
|
|
|
+ val = upper_32_bits(eq->dma_addr[pg]);
|
|
|
+
|
|
|
+ hinic_hwif_write_reg(hwif, addr, val);
|
|
|
+
|
|
|
+ addr = EQ_LO_PHYS_ADDR_REG(eq, pg);
|
|
|
+ val = lower_32_bits(eq->dma_addr[pg]);
|
|
|
+
|
|
|
+ hinic_hwif_write_reg(hwif, addr, val);
|
|
|
+ }
|
|
|
+
|
|
|
+ init_val = HINIC_EQ_ELEM_DESC_SET(eq->wrapped, WRAPPED);
|
|
|
+
|
|
|
+ if (eq->type == HINIC_AEQ)
|
|
|
+ aeq_elements_init(eq, init_val);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_dma_alloc:
|
|
|
+ while (--pg >= 0)
|
|
|
+ dma_free_coherent(&pdev->dev, eq->page_size,
|
|
|
+ eq->virt_addr[pg],
|
|
|
+ eq->dma_addr[pg]);
|
|
|
+
|
|
|
+ devm_kfree(&pdev->dev, eq->virt_addr);
|
|
|
+
|
|
|
+err_virt_addr_alloc:
|
|
|
+ devm_kfree(&pdev->dev, eq->dma_addr);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * free_eq_pages - free the pages of the queue
|
|
|
+ * @eq: the Event Queue
|
|
|
+ **/
|
|
|
+static void free_eq_pages(struct hinic_eq *eq)
|
|
|
+{
|
|
|
+ struct hinic_hwif *hwif = eq->hwif;
|
|
|
+ struct pci_dev *pdev = hwif->pdev;
|
|
|
+ int pg;
|
|
|
+
|
|
|
+ for (pg = 0; pg < eq->num_pages; pg++)
|
|
|
+ dma_free_coherent(&pdev->dev, eq->page_size,
|
|
|
+ eq->virt_addr[pg],
|
|
|
+ eq->dma_addr[pg]);
|
|
|
+
|
|
|
+ devm_kfree(&pdev->dev, eq->virt_addr);
|
|
|
+ devm_kfree(&pdev->dev, eq->dma_addr);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* init_eq - initialize Event Queue
|
|
|
* @eq: the event queue
|
|
@@ -77,8 +453,81 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
|
|
|
enum hinic_eq_type type, int q_id, u32 q_len, u32 page_size,
|
|
|
struct msix_entry entry)
|
|
|
{
|
|
|
- /* should be implemented */
|
|
|
+ struct pci_dev *pdev = hwif->pdev;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ eq->hwif = hwif;
|
|
|
+ eq->type = type;
|
|
|
+ eq->q_id = q_id;
|
|
|
+ eq->q_len = q_len;
|
|
|
+ eq->page_size = page_size;
|
|
|
+
|
|
|
+ /* Clear PI and CI, also clear the ARM bit */
|
|
|
+ hinic_hwif_write_reg(eq->hwif, EQ_CONS_IDX_REG_ADDR(eq), 0);
|
|
|
+ hinic_hwif_write_reg(eq->hwif, EQ_PROD_IDX_REG_ADDR(eq), 0);
|
|
|
+
|
|
|
+ eq->cons_idx = 0;
|
|
|
+ eq->wrapped = 0;
|
|
|
+
|
|
|
+ if (type == HINIC_AEQ) {
|
|
|
+ eq->elem_size = HINIC_AEQE_SIZE;
|
|
|
+ } else {
|
|
|
+ dev_err(&pdev->dev, "Invalid EQ type\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ eq->num_pages = GET_EQ_NUM_PAGES(eq, page_size);
|
|
|
+ eq->num_elem_in_pg = GET_EQ_NUM_ELEMS_IN_PG(eq, page_size);
|
|
|
+
|
|
|
+ eq->msix_entry = entry;
|
|
|
+
|
|
|
+ if (eq->num_elem_in_pg & (eq->num_elem_in_pg - 1)) {
|
|
|
+ dev_err(&pdev->dev, "num elements in eq page != power of 2\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eq->num_pages > EQ_MAX_PAGES) {
|
|
|
+ dev_err(&pdev->dev, "too many pages for eq\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ set_eq_ctrls(eq);
|
|
|
+ eq_update_ci(eq);
|
|
|
+
|
|
|
+ err = alloc_eq_pages(eq);
|
|
|
+ if (err) {
|
|
|
+ dev_err(&pdev->dev, "Failed to allocate pages for eq\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type == HINIC_AEQ) {
|
|
|
+ struct hinic_eq_work *aeq_work = &eq->aeq_work;
|
|
|
+
|
|
|
+ INIT_WORK(&aeq_work->work, eq_irq_work);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* set the attributes of the msix entry */
|
|
|
+ hinic_msix_attr_set(eq->hwif, eq->msix_entry.entry,
|
|
|
+ HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT,
|
|
|
+ HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT,
|
|
|
+ HINIC_EQ_MSIX_LLI_TIMER_DEFAULT,
|
|
|
+ HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT,
|
|
|
+ HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT);
|
|
|
+
|
|
|
+ if (type == HINIC_AEQ)
|
|
|
+ err = request_irq(entry.vector, aeq_interrupt, 0,
|
|
|
+ "hinic_aeq", eq);
|
|
|
+
|
|
|
+ if (err) {
|
|
|
+ dev_err(&pdev->dev, "Failed to request irq for the EQ\n");
|
|
|
+ goto err_req_irq;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
+
|
|
|
+err_req_irq:
|
|
|
+ free_eq_pages(eq);
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -87,7 +536,17 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
|
|
|
**/
|
|
|
static void remove_eq(struct hinic_eq *eq)
|
|
|
{
|
|
|
- /* should be implemented */
|
|
|
+ struct msix_entry *entry = &eq->msix_entry;
|
|
|
+
|
|
|
+ free_irq(entry->vector, eq);
|
|
|
+
|
|
|
+ if (eq->type == HINIC_AEQ) {
|
|
|
+ struct hinic_eq_work *aeq_work = &eq->aeq_work;
|
|
|
+
|
|
|
+ cancel_work_sync(&aeq_work->work);
|
|
|
+ }
|
|
|
+
|
|
|
+ free_eq_pages(eq);
|
|
|
}
|
|
|
|
|
|
/**
|