summaryrefslogblamecommitdiffstats
path: root/src/drivers/infiniband/golan.c
blob: ce02a867fd6a948f11cb4828e9fd60702bf4ae34 (plain) (tree)
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958






















                                                                      





                            
                            

                          
                       
                    


                                                
                       
                                        



                                                            
 
 
                                                    
 



                                                                                
 




                              







                                                          


         

                                                        
 
                        
                             
                                                       

         

                                                
 
                  
 
                                          

                  
 











                                                                                
                                                                                          















































                                                                                                                                         






































































                                                                                  



















































































































































                                                                                                       
                                                  
































                                                                                            

                          





                                                             

                                                    
 

                                                                                                  











                                                                                                                  
                                                                                                                     















                                                                                                    
                  

 

                                                                            
                             

                          
                   
                                         

                                          










                                                                              







                                                             

                                                                                                  










                                                                                                              


                                                                                 


















                                                                                                                  






                                                          
                        




































                                                                                       
                                                                                                    

































                                                                                                                                 


                                                         













                                                         
                                                                                  
                             
                                        
         
                                                                                      
                             
                                              
         
                                                                                       
                             
                                               















                                                                                      




                                                        


























































                                                                                                  
                                                                                  













































                                                                                       


                                                               
                                                                           
                        


                                             













                                                                                
                                                              

















                                                                                                
                                                 







                                                        
                                            









                                                                                  

                                       


                                                                                       
                                                       


















































































































                                                                                                   

                                                                             






























                                                               






                                                                                   
                                                                              





                                                                                 
                                                                          
                              


                                             















                                                                                            
                                                                    
















                                                                                                
                                                      
                        
                                                                      

































                                                                                       

                                                                      











































                                                                             














































                                                                                             
                                                                          
                              


                                             
















                                                                                                
                                                                           














                                                                                
                                                                          



































                                                                                                                      
                                                                         
                       
                                                      




















































                                                                                                          
                                            























































































































































                                                                                                

                                                                         




































                                                                                   
















































                                                                                                  


                                                                                    





























































                                                                                               
                                                       







                                                                                   

                                       






















                                                                                                       
                                                   







                                                                              


                                       















                                                                                            
                                                    








                                                                                                      


                                       


                                                                                            














































































































































































































































































































                                                                                                             
                                  











































































                                                                                               




                                                                                        

                                                   

                                                                                







                                                                                          
                  






















































































































                                                                                          



















                                                                        

                          
                             
 




                                                                                                 

                                                       
                                                                                                   



                                                                                                         

                                          















































































                                                                                   









                                                        







                                                      
                                   



                                           

                                         





































                                                          

                                                                             








                                                





                                                       





                                                               











                                                         
                                                     





                                                       
                                                                  
                                                            
                 

         

                                   










                                                           



                                                  

                                   
                               
                                      



















                                                                  



                                                                  


                                                  
                               
                                      





                                                                            







                                                                           
                                                                     





                                                             


                                                                                       



                      











































































































                                                                                           
                                                          

  
                                                                   
                                            
















                                                                            
                                                    
                                                                  
                                                                                    
                











                                                                  
                                                      















                                                                                             

                                                                                             

                                                                                             

                                                                                             
                                                                                                                                 
                                                                                                               
                                                                                                               







                                                                       
/*
 * 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 <errno.h>
#include <strings.h>
#include <ipxe/malloc.h>
#include <ipxe/umalloc.h>
#include <ipxe/infiniband.h>
#include <ipxe/ib_smc.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include "flexboot_nodnic.h"
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <usr/ifmgmt.h>
#include <ipxe/in.h>
#include <byteswap.h>
#include "mlx_utils/include/public/mlx_pci_gw.h"
#include <config/general.h>
#include <ipxe/ipoib.h>
#include "mlx_nodnic/include/mlx_port.h"
#include "nodnic_shomron_prm.h"
#include "golan.h"
#include "mlx_utils/include/public/mlx_bail.h"
#include "mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h"


#define DEVICE_IS_CIB( device ) ( device == 0x1011 )

/******************************************************************************/
/************* Very simple memory management for umalloced pages **************/
/******* Temporary solution until full memory management is implemented *******/
/******************************************************************************/

struct golan_page {
	struct list_head list;
	userptr_t addr;
};

static void golan_free_fw_areas ( struct golan *golan ) {
	int i;

	for (i = 0; i < GOLAN_FW_AREAS_NUM; i++) {
		if ( golan->fw_areas[i].area ) {
			ufree ( golan->fw_areas[i].area );
			golan->fw_areas[i].area = UNULL;
		}
	}
}

static int golan_init_fw_areas ( struct golan *golan ) {
	int rc = 0, i =  0;

	if ( ! golan ) {
		rc = -EINVAL;
		goto err_golan_init_fw_areas_bad_param;
	}

	for (i = 0; i < GOLAN_FW_AREAS_NUM; i++)
		golan->fw_areas[i].area = UNULL;

	return rc;

	err_golan_init_fw_areas_bad_param:
	return rc;
}

/******************************************************************************/

const char *golan_qp_state_as_string[] = {
	"RESET",
	"INIT",
	"RTR",
	"RTS",
	"SQD",
	"SQE",
	"ERR"
};

static inline int golan_check_rc_and_cmd_status ( struct golan_cmd_layout *cmd, int rc ) {
	struct golan_outbox_hdr *out_hdr = ( struct golan_outbox_hdr * ) ( cmd->out );
	if ( rc == -EBUSY ) {
		DBG ( "HCA is busy (rc = -EBUSY)\n" );
		return rc;
	} else if ( out_hdr->status ) {
		DBG("%s status = 0x%x - syndrom = 0x%x\n", __FUNCTION__,
				out_hdr->status, be32_to_cpu(out_hdr->syndrome));
		return out_hdr->status;
	}
	return 0;
}

#define GOLAN_CHECK_RC_AND_CMD_STATUS(_lable)							\
		do {															\
			if ( ( rc = golan_check_rc_and_cmd_status ( cmd, rc ) ) )	\
				goto _lable;											\
		} while (0)

#define GOLAN_PRINT_RC_AND_CMD_STATUS	golan_check_rc_and_cmd_status ( cmd, rc )


struct mbox {
	union {
		struct golan_cmd_prot_block	mblock;
		u8	data[MAILBOX_STRIDE];
		__be64	qdata[MAILBOX_STRIDE >> 3];
	};
};

static inline uint32_t  ilog2(uint32_t mem)
{
	return ( fls ( mem ) - 1 );
}

#define	CTRL_SIG_SZ	(sizeof(mailbox->mblock) - sizeof(mailbox->mblock.bdata) - 2)

static inline u8 xor8_buf(void *buf, int len)
{
	u8 sum = 0;
	int i;
	u8 *ptr = buf;

	for (i = 0; i < len; ++i)
		sum ^= ptr[i];

	return sum;
}

static inline const char *cmd_status_str(u8 status)
{
	switch (status) {
		case 0x0:	return "OK";
		case 0x1:	return "internal error";
		case 0x2:	return "bad operation";
		case 0x3:	return "bad parameter";
		case 0x4:	return "bad system state";
		case 0x5:	return "bad resource";
		case 0x6:	return "resource busy";
		case 0x8:	return "limits exceeded";
		case 0x9:	return "bad resource state";
		case 0xa:	return "bad index";
		case 0xf:	return "no resources";
		case 0x50:	return "bad input length";
		case 0x51:	return "bad output length";
		case 0x10:	return "bad QP state";
		case 0x30:	return "bad packet (discarded)";
		case 0x40:	return "bad size too many outstanding CQEs";
		case 0xff:	return "Command Timed Out";
		default:	return "unknown status";
	}
}

static inline uint16_t fw_rev_maj(struct golan *golan)
{
	return be32_to_cpu(readl(&golan->iseg->fw_rev)) & 0xffff;
}

static inline u16 fw_rev_min(struct golan *golan)
{
	return be32_to_cpu(readl(&golan->iseg->fw_rev)) >> 16;
}

static inline u16 fw_rev_sub(struct golan *golan)
{
	return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) & 0xffff;
}

static inline u16 cmdif_rev(struct golan *golan)
{
	return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) >> 16;
}


static inline struct golan_cmd_layout *get_cmd( struct golan *golan, int idx )
{
	return golan->cmd.addr + (idx << golan->cmd.log_stride);
}

static inline void golan_calc_sig(struct golan *golan, uint32_t cmd_idx,
				uint32_t inbox_idx, uint32_t outbox_idx)
{
	struct golan_cmd_layout *cmd	= get_cmd(golan, cmd_idx);
	struct mbox *mailbox = NULL;

	if (inbox_idx != NO_MBOX) {
		mailbox				= GET_INBOX(golan, inbox_idx);
		mailbox->mblock.token		= cmd->token;
		mailbox->mblock.ctrl_sig	= ~xor8_buf(mailbox->mblock.rsvd0,
								CTRL_SIG_SZ);
	}
	if (outbox_idx != NO_MBOX) {
		mailbox				= GET_OUTBOX(golan, outbox_idx);
		mailbox->mblock.token		= cmd->token;
		mailbox->mblock.ctrl_sig	= ~xor8_buf(mailbox->mblock.rsvd0,
								CTRL_SIG_SZ);
	}
	cmd->sig = ~xor8_buf(cmd, sizeof(*cmd));
}

static inline void show_out_status(uint32_t *out)
{
	DBG("%x\n", be32_to_cpu(out[0]));
	DBG("%x\n", be32_to_cpu(out[1]));
	DBG("%x\n", be32_to_cpu(out[2]));
	DBG("%x\n", be32_to_cpu(out[3]));
}
/**
  * Check if CMD has finished.
  */
static inline uint32_t is_command_finished( struct golan *golan, int idx)
{
	wmb();
	return !(get_cmd( golan , idx )->status_own & CMD_OWNER_HW);
}

/**
 * Wait for Golan command completion
 *
 * @v golan		Golan device
 * @ret rc		Return status code
 */
static inline int golan_cmd_wait(struct golan *golan, int idx, const char *command)
{
	unsigned int wait;
	int	rc = -EBUSY;

	for ( wait = GOLAN_HCR_MAX_WAIT_MS ; wait ; --wait ) {
		if (is_command_finished(golan, idx)) {
			rc = CMD_STATUS(golan, idx);
			rmb();
			break;
		} else {
			mdelay ( 1 );
		}
	}
	if (rc) {
		DBGC (golan ,"[%s]RC is %s[%x]\n", command, cmd_status_str(rc), rc);
	}

	golan->cmd_bm &= ~(1 << idx);
	return rc;
}

/**
  * Notify the HW that commands are ready
  */
static inline void send_command(struct golan *golan)
{
	wmb(); //Make sure the command is visible in "memory".
	writel(cpu_to_be32(golan->cmd_bm) , &golan->iseg->cmd_dbell);
}

static inline int send_command_and_wait(struct golan *golan, uint32_t cmd_idx,
					uint32_t inbox_idx, uint32_t outbox_idx, const char *command)
{
	golan_calc_sig(golan, cmd_idx, inbox_idx, outbox_idx);
	send_command(golan);
	return golan_cmd_wait(golan, cmd_idx, command);
}

/**
  * Prepare a FW command,
  * In - comamnd idx (Must be valid)
  * writes the command parameters.
  */
static inline struct golan_cmd_layout *write_cmd(struct golan *golan, int idx,
							uint16_t opcode, uint16_t opmod,
							uint16_t inbox_idx,
							uint16_t outbox_idx, uint16_t inlen,
							uint16_t outlen)
{
	struct golan_cmd_layout	*cmd	= get_cmd(golan , idx);
	struct golan_inbox_hdr *hdr	= (struct golan_inbox_hdr *)cmd->in;
	static uint8_t token;

	memset(cmd, 0, sizeof(*cmd));

	cmd->type		= GOLAN_PCI_CMD_XPORT;
	cmd->status_own		= CMD_OWNER_HW;
	cmd->outlen		= cpu_to_be32(outlen);
	cmd->inlen		= cpu_to_be32(inlen);
	hdr->opcode		= cpu_to_be16(opcode);
	hdr->opmod		= cpu_to_be16(opmod);

	if (inbox_idx != NO_MBOX) {
		memset(GET_INBOX(golan, inbox_idx), 0, MAILBOX_SIZE);
		cmd->in_ptr	= VIRT_2_BE64_BUS(GET_INBOX(golan, inbox_idx));
		cmd->token	= ++token;
	}
	if (outbox_idx != NO_MBOX) {
		memset(GET_OUTBOX(golan, outbox_idx), 0, MAILBOX_SIZE);
		cmd->out_ptr = VIRT_2_BE64_BUS(GET_OUTBOX(golan, outbox_idx));
	}

	golan->cmd_bm |= 1 << idx;

	assert ( cmd != NULL );
	return cmd;
}

