diff options
author | Wissam Shoukair | 2016-03-21 14:09:13 +0100 |
---|---|---|
committer | Michael Brown | 2016-03-22 18:55:55 +0100 |
commit | 0a20373a2fb78622043277b4774676c55bbea22b (patch) | |
tree | 25c414a1bb1668c73f20c447cb7e868c2a8bf17f /src/drivers/infiniband/golan.c | |
parent | [pxe] Implicitly open network device in PXENV_UDP_OPEN (diff) | |
download | ipxe-0a20373a2fb78622043277b4774676c55bbea22b.tar.gz ipxe-0a20373a2fb78622043277b4774676c55bbea22b.tar.xz ipxe-0a20373a2fb78622043277b4774676c55bbea22b.zip |
[golan] Add Connect-IB, ConnectX-4 and ConnectX-4 Lx (Infiniband) support
Signed-off-by: Wissam Shoukair <wissams@mellanox.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/infiniband/golan.c')
-rwxr-xr-x | src/drivers/infiniband/golan.c | 2663 |
1 files changed, 2663 insertions, 0 deletions
diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c new file mode 100755 index 00000000..9225c187 --- /dev/null +++ b/src/drivers/infiniband/golan.c @@ -0,0 +1,2663 @@ +/* + * 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 <byteswap.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 <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/in.h> +#include <ipxe/ipoib.h> +#include "flexboot_nodnic.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" +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include "mlx_nodnic/include/mlx_port.h" + +/******************************************************************************/ +/************* Very simple memory management for umalloced pages **************/ +/******* Temporary solution until full memory management is implemented *******/ +/******************************************************************************/ +#define GOLAN_PAGES 20 +struct golan_page { + struct list_head list; + userptr_t addr; +}; + +static void golan_free_pages ( struct list_head *head ) { + struct golan_page *page, *tmp; + list_for_each_entry_safe ( page, tmp, head, list ) { + list_del ( &page->list ); + ufree ( page->addr ); + free ( page ); + } +} + +static int golan_init_pages ( struct list_head *head ) { + struct golan_page *new_entry; + int rc, i; + + if ( !head ) { + rc = -EINVAL; + goto err_golan_init_pages_bad_param; + } + + INIT_LIST_HEAD ( head ); + + for ( i = 0; i < GOLAN_PAGES; i++ ) { + new_entry = zalloc ( sizeof ( *new_entry ) ); + if ( new_entry == NULL ) { + rc = -ENOMEM; + goto err_golan_init_pages_alloc_page; + } + new_entry->addr = umalloc ( GOLAN_PAGE_SIZE ); + if ( new_entry->addr == UNULL ) { + free ( new_entry ); + rc = -ENOMEM; + goto err_golan_init_pages_alloc_page; + } + list_add ( &new_entry->list, head ); + } + + return 0; + +err_golan_init_pages_alloc_page: + golan_free_pages ( head ); +err_golan_init_pages_bad_param: + return rc; +} + +static userptr_t golan_get_page ( struct list_head *head ) { + struct golan_page *page; + userptr_t addr; + + if ( list_empty ( head ) ) + return UNULL; + + page = list_first_entry ( head, struct golan_page, list ); + list_del ( &page->list ); + addr = page->addr; + free ( page ); + return addr; +} + +/******************************************************************************/ + +const char *golan_qp_state_as_string[] = { + "RESET", + "INIT", + "RTR", + "RTS", + "SQD", + "SQE", + "ERR" +}; + +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 int verify_block_sig(struct golan_cmd_prot_block *block) +{ + if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff) + return -EINVAL; + + if (xor8_buf(block, sizeof(*block)) != 0xff) + return -EINVAL; + return 0; +} + +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)); +} + +/** + * Get Golan FW + */ +static int fw_ver_and_cmdif ( struct golan *golan ) { + DBGC (golan ,"\n[%x:%x]rev maj.min.submin = %x.%x.%x cmdif = %x\n", + golan->iseg->fw_rev, + golan->iseg->cmdif_rev_fw_sub, + fw_rev_maj ( golan ), fw_rev_min ( golan ), + fw_rev_sub ( golan ), cmdif_rev ( golan)); + + if (cmdif_rev ( golan) != PXE_CMDIF_REF) { + DBGC (golan ,"CMDIF %d not supported current is %d\n", + cmdif_rev ( golan ), PXE_CMDIF_REF); + return 1; + } + return 0; +} + +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; + + + 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 = sizeof(struct golan_manage_pages_inbox); + int size_obox = sizeof(struct golan_manage_pages_outbox); + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + while ( pages > 0 ) { + uint32_t pas_num = min(pages, MAX_PASE_MBOX); + unsigned i; + struct golan_cmd_layout *cmd; + struct golan_manage_pages_inbox *in; + struct golan_manage_pages_outbox_data *out; + + size_ibox += (pas_num * GOLAN_PAS_SIZE); + size_obox += (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 = (struct golan_manage_pages_outbox_data *)GET_OUTBOX(golan, MEM_MBOX); + out_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries); + for (i = 0; i < out_num_entries; ++i) { + ufree(BE64_BUS_2_USR(out->pas[i])); + } + } 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 0; +} + +static inline int golan_provide_pages ( struct golan *golan , uint32_t pages, __be16 func_id ) { + struct mbox *mailbox; + int size_ibox = sizeof(struct golan_manage_pages_inbox); + int size_obox = sizeof(struct golan_manage_pages_outbox); + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + 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 += (pas_num * GOLAN_PAS_SIZE); + size_obox += (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 ) { + if (!(addr = umalloc(GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + DBGC (golan ,"Couldnt allocated page \n"); + goto malloc_dma_failed; + } + 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); + } + ufree ( addr ); + goto err_send_command; + } + } + DBGC( golan , "%s Pages handled\n", __FUNCTION__); + return 0; + +err_send_command: +malloc_dma_failed: + /* 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); + } 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_dma(golan->mboxes.outbox, GOLAN_PAGE_SIZE); + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); + free_dma(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_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_failed; + } + if (!(golan->mboxes.inbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_inbox_failed; + } + if (!(golan->mboxes.outbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_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_dma_outbox_failed: + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); +malloc_dma_inbox_failed: + free_dma(golan->cmd.addr, GOLAN_PAGE_SIZE); +malloc_dma_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 *)(ioremap(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; + userptr_t addr; + + eq->cons_index = 0; + eq->size = GOLAN_NUM_EQES * sizeof(eq->eqes[0]); + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_eq_eqe_alloc; + } + eq->eqes = (struct golan_eqe *)user_to_virt(addr, 0); + + /* 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] = USR_2_BE64_BUS(addr); + 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: + ufree(virt_to_user(golan->eq.eqes)); +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; + 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)); + + ((struct golan_destroy_eq_mbox_in *)(cmd->in))->eqn = eqn; + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + ufree(virt_to_user(golan->eq.eqes)); + 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 = ioremap ( 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; + userptr_t addr; + + 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_dma(GOLAN_CQ_DB_RECORD_SIZE, + GOLAN_CQ_DB_RECORD_SIZE); + if (!golan_cq->doorbell_record) { + rc = -ENOMEM; + goto err_create_cq_db_alloc; + } + + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_cq_cqe_alloc; + } + golan_cq->cqes = (struct golan_cqe64 *)user_to_virt(addr, 0); + + /* 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] = USR_2_BE64_BUS(addr); + 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: + ufree(virt_to_user(golan_cq->cqes)); +err_create_cq_cqe_alloc: + free_dma(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); + ufree(virt_to_user(golan_cq->cqes)); + free_dma(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; + userptr_t addr; + 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 */ + addr = golan_get_page ( &golan->pages ); + if (!addr) { + rc = -ENOMEM; + goto err_create_qp_wqe_alloc; + } + golan_qp->wqes = user_to_virt(addr, 0); + 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_dma(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] = USR_2_BE64_BUS(addr); + 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_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); +err_create_qp_db_alloc: + ufree((userptr_t)golan_qp->wqes); +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; /* default index */ + /* 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_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); + ufree((userptr_t)golan_qp->wqes); + 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; +// static uint8_t toggle = 0; + + + 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 + 0x800);// + +// ((toggle++ & 0x1) ? 0x100 : 0x0)); + 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_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) ); + + ((struct golan_query_hca_vport_context_inbox *)(cmd->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; + 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) ); + + ((struct golan_query_hca_vport_gid_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + ((struct golan_query_hca_vport_gid_inbox *)(cmd->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_data *pkey_table; + 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 ); + + ((struct golan_query_hca_vport_pkey_inbox *)(cmd->in))->port_num = (u8)ibdev->port; + ((struct golan_query_hca_vport_pkey_inbox *)(cmd->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 ); + + //pkey_table = (struct golan_query_hca_vport_pkey_data *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + 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 ); + 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; + 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_utils utils; + mlx_status status; + int i = 0; + + memset ( &utils, 0, sizeof ( utils ) ); + + status = mlx_utils_init ( &utils, golan->pci ); + MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" ); + + status = mlx_pci_gw_init ( &utils ); + MLX_CHECK_STATUS ( golan->pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); + + for ( i = 0; i < golan->caps.num_ports; ++i ) { + status = mlx_set_link_speed( &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: + mlx_pci_gw_teardown( &utils ); +pci_gw_init_err: +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 __unused ) {} + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int golan_ib_open ( struct ib_device *ibdev ) { + DBG ( "%s start\n", __FUNCTION__ ); + + if ( ! ibdev ) + return -EINVAL; + + 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; + } + + if ( golan_init_pages( &golan->pages ) ) { + 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 ( fw_ver_and_cmdif( golan ) ) { + rc = -1; + goto err_fw_ver_cmdif; + } + + if ( golan_bring_up( golan ) ) { + DBGC (golan ,"golan bringup failed\n"); + rc = -1; + goto err_golan_bringup; + } + + /* 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); + 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; + } + + 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 ); + + golan_bring_down ( golan ); +err_golan_bringup: +err_fw_ver_cmdif: + golan_free_pages( &golan->pages ); +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 ); + netdev_put ( golan->ports[i].netdev ); + } + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + ibdev_put ( golan->ports[i].ibdev ); + } + + golan_bring_down(golan); + + golan_free_pages( &golan->pages ); + free(golan); +} + +/*************************************************************************** + * NODNIC operations + **************************************************************************/ +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 ( ð_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND, + wqe_index, wqe_index & 0xFFFF); + MLX_FILL_2 ( ð_wqe->ctrl, 1, ds, 0x4 , qpn, qpn ); + MLX_FILL_1 ( ð_wqe->ctrl, 2, + ce, SHOMRON_GENERATE_CQE /* generate completion */ + ); + MLX_FILL_2 ( ð_wqe->ctrl, 7, + inline_headers1, + cpu_to_be16(*(mlx_uint16 *)iobuf->data), + inline_headers_size, SHOMRON_INLINE_HEADERS_SIZE + ); + memcpy((void *)ð_wqe->ctrl + SHOMRON_INLINE_HEADERS_OFFSET, + iobuf->data + 2, SHOMRON_INLINE_HEADERS_SIZE - 2); + iob_pull(iobuf, SHOMRON_INLINE_HEADERS_SIZE); + MLX_FILL_1 ( ð_wqe->data[0], 0, + byte_count, iob_len ( iobuf ) ); + MLX_FILL_1 ( ð_wqe->data[0], 1, l_key, + flexboot_nodnic->device_priv.lkey ); + MLX_FILL_H ( ð_wqe->data[0], 2, + local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( ð_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, +}; + +static int shomron_nodnic_supported = 0; + +static int shomron_nodnic_is_supported ( struct pci_device *pci ) { + if ( pci->device == 0x1011 ) + 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; + } + + shomron_nodnic_supported = shomron_nodnic_is_supported ( pci ); + if ( shomron_nodnic_supported ) { + rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL ); + if ( rc == 0 ) { + DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); + goto probe_done; + } + shomron_nodnic_supported = 0; + } + + if ( ! shomron_nodnic_supported ) { + 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_supported ) { + 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 ), +}; + +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, +}; |