#ifndef _GOLAN_H_
#define _GOLAN_H_

/*
 * Copyright (C) 2013-2015 Mellanox Technologies Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <ipxe/pci.h>
#include <ipxe/pcibackup.h>
#include <byteswap.h>
#include <errno.h>
#include <ipxe/io.h>
#include <stdio.h>
#include <unistd.h>
#include "CIB_PRM.h"
#include "mlx_utils/include/public/mlx_utils.h"

#define GOLAN_PCI_CONFIG_BAR_SIZE	0x100000//HERMON_PCI_CONFIG_BAR_SIZE //TODO: What is the BAR size?

#define GOLAN_PAS_SIZE	sizeof(uint64_t)

#define GOLAN_INVALID_LKEY 0x00000100UL

#define GOLAN_MAX_PORTS	2
#define GOLAN_PORT_BASE 1

#define MELLANOX_VID	0x15b3
#define GOLAN_HCA_BAR	PCI_BASE_ADDRESS_0	//BAR 0

#define GOLAN_HCR_MAX_WAIT_MS	10000

#define min(a,b) ((a)<(b)?(a):(b))

#define GOLAN_PAGE_SHIFT	12
#define	GOLAN_PAGE_SIZE		(1 << GOLAN_PAGE_SHIFT)
#define GOLAN_PAGE_MASK		(GOLAN_PAGE_SIZE - 1)

#define MAX_MBOX	( GOLAN_PAGE_SIZE / MAILBOX_STRIDE )
#define DEF_CMD_IDX	1
#define MEM_CMD_IDX	0
#define NO_MBOX		0xffff
#define MEM_MBOX	MEM_CMD_IDX
#define GEN_MBOX	DEF_CMD_IDX

#define CMD_IF_REV	4

#define MAX_PASE_MBOX	((GOLAN_CMD_PAS_CNT) - 2)

#define CMD_STATUS( golan , idx )		((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->status
#define CMD_SYND( golan , idx )		((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->syndrome
#define QRY_PAGES_OUT( golan, idx )		((struct golan_query_pages_outbox *)(get_cmd( (golan) , (idx) )->out))

#define VIRT_2_BE64_BUS( addr )		cpu_to_be64(((unsigned long long )virt_to_bus(addr)))
#define BE64_BUS_2_VIRT( addr )		bus_to_virt(be64_to_cpu(addr))
#define USR_2_BE64_BUS( addr )		cpu_to_be64(((unsigned long long )user_to_phys(addr, 0)))
#define BE64_BUS_2_USR( addr )		be64_to_cpu(phys_to_user(addr))

#define GET_INBOX(golan, idx)		(&(((struct mbox *)(golan->mboxes.inbox))[idx]))
#define GET_OUTBOX(golan, idx)		(&(((struct mbox *)(golan->mboxes.outbox))[idx]))

#define GOLAN_MBOX_IN( cmd_ptr, in_ptr ) ( {				  \
	union {								  \
		__be32 raw[4];						  \
		typeof ( *(in_ptr) ) cooked;				  \
	} *u = container_of ( &(cmd_ptr)->in[0], typeof ( *u ), raw[0] ); \
	&u->cooked; } )

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

/* Fw status fields */
typedef enum {
	NO_ERRORS          = 0x0,
	SIGNATURE_ERROR    = 0x1,
	TOKEN_ERROR        = 0x2,
	BAD_BLOCK_NUMBER   = 0x3,
	BAD_OUTPUT_POINTER = 0x4,   // pointer not align to mailbox size
	BAD_INPUT_POINTER  = 0x5,   // pointer not align to mailbox size
	INTERNAL_ERROR     = 0x6,
	INPUT_LEN_ERROR    = 0x7,   // input  length less than 0x8.
	OUTPUT_LEN_ERROR   = 0x8,   // output length less than 0x8.
	RESERVE_NOT_ZERO   = 0x9,
	BAD_CMD_TYPE       = 0x10,
} return_hdr_t;

struct golan_cmdq_md {
	void	*addr;
	u16	log_stride;
	u16	size;
};

struct golan_uar {
    uint32_t    	index;
    void        	*virt;
    unsigned long	phys;
};