static inline int golan_core_enable_hca(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	int rc = 0;

	DBGC(golan, "%s\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ENABLE_HCA, 0x0,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_enable_hca_mbox_in),
			sizeof(struct golan_enable_hca_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	return rc;
}

static inline void golan_disable_hca(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DISABLE_HCA, 0x0,
					NO_MBOX, NO_MBOX,
				    sizeof(struct golan_disable_hca_mbox_in),
				    sizeof(struct golan_disable_hca_mbox_out));
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
}

static inline int golan_set_hca_cap(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	int rc;

	DBGC(golan, "%s\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_SET_HCA_CAP, 0x0,
			GEN_MBOX, NO_MBOX,
			sizeof(struct golan_cmd_set_hca_cap_mbox_in),
			sizeof(struct golan_cmd_set_hca_cap_mbox_out));

	golan->caps.flags &= ~GOLAN_DEV_CAP_FLAG_CMDIF_CSUM;
	DBGC( golan , "%s caps.uar_sz = %d\n", __FUNCTION__, golan->caps.uar_sz);
	DBGC( golan , "%s caps.log_pg_sz = %d\n", __FUNCTION__, golan->caps.log_pg_sz);
	DBGC( golan , "%s caps.log_uar_sz = %d\n", __FUNCTION__, be32_to_cpu(golan->caps.uar_page_sz));
	golan->caps.uar_page_sz = 0;
	golan->caps.log_max_qp = GOLAN_LOG_MAX_QP;

	memcpy(((struct golan_hca_cap *)GET_INBOX(golan, GEN_MBOX)),
		   &(golan->caps),
		   sizeof(struct golan_hca_cap));

	//if command failed we should reset the caps in golan->caps
	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	return rc;
}

static inline int golan_qry_hca_cap(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	int rc = 0;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_CAP, 0x1,
					NO_MBOX, GEN_MBOX,
					sizeof(struct golan_cmd_query_hca_cap_mbox_in),
					sizeof(struct golan_cmd_query_hca_cap_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, GEN_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_hca_cap );

	memcpy(&(golan->caps),
		   ((struct golan_hca_cap *)GET_OUTBOX(golan, GEN_MBOX)),
		   sizeof(struct golan_hca_cap));
err_query_hca_cap:
	return rc;
}

static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 func_id ) {
	uint32_t out_num_entries = 0;
	int size_ibox = 0;
	int size_obox = 0;
	int rc = 0;

	DBGC(golan, "%s\n", __FUNCTION__);

	while ( pages > 0 ) {
		uint32_t pas_num = min(pages, MAX_PASE_MBOX);
		struct golan_cmd_layout	*cmd;
		struct golan_manage_pages_inbox *in;

		size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE);
		size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE);

		cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_TAKE,
				MEM_MBOX, MEM_MBOX,
				size_ibox,
				size_obox);

		in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */

		in->func_id 	= func_id; /* Already BE */
		in->num_entries = cpu_to_be32(pas_num);

		if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) {
			out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries);
		} else {
			if ( rc == -EBUSY ) {
				DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" );
			} else {
				DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n",
						__FUNCTION__, rc, cmd_status_str(rc),
						CMD_SYND(golan, MEM_CMD_IDX),
						get_cmd( golan , MEM_CMD_IDX )->status_own,
						be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num);
			}
			return rc;
		}

		pages -= out_num_entries;
	}
	DBGC( golan , "%s Pages handled\n", __FUNCTION__);
	return rc;
}

static inline int golan_provide_pages ( struct golan *golan , uint32_t pages
		, __be16 func_id,struct golan_firmware_area *fw_area) {
	struct mbox *mailbox;
	int size_ibox = 0;
	int size_obox = 0;
	int rc = 0;
	userptr_t next_page_addr = UNULL;

	DBGC(golan, "%s\n", __FUNCTION__);
	if ( ! fw_area->area ) {
		fw_area->area = umalloc ( GOLAN_PAGE_SIZE * pages );
		if ( fw_area->area == UNULL ) {
			rc = -ENOMEM;
			DBGC (golan ,"Failed to allocated %d pages \n",pages);
			goto err_golan_alloc_fw_area;
		}
		fw_area->npages = pages;
	}
	assert ( fw_area->npages == pages );
	next_page_addr = fw_area->area;
	while ( pages > 0 ) {
		uint32_t pas_num = min(pages, MAX_PASE_MBOX);
		unsigned i, j;
		struct golan_cmd_layout	*cmd;
		struct golan_manage_pages_inbox *in;
		userptr_t addr = 0;

		mailbox = GET_INBOX(golan, MEM_MBOX);
		size_ibox = sizeof(struct golan_manage_pages_inbox) + (pas_num * GOLAN_PAS_SIZE);
		size_obox = sizeof(struct golan_manage_pages_outbox) + (pas_num * GOLAN_PAS_SIZE);

		cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_GIVE,
				MEM_MBOX, MEM_MBOX,
				size_ibox,
				size_obox);

		in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */

		in->func_id 	= func_id; /* Already BE */
		in->num_entries = cpu_to_be32(pas_num);

		for ( i = 0 , j = MANAGE_PAGES_PSA_OFFSET; i < pas_num; ++i ,++j,
				next_page_addr += GOLAN_PAGE_SIZE ) {
			addr = next_page_addr;
			if (GOLAN_PAGE_MASK & user_to_phys(addr, 0)) {
				DBGC (golan ,"Addr not Page alligned [%lx %lx]\n", user_to_phys(addr, 0), addr);
			}
			mailbox->mblock.data[j]	= USR_2_BE64_BUS(addr);
		}

		if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) {
			pages -= pas_num;
			golan->total_dma_pages += pas_num;
		} else {
			if ( rc == -EBUSY ) {
				DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" );
			} else {
				DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n",
						__FUNCTION__, rc, cmd_status_str(rc),
						CMD_SYND(golan, MEM_CMD_IDX),
						get_cmd( golan , MEM_CMD_IDX )->status_own,
						be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num);
			}
			goto err_send_command;
		}
	}
	DBGC( golan , "%s Pages handled\n", __FUNCTION__);
	return 0;

err_send_command:
err_golan_alloc_fw_area:
	/* Go over In box and free pages */
	/* Send Error to FW */
	/* What is next - Disable HCA? */
	DBGC (golan ,"%s Failed (rc = 0x%x)\n", __FUNCTION__, rc);
	return rc;
}

static inline int golan_handle_pages(struct golan *golan,
					enum golan_qry_pages_mode qry,
					enum golan_manage_pages_mode mode)
{
	struct golan_cmd_layout	*cmd;

	int rc = 0;
	int32_t pages;
	uint16_t total_pages;
	__be16	func_id;

	DBGC(golan, "%s\n", __FUNCTION__);

	cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, qry,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_query_pages_inbox),
			sizeof(struct golan_query_pages_outbox));

	rc = send_command_and_wait(golan, MEM_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_handle_pages_query );

	pages = be32_to_cpu(QRY_PAGES_OUT(golan, MEM_CMD_IDX)->num_pages);

	DBGC( golan , "%s pages needed: %d\n", __FUNCTION__, pages);

	func_id	= QRY_PAGES_OUT(golan, MEM_CMD_IDX)->func_id;

	total_pages = (( pages >= 0 ) ? pages : ( pages * ( -1 ) ));

	if ( mode == GOLAN_PAGES_GIVE ) {
		rc = golan_provide_pages(golan, total_pages, func_id, & ( golan->fw_areas[qry-1] ));
	} else {
		rc = golan_take_pages(golan, golan->total_dma_pages, func_id);
		golan->total_dma_pages = 0;
	}

	if ( rc ) {
		DBGC (golan , "Failed to %s pages (rc = %d) - DMA pages allocated = %d\n",
			( ( mode == GOLAN_PAGES_GIVE ) ? "give" : "take" ), rc , golan->total_dma_pages );
		return rc;
	}

	return 0;

err_handle_pages_query:
	DBGC (golan ,"%s Qyery pages failed (rc = 0x%x)\n", __FUNCTION__, rc);
	return rc;
}

static inline int golan_set_access_reg ( struct golan *golan __attribute__ (( unused )), uint32_t reg __attribute__ (( unused )))
{
#if 0
	write_cmd(golan, _CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, 0x0,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_reg_host_endianess),
			sizeof(struct golan_reg_host_endianess));
        in->arg = cpu_to_be32(arg);
        in->register_id = cpu_to_be16(reg_num);
#endif
	DBGC (golan ," %s Not implemented yet\n", __FUNCTION__);
	return 0;
}

static inline void golan_cmd_uninit ( struct golan *golan )
{
	free_phys(golan->mboxes.outbox, GOLAN_PAGE_SIZE);
	free_phys(golan->mboxes.inbox, GOLAN_PAGE_SIZE);
	free_phys(golan->cmd.addr, GOLAN_PAGE_SIZE);
}

/**
 * Initialise Golan Command Q parameters
 *	-- Alocate a 4kb page for the Command Q
 *	-- Read the stride and log num commands available
 *	-- Write the address to cmdq_phy_addr in iseg
 * @v golan		Golan device
 */
