static int ntb_map_pci_bars(struct ntb_softc *ntb) { int rc; ntb->bar_info[NTB_CONFIG_BAR].pci_resource_id = PCIR_BAR(0); rc = map_pci_bar(ntb, map_mmr_bar, &ntb->bar_info[NTB_CONFIG_BAR]); if (rc != 0) return (rc); ntb->bar_info[NTB_B2B_BAR_1].pci_resource_id = PCIR_BAR(2); rc = map_pci_bar(ntb, map_memory_window_bar, &ntb->bar_info[NTB_B2B_BAR_1]); if (rc != 0) return (rc); ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4); if (HAS_FEATURE(NTB_REGS_THRU_MW) && !HAS_FEATURE(NTB_SPLIT_BAR)) rc = map_pci_bar(ntb, map_mmr_bar, &ntb->bar_info[NTB_B2B_BAR_2]); else rc = map_pci_bar(ntb, map_memory_window_bar, &ntb->bar_info[NTB_B2B_BAR_2]); if (!HAS_FEATURE(NTB_SPLIT_BAR)) return (rc); ntb->bar_info[NTB_B2B_BAR_3].pci_resource_id = PCIR_BAR(5); if (HAS_FEATURE(NTB_REGS_THRU_MW)) rc = map_pci_bar(ntb, map_mmr_bar, &ntb->bar_info[NTB_B2B_BAR_3]); else rc = map_pci_bar(ntb, map_memory_window_bar, &ntb->bar_info[NTB_B2B_BAR_3]); return (rc); }
static void configure_xeon_secondary_side_bars(struct ntb_softc *ntb) { if (ntb->dev_type == NTB_DEV_USD) { ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, MBAR23_DSD_ADDR); if (HAS_FEATURE(NTB_REGS_THRU_MW)) ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, MBAR01_DSD_ADDR); else { if (HAS_FEATURE(NTB_SPLIT_BAR)) { ntb_reg_write(4, XEON_PBAR4XLAT_OFFSET, MBAR4_DSD_ADDR); ntb_reg_write(4, XEON_PBAR5XLAT_OFFSET, MBAR5_DSD_ADDR); } else ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, MBAR4_DSD_ADDR); /* * B2B_XLAT_OFFSET is a 64-bit register but can only be * written 32 bits at a time. */ ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, MBAR01_DSD_ADDR & 0xffffffff); ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, MBAR01_DSD_ADDR >> 32); } ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_USD_ADDR); ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_USD_ADDR); if (HAS_FEATURE(NTB_SPLIT_BAR)) { ntb_reg_write(4, XEON_SBAR4BASE_OFFSET, MBAR4_USD_ADDR); ntb_reg_write(4, XEON_SBAR5BASE_OFFSET, MBAR5_USD_ADDR); } else ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR4_USD_ADDR); } else {
static void configure_xeon_secondary_side_bars(struct ntb_softc *ntb) { if (ntb->dev_type == NTB_DEV_USD) { ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_USD_ADDR); if (HAS_FEATURE(NTB_REGS_THRU_MW)) ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, MBAR01_DSD_ADDR); else ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, PBAR4XLAT_USD_ADDR); ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_USD_ADDR); ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_USD_ADDR); ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_USD_ADDR); } else { ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_DSD_ADDR); if (HAS_FEATURE(NTB_REGS_THRU_MW)) ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, MBAR01_USD_ADDR); else ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, PBAR4XLAT_DSD_ADDR); ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_DSD_ADDR); ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_DSD_ADDR); ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_DSD_ADDR); } }
static void cliWriteConfig(void) { int16_t result; if (!HAS_FEATURE(PPSEN) && (cfg.flags & FLAG_PPSEN)) { cli_puts("WARNING: PPS output not available on this hardware\r\n"); cfg.flags &= ~FLAG_PPSEN; } if ((cfg.flags & (FLAG_GPSEXT | FLAG_GPSOUT)) == (FLAG_GPSEXT | FLAG_GPSOUT)) { cli_puts("WARNING: gps_ext_in and gps_ext_out are mutually exclusive.\r\n"); cfg.flags &= ~FLAG_GPSOUT; } /* Check for more than one ntpkey type */ result = 0; if (cfg.flags & FLAG_NTPKEY_MD5) { result++; } if (cfg.flags & FLAG_NTPKEY_SHA1) { result++; } if (result > 1) { cli_puts("WARNING: More than one ntpkey type specified\r\n"); cfg.flags &= ~(FLAG_NTPKEY_MD5 | FLAG_NTPKEY_SHA1); } cli_puts("Writing EEPROM...\r\n"); result = eeprom_write_cfg(); if (result == EERR_OK) { cli_puts("OK\r\n"); serial_drain(cl_out); vTaskDelay(pdMS_TO_TICKS(1000)); NVIC_SystemReset(); } else { show_eeprom_error(result); } }
static int map_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) { int rc; uint8_t bar_size_bits = 0; bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY, &bar->pci_resource_id, RF_ACTIVE); if (bar->pci_resource == NULL) return (ENXIO); save_bar_parameters(bar); /* * Ivytown NTB BAR sizes are misreported by the hardware due to a * hardware issue. To work around this, query the size it should be * configured to by the device and modify the resource to correspond to * this new size. The BIOS on systems with this problem is required to * provide enough address space to allow the driver to make this change * safely. * * Ideally I could have just specified the size when I allocated the * resource like: * bus_alloc_resource(ntb->device, * SYS_RES_MEMORY, &bar->pci_resource_id, 0ul, ~0ul, * 1ul << bar_size_bits, RF_ACTIVE); * but the PCI driver does not honor the size in this call, so we have * to modify it after the fact. */ if (HAS_FEATURE(NTB_BAR_SIZE_4K)) { if (bar->pci_resource_id == PCIR_BAR(2)) bar_size_bits = pci_read_config(ntb->device, XEON_PBAR23SZ_OFFSET, 1); else bar_size_bits = pci_read_config(ntb->device, XEON_PBAR45SZ_OFFSET, 1); rc = bus_adjust_resource(ntb->device, SYS_RES_MEMORY, bar->pci_resource, bar->pbase, bar->pbase + (1ul << bar_size_bits) - 1); if (rc != 0) { device_printf(ntb->device, "unable to resize bar\n"); return (rc); } save_bar_parameters(bar); } /* Mark bar region as write combining to improve performance. */ rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, VM_MEMATTR_WRITE_COMBINING); if (rc != 0) { device_printf(ntb->device, "unable to mark bar as WRITE_COMBINING\n"); return (rc); } return (0); }
static void ntb_detect_max_mw(struct ntb_softc *ntb) { if (ntb->type == NTB_SOC) { ntb->limits.max_mw = SOC_MAX_MW; return; } if (HAS_FEATURE(NTB_SPLIT_BAR)) ntb->limits.max_mw = XEON_HSXSPLIT_MAX_MW; else ntb->limits.max_mw = XEON_SNB_MAX_MW; }
static void main_thread(void *pdata) { QueueSetHandle_t qs; QueueSetMemberHandle_t active; ASSERT((qs = xQueueCreateSet(SERIAL_RX_SIZE * 3))); serial_start(cli_serial, 115200, qs); serial_start(&Serial4, 57600, qs); serial_start(&Serial5, cfg.gps_baud_rate ? cfg.gps_baud_rate : 57600, qs); cli_set_output(cli_serial); log_start(cli_serial); cl_enabled = 1; load_eeprom(); cfg.flags &= ~FLAG_HOLDOVER_TEST; if (cfg.flags & FLAG_GPSEXT) { gps_serial = &Serial5; } else { gps_serial = &Serial4; } if (!cfg.holdover) { cfg.holdover = 60; } if (!cfg.loopstats_interval) { cfg.loopstats_interval = 60; } ppscapture_start(); vtimer_start(); tcpip_start(); test_reset(); cli_banner(); if (!(cfg.flags & FLAG_GPSEXT)) { ublox_configure(); if (HAS_FEATURE(PPSEN) && (cfg.flags & FLAG_PPSEN)) { GPIO_OFF(PPSEN); } } cl_enabled = 0; while (1) { watchdog_main = 5; active = xQueueSelectFromSet(qs, pdMS_TO_TICKS(1000)); if (active == cli_serial->rx_q) { int16_t val = serial_get(cli_serial, TIMEOUT_NOBLOCK); ASSERT(val >= 0); cli_feed(val); } else if (active == gps_serial->rx_q) { int16_t val = serial_get(gps_serial, TIMEOUT_NOBLOCK); ASSERT(val >= 0); gps_byte_received(val); if (cfg.flags & FLAG_GPSOUT) { char tmp = val; serial_write(&Serial5, &tmp, 1); } #if 0 } else if (active == Serial5.rx_q) { char tmp = serial_get(&Serial5, TIMEOUT_NOBLOCK); serial_write(&Serial4, &tmp, 1); #endif } } }
static void createNativeEvents( ) { char name[64]; char sanitized_name[PAPI_MAX_STR_LEN]; char names[device_count][64]; int i, nameLen = 0, j; int isUnique = 1; nvml_native_event_entry_t* entry; nvmlReturn_t ret; nvml_native_table = (nvml_native_event_entry_t*) papi_malloc( sizeof(nvml_native_event_entry_t) * num_events ); memset( nvml_native_table, 0x0, sizeof(nvml_native_event_entry_t) * num_events ); entry = &nvml_native_table[0]; for (i=0; i < device_count; i++ ) { memset( names[i], 0x0, 64 ); isUnique = 1; ret = nvmlDeviceGetName( devices[i], name, 64 ); for (j=0; j < i; j++ ) { if ( 0 == strncmp( name, names[j], 64 ) ) isUnique = 0; } if ( isUnique ) { nameLen = strlen(name); strncpy(sanitized_name, name, PAPI_MAX_STR_LEN ); for (j=0; j < nameLen; j++) if ( ' ' == sanitized_name[j] ) sanitized_name[j] = '_'; if ( HAS_FEATURE( features[i], FEATURE_CLOCK_INFO ) ) { sprintf( entry->name, "NVML.%s.graphics_clock", sanitized_name ); strncpy(entry->description,"Graphics clock domain (MHz).", PAPI_MAX_STR_LEN ); entry->options.clock = NVML_CLOCK_GRAPHICS; entry->type = FEATURE_CLOCK_INFO; entry++; sprintf( entry->name, "NVML.%s.sm_clock", sanitized_name); strncpy(entry->description,"SM clock domain (MHz).", PAPI_MAX_STR_LEN); entry->options.clock = NVML_CLOCK_SM; entry->type = FEATURE_CLOCK_INFO; entry++; sprintf( entry->name, "NVML.%s.memory_clock", sanitized_name); strncpy(entry->description,"Memory clock domain (MHz).", PAPI_MAX_STR_LEN); entry->options.clock = NVML_CLOCK_MEM; entry->type = FEATURE_CLOCK_INFO; entry++; } if ( HAS_FEATURE( features[i], FEATURE_ECC_LOCAL_ERRORS ) ) { sprintf(entry->name, "NVML.%s.l1_single_ecc_errors", sanitized_name); strncpy(entry->description,"L1 cache single bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_SINGLE_BIT_ECC, .which_one = LOCAL_ECC_L1, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.l2_single_ecc_errors", sanitized_name); strncpy(entry->description,"L2 cache single bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_SINGLE_BIT_ECC, .which_one = LOCAL_ECC_L2, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.memory_single_ecc_errors", sanitized_name); strncpy(entry->description,"Device memory single bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_SINGLE_BIT_ECC, .which_one = LOCAL_ECC_MEM, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.regfile_single_ecc_errors", sanitized_name); strncpy(entry->description,"Register file single bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_SINGLE_BIT_ECC, .which_one = LOCAL_ECC_REGFILE, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.1l_double_ecc_errors", sanitized_name); strncpy(entry->description,"L1 cache double bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_DOUBLE_BIT_ECC, .which_one = LOCAL_ECC_L1, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.l2_double_ecc_errors", sanitized_name); strncpy(entry->description,"L2 cache double bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_DOUBLE_BIT_ECC, .which_one = LOCAL_ECC_L2, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.memory_double_ecc_errors", sanitized_name); strncpy(entry->description,"Device memory double bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_DOUBLE_BIT_ECC, .which_one = LOCAL_ECC_MEM, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; sprintf(entry->name, "NVML.%s.regfile_double_ecc_errors", sanitized_name); strncpy(entry->description,"Register file double bit ECC", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_DOUBLE_BIT_ECC, .which_one = LOCAL_ECC_REGFILE, }; entry->type = FEATURE_ECC_LOCAL_ERRORS; entry++; } if ( HAS_FEATURE( features[i], FEATURE_FAN_SPEED ) ) { sprintf( entry->name, "NVML.%s.fan_speed", sanitized_name); strncpy(entry->description,"The fan speed expressed as a percent of the maximum, i.e. full speed is 100%", PAPI_MAX_STR_LEN); entry->type = FEATURE_FAN_SPEED; entry++; } if ( HAS_FEATURE( features[i], FEATURE_MAX_CLOCK ) ) { sprintf( entry->name, "NVML.%s.graphics_max_clock", sanitized_name); strncpy(entry->description,"Maximal Graphics clock domain (MHz).", PAPI_MAX_STR_LEN); entry->options.clock = NVML_CLOCK_GRAPHICS; entry->type = FEATURE_MAX_CLOCK; entry++; sprintf( entry->name, "NVML.%s.sm_max_clock", sanitized_name); strncpy(entry->description,"Maximal SM clock domain (MHz).", PAPI_MAX_STR_LEN); entry->options.clock = NVML_CLOCK_SM; entry->type = FEATURE_MAX_CLOCK; entry++; sprintf( entry->name, "NVML.%s.memory_max_clock", sanitized_name); strncpy(entry->description,"Maximal Memory clock domain (MHz).", PAPI_MAX_STR_LEN); entry->options.clock = NVML_CLOCK_MEM; entry->type = FEATURE_MAX_CLOCK; entry++; } if ( HAS_FEATURE( features[i], FEATURE_MEMORY_INFO ) ) { sprintf( entry->name, "NVML.%s.total_memory", sanitized_name); strncpy(entry->description,"Total installed FB memory (in bytes).", PAPI_MAX_STR_LEN); entry->options.which_one = MEMINFO_TOTAL_MEMORY; entry->type = FEATURE_MEMORY_INFO; entry++; sprintf( entry->name, "NVML.%s.unallocated_memory", sanitized_name); strncpy(entry->description,"Uncallocated FB memory (in bytes).", PAPI_MAX_STR_LEN); entry->options.which_one = MEMINFO_UNALLOCED; entry->type = FEATURE_MEMORY_INFO; entry++; sprintf( entry->name, "NVML.%s.allocated_memory", sanitized_name); strncpy(entry->description, "Allocated FB memory (in bytes). Note that the driver/GPU always sets aside a small amount of memory for bookkeeping.", PAPI_MAX_STR_LEN); entry->options.which_one = MEMINFO_ALLOCED; entry->type = FEATURE_MEMORY_INFO; entry++; } if ( HAS_FEATURE( features[i], FEATURE_PERF_STATES ) ) { sprintf( entry->name, "NVML.%s.pstate", sanitized_name); strncpy(entry->description,"The performance state of the device.", PAPI_MAX_STR_LEN); entry->type = FEATURE_PERF_STATES; entry++; } if ( HAS_FEATURE( features[i], FEATURE_POWER ) ) { sprintf( entry->name, "NVML.%s.power", sanitized_name); strncpy(entry->description,"Power usage reading for the device, in miliwatts. This is the power draw for the entire board, including GPU, memory, etc.\n The reading is accurate to within a range of +/-5 watts.", PAPI_MAX_STR_LEN); entry->type = FEATURE_POWER; entry++; } if ( HAS_FEATURE( features[i], FEATURE_TEMP ) ) { sprintf( entry->name, "NVML.%s.temperature", sanitized_name); strncpy(entry->description,"Current temperature readings for the device, in degrees C.", PAPI_MAX_STR_LEN); entry->type = FEATURE_TEMP; entry++; } if ( HAS_FEATURE( features[i], FEATURE_ECC_TOTAL_ERRORS ) ) { sprintf( entry->name, "NVML.%s.total_ecc_errors", sanitized_name); strncpy(entry->description,"Total single bit errors.", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_SINGLE_BIT_ECC, }; entry->type = FEATURE_ECC_TOTAL_ERRORS; entry++; sprintf( entry->name, "NVML.%s.total_ecc_errors", sanitized_name); strncpy(entry->description,"Total double bit errors.", PAPI_MAX_STR_LEN); entry->options.ecc_opts = (struct local_ecc){ .bits = NVML_DOUBLE_BIT_ECC, }; entry->type = FEATURE_ECC_TOTAL_ERRORS; entry++; } if ( HAS_FEATURE( features[i], FEATURE_UTILIZATION ) ) { sprintf( entry->name, "NVML.%s.gpu_utilization", sanitized_name); strncpy(entry->description,"Percent of time over the past second during which one or more kernels was executing on the GPU.", PAPI_MAX_STR_LEN); entry->options.which_one = GPU_UTILIZATION; entry->type = FEATURE_UTILIZATION; entry++; sprintf( entry->name, "NVML.%s.memory_utilization", sanitized_name); strncpy(entry->description,"Percent of time over the past second during which global (device) memory was being read or written.", PAPI_MAX_STR_LEN); entry->options.which_one = MEMORY_UTILIZATION; entry->type = FEATURE_UTILIZATION; entry++; } strncpy( names[i], name, 64); } } }
/* hardware or reads values from the operatings system. */ static int nvml_hardware_read( long long *value, int which_one) //, nvml_context_t *ctx) { nvml_native_event_entry_t *entry; nvmlDevice_t handle; int cudaIdx = -1; entry = &nvml_native_table[which_one]; *value = (long long) -1; /* replace entry->resources with the current cuda_device->nvml device */ cudaGetDevice( &cudaIdx ); if ( cudaIdx < 0 || cudaIdx > device_count ) return PAPI_EINVAL; /* Make sure the device we are running on has the requested event */ if ( !HAS_FEATURE( features[cudaIdx] , entry->type) ) return PAPI_EINVAL; handle = devices[cudaIdx]; switch (entry->type) { case FEATURE_CLOCK_INFO: *value = getClockSpeed( handle, (nvmlClockType_t)entry->options.clock ); break; case FEATURE_ECC_LOCAL_ERRORS: *value = getEccLocalErrors( handle, (nvmlEccBitType_t)entry->options.ecc_opts.bits, (int)entry->options.ecc_opts.which_one); break; case FEATURE_FAN_SPEED: *value = getFanSpeed( handle ); break; case FEATURE_MAX_CLOCK: *value = getMaxClockSpeed( handle, (nvmlClockType_t)entry->options.clock ); break; case FEATURE_MEMORY_INFO: *value = getMemoryInfo( handle, (int)entry->options.which_one ); break; case FEATURE_PERF_STATES: *value = getPState( handle ); break; case FEATURE_POWER: *value = getPowerUsage( handle ); break; case FEATURE_TEMP: *value = getTemperature( handle ); break; case FEATURE_ECC_TOTAL_ERRORS: *value = getTotalEccErrors( handle, (nvmlEccBitType_t)entry->options.ecc_opts.bits ); break; case FEATURE_UTILIZATION: *value = getUtilization( handle, (int)entry->options.which_one ); break; default: return PAPI_EINVAL; } return PAPI_OK; }
static int ntb_setup_xeon(struct ntb_softc *ntb) { ntb->reg_ofs.ldb = XEON_PDOORBELL_OFFSET; ntb->reg_ofs.ldb_mask = XEON_PDBMSK_OFFSET; ntb->reg_ofs.spad_local = XEON_SPAD_OFFSET; ntb->reg_ofs.bar2_xlat = XEON_SBAR2XLAT_OFFSET; ntb->reg_ofs.bar4_xlat = XEON_SBAR4XLAT_OFFSET; if (HAS_FEATURE(NTB_SPLIT_BAR)) ntb->reg_ofs.bar5_xlat = XEON_SBAR5XLAT_OFFSET; switch (ntb->conn_type) { case NTB_CONN_B2B: /* * reg_ofs.rdb and reg_ofs.spad_remote are effectively ignored * with the NTB_REGS_THRU_MW errata mode enabled. (See * ntb_ring_doorbell() and ntb_read/write_remote_spad().) */ ntb->reg_ofs.rdb = XEON_B2B_DOORBELL_OFFSET; ntb->reg_ofs.spad_remote = XEON_B2B_SPAD_OFFSET; ntb->limits.max_spads = XEON_MAX_SPADS; break; case NTB_CONN_RP: /* * Every Xeon today needs NTB_REGS_THRU_MW, so punt on RP for * now. */ KASSERT(HAS_FEATURE(NTB_REGS_THRU_MW), ("Xeon without MW errata unimplemented")); device_printf(ntb->device, "NTB-RP disabled to due hardware errata.\n"); return (ENXIO); case NTB_CONN_TRANSPARENT: default: device_printf(ntb->device, "Connection type %d not supported\n", ntb->conn_type); return (ENXIO); } /* * There is a Xeon hardware errata related to writes to SDOORBELL or * B2BDOORBELL in conjunction with inbound access to NTB MMIO space, * which may hang the system. To workaround this use the second memory * window to access the interrupt and scratch pad registers on the * remote system. * * There is another HW errata on the limit registers -- they can only * be written when the base register is (?)4GB aligned and < 32-bit. * This should already be the case based on the driver defaults, but * write the limit registers first just in case. */ if (HAS_FEATURE(NTB_REGS_THRU_MW)) { /* * Set the Limit register to 4k, the minimum size, to prevent * an illegal access. * * XXX: Should this be PBAR5LMT / get_mw_size(, max_mw - 1)? */ ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, ntb_get_mw_size(ntb, 1) + 0x1000); /* Reserve the last MW for mapping remote spad */ ntb->limits.max_mw--; } else /* * Disable the limit register, just in case it is set to * something silly. A 64-bit write will also clear PBAR5LMT in * split-bar mode, and this is desired. */ ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0); ntb->reg_ofs.lnk_cntl = XEON_NTBCNTL_OFFSET; ntb->reg_ofs.lnk_stat = XEON_LINK_STATUS_OFFSET; ntb->reg_ofs.spci_cmd = XEON_PCICMD_OFFSET; ntb->limits.max_db_bits = XEON_MAX_DB_BITS; ntb->limits.msix_cnt = XEON_MSIX_CNT; ntb->bits_per_vector = XEON_DB_BITS_PER_VEC; /* * HW Errata on bit 14 of b2bdoorbell register. Writes will not be * mirrored to the remote system. Shrink the number of bits by one, * since bit 14 is the last bit. * * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register * anyway. Nor for non-B2B connection types. */ if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14) && !HAS_FEATURE(NTB_REGS_THRU_MW) && ntb->conn_type == NTB_CONN_B2B) ntb->limits.max_db_bits = XEON_MAX_DB_BITS - 1; configure_xeon_secondary_side_bars(ntb); /* Enable Bus Master and Memory Space on the secondary side */ if (ntb->conn_type == NTB_CONN_B2B) ntb_reg_write(2, ntb->reg_ofs.spci_cmd, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); /* Enable link training */ ntb_hw_link_up(ntb); return (0); }