struct golan_firmware_area {
	/* length of area in pages */
	uint32_t npages;
	/** Firmware area in external memory
	 *
	 * This is allocated when first needed, and freed only on
	 * final teardown, in order to avoid memory map changes at
	 * runtime.
	 */
	userptr_t area;
};
/* Queue Pair */
#define GOLAN_SEND_WQE_BB_SIZE			64
#define GOLAN_SEND_UD_WQE_SIZE			sizeof(struct golan_send_wqe_ud)
#define GOLAN_RECV_WQE_SIZE				sizeof(struct golan_recv_wqe_ud)
#define GOLAN_WQEBBS_PER_SEND_UD_WQE	DIV_ROUND_UP(GOLAN_SEND_UD_WQE_SIZE, GOLAN_SEND_WQE_BB_SIZE)
#define GOLAN_SEND_OPCODE			0x0a
#define GOLAN_WQE_CTRL_WQE_IDX_BIT	8

enum golan_ib_qp_state {
	GOLAN_IB_QPS_RESET,
	GOLAN_IB_QPS_INIT,
	GOLAN_IB_QPS_RTR,
	GOLAN_IB_QPS_RTS,
	GOLAN_IB_QPS_SQD,
	GOLAN_IB_QPS_SQE,
	GOLAN_IB_QPS_ERR
};

struct golan_send_wqe_ud {
	struct golan_wqe_ctrl_seg ctrl;
	struct golan_av datagram;
	struct golan_wqe_data_seg data;
};

union golan_send_wqe {
	struct golan_send_wqe_ud ud;
	uint8_t pad[GOLAN_WQEBBS_PER_SEND_UD_WQE * GOLAN_SEND_WQE_BB_SIZE];
};

struct golan_recv_wqe_ud {
	struct golan_wqe_data_seg data[2];
};

struct golan_recv_wq {
	struct golan_recv_wqe_ud *wqes;
	/* WQ size in bytes */
	int	size;
	/* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */
	u16 next_idx;
	/** GRH buffers (if applicable) */
	struct ib_global_route_header *grh;
	/** Size of GRH buffers */
	size_t grh_size;
};

struct golan_send_wq {
	union golan_send_wqe *wqes;
	/* WQ size in bytes */
	int size;
	/* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */
	u16 next_idx;
};

struct golan_queue_pair {
	void *wqes;
	int size;
	struct golan_recv_wq rq;
	struct golan_send_wq sq;
	struct golan_qp_db *doorbell_record;
	u32 doorbell_qpn;
	enum golan_ib_qp_state state;
};

/* Completion Queue */
#define GOLAN_CQE_OPCODE_NOT_VALID	0x0f
#define GOLAN_CQE_OPCODE_BIT		4
#define GOLAN_CQ_DB_RECORD_SIZE		sizeof(uint64_t)
#define GOLAN_CQE_OWNER_MASK		1

#define MANAGE_PAGES_PSA_OFFSET		0
#define PXE_CMDIF_REF			5

enum {
	GOLAN_CQE_SW_OWNERSHIP = 0x0,
	GOLAN_CQE_HW_OWNERSHIP = 0x1
};

enum {
	GOLAN_CQE_SIZE_64	= 0,
	GOLAN_CQE_SIZE_128	= 1
};

struct golan_completion_queue {
	struct golan_cqe64	*cqes;
	int					size;
	__be64		*doorbell_record;
};


/* Event Queue */
#define GOLAN_EQE_SIZE				sizeof(struct golan_eqe)
#define GOLAN_NUM_EQES 				8
#define GOLAN_EQ_DOORBELL_OFFSET		0x40
#define DB_BUFFER0_EVEN_OFFSET	0x800
#define DB_BUFFER0_ODD_OFFSET	0x900

#define GOLAN_EQ_MAP_ALL_EVENTS					\
	((1 << GOLAN_EVENT_TYPE_PATH_MIG         	)|	\
	(1 << GOLAN_EVENT_TYPE_COMM_EST          	)|	\
	(1 << GOLAN_EVENT_TYPE_SQ_DRAINED        	)|	\
	(1 << GOLAN_EVENT_TYPE_SRQ_LAST_WQE		)|	\
	(1 << GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT      	)|	\
	(1 << GOLAN_EVENT_TYPE_CQ_ERROR          	)|	\
	(1 << GOLAN_EVENT_TYPE_WQ_CATAS_ERROR    	)|	\
	(1 << GOLAN_EVENT_TYPE_PATH_MIG_FAILED   	)|	\
	(1 << GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR	)|	\
	(1 << GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR	 	)|	\
	(1 << GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR   	)|	\
	(1 << GOLAN_EVENT_TYPE_INTERNAL_ERROR  	 	)|	\
	(1 << GOLAN_EVENT_TYPE_PORT_CHANGE   	  	)|	\
	(1 << GOLAN_EVENT_TYPE_GPIO_EVENT         	)|	\
	(1 << GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER 	)|	\
	(1 << GOLAN_EVENT_TYPE_REMOTE_CONFIG     	)|	\
	(1 << GOLAN_EVENT_TYPE_DB_BF_CONGESTION   	)|	\
	(1 << GOLAN_EVENT_TYPE_STALL_EVENT        	)|	\
	(1 << GOLAN_EVENT_TYPE_PACKET_DROPPED     	)|	\
	(1 << GOLAN_EVENT_TYPE_CMD             	  	)|	\
	(1 << GOLAN_EVENT_TYPE_PAGE_REQUEST       	))