static inline int golan_cmd_init ( struct golan *golan )
{
	int rc = 0;
	uint32_t addr_l_sz;

	if (!(golan->cmd.addr = malloc_phys(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) {
		rc = -ENOMEM;
		goto malloc_phys_failed;
	}
	if (!(golan->mboxes.inbox = malloc_phys(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) {
		rc = -ENOMEM;
		goto malloc_phys_inbox_failed;
	}
	if (!(golan->mboxes.outbox = malloc_phys(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) {
		rc = -ENOMEM;
		goto malloc_phys_outbox_failed;
	}
	addr_l_sz	= be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz));

	golan->cmd.log_stride	= addr_l_sz & 0xf;
	golan->cmd.size		= 1 << (( addr_l_sz >> 4 ) & 0xf);

	addr_l_sz = virt_to_bus(golan->cmd.addr);
	writel(0 /* cpu_to_be32(golan->cmd.addr) >> 32 */, &golan->iseg->cmdq_addr_h);
	writel(cpu_to_be32(addr_l_sz), &golan->iseg->cmdq_addr_l_sz);
	wmb(); //Make sure the addr is visible in "memory".

	addr_l_sz = be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz));

	DBGC( golan , "%s Command interface was initialized\n", __FUNCTION__);
	return 0;

malloc_phys_outbox_failed:
	free_phys(golan->mboxes.inbox, GOLAN_PAGE_SIZE);
malloc_phys_inbox_failed:
	free_phys(golan->cmd.addr, GOLAN_PAGE_SIZE);
malloc_phys_failed:
	DBGC (golan ,"%s Failed to initialize command interface (rc = 0x%x)\n",
		   __FUNCTION__, rc);
	return rc;
}

static inline int golan_hca_init(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	int rc = 0;

	DBGC(golan, "%s\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_INIT_HCA, 0x0,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_cmd_init_hca_mbox_in),
			sizeof(struct golan_cmd_init_hca_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	return rc;
}

static inline void golan_teardown_hca(struct golan *golan, enum golan_teardown op_mod)
{
	struct golan_cmd_layout	*cmd;
	int rc;

	DBGC (golan, "%s in\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_TEARDOWN_HCA, op_mod,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_cmd_teardown_hca_mbox_in),
			sizeof(struct golan_cmd_teardown_hca_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;

	DBGC (golan, "%s HCA teardown compleated\n", __FUNCTION__);
}

static inline int golan_alloc_uar(struct golan *golan)
{
	struct golan_uar *uar = &golan->uar;
	struct golan_cmd_layout *cmd;
	struct golan_alloc_uar_mbox_out *out;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_UAR, 0x0,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_alloc_uar_mbox_in),
			sizeof(struct golan_alloc_uar_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_uar_cmd );
	out = (struct golan_alloc_uar_mbox_out *) ( cmd->out );

	uar->index	= be32_to_cpu(out->uarn) & 0xffffff;

	uar->phys = (pci_bar_start(golan->pci, GOLAN_HCA_BAR) + (uar->index << GOLAN_PAGE_SHIFT));
	uar->virt = (void *)(pci_ioremap(golan->pci, uar->phys, GOLAN_PAGE_SIZE));

	DBGC( golan , "%s: UAR allocated with index 0x%x\n", __FUNCTION__, uar->index);
	return 0;

err_alloc_uar_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static void golan_dealloc_uar(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	uint32_t uar_index = golan->uar.index;
	int rc;

	DBGC (golan, "%s in\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_UAR, 0x0,
					NO_MBOX, NO_MBOX,
					sizeof(struct golan_free_uar_mbox_in),
					sizeof(struct golan_free_uar_mbox_out));

	((struct golan_free_uar_mbox_in *)(cmd->in))->uarn = cpu_to_be32(uar_index);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	golan->uar.index = 0;

	DBGC (golan, "%s UAR (0x%x) was destroyed\n", __FUNCTION__, uar_index);
}

static void golan_eq_update_ci(struct golan_event_queue *eq, int arm)
{
	__be32 *addr = eq->doorbell + (arm ? 0 : 2);
	u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24);
	writel(cpu_to_be32(val) , addr);
	/* We still want ordering, just not swabbing, so add a barrier */
	wmb();
}

static int golan_create_eq(struct golan *golan)
{
	struct golan_event_queue *eq = &golan->eq;
	struct golan_create_eq_mbox_in_data *in;
	struct golan_cmd_layout	*cmd;
	struct golan_create_eq_mbox_out *out;
	int rc, i;

	eq->cons_index	= 0;
	eq->size	= GOLAN_NUM_EQES * sizeof(eq->eqes[0]);
	eq->eqes	= malloc_phys ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
	if (!eq->eqes) {
		rc = -ENOMEM;
		goto err_create_eq_eqe_alloc;
	}

	/* Set EQEs ownership bit to HW ownership */
	for (i = 0; i < GOLAN_NUM_EQES; ++i) {
		eq->eqes[i].owner = GOLAN_EQE_HW_OWNERSHIP;
	}

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_EQ, 0x0,
			GEN_MBOX, NO_MBOX,
			sizeof(struct golan_create_eq_mbox_in) + GOLAN_PAS_SIZE,
			sizeof(struct golan_create_eq_mbox_out));

	in = (struct golan_create_eq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);

	/* Fill the physical address of the page */
	in->pas[0]		= VIRT_2_BE64_BUS( eq->eqes );
	in->ctx.log_sz_usr_page	= cpu_to_be32((ilog2(GOLAN_NUM_EQES)) << 24 | golan->uar.index);
	DBGC( golan , "UAR idx %x (BE %x)\n", golan->uar.index, in->ctx.log_sz_usr_page);
	in->events_mask		= cpu_to_be64(1 << GOLAN_EVENT_TYPE_PORT_CHANGE);

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_eq_cmd );
	out = (struct golan_create_eq_mbox_out *)cmd->out;

	eq->eqn		= out->eq_number;
	eq->doorbell	= ((void *)golan->uar.virt) + GOLAN_EQ_DOORBELL_OFFSET;

	/* EQs are created in ARMED state */
	golan_eq_update_ci(eq, GOLAN_EQ_UNARMED);

	DBGC( golan , "%s: Event queue created (EQN = 0x%x)\n", __FUNCTION__, eq->eqn);
	return 0;

err_create_eq_cmd:
	free_phys ( eq->eqes , GOLAN_PAGE_SIZE );
err_create_eq_eqe_alloc:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static void golan_destory_eq(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	struct golan_destroy_eq_mbox_in *in;
	uint8_t eqn = golan->eq.eqn;
	int rc;

	DBGC (golan, "%s in\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_EQ, 0x0,
					NO_MBOX, NO_MBOX,
					sizeof(struct golan_destroy_eq_mbox_in),
					sizeof(struct golan_destroy_eq_mbox_out));

	in = GOLAN_MBOX_IN ( cmd, in );
	in->eqn = eqn;
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;

	free_phys ( golan->eq.eqes , GOLAN_PAGE_SIZE );
	golan->eq.eqn = 0;

	DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn);
}

static int golan_alloc_pd(struct golan *golan)
{
	struct golan_cmd_layout *cmd;
	struct golan_alloc_pd_mbox_out *out;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_PD, 0x0,
			NO_MBOX, NO_MBOX,
			sizeof(struct golan_alloc_pd_mbox_in),
			sizeof(struct golan_alloc_pd_mbox_out));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_pd_cmd );
	out = (struct golan_alloc_pd_mbox_out *) ( cmd->out );

	golan->pdn = (be32_to_cpu(out->pdn) & 0xffffff);
	DBGC( golan , "%s: Protection domain created (PDN = 0x%x)\n", __FUNCTION__,
		golan->pdn);
	return 0;

err_alloc_pd_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static void golan_dealloc_pd(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	uint32_t pdn = golan->pdn;
	int rc;

	DBGC (golan,"%s in\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_PD, 0x0,
					NO_MBOX, NO_MBOX,
					sizeof(struct golan_alloc_pd_mbox_in),
					sizeof(struct golan_alloc_pd_mbox_out));

	((struct golan_dealloc_pd_mbox_in *)(cmd->in))->pdn = cpu_to_be32(pdn);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	golan->pdn = 0;

	DBGC (golan ,"%s Protection domain (0x%x) was destroyed\n", __FUNCTION__, pdn);
}

static int golan_create_mkey(struct golan *golan)
{
	struct golan_create_mkey_mbox_in_data *in;
	struct golan_cmd_layout	*cmd;
	struct golan_create_mkey_mbox_out *out;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_MKEY, 0x0,
					GEN_MBOX, NO_MBOX,
					sizeof(struct golan_create_mkey_mbox_in),
					sizeof(struct golan_create_mkey_mbox_out));

	in = (struct golan_create_mkey_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);

	in->seg.flags			= GOLAN_IB_ACCESS_LOCAL_WRITE | GOLAN_IB_ACCESS_LOCAL_READ;
	in->seg.flags_pd		= cpu_to_be32(golan->pdn | GOLAN_MKEY_LEN64);
	in->seg.qpn_mkey7_0		= cpu_to_be32(0xffffff << GOLAN_CREATE_MKEY_SEG_QPN_BIT);

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_mkey_cmd );
	out = (struct golan_create_mkey_mbox_out *) ( cmd->out );

	golan->mkey = ((be32_to_cpu(out->mkey) & 0xffffff) << 8);
	DBGC( golan , "%s: Got DMA Key for local access read/write (MKEY = 0x%x)\n",
		   __FUNCTION__, golan->mkey);
	return 0;
err_create_mkey_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static void golan_destroy_mkey(struct golan *golan)
{
	struct golan_cmd_layout	*cmd;
	u32 mkey = golan->mkey;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_MKEY, 0x0,
					NO_MBOX, NO_MBOX,
					sizeof(struct golan_destroy_mkey_mbox_in),
					sizeof(struct golan_destroy_mkey_mbox_out));
	((struct golan_destroy_mkey_mbox_in *)(cmd->in))->mkey = cpu_to_be32(mkey >> 8);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	golan->mkey = 0;

	DBGC( golan , "%s DMA Key (0x%x) for local access write was destroyed\n"
		   , __FUNCTION__, mkey);
}


/**
 * Initialise Golan PCI parameters
 *
 * @v golan		Golan device
 */
static inline void golan_pci_init(struct golan *golan)
{
	struct pci_device *pci = golan->pci;

	/* Fix up PCI device */
	adjust_pci_device ( pci );

	/* Get HCA BAR */
	golan->iseg = pci_ioremap ( pci, pci_bar_start ( pci, GOLAN_HCA_BAR),
				    GOLAN_PCI_CONFIG_BAR_SIZE );
}

static inline struct golan *golan_alloc()
{
	void *golan = zalloc(sizeof(struct golan));
	if ( !golan )
		goto err_zalloc;

	return golan;

err_zalloc:
	return NULL;
}

/**
 * Create completion queue
 *
 * @v ibdev		Infiniband device
 * @v cq		Completion queue
 * @ret rc		Return status code
 */
static int golan_create_cq(struct ib_device *ibdev,
				struct ib_completion_queue *cq)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_completion_queue *golan_cq;
	struct golan_cmd_layout *cmd;
	struct golan_create_cq_mbox_in_data *in;
	struct golan_create_cq_mbox_out *out;
	int	rc;
	unsigned int i;

	golan_cq = zalloc(sizeof(*golan_cq));
	if (!golan_cq) {
		rc = -ENOMEM;
		goto err_create_cq;
	}
	golan_cq->size 			= sizeof(golan_cq->cqes[0]) * cq->num_cqes;
	golan_cq->doorbell_record 	= malloc_phys(GOLAN_CQ_DB_RECORD_SIZE,
							GOLAN_CQ_DB_RECORD_SIZE);
	if (!golan_cq->doorbell_record) {
		rc = -ENOMEM;
		goto err_create_cq_db_alloc;
	}

	golan_cq->cqes = malloc_phys ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
	if (!golan_cq->cqes) {
		rc = -ENOMEM;
		goto err_create_cq_cqe_alloc;
	}

	/* Set CQEs ownership bit to HW ownership */
	for (i = 0; i < cq->num_cqes; ++i) {
		golan_cq->cqes[i].op_own = ((GOLAN_CQE_OPCODE_NOT_VALID <<
								    GOLAN_CQE_OPCODE_BIT) |
								    GOLAN_CQE_HW_OWNERSHIP);
	}

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_CQ, 0x0,
					GEN_MBOX, NO_MBOX,
				    sizeof(struct golan_create_cq_mbox_in) + GOLAN_PAS_SIZE,
				    sizeof(struct golan_create_cq_mbox_out));

	in = (struct golan_create_cq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);

	/* Fill the physical address of the page */
	in->pas[0]		= VIRT_2_BE64_BUS( golan_cq->cqes );
	in->ctx.cqe_sz_flags	= GOLAN_CQE_SIZE_64 << 5;
	in->ctx.log_sz_usr_page = cpu_to_be32(((ilog2(cq->num_cqes)) << 24) | golan->uar.index);
	in->ctx.c_eqn		= cpu_to_be16(golan->eq.eqn);
	in->ctx.db_record_addr	= VIRT_2_BE64_BUS(golan_cq->doorbell_record);

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_cq_cmd );
	out = (struct golan_create_cq_mbox_out *) ( cmd->out );

	cq->cqn	= (be32_to_cpu(out->cqn) & 0xffffff);

	ib_cq_set_drvdata(cq, golan_cq);

	DBGC( golan , "%s CQ created successfully (CQN = 0x%lx)\n", __FUNCTION__, cq->cqn);
	return 0;

err_create_cq_cmd:
	free_phys( golan_cq->cqes , GOLAN_PAGE_SIZE );
err_create_cq_cqe_alloc:
	free_phys(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE);
err_create_cq_db_alloc:
	free ( golan_cq );
err_create_cq:
	DBGC (golan ,"%s out rc = 0x%x\n", __FUNCTION__, rc);
	return rc;
}

/**
 * Destroy completion queue
 *
 * @v ibdev		Infiniband device
 * @v cq		Completion queue
 */
static void golan_destroy_cq(struct ib_device *ibdev,
				struct ib_completion_queue *cq)
{
	struct golan			*golan		= ib_get_drvdata(ibdev);
	struct golan_completion_queue	*golan_cq	= ib_cq_get_drvdata(cq);
	struct golan_cmd_layout		*cmd;
	uint32_t cqn = cq->cqn;
	int rc;

