]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[hermon] Reorder code in preparation for quiescing patch
authorMichael Brown <mcb30@ipxe.org>
Mon, 14 Nov 2011 19:11:47 +0000 (19:11 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 15 Nov 2011 03:59:10 +0000 (03:59 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/infiniband/hermon.c

index 7a487c91c0ced3097f617cac83fc43f174fd6350..fd091a6065251872c9c0397ca6498064894ec1b8 100644 (file)
@@ -2040,1284 +2040,1379 @@ static void hermon_poll_eq ( struct ib_device *ibdev ) {
 
 /***************************************************************************
  *
- * Infiniband link-layer operations
+ * Firmware control
  *
  ***************************************************************************
  */
 
 /**
- * Initialise Infiniband link
+ * Map virtual to physical address for firmware usage
  *
- * @v ibdev            Infiniband device
+ * @v hermon           Hermon device
+ * @v map              Mapping function
+ * @v va               Virtual address
+ * @v pa               Physical address
+ * @v len              Length of region
  * @ret rc             Return status code
  */
-static int hermon_open ( struct ib_device *ibdev ) {
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
-       union hermonprm_set_port set_port;
+static int hermon_map_vpm ( struct hermon *hermon,
+                           int ( *map ) ( struct hermon *hermon,
+                           const struct hermonprm_virtual_physical_mapping* ),
+                           uint64_t va, physaddr_t pa, size_t len ) {
+       struct hermonprm_virtual_physical_mapping mapping;
+       physaddr_t start;
+       physaddr_t low;
+       physaddr_t high;
+       physaddr_t end;
+       size_t size;
        int rc;
 
-       /* Set port parameters */
-       memset ( &set_port, 0, sizeof ( set_port ) );
-       MLX_FILL_8 ( &set_port.ib, 0,
-                    mmc, 1,
-                    mvc, 1,
-                    mp, 1,
-                    mg, 1,
-                    mtu_cap, IB_MTU_2048,
-                    vl_cap, IB_VL_0,
-                    rcm, 1,
-                    lss, 1 );
-       MLX_FILL_2 ( &set_port.ib, 10,
-                    max_pkey, 1,
-                    max_gid, 1 );
-       MLX_FILL_1 ( &set_port.ib, 28,
-                    link_speed_supported, 1 );
-       if ( ( rc = hermon_cmd_set_port ( hermon, 0, ibdev->port,
-                                         &set_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not set port: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
-               return rc;
-       }
+       /* Sanity checks */
+       assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
+       assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
+       assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
 
-       /* Initialise port */
-       if ( ( rc = hermon_cmd_init_port ( hermon, ibdev->port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not initialise port: "
-                      "%s\n", hermon, ibdev->port, strerror ( rc ) );
-               return rc;
-       }
+       /* Calculate starting points */
+       start = pa;
+       end = ( start + len );
+       size = ( 1UL << ( fls ( start ^ end ) - 1 ) );
+       low = high = ( end & ~( size - 1 ) );
+       assert ( start < low );
+       assert ( high <= end );
 
-       /* Update MAD parameters */
-       ib_smc_update ( ibdev, hermon_mad );
+       /* These mappings tend to generate huge volumes of
+        * uninteresting debug data, which basically makes it
+        * impossible to use debugging otherwise.
+        */
+       DBG_DISABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
+
+       /* Map blocks in descending order of size */
+       while ( size >= HERMON_PAGE_SIZE ) {
+
+               /* Find the next candidate block */
+               if ( ( low - size ) >= start ) {
+                       low -= size;
+                       pa = low;
+               } else if ( ( high + size ) <= end ) {
+                       pa = high;
+                       high += size;
+               } else {
+                       size >>= 1;
+                       continue;
+               }
+               assert ( ( va & ( size - 1 ) ) == 0 );
+               assert ( ( pa & ( size - 1 ) ) == 0 );
+
+               /* Map this block */
+               memset ( &mapping, 0, sizeof ( mapping ) );
+               MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) );
+               MLX_FILL_1 ( &mapping, 1, va_l, ( va >> 12 ) );
+               MLX_FILL_H ( &mapping, 2, pa_h, pa );
+               MLX_FILL_2 ( &mapping, 3,
+                            log2size, ( ( fls ( size ) - 1 ) - 12 ),
+                            pa_l, ( pa >> 12 ) );
+               if ( ( rc = map ( hermon, &mapping ) ) != 0 ) {
+                       DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
+                       DBGC ( hermon, "Hermon %p could not map %08llx+%zx to "
+                              "%08lx: %s\n",
+                              hermon, va, size, pa, strerror ( rc ) );
+                       return rc;
+               }
+               va += size;
+       }
+       assert ( low == start );
+       assert ( high == end );
 
+       DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
        return 0;
 }
 
 /**
- * Close Infiniband link
+ * Start firmware running
  *
- * @v ibdev            Infiniband device
+ * @v hermon           Hermon device
+ * @ret rc             Return status code
  */
-static void hermon_close ( struct ib_device *ibdev ) {
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
+static int hermon_start_firmware ( struct hermon *hermon ) {
+       struct hermonprm_query_fw fw;
+       unsigned int fw_pages;
+       size_t fw_size;
+       physaddr_t fw_base;
        int rc;
 
-       if ( ( rc = hermon_cmd_close_port ( hermon, ibdev->port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not close port: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
-               /* Nothing we can do about this */
+       /* Get firmware parameters */
+       if ( ( rc = hermon_cmd_query_fw ( hermon, &fw ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not query firmware: %s\n",
+                      hermon, strerror ( rc ) );
+               goto err_query_fw;
        }
+       DBGC ( hermon, "Hermon %p firmware version %d.%d.%d\n", hermon,
+              MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ),
+              MLX_GET ( &fw, fw_rev_subminor ) );
+       fw_pages = MLX_GET ( &fw, fw_pages );
+       DBGC ( hermon, "Hermon %p requires %d pages (%d kB) for firmware\n",
+              hermon, fw_pages, ( fw_pages * 4 ) );
+
+       /* Allocate firmware pages and map firmware area */
+       fw_size = ( fw_pages * HERMON_PAGE_SIZE );
+       hermon->firmware_area = umalloc ( fw_size );
+       if ( ! hermon->firmware_area ) {
+               rc = -ENOMEM;
+               goto err_alloc_fa;
+       }
+       fw_base = user_to_phys ( hermon->firmware_area, 0 );
+       DBGC ( hermon, "Hermon %p firmware area at physical [%08lx,%08lx)\n",
+              hermon, fw_base, ( fw_base + fw_size ) );
+       if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_fa,
+                                    0, fw_base, fw_size ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not map firmware: %s\n",
+                      hermon, strerror ( rc ) );
+               goto err_map_fa;
+       }
+
+       /* Start firmware */
+       if ( ( rc = hermon_cmd_run_fw ( hermon ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not run firmware: %s\n",
+                      hermon, strerror ( rc ) );
+               goto err_run_fw;
+       }
+
+       DBGC ( hermon, "Hermon %p firmware started\n", hermon );
+       return 0;
+
+ err_run_fw:
+ err_map_fa:
+       hermon_cmd_unmap_fa ( hermon );
+       ufree ( hermon->firmware_area );
+       hermon->firmware_area = UNULL;
+ err_alloc_fa:
+ err_query_fw:
+       return rc;
 }
 
 /**
- * Inform embedded subnet management agent of a received MAD
+ * Stop firmware running
  *
- * @v ibdev            Infiniband device
- * @v mad              MAD
- * @ret rc             Return status code
+ * @v hermon           Hermon device
  */
-static int hermon_inform_sma ( struct ib_device *ibdev,
-                              union ib_mad *mad ) {
+static void hermon_stop_firmware ( struct hermon *hermon ) {
        int rc;
 
-       /* Send the MAD to the embedded SMA */
-       if ( ( rc = hermon_mad ( ibdev, mad ) ) != 0 )
-               return rc;
-
-       /* Update parameters held in software */
-       ib_smc_update ( ibdev, hermon_mad );
-
-       return 0;
+       if ( ( rc = hermon_cmd_unmap_fa ( hermon ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p FATAL could not stop firmware: %s\n",
+                      hermon, strerror ( rc ) );
+               /* Leak memory and return; at least we avoid corruption */
+               return;
+       }
+       ufree ( hermon->firmware_area );
+       hermon->firmware_area = UNULL;
 }
 
 /***************************************************************************
  *
- * Multicast group operations
+ * Infinihost Context Memory management
  *
  ***************************************************************************
  */
 
 /**
- * Attach to multicast group
+ * Get device limits
  *
- * @v ibdev            Infiniband device
- * @v qp               Queue pair
- * @v gid              Multicast GID
+ * @v hermon           Hermon device
  * @ret rc             Return status code
  */
-static int hermon_mcast_attach ( struct ib_device *ibdev,
-                                struct ib_queue_pair *qp,
-                                union ib_gid *gid ) {
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
-       struct hermonprm_mgm_hash hash;
-       struct hermonprm_mcg_entry mcg;
-       unsigned int index;
+static int hermon_get_cap ( struct hermon *hermon ) {
+       struct hermonprm_query_dev_cap dev_cap;
        int rc;
 
-       /* Generate hash table index */
-       if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
+       if ( ( rc = hermon_cmd_query_dev_cap ( hermon, &dev_cap ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not get device limits: %s\n",
                       hermon, strerror ( rc ) );
                return rc;
        }
-       index = MLX_GET ( &hash, hash );
 
-       /* Check for existing hash table entry */
-       if ( ( rc = hermon_cmd_read_mcg ( hermon, index, &mcg ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not read MCG %#x: %s\n",
-                      hermon, index, strerror ( rc ) );
-               return rc;
-       }
-       if ( MLX_GET ( &mcg, hdr.members_count ) != 0 ) {
-               /* FIXME: this implementation allows only a single QP
-                * per multicast group, and doesn't handle hash
-                * collisions.  Sufficient for IPoIB but may need to
-                * be extended in future.
-                */
-               DBGC ( hermon, "Hermon %p MGID index %#x already in use\n",
-                      hermon, index );
-               return -EBUSY;
+       hermon->cap.cmpt_entry_size = MLX_GET ( &dev_cap, c_mpt_entry_sz );
+       hermon->cap.reserved_qps =
+               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_qps ) );
+       hermon->cap.qpc_entry_size = MLX_GET ( &dev_cap, qpc_entry_sz );
+       hermon->cap.altc_entry_size = MLX_GET ( &dev_cap, altc_entry_sz );
+       hermon->cap.auxc_entry_size = MLX_GET ( &dev_cap, aux_entry_sz );
+       hermon->cap.reserved_srqs =
+               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_srqs ) );
+       hermon->cap.srqc_entry_size = MLX_GET ( &dev_cap, srq_entry_sz );
+       hermon->cap.reserved_cqs =
+               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_cqs ) );
+       hermon->cap.cqc_entry_size = MLX_GET ( &dev_cap, cqc_entry_sz );
+       hermon->cap.reserved_eqs = MLX_GET ( &dev_cap, num_rsvd_eqs );
+       if ( hermon->cap.reserved_eqs == 0 ) {
+               /* Backward compatibility */
+               hermon->cap.reserved_eqs =
+                       ( 1 << MLX_GET ( &dev_cap, log2_rsvd_eqs ) );
        }
-
-       /* Update hash table entry */
-       MLX_FILL_1 ( &mcg, 1, hdr.members_count, 1 );
-       MLX_FILL_1 ( &mcg, 8, qp[0].qpn, qp->qpn );
-       memcpy ( &mcg.u.dwords[4], gid, sizeof ( *gid ) );
-       if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
-                      hermon, index, strerror ( rc ) );
-               return rc;
+       hermon->cap.eqc_entry_size = MLX_GET ( &dev_cap, eqc_entry_sz );
+       hermon->cap.reserved_mtts =
+               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mtts ) );
+       hermon->cap.mtt_entry_size = MLX_GET ( &dev_cap, mtt_entry_sz );
+       hermon->cap.reserved_mrws =
+               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) );
+       hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz );
+       hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars );
+       hermon->cap.num_ports = MLX_GET ( &dev_cap, num_ports );
+       hermon->cap.dpdp = MLX_GET ( &dev_cap, dpdp );
+
+       /* Sanity check */
+       if ( hermon->cap.num_ports > HERMON_MAX_PORTS ) {
+               DBGC ( hermon, "Hermon %p has %d ports (only %d supported)\n",
+                      hermon, hermon->cap.num_ports, HERMON_MAX_PORTS );
+               hermon->cap.num_ports = HERMON_MAX_PORTS;
        }
 
        return 0;
 }
 
 /**
- * Detach from multicast group
+ * Align ICM table
  *
- * @v ibdev            Infiniband device
- * @v qp               Queue pair
- * @v gid              Multicast GID
+ * @v icm_offset       Current ICM offset
+ * @v len              ICM table length
+ * @ret icm_offset     ICM offset
  */
-static void hermon_mcast_detach ( struct ib_device *ibdev,
-                                 struct ib_queue_pair *qp __unused,
-                                 union ib_gid *gid ) {
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
-       struct hermonprm_mgm_hash hash;
-       struct hermonprm_mcg_entry mcg;
-       unsigned int index;
-       int rc;
-
-       /* Generate hash table index */
-       if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
-                      hermon, strerror ( rc ) );
-               return;
-       }
-       index = MLX_GET ( &hash, hash );
+static uint64_t icm_align ( uint64_t icm_offset, size_t len ) {
 
-       /* Clear hash table entry */
-       memset ( &mcg, 0, sizeof ( mcg ) );
-       if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
-                      hermon, index, strerror ( rc ) );
-               return;
-       }
+       /* Round up to a multiple of the table size */
+       assert ( len == ( 1UL << ( fls ( len ) - 1 ) ) );
+       return ( ( icm_offset + len - 1 ) & ~( ( ( uint64_t ) len ) - 1 ) );
 }
 
