// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All rights reserved.
*/
#include "efa_com.h"
#include "efa_regs_defs.h"
#define ADMIN_CMD_TIMEOUT_US 30000000 /* usecs */
#define EFA_REG_READ_TIMEOUT_US 50000 /* usecs */
#define EFA_MMIO_READ_INVALID 0xffffffff
#define EFA_POLL_INTERVAL_MS 100 /* msecs */
#define EFA_ASYNC_QUEUE_DEPTH 16
#define EFA_ADMIN_QUEUE_DEPTH 32
#define MIN_EFA_VER\
((EFA_ADMIN_API_VERSION_MAJOR << EFA_REGS_VERSION_MAJOR_VERSION_SHIFT) | \
(EFA_ADMIN_API_VERSION_MINOR & EFA_REGS_VERSION_MINOR_VERSION_MASK))
#define EFA_CTRL_MAJOR 0
#define EFA_CTRL_MINOR 0
#define EFA_CTRL_SUB_MINOR 1
#define MIN_EFA_CTRL_VER \
(((EFA_CTRL_MAJOR) << \
(EFA_REGS_CONTROLLER_VERSION_MAJOR_VERSION_SHIFT)) | \
((EFA_CTRL_MINOR) << \
(EFA_REGS_CONTROLLER_VERSION_MINOR_VERSION_SHIFT)) | \
(EFA_CTRL_SUB_MINOR))
#define EFA_DMA_ADDR_TO_UINT32_LOW(x) ((u32)((u64)(x)))
#define EFA_DMA_ADDR_TO_UINT32_HIGH(x) ((u32)(((u64)(x)) >> 32))
#define EFA_REGS_ADMIN_INTR_MASK 1
enum efa_cmd_status {
EFA_CMD_SUBMITTED,
EFA_CMD_COMPLETED,
/* Abort - canceled by the driver */
EFA_CMD_ABORTED,
};
struct efa_comp_ctx {
struct completion wait_event;
struct efa_admin_acq_entry *user_cqe;
u32 comp_size;
enum efa_cmd_status status;
/* status from the device */
u8 comp_status;
u8 cmd_opcode;
u8 occupied;
};
static const char *efa_com_cmd_str(u8 cmd)
{
#define EFA_CMD_STR_CASE(_cmd) case EFA_ADMIN_##_cmd: return #_cmd
switch (cmd) {
EFA_CMD_STR_CASE(CREATE_QP);
EFA_CMD_STR_CASE(MODIFY_QP);
EFA_CMD_STR_CASE(QUERY_QP);
EFA_CMD_STR_CASE(DESTROY_QP);
EFA_CMD_STR_CASE(CREATE_AH);
EFA_CMD_STR_CASE(DESTROY_AH);
EFA_CMD_STR_CASE(REG_MR);
EFA_CMD_STR_CASE(DEREG_MR);
EFA_CMD_STR_CASE(CREATE_CQ);
EFA_CMD_STR_CASE(DESTROY_CQ);
EFA_CMD_STR_CASE(GET_FEATURE);
EFA_CMD_STR_CASE(SET_FEATURE);
EFA_CMD_STR_CASE(GET_STATS);
EFA_CMD_STR_CASE(ALLOC_PD);
EFA_CMD_STR_CASE(DEALLOC_PD);
EFA_CMD_STR_CASE(ALLOC_UAR);
EFA_CMD_STR_CASE(DEALLOC_UAR);
default: return "unknown command opcode";
}
#undef EFA_CMD_STR_CASE
}
static u32 efa_com_reg_read32(struct efa_com_dev *edev, u16 offset)
{
struct efa_com_mmio_read *mmio_read = &edev->mmio_read;
struct efa_admin_mmio_req_read_less_resp *read_resp;
unsigned long exp_time;
u32 mmio_read_reg;
u32 err;
read_resp = mmio_read->read_resp;
spin_lock(&mmio_read->lock);
mmio_read->seq_num++;
/* trash DMA req_id to identify when hardware is done */
read_resp->req_id = mmio_read->seq_num + 0x9aL;
mmio_read_reg = (offset << EFA_REGS_MMIO_REG_READ_REG_OFF_SHIFT) &
EFA_REGS_MMIO_REG_READ_REG_OFF_MASK;
mmio_read_reg |= mmio_read->seq_num &
EFA_REGS_MMIO_REG_READ_REQ_ID_MASK;
writel(mmio_read_reg, edev->reg_bar + EFA_REGS_MMIO_REG_READ_OFF);
exp_time = jiffies + usecs_to_jiffies(mmio_read->mmio_read_timeout);
do {
if (READ_ONCE(read_resp->req_id) == mmio_read->seq_num)
break;
udelay(1);
} while (time_is_after_jiffies(exp_time));
if (read_resp->req_id != mmio_read->seq_num) {
ibdev_err(edev->efa_dev,
"Reading register timed out. expected: req id[%u] offset[%#x] actual: req id[%u] offset[%#x]\n",
mmio_read->seq_num, offset, read_resp->req_id,
read_resp->reg_off);
err = EFA_MMIO_READ_INVALID;
goto out;
}
if (read_resp->reg_off != offset) {
ibdev_err(edev->efa_dev,
"Reading register failed: wrong offset provided\n");
err = EFA_MMIO_READ_INVALID;
goto out;
}
err = read_resp->reg_val;
out:
spin_unlock(&mmio_read->lock);
return err;
}
static int efa_com_admin_init_sq(struct efa_com_dev *edev)
{
struct efa_com_admin_queue