summaryrefslogtreecommitdiffstats
path: root/src/drivers/infiniband/flexboot_nodnic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/infiniband/flexboot_nodnic.c')
-rw-r--r--src/drivers/infiniband/flexboot_nodnic.c1479
1 files changed, 1479 insertions, 0 deletions
diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c
new file mode 100644
index 000000000..dea19ca69
--- /dev/null
+++ b/src/drivers/infiniband/flexboot_nodnic.c
@@ -0,0 +1,1479 @@
+/*
+ * Copyright (C) 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 <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/malloc.h>
+#include <ipxe/umalloc.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/vlan.h>
+#include <ipxe/io.h>
+#include "flexboot_nodnic.h"
+#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h"
+#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h"
+#include "mlx_utils/include/public/mlx_pci_gw.h"
+#include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h"
+#include "mlx_utils/include/public/mlx_types.h"
+#include "mlx_utils/include/public/mlx_utils.h"
+#include "mlx_utils/include/public/mlx_bail.h"
+#include "mlx_nodnic/include/mlx_cmd.h"
+#include "mlx_utils/include/public/mlx_memory.h"
+#include "mlx_utils/include/public/mlx_pci.h"
+#include "mlx_nodnic/include/mlx_device.h"
+#include "mlx_nodnic/include/mlx_port.h"
+
+/***************************************************************************
+ *
+ * Completion queue operations
+ *
+ ***************************************************************************
+ */
+static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) {
+#ifndef DEVICE_CX3
+ mlx_uint32 val = ( port->eth_cq->next_idx & 0xffff );
+ if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) {
+ MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" );
+ return MLX_FAILED;
+ }
+#else
+ mlx_utils *utils = port->port_priv.device->utils;
+ nodnic_port_data_flow_gw *ptr = port->port_priv.data_flow_gw;
+ mlx_uint32 data = 0;
+ mlx_uint32 val = 0;
+
+ if ( port->port_priv.device->device_cap.crspace_doorbells == 0 ) {
+ val = ( port->eth_cq->next_idx & 0xffff );
+ if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) {
+ MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" );
+ return MLX_FAILED;
+ }
+ } else {
+ /* Arming the CQ with CQ CI should be with this format -
+ * 16 bit - CQ CI - same endianness as the FW (don't swap bytes)
+ * 15 bit - reserved
+ * 1 bit - arm CQ - must correct the endianness with the reserved above */
+ data = ( ( ( port->eth_cq->next_idx & 0xffff ) << 16 ) | 0x0080 );
+ /* Write the new index and update FW that new data was submitted */
+ mlx_pci_mem_write ( utils, MlxPciWidthUint32, 0,
+ ( mlx_uint64 ) & ( ptr->armcq_cq_ci_dword ), 1, &data );
+ }
+#endif
+ return 0;
+}
+
+/**
+ * Create completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_create_cq ( struct ib_device *ibdev ,
+ struct ib_completion_queue *cq ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq;
+ mlx_status status = MLX_SUCCESS;
+
+ flexboot_nodnic_cq = (struct flexboot_nodnic_completion_queue *)
+ zalloc(sizeof(*flexboot_nodnic_cq));
+ if ( flexboot_nodnic_cq == NULL ) {
+ status = MLX_OUT_OF_RESOURCES;
+ goto qp_alloc_err;
+ }
+
+ status = nodnic_port_create_cq(&port->port_priv,
+ cq->num_cqes *
+ flexboot_nodnic->callbacks->get_cqe_size(),
+ &flexboot_nodnic_cq->nodnic_completion_queue
+ );
+ MLX_FATAL_CHECK_STATUS(status, create_err,
+ "nodnic_port_create_cq failed");
+ flexboot_nodnic->callbacks->cqe_set_owner(
+ flexboot_nodnic_cq->nodnic_completion_queue->cq_virt,
+ cq->num_cqes);
+
+
+ ib_cq_set_drvdata ( cq, flexboot_nodnic_cq );
+ return status;
+create_err:
+ free(flexboot_nodnic_cq);
+qp_alloc_err:
+ return status;
+}
+
+/**
+ * Destroy completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ */
+static void flexboot_nodnic_destroy_cq ( struct ib_device *ibdev ,
+ struct ib_completion_queue *cq ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq );
+
+ nodnic_port_destroy_cq(&port->port_priv,
+ flexboot_nodnic_cq->nodnic_completion_queue);
+
+ free(flexboot_nodnic_cq);
+}
+
+static
+struct ib_work_queue * flexboot_nodnic_find_wq ( struct ib_device *ibdev ,
+ struct ib_completion_queue *cq,
+ unsigned long qpn, int is_send ) {
+ struct ib_work_queue *wq;
+ struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp;
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct nodnic_ring *ring;
+ mlx_uint32 out_qpn;
+ list_for_each_entry ( wq, &cq->work_queues, list ) {
+ flexboot_nodnic_qp = ib_qp_get_drvdata ( wq->qp );
+ if( wq->is_send == is_send && wq->is_send == TRUE ) {
+ ring = &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring;
+ } else if( wq->is_send == is_send && wq->is_send == FALSE ) {
+ ring = &flexboot_nodnic_qp->nodnic_queue_pair->receive.nodnic_ring;
+ } else {
+ continue;
+ }
+ nodnic_port_get_qpn(&port->port_priv, ring, &out_qpn);
+ if ( out_qpn == qpn )
+ return wq;
+ }
+ return NULL;
+}
+
+/**
+ * Handle completion
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v cqe Hardware completion queue entry
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_complete ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ struct cqe_data *cqe_data ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct ib_work_queue *wq;
+ struct ib_queue_pair *qp;
+ struct io_buffer *iobuf;
+ struct ib_address_vector recv_dest;
+ struct ib_address_vector recv_source;
+ unsigned long qpn;
+ unsigned long wqe_idx;
+ unsigned long wqe_idx_mask;
+ size_t len;
+ int rc = 0;
+
+ /* Parse completion */
+ qpn = cqe_data->qpn;
+
+ if ( cqe_data->is_error == TRUE ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx syndrome %x vendor %x\n",
+ flexboot_nodnic, cq->cqn, cqe_data->syndrome,
+ cqe_data->vendor_err_syndrome );
+ rc = -EIO;
+ /* Don't return immediately; propagate error to completer */
+ }
+
+ /* Identify work queue */
+ wq = flexboot_nodnic_find_wq( ibdev, cq, qpn, cqe_data->is_send );
+ if ( wq == NULL ) {
+ DBGC ( flexboot_nodnic,
+ "flexboot_nodnic %p CQN %#lx unknown %s QPN %#lx\n",
+ flexboot_nodnic, cq->cqn,
+ ( cqe_data->is_send ? "send" : "recv" ), qpn );
+ return -EIO;
+ }
+ qp = wq->qp;
+
+ /* Identify work queue entry */
+ wqe_idx = cqe_data->wqe_counter;
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ DBGCP ( flexboot_nodnic,
+ "NODNIC %p CQN %#lx QPN %#lx %s WQE %#lx completed:\n",
+ flexboot_nodnic, cq->cqn, qp->qpn,
+ ( cqe_data->is_send ? "send" : "recv" ),
+ wqe_idx );
+
+ /* Identify I/O buffer */
+ iobuf = wq->iobufs[wqe_idx & wqe_idx_mask];
+ if ( iobuf == NULL ) {
+ DBGC ( flexboot_nodnic,
+ "NODNIC %p CQN %#lx QPN %#lx empty %s WQE %#lx\n",
+ flexboot_nodnic, cq->cqn, qp->qpn,
+ ( cqe_data->is_send ? "send" : "recv" ), wqe_idx );
+ return -EIO;
+ }
+ wq->iobufs[wqe_idx & wqe_idx_mask] = NULL;
+
+ if ( cqe_data->is_send == TRUE ) {
+ /* Hand off to completion handler */
+ ib_complete_send ( ibdev, qp, iobuf, rc );
+ } else if ( rc != 0 ) {
+ /* Propagate error to receive completion handler */
+ ib_complete_recv ( ibdev, qp, NULL, NULL, iobuf, rc );
+ } else {
+ /* Set received length */
+ len = cqe_data->byte_cnt;
+ assert ( len <= iob_tailroom ( iobuf ) );
+ iob_put ( iobuf, len );
+ memset ( &recv_dest, 0, sizeof ( recv_dest ) );
+ recv_dest.qpn = qpn;
+ memset ( &recv_source, 0, sizeof ( recv_source ) );
+ switch ( qp->type ) {
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ case IB_QPT_UD:
+ case IB_QPT_RC:
+ break;
+ case IB_QPT_ETH:
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
+ /* Hand off to completion handler */
+ ib_complete_recv ( ibdev, qp, &recv_dest,
+ &recv_source, iobuf, rc );
+ }
+
+ return rc;
+}
+/**
+ * Poll completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queues
+ */
+static void flexboot_nodnic_poll_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq );
+ void *cqe;
+ mlx_size cqe_size;
+ struct cqe_data cqe_data;
+ unsigned int cqe_idx_mask;
+ int rc;
+
+ cqe_size = flexboot_nodnic->callbacks->get_cqe_size();
+ while ( TRUE ) {
+ /* Look for completion entry */
+ cqe_idx_mask = ( cq->num_cqes - 1 );
+ cqe = ((uint8_t *)flexboot_nodnic_cq->nodnic_completion_queue->cq_virt) +
+ cqe_size * (cq->next_idx & cqe_idx_mask);
+
+ /* TODO: check fill_completion */
+ flexboot_nodnic->callbacks->fill_completion(cqe, &cqe_data);
+ if ( cqe_data.owner ^
+ ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) {
+ /* Entry still owned by hardware; end of poll */
+ break;
+ }
+ /* Handle completion */
+ rc = flexboot_nodnic_complete ( ibdev, cq, &cqe_data );
+ if ( rc != 0 ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx failed to complete: %s\n",
+ flexboot_nodnic, cq->cqn, strerror ( rc ) );
+ DBGC_HDA ( flexboot_nodnic, virt_to_phys ( cqe ),
+ cqe, sizeof ( *cqe ) );
+ }
+
+ /* Update completion queue's index */
+ cq->next_idx++;
+ }
+}
+/***************************************************************************
+ *
+ * Queue pair operations
+ *
+ ***************************************************************************
+ */
+
+
+/**
+ * Create queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_create_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp;
+ mlx_status status = MLX_SUCCESS;
+
+ flexboot_nodnic_qp = (struct flexboot_nodnic_queue_pair *)zalloc(sizeof(*flexboot_nodnic_qp));
+ if ( flexboot_nodnic_qp == NULL ) {
+ status = MLX_OUT_OF_RESOURCES;
+ goto qp_alloc_err;
+ }
+
+ status = nodnic_port_create_qp(&port->port_priv, qp->type,
+ qp->send.num_wqes * sizeof(struct nodnic_send_wqbb),
+ qp->send.num_wqes,
+ qp->recv.num_wqes * sizeof(struct nodnic_recv_wqe),
+ qp->recv.num_wqes,
+ &flexboot_nodnic_qp->nodnic_queue_pair);
+ MLX_FATAL_CHECK_STATUS(status, create_err,
+ "nodnic_port_create_qp failed");
+ ib_qp_set_drvdata ( qp, flexboot_nodnic_qp );
+ return status;
+create_err:
+ free(flexboot_nodnic_qp);
+qp_alloc_err:
+ return status;
+}
+
+/**
+ * Modify queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_modify_qp ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp __unused) {
+ /*not needed*/
+ return 0;
+}
+
+/**
+ * Destroy queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+static void flexboot_nodnic_destroy_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ 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_port_destroy_qp(&port->port_priv, qp->type,
+ flexboot_nodnic_qp->nodnic_queue_pair);
+
+ free(flexboot_nodnic_qp);
+}
+
+/***************************************************************************
+ *
+ * Work request operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * 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 flexboot_nodnic_post_send ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf) {
+
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct ib_work_queue *wq = &qp->send;
+ struct nodnic_send_wqbb *wqbb;
+ nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair;
+ struct nodnic_send_ring *send_ring = &nodnic_qp->send;
+ mlx_status status = MLX_SUCCESS;
+ unsigned int wqe_idx_mask;
+ unsigned long wqe_idx;
+
+ if ( ( port->port_priv.dma_state == FALSE ) ||
+ ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic DMA disabled\n");
+ status = -ENETDOWN;
+ goto post_send_done;
+ }
+
+ /* Allocate work queue entry */
+ wqe_idx = wq->next_idx;
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wqe_idx & wqe_idx_mask] ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx send queue full\n",
+ flexboot_nodnic, qp->qpn );
+ status = -ENOBUFS;
+ goto post_send_done;
+ }
+ wqbb = &send_ring->wqe_virt[wqe_idx & wqe_idx_mask];
+ wq->iobufs[wqe_idx & wqe_idx_mask] = iobuf;
+
+ assert ( flexboot_nodnic->callbacks->
+ fill_send_wqe[qp->type] != NULL );
+ status = flexboot_nodnic->callbacks->
+ fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf,
+ wqbb, wqe_idx );
+ if ( status != 0 ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx fill send wqe failed\n",
+ flexboot_nodnic, qp->qpn );
+ goto post_send_done;
+ }
+
+ wq->next_idx++;
+
+ status = port->port_priv.send_doorbell ( &port->port_priv,
+ &send_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx );
+ if ( status != 0 ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring send doorbell failed\n", flexboot_nodnic );
+ }
+
+post_send_done:
+ return status;
+}
+
+/**
+ * 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 flexboot_nodnic_post_recv ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct io_buffer *iobuf ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ struct ib_work_queue *wq = &qp->recv;
+ nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair;
+ struct nodnic_recv_ring *recv_ring = &nodnic_qp->receive;
+ struct nodnic_recv_wqe *wqe;
+ unsigned int wqe_idx_mask;
+ mlx_status status = MLX_SUCCESS;
+
+ /* Allocate work queue entry */
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) {
+ DBGC ( flexboot_nodnic,
+ "flexboot_nodnic %p QPN %#lx receive queue full\n",
+ flexboot_nodnic, qp->qpn );
+ status = -ENOBUFS;
+ goto post_recv_done;
+ }
+ wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
+ wqe = &((struct nodnic_recv_wqe*)recv_ring->wqe_virt)[wq->next_idx & wqe_idx_mask];
+
+ MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( 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 ) );
+
+ wq->next_idx++;
+
+ status = port->port_priv.recv_doorbell ( &port->port_priv,
+ &recv_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx );
+ if ( status != 0 ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring receive doorbell failed\n", flexboot_nodnic );
+ }
+post_recv_done:
+ return status;
+}
+
+/***************************************************************************
+ *
+ * Event queues
+ *
+ ***************************************************************************
+ */
+
+static void flexboot_nodnic_poll_eq ( struct ib_device *ibdev ) {
+ struct flexboot_nodnic *flexboot_nodnic;
+ struct flexboot_nodnic_port *port;
+ struct net_device *netdev;
+ nodnic_port_state state = 0;
+ mlx_status status;
+
+ if ( ! ibdev ) {
+ DBG ( "%s: ibdev = NULL!!!\n", __FUNCTION__ );
+ return;
+ }
+
+ flexboot_nodnic = ib_get_drvdata ( ibdev );
+ port = &flexboot_nodnic->port[ibdev->port - 1];
+ netdev = port->netdev;
+
+ if ( ! netdev_is_open ( netdev ) ) {
+ DBG2( "%s: port %d is closed\n", __FUNCTION__, port->ibdev->port );
+ return;
+ }
+
+ /* we don't poll EQ. Just poll link status if it's not active */
+ if ( ! netdev_link_ok ( netdev ) ) {
+ status = nodnic_port_get_state ( &port->port_priv, &state );
+ MLX_FATAL_CHECK_STATUS(status, state_err, "nodnic_port_get_state failed");
+
+ if ( state == nodnic_port_state_active ) {
+ DBG( "%s: port %d physical link is up\n", __FUNCTION__,
+ port->ibdev->port );
+ port->type->state_change ( flexboot_nodnic, port, 1 );
+ }
+ }
+state_err:
+ return;
+}
+
+/***************************************************************************
+ *
+ * Multicast group operations
+ *
+ ***************************************************************************
+ */
+static int flexboot_nodnic_mcast_attach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ union ib_gid *gid) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ mlx_mac_address mac;
+ mlx_status status = MLX_SUCCESS;
+
+ switch (qp->type) {
+ case IB_QPT_ETH:
+ memcpy(&mac, &gid, sizeof(mac));
+ status = nodnic_port_add_mac_filter(&port->port_priv, mac);
+ MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err,
+ "nodnic_port_add_mac_filter failed");
+ break;
+ default:
+ break;
+ }
+mac_err:
+ return status;
+}
+static void flexboot_nodnic_mcast_detach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ union ib_gid *gid ) {
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1];
+ mlx_mac_address mac;
+ mlx_status status = MLX_SUCCESS;
+
+ switch (qp->type) {
+ case IB_QPT_ETH:
+ memcpy(&mac, &gid, sizeof(mac));
+ status = nodnic_port_remove_mac_filter(&port->port_priv, mac);
+ MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err,
+ "nodnic_port_remove_mac_filter failed");
+ break;
+ default:
+ break;
+ }
+mac_err:
+ return;
+}
+/***************************************************************************
+ *
+ * Infiniband link-layer operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Initialise Infiniband link
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_ib_open ( struct ib_device *ibdev __unused) {
+ int rc = 0;
+
+ /*TODO: add implementation*/
+ return rc;
+}
+
+/**
+ * Close Infiniband link
+ *
+ * @v ibdev Infiniband device
+ */
+static void flexboot_nodnic_ib_close ( struct ib_device *ibdev __unused) {
+ /*TODO: add implementation*/
+}
+
+/**
+ * Inform embedded subnet management agent of a received MAD
+ *
+ * @v ibdev Infiniband device
+ * @v mad MAD
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_inform_sma ( struct ib_device *ibdev __unused,
+ union ib_mad *mad __unused) {
+ /*TODO: add implementation*/
+ return 0;
+}
+
+/** flexboot_nodnic Infiniband operations */
+static struct ib_device_operations flexboot_nodnic_ib_operations = {
+ .create_cq = flexboot_nodnic_create_cq,
+ .destroy_cq = flexboot_nodnic_destroy_cq,
+ .create_qp = flexboot_nodnic_create_qp,
+ .modify_qp = flexboot_nodnic_modify_qp,
+ .destroy_qp = flexboot_nodnic_destroy_qp,
+ .post_send = flexboot_nodnic_post_send,
+ .post_recv = flexboot_nodnic_post_recv,
+ .poll_cq = flexboot_nodnic_poll_cq,
+ .poll_eq = flexboot_nodnic_poll_eq,
+ .open = flexboot_nodnic_ib_open,
+ .close = flexboot_nodnic_ib_close,
+ .mcast_attach = flexboot_nodnic_mcast_attach,
+ .mcast_detach = flexboot_nodnic_mcast_detach,
+ .set_port_info = flexboot_nodnic_inform_sma,
+ .set_pkey_table = flexboot_nodnic_inform_sma,
+};
+/***************************************************************************
+ *
+ *
+ *
+ ***************************************************************************
+ */
+
+#define FLEX_NODNIC_TX_POLL_TOUT 500000
+#define FLEX_NODNIC_TX_POLL_USLEEP 10
+
+static void flexboot_nodnic_complete_all_tx ( struct flexboot_nodnic_port *port ) {
+ struct ib_device *ibdev = port->ibdev;
+ struct ib_completion_queue *cq;
+ struct ib_work_queue *wq;
+ int keep_polling = 0;
+ int timeout = FLEX_NODNIC_TX_POLL_TOUT;
+
+ list_for_each_entry ( cq, &ibdev->cqs, list ) {
+ do {
+ ib_poll_cq ( ibdev, cq );
+ keep_polling = 0;
+ list_for_each_entry ( wq, &cq->work_queues, list ) {
+ if ( wq->is_send )
+ keep_polling += ( wq->fill > 0 );
+ }
+ udelay ( FLEX_NODNIC_TX_POLL_USLEEP );
+ } while ( keep_polling && ( timeout-- > 0 ) );
+ }
+}
+
+static void flexboot_nodnic_port_disable_dma ( struct flexboot_nodnic_port *port ) {
+ nodnic_port_priv *port_priv = & ( port->port_priv );
+ mlx_status status;
+
+ if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) )
+ return;
+
+ port_priv->port_state |= NODNIC_PORT_DISABLING_DMA;
+ flexboot_nodnic_complete_all_tx ( port );
+ if ( ( status = nodnic_port_disable_dma ( port_priv ) ) ) {
+ MLX_DEBUG_WARN ( port, "Failed to disable DMA %d\n", status );
+ }
+
+ port_priv->port_state &= ~NODNIC_PORT_DISABLING_DMA;
+}
+
+/***************************************************************************
+ *
+ * Ethernet operation
+ *
+ ***************************************************************************
+ */
+
+/** Number of flexboot_nodnic Ethernet send work queue entries */
+#define FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES 64
+
+/** Number of flexboot_nodnic Ethernet receive work queue entries */
+#define FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES 64
+/** flexboot nodnic Ethernet queue pair operations */
+static struct ib_queue_pair_operations flexboot_nodnic_eth_qp_op = {
+ .alloc_iob = alloc_iob,
+};
+
+/**
+ * Transmit packet via flexboot_nodnic Ethernet device
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_eth_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf) {
+ struct flexboot_nodnic_port *port = netdev->priv;
+ struct ib_device *ibdev = port->ibdev;
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ int rc;
+
+ rc = ib_post_send ( ibdev, port->eth_qp, NULL, iobuf);
+ /* Transmit packet */
+ if ( rc != 0) {
+ DBGC ( flexboot_nodnic, "NODNIC %p port %d could not transmit: %s\n",
+ flexboot_nodnic, ibdev->port, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle flexboot_nodnic Ethernet device send completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void flexboot_nodnic_eth_complete_send ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct io_buffer *iobuf,
+ int rc) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/**
+ * Handle flexboot_nodnic Ethernet device receive completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector, or NULL
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *dest __unused,
+ struct ib_address_vector *source,
+ struct io_buffer *iobuf,
+ int rc) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+
+ if ( rc != 0 ) {
+ DBG ( "Received packet with error\n" );
+ netdev_rx_err ( netdev, iobuf, rc );
+ return;
+ }
+
+ if ( source == NULL ) {
+ DBG ( "Received packet without address vector\n" );
+ netdev_rx_err ( netdev, iobuf, -ENOTTY );
+ return;
+ }
+ netdev_rx ( netdev, iobuf );
+}
+
+/** flexboot_nodnic Ethernet device completion operations */
+static struct ib_completion_queue_operations flexboot_nodnic_eth_cq_op = {
+ .complete_send = flexboot_nodnic_eth_complete_send,
+ .complete_recv = flexboot_nodnic_eth_complete_recv,
+};
+
+/**
+ * Poll flexboot_nodnic Ethernet device
+ *
+ * @v netdev Network device
+ */
+static void flexboot_nodnic_eth_poll ( struct net_device *netdev) {
+ struct flexboot_nodnic_port *port = netdev->priv;
+ struct ib_device *ibdev = port->ibdev;
+
+ ib_poll_eq ( ibdev );
+}
+
+/**
+ * Open flexboot_nodnic Ethernet device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_eth_open ( struct net_device *netdev ) {
+ struct flexboot_nodnic_port *port = netdev->priv;
+ struct ib_device *ibdev = port->ibdev;
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ mlx_status status = MLX_SUCCESS;
+ struct ib_completion_queue *dummy_cq = NULL;
+ struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = NULL;
+ mlx_uint64 cq_size = 0;
+ mlx_uint32 qpn = 0;
+ nodnic_port_state state = nodnic_port_state_down;
+
+ if ( port->port_priv.port_state & NODNIC_PORT_OPENED ) {
+ DBGC ( flexboot_nodnic, "%s: port %d is already opened\n",
+ __FUNCTION__, port->ibdev->port );
+ return 0;
+ }
+
+ port->port_priv.port_state |= NODNIC_PORT_OPENED;
+
+ dummy_cq = zalloc ( sizeof ( struct ib_completion_queue ) );
+ if ( dummy_cq == NULL ) {
+ DBGC ( flexboot_nodnic, "%s: Failed to allocate dummy CQ\n", __FUNCTION__ );
+ status = MLX_OUT_OF_RESOURCES;
+ goto err_create_dummy_cq;
+ }
+ INIT_LIST_HEAD ( &dummy_cq->work_queues );
+
+ port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH,
+ FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq,
+ FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq,
+ &flexboot_nodnic_eth_qp_op, netdev->name );
+ if ( !port->eth_qp ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create queue pair\n",
+ flexboot_nodnic, ibdev->port );
+ status = MLX_OUT_OF_RESOURCES;
+ goto err_create_qp;
+ }
+
+ ib_qp_set_ownerdata ( port->eth_qp, netdev );
+
+ status = nodnic_port_get_cq_size(&port->port_priv, &cq_size);
+ MLX_FATAL_CHECK_STATUS(status, get_cq_size_err,
+ "nodnic_port_get_cq_size failed");
+
+ port->eth_cq = ib_create_cq ( ibdev, cq_size,
+ &flexboot_nodnic_eth_cq_op );
+ if ( !port->eth_cq ) {
+ DBGC ( flexboot_nodnic,
+ "flexboot_nodnic %p port %d could not create completion queue\n",
+ flexboot_nodnic, ibdev->port );
+ status = MLX_OUT_OF_RESOURCES;
+ goto err_create_cq;
+ }
+ port->eth_qp->send.cq = port->eth_cq;
+ list_del(&port->eth_qp->send.list);
+ list_add ( &port->eth_qp->send.list, &port->eth_cq->work_queues );
+ port->eth_qp->recv.cq = port->eth_cq;
+ list_del(&port->eth_qp->recv.list);
+ list_add ( &port->eth_qp->recv.list, &port->eth_cq->work_queues );
+
+ status = nodnic_port_allocate_eq(&port->port_priv,
+ flexboot_nodnic->device_priv.device_cap.log_working_buffer_size);
+ MLX_FATAL_CHECK_STATUS(status, eq_alloc_err,
+ "nodnic_port_allocate_eq failed");
+
+ status = nodnic_port_init(&port->port_priv);
+ MLX_FATAL_CHECK_STATUS(status, init_err,
+ "nodnic_port_init failed");
+
+ /* update qp - qpn */
+ flexboot_nodnic_qp = ib_qp_get_drvdata ( port->eth_qp );
+ status = nodnic_port_get_qpn(&port->port_priv,
+ &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring,
+ &qpn);
+ MLX_FATAL_CHECK_STATUS(status, qpn_err,
+ "nodnic_port_get_qpn failed");
+ port->eth_qp->qpn = qpn;
+
+ /* Fill receive rings */
+ ib_refill_recv ( ibdev, port->eth_qp );
+
+ status = nodnic_port_enable_dma(&port->port_priv);
+ MLX_FATAL_CHECK_STATUS(status, dma_err,
+ "nodnic_port_enable_dma failed");
+
+ if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) {
+ status = nodnic_port_set_promisc(&port->port_priv, TRUE);
+ MLX_FATAL_CHECK_STATUS(status, promisc_err,
+ "nodnic_port_set_promisc failed");
+ }
+
+ status = nodnic_port_get_state(&port->port_priv, &state);
+ MLX_FATAL_CHECK_STATUS(status, state_err,
+ "nodnic_port_get_state failed");
+
+ port->type->state_change (
+ flexboot_nodnic, port, state == nodnic_port_state_active );
+
+ DBGC ( flexboot_nodnic, "%s: port %d opened (link is %s)\n",
+ __FUNCTION__, port->ibdev->port,
+ ( ( state == nodnic_port_state_active ) ? "Up" : "Down" ) );
+
+ free(dummy_cq);
+ return 0;
+state_err:
+promisc_err:
+dma_err:
+qpn_err:
+ nodnic_port_close(&port->port_priv);
+init_err:
+ nodnic_port_free_eq(&port->port_priv);
+eq_alloc_err:
+err_create_cq:
+get_cq_size_err:
+ ib_destroy_qp(ibdev, port->eth_qp );
+err_create_qp:
+ free(dummy_cq);
+err_create_dummy_cq:
+ port->port_priv.port_state &= ~NODNIC_PORT_OPENED;
+ return status;
+}
+
+/**
+ * Close flexboot_nodnic Ethernet device
+ *
+ * @v netdev Network device
+ */
+static void flexboot_nodnic_eth_close ( struct net_device *netdev) {
+ struct flexboot_nodnic_port *port = netdev->priv;
+ struct ib_device *ibdev = port->ibdev;
+ struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev );
+ mlx_status status = MLX_SUCCESS;
+
+ if ( ! ( port->port_priv.port_state & NODNIC_PORT_OPENED ) ) {
+ DBGC ( flexboot_nodnic, "%s: port %d is already closed\n",
+ __FUNCTION__, port->ibdev->port );
+ return;
+ }
+
+ if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) {
+ if ( ( status = nodnic_port_set_promisc( &port->port_priv, FALSE ) ) ) {
+ DBGC ( flexboot_nodnic,
+ "nodnic_port_set_promisc failed (status = %d)\n", status );
+ }
+ }
+
+ flexboot_nodnic_port_disable_dma ( port );
+
+ port->port_priv.port_state &= ~NODNIC_PORT_OPENED;
+
+ port->type->state_change ( flexboot_nodnic, port, FALSE );
+
+ /* Close port */
+ status = nodnic_port_close(&port->port_priv);
+ if ( status != MLX_SUCCESS ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not close port: %s\n",
+ flexboot_nodnic, ibdev->port, strerror ( status ) );
+ /* Nothing we can do about this */
+ }
+
+ ib_destroy_qp ( ibdev, port->eth_qp );
+ port->eth_qp = NULL;
+ ib_destroy_cq ( ibdev, port->eth_cq );
+ port->eth_cq = NULL;
+
+ nodnic_port_free_eq(&port->port_priv);
+
+ DBGC ( flexboot_nodnic, "%s: port %d closed\n", __FUNCTION__, port->ibdev->port );
+}
+
+void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ) {
+ struct flexboot_nodnic_port *port = netdev->priv;
+
+ if ( enable ) {
+ if ( ( port->port_priv.port_state & NODNIC_PORT_OPENED ) &&
+ ! ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) {
+ flexboot_nodnic_arm_cq ( port );
+ } else {
+ /* do nothing */
+ }
+ } else {
+ nodnic_device_clear_int( port->port_priv.device );
+ }
+}
+
+/** flexboot_nodnic Ethernet network device operations */
+static struct net_device_operations flexboot_nodnic_eth_operations = {
+ .open = flexboot_nodnic_eth_open,
+ .close = flexboot_nodnic_eth_close,
+ .transmit = flexboot_nodnic_eth_transmit,
+ .poll = flexboot_nodnic_eth_poll,
+};
+
+/**
+ * Register flexboot_nodnic Ethernet device
+ */
+static int flexboot_nodnic_register_netdev ( struct flexboot_nodnic *flexboot_nodnic,
+ struct flexboot_nodnic_port *port) {
+ mlx_status status = MLX_SUCCESS;
+ struct net_device *netdev;
+ struct ib_device *ibdev = port->ibdev;
+ union {
+ uint8_t bytes[8];
+ uint32_t dwords[2];
+ } mac;
+
+ /* Allocate network devices */
+ netdev = alloc_etherdev ( 0 );
+ if ( netdev == NULL ) {
+ DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not allocate net device\n",
+ flexboot_nodnic, ibdev->port );
+ status = MLX_OUT_OF_RESOURCES;
+ goto alloc_err;
+ }
+ port->netdev = netdev;
+ netdev_init ( netdev, &flexboot_nodnic_eth_operations );
+ netdev->dev = ibdev->dev;
+ netdev->priv = port;
+
+ status = nodnic_port_query(&port->port_priv,
+ nodnic_port_option_mac_high,
+ &mac.dwords[0]);
+ MLX_FATAL_CHECK_STATUS(status, mac_err,
+ "failed to query mac high");
+ status = nodnic_port_query(&port->port_priv,
+ nodnic_port_option_mac_low,
+ &mac.dwords[1]);
+ MLX_FATAL_CHECK_STATUS(status, mac_err,
+ "failed to query mac low");
+ mac.dwords[0] = htonl(mac.dwords[0]);
+ mac.dwords[1] = htonl(mac.dwords[1]);
+ memcpy ( netdev->hw_addr,
+ &mac.bytes[2], ETH_ALEN);
+ /* Register network device */
+ status = register_netdev ( netdev );
+ if ( status != MLX_SUCCESS ) {
+ DBGC ( flexboot_nodnic,
+ "flexboot_nodnic %p port %d could not register network device: %s\n",
+ flexboot_nodnic, ibdev->port, strerror ( status ) );
+ goto reg_err;
+ }
+ return status;
+reg_err:
+mac_err:
+ netdev_put ( netdev );
+alloc_err:
+ return status;
+}
+
+/**
+ * Handle flexboot_nodnic Ethernet device port state change
+ */
+static void flexboot_nodnic_state_change_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused,
+ struct flexboot_nodnic_port *port,
+ int link_up ) {
+ struct net_device *netdev = port->netdev;
+
+ if ( link_up )
+ netdev_link_up ( netdev );
+ else
+ netdev_link_down ( netdev );
+
+}
+
+/**
+ * Unregister flexboot_nodnic Ethernet device
+ */
+static void flexboot_nodnic_unregister_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused,
+ struct flexboot_nodnic_port *port ) {
+ struct net_device *netdev = port->netdev;
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** flexboot_nodnic Ethernet port type */
+static struct flexboot_nodnic_port_type flexboot_nodnic_port_type_eth = {
+ .register_dev = flexboot_nodnic_register_netdev,
+ .state_change = flexboot_nodnic_state_change_netdev,
+ .unregister_dev = flexboot_nodnic_unregister_netdev,
+};
+
+/***************************************************************************
+ *
+ * PCI interface helper functions
+ *
+ ***************************************************************************
+ */
+static
+mlx_status
+flexboot_nodnic_allocate_infiniband_devices( struct flexboot_nodnic *flexboot_nodnic_priv ) {
+ mlx_status status = MLX_SUCCESS;
+ nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv;
+ struct pci_device *pci = flexboot_nodnic_priv->pci;
+ struct ib_device *ibdev = NULL;
+ unsigned int i = 0;
+
+ /* Allocate Infiniband devices */
+ for (; i < device_priv->device_cap.num_ports; i++) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ ibdev = alloc_ibdev(0);
+ if (ibdev == NULL) {
+ status = MLX_OUT_OF_RESOURCES;
+ goto err_alloc_ibdev;
+ }
+ flexboot_nodnic_priv->port[i].ibdev = ibdev;
+ ibdev->op = &flexboot_nodnic_ib_operations;
+ ibdev->dev = &pci->dev;
+ ibdev->port = ( FLEXBOOT_NODNIC_PORT_BASE + i);
+ ib_set_drvdata(ibdev, flexboot_nodnic_priv);
+ }
+ return status;
+err_alloc_ibdev:
+ for ( i-- ; ( signed int ) i >= 0 ; i-- )
+ ibdev_put ( flexboot_nodnic_priv->port[i].ibdev );
+ return status;
+}
+
+static
+mlx_status
+flexboot_nodnic_thin_init_ports( struct flexboot_nodnic *flexboot_nodnic_priv ) {
+ mlx_status status = MLX_SUCCESS;
+ nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv;
+ nodnic_port_priv *port_priv = NULL;
+ unsigned int i = 0;
+
+ for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ port_priv = &flexboot_nodnic_priv->port[i].port_priv;
+ status = nodnic_port_thin_init( device_priv, port_priv, i );
+ MLX_FATAL_CHECK_STATUS(status, thin_init_err,
+ "flexboot_nodnic_thin_init_ports failed");
+ }
+thin_init_err:
+ return status;
+}
+
+
+static
+mlx_status
+flexboot_nodnic_set_ports_type ( struct flexboot_nodnic *flexboot_nodnic_priv ) {
+ mlx_status status = MLX_SUCCESS;
+ nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv;
+ nodnic_port_priv *port_priv = NULL;
+ nodnic_port_type type = NODNIC_PORT_TYPE_UNKNOWN;
+ unsigned int i = 0;
+
+ for ( i = 0 ; i < device_priv->device_cap.num_ports ; i++ ) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ port_priv = &flexboot_nodnic_priv->port[i].port_priv;
+ status = nodnic_port_get_type(port_priv, &type);
+ MLX_FATAL_CHECK_STATUS(status, type_err,
+ "nodnic_port_get_type failed");
+ switch ( type ) {
+ case NODNIC_PORT_TYPE_ETH:
+ DBGC ( flexboot_nodnic_priv, "Port %d type is Ethernet\n", i );
+ flexboot_nodnic_priv->port[i].type = &flexboot_nodnic_port_type_eth;
+ break;
+ case NODNIC_PORT_TYPE_IB:
+ DBGC ( flexboot_nodnic_priv, "Port %d type is Infiniband\n", i );
+ status = MLX_UNSUPPORTED;
+ goto type_err;
+ default:
+ DBGC ( flexboot_nodnic_priv, "Port %d type is unknown\n", i );
+ status = MLX_UNSUPPORTED;
+ goto type_err;
+ }
+ }
+type_err:
+ return status;
+}
+
+static
+mlx_status
+flexboot_nodnic_ports_register_dev( struct flexboot_nodnic *flexboot_nodnic_priv ) {
+ mlx_status status = MLX_SUCCESS;
+ nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv;
+ struct flexboot_nodnic_port *port = NULL;
+ unsigned int i = 0;
+
+ for (; i < device_priv->device_cap.num_ports; i++) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ port = &flexboot_nodnic_priv->port[i];
+ status = port->type->register_dev ( flexboot_nodnic_priv, port );
+ MLX_FATAL_CHECK_STATUS(status, reg_err,
+ "port register_dev failed");
+ }
+reg_err:
+ return status;
+}
+
+static
+mlx_status
+flexboot_nodnic_ports_unregister_dev ( struct flexboot_nodnic *flexboot_nodnic_priv ) {
+ struct flexboot_nodnic_port *port;
+ nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv;
+ int i = (device_priv->device_cap.num_ports - 1);
+
+ for (; i >= 0; i--) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ port = &flexboot_nodnic_priv->port[i];
+ port->type->unregister_dev(flexboot_nodnic_priv, port);
+ ibdev_put(flexboot_nodnic_priv->port[i].ibdev);
+ }
+ return MLX_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * flexboot nodnic interface
+ *
+ ***************************************************************************
+ */
+__unused static void flexboot_nodnic_enable_dma ( struct flexboot_nodnic *nodnic ) {
+ nodnic_port_priv *port_priv;
+ mlx_status status;
+ int i;
+
+ for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) {
+ if ( ! ( nodnic->port_mask & ( i + 1 ) ) )
+ continue;
+ port_priv = & ( nodnic->port[i].port_priv );
+ if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) )
+ continue;
+
+ if ( ( status = nodnic_port_enable_dma ( port_priv ) ) ) {
+ MLX_DEBUG_WARN ( nodnic, "Failed to enable DMA %d\n", status );
+ }
+ }
+}
+
+__unused static void flexboot_nodnic_disable_dma ( struct flexboot_nodnic *nodnic ) {
+ int i;
+
+ for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) {
+ if ( ! ( nodnic->port_mask & ( i + 1 ) ) )
+ continue;
+ flexboot_nodnic_port_disable_dma ( & ( nodnic->port[i] ) );
+ }
+}
+
+int flexboot_nodnic_is_supported ( struct pci_device *pci ) {
+ mlx_utils utils;
+ mlx_pci_gw_buffer buffer;
+ mlx_status status;
+ int is_supported = 0;
+
+ DBG ( "%s: start\n", __FUNCTION__ );
+
+ memset ( &utils, 0, sizeof ( utils ) );
+
+ status = mlx_utils_init ( &utils, pci );
+ MLX_CHECK_STATUS ( pci, status, utils_init_err, "mlx_utils_init failed" );
+
+ status = mlx_pci_gw_init ( &utils );
+ MLX_CHECK_STATUS ( pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" );
+
+ status = mlx_pci_gw_read ( &utils, PCI_GW_SPACE_NODNIC,
+ NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, &buffer );
+
+ if ( status == MLX_SUCCESS ) {
+ buffer >>= NODNIC_NIC_INTERFACE_SUPPORTED_BIT;
+ is_supported = ( buffer & 0x1 );
+ }
+
+ mlx_pci_gw_teardown( &utils );
+
+pci_gw_init_err:
+utils_init_err:
+ DBG ( "%s: NODNIC is %s supported (status = %d)\n",
+ __FUNCTION__, ( is_supported ? "": "not" ), status );
+ return is_supported;
+}
+
+void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte,
+ uint16_t high_byte ) {
+ union mac_addr {
+ struct {
+ uint32_t low_byte;
+ uint16_t high_byte;
+ };
+ uint8_t mac_addr[ETH_ALEN];
+ } mac_addr_aux;
+
+ mac_addr_aux.high_byte = high_byte;
+ mac_addr_aux.low_byte = low_byte;
+
+ mac_addr[0] = mac_addr_aux.mac_addr[5];
+ mac_addr[1] = mac_addr_aux.mac_addr[4];
+ mac_addr[2] = mac_addr_aux.mac_addr[3];
+ mac_addr[3] = mac_addr_aux.mac_addr[2];
+ mac_addr[4] = mac_addr_aux.mac_addr[1];
+ mac_addr[5] = mac_addr_aux.mac_addr[0];
+}
+
+static mlx_status flexboot_nodnic_get_factory_mac (
+ struct flexboot_nodnic *flexboot_nodnic_priv, uint8_t port __unused ) {
+ struct mlx_vmac_query_virt_mac virt_mac;
+ mlx_status status;
+
+ memset ( & virt_mac, 0, sizeof ( virt_mac ) );
+ status = mlx_vmac_query_virt_mac ( flexboot_nodnic_priv->device_priv.utils,
+ &virt_mac );
+ if ( ! status ) {
+ DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n",
+ flexboot_nodnic_priv );
+ }
+
+ return status;
+}
+
+/**
+ * Set port masking
+ *
+ * @v flexboot_nodnic nodnic device
+ * @ret rc Return status code
+ */
+static int flexboot_nodnic_set_port_masking ( struct flexboot_nodnic *flexboot_nodnic ) {
+ unsigned int i;
+ nodnic_device_priv *device_priv = &flexboot_nodnic->device_priv;
+
+ flexboot_nodnic->port_mask = 0;
+ for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) {
+ flexboot_nodnic->port_mask |= (i + 1);
+ }
+
+ if ( ! flexboot_nodnic->port_mask ) {
+ /* No port was enabled */
+ DBGC ( flexboot_nodnic, "NODNIC %p No port was enabled for "
+ "booting\n", flexboot_nodnic );
+ return -ENETUNREACH;
+ }
+
+ return 0;
+}
+
+int flexboot_nodnic_probe ( struct pci_device *pci,
+ struct flexboot_nodnic_callbacks *callbacks,
+ void *drv_priv __unused ) {
+ mlx_status status = MLX_SUCCESS;
+ struct flexboot_nodnic *flexboot_nodnic_priv = NULL;
+ nodnic_device_priv *device_priv = NULL;
+ int i = 0;
+
+ if ( ( pci == NULL ) || ( callbacks == NULL ) ) {
+ DBGC ( flexboot_nodnic_priv, "%s: Bad Parameter\n", __FUNCTION__ );
+ return -EINVAL;
+ }
+
+ flexboot_nodnic_priv = zalloc( sizeof ( *flexboot_nodnic_priv ) );
+ if ( flexboot_nodnic_priv == NULL ) {
+ DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate priv data\n", __FUNCTION__ );
+ status = MLX_OUT_OF_RESOURCES;
+ goto device_err_alloc;
+ }
+
+ /* Register settings
+ * Note that pci->priv will be the device private data */
+ flexboot_nodnic_priv->pci = pci;
+ flexboot_nodnic_priv->callbacks = callbacks;
+ pci_set_drvdata ( pci, flexboot_nodnic_priv );
+
+ device_priv = &flexboot_nodnic_priv->device_priv;
+ device_priv->utils = (mlx_utils *)zalloc( sizeof ( mlx_utils ) );
+ if ( device_priv->utils == NULL ) {
+ DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate utils\n", __FUNCTION__ );
+ status = MLX_OUT_OF_RESOURCES;
+ goto utils_err_alloc;
+ }
+
+ status = mlx_utils_init( device_priv->utils, pci );
+ MLX_FATAL_CHECK_STATUS(status, utils_init_err,
+ "mlx_utils_init failed");
+
+ /* nodnic init*/
+ status = mlx_pci_gw_init( device_priv->utils );
+ MLX_FATAL_CHECK_STATUS(status, cmd_init_err,
+ "mlx_pci_gw_init failed");
+
+ /* init device */
+ status = nodnic_device_init( device_priv );
+ MLX_FATAL_CHECK_STATUS(status, device_init_err,
+ "nodnic_device_init failed");
+
+ status = nodnic_device_get_cap( device_priv );
+ MLX_FATAL_CHECK_STATUS(status, get_cap_err,
+ "nodnic_device_get_cap failed");
+
+ status = flexboot_nodnic_set_port_masking ( flexboot_nodnic_priv );
+ MLX_FATAL_CHECK_STATUS(status, err_set_masking,
+ "flexboot_nodnic_set_port_masking failed");
+
+ status = flexboot_nodnic_allocate_infiniband_devices( flexboot_nodnic_priv );
+ MLX_FATAL_CHECK_STATUS(status, err_alloc_ibdev,
+ "flexboot_nodnic_allocate_infiniband_devices failed");
+
+ /* port init */
+ status = flexboot_nodnic_thin_init_ports( flexboot_nodnic_priv );
+ MLX_FATAL_CHECK_STATUS(status, err_thin_init_ports,
+ "flexboot_nodnic_thin_init_ports failed");
+
+ /* device reg */
+ status = flexboot_nodnic_set_ports_type( flexboot_nodnic_priv );
+ MLX_CHECK_STATUS( flexboot_nodnic_priv, status, err_set_ports_types,
+ "flexboot_nodnic_set_ports_type failed");
+
+ status = flexboot_nodnic_ports_register_dev( flexboot_nodnic_priv );
+ MLX_FATAL_CHECK_STATUS(status, reg_err,
+ "flexboot_nodnic_ports_register_dev failed");
+
+ for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) {
+ if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) )
+ continue;
+ flexboot_nodnic_get_factory_mac ( flexboot_nodnic_priv, i );
+ }
+
+ /* Update ETH operations with IRQ function if supported */
+ DBGC ( flexboot_nodnic_priv, "%s: %s IRQ function\n",
+ __FUNCTION__, ( callbacks->irq ? "Valid" : "No" ) );
+ flexboot_nodnic_eth_operations.irq = callbacks->irq;
+ return 0;
+
+ flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv );
+reg_err:
+err_set_ports_types:
+err_thin_init_ports:
+err_alloc_ibdev:
+err_set_masking:
+get_cap_err:
+ nodnic_device_teardown ( device_priv );
+device_init_err:
+ mlx_pci_gw_teardown ( device_priv->utils );
+cmd_init_err:
+utils_init_err:
+ free ( device_priv->utils );
+utils_err_alloc:
+ free ( flexboot_nodnic_priv );
+device_err_alloc:
+ return status;
+}
+
+void flexboot_nodnic_remove ( struct pci_device *pci )
+{
+ struct flexboot_nodnic *flexboot_nodnic_priv = pci_get_drvdata ( pci );
+ nodnic_device_priv *device_priv = & ( flexboot_nodnic_priv->device_priv );
+
+ flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv );
+ nodnic_device_teardown( device_priv );
+ mlx_pci_gw_teardown( device_priv->utils );
+ free( device_priv->utils );
+ free( flexboot_nodnic_priv );
+}