	DBGC (golan, "%s in\n", __FUNCTION__);

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_CQ, 0x0,
					NO_MBOX, NO_MBOX,
				    sizeof(struct golan_destroy_cq_mbox_in),
				    sizeof(struct golan_destroy_cq_mbox_out));
	((struct golan_destroy_cq_mbox_in *)(cmd->in))->cqn = cpu_to_be32(cqn);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	cq->cqn = 0;

	ib_cq_set_drvdata(cq, NULL);
	free_phys ( golan_cq->cqes , GOLAN_PAGE_SIZE );
	free_phys(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE);
	free(golan_cq);

	DBGC (golan, "%s CQ number 0x%x was destroyed\n", __FUNCTION__, cqn);
}

static void golan_cq_clean(struct ib_completion_queue *cq)
{
	ib_poll_cq(cq->ibdev, cq);
}

static int golan_qp_type_to_st(enum ib_queue_pair_type type)
{
	int qpt = type;

	switch (qpt) {
	case IB_QPT_RC:
		return GOLAN_QP_ST_RC;
	case IB_QPT_UD:
		return GOLAN_QP_ST_UD;
	case IB_QPT_SMI:
		return GOLAN_QP_ST_QP0;
	case IB_QPT_GSI:
		return GOLAN_QP_ST_QP1;
	case IB_QPT_ETH:
	default:
		return -EINVAL;
	}
}
#if 0
static int golan_is_special_qp(enum ib_queue_pair_type type)
{
	return (type == IB_QPT_GSI || type == IB_QPT_SMI);
}
#endif
static int golan_create_qp_aux(struct ib_device *ibdev,
				struct ib_queue_pair *qp,
				int *qpn)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_queue_pair *golan_qp;
	struct golan_create_qp_mbox_in_data *in;
	struct golan_cmd_layout *cmd;
	struct golan_wqe_data_seg *data;
	struct golan_create_qp_mbox_out *out;
	uint32_t wqe_size_in_bytes;
	uint32_t max_qp_size_in_wqes;
	unsigned int i;
	int rc;

	golan_qp = zalloc(sizeof(*golan_qp));
	if (!golan_qp) {
		rc = -ENOMEM;
		goto err_create_qp;
	}

	if ( ( qp->type == IB_QPT_SMI ) || ( qp->type == IB_QPT_GSI ) ||
		 ( qp->type == IB_QPT_UD ) ) {
		golan_qp->rq.grh_size = ( qp->recv.num_wqes *
					sizeof ( golan_qp->rq.grh[0] ));
	}

	/* Calculate receive queue size */
	golan_qp->rq.size = qp->recv.num_wqes * GOLAN_RECV_WQE_SIZE;
	if (GOLAN_RECV_WQE_SIZE > be16_to_cpu(golan->caps.max_wqe_sz_rq)) {
		DBGC (golan ,"%s receive wqe size [%zd] > max wqe size [%d]\n", __FUNCTION__,
				GOLAN_RECV_WQE_SIZE, be16_to_cpu(golan->caps.max_wqe_sz_rq));
		rc = -EINVAL;
		goto err_create_qp_rq_size;
	}

	wqe_size_in_bytes = sizeof(golan_qp->sq.wqes[0]);
	/* Calculate send queue size */
	if (wqe_size_in_bytes >	be16_to_cpu(golan->caps.max_wqe_sz_sq)) {
		DBGC (golan ,"%s send WQE size [%d] > max WQE size [%d]\n", __FUNCTION__,
				wqe_size_in_bytes,
				be16_to_cpu(golan->caps.max_wqe_sz_sq));
		rc = -EINVAL;
		goto err_create_qp_sq_wqe_size;
	}
	golan_qp->sq.size = (qp->send.num_wqes * wqe_size_in_bytes);
	max_qp_size_in_wqes = (1 << ((uint32_t)(golan->caps.log_max_qp_sz)));
	if (qp->send.num_wqes > max_qp_size_in_wqes) {
		DBGC (golan ,"%s send wq size [%d] > max wq size [%d]\n", __FUNCTION__,
				golan_qp->sq.size, max_qp_size_in_wqes);
		rc = -EINVAL;
		goto err_create_qp_sq_size;
	}

	golan_qp->size = golan_qp->sq.size + golan_qp->rq.size;

	/* allocate dma memory for WQEs (1 page is enough) - should change it */
	golan_qp->wqes = malloc_phys ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE );
	if (!golan_qp->wqes) {
		rc = -ENOMEM;
		goto err_create_qp_wqe_alloc;
	}
	golan_qp->rq.wqes	= golan_qp->wqes;
	golan_qp->sq.wqes	= golan_qp->wqes + golan_qp->rq.size;//(union golan_send_wqe *)&
			//(((struct golan_recv_wqe_ud *)(golan_qp->wqes))[qp->recv.num_wqes]);

	if ( golan_qp->rq.grh_size ) {
		golan_qp->rq.grh = ( golan_qp->wqes +
				golan_qp->sq.size +
				golan_qp->rq.size );
	}

	/* Invalidate all WQEs */
	data = &golan_qp->rq.wqes[0].data[0];
	for ( i = 0 ; i < ( golan_qp->rq.size / sizeof ( *data ) ); i++ ){
		data->lkey = cpu_to_be32 ( GOLAN_INVALID_LKEY );
		data++;
	}

	golan_qp->doorbell_record = malloc_phys(sizeof(struct golan_qp_db),
						sizeof(struct golan_qp_db));
	if (!golan_qp->doorbell_record) {
		rc = -ENOMEM;
		goto err_create_qp_db_alloc;
	}
	memset(golan_qp->doorbell_record, 0, sizeof(struct golan_qp_db));

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_QP, 0x0,
			GEN_MBOX, NO_MBOX,
			sizeof(struct golan_create_qp_mbox_in) + GOLAN_PAS_SIZE,
			sizeof(struct golan_create_qp_mbox_out));

	in = (struct golan_create_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);

	/* Fill the physical address of the page */
	in->pas[0]			= VIRT_2_BE64_BUS(golan_qp->wqes);
	in->ctx.qp_counter_set_usr_page	= cpu_to_be32(golan->uar.index);

	in->ctx.flags_pd 	= cpu_to_be32(golan->pdn);
	in->ctx.flags		= cpu_to_be32((golan_qp_type_to_st(qp->type)
						<< GOLAN_QP_CTX_ST_BIT) |
						(GOLAN_QP_PM_MIGRATED <<
						GOLAN_QP_CTX_PM_STATE_BIT));
//	cgs	set to 0, initialy.
//	atomic mode
	in->ctx.rq_size_stride	= ((ilog2(qp->recv.num_wqes) <<
								GOLAN_QP_CTX_RQ_SIZE_BIT) |
								(sizeof(golan_qp->rq.wqes[0]) / GOLAN_RECV_WQE_SIZE));
	in->ctx.sq_crq_size		= cpu_to_be16(ilog2(golan_qp->sq.size / GOLAN_SEND_WQE_BB_SIZE)
										  << GOLAN_QP_CTX_SQ_SIZE_BIT);
	in->ctx.cqn_send 		= cpu_to_be32(qp->send.cq->cqn);
	in->ctx.cqn_recv 		= cpu_to_be32(qp->recv.cq->cqn);
	in->ctx.db_rec_addr 	= VIRT_2_BE64_BUS(golan_qp->doorbell_record);

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_qp_cmd );
	out = (struct golan_create_qp_mbox_out *)cmd->out;

	*qpn = (be32_to_cpu(out->qpn) & 0xffffff);
	/*
	* Hardware wants QPN written in big-endian order (after
	* shifting) for send doorbell.  Precompute this value to save
	* a little bit when posting sends.
	*/
	golan_qp->doorbell_qpn	= cpu_to_be32(*qpn << 8);
	golan_qp->state			= GOLAN_IB_QPS_RESET;

	ib_qp_set_drvdata(qp, golan_qp);

	return 0;

err_create_qp_cmd:
	free_phys(golan_qp->doorbell_record, sizeof(struct golan_qp_db));
err_create_qp_db_alloc:
	free_phys ( golan_qp->wqes, GOLAN_PAGE_SIZE );
err_create_qp_wqe_alloc:
err_create_qp_sq_size:
err_create_qp_sq_wqe_size:
err_create_qp_rq_size:
	free ( golan_qp );
err_create_qp:
	return rc;
}

/**
 * Create queue pair
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 * @ret rc		Return status code
 */
static int golan_create_qp(struct ib_device *ibdev,
				struct ib_queue_pair *qp)
{
	int rc, qpn = -1;

	switch (qp->type) {
	case IB_QPT_UD:
	case IB_QPT_SMI:
	case IB_QPT_GSI:
		rc = golan_create_qp_aux(ibdev, qp, &qpn);
		if (rc) {
			DBG ( "%s Failed to create QP (rc = 0x%x)\n", __FUNCTION__, rc);
			return rc;
		}
		qp->qpn = qpn;

		break;
	case IB_QPT_ETH:
	case IB_QPT_RC:
	default:
		DBG ( "%s unsupported QP type (0x%x)\n", __FUNCTION__, qp->type);
		return -EINVAL;
	}

	return 0;
}

static int golan_modify_qp_rst_to_init(struct ib_device *ibdev,
					struct ib_queue_pair *qp __unused,
					struct golan_modify_qp_mbox_in_data *in)
{
	int rc = 0;

	in->ctx.qkey			= cpu_to_be32((uint32_t)(qp->qkey));

	in->ctx.pri_path.port		= ibdev->port;
	in->ctx.flags			|= cpu_to_be32(GOLAN_QP_PM_MIGRATED << GOLAN_QP_CTX_PM_STATE_BIT);
	in->ctx.pri_path.pkey_index	= 0;
	/* QK is 0 */
	/* QP cntr set 0 */
	return rc;
}

static int golan_modify_qp_init_to_rtr(struct ib_device *ibdev __unused,
					struct ib_queue_pair *qp __unused,
					struct golan_modify_qp_mbox_in_data *in)
{
	int rc = 0;

	in->optparam = 0;
	return rc;
}

static int golan_modify_qp_rtr_to_rts(struct ib_device *ibdev __unused,
					struct ib_queue_pair *qp __unused,
					struct golan_modify_qp_mbox_in_data *in __unused)
{
	int rc = 0;

	in->optparam = 0;
	/* In good flow psn in 0 */
	return rc;
}

static int golan_modify_qp_to_rst(struct ib_device *ibdev,
					struct ib_queue_pair *qp)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp);
	struct golan_cmd_layout	*cmd;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_2RST_QP, 0x0,
					NO_MBOX, NO_MBOX,
					sizeof(struct golan_modify_qp_mbox_in),
					sizeof(struct golan_modify_qp_mbox_out));
	((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_2rst_cmd );

	golan_qp->state = GOLAN_IB_QPS_RESET;
	DBGC( golan , "%s QP number 0x%lx was modified to RESET\n",
		__FUNCTION__, qp->qpn);

	return 0;

err_modify_qp_2rst_cmd:
	DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n",
		__FUNCTION__, qp->qpn, rc);
	return rc;
}

static int (*golan_modify_qp_methods[])(struct ib_device *ibdev,
					struct ib_queue_pair *qp,
					struct golan_modify_qp_mbox_in_data *in) = {

	[GOLAN_IB_QPS_RESET]	= golan_modify_qp_rst_to_init,
	[GOLAN_IB_QPS_INIT]	= golan_modify_qp_init_to_rtr,
	[GOLAN_IB_QPS_RTR]	= golan_modify_qp_rtr_to_rts
};

