/***************************************************************************
*
- * 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;
}
/***************************************************************************
***************************************************************************
*/
-/**
- * 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
*