-/** Hermon Infiniband operations */
-static struct ib_device_operations hermon_ib_operations = {
-       .create_cq      = hermon_create_cq,
-       .destroy_cq     = hermon_destroy_cq,
-       .create_qp      = hermon_create_qp,
-       .modify_qp      = hermon_modify_qp,
-       .destroy_qp     = hermon_destroy_qp,
-       .post_send      = hermon_post_send,
-       .post_recv      = hermon_post_recv,
-       .poll_cq        = hermon_poll_cq,
-       .poll_eq        = hermon_poll_eq,
-       .open           = hermon_open,
-       .close          = hermon_close,
-       .mcast_attach   = hermon_mcast_attach,
-       .mcast_detach   = hermon_mcast_detach,
-       .set_port_info  = hermon_inform_sma,
-       .set_pkey_table = hermon_inform_sma,
-};
-
 /**
- * Register Hermon Infiniband device
+ * Allocate ICM
  *
  * @v hermon           Hermon device
- * @v port             Hermon port
+ * @v init_hca         INIT_HCA structure to fill in
  * @ret rc             Return status code
  */
-static int hermon_register_ibdev ( struct hermon *hermon,
-                                  struct hermon_port *port ) {
-       struct ib_device *ibdev = port->ibdev;
+static int hermon_alloc_icm ( struct hermon *hermon,
+                             struct hermonprm_init_hca *init_hca ) {
+       struct hermonprm_scalar_parameter icm_size;
+       struct hermonprm_scalar_parameter icm_aux_size;
+       uint64_t icm_offset = 0;
+       unsigned int log_num_qps, log_num_srqs, log_num_cqs, log_num_eqs;
+       unsigned int log_num_mtts, log_num_mpts, log_num_mcs;
+       size_t cmpt_max_len;
+       size_t icm_len, icm_aux_len;
+       size_t len;
+       physaddr_t icm_phys;
+       int i;
        int rc;
 
-       /* Initialise parameters using SMC */
-       ib_smc_init ( ibdev, hermon_mad );
-
-       /* Register Infiniband device */
-       if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not register IB "
-                      "device: %s\n", hermon, ibdev->port, strerror ( rc ) );
-               return rc;
-       }
-
-       return 0;
-}
-
-/**
- * Handle Hermon Infiniband device port state change
- *
- * @v hermon           Hermon device
- * @v port             Hermon port
- * @v link_up          Link is up
- */
-static void hermon_state_change_ibdev ( struct hermon *hermon __unused,
-                                       struct hermon_port *port,
-                                       int link_up __unused ) {
-       struct ib_device *ibdev = port->ibdev;
+       /*
+        * Start by carving up the ICM virtual address space
+        *
+        */
 
-       /* Update MAD parameters */
-       ib_smc_update ( ibdev, hermon_mad );
-}
+       /* Calculate number of each object type within ICM */
+       log_num_qps = fls ( hermon->cap.reserved_qps +
+                           HERMON_RSVD_SPECIAL_QPS + HERMON_MAX_QPS - 1 );
+       log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 );
+       log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 );
+       log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 );
+       log_num_mtts = fls ( hermon->cap.reserved_mtts + HERMON_MAX_MTTS - 1 );
+       log_num_mpts = fls ( hermon->cap.reserved_mrws + 1 - 1 );
+       log_num_mcs = HERMON_LOG_MULTICAST_HASH_SIZE;
 