static int golan_modify_qp(struct ib_device *ibdev,
				struct ib_queue_pair *qp)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp);
	struct golan_modify_qp_mbox_in_data *in;
	struct golan_cmd_layout	*cmd;
	enum golan_ib_qp_state prev_state;
	int rc;
	int modify_cmd[] = {GOLAN_CMD_OP_RST2INIT_QP,
				GOLAN_CMD_OP_INIT2RTR_QP,
				GOLAN_CMD_OP_RTR2RTS_QP};

	while (golan_qp->state < GOLAN_IB_QPS_RTS) {
		prev_state = golan_qp->state;
		cmd = write_cmd(golan, DEF_CMD_IDX, modify_cmd[golan_qp->state], 0x0,
						GEN_MBOX, NO_MBOX,
						sizeof(struct golan_modify_qp_mbox_in),
						sizeof(struct golan_modify_qp_mbox_out));

		in = (struct golan_modify_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX);
		((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn);
		rc = golan_modify_qp_methods[golan_qp->state](ibdev, qp, in);
		if (rc) {
			goto err_modify_qp_fill_inbox;
		}
//		in->ctx.qp_counter_set_usr_page	= cpu_to_be32(golan->uar.index);
		rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
		GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_cmd );

		++(golan_qp->state);

		DBGC( golan , "%s QP number 0x%lx was modified from %s to %s\n",
			__FUNCTION__, qp->qpn, golan_qp_state_as_string[prev_state],
			golan_qp_state_as_string[golan_qp->state]);
	}

	DBGC( golan , "%s QP number 0x%lx is ready to receive/send packets.\n",
		__FUNCTION__, qp->qpn);
	return 0;

err_modify_qp_cmd:
err_modify_qp_fill_inbox:
	DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n",
		   __FUNCTION__, qp->qpn, rc);
	return rc;
}

/**
 * Destroy queue pair
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 */
static void golan_destroy_qp(struct ib_device *ibdev,
				struct ib_queue_pair *qp)
{
	struct golan		*golan		= ib_get_drvdata(ibdev);
	struct golan_queue_pair	*golan_qp	= ib_qp_get_drvdata(qp);
	struct golan_cmd_layout			*cmd;
	unsigned long		 qpn = qp->qpn;
	int rc;

	DBGC (golan, "%s in\n", __FUNCTION__);

	if (golan_qp->state != GOLAN_IB_QPS_RESET) {
		if (golan_modify_qp_to_rst(ibdev, qp)) {
			DBGC (golan ,"%s Failed to modify QP 0x%lx to RESET\n", __FUNCTION__,
				   qp->qpn);
		}
	}

	if (qp->recv.cq) {
		golan_cq_clean(qp->recv.cq);
	}
	if (qp->send.cq && (qp->send.cq != qp->recv.cq)) {
		golan_cq_clean(qp->send.cq);
	}

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_QP, 0x0,
					NO_MBOX, NO_MBOX,
				    sizeof(struct golan_destroy_qp_mbox_in),
				    sizeof(struct golan_destroy_qp_mbox_out));
	((struct golan_destroy_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qpn);
	rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;
	qp->qpn = 0;

	ib_qp_set_drvdata(qp, NULL);
	free_phys(golan_qp->doorbell_record, sizeof(struct golan_qp_db));
	free_phys ( golan_qp->wqes, GOLAN_PAGE_SIZE );
	free(golan_qp);

	DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn);
}

/**
 * Calculate transmission rate
 *
 * @v av		Address vector
 * @ret golan_rate	Golan rate
 */
static unsigned int golan_rate(enum ib_rate rate) {
	return (((rate >= IB_RATE_2_5) && (rate <= IB_RATE_120)) ? (rate + 5) : 0);
}

/**
 * Post send work queue entry
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 * @v av		Address vector
 * @v iobuf		I/O buffer
 * @ret rc		Return status code
 */
static int golan_post_send(struct ib_device *ibdev,
				struct ib_queue_pair *qp,
				struct ib_address_vector *av,
				struct io_buffer *iobuf)
{
	struct golan			*golan		= ib_get_drvdata(ibdev);
	struct golan_queue_pair		*golan_qp	= ib_qp_get_drvdata(qp);
	struct golan_send_wqe_ud *wqe		= NULL;
	struct golan_av			*datagram	= NULL;
	unsigned long			wqe_idx_mask;
	unsigned long			wqe_idx;
	struct golan_wqe_data_seg	*data		= NULL;
	struct golan_wqe_ctrl_seg	*ctrl		= NULL;


	wqe_idx_mask = (qp->send.num_wqes - 1);
	wqe_idx = (qp->send.next_idx & wqe_idx_mask);
	if (qp->send.iobufs[wqe_idx]) {
		DBGC (golan ,"%s Send queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn);
		return -ENOMEM;
	}

	qp->send.iobufs[wqe_idx] = iobuf;

	// change to this
	//wqe_size_in_octa_words = golan_qp->sq.wqe_size_in_wqebb >> 4;

	wqe 			= &golan_qp->sq.wqes[wqe_idx].ud;

	//CHECK HW OWNERSHIP BIT ???

	memset(wqe, 0, sizeof(*wqe));

	ctrl			= &wqe->ctrl;
	ctrl->opmod_idx_opcode	= cpu_to_be32(GOLAN_SEND_OPCODE |
						  ((u32)(golan_qp->sq.next_idx) <<
						  GOLAN_WQE_CTRL_WQE_IDX_BIT));
	ctrl->qpn_ds		= cpu_to_be32(GOLAN_SEND_UD_WQE_SIZE >> 4) |
							  golan_qp->doorbell_qpn;
	ctrl->fm_ce_se		= 0x8;//10 - 0 - 0
	data			= &wqe->data;
	data->byte_count	= cpu_to_be32(iob_len(iobuf));
	data->lkey		= cpu_to_be32(golan->mkey);
	data->addr		= VIRT_2_BE64_BUS(iobuf->data);

	datagram		= &wqe->datagram;
	datagram->key.qkey.qkey	= cpu_to_be32(av->qkey);
	datagram->dqp_dct	= cpu_to_be32((1 << 31) | av->qpn);
	datagram->stat_rate_sl	= ((golan_rate(av->rate) << 4) | av->sl);
	datagram->fl_mlid	= (ibdev->lid & 0x007f); /* take only the 7 low bits of the LID */
	datagram->rlid		= cpu_to_be16(av->lid);
	datagram->grh_gid_fl	= cpu_to_be32(av->gid_present << 30);
	memcpy(datagram->rgid, av->gid.bytes, 16 /* sizeof(datagram->rgid) */);

	/*
	* Make sure that descriptors are written before
	* updating doorbell record and ringing the doorbell
	*/
	++(qp->send.next_idx);
	golan_qp->sq.next_idx = (golan_qp->sq.next_idx + GOLAN_WQEBBS_PER_SEND_UD_WQE);
	golan_qp->doorbell_record->send_db = cpu_to_be16(golan_qp->sq.next_idx);
	wmb();
	writeq(*((__be64 *)ctrl), golan->uar.virt
			+ ( ( golan_qp->sq.next_idx & 0x1 ) ? DB_BUFFER0_EVEN_OFFSET
					: DB_BUFFER0_ODD_OFFSET ) );
	return 0;
}

/**
 * Post receive work queue entry
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 * @v iobuf		I/O buffer
 * @ret rc		Return status code
 */
static int golan_post_recv(struct ib_device *ibdev,
				struct ib_queue_pair *qp,
				struct io_buffer *iobuf)
{
	struct golan		*golan		= ib_get_drvdata(ibdev);
	struct golan_queue_pair	*golan_qp	= ib_qp_get_drvdata(qp);
	struct ib_work_queue		*wq	= &qp->recv;
	struct golan_recv_wqe_ud	*wqe;
	struct ib_global_route_header *grh;
	struct golan_wqe_data_seg *data;
	unsigned int wqe_idx_mask;

	/* Allocate work queue entry */
	wqe_idx_mask = (wq->num_wqes - 1);
	if (wq->iobufs[wq->next_idx & wqe_idx_mask]) {
		DBGC (golan ,"%s Receive queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn);
		return -ENOMEM;
	}

	wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
	wqe = & golan_qp->rq.wqes[wq->next_idx & wqe_idx_mask];

	memset(wqe, 0, sizeof(*wqe));
	data = &wqe->data[0];
	if ( golan_qp->rq.grh ) {
		grh = &golan_qp->rq.grh[wq->next_idx & wqe_idx_mask];
		data->byte_count = cpu_to_be32 ( sizeof ( *grh ) );
		data->lkey = cpu_to_be32 ( golan->mkey );
		data->addr = VIRT_2_BE64_BUS ( grh );
		data++;
	}

	data->byte_count = cpu_to_be32(iob_tailroom(iobuf));
	data->lkey = cpu_to_be32(golan->mkey);
	data->addr = VIRT_2_BE64_BUS(iobuf->data);

	++wq->next_idx;

	/*
	* Make sure that descriptors are written before
	* updating doorbell record and ringing the doorbell
	*/
	wmb();
	golan_qp->doorbell_record->recv_db = cpu_to_be16(qp->recv.next_idx & 0xffff);

	return 0;
}

static int golan_query_vport_context ( struct ib_device *ibdev ) {
	struct golan *golan = ib_get_drvdata ( ibdev );
	struct golan_cmd_layout	*cmd;
	struct golan_query_hca_vport_context_inbox *in;
	struct golan_query_hca_vport_context_data *context_data;
	int rc;

	cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_CONTEXT,
			0x0, GEN_MBOX, GEN_MBOX,
			sizeof(struct golan_query_hca_vport_context_inbox),
			sizeof(struct golan_query_hca_vport_context_outbox) );

	in = GOLAN_MBOX_IN ( cmd, in );
	in->port_num = (u8)ibdev->port;

	rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ );
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_context_cmd );

	context_data = (struct golan_query_hca_vport_context_data *)( GET_OUTBOX ( golan, GEN_MBOX ) );

	ibdev->node_guid.dwords[0] 	= context_data->node_guid[0];
	ibdev->node_guid.dwords[1] 	= context_data->node_guid[1];
	ibdev->lid					= be16_to_cpu( context_data->lid );
	ibdev->sm_lid				= be16_to_cpu( context_data->sm_lid );
	ibdev->sm_sl				= context_data->sm_sl;
	ibdev->port_state			= context_data->port_state;

	return 0;
err_query_vport_context_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}


static int golan_query_vport_gid ( struct ib_device *ibdev ) {
	struct golan *golan = ib_get_drvdata( ibdev );
	struct golan_cmd_layout	*cmd;
	struct golan_query_hca_vport_gid_inbox *in;
	union ib_gid *ib_gid;
	int rc;

	cmd = write_cmd( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_GID,
			0x0, GEN_MBOX, GEN_MBOX,
			sizeof(struct golan_query_hca_vport_gid_inbox),
			sizeof(struct golan_query_hca_vport_gid_outbox) );

	in = GOLAN_MBOX_IN ( cmd, in );
	in->port_num = (u8)ibdev->port;
	in->gid_index = 0;
	rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ );
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_gid_cmd );

	ib_gid = (union ib_gid *)( GET_OUTBOX ( golan, GEN_MBOX ) );

	memcpy ( &ibdev->gid, ib_gid, sizeof(ibdev->gid) );

	return 0;
err_query_vport_gid_cmd:
		DBGC ( golan, "%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static int golan_query_vport_pkey ( struct ib_device *ibdev ) {
	struct golan *golan = ib_get_drvdata ( ibdev );
	struct golan_cmd_layout	*cmd;
	struct golan_query_hca_vport_pkey_inbox *in;
	int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size));
	int rc;

	cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_PKEY,
			0x0, GEN_MBOX, GEN_MBOX,
			sizeof(struct golan_query_hca_vport_pkey_inbox),
			sizeof(struct golan_outbox_hdr) + 8 +
			sizeof(struct golan_query_hca_vport_pkey_data) * pkey_table_size_in_entries );

	in = GOLAN_MBOX_IN ( cmd, in );
	in->port_num = (u8)ibdev->port;
	in->pkey_index = 0xffff;
	rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ );
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_pkey_cmd );

	return 0;
