/*
 * Copyright (C) 2008-2013 Emulex.  All rights reserved.
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __OCRDMA_MAIN_H__
#define __OCRDMA_MAIN_H__

#include <inttypes.h>
#include <stddef.h>
#include <endian.h>

#include <infiniband/driver.h>
#include <util/udma_barrier.h>

#include <ccan/list.h>

#define ocrdma_err(format, arg...) printf(format, ##arg)

#define OCRDMA_DPP_PAGE_SIZE (4096)

#define ROUND_UP_X(_val, _x) \
    (((unsigned long)(_val) + ((_x)-1)) & (long)~((_x)-1))

struct ocrdma_qp;

struct ocrdma_device {
	struct verbs_device ibv_dev;
	struct ocrdma_qp **qp_tbl;
	pthread_mutex_t dev_lock;
	pthread_spinlock_t flush_q_lock;
	int id;
	int gen;
	uint32_t wqe_size;
	uint32_t rqe_size;
	uint32_t dpp_wqe_size;
	uint32_t max_inline_data;
	uint8_t fw_ver[32];
};

struct ocrdma_devctx {
	struct verbs_context ibv_ctx;
	uint32_t *ah_tbl;
	uint32_t ah_tbl_len;
	pthread_mutex_t tbl_lock;
};

struct ocrdma_pd {
	struct ibv_pd ibv_pd;
	struct ocrdma_device *dev;
	struct ocrdma_devctx *uctx;
	void *dpp_va;
};

struct ocrdma_mr {
	struct verbs_mr vmr;
};

struct ocrdma_cq {
	struct ibv_cq ibv_cq;
	struct ocrdma_device *dev;
	uint16_t cq_id;
	uint16_t cq_dbid;
	uint16_t getp;
	pthread_spinlock_t cq_lock;
	uint32_t max_hw_cqe;
	uint32_t cq_mem_size;
	struct ocrdma_cqe *va;
	void *db_va;

	uint32_t db_size;

	uint32_t phase;
	int phase_change;

	uint8_t deferred_arm;
	uint8_t deferred_sol;
	uint8_t first_arm;
	struct list_head sq_head;
	struct list_head rq_head;
};

enum {
	OCRDMA_DPP_WQE_INDEX_MASK	= 0xFFFF,
	OCRDMA_DPP_CQE_VALID_BIT_SHIFT	= 31,
	OCRDMA_DPP_CQE_VALID_BIT_MASK	= 1 << 31
};

struct ocrdma_dpp_cqe {
	uint32_t wqe_idx_valid;
};

enum {
	OCRDMA_PD_MAX_DPP_ENABLED_QP = 16
};

struct ocrdma_qp_hwq_info {
	uint8_t *va;		/* virtual address */
	uint32_t max_sges;
	uint32_t free_cnt;

	uint32_t head, tail;
	uint32_t entry_size;
	uint32_t max_cnt;
	uint32_t max_wqe_idx;
	uint32_t len;
	uint16_t dbid;		/* qid, where to ring the doorbell. */
};

struct ocrdma_srq {
	struct ibv_srq ibv_srq;
	struct ocrdma_device *dev;
	void *db_va;
	uint32_t db_size;
	pthread_spinlock_t q_lock;

	struct ocrdma_qp_hwq_info rq;
	uint32_t max_rq_sges;
	uint32_t id;
	uint64_t *rqe_wr_id_tbl;
	uint32_t *idx_bit_fields;
	uint32_t bit_fields_len;
	uint32_t db_shift;
};

enum {
	OCRDMA_CREATE_QP_REQ_DPP_CREDIT_LIMIT = 1
};

enum ocrdma_qp_state {
	OCRDMA_QPS_RST 		= 0,
	OCRDMA_QPS_INIT 	= 1,
	OCRDMA_QPS_RTR 		= 2,
	OCRDMA_QPS_RTS 		= 3,
	OCRDMA_QPS_SQE 		= 4,
	OCRDMA_QPS_SQ_DRAINING 	= 5,
	OCRDMA_QPS_ERR 		= 6,
	OCRDMA_QPS_SQD 		= 7
};

struct ocrdma_qp {
	struct ibv_qp ibv_qp;
	struct ocrdma_device *dev;
	pthread_spinlock_t q_lock;

	struct ocrdma_qp_hwq_info sq;
	struct ocrdma_cq *sq_cq;
	struct {
		uint64_t wrid;
		uint16_t dpp_wqe_idx;
		uint16_t dpp_wqe;
		uint8_t  signaled;
		uint8_t  rsvd[3];
	} *wqe_wr_id_tbl;
	struct ocrdma_qp_hwq_info dpp_q;
	int dpp_enabled;

	struct ocrdma_qp_hwq_info rq;
	struct ocrdma_cq *rq_cq;
	uint64_t *rqe_wr_id_tbl;
	void *db_va;
	void *db_sq_va;
	void *db_rq_va;
	uint32_t max_inline_data;

	struct ocrdma_srq *srq;
	struct ocrdma_cq *dpp_cq;

