/* * Copyright (c) 2007 Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ FILE_LICENCE ( GPL2_ONLY ); #include #include #include #include #include #include #include #include #include #include #include #include "mtnic.h" /* mtnic.c - gPXE driver for Mellanox 10Gig ConnectX EN */ /******************************************************************** * * MTNIC allocation functions * *********************************************************************/ /** * mtnic_alloc_aligned * * @v unsigned int size size * @v void **va virtual address * @v u32 *pa physical address * @v u32 aligment aligment * * Function allocate aligned buffer and put it's virtual address in 'va' * and it's physical aligned address in 'pa' */ static int mtnic_alloc_aligned(unsigned int size, void **va, unsigned long *pa, unsigned int alignment) { *va = alloc_memblock(size, alignment); if (!*va) { return -EADDRINUSE; } *pa = (u32)virt_to_bus(*va); return 0; } /** * * mtnic alloc command interface * */ static int mtnic_alloc_cmdif(struct mtnic *mtnic) { u32 bar = mtnic_pci_dev.dev.bar[0]; mtnic->hcr = ioremap(bar + MTNIC_HCR_BASE, MTNIC_HCR_SIZE); if ( !mtnic->hcr ) { DBG("Couldn't map command register\n"); return -EADDRINUSE; } mtnic_alloc_aligned(PAGE_SIZE, (void *)&mtnic->cmd.buf, &mtnic->cmd.mapping, PAGE_SIZE); if ( !mtnic->cmd.buf ) { DBG("Error in allocating buffer for command interface\n"); return -EADDRINUSE; } return 0; } /** * Free RX io buffers */ static void mtnic_free_io_buffers(struct mtnic_ring *ring) { int index; for (; ring->cons <= ring->prod; ++ring->cons) { index = ring->cons & ring->size_mask; if ( ring->iobuf[index] ) { free_iob(ring->iobuf[index]); } } } /** * * mtnic alloc and attach io buffers * */ static int mtnic_alloc_iobuf(struct mtnic_port *priv, struct mtnic_ring *ring, unsigned int size) { struct mtnic_rx_desc *rx_desc_ptr = ring->buf; u32 index; while ((u32)(ring->prod - ring->cons) < UNITS_BUFFER_SIZE) { index = ring->prod & ring->size_mask; ring->iobuf[index] = alloc_iob(size); if (!ring->iobuf[index]) { if (ring->prod <= (ring->cons + 1)) { DBG ( "Dropping packet, buffer is full\n" ); } break; } /* Attach io_buffer to descriptor */ rx_desc_ptr = ring->buf + (sizeof(struct mtnic_rx_desc) * index); rx_desc_ptr->data.count = cpu_to_be32(size); rx_desc_ptr->data.mem_type = priv->mtnic->fw.mem_type_snoop_be; rx_desc_ptr->data.addr_l = cpu_to_be32( virt_to_bus(ring->iobuf[index]->data)); ++ ring->prod; } /* Update RX producer index (PI) */ ring->db->count = cpu_to_be32(ring->prod & 0xffff); return 0; } /** * mtnic alloc ring * * Alloc and configure TX or RX ring * */ static int mtnic_alloc_ring(struct mtnic_port *priv, struct mtnic_ring *ring, u32 size, u16 stride, u16 cq, u8 is_rx) { unsigned int i; int err; struct mtnic_rx_desc *rx_desc; struct mtnic_tx_desc *tx_desc; ring->size = size; /* Number of descriptors */ ring->size_mask = size - 1; ring->stride = stride; /* Size of each entry */ ring->cq = cq; /* CQ number associated with this ring */ ring->cons = 0; ring->prod = 0; /* Alloc descriptors buffer */ ring->buf_size = ring->size * ((is_rx) ? sizeof(struct mtnic_rx_desc) : sizeof(struct mtnic_tx_desc)); err = mtnic_alloc_aligned(ring->buf_size, (void *)&ring->buf, &ring->dma, PAGE_SIZE); if (err) { DBG("Failed allocating descriptor ring sizeof %x\n", ring->buf_size); return -EADDRINUSE; } memset(ring->buf, 0, ring->buf_size); DBG("Allocated %s ring (addr:%p) - buf:%p size:%x" "buf_size:%x dma:%lx\n", is_rx ? "Rx" : "Tx", ring, ring->buf, ring->size, ring->buf_size, ring->dma); if (is_rx) { /* RX ring */ /* Alloc doorbell */ err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record), (void *)&ring->db, &ring->db_dma, 32); if (err) { DBG("Failed allocating Rx ring doorbell record\n"); free_memblock(ring->buf, ring->buf_size); return -EADDRINUSE; } /* ==- Configure Descriptor -== */ /* Init ctrl seg of rx desc */ for (i = 0; i < UNITS_BUFFER_SIZE; ++i) { rx_desc = ring->buf + (sizeof(struct mtnic_rx_desc) * i); /* Pre-link descriptor */ rx_desc->next = cpu_to_be16(i + 1); } /*The last ctrl descriptor is '0' and points to the first one*/ /* Alloc IO_BUFFERS */ err = mtnic_alloc_iobuf ( priv, ring, DEF_IOBUF_SIZE ); if (err) { DBG("ERROR Allocating io buffer\n"); free_memblock(ring->buf, ring->buf_size); return -EADDRINUSE; } } else { /* TX ring */ /* Set initial ownership of all Tx Desc' to SW (1) */ for (i = 0; i < ring->size; i++) { tx_desc = ring->buf + ring->stride * i; tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_BIT_DESC_OWN); } /* DB */ ring->db_offset = cpu_to_be32( ((u32) priv->mtnic->fw.tx_offset[priv->port]) << 8); /* Map Tx+CQ doorbells */ DBG("Mapping TxCQ doorbell at offset:0x%x\n", priv->mtnic->fw.txcq_db_offset); ring->txcq_db = ioremap(mtnic_pci_dev.dev.bar[2] + priv->mtnic->fw.txcq_db_offset, PAGE_SIZE); if (!ring->txcq_db) { DBG("Couldn't map txcq doorbell, aborting...\n"); free_memblock(ring->buf, ring->buf_size); return -EADDRINUSE; } } return 0; } /** * mtnic alloc CQ * * Alloc and configure CQ. * */ static int mtnic_alloc_cq(struct net_device *dev, int num, struct mtnic_cq *cq, u8 is_rx, u32 size, u32 offset_ind) { int err ; unsigned int i; cq->num = num; cq->dev = dev; cq->size = size; cq->last = 0; cq->is_rx = is_rx; cq->offset_ind = offset_ind; /* Alloc doorbell */ err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record), (void *)&cq->db, &cq->db_dma, 32); if (err) { DBG("Failed allocating CQ doorbell record\n"); return -EADDRINUSE; } memset(cq->db, 0, sizeof(struct mtnic_cq_db_record)); /* Alloc CQEs buffer */ cq->buf_size = size * sizeof(struct mtnic_cqe); err = mtnic_alloc_aligned(cq->buf_size, (void *)&cq->buf, &cq->dma, PAGE_SIZE); if (err) { DBG("Failed allocating CQ buffer\n"); free_memblock(cq->db, sizeof(struct mtnic_cq_db_record)); return -EADDRINUSE; } memset(cq->buf, 0, cq->buf_size); DBG("Allocated CQ (addr:%p) - size:%x buf:%p buf_size:%x " "dma:%lx db:%p db_dma:%lx\n" "cqn offset:%x \n", cq, cq->size, cq->buf, cq->buf_size, cq->dma, cq->db, cq->db_dma, offset_ind); /* Set ownership of all CQEs to HW */ DBG("Setting HW ownership for CQ:%d\n", num); for (i = 0; i < cq->size; i++) { /* Initial HW ownership is 1 */ cq->buf[i].op_tr_own = MTNIC_BIT_CQ_OWN; } return 0; } /** * mtnic_alloc_resources * * Alloc and configure CQs, Tx, Rx */ unsigned int mtnic_alloc_resources(struct net_device *dev) { struct mtnic_port *priv = netdev_priv(dev); int err; int cq_ind = 0; int cq_offset = priv->mtnic->fw.cq_offset; /* Alloc 1st CQ */ err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 1 /* RX */, UNITS_BUFFER_SIZE, cq_offset + cq_ind); if (err) { DBG("Failed allocating Rx CQ\n"); return -EADDRINUSE; } /* Alloc RX */ err = mtnic_alloc_ring(priv, &priv->rx_ring, UNITS_BUFFER_SIZE, sizeof(struct mtnic_rx_desc), cq_ind, /* RX */1); if (err) { DBG("Failed allocating Rx Ring\n"); goto cq0_error; } ++cq_ind; /* alloc 2nd CQ */ err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 0 /* TX */, UNITS_BUFFER_SIZE, cq_offset + cq_ind); if (err) { DBG("Failed allocating Tx CQ\n"); goto rx_error; } /* Alloc TX */ err = mtnic_alloc_ring(priv, &priv->tx_ring, UNITS_BUFFER_SIZE, sizeof(struct mtnic_tx_desc), cq_ind, /* TX */ 0); if (err) { DBG("Failed allocating Tx ring\n"); goto cq1_error; } return 0; cq1_error: free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); rx_error: free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); mtnic_free_io_buffers(&priv->rx_ring); cq0_error: free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); return -EADDRINUSE; } /** * mtnic alloc_eq * * Note: EQ is not used by the driver but must be allocated */ static int mtnic_alloc_eq(struct mtnic *mtnic) { int err; unsigned int i; struct mtnic_eqe *eqe_desc = NULL; /* Allocating doorbell */ mtnic->eq_db = ioremap(mtnic_pci_dev.dev.bar[2] + mtnic->fw.eq_db_offset, sizeof(u32)); if (!mtnic->eq_db) { DBG("Couldn't map EQ doorbell, aborting...\n"); return -EADDRINUSE; } /* Allocating buffer */ mtnic->eq.size = NUM_EQES; mtnic->eq.buf_size = mtnic->eq.size * sizeof(struct mtnic_eqe); err = mtnic_alloc_aligned(mtnic->eq.buf_size, (void *)&mtnic->eq.buf, &mtnic->eq.dma, PAGE_SIZE); if (err) { DBG("Failed allocating EQ buffer\n"); iounmap(mtnic->eq_db); return -EADDRINUSE; } memset(mtnic->eq.buf, 0, mtnic->eq.buf_size); for (i = 0; i < mtnic->eq.size; i++) eqe_desc = mtnic->eq.buf + (sizeof(struct mtnic_eqe) * i); eqe_desc->own |= MTNIC_BIT_EQE_OWN; mdelay(20); return 0; } /******************************************************************** * * Mtnic commands functions * -=-=-=-=-=-=-=-=-=-=-=-= * * * *********************************************************************/ static inline int cmdif_go_bit(struct mtnic *mtnic) { struct mtnic_if_cmd_reg *hcr = mtnic->hcr; u32 status; int i; for (i = 0; i < TBIT_RETRIES; i++) { status = be32_to_cpu(readl(&hcr->status_go_opcode)); if ((status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_T_BIT)) == (mtnic->cmd.tbit << MTNIC_BC_OFF(MTNIC_MASK_CMD_REG_T_BIT))) { /* Read expected t-bit - now return go-bit value */ return status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT); } } DBG("Invalid tbit after %d retries!\n", TBIT_RETRIES); return -EBUSY; /* Return busy... */ } /* Base Command interface */ static int mtnic_cmd(struct mtnic *mtnic, void *in_imm, void *out_imm, u32 in_modifier, u16 op) { struct mtnic_if_cmd_reg *hcr = mtnic->hcr; int err = 0; u32 out_param_h = 0; u32 out_param_l = 0; u32 in_param_h = 0; u32 in_param_l = 0; static u16 token = 0x8000; u32 status; unsigned int timeout = 0; token++; if ( cmdif_go_bit ( mtnic ) ) { DBG("GO BIT BUSY:%p.\n", hcr + 6); err = -EBUSY; goto out; } if (in_imm) { in_param_h = *((u32*)in_imm); in_param_l = *((u32*)in_imm + 1); } else { in_param_l = cpu_to_be32(mtnic->cmd.mapping); } out_param_l = cpu_to_be32(mtnic->cmd.mapping); /* writing to MCR */ writel(in_param_h, &hcr->in_param_h); writel(in_param_l, &hcr->in_param_l); writel((u32) cpu_to_be32(in_modifier), &hcr->input_modifier); writel(out_param_h, &hcr->out_param_h); writel(out_param_l, &hcr->out_param_l); writel((u32)cpu_to_be32(token << 16), &hcr->token); wmb(); /* flip toggle bit before each write to the HCR */ mtnic->cmd.tbit = !mtnic->cmd.tbit; writel( ( u32 ) cpu_to_be32(MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT) | ( mtnic->cmd.tbit << MTNIC_BC_OFF ( MTNIC_MASK_CMD_REG_T_BIT ) ) | op ), &hcr->status_go_opcode); while ( cmdif_go_bit ( mtnic ) && ( timeout <= GO_BIT_TIMEOUT ) ) { mdelay ( 1 ); ++timeout; } if ( cmdif_go_bit ( mtnic ) ) { DBG("Command opcode:0x%x token:0x%x TIMEOUT.\n", op, token); err = -EBUSY; goto out; } if (out_imm) { *((u32 *)out_imm) = readl(&hcr->out_param_h); *((u32 *)out_imm + 1) = readl(&hcr->out_param_l); } status = be32_to_cpu((u32)readl(&hcr->status_go_opcode)) >> 24; if (status) { DBG("Command opcode:0x%x token:0x%x returned:0x%x\n", op, token, status); return status; } out: return err; } /* MAP PAGES wrapper */ static int mtnic_map_cmd(struct mtnic *mtnic, u16 op, struct mtnic_pages pages) { unsigned int j; u32 addr; unsigned int len; u32 *page_arr = mtnic->cmd.buf; int nent = 0; int err = 0; memset(page_arr, 0, PAGE_SIZE); len = PAGE_SIZE * pages.num; pages.buf = (u32 *)umalloc(PAGE_SIZE * (pages.num + 1)); addr = PAGE_SIZE + ((virt_to_bus(pages.buf) & 0xfffff000) + PAGE_SIZE); DBG("Mapping pages: size: %x address: %p\n", pages.num, pages.buf); if (addr & (PAGE_MASK)) { DBG("Got FW area not aligned to %d (%llx/%x)\n", PAGE_SIZE, (u64) addr, len); return -EADDRINUSE; } /* Function maps each PAGE seperately */ for (j = 0; j < len; j+= PAGE_SIZE) { page_arr[nent * 4 + 3] = cpu_to_be32(addr + j); if (++nent == MTNIC_MAILBOX_SIZE / 16) { err = mtnic_cmd(mtnic, NULL, NULL, nent, op); if (err) return -EIO; nent = 0; } } if (nent) { err = mtnic_cmd(mtnic, NULL, NULL, nent, op); } return err; } /* * Query FW */ static int mtnic_QUERY_FW ( struct mtnic *mtnic ) { int err; struct mtnic_if_query_fw_out_mbox *cmd = mtnic->cmd.buf; err = mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_QUERY_FW); if (err) return -EIO; /* Get FW and interface versions */ mtnic->fw_ver = ((u64) be16_to_cpu(cmd->rev_maj) << 32) | ((u64) be16_to_cpu(cmd->rev_min) << 16) | (u64) be16_to_cpu(cmd->rev_smin); mtnic->fw.ifc_rev = be16_to_cpu(cmd->ifc_rev); /* Get offset for internal error reports (debug) */ mtnic->fw.err_buf.offset = be64_to_cpu(cmd->err_buf_start); mtnic->fw.err_buf.size = be32_to_cpu(cmd->err_buf_size); DBG("Error buf offset is %llx\n", mtnic->fw.err_buf.offset); /* Get number of required FW (4k) pages */ mtnic->fw.fw_pages.num = be16_to_cpu(cmd->fw_pages); return 0; } static int mtnic_OPEN_NIC(struct mtnic *mtnic) { struct mtnic_if_open_nic_in_mbox *open_nic = mtnic->cmd.buf; u32 extra_pages[2] = {0}; int err; memset(open_nic, 0, sizeof *open_nic); /* port 1 */ open_nic->log_rx_p1 = 0; open_nic->log_cq_p1 = 1; open_nic->log_tx_p1 = 0; open_nic->steer_p1 = MTNIC_IF_STEER_RSS; /* MAC + VLAN - leave reserved */ /* port 2 */ open_nic->log_rx_p2 = 0; open_nic->log_cq_p2 = 1; open_nic->log_tx_p2 = 0; open_nic->steer_p2 = MTNIC_IF_STEER_RSS; /* MAC + VLAN - leave reserved */ err = mtnic_cmd(mtnic, NULL, extra_pages, 0, MTNIC_IF_CMD_OPEN_NIC); mtnic->fw.extra_pages.num = be32_to_cpu(*(extra_pages+1)); DBG("Extra pages num is %x\n", mtnic->fw.extra_pages.num); return err; } static int mtnic_CONFIG_RX(struct mtnic *mtnic) { struct mtnic_if_config_rx_in_imm config_rx; memset(&config_rx, 0, sizeof config_rx); return mtnic_cmd(mtnic, &config_rx, NULL, 0, MTNIC_IF_CMD_CONFIG_RX); } static int mtnic_CONFIG_TX(struct mtnic *mtnic) { struct mtnic_if_config_send_in_imm config_tx; config_tx.enph_gpf = 0; return mtnic_cmd(mtnic, &config_tx, NULL, 0, MTNIC_IF_CMD_CONFIG_TX); } static int mtnic_HEART_BEAT(struct mtnic_port *priv, u32 *link_state) { struct mtnic_if_heart_beat_out_imm heart_beat; int err; u32 flags; err = mtnic_cmd(priv->mtnic, NULL, &heart_beat, 0, MTNIC_IF_CMD_HEART_BEAT); if (!err) { flags = be32_to_cpu(heart_beat.flags); if (flags & MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR)) { DBG("Internal error detected\n"); return -EIO; } *link_state = flags & ~((u32) MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR)); } return err; } /* * Port commands */ static int mtnic_SET_PORT_DEFAULT_RING(struct mtnic_port *priv, u8 port, u16 ring) { struct mtnic_if_set_port_default_ring_in_imm def_ring; memset(&def_ring, 0, sizeof(def_ring)); def_ring.ring = ring; return mtnic_cmd(priv->mtnic, &def_ring, NULL, port + 1, MTNIC_IF_CMD_SET_PORT_DEFAULT_RING); } static int mtnic_CONFIG_PORT_RSS_STEER(struct mtnic_port *priv, int port) { memset(priv->mtnic->cmd.buf, 0, PAGE_SIZE); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_CONFIG_PORT_RSS_STEER); } static int mtnic_SET_PORT_RSS_INDIRECTION(struct mtnic_port *priv, int port) { memset(priv->mtnic->cmd.buf, 0, PAGE_SIZE); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_SET_PORT_RSS_INDIRECTION); } /* * Config commands */ static int mtnic_CONFIG_CQ(struct mtnic_port *priv, int port, u16 cq_ind, struct mtnic_cq *cq) { struct mtnic_if_config_cq_in_mbox *config_cq = priv->mtnic->cmd.buf; memset(config_cq, 0, sizeof *config_cq); config_cq->cq = cq_ind; config_cq->size = fls(UNITS_BUFFER_SIZE - 1); config_cq->offset = ((cq->dma) & (PAGE_MASK)) >> 6; config_cq->db_record_addr_l = cpu_to_be32(cq->db_dma); config_cq->page_address[1] = cpu_to_be32(cq->dma); DBG("config cq address: %x dma_address: %lx" "offset: %d size %d index: %d\n" , config_cq->page_address[1],cq->dma, config_cq->offset, config_cq->size, config_cq->cq ); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_CONFIG_CQ); } static int mtnic_CONFIG_TX_RING(struct mtnic_port *priv, u8 port, u16 ring_ind, struct mtnic_ring *ring) { struct mtnic_if_config_send_ring_in_mbox *config_tx_ring = priv->mtnic->cmd.buf; memset(config_tx_ring, 0, sizeof *config_tx_ring); config_tx_ring->ring = cpu_to_be16(ring_ind); config_tx_ring->size = fls(UNITS_BUFFER_SIZE - 1); config_tx_ring->cq = cpu_to_be16(ring->cq); config_tx_ring->page_address[1] = cpu_to_be32(ring->dma); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_CONFIG_TX_RING); } static int mtnic_CONFIG_RX_RING(struct mtnic_port *priv, u8 port, u16 ring_ind, struct mtnic_ring *ring) { struct mtnic_if_config_rx_ring_in_mbox *config_rx_ring = priv->mtnic->cmd.buf; memset(config_rx_ring, 0, sizeof *config_rx_ring); config_rx_ring->ring = ring_ind; MTNIC_BC_PUT(config_rx_ring->stride_size, fls(UNITS_BUFFER_SIZE - 1), MTNIC_MASK_CONFIG_RX_RING_SIZE); MTNIC_BC_PUT(config_rx_ring->stride_size, 1, MTNIC_MASK_CONFIG_RX_RING_STRIDE); config_rx_ring->cq = cpu_to_be16(ring->cq); config_rx_ring->db_record_addr_l = cpu_to_be32(ring->db_dma); DBG("Config RX ring starting at address:%lx\n", ring->dma); config_rx_ring->page_address[1] = cpu_to_be32(ring->dma); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_CONFIG_RX_RING); } static int mtnic_CONFIG_EQ(struct mtnic *mtnic) { struct mtnic_if_config_eq_in_mbox *eq = mtnic->cmd.buf; if (mtnic->eq.dma & (PAGE_MASK)) { DBG("misalligned eq buffer:%lx\n", mtnic->eq.dma); return -EADDRINUSE; } memset(eq, 0, sizeof *eq); MTNIC_BC_PUT(eq->offset, mtnic->eq.dma >> 6, MTNIC_MASK_CONFIG_EQ_OFFSET); MTNIC_BC_PUT(eq->size, fls(mtnic->eq.size - 1) - 1, MTNIC_MASK_CONFIG_EQ_SIZE); MTNIC_BC_PUT(eq->int_vector, 0, MTNIC_MASK_CONFIG_EQ_INT_VEC); eq->page_address[1] = cpu_to_be32(mtnic->eq.dma); return mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_CONFIG_EQ); } static int mtnic_SET_RX_RING_ADDR(struct mtnic_port *priv, u8 port, u64* mac) { struct mtnic_if_set_rx_ring_addr_in_imm ring_addr; u32 modifier = ((u32) port + 1) << 16; memset(&ring_addr, 0, sizeof(ring_addr)); ring_addr.mac_31_0 = cpu_to_be32(*mac & 0xffffffff); ring_addr.mac_47_32 = cpu_to_be16((*mac >> 32) & 0xffff); ring_addr.flags_vlan_id |= cpu_to_be16( MTNIC_BC_MASK(MTNIC_MASK_SET_RX_RING_ADDR_BY_MAC)); return mtnic_cmd(priv->mtnic, &ring_addr, NULL, modifier, MTNIC_IF_CMD_SET_RX_RING_ADDR); } static int mtnic_SET_PORT_STATE(struct mtnic_port *priv, u8 port, u8 state) { struct mtnic_if_set_port_state_in_imm port_state; port_state.state = state ? cpu_to_be32( MTNIC_BC_MASK(MTNIC_MASK_CONFIG_PORT_STATE)) : 0; port_state.reserved = 0; return mtnic_cmd(priv->mtnic, &port_state, NULL, port + 1, MTNIC_IF_CMD_SET_PORT_STATE); } static int mtnic_SET_PORT_MTU(struct mtnic_port *priv, u8 port, u16 mtu) { struct mtnic_if_set_port_mtu_in_imm set_mtu; memset(&set_mtu, 0, sizeof(set_mtu)); set_mtu.mtu = cpu_to_be16(mtu); return mtnic_cmd(priv->mtnic, &set_mtu, NULL, port + 1, MTNIC_IF_CMD_SET_PORT_MTU); } /* static int mtnic_CONFIG_PORT_VLAN_FILTER(struct mtnic_port *priv, int port) { struct mtnic_if_config_port_vlan_filter_in_mbox *vlan_filter = priv->mtnic->cmd.buf; // When no vlans are configured we disable the filter // (i.e., pass all vlans) because we ignore them anyhow memset(vlan_filter, 0xff, sizeof(*vlan_filter)); return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, MTNIC_IF_CMD_CONFIG_PORT_VLAN_FILTER); } */ static int mtnic_RELEASE_RESOURCE(struct mtnic_port *priv, u8 port, u8 type, u8 index) { struct mtnic_if_release_resource_in_imm rel; memset(&rel, 0, sizeof rel); rel.index = index; rel.type = type; return mtnic_cmd ( priv->mtnic, &rel, NULL, ( type == MTNIC_IF_RESOURCE_TYPE_EQ ) ? 0 : port + 1, MTNIC_IF_CMD_RELEASE_RESOURCE ); } static int mtnic_QUERY_CAP(struct mtnic *mtnic, u8 index, u8 mod, u64 *result) { struct mtnic_if_query_cap_in_imm cap; u32 out_imm[2]; int err; memset(&cap, 0, sizeof cap); cap.cap_index = index; cap.cap_modifier = mod; err = mtnic_cmd(mtnic, &cap, &out_imm, 0, MTNIC_IF_CMD_QUERY_CAP); *((u32*)result) = be32_to_cpu(*(out_imm+1)); *((u32*)result + 1) = be32_to_cpu(*out_imm); DBG("Called Query cap with index:0x%x mod:%d result:0x%llx" " error:%d\n", index, mod, *result, err); return err; } #define DO_QUERY_CAP(cap, mod, var) \ err = mtnic_QUERY_CAP(mtnic, cap, mod, &result);\ if (err) \ return err; \ (var) = result static int mtnic_query_num_ports(struct mtnic *mtnic) { int err = 0; u64 result; DO_QUERY_CAP(MTNIC_IF_CAP_NUM_PORTS, 0, mtnic->fw.num_ports); return 0; } static int mtnic_query_mac(struct mtnic *mtnic) { int err = 0; int i; u64 result; for (i = 0; i < mtnic->fw.num_ports; i++) { DO_QUERY_CAP(MTNIC_IF_CAP_DEFAULT_MAC, i + 1, mtnic->fw.mac[i]); } return 0; } static int mtnic_query_offsets(struct mtnic *mtnic) { int err; int i; u64 result; DO_QUERY_CAP(MTNIC_IF_CAP_MEM_KEY, MTNIC_IF_MEM_TYPE_SNOOP, mtnic->fw.mem_type_snoop_be); mtnic->fw.mem_type_snoop_be = cpu_to_be32(mtnic->fw.mem_type_snoop_be); DO_QUERY_CAP(MTNIC_IF_CAP_TX_CQ_DB_OFFSET, 0, mtnic->fw.txcq_db_offset); DO_QUERY_CAP(MTNIC_IF_CAP_EQ_DB_OFFSET, 0, mtnic->fw.eq_db_offset); for (i = 0; i < mtnic->fw.num_ports; i++) { DO_QUERY_CAP(MTNIC_IF_CAP_CQ_OFFSET, i + 1, mtnic->fw.cq_offset); DO_QUERY_CAP(MTNIC_IF_CAP_TX_OFFSET, i + 1, mtnic->fw.tx_offset[i]); DO_QUERY_CAP(MTNIC_IF_CAP_RX_OFFSET, i + 1, mtnic->fw.rx_offset[i]); DBG("--> Port %d CQ offset:0x%x\n", i, mtnic->fw.cq_offset); DBG("--> Port %d Tx offset:0x%x\n", i, mtnic->fw.tx_offset[i]); DBG("--> Port %d Rx offset:0x%x\n", i, mtnic->fw.rx_offset[i]); } mdelay(20); return 0; } /******************************************************************** * * MTNIC initalization functions * * * * *********************************************************************/ /** * Reset device */ void mtnic_reset ( void ) { void *reset = ioremap ( mtnic_pci_dev.dev.bar[0] + MTNIC_RESET_OFFSET, 4 ); writel ( cpu_to_be32 ( 1 ), reset ); iounmap ( reset ); } /** * Restore PCI config */ static int restore_config(void) { int i; int rc; for (i = 0; i < 64; ++i) { if (i != 22 && i != 23) { rc = pci_write_config_dword(mtnic_pci_dev.dev.dev, i << 2, mtnic_pci_dev.dev. dev_config_space[i]); if (rc) return rc; } } return 0; } /** * Init PCI configuration */ static int mtnic_init_pci(struct pci_device *dev) { int i; int err; /* save bars */ DBG("bus=%d devfn=0x%x\n", dev->bus, dev->devfn); for (i = 0; i < 6; ++i) { mtnic_pci_dev.dev.bar[i] = pci_bar_start(dev, PCI_BASE_ADDRESS_0 + (i << 2)); DBG("bar[%d]= 0x%08lx \n", i, mtnic_pci_dev.dev.bar[i]); } /* save config space */ for (i = 0; i < 64; ++i) { err = pci_read_config_dword(dev, i << 2, &mtnic_pci_dev.dev. dev_config_space[i]); if (err) { DBG("Can not save configuration space"); return err; } } mtnic_pci_dev.dev.dev = dev; return 0; } /** * Initial hardware */ static inline int mtnic_init_card(struct mtnic *mtnic) { int err = 0; /* Alloc command interface */ err = mtnic_alloc_cmdif ( mtnic ); if (err) { DBG("Failed to init command interface, aborting\n"); return -EADDRINUSE; } /** * Bring up HW */ err = mtnic_QUERY_FW ( mtnic ); if (err) { DBG("QUERY_FW command failed, aborting\n"); goto cmd_error; } DBG("Command interface revision:%d\n", mtnic->fw.ifc_rev); /* Allocate memory for FW and start it */ err = mtnic_map_cmd(mtnic, MTNIC_IF_CMD_MAP_FW, mtnic->fw.fw_pages); if (err) { DBG("Eror In MAP_FW\n"); if (mtnic->fw.fw_pages.buf) ufree((intptr_t)mtnic->fw.fw_pages.buf); goto cmd_error; } /* Run firmware */ err = mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_RUN_FW); if (err) { DBG("Eror In RUN FW\n"); goto map_fw_error; } DBG("FW version:%d.%d.%d\n", (u16) (mtnic->fw_ver >> 32), (u16) ((mtnic->fw_ver >> 16) & 0xffff), (u16) (mtnic->fw_ver & 0xffff)); /* Query num ports */ err = mtnic_query_num_ports(mtnic); if (err) { DBG("Insufficient resources, aborting\n"); goto map_fw_error; } /* Open NIC */ err = mtnic_OPEN_NIC(mtnic); if (err) { DBG("Failed opening NIC, aborting\n"); goto map_fw_error; } /* Allocate and map pages worksace */ err = mtnic_map_cmd(mtnic, MTNIC_IF_CMD_MAP_PAGES, mtnic->fw.extra_pages); if (err) { DBG("Couldn't allocate %x FW extra pages, aborting\n", mtnic->fw.extra_pages.num); if (mtnic->fw.extra_pages.buf) ufree((intptr_t)mtnic->fw.extra_pages.buf); goto map_fw_error; } /* Get device information */ err = mtnic_query_mac(mtnic); if (err) { DBG("Insufficient resources in quesry mac, aborting\n"); goto map_fw_error; } /* Get device offsets */ err = mtnic_query_offsets(mtnic); if (err) { DBG("Failed retrieving resource offests, aborting\n"); ufree((intptr_t)mtnic->fw.extra_pages.buf); goto map_extra_error; } /* Alloc EQ */ err = mtnic_alloc_eq(mtnic); if (err) { DBG("Failed init shared resources. error: %d\n", err); goto map_extra_error; } /* Configure HW */ err = mtnic_CONFIG_EQ(mtnic); if (err) { DBG("Failed configuring EQ\n"); goto eq_error; } err = mtnic_CONFIG_RX(mtnic); if (err) { DBG("Failed Rx configuration\n"); goto eq_error; } err = mtnic_CONFIG_TX(mtnic); if (err) { DBG("Failed Tx configuration\n"); goto eq_error; } return 0; eq_error: iounmap(mtnic->eq_db); free_memblock(mtnic->eq.buf, mtnic->eq.buf_size); map_extra_error: ufree((intptr_t)mtnic->fw.extra_pages.buf); map_fw_error: ufree((intptr_t)mtnic->fw.fw_pages.buf); cmd_error: iounmap(mtnic->hcr); free_memblock(mtnic->cmd.buf, PAGE_SIZE); return -EADDRINUSE; } /******************************************************************* * * Process functions * * process compliations of TX and RX * * ********************************************************************/ void mtnic_process_tx_cq(struct mtnic_port *priv, struct net_device *dev, struct mtnic_cq *cq) { struct mtnic_cqe *cqe = cq->buf; struct mtnic_ring *ring = &priv->tx_ring; u16 index; index = cq->last & (cq->size-1); cqe = &cq->buf[index]; /* Owner bit changes every round */ while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) { netdev_tx_complete (dev, ring->iobuf[index]); ++cq->last; index = cq->last & (cq->size-1); cqe = &cq->buf[index]; } /* Update consumer index */ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); wmb(); /* ensure HW sees CQ consumer before we post new buffers */ ring->cons = cq->last; } int mtnic_process_rx_cq(struct mtnic_port *priv, struct net_device *dev, struct mtnic_cq *cq) { struct mtnic_cqe *cqe; struct mtnic_ring *ring = &priv->rx_ring; int index; int err; struct io_buffer *rx_iob; unsigned int length; /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx * descriptor offset can be deduced from the CQE index instead of * reading 'cqe->index' */ index = cq->last & (cq->size-1); cqe = &cq->buf[index]; /* Process all completed CQEs */ while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) { /* Drop packet on bad receive or bad checksum */ if ((cqe->op_tr_own & 0x1f) == MTNIC_OPCODE_ERROR) { DBG("CQE completed with error - vendor \n"); free_iob(ring->iobuf[index]); goto next; } if (cqe->enc_bf & MTNIC_BIT_BAD_FCS) { DBG("Accepted packet with bad FCS\n"); free_iob(ring->iobuf[index]); goto next; } /* * Packet is OK - process it. */ length = be32_to_cpu(cqe->byte_cnt); rx_iob = ring->iobuf[index]; iob_put(rx_iob, length); /* Add this packet to the receive queue. */ netdev_rx(dev, rx_iob); ring->iobuf[index] = NULL; next: ++cq->last; index = cq->last & (cq->size-1); cqe = &cq->buf[index]; } /* Update consumer index */ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); wmb(); /* ensure HW sees CQ consumer before we post new buffers */ ring->cons = cq->last; if (ring->prod - ring->cons < (MAX_GAP_PROD_CONS)) { err = mtnic_alloc_iobuf(priv, &priv->rx_ring, DEF_IOBUF_SIZE); if (err) { DBG("ERROR Allocating io buffer"); return -EADDRINUSE; } } return 0; } /******************************************************************** * * net_device functions * * * open, poll, close, probe, disable, irq * *********************************************************************/ static int mtnic_open(struct net_device *dev) { struct mtnic_port *priv = netdev_priv(dev); int err = 0; struct mtnic_ring *ring; struct mtnic_cq *cq; int cq_ind = 0; u32 dev_link_state; int link_check; DBG("starting port:%d, MAC Address: 0x%12llx\n", priv->port, priv->mtnic->fw.mac[priv->port]); /* Alloc and configure CQs, TX, RX */ err = mtnic_alloc_resources ( dev ); if (err) { DBG("Error allocating resources\n"); return -EADDRINUSE; } /* Pass CQs configuration to HW */ for (cq_ind = 0; cq_ind < NUM_CQS; ++cq_ind) { cq = &priv->cq[cq_ind]; err = mtnic_CONFIG_CQ(priv, priv->port, cq_ind, cq); if (err) { DBG("Failed configuring CQ:%d error %d\n", cq_ind, err); if (cq_ind) goto cq_error; else goto allocation_error; } /* Update consumer index */ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); } /* Pass Tx configuration to HW */ ring = &priv->tx_ring; err = mtnic_CONFIG_TX_RING(priv, priv->port, 0, ring); if (err) { DBG("Failed configuring Tx ring:0\n"); goto cq_error; } /* Pass RX configuration to HW */ ring = &priv->rx_ring; err = mtnic_CONFIG_RX_RING(priv, priv->port, 0, ring); if (err) { DBG("Failed configuring Rx ring:0\n"); goto tx_error; } /* Configure Rx steering */ err = mtnic_CONFIG_PORT_RSS_STEER(priv, priv->port); if (!err) err = mtnic_SET_PORT_RSS_INDIRECTION(priv, priv->port); if (err) { DBG("Failed configuring RSS steering\n"); goto rx_error; } /* Set the port default ring to ring 0 */ err = mtnic_SET_PORT_DEFAULT_RING(priv, priv->port, 0); if (err) { DBG("Failed setting default ring\n"); goto rx_error; } /* Set Mac address */ err = mtnic_SET_RX_RING_ADDR(priv, priv->port, &priv->mtnic->fw.mac[priv->port]); if (err) { DBG("Failed setting default MAC address\n"); goto rx_error; } /* Set MTU */ err = mtnic_SET_PORT_MTU(priv, priv->port, DEF_MTU); if (err) { DBG("Failed setting MTU\n"); goto rx_error; } /* Configure VLAN filter */ /* By adding this function, The second port won't accept packets err = mtnic_CONFIG_PORT_VLAN_FILTER(priv, priv->port); if (err) { DBG("Failed configuring VLAN filter\n"); goto rx_error; } */ /* Bring up physical link */ err = mtnic_SET_PORT_STATE(priv, priv->port, 1); if (err) { DBG("Failed bringing up port\n"); goto rx_error; } /* PORT IS UP */ priv->state = CARD_UP; /* Checking Link is up */ DBG ( "Checking if link is up\n" ); for ( link_check = 0; link_check < CHECK_LINK_TIMES; link_check ++ ) { /* Let link state stabilize if cable was connected */ mdelay ( DELAY_LINK_CHECK ); err = mtnic_HEART_BEAT(priv, &dev_link_state); if (err) { DBG("Failed getting device link state\n"); return -ENETDOWN; } if ( dev_link_state & priv->port ) { /* Link is up */ break; } } if ( ! ( dev_link_state & 0x3 ) ) { DBG("Link down, check cables and restart\n"); netdev_link_down ( dev ); return -ENETDOWN; } DBG ( "Link is up!\n" ); /* Mark as link up */ netdev_link_up ( dev ); return 0; rx_error: err = mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_RX_RING, 0); tx_error: err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_TX_RING, 0); cq_error: while (cq_ind) { err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_CQ, --cq_ind); } if (err) DBG("Eror Releasing resources\n"); allocation_error: free_memblock(priv->tx_ring.buf, priv->tx_ring.buf_size); iounmap(priv->tx_ring.txcq_db); free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); mtnic_free_io_buffers(&priv->rx_ring); return -ENETDOWN; } /** Check if we got completion for receive and transmit and * check the line with heart_bit command */ static void mtnic_poll ( struct net_device *dev ) { struct mtnic_port *priv = netdev_priv(dev); struct mtnic_cq *cq; u32 dev_link_state; int err; unsigned int i; /* In case of an old error then return */ if (priv->state != CARD_UP) return; /* We do not check the device every call _poll call, since it will slow it down */ if ((priv->poll_counter % ROUND_TO_CHECK) == 0) { /* Check device */ err = mtnic_HEART_BEAT(priv, &dev_link_state); if (err) { DBG("Device has internal error\n"); priv->state = CARD_LINK_DOWN; return; } if (!(dev_link_state & 0x3)) { DBG("Link down, check cables and restart\n"); priv->state = CARD_LINK_DOWN; return; } } /* Polling CQ */ for (i = 0; i < NUM_CQS; i++) { cq = &priv->cq[i]; //Passing on the 2 cqs. if (cq->is_rx) { err = mtnic_process_rx_cq(priv, cq->dev, cq); if (err) { priv->state = CARD_LINK_DOWN; DBG(" Error allocating RX buffers\n"); return; } } else { mtnic_process_tx_cq(priv, cq->dev, cq); } } ++ priv->poll_counter; } static int mtnic_transmit( struct net_device *dev, struct io_buffer *iobuf ) { struct mtnic_port *priv = netdev_priv(dev); struct mtnic_ring *ring; struct mtnic_tx_desc *tx_desc; struct mtnic_data_seg *data; u32 index; /* In case of an error then return */ if (priv->state != CARD_UP) return -ENETDOWN; ring = &priv->tx_ring; index = ring->prod & ring->size_mask; if ((ring->prod - ring->cons) >= ring->size) { DBG("No space left for descriptors!!! cons: %x prod: %x\n", ring->cons, ring->prod); mdelay(5); return -EAGAIN;/* no space left */ } /* get current descriptor */ tx_desc = ring->buf + (index * sizeof(struct mtnic_tx_desc)); /* Prepare Data Seg */ data = &tx_desc->data; data->addr_l = cpu_to_be32((u32)virt_to_bus(iobuf->data)); data->count = cpu_to_be32(iob_len(iobuf)); data->mem_type = priv->mtnic->fw.mem_type_snoop_be; /* Prepare ctrl segement */ tx_desc->ctrl.size_vlan = cpu_to_be32(2); tx_desc->ctrl.flags = cpu_to_be32(MTNIC_BIT_TX_COMP | MTNIC_BIT_NO_ICRC); tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_OPCODE_SEND) | ((ring->prod & ring->size) ? cpu_to_be32(MTNIC_BIT_DESC_OWN) : 0); /* Attach io_buffer */ ring->iobuf[index] = iobuf; /* Update producer index */ ++ring->prod; /* Ring doorbell! */ wmb(); writel((u32) ring->db_offset, &ring->txcq_db->send_db); return 0; } static void mtnic_close(struct net_device *dev) { struct mtnic_port *priv = netdev_priv(dev); int err = 0; DBG("Close called for port:%d\n", priv->port); if ( ( priv->state == CARD_UP ) || ( priv->state == CARD_LINK_DOWN ) ) { /* Disable port */ err |= mtnic_SET_PORT_STATE(priv, priv->port, 0); /* * Stop HW associated with this port */ mdelay(5); /* Stop RX */ err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_RX_RING, 0); /* Stop TX */ err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_TX_RING, 0); /* Stop CQs */ err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_CQ, 0); err |= mtnic_RELEASE_RESOURCE(priv, priv->port, MTNIC_IF_RESOURCE_TYPE_CQ, 1); if (err) { DBG("Close reported error %d\n", err); } mdelay ( 10 ); /* free memory */ free_memblock(priv->tx_ring.buf, priv->tx_ring.buf_size); iounmap(priv->tx_ring.txcq_db); free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); /* Free RX buffers */ mtnic_free_io_buffers(&priv->rx_ring); } priv->state = CARD_INITIALIZED; } static void mtnic_disable(struct pci_device *pci) { int err; int i; struct mtnic *mtnic = pci_get_drvdata(pci); struct net_device *dev; struct mtnic_port *priv; for ( i = ( mtnic->fw.num_ports - 1 ); i >= 0; i-- ) { dev = mtnic->netdev[i]; priv = netdev_priv(dev); /* Just in case */ if ( ( priv->state == CARD_UP ) || ( priv->state == CARD_LINK_DOWN ) ) mtnic_close ( dev ); } /* Releasing EQ */ priv = netdev_priv ( mtnic->netdev[0] ); err = mtnic_RELEASE_RESOURCE(priv, 1, MTNIC_IF_RESOURCE_TYPE_EQ, 0); DBG("Calling MTNIC_CLOSE command\n"); err |= mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_CLOSE_NIC); if (err) { DBG("Error Releasing resources %d\n", err); } free_memblock(mtnic->cmd.buf, PAGE_SIZE); iounmap(mtnic->hcr); ufree((intptr_t)mtnic->fw.fw_pages.buf); ufree((intptr_t)mtnic->fw.extra_pages.buf); free_memblock(mtnic->eq.buf, mtnic->eq.buf_size); iounmap(mtnic->eq_db); for ( i = ( mtnic->fw.num_ports - 1 ); i >= 0; i-- ) { dev = mtnic->netdev[i]; unregister_netdev ( dev ); netdev_nullify ( dev ); netdev_put ( dev ); } free ( mtnic ); mtnic_reset (); mdelay ( 1000 ); /* Restore config, if we would like to retry booting */ restore_config (); } static void mtnic_irq(struct net_device *netdev __unused, int enable __unused) { /* Not implemented */ } /** mtnic net device operations */ static struct net_device_operations mtnic_operations = { .open = mtnic_open, .close = mtnic_close, .transmit = mtnic_transmit, .poll = mtnic_poll, .irq = mtnic_irq, }; static int mtnic_probe(struct pci_device *pci, const struct pci_device_id *id __unused) { struct mtnic_port *priv; struct mtnic *mtnic; int err; u64 mac; int port_index; adjust_pci_device(pci); err = mtnic_init_pci(pci); if (err) { DBG("Error in pci_init\n"); return -EIO; } mtnic_reset(); mdelay(1000); err = restore_config(); if (err) { DBG("Error in restoring config\n"); return err; } mtnic = zalloc ( sizeof ( *mtnic ) ); if ( ! mtnic ) { DBG ( "Error Allocating mtnic buffer\n" ); return -EADDRINUSE; } pci_set_drvdata(pci, mtnic); mtnic->pdev = pci; /* Initialize hardware */ err = mtnic_init_card ( mtnic ); if (err) { DBG("Error in init_card\n"); goto err_init_card; } for ( port_index = 0; port_index < mtnic->fw.num_ports; port_index ++ ) { /* Initializing net device */ mtnic->netdev[port_index] = alloc_etherdev( sizeof ( struct mtnic_port ) ); if ( mtnic->netdev[port_index] == NULL ) { DBG("Net device allocation failed\n"); goto err_alloc_mtnic; } /* * Initialize driver private data */ mtnic->netdev[port_index]->dev = &pci->dev; priv = netdev_priv ( mtnic->netdev[port_index] ); memset ( priv, 0, sizeof ( struct mtnic_port ) ); priv->mtnic = mtnic; priv->netdev = mtnic->netdev[port_index]; /* Attach pci device */ netdev_init(mtnic->netdev[port_index], &mtnic_operations); /* Set port number */ priv->port = port_index; /* Set state */ priv->state = CARD_DOWN; } int mac_idx; for ( port_index = 0; port_index < mtnic->fw.num_ports; port_index ++ ) { priv = netdev_priv ( mtnic->netdev[port_index] ); /* Program the MAC address */ mac = priv->mtnic->fw.mac[port_index]; for (mac_idx = 0; mac_idx < MAC_ADDRESS_SIZE; ++mac_idx) { mtnic->netdev[port_index]->hw_addr[MAC_ADDRESS_SIZE - mac_idx - 1] = mac & 0xFF; mac = mac >> 8; } if ( register_netdev ( mtnic->netdev[port_index] ) ) { DBG("Netdev registration failed\n"); priv->state = CARD_INITIALIZED; goto err_alloc_mtnic; } } return 0; err_alloc_mtnic: free ( mtnic ); err_init_card: return -EIO; } static struct pci_device_id mtnic_nics[] = { PCI_ROM ( 0x15b3, 0x6368, "mt25448", "Mellanox ConnectX EN driver", 0 ), PCI_ROM ( 0x15b3, 0x6372, "mt25458", "Mellanox ConnectX ENt driver", 0 ), PCI_ROM ( 0x15b3, 0x6750, "mt26448", "Mellanox ConnectX EN GEN2 driver", 0 ), PCI_ROM ( 0x15b3, 0x675a, "mt26458", "Mellanox ConnectX ENt GEN2 driver", 0 ), }; struct pci_driver mtnic_driver __pci_driver = { .ids = mtnic_nics, .id_count = sizeof(mtnic_nics) / sizeof(mtnic_nics[0]), .probe = mtnic_probe, .remove = mtnic_disable, };