err_query_vport_pkey_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static int golan_get_ib_info ( struct ib_device *ibdev ) {
	int rc;

	rc = golan_query_vport_context ( ibdev );
	if ( rc != 0 ) {
		DBG ( "golan_get_ib_info: golan_query_vport_context Failed (rc = %d)\n",rc );
		goto err_query_vport_context;
	}

	rc = golan_query_vport_gid ( ibdev );
	if ( rc != 0 ) {
		DBG ( "golan_get_ib_info: golan_query_vport_gid Failed (rc = %d)\n",rc );
		goto err_query_vport_gid;
	}

	rc = golan_query_vport_pkey ( ibdev );
	if ( rc != 0 ) {
		DBG ( "golan_get_ib_info: golan_query_vport_pkey Failed (rc = %d)\n",rc );
		goto err_query_vport_pkey;
	}
	return rc;
err_query_vport_pkey:
err_query_vport_gid:
err_query_vport_context:
	DBG ( "%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

static int golan_complete(struct ib_device *ibdev,
				struct ib_completion_queue *cq,
				struct golan_cqe64 *cqe64)
{
	struct golan *golan	= ib_get_drvdata(ibdev);
	struct ib_work_queue *wq;
	struct golan_queue_pair *golan_qp;
	struct ib_queue_pair *qp;
	struct io_buffer *iobuf = NULL;
	struct ib_address_vector recv_dest;
	struct ib_address_vector recv_source;
	struct ib_global_route_header *grh;
	struct golan_err_cqe *err_cqe64;
	int gid_present, idx;
	u16 wqe_ctr;
	uint8_t opcode;
	static int error_state;
	uint32_t qpn = be32_to_cpu(cqe64->sop_drop_qpn) & 0xffffff;
	int is_send = 0;
	size_t len;

	opcode = cqe64->op_own >> GOLAN_CQE_OPCODE_BIT;
	DBGC2( golan , "%s completion with opcode 0x%x\n", __FUNCTION__, opcode);

	if (opcode == GOLAN_CQE_REQ || opcode == GOLAN_CQE_REQ_ERR) {
		is_send = 1;
	} else {
		is_send = 0;
	}
	if (opcode == GOLAN_CQE_REQ_ERR || opcode == GOLAN_CQE_RESP_ERR) {
		err_cqe64 = (struct golan_err_cqe *)cqe64;
		int i = 0;
		if (!error_state++) {
			DBGC (golan ,"\n");
			for ( i = 0 ; i < 16 ; i += 2 ) {
				DBGC (golan ,"%x       %x\n",
						be32_to_cpu(((uint32_t *)(err_cqe64))[i]),
						be32_to_cpu(((uint32_t *)(err_cqe64))[i + 1]));
			}
			DBGC (golan ,"CQE with error: Syndrome(0x%x), VendorSynd(0x%x), HW_SYN(0x%x)\n",
					err_cqe64->syndrome, err_cqe64->vendor_err_synd,
					err_cqe64->hw_syndrom);
		}
	}
	/* Identify work queue */
	wq = ib_find_wq(cq, qpn, is_send);
	if (!wq) {
		DBGC (golan ,"%s unknown %s QPN 0x%x in CQN 0x%lx\n",
		       __FUNCTION__, (is_send ? "send" : "recv"), qpn, cq->cqn);
		return -EINVAL;
	}

	qp = wq->qp;
	golan_qp = ib_qp_get_drvdata ( qp );

	wqe_ctr = be16_to_cpu(cqe64->wqe_counter);
	if (is_send) {
		wqe_ctr &= ((GOLAN_WQEBBS_PER_SEND_UD_WQE * wq->num_wqes) - 1);
		idx = wqe_ctr / GOLAN_WQEBBS_PER_SEND_UD_WQE;
	} else {
		idx = wqe_ctr & (wq->num_wqes - 1);
	}

	iobuf = wq->iobufs[idx];
	if (!iobuf) {
		DBGC (golan ,"%s IO Buffer 0x%x not found in QPN 0x%x\n",
			   __FUNCTION__, idx, qpn);
		return -EINVAL;
	}
	wq->iobufs[idx] = NULL;

	if (is_send) {
		ib_complete_send(ibdev, qp, iobuf, (opcode == GOLAN_CQE_REQ_ERR));
	} else {
		len = be32_to_cpu(cqe64->byte_cnt);
		memset(&recv_dest, 0, sizeof(recv_dest));
		recv_dest.qpn = qpn;
		/* Construct address vector */
		memset(&recv_source, 0, sizeof(recv_source));
		switch (qp->type) {
		case IB_QPT_SMI:
		case IB_QPT_GSI:
		case IB_QPT_UD:
			/* Locate corresponding GRH */
			assert ( golan_qp->rq.grh != NULL );
			grh = &golan_qp->rq.grh[ idx ];

			recv_source.qpn = be32_to_cpu(cqe64->flags_rqpn) & 0xffffff;
			recv_source.lid = be16_to_cpu(cqe64->slid);
			recv_source.sl	= (be32_to_cpu(cqe64->flags_rqpn) >> 24) & 0xf;
			gid_present = (be32_to_cpu(cqe64->flags_rqpn) >> 28) & 3;
			if (!gid_present) {
				recv_dest.gid_present = recv_source.gid_present = 0;
			} else {
				recv_dest.gid_present = recv_source.gid_present = 1;
				//if (recv_source.gid_present == 0x1) {
				memcpy(&recv_source.gid, &grh->sgid, sizeof(recv_source.gid));
				memcpy(&recv_dest.gid, &grh->dgid, sizeof(recv_dest.gid));
				//} else { // recv_source.gid_present = 0x3
					/* GRH is located in the upper 64 byte of the CQE128
					 * currently not supported */
					//;
				//}
			}
			len -= sizeof ( *grh );
			break;
		case IB_QPT_RC:
		case IB_QPT_ETH:
		default:
			DBGC (golan ,"%s Unsupported QP type (0x%x)\n", __FUNCTION__, qp->type);
			return -EINVAL;
		}
		assert(len <= iob_tailroom(iobuf));
		iob_put(iobuf, len);
		ib_complete_recv(ibdev, qp, &recv_dest, &recv_source, iobuf, (opcode == GOLAN_CQE_RESP_ERR));
	}
	return 0;
}

static int golan_is_hw_ownership(struct ib_completion_queue *cq,
								 struct golan_cqe64 *cqe64)
{
	return ((cqe64->op_own & GOLAN_CQE_OWNER_MASK) !=
			((cq->next_idx >> ilog2(cq->num_cqes)) & 1));
}
static void golan_poll_cq(struct ib_device *ibdev,
				struct ib_completion_queue *cq)
{
	unsigned int		i;
	int			rc = 0;
	unsigned int		cqe_idx_mask;
	struct golan_cqe64	*cqe64;
	struct golan_completion_queue *golan_cq = ib_cq_get_drvdata(cq);
	struct golan		*golan	= ib_get_drvdata(ibdev);

	for (i = 0; i < cq->num_cqes; ++i) {
		/* Look for completion entry */
		cqe_idx_mask = (cq->num_cqes - 1);
		cqe64 = &golan_cq->cqes[cq->next_idx & cqe_idx_mask];
		/* temporary valid only for 64 byte CQE */
		if (golan_is_hw_ownership(cq, cqe64) ||
			((cqe64->op_own >> GOLAN_CQE_OPCODE_BIT) ==
			GOLAN_CQE_OPCODE_NOT_VALID)) {
			break;	/* HW ownership */
		}

		DBGC2( golan , "%s CQN 0x%lx [%ld] \n", __FUNCTION__, cq->cqn, cq->next_idx);
		/*
		 * Make sure we read CQ entry contents after we've checked the
		 * ownership bit. (PRM - 6.5.3.2)
		 */
		rmb();
		rc = golan_complete(ibdev, cq, cqe64);
		if (rc != 0) {
			DBGC (golan ,"%s CQN 0x%lx failed to complete\n", __FUNCTION__, cq->cqn);
		}

		/* Update completion queue's index */
		cq->next_idx++;

		/* Update doorbell record */
		*(golan_cq->doorbell_record) = cpu_to_be32(cq->next_idx & 0xffffff);
	}
}

static const char *golan_eqe_type_str(u8 type)
{
	switch (type) {
	case GOLAN_EVENT_TYPE_COMP:
		return "GOLAN_EVENT_TYPE_COMP";
	case GOLAN_EVENT_TYPE_PATH_MIG:
		return "GOLAN_EVENT_TYPE_PATH_MIG";
	case GOLAN_EVENT_TYPE_COMM_EST:
		return "GOLAN_EVENT_TYPE_COMM_EST";
	case GOLAN_EVENT_TYPE_SQ_DRAINED:
		return "GOLAN_EVENT_TYPE_SQ_DRAINED";
	case GOLAN_EVENT_TYPE_SRQ_LAST_WQE:
		return "GOLAN_EVENT_TYPE_SRQ_LAST_WQE";
	case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT:
		return "GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT";
	case GOLAN_EVENT_TYPE_CQ_ERROR:
		return "GOLAN_EVENT_TYPE_CQ_ERROR";
	case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR:
		return "GOLAN_EVENT_TYPE_WQ_CATAS_ERROR";
	case GOLAN_EVENT_TYPE_PATH_MIG_FAILED:
		return "GOLAN_EVENT_TYPE_PATH_MIG_FAILED";
	case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
		return "GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR";
	case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR:
		return "GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR";
	case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR:
		return "GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR";
	case GOLAN_EVENT_TYPE_INTERNAL_ERROR:
		return "GOLAN_EVENT_TYPE_INTERNAL_ERROR";
	case GOLAN_EVENT_TYPE_PORT_CHANGE:
		return "GOLAN_EVENT_TYPE_PORT_CHANGE";
	case GOLAN_EVENT_TYPE_GPIO_EVENT:
		return "GOLAN_EVENT_TYPE_GPIO_EVENT";
	case GOLAN_EVENT_TYPE_REMOTE_CONFIG:
		return "GOLAN_EVENT_TYPE_REMOTE_CONFIG";
	case GOLAN_EVENT_TYPE_DB_BF_CONGESTION:
		return "GOLAN_EVENT_TYPE_DB_BF_CONGESTION";
	case GOLAN_EVENT_TYPE_STALL_EVENT:
		return "GOLAN_EVENT_TYPE_STALL_EVENT";
	case GOLAN_EVENT_TYPE_CMD:
		return "GOLAN_EVENT_TYPE_CMD";
	case GOLAN_EVENT_TYPE_PAGE_REQUEST:
		return "GOLAN_EVENT_TYPE_PAGE_REQUEST";
	default:
		return "Unrecognized event";
	}
}

static const char *golan_eqe_port_subtype_str(u8 subtype)
{
	switch (subtype) {
	case GOLAN_PORT_CHANGE_SUBTYPE_DOWN:
		return "GOLAN_PORT_CHANGE_SUBTYPE_DOWN";
	case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE:
		return "GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE";
	case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED:
		return "GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED";
	case GOLAN_PORT_CHANGE_SUBTYPE_LID:
		return "GOLAN_PORT_CHANGE_SUBTYPE_LID";
	case GOLAN_PORT_CHANGE_SUBTYPE_PKEY:
		return "GOLAN_PORT_CHANGE_SUBTYPE_PKEY";
	case GOLAN_PORT_CHANGE_SUBTYPE_GUID:
		return "GOLAN_PORT_CHANGE_SUBTYPE_GUID";
	case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
		return "GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG";
	default:
		return "Unrecognized event";
	}
}

/**
 * Update Infiniband parameters using Commands
 *
 * @v ibdev		Infiniband device
 * @ret rc		Return status code
 */
static int golan_ib_update ( struct ib_device *ibdev ) {
	int rc;

	/* Get IB parameters */
	if ( ( rc = golan_get_ib_info ( ibdev ) ) != 0 )
		return rc;

	/* Notify Infiniband core of potential link state change */
	ib_link_state_changed ( ibdev );

	return 0;
}

static inline void golan_handle_port_event(struct golan *golan, struct golan_eqe *eqe)
{
	struct ib_device *ibdev;
	u8 port;

	port = (eqe->data.port.port >> 4) & 0xf;
	ibdev = golan->ports[port - 1].ibdev;

	if ( ! ib_is_open ( ibdev ) )
		return;

	switch (eqe->sub_type) {
	case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
	case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE:
		golan_ib_update ( ibdev );
		/* Fall through */
	case GOLAN_PORT_CHANGE_SUBTYPE_DOWN:
	case GOLAN_PORT_CHANGE_SUBTYPE_LID:
	case GOLAN_PORT_CHANGE_SUBTYPE_PKEY:
	case GOLAN_PORT_CHANGE_SUBTYPE_GUID:
	case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED:
		DBGC( golan , "%s event %s(%d) (sub event %s(%d))arrived on port %d\n",
			   __FUNCTION__, golan_eqe_type_str(eqe->type), eqe->type,
			   golan_eqe_port_subtype_str(eqe->sub_type),
			   eqe->sub_type, port);
		break;
	default:
		DBGC (golan ,"%s Port event with unrecognized subtype: port %d, sub_type %d\n",
			   __FUNCTION__, port, eqe->sub_type);
	}
}

static struct golan_eqe *golan_next_eqe_sw(struct golan_event_queue *eq)
{
	uint32_t entry = (eq->cons_index & (GOLAN_NUM_EQES - 1));
	struct golan_eqe *eqe = &(eq->eqes[entry]);
	return ((eqe->owner != ((eq->cons_index >> ilog2(GOLAN_NUM_EQES)) & 1)) ? NULL : eqe);
}


/**
 * Poll event queue
 *
 * @v ibdev		Infiniband device
 */
static void golan_poll_eq(struct ib_device *ibdev)
{
	struct golan		*golan	= ib_get_drvdata(ibdev);
	struct golan_event_queue *eq	= &(golan->eq);
	struct golan_eqe	*eqe;
	u32 cqn;
	int counter = 0;

	while ((eqe = golan_next_eqe_sw(eq)) && (counter < GOLAN_NUM_EQES)) {
		/*
		 * Make sure we read EQ entry contents after we've
		 * checked the ownership bit.
		 */
		rmb();

		DBGC( golan , "%s eqn %d, eqe type %s\n", __FUNCTION__, eq->eqn,
			   golan_eqe_type_str(eqe->type));
		switch (eqe->type) {
		case GOLAN_EVENT_TYPE_COMP:
			/* We dont need to handle completion events since we
			 * poll all the CQs after polling the EQ */
			break;
		case GOLAN_EVENT_TYPE_PATH_MIG:
		case GOLAN_EVENT_TYPE_COMM_EST:
		case GOLAN_EVENT_TYPE_SQ_DRAINED:
		case GOLAN_EVENT_TYPE_SRQ_LAST_WQE:
		case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR:
		case GOLAN_EVENT_TYPE_PATH_MIG_FAILED:
		case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
		case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR:
		case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT:
		case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR:
			DBGC( golan , "%s event %s(%d) arrived\n", __FUNCTION__,
				   golan_eqe_type_str(eqe->type), eqe->type);
			break;
		case GOLAN_EVENT_TYPE_CMD:
//			golan_cmd_comp_handler(be32_to_cpu(eqe->data.cmd.vector));
			break;
		case GOLAN_EVENT_TYPE_PORT_CHANGE:
			golan_handle_port_event(golan, eqe);
			break;
		case GOLAN_EVENT_TYPE_CQ_ERROR:
			cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff;
			DBGC (golan ,"CQ error on CQN 0x%x, syndrom 0x%x\n",
				   cqn, eqe->data.cq_err.syndrome);
//			mlx5_cq_event(dev, cqn, eqe->type);
			break;
		/*
		 * currently the driver do not support dynamic memory request
		 * during FW run, a follow up change will allocate FW pages once and
		 * never release them till driver shutdown, this change will not support
		 * this request as currently this request is not issued anyway.
		case GOLAN_EVENT_TYPE_PAGE_REQUEST:
			{
				// we should check if we get this event while we
				// waiting for a command
				u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
				s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages);

				DBGC (golan ,"%s page request for func 0x%x, napges %d\n",
					   __FUNCTION__, func_id, npages);
				golan_provide_pages(golan, npages, func_id);
			}
			break;
		*/
		default:
			DBGC (golan ,"%s Unhandled event 0x%x on EQ 0x%x\n", __FUNCTION__,
				   eqe->type, eq->eqn);
			break;
		}

		++eq->cons_index;
		golan_eq_update_ci(eq, GOLAN_EQ_UNARMED);
		++counter;
	}
}

/**
 * Attach to multicast group
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 * @v gid		Multicast GID
 * @ret rc		Return status code
 */
static int golan_mcast_attach(struct ib_device *ibdev,
				struct ib_queue_pair *qp,
				union ib_gid *gid)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_cmd_layout	*cmd;
	int rc;

	if ( qp == NULL ) {
		DBGC( golan, "%s: Invalid pointer, could not attach QPN to MCG\n",
			__FUNCTION__ );
		return -EFAULT;
	}

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ATTACH_TO_MCG, 0x0,
					GEN_MBOX, NO_MBOX,
					sizeof(struct golan_attach_mcg_mbox_in),
					sizeof(struct golan_attach_mcg_mbox_out));
	((struct golan_attach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn);

	memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_CHECK_RC_AND_CMD_STATUS( err_attach_to_mcg_cmd );

	DBGC( golan , "%s: QPN 0x%lx was attached to MCG\n", __FUNCTION__, qp->qpn);
	return 0;
err_attach_to_mcg_cmd:
	DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc);
	return rc;
}