	uint32_t db_size;
	uint32_t max_ord;
	uint32_t max_ird;
	uint32_t dpp_prev_indx;

	enum ibv_qp_type qp_type;
	enum ocrdma_qp_state state;
	struct list_node sq_entry;
	struct list_node rq_entry;
	uint16_t id;
	uint16_t rsvd;
	uint32_t db_shift;
	int	signaled;	/* signaled QP */
};

enum {
	OCRDMA_AH_ID_MASK               = 0x3FF,
	OCRDMA_AH_VLAN_VALID_MASK       = 0x01,
	OCRDMA_AH_VLAN_VALID_SHIFT      = 0x1F,
	OCRDMA_AH_L3_TYPE_MASK		= 0x03,
	OCRDMA_AH_L3_TYPE_SHIFT		= 0x1D
};

struct ocrdma_ah {
	struct ibv_ah ibv_ah;
	struct ocrdma_pd *pd;
	uint16_t id;
	uint8_t isvlan;
	uint8_t hdr_type;
};

#define get_ocrdma_xxx(xxx, type)                                              \
	container_of(ib##xxx, struct ocrdma_##type, ibv_##xxx)

static inline struct ocrdma_devctx *get_ocrdma_ctx(struct ibv_context *ibctx)
{
	return container_of(ibctx, struct ocrdma_devctx, ibv_ctx.context);
}

static inline struct ocrdma_device *get_ocrdma_dev(struct ibv_device *ibdev)
{
	return container_of(ibdev, struct ocrdma_device, ibv_dev.device);
}

static inline struct ocrdma_qp *get_ocrdma_qp(struct ibv_qp *ibqp)
{
	return get_ocrdma_xxx(qp, qp);
}

static inline struct ocrdma_srq *get_ocrdma_srq(struct ibv_srq *ibsrq)
{
	return get_ocrdma_xxx(srq, srq);
}

static inline struct ocrdma_pd *get_ocrdma_pd(struct ibv_pd *ibpd)
{
	return get_ocrdma_xxx(pd, pd);
}

static inline struct ocrdma_cq *get_ocrdma_cq(struct ibv_cq *ibcq)
{
	return get_ocrdma_xxx(cq, cq);
}

static inline struct ocrdma_ah *get_ocrdma_ah(struct ibv_ah *ibah)
{
	return get_ocrdma_xxx(ah, ah);
}

void ocrdma_init_ahid_tbl(struct ocrdma_devctx *ctx);
int ocrdma_query_device(struct ibv_context *context,
			const struct ibv_query_device_ex_input *input,
			struct ibv_device_attr_ex *attr, size_t attr_size);
int ocrdma_query_port(struct ibv_context *, uint8_t, struct ibv_port_attr *);
struct ibv_pd *ocrdma_alloc_pd(struct ibv_context *);
int ocrdma_free_pd(struct ibv_pd *);
struct ibv_mr *ocrdma_reg_mr(struct ibv_pd *pd, void *addr, size_t len,
			     uint64_t hca_va, int access);
int ocrdma_dereg_mr(struct verbs_mr *vmr);

struct ibv_cq *ocrdma_create_cq(struct ibv_context *, int,
				struct ibv_comp_channel *, int);
int ocrdma_resize_cq(struct ibv_cq *, int);
int ocrdma_destroy_cq(struct ibv_cq *);
int ocrdma_poll_cq(struct ibv_cq *, int, struct ibv_wc *);
int ocrdma_arm_cq(struct ibv_cq *, int);

struct ibv_qp *ocrdma_create_qp(struct ibv_pd *, struct ibv_qp_init_attr *);
int ocrdma_modify_qp(struct ibv_qp *, struct ibv_qp_attr *,
		     int ibv_qp_attr_mask);
int ocrdma_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask,
		    struct ibv_qp_init_attr *init_attr);
int ocrdma_destroy_qp(struct ibv_qp *);
int ocrdma_post_send(struct ibv_qp *, struct ibv_send_wr *,
		     struct ibv_send_wr **);
int ocrdma_post_recv(struct ibv_qp *, struct ibv_recv_wr *,
		     struct ibv_recv_wr **);

struct ibv_srq *ocrdma_create_srq(struct ibv_pd *, struct ibv_srq_init_attr *);
int ocrdma_modify_srq(struct ibv_srq *, struct ibv_srq_attr *, int);
int ocrdma_destroy_srq(struct ibv_srq *);
int ocrdma_query_srq(struct ibv_srq *ibsrq, struct ibv_srq_attr *attr);
int ocrdma_post_srq_recv(struct ibv_srq *, struct ibv_recv_wr *,
			 struct ibv_recv_wr **);
struct ibv_ah *ocrdma_create_ah(struct ibv_pd *, struct ibv_ah_attr *);
int ocrdma_destroy_ah(struct ibv_ah *);
int ocrdma_attach_mcast(struct ibv_qp *, const union ibv_gid *, uint16_t);
int ocrdma_detach_mcast(struct ibv_qp *, const union ibv_gid *, uint16_t);
void ocrdma_async_event(struct ibv_async_event *event);

#endif				/* __OCRDMA_MAIN_H__ */