enum golan_event {
	GOLAN_EVENT_TYPE_COMP			= 0x0,

	GOLAN_EVENT_TYPE_PATH_MIG		= 0x01,
	GOLAN_EVENT_TYPE_COMM_EST		= 0x02,
	GOLAN_EVENT_TYPE_SQ_DRAINED		= 0x03,
	GOLAN_EVENT_TYPE_SRQ_LAST_WQE		= 0x13,
	GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT		= 0x14,

	GOLAN_EVENT_TYPE_CQ_ERROR		= 0x04,
	GOLAN_EVENT_TYPE_WQ_CATAS_ERROR		= 0x05,
	GOLAN_EVENT_TYPE_PATH_MIG_FAILED	= 0x07,
	GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR	= 0x10,
	GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR	= 0x11,
	GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR	= 0x12,

	GOLAN_EVENT_TYPE_INTERNAL_ERROR		= 0x08,
	GOLAN_EVENT_TYPE_PORT_CHANGE		= 0x09,
	GOLAN_EVENT_TYPE_GPIO_EVENT		= 0x15,
//	GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER	= 0x16,
	GOLAN_EVENT_TYPE_REMOTE_CONFIG		= 0x19,

	GOLAN_EVENT_TYPE_DB_BF_CONGESTION	= 0x1a,
	GOLAN_EVENT_TYPE_STALL_EVENT		= 0x1b,

	GOLAN_EVENT_TYPE_PACKET_DROPPED		= 0x1f,

	GOLAN_EVENT_TYPE_CMD			= 0x0a,
	GOLAN_EVENT_TYPE_PAGE_REQUEST		= 0x0b,
	GOLAN_EVENT_TYPE_PAGE_FAULT		= 0x0C,
};

enum golan_port_sub_event {
    GOLAN_PORT_CHANGE_SUBTYPE_DOWN		= 1,
    GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE		= 4,
    GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED	= 5,
    GOLAN_PORT_CHANGE_SUBTYPE_LID		= 6,
    GOLAN_PORT_CHANGE_SUBTYPE_PKEY		= 7,
    GOLAN_PORT_CHANGE_SUBTYPE_GUID		= 8,
    GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG	= 9
};


enum {
	GOLAN_EQE_SW_OWNERSHIP = 0x0,
	GOLAN_EQE_HW_OWNERSHIP = 0x1
};

enum {
	GOLAN_EQ_UNARMED	= 0,
	GOLAN_EQ_ARMED		= 1,
};

struct golan_event_queue {
	uint8_t			eqn;
	uint64_t		mask;
	struct golan_eqe	*eqes;
	int			size;
	__be32			*doorbell;
	uint32_t		cons_index;
};

struct golan_port {
	/** Infiniband device */
	struct ib_device	*ibdev;
	/** Network device */
	struct net_device	*netdev;
	/** VEP number */
	u8 vep_number;
};

struct golan_mboxes {
	void 	*inbox;
	void	*outbox;
};

#define GOLAN_OPEN	0x1

struct golan {
	struct pci_device		*pci;
	struct golan_hca_init_seg	*iseg;
	struct golan_cmdq_md		cmd;
	struct golan_hca_cap		caps; /* stored as big indian*/
	struct golan_mboxes		mboxes;
	struct list_head		pages;
	uint32_t			cmd_bm;
	uint32_t			total_dma_pages;
	struct golan_uar		uar;
	struct golan_event_queue 	eq;
	uint32_t			pdn;
	u32				mkey;
	u32				flags;
	mlx_utils		*utils;

	struct golan_port		ports[GOLAN_MAX_PORTS];
#define GOLAN_FW_AREAS_NUM 2
	struct golan_firmware_area fw_areas[GOLAN_FW_AREAS_NUM];
};

#endif /* _GOLAN_H_*/