/**
 * Detach from multicast group
 *
 * @v ibdev		Infiniband device
 * @v qp		Queue pair
 * @v gid		Multicast GID
 * @ret rc		Return status code
 */
static void golan_mcast_detach(struct ib_device *ibdev,
				struct ib_queue_pair *qp,
				union ib_gid *gid)
{
	struct golan *golan = ib_get_drvdata(ibdev);
	struct golan_cmd_layout	*cmd;
	int rc;

	cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DETACH_FROM_MCG, 0x0,
					GEN_MBOX, NO_MBOX,
				    sizeof(struct golan_detach_mcg_mbox_in),
				    sizeof(struct golan_detach_mcg_mbox_out));
	((struct golan_detach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn);

	memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid));

	rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__);
	GOLAN_PRINT_RC_AND_CMD_STATUS;

	DBGC( golan , "%s: QPN 0x%lx was detached from MCG\n", __FUNCTION__, qp->qpn);
}

/**
 * Inform embedded subnet management agent of a received MAD
 *
 * @v ibdev		Infiniband device
 * @v mad		MAD
 * @ret rc		Return status code
 */
static int golan_inform_sma(struct ib_device *ibdev,
				union ib_mad *mad)
{
	if (!ibdev || !mad) {
		return 1;
	}

	return 0;
}

static int golan_register_ibdev(struct golan_port *port)
{
	struct ib_device *ibdev = port->ibdev;
	int rc;

	golan_get_ib_info ( ibdev );
	/* Register Infiniband device */
	if ((rc = register_ibdev(ibdev)) != 0) {
		DBG ( "%s port %d could not register IB device: (rc = %d)\n",
			__FUNCTION__, ibdev->port, rc);
		return rc;
	}

	port->netdev = ipoib_netdev( ibdev );

	return 0;
}

static inline void golan_bring_down(struct golan *golan)
{
	DBGC(golan, "%s: start\n", __FUNCTION__);

	if (~golan->flags & GOLAN_OPEN) {
		DBGC(golan, "%s: end (already closed)\n", __FUNCTION__);
		return;
	}

	golan_destroy_mkey(golan);
	golan_dealloc_pd(golan);
	golan_destory_eq(golan);
	golan_dealloc_uar(golan);
	golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL);
	golan_handle_pages(golan, GOLAN_REG_PAGES , GOLAN_PAGES_TAKE);
	golan_disable_hca(golan);
	golan_cmd_uninit(golan);
	golan->flags &= ~GOLAN_OPEN;
	DBGC(golan, "%s: end\n", __FUNCTION__);
}

static int golan_set_link_speed ( struct golan *golan ){
	mlx_status status;
	int i = 0;
	int utils_inited = 0;

	if ( ! golan->utils ) {
		utils_inited = 1;
		status = init_mlx_utils ( & golan->utils, golan->pci );
		MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" );
	}

	for ( i = 0; i < golan->caps.num_ports; ++i ) {
		status = mlx_set_link_speed ( golan->utils, i + 1, LINK_SPEED_IB, LINK_SPEED_SDR );
		MLX_CHECK_STATUS ( golan->pci, status, set_link_speed_err, "mlx_set_link_speed failed" );
	}

set_link_speed_err:
if ( utils_inited )
	free_mlx_utils ( & golan->utils );
utils_init_err:
	return status;
}

static inline int golan_bring_up(struct golan *golan)
{
	int rc = 0;
	DBGC(golan, "%s\n", __FUNCTION__);

	if (golan->flags & GOLAN_OPEN)
		return 0;

	if (( rc = golan_cmd_init(golan) ))
		goto out;

	if (( rc = golan_core_enable_hca(golan) ))
		goto cmd_uninit;

	/* Query for need for boot pages */
	if (( rc = golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_GIVE) ))
		goto disable;

	if (( rc = golan_qry_hca_cap(golan) ))
		goto pages;

	if (( rc = golan_set_hca_cap(golan) ))
		goto pages;

	if (( rc = golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_GIVE) ))
		goto pages;

	if (( rc = golan_set_link_speed ( golan ) ))
		goto pages_teardown;

	//Reg Init?
	if (( rc = golan_hca_init(golan) ))
		goto pages_2;

	if (( rc = golan_alloc_uar(golan) ))
		goto teardown;

	if (( rc = golan_create_eq(golan) ))
		goto de_uar;

	if (( rc = golan_alloc_pd(golan) ))
		goto de_eq;

	if (( rc = golan_create_mkey(golan) ))
		goto de_pd;

	golan->flags |= GOLAN_OPEN;
	return 0;

	golan_destroy_mkey(golan);
de_pd:
	golan_dealloc_pd(golan);
de_eq:
	golan_destory_eq(golan);
de_uar:
	golan_dealloc_uar(golan);
teardown:
	golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL);
pages_2:
pages_teardown:
	golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_TAKE);
pages:
	golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_TAKE);
disable:
	golan_disable_hca(golan);
cmd_uninit:
	golan_cmd_uninit(golan);
out:
	return rc;
}

/**
 * Close Infiniband link
 *
 * @v ibdev		Infiniband device
 */
static void golan_ib_close ( struct ib_device *ibdev ) {
	struct golan *golan = NULL;

	DBG ( "%s start\n", __FUNCTION__ );
	if ( ! ibdev )
		return;
	golan = ib_get_drvdata ( ibdev );
	golan_bring_down ( golan );
	DBG ( "%s end\n", __FUNCTION__ );
}

/**
 * Initialise Infiniband link
 *
 * @v ibdev		Infiniband device
 * @ret rc		Return status code
 */
static int golan_ib_open ( struct ib_device *ibdev ) {
	struct golan *golan = NULL;
	DBG ( "%s start\n", __FUNCTION__ );

	if ( ! ibdev )
		return -EINVAL;
	golan = ib_get_drvdata ( ibdev );
	golan_bring_up ( golan );
	golan_ib_update ( ibdev );

	DBG ( "%s end\n", __FUNCTION__ );
	return 0;
}

/** Golan Infiniband operations */
static struct ib_device_operations golan_ib_operations = {
	.create_cq	= golan_create_cq,
	.destroy_cq	= golan_destroy_cq,
	.create_qp	= golan_create_qp,
	.modify_qp	= golan_modify_qp,
	.destroy_qp	= golan_destroy_qp,
	.post_send	= golan_post_send,
	.post_recv	= golan_post_recv,
	.poll_cq	= golan_poll_cq,
	.poll_eq	= golan_poll_eq,
	.open		= golan_ib_open,
	.close		= golan_ib_close,
	.mcast_attach	= golan_mcast_attach,
	.mcast_detach	= golan_mcast_detach,
	.set_port_info	= golan_inform_sma,
	.set_pkey_table	= golan_inform_sma,
};

static int golan_probe_normal ( struct pci_device *pci ) {
	struct golan *golan;
	struct ib_device *ibdev;
	struct golan_port *port;
	int i;
	int rc = 0;

	golan = golan_alloc();
	if ( !golan ) {
		rc = -ENOMEM;
		goto err_golan_alloc;
	}

	/* at POST stage some BIOSes have limited available dynamic memory */
	if ( golan_init_fw_areas ( golan ) ) {
		rc = -ENOMEM;
		goto err_golan_golan_init_pages;
	}

	/* Setup PCI bus and HCA BAR */
	pci_set_drvdata( pci, golan );
	golan->pci = pci;
	golan_pci_init( golan );
	/* config command queues */
	if ( golan_bring_up( golan ) ) {
		DBGC (golan ,"golan bringup failed\n");
		rc = -1;
		goto err_golan_bringup;
	}

	if ( ! DEVICE_IS_CIB ( pci->device ) ) {
		if ( init_mlx_utils ( & golan->utils, pci ) ) {
			rc = -1;
			goto err_utils_init;
		}
	}
	/* Allocate Infiniband devices */
	for (i = 0; i < golan->caps.num_ports; ++i) {
		ibdev = alloc_ibdev( 0 );
		if ( !ibdev ) {
			rc = -ENOMEM;
			goto err_golan_probe_alloc_ibdev;
		}
		golan->ports[i].ibdev = ibdev;
		golan->ports[i].vep_number = 0;
		ibdev->op = &golan_ib_operations;
		ibdev->dev = &pci->dev;
		ibdev->port = (GOLAN_PORT_BASE + i);
		ibdev->ports = golan->caps.num_ports;
		ib_set_drvdata( ibdev, golan );
	}

	/* Register devices */
	for ( i = 0; i < golan->caps.num_ports; ++i ) {
		port = &golan->ports[i];
		if ((rc = golan_register_ibdev ( port ) ) != 0 ) {
			goto err_golan_probe_register_ibdev;
		}
	}

	golan_bring_down ( golan );

	return 0;

	i = golan->caps.num_ports;
err_golan_probe_register_ibdev:
	for ( i-- ; ( signed int ) i >= 0 ; i-- )
		unregister_ibdev ( golan->ports[i].ibdev );

	i = golan->caps.num_ports;
err_golan_probe_alloc_ibdev:
	for ( i-- ; ( signed int ) i >= 0 ; i-- )
		ibdev_put ( golan->ports[i].ibdev );
	if ( ! DEVICE_IS_CIB ( pci->device ) ) {
		free_mlx_utils ( & golan->utils );
	}
err_utils_init:
	golan_bring_down ( golan );
err_golan_bringup:
	iounmap( golan->iseg );
	golan_free_fw_areas ( golan );
err_golan_golan_init_pages:
	free ( golan );
err_golan_alloc:
	DBGC (golan ,"%s rc = %d\n", __FUNCTION__, rc);
	return rc;
}

static void golan_remove_normal ( struct pci_device *pci ) {
	struct golan	*golan = pci_get_drvdata(pci);
	struct golan_port *port;
	int i;

	DBGC(golan, "%s\n", __FUNCTION__);

	for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) {
		port = &golan->ports[i];
		unregister_ibdev ( port->ibdev );
	}
	for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) {
		netdev_nullify ( golan->ports[i].netdev );
	}
	for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) {
		ibdev_put ( golan->ports[i].ibdev );
	}
	if ( ! DEVICE_IS_CIB ( pci->device ) ) {
		free_mlx_utils ( & golan->utils );
	}
	iounmap( golan->iseg );
	golan_free_fw_areas ( golan );
	free(golan);
}

/***************************************************************************
 * NODNIC operations
 **************************************************************************/
static mlx_status shomron_tx_uar_send_db ( struct ib_device *ibdev,
		struct nodnic_send_wqbb *wqbb ) {
	mlx_status status = MLX_SUCCESS;
	struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
	struct shomron_nodnic_eth_send_wqe *eth_wqe =
			( struct shomron_nodnic_eth_send_wqe * )wqbb;
	struct shomronprm_wqe_segment_ctrl_send *ctrl;

	if ( ! eth_wqe || ! flexboot_nodnic->device_priv.uar.virt ) {
		DBG("%s: Invalid parameters\n",__FUNCTION__);
		status = MLX_FAILED;
		goto err;
	}
	wmb();
	ctrl = & eth_wqe->ctrl;
	writeq(*((__be64 *)ctrl), flexboot_nodnic->device_priv.uar.virt +
			( ( MLX_GET ( ctrl, wqe_index ) & 0x1 ) ? DB_BUFFER0_ODD_OFFSET
			: DB_BUFFER0_EVEN_OFFSET ) );
err:
	return status;
}

static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev,
			   struct ib_queue_pair *qp, struct ib_address_vector *av __unused,
			   struct io_buffer *iobuf, struct nodnic_send_wqbb *wqbb,
			   unsigned long wqe_index ) {
	mlx_status status = MLX_SUCCESS;
	struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
	struct shomron_nodnic_eth_send_wqe *eth_wqe =  NULL;
	struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
	struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp =
			ib_qp_get_drvdata ( qp );
	nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair;
	struct nodnic_send_ring *send_ring = &nodnic_qp->send;
	mlx_uint32 qpn = 0;

	eth_wqe = (struct shomron_nodnic_eth_send_wqe *)wqbb;
	memset ( ( ( ( void * ) eth_wqe ) ), 0,
			   ( sizeof ( *eth_wqe ) ) );

	status = nodnic_port_get_qpn(&port->port_priv, &send_ring->nodnic_ring,
			&qpn);
	if ( status != MLX_SUCCESS ) {
		DBG("nodnic_port_get_qpn failed\n");
		goto err;
	}

#define SHOMRON_GENERATE_CQE 0x3
#define SHOMRON_INLINE_HEADERS_SIZE 18
#define SHOMRON_INLINE_HEADERS_OFFSET 32
	MLX_FILL_2 ( &eth_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND,
			wqe_index, wqe_index & 0xFFFF);
	MLX_FILL_2 ( &eth_wqe->ctrl, 1, ds, 0x4 , qpn, qpn );
	MLX_FILL_1 ( &eth_wqe->ctrl, 2,
		     ce, SHOMRON_GENERATE_CQE /* generate completion */
			 );
	MLX_FILL_2 ( &eth_wqe->ctrl, 7,
			inline_headers1,
			cpu_to_be16(*(mlx_uint16 *)iobuf->data),
			inline_headers_size, SHOMRON_INLINE_HEADERS_SIZE
			 );
	memcpy((void *)&eth_wqe->ctrl + SHOMRON_INLINE_HEADERS_OFFSET,
			iobuf->data + 2, SHOMRON_INLINE_HEADERS_SIZE - 2);
	iob_pull(iobuf, SHOMRON_INLINE_HEADERS_SIZE);
	MLX_FILL_1 ( &eth_wqe->data[0], 0,
		     byte_count, iob_len ( iobuf ) );
	MLX_FILL_1 ( &eth_wqe->data[0], 1, l_key,
			flexboot_nodnic->device_priv.lkey );
	MLX_FILL_H ( &eth_wqe->data[0], 2,
		     local_address_h, virt_to_bus ( iobuf->data ) );
	MLX_FILL_1 ( &eth_wqe->data[0], 3,
		     local_address_l, virt_to_bus ( iobuf->data ) );
err:
	return status;
}

static mlx_status shomron_fill_completion( void *cqe, struct cqe_data *cqe_data ) {
	union shomronprm_completion_entry *cq_entry;
	uint32_t opcode;

	cq_entry = (union shomronprm_completion_entry *)cqe;
	cqe_data->owner = MLX_GET ( &cq_entry->normal, owner );
	opcode = MLX_GET ( &cq_entry->normal, opcode );
#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND 0
#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV 2
#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR 13
#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR 14
	cqe_data->is_error =
			( opcode >= FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR);
	if ( cqe_data->is_error ) {
		cqe_data->syndrome = MLX_GET ( &cq_entry->error, syndrome );
		cqe_data->vendor_err_syndrome =
				MLX_GET ( &cq_entry->error, vendor_error_syndrome );
		cqe_data->is_send =
					(opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR);
	} else {
		cqe_data->is_send =
			(opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND);
		cqe_data->wqe_counter = MLX_GET ( &cq_entry->normal, wqe_counter );
		cqe_data->byte_cnt = MLX_GET ( &cq_entry->normal, byte_cnt );

	}
	if ( cqe_data->is_send == TRUE )
		cqe_data->qpn = MLX_GET ( &cq_entry->normal, qpn );
	else
		cqe_data->qpn = MLX_GET ( &cq_entry->normal, srqn );

	return 0;
}

static mlx_status shomron_cqe_set_owner ( void *cq, unsigned int num_cqes ) {
	unsigned int i = 0;
	union shomronprm_completion_entry *cq_list;

	cq_list = (union shomronprm_completion_entry *)cq;
	for ( ; i < num_cqes ; i++ )
		MLX_FILL_1 ( &cq_list[i].normal, 15, owner, 1 );
	return 0;
}

static mlx_size shomron_get_cqe_size () {
	return sizeof ( union shomronprm_completion_entry );
}

struct flexboot_nodnic_callbacks shomron_nodnic_callbacks = {
	.get_cqe_size = shomron_get_cqe_size,
	.fill_send_wqe[IB_QPT_ETH] = shomron_fill_eth_send_wqe,
	.fill_completion = shomron_fill_completion,
	.cqe_set_owner = shomron_cqe_set_owner,
	.irq = flexboot_nodnic_eth_irq,
	.tx_uar_send_doorbell_fn = shomron_tx_uar_send_db,
};

static int shomron_nodnic_is_supported ( struct pci_device *pci ) {
	if ( DEVICE_IS_CIB ( pci->device ) )
		return 0;

	return flexboot_nodnic_is_supported ( pci );
}
/**************************************************************************/

static int golan_probe ( struct pci_device *pci ) {
	int rc = -ENOTSUP;

	DBG ( "%s: start\n", __FUNCTION__ );

	if ( ! pci ) {
		DBG ( "%s: PCI is NULL\n", __FUNCTION__ );
		rc = -EINVAL;
		goto probe_done;
	}

	if ( shomron_nodnic_is_supported ( pci ) ) {
		DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ );
		rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL );
	} else {
		DBG ( "%s: Using normal driver\n", __FUNCTION__ );
		rc = golan_probe_normal ( pci );
	}

probe_done:
	DBG ( "%s: rc = %d\n", __FUNCTION__, rc );
	return rc;
}

static void golan_remove ( struct pci_device *pci ) {
	DBG ( "%s: start\n", __FUNCTION__ );

	if ( ! shomron_nodnic_is_supported ( pci ) ) {
		DBG ( "%s: Using normal driver remove\n", __FUNCTION__ );
		golan_remove_normal ( pci );
		return;
	}

	DBG ( "%s: Using NODNIC driver remove\n", __FUNCTION__ );

	flexboot_nodnic_remove ( pci );

	DBG ( "%s: end\n", __FUNCTION__ );
}

static struct pci_device_id golan_nics[] = {
	PCI_ROM ( 0x15b3, 0x1011, "ConnectIB", "ConnectIB HCA driver: DevID 4113", 0 ),
	PCI_ROM ( 0x15b3, 0x1013, "ConnectX-4", "ConnectX-4 HCA driver, DevID 4115", 0 ),
	PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ),
	PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ),
	PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ),
	PCI_ROM ( 0x15b3, 0x101b, "ConnectX-6", "ConnectX-6 HCA driver, DevID 4123", 0 ),
	PCI_ROM ( 0x15b3, 0x101d, "ConnectX-6DX", "ConnectX-6DX HCA driver, DevID 4125", 0 ),
	PCI_ROM ( 0x15b3, 0x101f, "ConnectX-6Lx", "ConnectX-6LX HCA driver, DevID 4127", 0 ),
	PCI_ROM ( 0x15b3, 0x1021, "ConnectX-7", "ConnectX-7 HCA driver, DevID 4129", 0 ),
	PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ),
	PCI_ROM ( 0x15b3, 0xa2d6, "BlueField-2", "BlueField-2 network controller HCA driver, DevID 41686", 0 ),
	PCI_ROM ( 0x15b3, 0xa2dc, "BlueField-3", "BlueField-3 network controller HCA driver, DevID 41692", 0 ),
};

struct pci_driver golan_driver __pci_driver = {
	.ids		= golan_nics,
	.id_count	= (sizeof(golan_nics) / sizeof(golan_nics[0])),
	.probe		= golan_probe,
	.remove		= golan_remove,
};