-/**
- * Unregister Hermon Infiniband device
- *
- * @v hermon           Hermon device
- * @v port             Hermon port
- */
-static void hermon_unregister_ibdev ( struct hermon *hermon __unused,
-                                     struct hermon_port *port ) {
-       struct ib_device *ibdev = port->ibdev;
+       /* ICM starts with the cMPT tables, which are sparse */
+       cmpt_max_len = ( HERMON_CMPT_MAX_ENTRIES *
+                        ( ( uint64_t ) hermon->cap.cmpt_entry_size ) );
+       len = ( ( ( ( 1 << log_num_qps ) * hermon->cap.cmpt_entry_size ) +
+                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
+       hermon->icm_map[HERMON_ICM_QP_CMPT].offset = icm_offset;
+       hermon->icm_map[HERMON_ICM_QP_CMPT].len = len;
+       icm_offset += cmpt_max_len;
+       len = ( ( ( ( 1 << log_num_srqs ) * hermon->cap.cmpt_entry_size ) +
+                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
+       hermon->icm_map[HERMON_ICM_SRQ_CMPT].offset = icm_offset;
+       hermon->icm_map[HERMON_ICM_SRQ_CMPT].len = len;
+       icm_offset += cmpt_max_len;
+       len = ( ( ( ( 1 << log_num_cqs ) * hermon->cap.cmpt_entry_size ) +
+                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
+       hermon->icm_map[HERMON_ICM_CQ_CMPT].offset = icm_offset;
+       hermon->icm_map[HERMON_ICM_CQ_CMPT].len = len;
+       icm_offset += cmpt_max_len;
+       len = ( ( ( ( 1 << log_num_eqs ) * hermon->cap.cmpt_entry_size ) +
+                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
+       hermon->icm_map[HERMON_ICM_EQ_CMPT].offset = icm_offset;
+       hermon->icm_map[HERMON_ICM_EQ_CMPT].len = len;
+       icm_offset += cmpt_max_len;
 
-       unregister_ibdev ( ibdev );
-}
+       hermon->icm_map[HERMON_ICM_OTHER].offset = icm_offset;
 
-/** Hermon Infiniband port type */
-static struct hermon_port_type hermon_port_type_ib = {
-       .register_dev = hermon_register_ibdev,
-       .state_change = hermon_state_change_ibdev,
-       .unregister_dev = hermon_unregister_ibdev,
-};
+       /* Queue pair contexts */
+       len = ( ( 1 << log_num_qps ) * hermon->cap.qpc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 12,
+                    qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_2 ( init_hca, 13,
+                    qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l,
+                    ( icm_offset >> 5 ),
+                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp,
+                    log_num_qps );
+       DBGC ( hermon, "Hermon %p ICM QPC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_qps ), hermon->cap.qpc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-/***************************************************************************
- *
- * Ethernet operation
- *
- ***************************************************************************
- */
+       /* Extended alternate path contexts */
+       len = ( ( 1 << log_num_qps ) * hermon->cap.altc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 24,
+                    qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( init_hca, 25,
+                    qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_l,
+                    icm_offset );
+       DBGC ( hermon, "Hermon %p ICM ALTC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_qps ), hermon->cap.altc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-/** Number of Hermon Ethernet send work queue entries */
-#define HERMON_ETH_NUM_SEND_WQES 2
+       /* Extended auxiliary contexts */
+       len = ( ( 1 << log_num_qps ) * hermon->cap.auxc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 28,
+                    qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( init_hca, 29,
+                    qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_l,
+                    icm_offset );
+       DBGC ( hermon, "Hermon %p ICM AUXC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_qps ), hermon->cap.auxc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-/** Number of Hermon Ethernet receive work queue entries */
-#define HERMON_ETH_NUM_RECV_WQES 4
+       /* Shared receive queue contexts */
+       len = ( ( 1 << log_num_srqs ) * hermon->cap.srqc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 18,
+                    qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_2 ( init_hca, 19,
+                    qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l,
+                    ( icm_offset >> 5 ),
+                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq,
+                    log_num_srqs );
+       DBGC ( hermon, "Hermon %p ICM SRQC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_srqs ), hermon->cap.srqc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-/** Number of Hermon Ethernet completion entries */
-#define HERMON_ETH_NUM_CQES 8
+       /* Completion queue contexts */
+       len = ( ( 1 << log_num_cqs ) * hermon->cap.cqc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 20,
+                    qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_2 ( init_hca, 21,
+                    qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l,
+                    ( icm_offset >> 5 ),
+                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq,
+                    log_num_cqs );
+       DBGC ( hermon, "Hermon %p ICM CQC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_cqs ), hermon->cap.cqc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-/**
- * Transmit packet via Hermon Ethernet device
- *
- * @v netdev           Network device
- * @v iobuf            I/O buffer
- * @ret rc             Return status code
- */
-static int hermon_eth_transmit ( struct net_device *netdev,
-                                struct io_buffer *iobuf ) {
-       struct hermon_port *port = netdev->priv;
-       struct ib_device *ibdev = port->ibdev;
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
-       int rc;
+       /* Event queue contexts */
+       len = ( ( 1 << log_num_eqs ) * hermon->cap.eqc_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 32,
+                    qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_2 ( init_hca, 33,
+                    qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l,
+                    ( icm_offset >> 5 ),
+                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_eq,
+                    log_num_eqs );
+       DBGC ( hermon, "Hermon %p ICM EQC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_eqs ), hermon->cap.eqc_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
 
-       /* Transmit packet */
-       if ( ( rc = ib_post_send ( ibdev, port->eth_qp, NULL,
-                                  iobuf ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not transmit: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
-               return rc;
+       /* Memory translation table */
+       len = ( ( 1 << log_num_mtts ) * hermon->cap.mtt_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 64,
+                    tpt_parameters.mtt_base_addr_h, ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( init_hca, 65,
+                    tpt_parameters.mtt_base_addr_l, icm_offset );
+       DBGC ( hermon, "Hermon %p ICM MTT is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_mtts ), hermon->cap.mtt_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
+
+       /* Memory protection table */
+       len = ( ( 1 << log_num_mpts ) * hermon->cap.dmpt_entry_size );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 60,
+                    tpt_parameters.dmpt_base_adr_h, ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( init_hca, 61,
+                    tpt_parameters.dmpt_base_adr_l, icm_offset );
+       MLX_FILL_1 ( init_hca, 62,
+                    tpt_parameters.log_dmpt_sz, log_num_mpts );
+       DBGC ( hermon, "Hermon %p ICM DMPT is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_mpts ), hermon->cap.dmpt_entry_size,
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
+
+       /* Multicast table */
+       len = ( ( 1 << log_num_mcs ) * sizeof ( struct hermonprm_mcg_entry ) );
+       icm_offset = icm_align ( icm_offset, len );
+       MLX_FILL_1 ( init_hca, 48,
+                    multicast_parameters.mc_base_addr_h,
+                    ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( init_hca, 49,
+                    multicast_parameters.mc_base_addr_l, icm_offset );
+       MLX_FILL_1 ( init_hca, 52,
+                    multicast_parameters.log_mc_table_entry_sz,
+                    fls ( sizeof ( struct hermonprm_mcg_entry ) - 1 ) );
+       MLX_FILL_1 ( init_hca, 53,
+                    multicast_parameters.log_mc_table_hash_sz, log_num_mcs );
+       MLX_FILL_1 ( init_hca, 54,
+                    multicast_parameters.log_mc_table_sz, log_num_mcs );
+       DBGC ( hermon, "Hermon %p ICM MC is %d x %#zx at [%08llx,%08llx)\n",
+              hermon, ( 1 << log_num_mcs ),
+              sizeof ( struct hermonprm_mcg_entry ),
+              icm_offset, ( icm_offset + len ) );
+       icm_offset += len;
+
+
+       hermon->icm_map[HERMON_ICM_OTHER].len =
+               ( icm_offset - hermon->icm_map[HERMON_ICM_OTHER].offset );
+
+       /*
+        * Allocate and map physical memory for (portions of) ICM
+        *
+        * Map is:
+        *   ICM AUX area (aligned to its own size)
+        *   cMPT areas
+        *   Other areas
+        */
+
+       /* Calculate physical memory required for ICM */
+       icm_len = 0;
+       for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
+               icm_len += hermon->icm_map[i].len;
        }
 
-       return 0;
-}
+       /* Get ICM auxiliary area size */
+       memset ( &icm_size, 0, sizeof ( icm_size ) );
+       MLX_FILL_1 ( &icm_size, 0, value_hi, ( icm_offset >> 32 ) );
+       MLX_FILL_1 ( &icm_size, 1, value, icm_offset );
+       if ( ( rc = hermon_cmd_set_icm_size ( hermon, &icm_size,
+                                             &icm_aux_size ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not set ICM size: %s\n",
+                      hermon, strerror ( rc ) );
+               goto err_set_icm_size;
+       }
+       icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * HERMON_PAGE_SIZE );
 
-/**
- * Handle Hermon Ethernet device send completion
- *
- * @v ibdev            Infiniband device
- * @v qp               Queue pair
- * @v iobuf            I/O buffer
- * @v rc               Completion status code
- */
-static void hermon_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 );
+       /* Allocate ICM data and auxiliary area */
+       DBGC ( hermon, "Hermon %p requires %zd kB ICM and %zd kB AUX ICM\n",
+              hermon, ( icm_len / 1024 ), ( icm_aux_len / 1024 ) );
+       hermon->icm = umalloc ( icm_aux_len + icm_len );
+       if ( ! hermon->icm ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       icm_phys = user_to_phys ( hermon->icm, 0 );
 
-       netdev_tx_complete_err ( netdev, iobuf, rc );
+       /* Map ICM auxiliary area */
+       DBGC ( hermon, "Hermon %p mapping ICM AUX => %08lx\n",
+              hermon, icm_phys );
+       if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm_aux,
+                                    0, icm_phys, icm_aux_len ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not map AUX ICM: %s\n",
+                      hermon, strerror ( rc ) );
+               goto err_map_icm_aux;
+       }
+       icm_phys += icm_aux_len;
+
+       /* MAP ICM area */
+       for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
+               DBGC ( hermon, "Hermon %p mapping ICM %llx+%zx => %08lx\n",
+                      hermon, hermon->icm_map[i].offset,
+                      hermon->icm_map[i].len, icm_phys );
+               if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm,
+                                            hermon->icm_map[i].offset,
+                                            icm_phys,
+                                            hermon->icm_map[i].len ) ) != 0 ){
+                       DBGC ( hermon, "Hermon %p could not map ICM: %s\n",
+                              hermon, strerror ( rc ) );
+                       goto err_map_icm;
+               }
+               icm_phys += hermon->icm_map[i].len;
+       }
+
+       return 0;
+
+ err_map_icm:
+       assert ( i == 0 ); /* We don't handle partial failure at present */
+ err_map_icm_aux:
+       hermon_cmd_unmap_icm_aux ( hermon );
+       ufree ( hermon->icm );
+       hermon->icm = UNULL;
+ err_alloc:
+ err_set_icm_size:
+       return rc;
 }
 
 /**
- * Handle Hermon Ethernet device receive completion
+ * Free ICM
  *
- * @v ibdev            Infiniband device
- * @v qp               Queue pair
- * @v av               Address vector, or NULL
- * @v iobuf            I/O buffer
- * @v rc               Completion status code
+ * @v hermon           Hermon device
  */
-static void hermon_eth_complete_recv ( struct ib_device *ibdev __unused,
-                                      struct ib_queue_pair *qp,
-                                      struct ib_address_vector *av,
-                                      struct io_buffer *iobuf, int rc ) {
-       struct net_device *netdev = ib_qp_get_ownerdata ( qp );
-       struct net_device *vlan;
-
-       /* Find VLAN device, if applicable */
-       if ( av->vlan_present ) {
-               if ( ( vlan = vlan_find ( netdev, av->vlan ) ) != NULL ) {
-                       netdev = vlan;
-               } else if ( rc == 0 ) {
-                       rc = -ENODEV;
-               }
-       }
+static void hermon_free_icm ( struct hermon *hermon ) {
+       struct hermonprm_scalar_parameter unmap_icm;
+       int i;
 
-       /* Hand off to network layer */
-       if ( rc == 0 ) {
-               netdev_rx ( netdev, iobuf );
-       } else {
-               netdev_rx_err ( netdev, iobuf, rc );
+       for ( i = ( HERMON_ICM_NUM_REGIONS - 1 ) ; i >= 0 ; i-- ) {
+               memset ( &unmap_icm, 0, sizeof ( unmap_icm ) );
+               MLX_FILL_1 ( &unmap_icm, 0, value_hi,
+                            ( hermon->icm_map[i].offset >> 32 ) );
+               MLX_FILL_1 ( &unmap_icm, 1, value,
+                            hermon->icm_map[i].offset );
+               hermon_cmd_unmap_icm ( hermon,
+                                      ( 1 << fls ( ( hermon->icm_map[i].len /
+                                                     HERMON_PAGE_SIZE ) - 1)),
+                                      &unmap_icm );
        }
+       hermon_cmd_unmap_icm_aux ( hermon );
+       ufree ( hermon->icm );
+       hermon->icm = UNULL;
 }
 
-/** Hermon Ethernet device completion operations */
-static struct ib_completion_queue_operations hermon_eth_cq_op = {
-       .complete_send = hermon_eth_complete_send,
-       .complete_recv = hermon_eth_complete_recv,
-};
+/***************************************************************************
+ *
+ * Initialisation
+ *
+ ***************************************************************************
+ */
 
 /**
- * Poll Hermon Ethernet device
+ * Reset device
  *
- * @v netdev           Network device
+ * @v hermon           Hermon device
+ * @v pci              PCI device
  */
-static void hermon_eth_poll ( struct net_device *netdev ) {
-       struct hermon_port *port = netdev->priv;
-       struct ib_device *ibdev = port->ibdev;
+static void hermon_reset ( struct hermon *hermon,
+                          struct pci_device *pci ) {
+       struct pci_config_backup backup;
+       static const uint8_t backup_exclude[] =
+               PCI_CONFIG_BACKUP_EXCLUDE ( 0x58, 0x5c );
 
-       ib_poll_eq ( ibdev );
+       pci_backup ( pci, &backup, backup_exclude );
+       writel ( HERMON_RESET_MAGIC,
+                ( hermon->config + HERMON_RESET_OFFSET ) );
+       mdelay ( HERMON_RESET_WAIT_TIME_MS );
+       pci_restore ( pci, &backup, backup_exclude );
 }
 
 /**
- * Open Hermon Ethernet device
+ * Set up memory protection table
  *
- * @v netdev           Network device
+ * @v hermon           Hermon device
  * @ret rc             Return status code
  */
-static int hermon_eth_open ( struct net_device *netdev ) {
-       struct hermon_port *port = netdev->priv;
-       struct ib_device *ibdev = port->ibdev;
-       struct hermon *hermon = ib_get_drvdata ( ibdev );
-       union hermonprm_set_port set_port;
+static int hermon_setup_mpt ( struct hermon *hermon ) {
+       struct hermonprm_mpt mpt;
+       uint32_t key;
        int rc;
 
-       /* Allocate completion queue */
-       port->eth_cq = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES,
-                                     &hermon_eth_cq_op );
-       if ( ! port->eth_cq ) {
-               DBGC ( hermon, "Hermon %p port %d could not create completion "
-                      "queue\n", hermon, ibdev->port );
-               rc = -ENOMEM;
-               goto err_create_cq;
-       }
+       /* Derive key */
+       key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX );
+       hermon->lkey = ( ( key << 8 ) | ( key >> 24 ) );
 
-       /* Allocate queue pair */
-       port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH,
-                                     HERMON_ETH_NUM_SEND_WQES, port->eth_cq,
-                                     HERMON_ETH_NUM_RECV_WQES, port->eth_cq );
-       if ( ! port->eth_qp ) {
-               DBGC ( hermon, "Hermon %p port %d could not create queue "
-                      "pair\n", hermon, ibdev->port );
-               rc = -ENOMEM;
-               goto err_create_qp;
+       /* Initialise memory protection table */
+       memset ( &mpt, 0, sizeof ( mpt ) );
+       MLX_FILL_7 ( &mpt, 0,
+                    atomic, 1,
+                    rw, 1,
+                    rr, 1,
+                    lw, 1,
+                    lr, 1,
+                    pa, 1,
+                    r_w, 1 );
+       MLX_FILL_1 ( &mpt, 2, mem_key, key );
+       MLX_FILL_1 ( &mpt, 3,
+                    pd, HERMON_GLOBAL_PD );
+       MLX_FILL_1 ( &mpt, 10, len64, 1 );
+       if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon,
+                                          hermon->cap.reserved_mrws,
+                                          &mpt ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not set up MPT: %s\n",
+                      hermon, strerror ( rc ) );
+               return rc;
        }
-       ib_qp_set_ownerdata ( port->eth_qp, netdev );
 
-       /* Activate queue pair */
-       if ( ( rc = ib_modify_qp ( ibdev, port->eth_qp ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not modify queue "
-                      "pair: %s\n", hermon, ibdev->port, strerror ( rc ) );
-               goto err_modify_qp;
-       }
+       return 0;
+}
 
-       /* Fill receive rings */
-       ib_refill_recv ( ibdev, port->eth_qp );
+/**
+ * Configure special queue pairs
+ *
+ * @v hermon           Hermon device
+ * @ret rc             Return status code
+ */
+static int hermon_configure_special_qps ( struct hermon *hermon ) {
+       int rc;
 
-       /* Set port general parameters */
-       memset ( &set_port, 0, sizeof ( set_port ) );
-       MLX_FILL_3 ( &set_port.general, 0,
-                    v_mtu, 1,
-                    v_pprx, 1,
-                    v_pptx, 1 );
-       MLX_FILL_1 ( &set_port.general, 1,
-                    mtu, ( ETH_FRAME_LEN + 40 /* Used by card */ ) );
-       MLX_FILL_1 ( &set_port.general, 2,
-                    pfctx, ( 1 << FCOE_VLAN_PRIORITY ) );
-       MLX_FILL_1 ( &set_port.general, 3,
-                    pfcrx, ( 1 << FCOE_VLAN_PRIORITY ) );
-       if ( ( rc = hermon_cmd_set_port ( hermon, 1,
-                                         ( HERMON_SET_PORT_GENERAL_PARAM |
-                                           ibdev->port ),
-                                         &set_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not set port general "
-                      "parameters: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
-               goto err_set_port_general_params;
+       /* Special QP block must be aligned on its own size */
+       hermon->special_qpn_base = ( ( hermon->cap.reserved_qps +
+                                      HERMON_NUM_SPECIAL_QPS - 1 )
+                                    & ~( HERMON_NUM_SPECIAL_QPS - 1 ) );
+       hermon->qpn_base = ( hermon->special_qpn_base +
+                            HERMON_NUM_SPECIAL_QPS );
+       DBGC ( hermon, "Hermon %p special QPs at [%lx,%lx]\n", hermon,
+              hermon->special_qpn_base, ( hermon->qpn_base - 1 ) );
+
+       /* Issue command to configure special QPs */
+       if ( ( rc = hermon_cmd_conf_special_qp ( hermon, 0x00,
+                                         hermon->special_qpn_base ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not configure special QPs: "
+                      "%s\n", hermon, strerror ( rc ) );
+               return rc;
        }
 
-       /* Set port receive QP */
+       return 0;
+}
+
+/***************************************************************************
+ *
+ * Infiniband link-layer operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Initialise Infiniband link
+ *
+ * @v ibdev            Infiniband device
+ * @ret rc             Return status code
+ */
+static int hermon_open ( struct ib_device *ibdev ) {
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
+       union hermonprm_set_port set_port;
+       int rc;
+
+       /* Set port parameters */
        memset ( &set_port, 0, sizeof ( set_port ) );
-       MLX_FILL_1 ( &set_port.rqp_calc, 0, base_qpn, port->eth_qp->qpn );
-       MLX_FILL_1 ( &set_port.rqp_calc, 2,
-                    mac_miss_index, 128 /* MAC misses go to promisc QP */ );
-       MLX_FILL_2 ( &set_port.rqp_calc, 3,
-                    vlan_miss_index, 127 /* VLAN misses go to promisc QP */,
-                    no_vlan_index, 126 /* VLAN-free go to promisc QP */ );
-       MLX_FILL_2 ( &set_port.rqp_calc, 5,
-                    promisc_qpn, port->eth_qp->qpn,
-                    en_uc_promisc, 1 );
-       MLX_FILL_2 ( &set_port.rqp_calc, 6,
-                    def_mcast_qpn, port->eth_qp->qpn,
-                    mc_promisc_mode, 2 /* Receive all multicasts */ );
-       if ( ( rc = hermon_cmd_set_port ( hermon, 1,
-                                         ( HERMON_SET_PORT_RECEIVE_QP |
-                                           ibdev->port ),
+       MLX_FILL_8 ( &set_port.ib, 0,
+                    mmc, 1,
+                    mvc, 1,
+                    mp, 1,
+                    mg, 1,
+                    mtu_cap, IB_MTU_2048,
+                    vl_cap, IB_VL_0,
+                    rcm, 1,
+                    lss, 1 );
+       MLX_FILL_2 ( &set_port.ib, 10,
+                    max_pkey, 1,
+                    max_gid, 1 );
+       MLX_FILL_1 ( &set_port.ib, 28,
+                    link_speed_supported, 1 );
+       if ( ( rc = hermon_cmd_set_port ( hermon, 0, ibdev->port,
                                          &set_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not set port receive "
-                      "QP: %s\n", hermon, ibdev->port, strerror ( rc ) );
-               goto err_set_port_receive_qp;
+               DBGC ( hermon, "Hermon %p port %d could not set port: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               return rc;
        }
 
        /* Initialise port */
        if ( ( rc = hermon_cmd_init_port ( hermon, ibdev->port ) ) != 0 ) {
                DBGC ( hermon, "Hermon %p port %d could not initialise port: "
                       "%s\n", hermon, ibdev->port, strerror ( rc ) );
-               goto err_init_port;
+               return rc;
        }
 
-       return 0;
+       /* Update MAD parameters */
+       ib_smc_update ( ibdev, hermon_mad );
 
- err_init_port:
- err_set_port_receive_qp:
- err_set_port_general_params:
- err_modify_qp:
-       ib_destroy_qp ( ibdev, port->eth_qp );
- err_create_qp:
-       ib_destroy_cq ( ibdev, port->eth_cq );
- err_create_cq:
-       return rc;
+       return 0;
 }
 
 /**
- * Close Hermon Ethernet device
+ * Close Infiniband link
  *
- * @v netdev           Network device
+ * @v ibdev            Infiniband device
  */
-static void hermon_eth_close ( struct net_device *netdev ) {
-       struct hermon_port *port = netdev->priv;
-       struct ib_device *ibdev = port->ibdev;
+static void hermon_close ( struct ib_device *ibdev ) {
        struct hermon *hermon = ib_get_drvdata ( ibdev );
        int rc;
 
-       /* Close port */
        if ( ( rc = hermon_cmd_close_port ( hermon, ibdev->port ) ) != 0 ) {
                DBGC ( hermon, "Hermon %p port %d could not close port: %s\n",
                       hermon, ibdev->port, strerror ( rc ) );
                /* Nothing we can do about this */
        }
-
-       /* Tear down the queues */
-       ib_destroy_qp ( ibdev, port->eth_qp );
-       ib_destroy_cq ( ibdev, port->eth_cq );
 }
 
-/** Hermon Ethernet network device operations */
-static struct net_device_operations hermon_eth_operations = {
-       .open           = hermon_eth_open,
-       .close          = hermon_eth_close,
-       .transmit       = hermon_eth_transmit,
-       .poll           = hermon_eth_poll,
-};
-
 /**
- * Register Hermon Ethernet device
+ * Inform embedded subnet management agent of a received MAD
  *
- * @v hermon           Hermon device
- * @v port             Hermon port
+ * @v ibdev            Infiniband device
+ * @v mad              MAD
  * @ret rc             Return status code
  */
-static int hermon_register_netdev ( struct hermon *hermon,
-                                   struct hermon_port *port ) {
-       struct net_device *netdev = port->netdev;
-       struct ib_device *ibdev = port->ibdev;
-       struct hermonprm_query_port_cap query_port;
-       union {
-               uint8_t bytes[8];
-               uint32_t dwords[2];
-       } mac;
+static int hermon_inform_sma ( struct ib_device *ibdev,
+                              union ib_mad *mad ) {
        int rc;
 
-       /* Retrieve MAC address */
-       if ( ( rc = hermon_cmd_query_port ( hermon, ibdev->port,
-                                           &query_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not query port: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
+       /* Send the MAD to the embedded SMA */
+       if ( ( rc = hermon_mad ( ibdev, mad ) ) != 0 )
                return rc;
-       }
-       mac.dwords[0] = htonl ( MLX_GET ( &query_port, mac_47_32 ) );
-       mac.dwords[1] = htonl ( MLX_GET ( &query_port, mac_31_0 ) );
-       memcpy ( netdev->hw_addr,
-                &mac.bytes[ sizeof ( mac.bytes ) - ETH_ALEN ], ETH_ALEN );
 
-       /* Register network device */
-       if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not register network "
-                      "device: %s\n", hermon, ibdev->port, strerror ( rc ) );
-               return rc;
-       }
+       /* Update parameters held in software */
+       ib_smc_update ( ibdev, hermon_mad );
 
        return 0;
 }
 
-/**
- * Handle Hermon Ethernet device port state change
+/***************************************************************************
  *
- * @v hermon           Hermon device
- * @v port             Hermon port
- * @v link_up          Link is up
+ * Multicast group operations
+ *
+ ***************************************************************************
  */
-static void hermon_state_change_netdev ( struct hermon *hermon __unused,
-                                        struct hermon_port *port,
-                                        int link_up ) {
-       struct net_device *netdev = port->netdev;
-
-       if ( link_up ) {
-               netdev_link_up ( netdev );
-       } else {
-               netdev_link_down ( netdev );
-       }
-}
 
 /**
- * Unregister Hermon Ethernet device
+ * Attach to multicast group
  *
- * @v hermon           Hermon device
- * @v port             Hermon port
+ * @v ibdev            Infiniband device
+ * @v qp               Queue pair
+ * @v gid              Multicast GID
+ * @ret rc             Return status code
  */
-static void hermon_unregister_netdev ( struct hermon *hermon __unused,
-                                      struct hermon_port *port ) {
-       struct net_device *netdev = port->netdev;
+static int hermon_mcast_attach ( struct ib_device *ibdev,
+                                struct ib_queue_pair *qp,
+                                union ib_gid *gid ) {
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
+       struct hermonprm_mgm_hash hash;
+       struct hermonprm_mcg_entry mcg;
+       unsigned int index;
+       int rc;
 
-       unregister_netdev ( netdev );
-}
+       /* Generate hash table index */
+       if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
+                      hermon, strerror ( rc ) );
+               return rc;
+       }
+       index = MLX_GET ( &hash, hash );
 
-/** Hermon Ethernet port type */
-static struct hermon_port_type hermon_port_type_eth = {
-       .register_dev = hermon_register_netdev,
-       .state_change = hermon_state_change_netdev,
-       .unregister_dev = hermon_unregister_netdev,
-};
+       /* Check for existing hash table entry */
+       if ( ( rc = hermon_cmd_read_mcg ( hermon, index, &mcg ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not read MCG %#x: %s\n",
+                      hermon, index, strerror ( rc ) );
+               return rc;
+       }
+       if ( MLX_GET ( &mcg, hdr.members_count ) != 0 ) {
+               /* FIXME: this implementation allows only a single QP
+                * per multicast group, and doesn't handle hash
+                * collisions.  Sufficient for IPoIB but may need to
+                * be extended in future.
+                */
+               DBGC ( hermon, "Hermon %p MGID index %#x already in use\n",
+                      hermon, index );
+               return -EBUSY;
+       }
 
-/***************************************************************************
- *
- * Port type detection
- *
- ***************************************************************************
- */
+       /* Update hash table entry */
+       MLX_FILL_1 ( &mcg, 1, hdr.members_count, 1 );
+       MLX_FILL_1 ( &mcg, 8, qp[0].qpn, qp->qpn );
+       memcpy ( &mcg.u.dwords[4], gid, sizeof ( *gid ) );
+       if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
+                      hermon, index, strerror ( rc ) );
+               return rc;
+       }
 
-/** Timeout for port sensing */
-#define HERMON_SENSE_PORT_TIMEOUT ( TICKS_PER_SEC / 2 )
+       return 0;
+}
 
 /**
- * Name port type
+ * Detach from multicast group
  *
- * @v port_type                Port type
- * @v port_type_name   Port type name
+ * @v ibdev            Infiniband device
+ * @v qp               Queue pair
+ * @v gid              Multicast GID
  */
-static inline const char * hermon_name_port_type ( unsigned int port_type ) {
-       switch ( port_type ) {
-       case HERMON_PORT_TYPE_UNKNOWN:  return "unknown";
-       case HERMON_PORT_TYPE_IB:       return "Infiniband";
-       case HERMON_PORT_TYPE_ETH:      return "Ethernet";
-       default:                        return "INVALID";
+static void hermon_mcast_detach ( struct ib_device *ibdev,
+                                 struct ib_queue_pair *qp __unused,
+                                 union ib_gid *gid ) {
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
+       struct hermonprm_mgm_hash hash;
+       struct hermonprm_mcg_entry mcg;
+       unsigned int index;
+       int rc;
+
+       /* Generate hash table index */
+       if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
+                      hermon, strerror ( rc ) );
+               return;
+       }
+       index = MLX_GET ( &hash, hash );
+
+       /* Clear hash table entry */
+       memset ( &mcg, 0, sizeof ( mcg ) );
+       if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
+                      hermon, index, strerror ( rc ) );
+               return;
        }
 }
 
+/** Hermon Infiniband operations */
+static struct ib_device_operations hermon_ib_operations = {
+       .create_cq      = hermon_create_cq,
+       .destroy_cq     = hermon_destroy_cq,
+       .create_qp      = hermon_create_qp,
+       .modify_qp      = hermon_modify_qp,
+       .destroy_qp     = hermon_destroy_qp,
+       .post_send      = hermon_post_send,
+       .post_recv      = hermon_post_recv,
+       .poll_cq        = hermon_poll_cq,
+       .poll_eq        = hermon_poll_eq,
+       .open           = hermon_open,
+       .close          = hermon_close,
+       .mcast_attach   = hermon_mcast_attach,
+       .mcast_detach   = hermon_mcast_detach,
+       .set_port_info  = hermon_inform_sma,
+       .set_pkey_table = hermon_inform_sma,
+};
+
 /**
- * Sense port type
+ * Register Hermon Infiniband device
  *
  * @v hermon           Hermon device
  * @v port             Hermon port
- * @ret port_type      Port type, or negative error
+ * @ret rc             Return status code
  */
-static int hermon_sense_port_type ( struct hermon *hermon,
-                                   struct hermon_port *port ) {
+static int hermon_register_ibdev ( struct hermon *hermon,
+                                  struct hermon_port *port ) {
        struct ib_device *ibdev = port->ibdev;
-       struct hermonprm_sense_port sense_port;
-       int port_type;
        int rc;
 
-       /* If DPDP is not supported, always assume Infiniband */
-       if ( ! hermon->cap.dpdp ) {
-               port_type = HERMON_PORT_TYPE_IB;
-               DBGC ( hermon, "Hermon %p port %d does not support DPDP; "
-                      "assuming an %s network\n", hermon, ibdev->port,
-                      hermon_name_port_type ( port_type ) );
-               return port_type;
-       }
+       /* Initialise parameters using SMC */
+       ib_smc_init ( ibdev, hermon_mad );
 
-       /* Sense the port type */
-       if ( ( rc = hermon_cmd_sense_port ( hermon, ibdev->port,
-                                           &sense_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d sense failed: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
+       /* Register Infiniband device */
+       if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not register IB "
+                      "device: %s\n", hermon, ibdev->port, strerror ( rc ) );
                return rc;
        }
-       port_type = MLX_GET ( &sense_port, port_type );
 
-       DBGC ( hermon, "Hermon %p port %d sensed an %s network\n",
-              hermon, ibdev->port, hermon_name_port_type ( port_type ) );
-       return port_type;
+       return 0;
 }
 
 /**
- * Set port type
+ * Handle Hermon Infiniband device port state change
  *
  * @v hermon           Hermon device
  * @v port             Hermon port
- * @ret rc             Return status code
+ * @v link_up          Link is up
  */
-static int hermon_set_port_type ( struct hermon *hermon,
-                                 struct hermon_port *port ) {
+static void hermon_state_change_ibdev ( struct hermon *hermon __unused,
+                                       struct hermon_port *port,
+                                       int link_up __unused ) {
        struct ib_device *ibdev = port->ibdev;
-       struct hermonprm_query_port_cap query_port;
-       int ib_supported;
-       int eth_supported;
-       int port_type;
-       unsigned long start;
-       unsigned long elapsed;
-       int rc;
-
-       /* Check to see which types are supported */
-       if ( ( rc = hermon_cmd_query_port ( hermon, ibdev->port,
-                                           &query_port ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p port %d could not query port: %s\n",
-                      hermon, ibdev->port, strerror ( rc ) );
-               return rc;
-       }
-       ib_supported = MLX_GET ( &query_port, ib );
-       eth_supported = MLX_GET ( &query_port, eth );
-       DBGC ( hermon, "Hermon %p port %d supports%s%s%s\n",
-              hermon, ibdev->port, ( ib_supported ? " Infiniband" : "" ),
-              ( ( ib_supported && eth_supported ) ? " and" : "" ),
-              ( eth_supported ? " Ethernet" : "" ) );
-
-       /* Sense network, if applicable */
-       if ( ib_supported && eth_supported ) {
-
-               /* Both types are supported; try sensing network */
-               start = currticks();
-               do {
-                       /* Try sensing port */
-                       port_type = hermon_sense_port_type ( hermon, port );
-                       if ( port_type < 0 ) {
-                               rc = port_type;
-                               return rc;
-                       }
-               } while ( ( port_type == HERMON_PORT_TYPE_UNKNOWN ) &&
-                         ( ( elapsed = ( currticks() - start ) ) <
-                           HERMON_SENSE_PORT_TIMEOUT ) );
 
-               /* Set port type based on sensed network, defaulting
-                * to Infiniband if nothing was sensed.
-                */
-               switch ( port_type ) {
-               case HERMON_PORT_TYPE_ETH:
-                       port->type = &hermon_port_type_eth;
-                       break;
-               case HERMON_PORT_TYPE_IB:
-               case HERMON_PORT_TYPE_UNKNOWN:
-                       port->type = &hermon_port_type_ib;
-                       break;
-               default:
-                       return -EINVAL;
-               }
+       /* Update MAD parameters */
+       ib_smc_update ( ibdev, hermon_mad );
+}
 
-       } else if ( eth_supported ) {
-               port->type = &hermon_port_type_eth;
-       } else {
-               port->type = &hermon_port_type_ib;
-       }
+/**
+ * Unregister Hermon Infiniband device
+ *
+ * @v hermon           Hermon device
+ * @v port             Hermon port
+ */
+static void hermon_unregister_ibdev ( struct hermon *hermon __unused,
+                                     struct hermon_port *port ) {
+       struct ib_device *ibdev = port->ibdev;
 
-       assert ( port->type != NULL );
-       return 0;
+       unregister_ibdev ( ibdev );
 }
 
+/** Hermon Infiniband port type */
+static struct hermon_port_type hermon_port_type_ib = {
+       .register_dev = hermon_register_ibdev,
+       .state_change = hermon_state_change_ibdev,
+       .unregister_dev = hermon_unregister_ibdev,
+};
+
 /***************************************************************************
  *
- * Firmware control
+ * Ethernet operation
  *
  ***************************************************************************
  */
 
+/** Number of Hermon Ethernet send work queue entries */
+#define HERMON_ETH_NUM_SEND_WQES 2
+
+/** Number of Hermon Ethernet receive work queue entries */
+#define HERMON_ETH_NUM_RECV_WQES 4
+
+/** Number of Hermon Ethernet completion entries */
+#define HERMON_ETH_NUM_CQES 8
+
 /**
- * Map virtual to physical address for firmware usage
+ * Transmit packet via Hermon Ethernet device
  *
- * @v hermon           Hermon device
- * @v map              Mapping function
- * @v va               Virtual address
- * @v pa               Physical address
- * @v len              Length of region
+ * @v netdev           Network device
+ * @v iobuf            I/O buffer
  * @ret rc             Return status code
  */
-static int hermon_map_vpm ( struct hermon *hermon,
-                           int ( *map ) ( struct hermon *hermon,
-                           const struct hermonprm_virtual_physical_mapping* ),
-                           uint64_t va, physaddr_t pa, size_t len ) {
-       struct hermonprm_virtual_physical_mapping mapping;
-       physaddr_t start;
-       physaddr_t low;
-       physaddr_t high;
-       physaddr_t end;
-       size_t size;
+static int hermon_eth_transmit ( struct net_device *netdev,
+                                struct io_buffer *iobuf ) {
+       struct hermon_port *port = netdev->priv;
+       struct ib_device *ibdev = port->ibdev;
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
        int rc;
 
-       /* Sanity checks */
-       assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
-       assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
-       assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
-
-       /* Calculate starting points */
-       start = pa;
-       end = ( start + len );
-       size = ( 1UL << ( fls ( start ^ end ) - 1 ) );
-       low = high = ( end & ~( size - 1 ) );
-       assert ( start < low );
-       assert ( high <= end );
-
-       /* These mappings tend to generate huge volumes of
-        * uninteresting debug data, which basically makes it
-        * impossible to use debugging otherwise.
-        */
-       DBG_DISABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
-
-       /* Map blocks in descending order of size */
-       while ( size >= HERMON_PAGE_SIZE ) {
-
-               /* Find the next candidate block */
-               if ( ( low - size ) >= start ) {
-                       low -= size;
-                       pa = low;
-               } else if ( ( high + size ) <= end ) {
-                       pa = high;
-                       high += size;
-               } else {
-                       size >>= 1;
-                       continue;
-               }
-               assert ( ( va & ( size - 1 ) ) == 0 );
-               assert ( ( pa & ( size - 1 ) ) == 0 );
-
-               /* Map this block */
-               memset ( &mapping, 0, sizeof ( mapping ) );
-               MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) );
-               MLX_FILL_1 ( &mapping, 1, va_l, ( va >> 12 ) );
-               MLX_FILL_H ( &mapping, 2, pa_h, pa );
-               MLX_FILL_2 ( &mapping, 3,
-                            log2size, ( ( fls ( size ) - 1 ) - 12 ),
-                            pa_l, ( pa >> 12 ) );
-               if ( ( rc = map ( hermon, &mapping ) ) != 0 ) {
-                       DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
-                       DBGC ( hermon, "Hermon %p could not map %08llx+%zx to "
-                              "%08lx: %s\n",
-                              hermon, va, size, pa, strerror ( rc ) );
-                       return rc;
-               }
-               va += size;
+       /* Transmit packet */
+       if ( ( rc = ib_post_send ( ibdev, port->eth_qp, NULL,
+                                  iobuf ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not transmit: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               return rc;
        }
-       assert ( low == start );
-       assert ( high == end );
 
-       DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
        return 0;
 }
 
 /**
- * Start firmware running
+ * Handle Hermon Ethernet device send completion
  *
- * @v hermon           Hermon device
- * @ret rc             Return status code
+ * @v ibdev            Infiniband device
+ * @v qp               Queue pair
+ * @v iobuf            I/O buffer
+ * @v rc               Completion status code
  */
-static int hermon_start_firmware ( struct hermon *hermon ) {
-       struct hermonprm_query_fw fw;
-       unsigned int fw_pages;
-       size_t fw_size;
-       physaddr_t fw_base;
-       int rc;
-
-       /* Get firmware parameters */
-       if ( ( rc = hermon_cmd_query_fw ( hermon, &fw ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not query firmware: %s\n",
-                      hermon, strerror ( rc ) );
-               goto err_query_fw;
-       }
-       DBGC ( hermon, "Hermon %p firmware version %d.%d.%d\n", hermon,
-              MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ),
-              MLX_GET ( &fw, fw_rev_subminor ) );
-       fw_pages = MLX_GET ( &fw, fw_pages );
-       DBGC ( hermon, "Hermon %p requires %d pages (%d kB) for firmware\n",
-              hermon, fw_pages, ( fw_pages * 4 ) );
-
-       /* Allocate firmware pages and map firmware area */
-       fw_size = ( fw_pages * HERMON_PAGE_SIZE );
-       hermon->firmware_area = umalloc ( fw_size );
-       if ( ! hermon->firmware_area ) {
-               rc = -ENOMEM;
-               goto err_alloc_fa;
-       }
-       fw_base = user_to_phys ( hermon->firmware_area, 0 );
-       DBGC ( hermon, "Hermon %p firmware area at physical [%08lx,%08lx)\n",
-              hermon, fw_base, ( fw_base + fw_size ) );
-       if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_fa,
-                                    0, fw_base, fw_size ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not map firmware: %s\n",
-                      hermon, strerror ( rc ) );
-               goto err_map_fa;
-       }
-
-       /* Start firmware */
-       if ( ( rc = hermon_cmd_run_fw ( hermon ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not run firmware: %s\n",
-                      hermon, strerror ( rc ) );
-               goto err_run_fw;
-       }
-
-       DBGC ( hermon, "Hermon %p firmware started\n", hermon );
-       return 0;
+static void hermon_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 );
 
- err_run_fw:
- err_map_fa:
-       hermon_cmd_unmap_fa ( hermon );
-       ufree ( hermon->firmware_area );
-       hermon->firmware_area = UNULL;
- err_alloc_fa:
- err_query_fw:
-       return rc;
+       netdev_tx_complete_err ( netdev, iobuf, rc );
 }
 
 /**
- * Stop firmware running
+ * Handle Hermon Ethernet device receive completion
  *
- * @v hermon           Hermon device
+ * @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 hermon_stop_firmware ( struct hermon *hermon ) {
-       int rc;
+static void hermon_eth_complete_recv ( struct ib_device *ibdev __unused,
+                                      struct ib_queue_pair *qp,
+                                      struct ib_address_vector *av,
+                                      struct io_buffer *iobuf, int rc ) {
+       struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+       struct net_device *vlan;
 
-       if ( ( rc = hermon_cmd_unmap_fa ( hermon ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p FATAL could not stop firmware: %s\n",
-                      hermon, strerror ( rc ) );
-               /* Leak memory and return; at least we avoid corruption */
-               return;
+       /* Find VLAN device, if applicable */
+       if ( av->vlan_present ) {
+               if ( ( vlan = vlan_find ( netdev, av->vlan ) ) != NULL ) {
+                       netdev = vlan;
+               } else if ( rc == 0 ) {
+                       rc = -ENODEV;
+               }
+       }
+
+       /* Hand off to network layer */
+       if ( rc == 0 ) {
+               netdev_rx ( netdev, iobuf );
+       } else {
+               netdev_rx_err ( netdev, iobuf, rc );
        }
-       ufree ( hermon->firmware_area );
-       hermon->firmware_area = UNULL;
 }
 
-/***************************************************************************
- *
- * Infinihost Context Memory management
+/** Hermon Ethernet device completion operations */
+static struct ib_completion_queue_operations hermon_eth_cq_op = {
+       .complete_send = hermon_eth_complete_send,
+       .complete_recv = hermon_eth_complete_recv,
+};
+
+/**
+ * Poll Hermon Ethernet device
  *
- ***************************************************************************
+ * @v netdev           Network device
  */
+static void hermon_eth_poll ( struct net_device *netdev ) {
+       struct hermon_port *port = netdev->priv;
+       struct ib_device *ibdev = port->ibdev;
+
+       ib_poll_eq ( ibdev );
+}
 
 /**
- * Get device limits
+ * Open Hermon Ethernet device
  *
- * @v hermon           Hermon device
+ * @v netdev           Network device
  * @ret rc             Return status code
  */
-static int hermon_get_cap ( struct hermon *hermon ) {
-       struct hermonprm_query_dev_cap dev_cap;
+static int hermon_eth_open ( struct net_device *netdev ) {
+       struct hermon_port *port = netdev->priv;
+       struct ib_device *ibdev = port->ibdev;
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
+       union hermonprm_set_port set_port;
        int rc;
 
-       if ( ( rc = hermon_cmd_query_dev_cap ( hermon, &dev_cap ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not get device limits: %s\n",
-                      hermon, strerror ( rc ) );
-               return rc;
+       /* Allocate completion queue */
+       port->eth_cq = ib_create_cq ( ibdev, HERMON_ETH_NUM_CQES,
+                                     &hermon_eth_cq_op );
+       if ( ! port->eth_cq ) {
+               DBGC ( hermon, "Hermon %p port %d could not create completion "
+                      "queue\n", hermon, ibdev->port );
+               rc = -ENOMEM;
+               goto err_create_cq;
        }
 
-       hermon->cap.cmpt_entry_size = MLX_GET ( &dev_cap, c_mpt_entry_sz );
-       hermon->cap.reserved_qps =
-               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_qps ) );
-       hermon->cap.qpc_entry_size = MLX_GET ( &dev_cap, qpc_entry_sz );
-       hermon->cap.altc_entry_size = MLX_GET ( &dev_cap, altc_entry_sz );
-       hermon->cap.auxc_entry_size = MLX_GET ( &dev_cap, aux_entry_sz );
-       hermon->cap.reserved_srqs =
-               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_srqs ) );
-       hermon->cap.srqc_entry_size = MLX_GET ( &dev_cap, srq_entry_sz );
-       hermon->cap.reserved_cqs =
-               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_cqs ) );
-       hermon->cap.cqc_entry_size = MLX_GET ( &dev_cap, cqc_entry_sz );
-       hermon->cap.reserved_eqs = MLX_GET ( &dev_cap, num_rsvd_eqs );
-       if ( hermon->cap.reserved_eqs == 0 ) {
-               /* Backward compatibility */
-               hermon->cap.reserved_eqs =
-                       ( 1 << MLX_GET ( &dev_cap, log2_rsvd_eqs ) );
+       /* Allocate queue pair */
+       port->eth_qp = ib_create_qp ( ibdev, IB_QPT_ETH,
+                                     HERMON_ETH_NUM_SEND_WQES, port->eth_cq,
+                                     HERMON_ETH_NUM_RECV_WQES, port->eth_cq );
+       if ( ! port->eth_qp ) {
+               DBGC ( hermon, "Hermon %p port %d could not create queue "
+                      "pair\n", hermon, ibdev->port );
+               rc = -ENOMEM;
+               goto err_create_qp;
        }
-       hermon->cap.eqc_entry_size = MLX_GET ( &dev_cap, eqc_entry_sz );
-       hermon->cap.reserved_mtts =
-               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mtts ) );
-       hermon->cap.mtt_entry_size = MLX_GET ( &dev_cap, mtt_entry_sz );
-       hermon->cap.reserved_mrws =
-               ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) );
-       hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz );
-       hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars );
-       hermon->cap.num_ports = MLX_GET ( &dev_cap, num_ports );
-       hermon->cap.dpdp = MLX_GET ( &dev_cap, dpdp );
+       ib_qp_set_ownerdata ( port->eth_qp, netdev );
 
-       /* Sanity check */
-       if ( hermon->cap.num_ports > HERMON_MAX_PORTS ) {
-               DBGC ( hermon, "Hermon %p has %d ports (only %d supported)\n",
-                      hermon, hermon->cap.num_ports, HERMON_MAX_PORTS );
-               hermon->cap.num_ports = HERMON_MAX_PORTS;
+       /* Activate queue pair */
+       if ( ( rc = ib_modify_qp ( ibdev, port->eth_qp ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not modify queue "
+                      "pair: %s\n", hermon, ibdev->port, strerror ( rc ) );
+               goto err_modify_qp;
+       }
+
+       /* Fill receive rings */
+       ib_refill_recv ( ibdev, port->eth_qp );
+
+       /* Set port general parameters */
+       memset ( &set_port, 0, sizeof ( set_port ) );
+       MLX_FILL_3 ( &set_port.general, 0,
+                    v_mtu, 1,
+                    v_pprx, 1,
+                    v_pptx, 1 );
+       MLX_FILL_1 ( &set_port.general, 1,
+                    mtu, ( ETH_FRAME_LEN + 40 /* Used by card */ ) );
+       MLX_FILL_1 ( &set_port.general, 2,
+                    pfctx, ( 1 << FCOE_VLAN_PRIORITY ) );
+       MLX_FILL_1 ( &set_port.general, 3,
+                    pfcrx, ( 1 << FCOE_VLAN_PRIORITY ) );
+       if ( ( rc = hermon_cmd_set_port ( hermon, 1,
+                                         ( HERMON_SET_PORT_GENERAL_PARAM |
+                                           ibdev->port ),
+                                         &set_port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not set port general "
+                      "parameters: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               goto err_set_port_general_params;
+       }
+
+       /* Set port receive QP */
+       memset ( &set_port, 0, sizeof ( set_port ) );
+       MLX_FILL_1 ( &set_port.rqp_calc, 0, base_qpn, port->eth_qp->qpn );
+       MLX_FILL_1 ( &set_port.rqp_calc, 2,
+                    mac_miss_index, 128 /* MAC misses go to promisc QP */ );
+       MLX_FILL_2 ( &set_port.rqp_calc, 3,
+                    vlan_miss_index, 127 /* VLAN misses go to promisc QP */,
+                    no_vlan_index, 126 /* VLAN-free go to promisc QP */ );
+       MLX_FILL_2 ( &set_port.rqp_calc, 5,
+                    promisc_qpn, port->eth_qp->qpn,
+                    en_uc_promisc, 1 );
+       MLX_FILL_2 ( &set_port.rqp_calc, 6,
+                    def_mcast_qpn, port->eth_qp->qpn,
+                    mc_promisc_mode, 2 /* Receive all multicasts */ );
+       if ( ( rc = hermon_cmd_set_port ( hermon, 1,
+                                         ( HERMON_SET_PORT_RECEIVE_QP |
+                                           ibdev->port ),
+                                         &set_port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not set port receive "
+                      "QP: %s\n", hermon, ibdev->port, strerror ( rc ) );
+               goto err_set_port_receive_qp;
+       }
+
+       /* Initialise port */
+       if ( ( rc = hermon_cmd_init_port ( hermon, ibdev->port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not initialise port: "
+                      "%s\n", hermon, ibdev->port, strerror ( rc ) );
+               goto err_init_port;
        }
 
        return 0;
+
+ err_init_port:
+ err_set_port_receive_qp:
+ err_set_port_general_params:
+ err_modify_qp:
+       ib_destroy_qp ( ibdev, port->eth_qp );
+ err_create_qp:
+       ib_destroy_cq ( ibdev, port->eth_cq );
+ err_create_cq:
+       return rc;
 }
 
 /**
- * Align ICM table
+ * Close Hermon Ethernet device
  *
- * @v icm_offset       Current ICM offset
- * @v len              ICM table length
- * @ret icm_offset     ICM offset
+ * @v netdev           Network device
  */
-static uint64_t icm_align ( uint64_t icm_offset, size_t len ) {
+static void hermon_eth_close ( struct net_device *netdev ) {
+       struct hermon_port *port = netdev->priv;
+       struct ib_device *ibdev = port->ibdev;
+       struct hermon *hermon = ib_get_drvdata ( ibdev );
+       int rc;
 
-       /* Round up to a multiple of the table size */
-       assert ( len == ( 1UL << ( fls ( len ) - 1 ) ) );
-       return ( ( icm_offset + len - 1 ) & ~( ( ( uint64_t ) len ) - 1 ) );
+       /* Close port */
+       if ( ( rc = hermon_cmd_close_port ( hermon, ibdev->port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not close port: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               /* Nothing we can do about this */
+       }
+
+       /* Tear down the queues */
+       ib_destroy_qp ( ibdev, port->eth_qp );
+       ib_destroy_cq ( ibdev, port->eth_cq );
 }
 
+/** Hermon Ethernet network device operations */
+static struct net_device_operations hermon_eth_operations = {
+       .open           = hermon_eth_open,
+       .close          = hermon_eth_close,
+       .transmit       = hermon_eth_transmit,
+       .poll           = hermon_eth_poll,
+};
+
 /**
- * Allocate ICM
+ * Register Hermon Ethernet device
  *
  * @v hermon           Hermon device
- * @v init_hca         INIT_HCA structure to fill in
+ * @v port             Hermon port
  * @ret rc             Return status code
  */
-static int hermon_alloc_icm ( struct hermon *hermon,
-                             struct hermonprm_init_hca *init_hca ) {
-       struct hermonprm_scalar_parameter icm_size;
-       struct hermonprm_scalar_parameter icm_aux_size;
-       uint64_t icm_offset = 0;
-       unsigned int log_num_qps, log_num_srqs, log_num_cqs, log_num_eqs;
-       unsigned int log_num_mtts, log_num_mpts, log_num_mcs;
-       size_t cmpt_max_len;
-       size_t icm_len, icm_aux_len;
-       size_t len;
-       physaddr_t icm_phys;
-       int i;
+static int hermon_register_netdev ( struct hermon *hermon,
+                                   struct hermon_port *port ) {
+       struct net_device *netdev = port->netdev;
+       struct ib_device *ibdev = port->ibdev;
+       struct hermonprm_query_port_cap query_port;
+       union {
+               uint8_t bytes[8];
+               uint32_t dwords[2];
+       } mac;
        int rc;
 
-       /*
-        * Start by carving up the ICM virtual address space
-        *
-        */
-
-       /* Calculate number of each object type within ICM */
-       log_num_qps = fls ( hermon->cap.reserved_qps +
-                           HERMON_RSVD_SPECIAL_QPS + HERMON_MAX_QPS - 1 );
-       log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 );
-       log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 );
-       log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 );
-       log_num_mtts = fls ( hermon->cap.reserved_mtts + HERMON_MAX_MTTS - 1 );
-       log_num_mpts = fls ( hermon->cap.reserved_mrws + 1 - 1 );
-       log_num_mcs = HERMON_LOG_MULTICAST_HASH_SIZE;
-
-       /* ICM starts with the cMPT tables, which are sparse */
-       cmpt_max_len = ( HERMON_CMPT_MAX_ENTRIES *
-                        ( ( uint64_t ) hermon->cap.cmpt_entry_size ) );
-       len = ( ( ( ( 1 << log_num_qps ) * hermon->cap.cmpt_entry_size ) +
-                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
-       hermon->icm_map[HERMON_ICM_QP_CMPT].offset = icm_offset;
-       hermon->icm_map[HERMON_ICM_QP_CMPT].len = len;
-       icm_offset += cmpt_max_len;
-       len = ( ( ( ( 1 << log_num_srqs ) * hermon->cap.cmpt_entry_size ) +
-                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
-       hermon->icm_map[HERMON_ICM_SRQ_CMPT].offset = icm_offset;
-       hermon->icm_map[HERMON_ICM_SRQ_CMPT].len = len;
-       icm_offset += cmpt_max_len;
-       len = ( ( ( ( 1 << log_num_cqs ) * hermon->cap.cmpt_entry_size ) +
-                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
-       hermon->icm_map[HERMON_ICM_CQ_CMPT].offset = icm_offset;
-       hermon->icm_map[HERMON_ICM_CQ_CMPT].len = len;
-       icm_offset += cmpt_max_len;
-       len = ( ( ( ( 1 << log_num_eqs ) * hermon->cap.cmpt_entry_size ) +
-                 HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
-       hermon->icm_map[HERMON_ICM_EQ_CMPT].offset = icm_offset;
-       hermon->icm_map[HERMON_ICM_EQ_CMPT].len = len;
-       icm_offset += cmpt_max_len;
-
-       hermon->icm_map[HERMON_ICM_OTHER].offset = icm_offset;
-
-       /* Queue pair contexts */
-       len = ( ( 1 << log_num_qps ) * hermon->cap.qpc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 12,
-                    qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_2 ( init_hca, 13,
-                    qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l,
-                    ( icm_offset >> 5 ),
-                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp,
-                    log_num_qps );
-       DBGC ( hermon, "Hermon %p ICM QPC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_qps ), hermon->cap.qpc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
-
-       /* Extended alternate path contexts */
-       len = ( ( 1 << log_num_qps ) * hermon->cap.altc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 24,
-                    qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( init_hca, 25,
-                    qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_l,
-                    icm_offset );
-       DBGC ( hermon, "Hermon %p ICM ALTC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_qps ), hermon->cap.altc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
-
-       /* Extended auxiliary contexts */
-       len = ( ( 1 << log_num_qps ) * hermon->cap.auxc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 28,
-                    qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( init_hca, 29,
-                    qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_l,
-                    icm_offset );
-       DBGC ( hermon, "Hermon %p ICM AUXC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_qps ), hermon->cap.auxc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
-
-       /* Shared receive queue contexts */
-       len = ( ( 1 << log_num_srqs ) * hermon->cap.srqc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 18,
-                    qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_2 ( init_hca, 19,
-                    qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l,
-                    ( icm_offset >> 5 ),
-                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq,
-                    log_num_srqs );
-       DBGC ( hermon, "Hermon %p ICM SRQC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_srqs ), hermon->cap.srqc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
-
-       /* Completion queue contexts */
-       len = ( ( 1 << log_num_cqs ) * hermon->cap.cqc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 20,
-                    qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_2 ( init_hca, 21,
-                    qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l,
-                    ( icm_offset >> 5 ),
-                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq,
-                    log_num_cqs );
-       DBGC ( hermon, "Hermon %p ICM CQC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_cqs ), hermon->cap.cqc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
+       /* Retrieve MAC address */
+       if ( ( rc = hermon_cmd_query_port ( hermon, ibdev->port,
+                                           &query_port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not query port: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               return rc;
+       }
+       mac.dwords[0] = htonl ( MLX_GET ( &query_port, mac_47_32 ) );
+       mac.dwords[1] = htonl ( MLX_GET ( &query_port, mac_31_0 ) );
+       memcpy ( netdev->hw_addr,
+                &mac.bytes[ sizeof ( mac.bytes ) - ETH_ALEN ], ETH_ALEN );
 
-       /* Event queue contexts */
-       len = ( ( 1 << log_num_eqs ) * hermon->cap.eqc_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 32,
-                    qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_2 ( init_hca, 33,
-                    qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l,
-                    ( icm_offset >> 5 ),
-                    qpc_eec_cqc_eqc_rdb_parameters.log_num_of_eq,
-                    log_num_eqs );
-       DBGC ( hermon, "Hermon %p ICM EQC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_eqs ), hermon->cap.eqc_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
+       /* Register network device */
+       if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not register network "
+                      "device: %s\n", hermon, ibdev->port, strerror ( rc ) );
+               return rc;
+       }
 
-       /* Memory translation table */
-       len = ( ( 1 << log_num_mtts ) * hermon->cap.mtt_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 64,
-                    tpt_parameters.mtt_base_addr_h, ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( init_hca, 65,
-                    tpt_parameters.mtt_base_addr_l, icm_offset );
-       DBGC ( hermon, "Hermon %p ICM MTT is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_mtts ), hermon->cap.mtt_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
+       return 0;
+}
 
-       /* Memory protection table */
-       len = ( ( 1 << log_num_mpts ) * hermon->cap.dmpt_entry_size );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 60,
-                    tpt_parameters.dmpt_base_adr_h, ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( init_hca, 61,
-                    tpt_parameters.dmpt_base_adr_l, icm_offset );
-       MLX_FILL_1 ( init_hca, 62,
-                    tpt_parameters.log_dmpt_sz, log_num_mpts );
-       DBGC ( hermon, "Hermon %p ICM DMPT is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_mpts ), hermon->cap.dmpt_entry_size,
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
+/**
+ * Handle Hermon Ethernet device port state change
+ *
+ * @v hermon           Hermon device
+ * @v port             Hermon port
+ * @v link_up          Link is up
+ */
+static void hermon_state_change_netdev ( struct hermon *hermon __unused,
+                                        struct hermon_port *port,
+                                        int link_up ) {
+       struct net_device *netdev = port->netdev;
 
-       /* Multicast table */
-       len = ( ( 1 << log_num_mcs ) * sizeof ( struct hermonprm_mcg_entry ) );
-       icm_offset = icm_align ( icm_offset, len );
-       MLX_FILL_1 ( init_hca, 48,
-                    multicast_parameters.mc_base_addr_h,
-                    ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( init_hca, 49,
-                    multicast_parameters.mc_base_addr_l, icm_offset );
-       MLX_FILL_1 ( init_hca, 52,
-                    multicast_parameters.log_mc_table_entry_sz,
-                    fls ( sizeof ( struct hermonprm_mcg_entry ) - 1 ) );
-       MLX_FILL_1 ( init_hca, 53,
-                    multicast_parameters.log_mc_table_hash_sz, log_num_mcs );
-       MLX_FILL_1 ( init_hca, 54,
-                    multicast_parameters.log_mc_table_sz, log_num_mcs );
-       DBGC ( hermon, "Hermon %p ICM MC is %d x %#zx at [%08llx,%08llx)\n",
-              hermon, ( 1 << log_num_mcs ),
-              sizeof ( struct hermonprm_mcg_entry ),
-              icm_offset, ( icm_offset + len ) );
-       icm_offset += len;
+       if ( link_up ) {
+               netdev_link_up ( netdev );
+       } else {
+               netdev_link_down ( netdev );
+       }
+}
 
+/**
+ * Unregister Hermon Ethernet device
+ *
+ * @v hermon           Hermon device
+ * @v port             Hermon port
+ */
+static void hermon_unregister_netdev ( struct hermon *hermon __unused,
+                                      struct hermon_port *port ) {
+       struct net_device *netdev = port->netdev;
 
-       hermon->icm_map[HERMON_ICM_OTHER].len =
-               ( icm_offset - hermon->icm_map[HERMON_ICM_OTHER].offset );
+       unregister_netdev ( netdev );
+}
 
-       /*
-        * Allocate and map physical memory for (portions of) ICM
-        *
-        * Map is:
-        *   ICM AUX area (aligned to its own size)
-        *   cMPT areas
-        *   Other areas
-        */
+/** Hermon Ethernet port type */
+static struct hermon_port_type hermon_port_type_eth = {
+       .register_dev = hermon_register_netdev,
+       .state_change = hermon_state_change_netdev,
+       .unregister_dev = hermon_unregister_netdev,
+};
 
-       /* Calculate physical memory required for ICM */
-       icm_len = 0;
-       for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
-               icm_len += hermon->icm_map[i].len;
-       }
+/***************************************************************************
+ *
+ * Port type detection
+ *
+ ***************************************************************************
+ */
 
-       /* Get ICM auxiliary area size */
-       memset ( &icm_size, 0, sizeof ( icm_size ) );
-       MLX_FILL_1 ( &icm_size, 0, value_hi, ( icm_offset >> 32 ) );
-       MLX_FILL_1 ( &icm_size, 1, value, icm_offset );
-       if ( ( rc = hermon_cmd_set_icm_size ( hermon, &icm_size,
-                                             &icm_aux_size ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not set ICM size: %s\n",
-                      hermon, strerror ( rc ) );
-               goto err_set_icm_size;
-       }
-       icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * HERMON_PAGE_SIZE );
+/** Timeout for port sensing */
+#define HERMON_SENSE_PORT_TIMEOUT ( TICKS_PER_SEC / 2 )
 
-       /* Allocate ICM data and auxiliary area */
-       DBGC ( hermon, "Hermon %p requires %zd kB ICM and %zd kB AUX ICM\n",
-              hermon, ( icm_len / 1024 ), ( icm_aux_len / 1024 ) );
-       hermon->icm = umalloc ( icm_aux_len + icm_len );
-       if ( ! hermon->icm ) {
-               rc = -ENOMEM;
-               goto err_alloc;
+/**
+ * Name port type
+ *
+ * @v port_type                Port type
+ * @v port_type_name   Port type name
+ */
+static inline const char * hermon_name_port_type ( unsigned int port_type ) {
+       switch ( port_type ) {
+       case HERMON_PORT_TYPE_UNKNOWN:  return "unknown";
+       case HERMON_PORT_TYPE_IB:       return "Infiniband";
+       case HERMON_PORT_TYPE_ETH:      return "Ethernet";
+       default:                        return "INVALID";
        }
-       icm_phys = user_to_phys ( hermon->icm, 0 );
+}
 
-       /* Map ICM auxiliary area */
-       DBGC ( hermon, "Hermon %p mapping ICM AUX => %08lx\n",
-              hermon, icm_phys );
-       if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm_aux,
-                                    0, icm_phys, icm_aux_len ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not map AUX ICM: %s\n",
-                      hermon, strerror ( rc ) );               
-               goto err_map_icm_aux;
-       }
-       icm_phys += icm_aux_len;
+/**
+ * Sense port type
+ *
+ * @v hermon           Hermon device
+ * @v port             Hermon port
+ * @ret port_type      Port type, or negative error
+ */
+static int hermon_sense_port_type ( struct hermon *hermon,
+                                   struct hermon_port *port ) {
+       struct ib_device *ibdev = port->ibdev;
+       struct hermonprm_sense_port sense_port;
+       int port_type;
+       int rc;
 
-       /* MAP ICM area */
-       for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
-               DBGC ( hermon, "Hermon %p mapping ICM %llx+%zx => %08lx\n",
-                      hermon, hermon->icm_map[i].offset,
-                      hermon->icm_map[i].len, icm_phys );
-               if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm,
-                                            hermon->icm_map[i].offset,
-                                            icm_phys,
-                                            hermon->icm_map[i].len ) ) != 0 ){
-                       DBGC ( hermon, "Hermon %p could not map ICM: %s\n",
-                              hermon, strerror ( rc ) );
-                       goto err_map_icm;
-               }
-               icm_phys += hermon->icm_map[i].len;
+       /* If DPDP is not supported, always assume Infiniband */
+       if ( ! hermon->cap.dpdp ) {
+               port_type = HERMON_PORT_TYPE_IB;
+               DBGC ( hermon, "Hermon %p port %d does not support DPDP; "
+                      "assuming an %s network\n", hermon, ibdev->port,
+                      hermon_name_port_type ( port_type ) );
+               return port_type;
        }
 
-       return 0;
+       /* Sense the port type */
+       if ( ( rc = hermon_cmd_sense_port ( hermon, ibdev->port,
+                                           &sense_port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d sense failed: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               return rc;
+       }
+       port_type = MLX_GET ( &sense_port, port_type );
 
- err_map_icm:
-       assert ( i == 0 ); /* We don't handle partial failure at present */
- err_map_icm_aux:
-       hermon_cmd_unmap_icm_aux ( hermon );
-       ufree ( hermon->icm );
-       hermon->icm = UNULL;
- err_alloc:
- err_set_icm_size:
-       return rc;
+       DBGC ( hermon, "Hermon %p port %d sensed an %s network\n",
+              hermon, ibdev->port, hermon_name_port_type ( port_type ) );
+       return port_type;
 }
 
 /**
- * Free ICM
+ * Set port type
  *
  * @v hermon           Hermon device
+ * @v port             Hermon port
+ * @ret rc             Return status code
  */
-static void hermon_free_icm ( struct hermon *hermon ) {
-       struct hermonprm_scalar_parameter unmap_icm;
-       int i;
+static int hermon_set_port_type ( struct hermon *hermon,
+                                 struct hermon_port *port ) {
+       struct ib_device *ibdev = port->ibdev;
+       struct hermonprm_query_port_cap query_port;
+       int ib_supported;
+       int eth_supported;
+       int port_type;
+       unsigned long start;
+       unsigned long elapsed;
+       int rc;
 
-       for ( i = ( HERMON_ICM_NUM_REGIONS - 1 ) ; i >= 0 ; i-- ) {
-               memset ( &unmap_icm, 0, sizeof ( unmap_icm ) );
-               MLX_FILL_1 ( &unmap_icm, 0, value_hi,
-                            ( hermon->icm_map[i].offset >> 32 ) );
-               MLX_FILL_1 ( &unmap_icm, 1, value,
-                            hermon->icm_map[i].offset );
-               hermon_cmd_unmap_icm ( hermon,
-                                      ( 1 << fls ( ( hermon->icm_map[i].len /
-                                                     HERMON_PAGE_SIZE ) - 1)),
-                                      &unmap_icm );
+       /* Check to see which types are supported */
+       if ( ( rc = hermon_cmd_query_port ( hermon, ibdev->port,
+                                           &query_port ) ) != 0 ) {
+               DBGC ( hermon, "Hermon %p port %d could not query port: %s\n",
+                      hermon, ibdev->port, strerror ( rc ) );
+               return rc;
        }
-       hermon_cmd_unmap_icm_aux ( hermon );
-       ufree ( hermon->icm );
-       hermon->icm = UNULL;
+       ib_supported = MLX_GET ( &query_port, ib );
+       eth_supported = MLX_GET ( &query_port, eth );
+       DBGC ( hermon, "Hermon %p port %d supports%s%s%s\n",
+              hermon, ibdev->port, ( ib_supported ? " Infiniband" : "" ),
+              ( ( ib_supported && eth_supported ) ? " and" : "" ),
+              ( eth_supported ? " Ethernet" : "" ) );
+
+       /* Sense network, if applicable */
+       if ( ib_supported && eth_supported ) {
+
+               /* Both types are supported; try sensing network */
+               start = currticks();
+               do {
+                       /* Try sensing port */
+                       port_type = hermon_sense_port_type ( hermon, port );
+                       if ( port_type < 0 ) {
+                               rc = port_type;
+                               return rc;
+                       }
+               } while ( ( port_type == HERMON_PORT_TYPE_UNKNOWN ) &&
+                         ( ( elapsed = ( currticks() - start ) ) <
+                           HERMON_SENSE_PORT_TIMEOUT ) );
+
+               /* Set port type based on sensed network, defaulting
+                * to Infiniband if nothing was sensed.
+                */
+               switch ( port_type ) {
+               case HERMON_PORT_TYPE_ETH:
+                       port->type = &hermon_port_type_eth;
+                       break;
+               case HERMON_PORT_TYPE_IB:
+               case HERMON_PORT_TYPE_UNKNOWN:
+                       port->type = &hermon_port_type_ib;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+       } else if ( eth_supported ) {
+               port->type = &hermon_port_type_eth;
+       } else {
+               port->type = &hermon_port_type_ib;
+       }
+
+       assert ( port->type != NULL );
+       return 0;
 }
 
 /***************************************************************************
@@ -3424,94 +3519,6 @@ static struct bofm_operations hermon_bofm_operations = {
  ***************************************************************************
  */
 
-/**
- * Set up memory protection table
- *
- * @v hermon           Hermon device
- * @ret rc             Return status code
- */
-static int hermon_setup_mpt ( struct hermon *hermon ) {
-       struct hermonprm_mpt mpt;
-       uint32_t key;
-       int rc;
-
-       /* Derive key */
-       key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX );
-       hermon->lkey = ( ( key << 8 ) | ( key >> 24 ) );
-
-       /* Initialise memory protection table */
-       memset ( &mpt, 0, sizeof ( mpt ) );
-       MLX_FILL_7 ( &mpt, 0,
-                    atomic, 1,
-                    rw, 1,
-                    rr, 1,
-                    lw, 1,
-                    lr, 1,
-                    pa, 1,
-                    r_w, 1 );
-       MLX_FILL_1 ( &mpt, 2, mem_key, key );
-       MLX_FILL_1 ( &mpt, 3,
-                    pd, HERMON_GLOBAL_PD );
-       MLX_FILL_1 ( &mpt, 10, len64, 1 );
-       if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon,
-                                          hermon->cap.reserved_mrws,
-                                          &mpt ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not set up MPT: %s\n",
-                      hermon, strerror ( rc ) );
-               return rc;
-       }
-
-       return 0;
-}
-
-/**
- * Configure special queue pairs
- *
- * @v hermon           Hermon device
- * @ret rc             Return status code
- */
-static int hermon_configure_special_qps ( struct hermon *hermon ) {
-       int rc;
-
-       /* Special QP block must be aligned on its own size */
-       hermon->special_qpn_base = ( ( hermon->cap.reserved_qps +
-                                      HERMON_NUM_SPECIAL_QPS - 1 )
-                                    & ~( HERMON_NUM_SPECIAL_QPS - 1 ) );
-       hermon->qpn_base = ( hermon->special_qpn_base +
-                            HERMON_NUM_SPECIAL_QPS );
-       DBGC ( hermon, "Hermon %p special QPs at [%lx,%lx]\n", hermon,
-              hermon->special_qpn_base, ( hermon->qpn_base - 1 ) );
-
-       /* Issue command to configure special QPs */
-       if ( ( rc = hermon_cmd_conf_special_qp ( hermon, 0x00,
-                                         hermon->special_qpn_base ) ) != 0 ) {
-               DBGC ( hermon, "Hermon %p could not configure special QPs: "
-                      "%s\n", hermon, strerror ( rc ) );
-               return rc;
-       }
-
-       return 0;
-}
-
-/**
- * Reset device
- *
- * @v hermon           Hermon device
- * @v pci              PCI device
- */
-static void hermon_reset ( struct hermon *hermon,
-                          struct pci_device *pci ) {
-       struct pci_config_backup backup;
-       static const uint8_t backup_exclude[] =
-               PCI_CONFIG_BACKUP_EXCLUDE ( 0x58, 0x5c );
-
-       pci_backup ( pci, &backup, backup_exclude );
-       writel ( HERMON_RESET_MAGIC,
-                ( hermon->config + HERMON_RESET_OFFSET ) );
-       mdelay ( HERMON_RESET_WAIT_TIME_MS );
-       pci_restore ( pci, &backup, backup_exclude );
-}
-
 /**
  * Allocate Hermon device
  *