Commit f00fe738 authored by Aviad Krawczyk's avatar Aviad Krawczyk Committed by David S. Miller

net-next/hinic: Add aeqs

Handle aeq elements that are accumulated on the aeq by calling the
registered handler for the specific event.
Signed-off-by: default avatarAviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: default avatarZhao Chen <zhaochen6@huawei.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0ac599c7
......@@ -65,4 +65,53 @@
#define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \
(HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE)
/* MSI-X registers */
#define HINIC_CSR_MSIX_CTRL_BASE 0x2000
#define HINIC_CSR_MSIX_CNT_BASE 0x2004
#define HINIC_CSR_MSIX_STRIDE 0x8
#define HINIC_CSR_MSIX_CTRL_ADDR(idx) \
(HINIC_CSR_MSIX_CTRL_BASE + (idx) * HINIC_CSR_MSIX_STRIDE)
#define HINIC_CSR_MSIX_CNT_ADDR(idx) \
(HINIC_CSR_MSIX_CNT_BASE + (idx) * HINIC_CSR_MSIX_STRIDE)
/* EQ registers */
#define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200
#define HINIC_EQ_MTT_OFF_STRIDE 0x40
#define HINIC_CSR_AEQ_MTT_OFF(id) \
(HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE)
#define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8
#define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \
(HINIC_CSR_AEQ_MTT_OFF(q_id) + \
(pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE)
#define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \
(HINIC_CSR_AEQ_MTT_OFF(q_id) + \
(pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4)
#define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00
#define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04
#define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08
#define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C
#define HINIC_EQ_OFF_STRIDE 0x80
#define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \
(HINIC_AEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
#define HINIC_CSR_AEQ_CTRL_1_ADDR(idx) \
(HINIC_AEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
#define HINIC_CSR_AEQ_CONS_IDX_ADDR(idx) \
(HINIC_AEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
#define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \
(HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
#endif
......@@ -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);
}
/**
......
......@@ -24,8 +24,84 @@
#include "hinic_hw_if.h"
#define HINIC_AEQ_CTRL_0_INT_IDX_SHIFT 0
#define HINIC_AEQ_CTRL_0_DMA_ATTR_SHIFT 12
#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_SHIFT 20
#define HINIC_AEQ_CTRL_0_INT_MODE_SHIFT 31
#define HINIC_AEQ_CTRL_0_INT_IDX_MASK 0x3FF
#define HINIC_AEQ_CTRL_0_DMA_ATTR_MASK 0x3F
#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3
#define HINIC_AEQ_CTRL_0_INT_MODE_MASK 0x1
#define HINIC_AEQ_CTRL_0_SET(val, member) \
(((u32)(val) & HINIC_AEQ_CTRL_0_##member##_MASK) << \
HINIC_AEQ_CTRL_0_##member##_SHIFT)
#define HINIC_AEQ_CTRL_0_CLEAR(val, member) \
((val) & (~(HINIC_AEQ_CTRL_0_##member##_MASK \
<< HINIC_AEQ_CTRL_0_##member##_SHIFT)))
#define HINIC_AEQ_CTRL_1_LEN_SHIFT 0
#define HINIC_AEQ_CTRL_1_ELEM_SIZE_SHIFT 24
#define HINIC_AEQ_CTRL_1_PAGE_SIZE_SHIFT 28
#define HINIC_AEQ_CTRL_1_LEN_MASK 0x1FFFFF
#define HINIC_AEQ_CTRL_1_ELEM_SIZE_MASK 0x3
#define HINIC_AEQ_CTRL_1_PAGE_SIZE_MASK 0xF
#define HINIC_AEQ_CTRL_1_SET(val, member) \
(((u32)(val) & HINIC_AEQ_CTRL_1_##member##_MASK) << \
HINIC_AEQ_CTRL_1_##member##_SHIFT)
#define HINIC_AEQ_CTRL_1_CLEAR(val, member) \
((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \
<< HINIC_AEQ_CTRL_1_##member##_SHIFT)))
#define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0
#define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7
#define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8
#define HINIC_EQ_ELEM_DESC_WRAPPED_SHIFT 31
#define HINIC_EQ_ELEM_DESC_TYPE_MASK 0x7F
#define HINIC_EQ_ELEM_DESC_SRC_MASK 0x1
#define HINIC_EQ_ELEM_DESC_SIZE_MASK 0xFF
#define HINIC_EQ_ELEM_DESC_WRAPPED_MASK 0x1
#define HINIC_EQ_ELEM_DESC_SET(val, member) \
(((u32)(val) & HINIC_EQ_ELEM_DESC_##member##_MASK) << \
HINIC_EQ_ELEM_DESC_##member##_SHIFT)
#define HINIC_EQ_ELEM_DESC_GET(val, member) \
(((val) >> HINIC_EQ_ELEM_DESC_##member##_SHIFT) & \
HINIC_EQ_ELEM_DESC_##member##_MASK)
#define HINIC_EQ_CI_IDX_SHIFT 0
#define HINIC_EQ_CI_WRAPPED_SHIFT 20
#define HINIC_EQ_CI_XOR_CHKSUM_SHIFT 24
#define HINIC_EQ_CI_INT_ARMED_SHIFT 31
#define HINIC_EQ_CI_IDX_MASK 0xFFFFF
#define HINIC_EQ_CI_WRAPPED_MASK 0x1
#define HINIC_EQ_CI_XOR_CHKSUM_MASK 0xF
#define HINIC_EQ_CI_INT_ARMED_MASK 0x1
#define HINIC_EQ_CI_SET(val, member) \
(((u32)(val) & HINIC_EQ_CI_##member##_MASK) << \
HINIC_EQ_CI_##member##_SHIFT)
#define HINIC_EQ_CI_CLEAR(val, member) \
((val) & (~(HINIC_EQ_CI_##member##_MASK \
<< HINIC_EQ_CI_##member##_SHIFT)))
#define HINIC_MAX_AEQS 4
#define HINIC_AEQE_SIZE 64
#define HINIC_AEQE_DESC_SIZE 4
#define HINIC_AEQE_DATA_SIZE \
(HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE)
#define HINIC_DEFAULT_AEQ_LEN 64
#define HINIC_EQ_PAGE_SIZE SZ_4K
......@@ -45,6 +121,11 @@ enum hinic_eqe_state {
HINIC_EQE_RUNNING = BIT(1),
};
struct hinic_aeq_elem {
u8 data[HINIC_AEQE_DATA_SIZE];
u32 desc;
};
struct hinic_eq_work {
struct work_struct work;
void *data;
......
......@@ -25,6 +25,96 @@
#define PCIE_ATTR_ENTRY 0
#define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs)
/**
* hinic_msix_attr_set - set message attribute for msix entry
* @hwif: the HW interface of a pci function device
* @msix_index: msix_index
* @pending_limit: the maximum pending interrupt events (unit 8)
* @coalesc_timer: coalesc period for interrupt (unit 8 us)
* @lli_timer: replenishing period for low latency credit (unit 8 us)
* @lli_credit_limit: maximum credits for low latency msix messages (unit 8)
* @resend_timer: maximum wait for resending msix (unit coalesc period)
*
* Return 0 - Success, negative - Failure
**/
int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index,
u8 pending_limit, u8 coalesc_timer,
u8 lli_timer, u8 lli_credit_limit,
u8 resend_timer)
{
u32 msix_ctrl, addr;
if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
return -EINVAL;
msix_ctrl = HINIC_MSIX_ATTR_SET(pending_limit, PENDING_LIMIT) |
HINIC_MSIX_ATTR_SET(coalesc_timer, COALESC_TIMER) |
HINIC_MSIX_ATTR_SET(lli_timer, LLI_TIMER) |
HINIC_MSIX_ATTR_SET(lli_credit_limit, LLI_CREDIT) |
HINIC_MSIX_ATTR_SET(resend_timer, RESEND_TIMER);
addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index);
hinic_hwif_write_reg(hwif, addr, msix_ctrl);
return 0;
}
/**
* hinic_msix_attr_get - get message attribute of msix entry
* @hwif: the HW interface of a pci function device
* @msix_index: msix_index
* @pending_limit: the maximum pending interrupt events (unit 8)
* @coalesc_timer: coalesc period for interrupt (unit 8 us)
* @lli_timer: replenishing period for low latency credit (unit 8 us)
* @lli_credit_limit: maximum credits for low latency msix messages (unit 8)
* @resend_timer: maximum wait for resending msix (unit coalesc period)
*
* Return 0 - Success, negative - Failure
**/
int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index,
u8 *pending_limit, u8 *coalesc_timer,
u8 *lli_timer, u8 *lli_credit_limit,
u8 *resend_timer)
{
u32 addr, val;
if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
return -EINVAL;
addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index);
val = hinic_hwif_read_reg(hwif, addr);
*pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT);
*coalesc_timer = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER);
*lli_timer = HINIC_MSIX_ATTR_GET(val, LLI_TIMER);
*lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT);
*resend_timer = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER);
return 0;
}
/**
* hinic_msix_attr_cnt_clear - clear message attribute counters for msix entry
* @hwif: the HW interface of a pci function device
* @msix_index: msix_index
*
* Return 0 - Success, negative - Failure
**/
int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index)
{
u32 msix_ctrl, addr;
if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
return -EINVAL;
msix_ctrl = HINIC_MSIX_CNT_SET(1, RESEND_TIMER);
addr = HINIC_CSR_MSIX_CNT_ADDR(msix_index);
hinic_hwif_write_reg(hwif, addr, msix_ctrl);
return 0;
}
/**
* hwif_ready - test if the HW is ready for use
* @hwif: the HW interface of a pci function device
......
......@@ -88,6 +88,34 @@
((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \
<< HINIC_PPF_ELECTION_##member##_SHIFT)))
#define HINIC_MSIX_PENDING_LIMIT_SHIFT 0
#define HINIC_MSIX_COALESC_TIMER_SHIFT 8
#define HINIC_MSIX_LLI_TIMER_SHIFT 16
#define HINIC_MSIX_LLI_CREDIT_SHIFT 24
#define HINIC_MSIX_RESEND_TIMER_SHIFT 29
#define HINIC_MSIX_PENDING_LIMIT_MASK 0xFF
#define HINIC_MSIX_COALESC_TIMER_MASK 0xFF
#define HINIC_MSIX_LLI_TIMER_MASK 0xFF
#define HINIC_MSIX_LLI_CREDIT_MASK 0x1F
#define HINIC_MSIX_RESEND_TIMER_MASK 0x7
#define HINIC_MSIX_ATTR_SET(val, member) \
(((u32)(val) & HINIC_MSIX_##member##_MASK) << \
HINIC_MSIX_##member##_SHIFT)
#define HINIC_MSIX_ATTR_GET(val, member) \
(((val) >> HINIC_MSIX_##member##_SHIFT) & \
HINIC_MSIX_##member##_MASK)
#define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29
#define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x1
#define HINIC_MSIX_CNT_SET(val, member) \
(((u32)(val) & HINIC_MSIX_CNT_##member##_MASK) << \
HINIC_MSIX_CNT_##member##_SHIFT)
#define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs)
#define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs)
#define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs)
......@@ -105,6 +133,12 @@
#define HINIC_PCIE_AT_DISABLE 0
#define HINIC_PCIE_PH_DISABLE 0
#define HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT 0 /* Disabled */
#define HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT 0xFF /* max */
#define HINIC_EQ_MSIX_LLI_TIMER_DEFAULT 0 /* Disabled */
#define HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT 0 /* Disabled */
#define HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT 7 /* max */
enum hinic_pcie_nosnoop {
HINIC_PCIE_SNOOP = 0,
HINIC_PCIE_NO_SNOOP = 1,
......@@ -166,6 +200,18 @@ static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg,
writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg);
}
int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index,
u8 pending_limit, u8 coalesc_timer,
u8 lli_timer_cfg, u8 lli_credit_limit,
u8 resend_timer);
int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index,
u8 *pending_limit, u8 *coalesc_timer_cfg,
u8 *lli_timer, u8 *lli_credit_limit,
u8 *resend_timer);
int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index);
int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev);
void hinic_free_hwif(struct hinic_hwif *hwif);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment