diff --git a/SOURCES/facebookexperimental-diff-ffc9245-40fc652.patch b/SOURCES/facebookexperimental-diff-ffc9245-40fc652.patch new file mode 100644 index 0000000..ca43613 --- /dev/null +++ b/SOURCES/facebookexperimental-diff-ffc9245-40fc652.patch @@ -0,0 +1,6383 @@ +diff --git a/cligen/gen/base.cxl.c b/cligen/gen/base.cxl.c +index ee2d6b0..2ef95d1 100644 +--- a/cligen/gen/base.cxl.c ++++ b/cligen/gen/base.cxl.c +@@ -69,7 +69,7 @@ static struct cmd_struct commands[] = { + { "write-labels", .c_fn = cmd_write_labels }, + { "id-cmd", .c_fn = cmd_identify }, + { "get-supported-logs", .c_fn = cmd_get_supported_logs }, +- { "get-cel-log", .c_fn = cmd_get_cel_log }, ++ { "get-log", .c_fn = cmd_get_log }, + { "get-event-interrupt-policy", .c_fn = cmd_get_event_interrupt_policy }, + { "set-event-interrupt-policy", .c_fn = cmd_set_event_interrupt_policy }, + { "get-timestamp", .c_fn = cmd_get_timestamp }, +diff --git a/cligen/gen/base.memdev.c b/cligen/gen/base.memdev.c +index 56c3ebd..c5ec60e 100644 +--- a/cligen/gen/base.memdev.c ++++ b/cligen/gen/base.memdev.c +@@ -668,10 +668,10 @@ int cmd_get_supported_logs(int argc, const char **argv, struct cxl_ctx *ctx) + return rc >= 0 ? 0 : EXIT_FAILURE; + } + +-int cmd_get_cel_log(int argc, const char **argv, struct cxl_ctx *ctx) ++int cmd_get_log(int argc, const char **argv, struct cxl_ctx *ctx) + { +- int rc = memdev_action(argc, argv, ctx, action_cmd_get_cel_log, cmd_get_cel_log_options, +- "cxl get-cel-log [..] []"); ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_log, cmd_get_log_options, ++ "cxl get-log [..] []"); + + return rc >= 0 ? 0 : EXIT_FAILURE; + } +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 5775f4a..e66d027 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -17,7 +17,7 @@ int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_identify(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_get_supported_logs(int argc, const char **argv, struct cxl_ctx *ctx); +-int cmd_get_cel_log(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_log(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_get_event_interrupt_policy(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_set_event_interrupt_policy(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_get_timestamp(int argc, const char **argv, struct cxl_ctx *ctx); +@@ -116,4 +116,44 @@ int cmd_osa_data_read(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_dimm_spd_read(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_ddr_training_status(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_dimm_slot_info(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_pmic_vtmon_info(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_margin_run(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_margin_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_margin_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_stats_run(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_stats_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_reboot_mode_set(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_curr_cxl_boot_mode_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_pcie_eye_run(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_pcie_eye_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_pcie_eye_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_cxl_link_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_device_info(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_read_ddr_temp(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_cxl_hpa_to_dpa(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_cxl_membridge_errors(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_ddr_bw(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_ddr_latency(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_i2c_read(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_i2c_write(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_ddr_ecc_err_info(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_start_ddr_ecc_scrub(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_ecc_scrub_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_cont_scrub_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_cont_scrub_set(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_init_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_get_cxl_membridge_stats(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_trigger_coredump(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_err_inj_en(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_dimm_level_training_status(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_param_set(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_param_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_core_volt_set(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_core_volt_get(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_oem_err_inj_viral(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_err_inj_ll_poison(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_pci_err_inj(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_read_ltssm_states(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_page_select_set(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_ddr_page_select_get(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index 4b430b5..c44bb75 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -73,7 +73,7 @@ static struct cmd_struct commands[] = { + { "write-labels", .c_fn = cmd_write_labels }, + { "id-cmd", .c_fn = cmd_identify }, + { "get-supported-logs", .c_fn = cmd_get_supported_logs }, +- { "get-cel-log", .c_fn = cmd_get_cel_log }, ++ { "get-log", .c_fn = cmd_get_log }, + { "get-event-interrupt-policy", .c_fn = cmd_get_event_interrupt_policy }, + { "set-event-interrupt-policy", .c_fn = cmd_set_event_interrupt_policy }, + { "get-timestamp", .c_fn = cmd_get_timestamp }, +@@ -172,6 +172,46 @@ static struct cmd_struct commands[] = { + { "dimm-spd-read", .c_fn = cmd_dimm_spd_read }, + { "ddr-training-status", .c_fn = cmd_ddr_training_status }, + { "dimm-slot-info", .c_fn = cmd_dimm_slot_info }, ++ { "pmic-vtmon-info", .c_fn = cmd_pmic_vtmon_info }, ++ { "ddr-margin-run", .c_fn = cmd_ddr_margin_run }, ++ { "ddr-margin-status", .c_fn = cmd_ddr_margin_status }, ++ { "ddr-margin-get", .c_fn = cmd_ddr_margin_get }, ++ { "ddr-stats-run", .c_fn = cmd_ddr_stats_run }, ++ { "ddr-stats-get", .c_fn = cmd_ddr_stats_get }, ++ { "reboot-mode-set", .c_fn = cmd_reboot_mode_set }, ++ { "curr-cxl-boot-mode-get", .c_fn = cmd_curr_cxl_boot_mode_get }, ++ { "pcie-eye-run", .c_fn = cmd_pcie_eye_run }, ++ { "pcie-eye-status", .c_fn = cmd_pcie_eye_status }, ++ { "pcie-eye-get", .c_fn = cmd_pcie_eye_get }, ++ { "get-cxl-link-status", .c_fn = cmd_get_cxl_link_status }, ++ { "get-device-info", .c_fn = cmd_get_device_info }, ++ { "read-ddr-temp", .c_fn = cmd_read_ddr_temp }, ++ { "cxl-hpa-to-dpa", .c_fn = cmd_cxl_hpa_to_dpa }, ++ { "get-cxl-membridge-errors", .c_fn = cmd_get_cxl_membridge_errors }, ++ { "get-ddr-bw", .c_fn = cmd_get_ddr_bw }, ++ { "get-ddr-latency", .c_fn = cmd_get_ddr_latency }, ++ { "i2c-read", .c_fn = cmd_i2c_read }, ++ { "i2c-write", .c_fn = cmd_i2c_write }, ++ { "get-ddr-ecc-err-info", .c_fn = cmd_get_ddr_ecc_err_info }, ++ { "start-ddr-ecc-scrub", .c_fn = cmd_start_ddr_ecc_scrub }, ++ { "ddr-ecc-scrub-status", .c_fn = cmd_ddr_ecc_scrub_status }, ++ { "ddr-cont-scrub-status", .c_fn = cmd_ddr_cont_scrub_status }, ++ { "ddr-cont-scrub-set", .c_fn = cmd_ddr_cont_scrub_set }, ++ { "ddr-init-status", .c_fn = cmd_ddr_init_status }, ++ { "get-cxl-membridge-stats", .c_fn = cmd_get_cxl_membridge_stats }, ++ { "trigger-coredump", .c_fn = cmd_trigger_coredump }, ++ { "ddr-err-inj-en", .c_fn = cmd_ddr_err_inj_en }, ++ { "ddr-dimm-level-training-status", .c_fn = cmd_ddr_dimm_level_training_status }, ++ { "ddr-param-set", .c_fn = cmd_ddr_param_set }, ++ { "ddr-param-get", .c_fn = cmd_ddr_param_get }, ++ { "core-volt-set", .c_fn = cmd_core_volt_set }, ++ { "core-volt-get", .c_fn = cmd_core_volt_get }, ++ { "oem-err-inj-viral", .c_fn = cmd_oem_err_inj_viral }, ++ { "err-inj-ll-poison", .c_fn = cmd_err_inj_ll_poison }, ++ { "pci-err-inj", .c_fn = cmd_pci_err_inj }, ++ { "read-ltssm-states", .c_fn = cmd_read_ltssm_states }, ++ { "ddr-page-select-set", .c_fn = cmd_ddr_page_select_set }, ++ { "ddr-page-select-get", .c_fn = cmd_ddr_page_select_get }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index ba265ed..6fcb20e 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1471,6 +1471,7 @@ out: + } + + #define CEL_UUID "0da9c0b5-bf41-4b78-8f79-96b1623b3f17" ++#define VENDOR_LOG_UUID "5e1819d9-11a9-400c-811f-d60719403d86" + + struct cxl_mbox_get_log { + uuid_t uuid; +@@ -1483,13 +1484,15 @@ struct cel_entry { + __le16 effect; + } __attribute__((packed)); + +-CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev, const char* uuid) ++CXL_EXPORT int cxl_memdev_get_log(struct cxl_memdev *memdev, const char* uuid, const unsigned int data_size) + { + struct cxl_cmd *cmd; + struct cxl_mbox_get_log *get_log_input; + struct cel_entry *cel_entries; + int no_cel_entries; + int rc = 0; ++ int remaining_bytes = data_size; ++ unsigned int bytes_read = 0; + + if (!uuid) { + fprintf(stderr, "%s: Please specify log uuid argument\n", +@@ -1497,49 +1500,63 @@ CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev, const char* uui + return -EINVAL; + } + +- cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_LOG); +- if (!cmd) { +- fprintf(stderr, "%s: cxl_memdev_get_cel_log returned Null output\n", +- cxl_memdev_get_devname(memdev)); +- return -ENOMEM; +- } ++ do { ++ cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_LOG); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_memdev_get_log returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } + +- get_log_input = (void *) cmd->send_cmd->in.payload; +- uuid_parse(uuid, get_log_input->uuid); +- get_log_input->offset = 0; +- get_log_input->length = cmd->memdev->payload_max; ++ get_log_input = (void *) cmd->send_cmd->in.payload; ++ uuid_parse(uuid, get_log_input->uuid); ++ get_log_input->offset = bytes_read; ++ get_log_input->length = cmd->memdev->payload_max; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } + +- rc = cxl_cmd_submit(cmd); +- if (rc < 0) { +- fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", +- cxl_memdev_get_devname(memdev), rc, strerror(-rc)); +- goto out; +- } ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } + +- rc = cxl_cmd_get_mbox_status(cmd); +- if (rc != 0) { +- fprintf(stderr, "%s: firmware status: %d:\n%s\n", +- cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); +- rc = -ENXIO; +- goto out; +- } ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_LOG) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_LOG); ++ return -EINVAL; ++ } + +- if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_LOG) { +- fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", +- cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_LOG); +- return -EINVAL; +- } ++ fprintf(stdout, "payload info\n"); ++ fprintf(stdout, " out size: 0x%x\n", cmd->send_cmd->out.size); ++ ++ if (!strcmp(uuid, CEL_UUID)) { ++ cel_entries = (void *)cmd->send_cmd->out.payload; ++ no_cel_entries = (cmd->send_cmd->out.size)/sizeof(struct cel_entry); ++ fprintf(stdout, " no_cel_entries size: %d\n", no_cel_entries); ++ for (int e = 0; e < no_cel_entries; ++e) { ++ fprintf(stdout, " cel_entry[%d] opcode: 0x%x, effect: 0x%x\n", e, ++ le16_to_cpu(cel_entries[e].opcode), ++ le16_to_cpu(cel_entries[e].effect)); ++ } ++ } else if (!strcmp(uuid, VENDOR_LOG_UUID)) { ++ fprintf(stdout, " number of received bytes = %d\n", cmd->send_cmd->out.size); ++ fprintf(stdout, "%s", (char *)cmd->send_cmd->out.payload); ++ } + +- fprintf(stdout, "payload info\n"); +- fprintf(stdout, " out size: 0x%x\n", cmd->send_cmd->out.size); +- cel_entries = (void *)cmd->send_cmd->out.payload; +- no_cel_entries = (cmd->send_cmd->out.size)/sizeof(struct cel_entry); +- fprintf(stdout, " no_cel_entries size: %d\n", no_cel_entries); +- for (int e = 0; e < no_cel_entries; ++e) { +- fprintf(stdout, " cel_entry[%d] opcode: 0x%x, effect: 0x%x\n", e, +- le16_to_cpu(cel_entries[e].opcode), +- le16_to_cpu(cel_entries[e].effect)); +- } ++ /* keep getting the data in chunks of payload max */ ++ bytes_read += cmd->send_cmd->out.size; ++ if (remaining_bytes >= cmd->send_cmd->out.size) ++ remaining_bytes -= cmd->send_cmd->out.size; ++ else ++ remaining_bytes = 0; ++ } while(remaining_bytes); + out: + cxl_cmd_unref(cmd); + return rc; +@@ -2342,6 +2359,7 @@ out: + + #define CXL_MEM_COMMAND_ID_GET_FW_INFO CXL_MEM_COMMAND_ID_RAW + #define CXL_MEM_COMMAND_ID_GET_FW_INFO_OPCODE 512 ++#define CXL_MEM_COMMAND_ID_GET_OS_INFO_OPCODE 0xcd03 + #define CXL_MEM_COMMAND_ID_GET_FW_INFO_PAYLOAD_OUT_SIZE 80 + + +@@ -2356,7 +2374,7 @@ struct cxl_mbox_get_fw_info_out { + char slot_4_fw_rev[16]; + } __attribute__((packed)); + +-CXL_EXPORT int cxl_memdev_get_fw_info(struct cxl_memdev *memdev) ++CXL_EXPORT int cxl_memdev_get_fw_info(struct cxl_memdev *memdev, bool is_os_img) + { + struct cxl_cmd *cmd; + struct cxl_mbox_get_fw_info_out *get_fw_info_out; +@@ -2365,8 +2383,19 @@ CXL_EXPORT int cxl_memdev_get_fw_info(struct cxl_memdev *memdev) + u8 active_slot; + u8 staged_slot_mask; + u8 staged_slot; ++ int opcode; ++ char *fw_name; ++ ++ //select vendor cci command if os image is specified, else default to cxl FW_INFO command ++ if (is_os_img) { ++ opcode = CXL_MEM_COMMAND_ID_GET_OS_INFO_OPCODE; ++ fw_name = "OS"; ++ } else { ++ opcode = CXL_MEM_COMMAND_ID_GET_FW_INFO_OPCODE; ++ fw_name = "FW"; ++ } + +- cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_FW_INFO_OPCODE); ++ cmd = cxl_cmd_new_raw(memdev, opcode); + if (!cmd) { + fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", + cxl_memdev_get_devname(memdev)); +@@ -2401,17 +2430,17 @@ CXL_EXPORT int cxl_memdev_get_fw_info(struct cxl_memdev *memdev) + staged_slot = get_fw_info_out->fw_slot_info & staged_slot_mask; + staged_slot = staged_slot>>3; + fprintf(stdout, "================================= get fw info ==================================\n"); +- fprintf(stdout, "FW Slots Supported: %x\n", get_fw_info_out->fw_slots_supp); +- fprintf(stdout, "Active FW Slot: %x\n", active_slot); ++ fprintf(stdout, "%s Slots Supported: %x\n", fw_name, get_fw_info_out->fw_slots_supp); ++ fprintf(stdout, "Active %s Slot: %x\n", fw_name, active_slot); + if (staged_slot) + { +- fprintf(stdout, "Staged FW Slot: %x\n", staged_slot); ++ fprintf(stdout, "Staged %s Slot: %x\n", fw_name, staged_slot); + } +- fprintf(stdout, "FW Activation Capabilities: %x\n", get_fw_info_out->fw_activation_capas); +- fprintf(stdout, "Slot 1 FW Revision: %s\n", get_fw_info_out->slot_1_fw_rev); +- fprintf(stdout, "Slot 2 FW Revision: %s\n", get_fw_info_out->slot_2_fw_rev); +- fprintf(stdout, "Slot 3 FW Revision: %s\n", get_fw_info_out->slot_3_fw_rev); +- fprintf(stdout, "Slot 4 FW Revision: %s\n", get_fw_info_out->slot_4_fw_rev); ++ fprintf(stdout, "%s Activation Capabilities: %x\n", fw_name, get_fw_info_out->fw_activation_capas); ++ fprintf(stdout, "Slot 1 %s Revision: %s\n", fw_name, get_fw_info_out->slot_1_fw_rev); ++ fprintf(stdout, "Slot 2 %s Revision: %s\n", fw_name, get_fw_info_out->slot_2_fw_rev); ++ fprintf(stdout, "Slot 3 %s Revision: %s\n", fw_name, get_fw_info_out->slot_3_fw_rev); ++ fprintf(stdout, "Slot 4 %s Revision: %s\n", fw_name, get_fw_info_out->slot_4_fw_rev); + + out: + cxl_cmd_unref(cmd); +@@ -6301,8 +6330,8 @@ struct cxl_mbox_health_counters_get_out { + __le32 cxl_mem_link_crc_errors; + __le32 cxl_io_link_lcrc_errors; + __le32 cxl_io_link_ecrc_errors; +- __le32 num_ddr_single_ecc_errors; +- __le32 num_ddr_double_ecc_errors; ++ __le32 num_ddr_correctable_ecc_errors; ++ __le32 num_ddr_uncorrectable_ecc_errors; + __le32 link_recovery_events; + __le32 time_in_throttled; + __le32 over_temperature_warning_level_exceeded; +@@ -6311,6 +6340,16 @@ struct cxl_mbox_health_counters_get_out { + __le32 rx_retry_request; + __le32 rcmd_qs0_hi_threshold_detect; + __le32 rcmd_qs1_hi_threshold_detect; ++ __le32 num_pscan_correctable_ecc_errors; ++ __le32 num_pscan_uncorrectable_ecc_errors; ++ __le32 num_ddr_dimm0_correctable_ecc_errors; ++ __le32 num_ddr_dimm0_uncorrectable_ecc_errors; ++ __le32 num_ddr_dimm1_correctable_ecc_errors; ++ __le32 num_ddr_dimm1_uncorrectable_ecc_errors; ++ __le32 num_ddr_dimm2_correctable_ecc_errors; ++ __le32 num_ddr_dimm2_uncorrectable_ecc_errors; ++ __le32 num_ddr_dimm3_correctable_ecc_errors; ++ __le32 num_ddr_dimm3_uncorrectable_ecc_errors; + } __attribute__((packed)); + + CXL_EXPORT int cxl_memdev_health_counters_get(struct cxl_memdev *memdev) +@@ -6358,14 +6397,23 @@ CXL_EXPORT int cxl_memdev_health_counters_get(struct cxl_memdev *memdev) + fprintf(stdout, "6: CXL_MEM_LINK_CRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_mem_link_crc_errors)); + fprintf(stdout, "7: CXL_IO_LINK_LCRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_lcrc_errors)); + fprintf(stdout, "8: CXL_IO_LINK_ECRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_ecrc_errors)); +- fprintf(stdout, "9: NUM_DDR_SINGLE_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_single_ecc_errors)); +- fprintf(stdout, "10: NUM_DDR_DOUBLE_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_double_ecc_errors)); ++ fprintf(stdout, "9: NUM_DDR_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_correctable_ecc_errors)); ++ fprintf(stdout, "10: NUM_DDR_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_uncorrectable_ecc_errors)); + fprintf(stdout, "11: LINK_RECOVERY_EVENTS = %d\n", le32_to_cpu(health_counters_get_out->link_recovery_events)); + fprintf(stdout, "12: TIME_IN_THROTTLED = %d\n", le32_to_cpu(health_counters_get_out->time_in_throttled)); + fprintf(stdout, "13: RX_RETRY_REQUEST = %d\n", le32_to_cpu(health_counters_get_out->rx_retry_request)); + fprintf(stdout, "14: RCMD_QS0_HI_THRESHOLD_DETECT = %d\n", le32_to_cpu(health_counters_get_out->rcmd_qs0_hi_threshold_detect)); + fprintf(stdout, "15: RCMD_QS1_HI_THRESHOLD_DETECT = %d\n", le32_to_cpu(health_counters_get_out->rcmd_qs1_hi_threshold_detect)); +- ++ fprintf(stdout, "16: NUM_PSCAN_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_pscan_correctable_ecc_errors)); ++ fprintf(stdout, "17: NUM_PSCAN_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_pscan_uncorrectable_ecc_errors)); ++ fprintf(stdout, "18: NUM_DDR_DIMM0_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm0_correctable_ecc_errors)); ++ fprintf(stdout, "19: NUM_DDR_DIMM0_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm0_uncorrectable_ecc_errors)); ++ fprintf(stdout, "20: NUM_DDR_DIMM1_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm1_correctable_ecc_errors)); ++ fprintf(stdout, "21: NUM_DDR_DIMM1_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm1_uncorrectable_ecc_errors)); ++ fprintf(stdout, "22: NUM_DDR_DIMM2_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm2_correctable_ecc_errors)); ++ fprintf(stdout, "23: NUM_DDR_DIMM2_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm2_uncorrectable_ecc_errors)); ++ fprintf(stdout, "24: NUM_DDR_DIMM3_COR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm3_correctable_ecc_errors)); ++ fprintf(stdout, "25: NUM_DDR_DIMM3_UNCOR_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_dimm3_uncorrectable_ecc_errors)); + out: + cxl_cmd_unref(cmd); + return rc; +@@ -10123,3 +10171,4373 @@ out: + cxl_cmd_unref(cmd); + return rc; + } ++ ++#define MAX_PMIC 8 ++#define PMIC_NAME_MAX_SIZE 20 ++ ++struct pmic_data { ++ char pmic_name[PMIC_NAME_MAX_SIZE]; ++ float vin; ++ float vout; ++ float iout; ++ float powr; ++ float temp; ++}; ++struct cxl_pmic_vtmon_info_out { ++ struct pmic_data pmic_data[MAX_PMIC]; ++} __attribute__((packed)); ++ ++#define CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO_OPCODE 0xFB00 ++#define CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO_PAYLOAD_IN_SIZE 0 ++ ++CXL_EXPORT int cxl_memdev_pmic_vtmon_info(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_pmic_vtmon_info_out *pmic_vtmon_info; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_PMIC_VTMON_INFO); ++ return -EINVAL; ++ } ++ ++ pmic_vtmon_info = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "=========================== PMIC VTMON SLOT INFO ============================\n"); ++ for (int i = 0; i < MAX_PMIC; i++) { ++ fprintf(stdout, "pmic name: %s\n", pmic_vtmon_info->pmic_data[i].pmic_name); ++ fprintf(stdout, "vin: %f\n", pmic_vtmon_info->pmic_data[i].vin); ++ fprintf(stdout, "vout: %f\n", pmic_vtmon_info->pmic_data[i].vout); ++ fprintf(stdout, "iout: %f\n", pmic_vtmon_info->pmic_data[i].iout); ++ fprintf(stdout, "powr: %f\n", pmic_vtmon_info->pmic_data[i].powr); ++ fprintf(stdout, "temp: %f\n", pmic_vtmon_info->pmic_data[i].temp); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR MARGIN */ ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN_OPCODE 0xFB0A ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN_PAYLOAD_IN_SIZE 4 ++ ++struct cxl_mbox_ddr_margin_run_in { ++ u8 slice_num; ++ u8 rd_wr_margin; ++ u8 ddr_id; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_margin_run(struct cxl_memdev *memdev, ++ u8 slice_num, u8 rd_wr_margin, u8 ddr_id) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_ddr_margin_run_in *ddr_margin_run_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_margin_run_in = (void *) cmd->send_cmd->in.payload; ++ ++ ddr_margin_run_in->slice_num = slice_num; ++ ddr_margin_run_in->rd_wr_margin = rd_wr_margin; ++ ddr_margin_run_in->ddr_id = ddr_id; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_RUN); ++ return -EINVAL; ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_STATUS_OPCODE 0xFB0B ++ ++struct cxl_ddr_margin_status_out { ++ int run_status; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_margin_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_margin_status_out *ddr_margin_status_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_MARGIN_SW_STATUS); ++ return -EINVAL; ++ } ++ ddr_margin_status_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "%s\n", ddr_margin_status_out->run_status ? ++ "DDR MARGIN IS RUNNING" : "DDR MARGIN IS NOT RUNNING/FINISHED"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_GET_SW CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_MARGIN_GET_SW_OPCODE 0xFB0C ++ ++/* MAX_NUM_ROWS per BIT_COUNT should be in Sync with the FW Mbox DDR MARGIN code */ ++#define MAX_NUM_ROWS 1024 ++#define MAX_MARGIN_BIT_COUNT 8 ++ ++struct ddr_margin_info ++{ ++ uint32_t slicenumber; ++ uint32_t bitnumber; ++ int32_t vreflevel; ++ int margin_low; ++ int margin_high; ++ double min_delay_ps; ++ double max_delay_ps; ++} __attribute__((packed));; ++ ++struct cxl_ddr_margin_get_sw_out ++{ ++ uint32_t row_count; ++ struct ddr_margin_info ddr_margin_slice_data[MAX_NUM_ROWS * MAX_MARGIN_BIT_COUNT]; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_margin_get(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_margin_get_sw_out *ddr_margin_get_sw_out; ++ int rc = 0; ++ uint32_t i; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_MARGIN_GET_SW_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_MARGIN_GET_SW) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_MARGIN_GET_SW); ++ return -EINVAL; ++ } ++ ddr_margin_get_sw_out = (struct cxl_ddr_margin_get_sw_out *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "SliceNo,bitNo, VrefLv, MinDelay, MaxDelay, MinDly(ps), MaxDly(ps)\n"); ++ ++ for(i = 0; i < ddr_margin_get_sw_out->row_count; i++) ++ { ++ fprintf(stdout, "%d,%d,%d,%d,%d,%3.2f,%3.2f\n", ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].slicenumber, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].bitnumber, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].vreflevel, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].margin_low, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].margin_high, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].min_delay_ps, ++ ddr_margin_get_sw_out->ddr_margin_slice_data[i].max_delay_ps); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR STATS START */ ++#define CXL_MEM_COMMAND_ID_DDR_STATS_RUN CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_STATS_RUN_OPCODE 0xFB1B ++#define CXL_MEM_COMMAND_ID_DDR_STATS_RUN_PAYLOAD_IN_SIZE 9 ++ ++struct cxl_mbox_ddr_stats_run_in { ++ uint8_t ddr_id; ++ uint32_t monitor_time; ++ uint32_t loop_count; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_stats_run(struct cxl_memdev *memdev, ++ u8 ddr_id, uint32_t monitor_time, uint32_t loop_count) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_ddr_stats_run_in *ddr_stats_run_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_STATS_RUN_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_STATS_RUN_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_stats_run_in = (void *) cmd->send_cmd->in.payload; ++ ++ ddr_stats_run_in->ddr_id = ddr_id; ++ ddr_stats_run_in->monitor_time = monitor_time; ++ ddr_stats_run_in->loop_count = loop_count; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_STATS_RUN) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_STATS_RUN); ++ return -EINVAL; ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_STATS_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_STATS_STATUS_OPCODE 0xFB1C ++ ++struct cxl_ddr_stats_status_out { ++ int run_status; ++ uint32_t loop_count; ++} __attribute__((packed)); ++ ++/* DDR STATS STATUS */ ++CXL_EXPORT int cxl_memdev_ddr_stats_status(struct cxl_memdev *memdev, int* run_status, uint32_t* loop_count) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_stats_status_out *ddr_stats_status_out; ++ ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_STATS_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_STATS_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_STATS_STATUS); ++ return -EINVAL; ++ } ++ ddr_stats_status_out = (void *)cmd->send_cmd->out.payload; ++ *run_status = ddr_stats_status_out->run_status; ++ *loop_count = ddr_stats_status_out->loop_count; ++ ++ fprintf(stdout, "%s\n", ddr_stats_status_out->run_status ? ++ "DDR STATS IS BUSY" : "DDR STATS IS NOT RUNNING/FINISHED"); ++ ++ fprintf(stdout, "Loop Count = %d\n", ddr_stats_status_out->loop_count); ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_STATS_GET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_STATS_GET_OPCODE 0xFB1D ++ ++#define NUM_BANK 16 ++#define NUM_CS 4 ++ ++struct dfi_cs_pm { ++ uint32_t mrw_cnt; ++ uint32_t refresh_cnt; ++ uint32_t act_cnt; ++ uint32_t write_cnt; ++ uint32_t read_cnt; ++ uint32_t pre_cnt; ++ uint32_t rr_cnt; ++ uint32_t ww_cnt; ++ uint32_t rw_cnt; ++} __attribute__((packed)); ++ ++struct dfi_cs_bank_pm { ++ uint32_t bank_act_cnt; ++ uint32_t bank_wr_cnt; ++ uint32_t bank_rd_cnt; ++ uint32_t bank_pre_cnt; ++} __attribute__((packed)); ++ ++struct dfi_mc_pm { ++ uint32_t cmd_queue_full_events; ++ uint32_t info_fifo_full_events; ++ uint32_t wrdata_hold_fifo_full_events; ++ uint32_t port_cmd_fifo0_full_events; ++ uint32_t port_wrresp_fifo0_full_events; ++ uint32_t port_wr_fifo0_full_events; ++ uint32_t port_rd_fifo0_full_events; ++ uint32_t port_cmd_fifo1_full_events; ++ uint32_t port_wrresp_fifo1_full_events; ++ uint32_t port_wr_fifo1_full_events; ++ uint32_t port_rd_fifo1_full_events; ++ uint32_t ecc_dataout_corrected; ++ uint32_t ecc_dataout_uncorrected; ++ uint32_t pd_ex; ++ uint32_t pd_en; ++ uint32_t srex; ++ uint32_t sren; ++ uint32_t write; ++ uint32_t read; ++ uint32_t rmw; ++ uint32_t bank_act; ++ uint32_t precharge; ++ uint32_t precharge_all; ++ uint32_t mrw; ++ uint32_t auto_ref; ++ uint32_t rw_auto_pre; ++ uint32_t zq_cal_short; ++ uint32_t zq_cal_long; ++ uint32_t same_addr_ww_collision; ++ uint32_t same_addr_wr_collision; ++ uint32_t same_addr_rw_collision; ++ uint32_t same_addr_rr_collision; ++} __attribute__((packed)); ++ ++struct ddr_pmon_data { ++ uint64_t fr_cnt; ++ uint32_t idle_cnt; ++ uint32_t rd_ot_cnt; ++ uint32_t wr_ot_cnt; ++ uint32_t wrd_ot_cnt; ++ uint32_t rd_cmd_cnt; ++ uint32_t rd_cmd_busy_cnt; ++ uint32_t wr_cmd_cnt; ++ uint32_t wr_cmd_busy_cnt; ++ uint32_t rd_data_cnt; ++ uint32_t rd_data_busy_cnt; ++ uint32_t wr_data_cnt; ++ uint32_t wr_data_busy_cnt; ++ uint64_t rd_avg_lat; ++ uint64_t wr_avg_lat; ++ uint32_t rd_trans_smpl_cnt; ++ uint32_t wr_trans_smpl_cnt; ++} __attribute__((packed)); ++ ++struct ddr_data { ++ struct ddr_pmon_data pmon; ++ struct dfi_cs_pm cs_pm[NUM_CS]; ++ struct dfi_cs_bank_pm cs_bank_pm[NUM_CS][NUM_BANK]; ++ struct dfi_mc_pm mc_pm; ++} __attribute__((packed)); ++ ++struct ddr_stats_data { ++ struct ddr_data stats; ++} __attribute__((packed)); ++ ++typedef struct ddr_stats_data ddr_stats_data_t; ++ ++#define MAX_CXL_TRANSFER_SZ (16 * 1024) ++ ++static void display_pmon_stats(ddr_stats_data_t* disp_stats, uint32_t loop_count) { ++ uint32_t loop; ++ fprintf(stderr,"PMON STATS:\n"); ++ fprintf(stderr, ++ "iteration, fr_cnt, idle_cnt, rd_ot_cnt, wr_ot_cnt, wrd_ot_cnt, " ++ "rd_cmd_cnt, rd_cmd_busy_cnt, wr_cmd_cnt, wr_cmd_busy_cnt, rd_data_cnt, " ++ "rd_data_busy_cnt, wr_data_cnt, wr_data_busy_cnt, " ++ "rd_avg_lat, wr_avg_lat, rd_trans_smpl_cnt, wr_trans_smpl_cnt\n"); ++ for (loop = 0; loop < loop_count; loop++) { ++ fprintf(stderr, ++ "[%d], %lu, %u, %u, %u, %u, " ++ "%u, %u, %u, %u, %u, " ++ "%u, %u, %u, " ++ "%lu, %lu, %u, %u\n", ++ loop, ++ disp_stats->stats.pmon.fr_cnt, ++ disp_stats->stats.pmon.idle_cnt, ++ disp_stats->stats.pmon.rd_ot_cnt, ++ disp_stats->stats.pmon.wr_ot_cnt, ++ disp_stats->stats.pmon.wrd_ot_cnt, ++ disp_stats->stats.pmon.rd_cmd_cnt, ++ disp_stats->stats.pmon.rd_cmd_busy_cnt, ++ disp_stats->stats.pmon.wr_cmd_cnt, ++ disp_stats->stats.pmon.wr_cmd_busy_cnt, ++ disp_stats->stats.pmon.rd_data_cnt, ++ disp_stats->stats.pmon.rd_data_busy_cnt, ++ disp_stats->stats.pmon.wr_data_cnt, ++ disp_stats->stats.pmon.wr_data_busy_cnt, ++ disp_stats->stats.pmon.rd_avg_lat, ++ disp_stats->stats.pmon.wr_avg_lat, ++ disp_stats->stats.pmon.rd_trans_smpl_cnt, ++ disp_stats->stats.pmon.wr_trans_smpl_cnt); ++ disp_stats++; ++ } ++ fprintf(stderr,"\n"); ++} ++ ++static void display_cs_pm_stats(ddr_stats_data_t* disp_stats, uint32_t loop_count) { ++ uint32_t rank, loop; ++ ++ fprintf(stderr, "CS PM STATS:\n"); ++ fprintf(stderr, ++ "iteration, rank, mrw_cnt, refresh_cnt, act_cnt, write_cnt, " ++ "read_cnt, pre_cnt, rr_cnt, ww_cnt, rw_cnt\n "); ++ ++ for (loop = 0; loop < loop_count; loop++) { ++ for (rank = 0; rank < NUM_CS; rank++) { ++ fprintf(stderr, ++ "[%d], %d, %u, %u, %u, %u, " ++ "%u, %u, %u, %u, %u\n", ++ loop, ++ rank, ++ disp_stats->stats.cs_pm[rank].mrw_cnt, ++ disp_stats->stats.cs_pm[rank].refresh_cnt, ++ disp_stats->stats.cs_pm[rank].act_cnt, ++ disp_stats->stats.cs_pm[rank].write_cnt, ++ disp_stats->stats.cs_pm[rank].read_cnt, ++ disp_stats->stats.cs_pm[rank].pre_cnt, ++ disp_stats->stats.cs_pm[rank].rr_cnt, ++ disp_stats->stats.cs_pm[rank].ww_cnt, ++ disp_stats->stats.cs_pm[rank].rw_cnt); ++ } ++ disp_stats++; ++ } ++ fprintf(stderr, "\n"); ++} ++ ++static void display_cs_bank_pm_stats(ddr_stats_data_t* disp_stats, uint32_t loop_count) { ++ uint32_t rank, bank, loop; ++ ++ fprintf(stderr, "CS BANK STATS:\n"); ++ fprintf(stderr, ++ "iteration, rank, bank, bank_act_cnt, bank_wr_cnt, bank_rd_cnt, bank_pre_cnt\n"); ++ for (loop = 0; loop < loop_count; loop++) { ++ for (rank = 0; rank < NUM_CS; rank++) { ++ for (bank = 0; bank < NUM_BANK; bank++) { ++ fprintf(stderr, ++ "[%d], %d, %d, %u, %u, %u, %u\n", ++ loop, ++ rank, ++ bank, ++ disp_stats->stats.cs_bank_pm[rank][bank].bank_act_cnt, ++ disp_stats->stats.cs_bank_pm[rank][bank].bank_wr_cnt, ++ disp_stats->stats.cs_bank_pm[rank][bank].bank_rd_cnt, ++ disp_stats->stats.cs_bank_pm[rank][bank].bank_pre_cnt); ++ } ++ } ++ disp_stats++; ++ } ++ fprintf(stderr, "\n"); ++} ++ ++static void display_mc_pm_stats(ddr_stats_data_t* disp_stats, uint32_t loop_count) { ++ uint32_t loop; ++ ++ fprintf(stderr, "PM STATS:\n"); ++ fprintf(stderr, ++ "iteration, cmd_queue_full_events, info_fifo_full_events, " ++ "wrdata_hold_fifo_full_events, port_cmd_fifo0_full_events, " ++ "port_wrresp_fifo0_full_events, port_wr_fifo0_full_events, " ++ "port_rd_fifo0_full_events, port_cmd_fifo1_full_events, " ++ "port_wrresp_fifo1_full_events, port_wr_fifo1_full_events, " ++ "port_rd_fifo1_full_events, ecc_dataout_corrected, " ++ "ecc_dataout_uncorrected, pd_ex, pd_en, srex, sren, " ++ "write, read, rmw, bank_act, precharge, precharge_all, " ++ "mrw, auto_ref, rw_auto_pre, zq_cal_short, zq_cal_long, " ++ "same_addr_ww_collision, same_addr_wr_collision, " ++ "same_addr_rw_collision, same_addr_rr_collision\n"); ++ ++ for (loop = 0; loop < loop_count; loop++) { ++ fprintf(stderr, ++ "[%d], %u, %u, " ++ "%u, %u, " ++ "%u, %u, " ++ "%u, %u, " ++ "%u, %u, " ++ "%u, %u, " ++ "%u, %u, %u, %u, %u," ++ "%u, %u, %u, %u, %u, %u," ++ "%u, %u, %u, %u, %u," ++ "%u, %u, " ++ "%u, %u\n", ++ loop, ++ disp_stats->stats.mc_pm.cmd_queue_full_events, ++ disp_stats->stats.mc_pm.info_fifo_full_events, ++ disp_stats->stats.mc_pm.wrdata_hold_fifo_full_events, ++ disp_stats->stats.mc_pm.port_cmd_fifo0_full_events, ++ disp_stats->stats.mc_pm.port_wrresp_fifo0_full_events, ++ disp_stats->stats.mc_pm.port_wr_fifo0_full_events, ++ disp_stats->stats.mc_pm.port_rd_fifo0_full_events, ++ disp_stats->stats.mc_pm.port_cmd_fifo1_full_events, ++ disp_stats->stats.mc_pm.port_wrresp_fifo1_full_events, ++ disp_stats->stats.mc_pm.port_wr_fifo1_full_events, ++ disp_stats->stats.mc_pm.port_rd_fifo1_full_events, ++ disp_stats->stats.mc_pm.ecc_dataout_corrected, ++ disp_stats->stats.mc_pm.ecc_dataout_uncorrected, ++ disp_stats->stats.mc_pm.pd_ex, ++ disp_stats->stats.mc_pm.pd_en, ++ disp_stats->stats.mc_pm.srex, ++ disp_stats->stats.mc_pm.sren, ++ disp_stats->stats.mc_pm.write, ++ disp_stats->stats.mc_pm.read, ++ disp_stats->stats.mc_pm.rmw, ++ disp_stats->stats.mc_pm.bank_act, ++ disp_stats->stats.mc_pm.precharge, ++ disp_stats->stats.mc_pm.precharge_all, ++ disp_stats->stats.mc_pm.mrw, ++ disp_stats->stats.mc_pm.auto_ref, ++ disp_stats->stats.mc_pm.rw_auto_pre, ++ disp_stats->stats.mc_pm.zq_cal_short, ++ disp_stats->stats.mc_pm.zq_cal_long, ++ disp_stats->stats.mc_pm.same_addr_ww_collision, ++ disp_stats->stats.mc_pm.same_addr_wr_collision, ++ disp_stats->stats.mc_pm.same_addr_rw_collision, ++ disp_stats->stats.mc_pm.same_addr_rr_collision); ++ disp_stats++; ++ } ++ fprintf(stderr, "\n"); ++} ++ ++struct cxl_ddr_stats_get_in { ++ uint32_t offset; ++ uint32_t transfer_sz; ++} __attribute__((packed)); ++ ++#define CXL_MEM_COMMAND_ID_DDR_STATS_GET_PAYLOAD_IN_SIZE 8 ++ ++static int cxl_ddr_stats_get(struct cxl_memdev *memdev, unsigned char *dst, int offset, int bytes_to_cpy) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_stats_get_in *ddr_stats_get_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_STATS_GET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_STATS_GET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_stats_get_in = (void *) cmd->send_cmd->in.payload; ++ ddr_stats_get_in->offset = offset; ++ ddr_stats_get_in->transfer_sz = bytes_to_cpy; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_STATS_GET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_STATS_GET); ++ return -EINVAL; ++ } ++ ++ memcpy(dst, (unsigned char*) cmd->send_cmd->out.payload, cmd->send_cmd->out.size); ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR GET STATS */ ++CXL_EXPORT int cxl_memdev_ddr_stats_get(struct cxl_memdev *memdev) ++{ ++ unsigned char *buf; ++ int total_bytes = 0, bytes_to_cpy = 0, bytes_copied = 0; ++ ddr_stats_data_t* ddr_stats_start; ++ int rc = 0; ++ int run_status; ++ uint32_t loop_count; ++ ++ rc = cxl_memdev_ddr_stats_status(memdev, &run_status, &loop_count); ++ if (rc < 0) ++ return rc; ++ ++ if (run_status) ++ return -EBUSY; ++ ++ total_bytes = sizeof(ddr_stats_data_t) * loop_count; ++ ++ buf =(unsigned char *)malloc(total_bytes); ++ ddr_stats_start = (ddr_stats_data_t*)buf; ++ ++ while(bytes_copied < total_bytes) ++ { ++ bytes_to_cpy = (total_bytes - bytes_copied) < MAX_CXL_TRANSFER_SZ ? ++ (total_bytes - bytes_copied) : MAX_CXL_TRANSFER_SZ; ++ rc = cxl_ddr_stats_get(memdev, buf + bytes_copied, bytes_copied, bytes_to_cpy); ++ bytes_copied = bytes_copied + bytes_to_cpy; ++ if (rc < 0) ++ goto out; ++ } ++ ++ display_pmon_stats(ddr_stats_start, loop_count); ++ display_cs_pm_stats(ddr_stats_start, loop_count); ++ display_cs_bank_pm_stats(ddr_stats_start, loop_count); ++ display_mc_pm_stats(ddr_stats_start, loop_count); ++out: ++ free(buf); ++ return rc; ++} ++ ++/* REBOOT MODE SET */ ++#define CXL_MEM_COMMAND_ID_REBOOT_MODE_SET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_REBOOT_MODE_SET_OPCODE 0xFB0D ++#define CXL_MEM_COMMAND_ID_REBOOT_MODE_SET_PAYLOAD_IN_SIZE 4 ++ ++#define CXL_IO_MEM_MODE 0x0 ++#define CXL_IO_MODE 0xCE ++ ++struct cxl_mbox_reboot_mode_set_in { ++ u8 reboot_mode; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_reboot_mode_set(struct cxl_memdev *memdev, u8 reboot_mode) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_reboot_mode_set_in *reboot_mode_set_in; ++ int rc = 0; ++ ++ if ((reboot_mode != CXL_IO_MEM_MODE) && (reboot_mode != CXL_IO_MODE)) { ++ fprintf(stderr,"Invalid Reboot Mode"); ++ return -EINVAL; ++ } ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_REBOOT_MODE_SET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_REBOOT_MODE_SET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ reboot_mode_set_in = (void *) cmd->send_cmd->in.payload; ++ ++ reboot_mode_set_in->reboot_mode = reboot_mode; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_REBOOT_MODE_SET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_REBOOT_MODE_SET); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_CXL_CURR_BOOT_MODE_GET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CXL_CURR_BOOT_MODE_GET_OPCODE 0xFB0E ++ ++struct cxl_curr_cxl_boot_mode_out ++{ ++ uint8_t curr_cxl_boot; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_curr_cxl_boot_mode_get(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_curr_cxl_boot_mode_out *curr_cxl_boot_mode_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CXL_CURR_BOOT_MODE_GET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CXL_CURR_BOOT_MODE_GET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_CXL_CURR_BOOT_MODE_GET); ++ return -EINVAL; ++ } ++ ++ curr_cxl_boot_mode_out = (struct cxl_curr_cxl_boot_mode_out *)cmd->send_cmd->out.payload; ++ if (curr_cxl_boot_mode_out->curr_cxl_boot == CXL_IO_MEM_MODE) ++ fprintf(stdout, "CXL_IO_MEM_MODE\n"); ++ else if (curr_cxl_boot_mode_out->curr_cxl_boot == CXL_IO_MODE) ++ fprintf(stdout, "CXL_IO_MODE\n"); ++ else ++ fprintf(stdout, "Invalid Mode\n"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN_OPCODE 0xFB02 ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN_PAYLOAD_IN_SIZE 4 ++ ++struct cxl_mbox_pcie_eye_run_in { ++ u8 lane; ++ u8 sw_scan; ++ u8 ber; ++} __attribute__((packed)); ++ ++struct cxl_pcie_eye_run_out { ++ int pcie_eye_run_status; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_pcie_eye_run(struct cxl_memdev *memdev, ++ u8 lane, u8 sw_scan, u8 ber) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_pcie_eye_run_in *pcie_eye_run_in; ++ struct cxl_pcie_eye_run_out *pcie_eye_run_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ pcie_eye_run_in = (void *) cmd->send_cmd->in.payload; ++ ++ pcie_eye_run_in->lane = lane; ++ pcie_eye_run_in->sw_scan = sw_scan; ++ pcie_eye_run_in->ber = ber; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_PCIE_EYE_SW_RUN); ++ return -EINVAL; ++ } ++ ++ pcie_eye_run_out = (void *)cmd->send_cmd->out.payload; ++ if (!pcie_eye_run_out->pcie_eye_run_status) ++ fprintf(stdout, "pcie eye is running\n"); ++ else ++ fprintf(stdout, "pcie eye already running OR fault, error : %d\n", ++ pcie_eye_run_out->pcie_eye_run_status); ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_STATUS_OPCODE 0xFB03 ++ ++struct cxl_pcie_eye_status_out { ++ int pcie_eye_status; ++ int error; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_pcie_eye_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_pcie_eye_status_out *pcie_eye_status_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCIE_EYE_SW_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCIE_EYE_SW_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_PCIE_EYE_SW_STATUS); ++ return -EINVAL; ++ } ++ pcie_eye_status_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "%s\n", pcie_eye_status_out->pcie_eye_status ? ++ "PCIE EYE SW IS RUNNING" : "PCIE EYE SW IS NOT RUNNING/FINISHED"); ++ if(pcie_eye_status_out->error) ++ fprintf(stdout, "pcie eye run error %d:\n", pcie_eye_status_out->error); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++ ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_GET_SW CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_GET_SW_OPCODE 0xFB04 ++ ++#define NUM_EYESCOPE_HORIZ_VALS 40 ++#define TOTAL_EYESCOPE_HORIZ_VALS ((NUM_EYESCOPE_HORIZ_VALS * 2) + 1) ++ ++struct cxl_pcie_eye_get_sw_out { ++ char pcie_eye_data[TOTAL_EYESCOPE_HORIZ_VALS + 1]; ++} __attribute__((packed)); ++ ++struct cxl_pcie_eye_get_sw_in { ++ uint offset; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_pcie_eye_get_sw(struct cxl_memdev *memdev, uint offset) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_pcie_eye_get_sw_in *pcie_eye_get_sw_in; ++ struct cxl_pcie_eye_get_sw_out *pcie_eye_get_sw_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCIE_EYE_GET_SW_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ pcie_eye_get_sw_in = (void *) cmd->send_cmd->in.payload; ++ pcie_eye_get_sw_in->offset = offset; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCIE_EYE_GET_SW) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_PCIE_EYE_GET_SW); ++ return -EINVAL; ++ } ++ pcie_eye_get_sw_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "%s\n", pcie_eye_get_sw_out->pcie_eye_data); ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_GET_HW CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_GET_HW_OPCODE 0xFB05 ++ ++struct eyescope_results { ++ double merged_horizontal_eye_left; ++ double merged_horizontal_eye_right; ++ double merged_vertical_eye_top; ++ double merged_vertical_eye_bottom; ++}; ++ ++struct rx_settings_t { ++ int iskew_signed; ++ int qskew_signed; ++ int dlev00_signed; ++ int dlev01_signed; ++ int dlev10_signed; ++ int dlev11_signed; ++ int irphase_signed; ++ int h1po; ++ int h1no; ++ int h1pe; ++ int h1ne; ++ int h2; ++ int h3; ++ int h4; ++ int h5; ++ int h6; ++ int h7; ++ int h8; ++ int h9; ++ int aeq; ++ int vga; ++ int appmd; ++ int rxrt; ++ int shd; ++ int step; ++ int wm; ++}; ++ ++ ++struct cxl_pcie_eye_get_hw_out { ++ struct eyescope_results eyescope_results; ++ struct rx_settings_t rx_settings_t; ++ int eyescope_request_status; ++} __attribute__((packed)); ++ ++ ++void display_rx_setttings(struct rx_settings_t *settings); ++void display_rx_setttings(struct rx_settings_t *settings) ++{ ++ fprintf(stdout, "rx_settings: \n"); ++ fprintf(stdout, "dlv0123 = [%d, %d, %d, %d],", settings->dlev00_signed, settings->dlev01_signed, ++ settings->dlev10_signed, settings->dlev11_signed); ++ fprintf(stdout, "*vga, aeq = [%d, %d],", settings->vga, settings->aeq); ++ fprintf(stdout, "h2-9 = [%d, %d, %d, %d, %d, %d, %d, %d],", ++ settings->h2, settings->h3, settings->h4, settings->h5, ++ settings->h6, settings->h7, settings->h8, settings->h9); ++ fprintf(stdout, "appmd, rxrt, shd, wm = [%d, %d, 'g%d', %d],", ++ settings->appmd, settings->rxrt, settings->shd, settings->wm); ++ fprintf(stdout, "h1ne/0, pe/o = [%d %d %d %d],", settings->h1ne, settings->h1no, ++ settings->h1pe, settings->h1po); ++ fprintf(stdout, "iskw, qskw = [%d %d]", settings->iskew_signed, settings->qskew_signed); ++ fprintf(stdout, "\n"); ++} ++ ++void display_merged_eye_results(struct eyescope_results *eyescope_results); ++void display_merged_eye_results(struct eyescope_results *eyescope_results){ ++ ++ fprintf(stdout, "Merged Top (mV): %f\n", ++ eyescope_results->merged_vertical_eye_top); ++ fprintf(stdout, "Merged Bottom (mV): %f\n", ++ eyescope_results->merged_vertical_eye_bottom); ++ fprintf(stdout, "Merged Right Eye (UI): %f\n", ++ eyescope_results->merged_horizontal_eye_right); ++ fprintf(stdout, "Merged Left Eye (UI): %f\n", ++ eyescope_results->merged_horizontal_eye_left); ++} ++ ++CXL_EXPORT int cxl_memdev_pcie_eye_get_hw(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_pcie_eye_get_hw_out *pcie_eye_get_hw_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCIE_EYE_GET_HW_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCIE_EYE_GET_HW) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_PCIE_EYE_GET_HW); ++ return -EINVAL; ++ } ++ pcie_eye_get_hw_out = (void *)cmd->send_cmd->out.payload; ++ if (pcie_eye_get_hw_out->eyescope_request_status) { ++ fprintf(stdout, "eyescope request status: PASS\n"); ++ display_rx_setttings(&pcie_eye_get_hw_out->rx_settings_t); ++ display_merged_eye_results(&pcie_eye_get_hw_out->eyescope_results); ++ } else { ++ fprintf(stdout, "eyescope request status: FAIL\n"); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_BER CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCIE_EYE_SW_BER_OPCODE 0xFB06 ++ ++struct cxl_pcie_eye_get_sw_ber_out { ++ float horiz_margin; ++ float vert_margin; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_pcie_eye_get_sw_ber(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_pcie_eye_get_sw_ber_out *pcie_eye_get_sw_ber_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCIE_EYE_SW_BER_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed OR BER is not enabled, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCIE_EYE_SW_BER) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_PCIE_EYE_SW_BER); ++ return -EINVAL; ++ } ++ pcie_eye_get_sw_ber_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "Extrapolation for BER at 1e-12\n"); ++ if(pcie_eye_get_sw_ber_out->vert_margin > 18 && pcie_eye_get_sw_ber_out->horiz_margin > 0.2) { ++ fprintf(stdout, "Eye Height and width margins are > 0.2UI and 18mV, Test PASSED\n"); ++ fprintf(stdout, "Eye width margin at 1e-12 is %f UI\n", pcie_eye_get_sw_ber_out->horiz_margin); ++ fprintf(stdout, "Eye height margin at 1e-12 is %f mV\n", pcie_eye_get_sw_ber_out->vert_margin); ++ } else { ++ fprintf(stdout, "Eye Height and width margins are not greater than 0.2UI and 18mV, Test FAILED\n"); ++ } ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_CXL_LINK_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_CXL_LINK_STATUS_OPCODE 0xFB07 ++ ++char ltssm_state_name[][20] = ++{ ++ "DETECT_QUIET", ++ "DETECT_ACT", ++ "POLL_ACTIVE", ++ "POLL_COMPLIANCE", ++ "POLL_CONFIG", ++ "PRE_DETECT_QUIET", ++ "DETECT_WAIT", ++ "CFG_LINKWD_START", ++ "CFG_LINKWD_ACEPT", ++ "CFG_LANENUM_WAIT", ++ "CFG_LANENUM_ACEPT", ++ "CFG_COMPLETE", ++ "CFG_IDLE", ++ "RCVRY_LOCK", ++ "RCVRY_SPEED", ++ "RCVRY_RCVRCFG", ++ "RCVRY_IDLE", ++ "L0", ++ "L0S", ++ "L123_SEND_EIDLE", ++ "L1_IDLE", ++ "L2_IDLE", ++ "L2_WAKE", ++ "DISABLED_ENTRY", ++ "DISABLED_IDLE", ++ "DISABLED", ++ "LPBK_ENTRY", ++ "LPBK_ACTIVE", ++ "LPBK_EXIT", ++ "LPBK_EXIT_TIMEOUT", ++ "HOT_RESET_ENTRY", ++ "HOT_RESET", ++ "RCVRY_EQ0", ++ "RCVRY_EQ1", ++ "RCVRY_EQ2", ++ "RCVRY_EQ3" ++}; ++ ++struct cxl_get_cxl_link_status_out { ++ float cxl_link_status; ++ uint32_t link_width; ++ uint32_t link_speed; ++ uint32_t ltssm_val; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_get_cxl_link_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_get_cxl_link_status_out *get_cxl_link_status_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_CXL_LINK_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_CXL_LINK_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_CXL_LINK_STATUS); ++ return -EINVAL; ++ } ++ get_cxl_link_status_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "Link is in CXL%0.1f mode\n", get_cxl_link_status_out->cxl_link_status); ++ fprintf(stdout, "Negotiated link width: x%d\n", get_cxl_link_status_out->link_width); ++ fprintf(stdout, "Negotiated link speed: Gen%d\n", get_cxl_link_status_out->link_speed); ++ fprintf(stdout, "ltssm state: %s, code 0x%x\n", ltssm_state_name[get_cxl_link_status_out->ltssm_val], get_cxl_link_status_out->ltssm_val); ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_DEVICE_INFO CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_DEVICE_INFO_OPCODE 0xFB08 ++ ++struct cxl_get_device_info_out { ++ uint16_t device_id; ++ uint8_t revision_id; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_get_device_info(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_get_device_info_out *get_device_info_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_DEVICE_INFO_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_DEVICE_INFO) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_DEVICE_INFO); ++ return -EINVAL; ++ } ++ get_device_info_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "Device id: 0x%x\n", get_device_info_out->device_id); ++ fprintf(stdout, "Revision id: 0x%x\n", get_device_info_out->revision_id); ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_READ_DDR_TEMP CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_READ_DDR_TEMP_OPCODE 0xC531 ++#define DDR_MAX_DIMM_CNT 4 ++ ++struct ddr_dimm_temp_info { ++ uint8_t ddr_temp_valid; ++ uint8_t dimm_id; ++ uint8_t spd_idx; ++ uint8_t rsvd; ++ float dimm_temp; ++}; ++ ++struct cxl_read_ddr_temp_out { ++ struct ddr_dimm_temp_info ddr_dimm_temp_info[DDR_MAX_DIMM_CNT]; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_read_ddr_temp(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_read_ddr_temp_out *read_ddr_temp_out; ++ int rc = 0; ++ int idx; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_READ_DDR_TEMP_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_READ_DDR_TEMP) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_READ_DDR_TEMP); ++ return -EINVAL; ++ } ++ read_ddr_temp_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "DDR DIMM temperature info:\n"); ++ for(idx = 0; idx < DDR_MAX_DIMM_CNT; idx++) { ++ fprintf(stdout, "dimm_id : 0x%x\n", read_ddr_temp_out->ddr_dimm_temp_info[idx].dimm_id); ++ fprintf(stdout, "spd_idx: 0x%x\n", read_ddr_temp_out->ddr_dimm_temp_info[idx].spd_idx); ++ fprintf(stdout, "dimm temp: %f\n", read_ddr_temp_out->ddr_dimm_temp_info[idx].dimm_temp); ++ fprintf(stdout, "ddr temperature is %s\n\n", read_ddr_temp_out->ddr_dimm_temp_info[idx].ddr_temp_valid ? "valid" : "invalid"); ++ } ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA_OPCODE 0xFB14 ++#define CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA_IN_PAYLOAD_SIZE sizeof(u64) ++ ++CXL_EXPORT int cxl_memdev_cxl_hpa_to_dpa(struct cxl_memdev *memdev, u64 hpa_address) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_command_info *cinfo; ++ struct cxl_mem_query_commands *query; ++ int rc = 0; ++ u64 *dpa_address_out; ++ u64 *hpa_address_in; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA_IN_PAYLOAD_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ hpa_address_in = (void *)cmd->send_cmd->in.payload; ++ *hpa_address_in = hpa_address; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_CXL_HPA_TO_DPA); ++ return -EINVAL; ++ } ++ dpa_address_out = (void*)cmd->send_cmd->out.payload; ++ fprintf(stdout, "dpa address:0x%lx\n", *dpa_address_out); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_ERRORS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_ERRORS_OPCODE 0xFB13 ++ ++typedef enum { ++ WSTRB_PARITY_ERROR = 0, ++ WDATA_PARITY_ERROR, ++ RDATA_RRESP_PARITY_ERROR, ++ RDATA_RESP_RID_PARITY_ERROR, ++ RDATA_PARITY_ERROR, ++ R_RESP_PARITY_ERROR, ++ B_RESP_PARITY_ERROR, ++ AW_QOS_PARITY_ERROR, ++ AW_MISC_PARITY_ERROR, ++ AW_ID_PARITY_ERROR, ++ AW_ADDR_RANGE_PARITY_ERROR, ++ AW_ADDR_PARITY_ERROR, ++ AR_QOS_PARITY_ERROR, ++ AR_MISC_PARITY_ERROR, ++ AR_ID_PARITY_ERROR, ++ AR_ADDR_RANGE_PARITY_ERROR, ++ AR_ADDR_PARITY_ERROR, ++ AXI_B_ID_PARITY_ERROR, ++ AXI_B_PARITY_ERROR, ++ AXI_R_ID_PARITY_ERROR, ++ AXI_R_DATA_PARITY_ERROR, ++ AXI_R_PARITY_ERROR, ++ DDR_PARITY_ERROR_COUNT = 22, ++} ddr_parity_errors; ++ ++char *ddr_parity_error_strings[DDR_PARITY_ERROR_COUNT] = { ++ "WSTRB_PARITY_ERROR", ++ "WDATA_PARITY_ERROR", ++ "RDATA_RRESP_PARITY_ERROR", ++ "RDATA_RESP_RID_PARITY_ERROR", ++ "RDATA_PARITY_ERROR", ++ "R_RESP_PARITY_ERROR", ++ "B_RESP_PARITY_ERROR", ++ "AW_QOS_PARITY_ERROR", ++ "AW_MISC_PARITY_ERROR", ++ "AW_ID_PARITY_ERROR", ++ "AW_ADDR_RANGE_PARITY_ERROR", ++ "AW_ADDR_PARITY_ERROR", ++ "AR_QOS_PARITY_ERROR", ++ "AR_MISC_PARITY_ERROR", ++ "AR_ID_PARITY_ERROR", ++ "AR_ADDR_RANGE_PARITY_ERROR", ++ "AR_ADDR_PARITY_ERROR", ++ "AXI_B_ID_PARITY_ERROR", ++ "AXI_B_PARITY_ERROR", ++ "AXI_R_ID_PARITY_ERROR", ++ "AXI_R_DATA_PARITY_ERROR", ++ "AXI_R_PARITY_ERROR", ++}; ++ ++typedef enum { ++ S2M_NDR_FIFO = 0, ++ S2M_DRC_FIFO, ++ M2S_RWD_FIFO, ++ M2S_REQ_FIFO, ++ DDR1_W_REQ_FIFO, ++ DDR1_RDATA_RESP_FIFO, ++ DDR1_R_RESP_FIFO, ++ DDR1_B_RESP_FIFO, ++ DDR1_AW_REQ_FIFO, ++ DDR1_AR_REQ_FIFO, ++ DDR0_W_REQ_FIFO, ++ DDR0_RDATA_RESP_FIFO, ++ DDR0_R_RESP_FIFO, ++ DDR0_B_RESP_FIFO, ++ DDR0_AW_REQ_FIFO, ++ DDR0_AR_REQ_FIFO, ++ FIFO_ERROR_COUNT = 16, ++} fifo_errors; ++ ++char *fifo_error_strings[FIFO_ERROR_COUNT] = { ++ "S2M_NDR_FIFO", ++ "S2M_DRC_FIFO", ++ "M2S_RWD_FIFO", ++ "M2S_REQ_FIFO", ++ "DDR1_W_REQ_FIFO", ++ "DDR1_RDATA_RESP_FIFO", ++ "DDR1_R_RESP_FIFO", ++ "DDR1_B_RESP_FIFO", ++ "DDR1_AW_REQ_FIFO", ++ "DDR1_AR_REQ_FIFO", ++ "DDR0_W_REQ_FIFO", ++ "DDR0_RDATA_RESP_FIFO", ++ "DDR0_R_RESP_FIFO", ++ "DDR0_B_RESP_FIFO", ++ "DDR0_AW_REQ_FIFO", ++ "DDR0_AR_REQ_FIFO" ++}; ++ ++typedef enum { ++ NDR_TAG_PARITY_ERROR = 0, ++ NDR_RESP_PARITY_ERROR, ++ M2S_RWD_ECC_CHECK_ERR_MULTPL_FAIL, ++ M2S_RWD_ECC_CHECK_ERR_DETECT_FAIL, ++ M2S_REQ_ECC_CHECK_ERR_MULTPL_FAIL, ++ M2S_REQ_ECC_CHECK_ERR_DETECT_FAIL, ++ DRC_TAG_PARITY_ERROR, ++ DRC_RESP_PARITY_ERROR, ++ DRC_DATA_PARITY_ERROR, ++ AW_MST_RWD_PARITY_ERROR, ++ AR_MST_REQ_PARITY_ERROR, ++ M2S_REQ_DUP_ADDR_PARITY_ERROR, ++ M2S_RWD_DUP_ADDR_PARITY_ERROR, ++ PARITY_ERROR_COUNT = 13, ++} parity_errors; ++ ++char *parity_error_strings[PARITY_ERROR_COUNT] = { ++ "NDR_TAG_PARITY_ERROR", ++ "NDR_RESP_PARITY_ERROR", ++ "M2S_RWD_ECC_CHECK_ERR_MULTPL_FAIL", ++ "M2S_RWD_ECC_CHECK_ERR_DETECT_FAIL", ++ "M2S_REQ_ECC_CHECK_ERR_MULTPL_FAIL", ++ "M2S_REQ_ECC_CHECK_ERR_DETECT_FAIL", ++ "DRC_TAG_PARITY_ERROR", ++ "DRC_RESP_PARITY_ERROR", ++ "DRC_DATA_PARITY_ERROR", ++ "AW_MST_RWD_PARITY_ERROR", ++ "AR_MST_REQ_PARITY_ERROR", ++ "M2S_REQ_DUP_ADDR_PARITY_ERROR", ++ "M2S_RWD_DUP_ADDR_PARITY_ERROR", ++}; ++ ++typedef enum { ++ MST_M2S_RWD_ERR_MULTPL = 0, ++ MST_M2S_RWD_ERR_DETECT, ++ MST_M2S_REQ_ERR_MULTPL, ++ MST_M2S_REQ_ERR_DETECT, ++ POISON_RECEIVED_IN_RWD, ++ RWD_ADDRESS_INVALID, ++ REQ_ADDRESS_INVALID, ++ DDR1_RRESP_ERROR, ++ DDR1_BRESP_ERROR, ++ DDR0_RRESP_ERROR, ++ DDR0_BRESP_ERROR, ++ DDR1_RPARITY_ERROR, ++ DDR1_BPARITY_ERROR, ++ DDR0_RPARITY_ERROR, ++ DDR0_BPARITY_ERROR, ++ HDM_DEC1_ERR_NOT_COMMITED, ++ RX_DEINIT_TIMEOUT, ++ TX_DEINIT_TIMEOUT, ++ VIRAL, ++ DDR0_BRESP_DEC_ERROR, ++ DDR1_BRESP_DEC_ERROR, ++ DDR0_RRESP_DEC_ERROR, ++ DDR1_RRESP_DEC_ERROR, ++ MEMBRIDGE_COMMON_ERROR_COUNT = 23 ++} membridge_common_errors; ++ ++char *membridge_common_error_strings[MEMBRIDGE_COMMON_ERROR_COUNT] = { ++ "MST_M2S_RWD_ERR_MULTPL", ++ "MST_M2S_RWD_ERR_DETECT", ++ "MST_M2S_REQ_ERR_MULTPL", ++ "MST_M2S_REQ_ERR_DETECT", ++ "POISON_RECEIVED_IN_RWD", ++ "RWD_ADDRESS_INVALID", ++ "REQ_ADDRESS_INVALID", ++ "DDR1_RRESP_ERROR", ++ "DDR1_BRESP_ERROR", ++ "DDR0_RRESP_ERROR", ++ "DDR0_BRESP_ERROR", ++ "DDR1_RPARITY_ERROR", ++ "DDR1_BPARITY_ERROR", ++ "DDR0_RPARITY_ERROR", ++ "DDR0_BPARITY_ERROR", ++ "HDM_DEC1_ERR_NOT_COMMITED", ++ "RX_DEINIT_TIMEOUT", ++ "TX_DEINIT_TIMEOUT", ++ "VIRAL", ++ "DDR0_BRESP_DEC_ERROR", ++ "DDR1_BRESP_DEC_ERROR", ++ "DDR0_RRESP_DEC_ERROR", ++ "DDR1_RRESP_DEC_ERROR", ++}; ++ ++struct cxl_membridge_errors_out { ++ uint32_t fifo_overflow; ++ uint32_t fifo_overflows[FIFO_ERROR_COUNT]; ++ uint32_t fifo_underflow; ++ uint32_t fifo_underflows[FIFO_ERROR_COUNT]; ++ uint32_t ddr0_parity_error; ++ uint32_t ddr0_parity_errors[DDR_PARITY_ERROR_COUNT]; ++ uint32_t ddr1_parity_error; ++ uint32_t ddr1_parity_errors[DDR_PARITY_ERROR_COUNT]; ++ uint32_t parity_error; ++ uint32_t parity_errors[PARITY_ERROR_COUNT]; ++ uint32_t common_errors[MEMBRIDGE_COMMON_ERROR_COUNT]; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_get_cxl_membridge_errors(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_membridge_errors_out *get_cxl_membridge_errors_out; ++ int rc = 0; ++ int idx; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_ERRORS_OPCODE); ++ ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_ERRORS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_ERRORS); ++ return -EINVAL; ++ } ++ ++ get_cxl_membridge_errors_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "fifo_overflow errors : %d\n", get_cxl_membridge_errors_out->fifo_overflow); ++ for(idx = 0; idx < FIFO_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->fifo_overflows[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", fifo_error_strings[idx], ++ get_cxl_membridge_errors_out->fifo_overflows[idx]); ++ } ++ ++ fprintf(stdout, "fifo_underflow errors : %d\n", get_cxl_membridge_errors_out->fifo_underflow); ++ for(idx = 0; idx < FIFO_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->fifo_underflows[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", fifo_error_strings[idx], ++ get_cxl_membridge_errors_out->fifo_underflows[idx]); ++ } ++ ++ fprintf(stdout, "ddr0 parity errors : %d\n", get_cxl_membridge_errors_out->ddr0_parity_error); ++ for(idx = 0; idx < DDR_PARITY_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->ddr0_parity_errors[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", ddr_parity_error_strings[idx], ++ get_cxl_membridge_errors_out->ddr0_parity_errors[idx]); ++ } ++ ++ fprintf(stdout, "ddr1 parity errors : %d\n", get_cxl_membridge_errors_out->ddr1_parity_error); ++ for(idx = 0; idx < DDR_PARITY_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->ddr1_parity_errors[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", ddr_parity_error_strings[idx], ++ get_cxl_membridge_errors_out->ddr1_parity_errors[idx]); ++ } ++ ++ fprintf(stdout, "membridge common errors :\n"); ++ for(idx = 0; idx < MEMBRIDGE_COMMON_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->common_errors[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", membridge_common_error_strings[idx], ++ get_cxl_membridge_errors_out->common_errors[idx]); ++ } ++ ++ fprintf(stdout, "parity errors : %d\n", get_cxl_membridge_errors_out->parity_error); ++ for(idx = 0; idx < PARITY_ERROR_COUNT; idx++) { ++ if (get_cxl_membridge_errors_out->parity_errors[idx] != 0) ++ fprintf(stdout, "%s : 0x%x\n", parity_error_strings[idx], ++ get_cxl_membridge_errors_out->parity_errors[idx]); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_DDR_BW CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_DDR_BW_OPCODE 0xFB09 ++ ++struct cxl_get_ddr_bw_in { ++ u32 timeout; ++ u32 iterations; ++} __attribute__((packed)); ++ ++typedef enum { ++ DDR_CTRL0 = 0, ++ DDR_CTRL1 = 1, ++ DDR_MAX_SUBSYS, ++} ddr_subsys; ++ ++struct cxl_get_ddr_bw_out { ++ float peak_bw[DDR_MAX_SUBSYS]; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_get_ddr_bw(struct cxl_memdev *memdev, u32 timeout, u32 iterations) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_get_ddr_bw_in *get_ddr_bw_in; ++ struct cxl_get_ddr_bw_out *get_ddr_bw_out; ++ float total_peak_bw = 0; ++ int rc = 0; ++ int i; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_DDR_BW_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ get_ddr_bw_in = (void *) cmd->send_cmd->in.payload; ++ ++ get_ddr_bw_in->timeout = timeout; ++ get_ddr_bw_in->iterations = iterations; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_DDR_BW) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_DDR_BW); ++ return -EINVAL; ++ } ++ get_ddr_bw_out = (void *)cmd->send_cmd->out.payload; ++ for(i = 0; i < DDR_MAX_SUBSYS; i++) { ++ fprintf(stdout, "ddr%d peak bandwidth = %f GB/s\n", i, get_ddr_bw_out->peak_bw[i]); ++ total_peak_bw += get_ddr_bw_out->peak_bw[i]; ++ } ++ fprintf(stdout, "total peak bandwidth = %f GB/s\n", total_peak_bw); ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_I2C_READ CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_I2C_READ_OPCODE 0xFB10 ++#define I2C_MAX_SIZE_NUM_BYTES 128 ++ ++struct cxl_i2c_read_in { ++ u16 slave_addr; ++ u8 reg_addr; ++ u8 num_bytes; ++} __attribute__((packed)); ++ ++struct cxl_i2c_read_out { ++ char buf[I2C_MAX_SIZE_NUM_BYTES]; ++ u8 num_bytes; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_i2c_read(struct cxl_memdev *memdev, u16 slave_addr, u8 reg_addr, u8 num_bytes) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_i2c_read_in *i2c_read_in; ++ struct cxl_i2c_read_out *i2c_read_out; ++ int rc = 0; ++ int i; ++ ++ if(num_bytes > I2C_MAX_SIZE_NUM_BYTES) { ++ fprintf(stderr, "%s: Max number of bytes supported is %d, cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), I2C_MAX_SIZE_NUM_BYTES, rc, strerror(-rc)); ++ return -EINVAL; ++ } ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_I2C_READ_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ i2c_read_in = (void *) cmd->send_cmd->in.payload; ++ ++ i2c_read_in->slave_addr = slave_addr; ++ i2c_read_in->reg_addr = reg_addr; ++ i2c_read_in->num_bytes= num_bytes; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_I2C_READ) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_I2C_READ); ++ return -EINVAL; ++ } ++ i2c_read_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "i2c read output:"); ++ for(i = 0; i < i2c_read_out->num_bytes; i++) { ++ fprintf(stdout, "0x%x\t", i2c_read_out->buf[i]); ++ } ++ fprintf(stdout, "\n"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_I2C_WRITE CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_I2C_WRITE_OPCODE 0xFB11 ++ ++struct cxl_i2c_write_in { ++ u16 slave_addr; ++ u8 reg_addr; ++ u8 data; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_i2c_write(struct cxl_memdev *memdev, u16 slave_addr, u8 reg_addr, u8 data) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_i2c_write_in *i2c_write_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_I2C_WRITE_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ i2c_write_in = (void *) cmd->send_cmd->in.payload; ++ ++ i2c_write_in->slave_addr = slave_addr; ++ i2c_write_in->reg_addr = reg_addr; ++ i2c_write_in->data = data; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_I2C_WRITE) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_I2C_WRITE); ++ return -EINVAL; ++ } ++ fprintf(stdout, "i2c write success\n"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_DDR_LATENCY CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_DDR_LATENCY_OPCODE 0xFB12 ++ ++struct ddr_lat_op { ++ uint64_t readlat; ++ uint64_t writelat; ++ uint32_t rdsamplecnt; ++ uint32_t wrsamplecnt; ++ float avg_rdlatency; ++ float avg_wrlatency; ++}; ++ ++struct cxl_get_ddr_latency_in { ++ u32 measure_time; ++} __attribute__((packed)); ++ ++struct cxl_get_ddr_latency_out { ++ struct ddr_lat_op ddr_lat_op[DDR_MAX_SUBSYS]; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_get_ddr_latency(struct cxl_memdev *memdev, u32 measure_time) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_get_ddr_latency_in *get_ddr_lat_in; ++ struct cxl_get_ddr_latency_out *get_ddr_lat_out; ++ int rc = 0; ++ int ddr_id; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_DDR_LATENCY_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ get_ddr_lat_in = (void *) cmd->send_cmd->in.payload; ++ ++ get_ddr_lat_in->measure_time = measure_time; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_DDR_LATENCY) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_DDR_LATENCY); ++ return -EINVAL; ++ } ++ get_ddr_lat_out = (void *)cmd->send_cmd->out.payload; ++ for(ddr_id = 0; ddr_id < DDR_MAX_SUBSYS; ddr_id++) { ++ fprintf(stdout, "\nDDR%d Latency:\n", ddr_id); ++ fprintf(stdout, ++ "readLat: %lu, rdSampleCnt: %u\n, writeLat: %lu, wrSampleCnt: %u\n", ++ get_ddr_lat_out->ddr_lat_op[ddr_id].readlat, ++ get_ddr_lat_out->ddr_lat_op[ddr_id].rdsamplecnt, ++ get_ddr_lat_out->ddr_lat_op[ddr_id].writelat, ++ get_ddr_lat_out->ddr_lat_op[ddr_id].wrsamplecnt); ++ ++ fprintf(stdout, "Average Latency:\n"); ++ fprintf(stdout, ++ "Avg Read Latency : %f ns \n Avg Write Latency : %f ns \n", ++ get_ddr_lat_out->ddr_lat_op[ddr_id].avg_rdlatency, ++ get_ddr_lat_out->ddr_lat_op[ddr_id].avg_wrlatency); ++ } ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_GET_DDR_ECC_ERR_INFO CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_DDR_ECC_ERR_INFO_OPCODE 0xFB0F ++ ++struct ddr_parity_err { ++ uint32_t parity_crit_bit2_cnt; /* Parity error on the address/control bus*/ ++ uint32_t parity_crit_bit1_cnt; /* Overlapping write data parity error */ ++ uint32_t parity_crit_bit0_cnt; /* Write data parity error */ ++}; ++ ++struct ddr_dfi_err { ++ uint32_t dfi_crit_bit5_cnt; /* DFI tINIT_COMPLETE value has timed out */ ++ uint32_t dfi_crit_bit2_cnt; /* Error received from the PHY on the DFI bus */ ++ ++ uint32_t dfi_warn_bit1_cnt; /* DFI PHY Master Interface error has occurred */ ++ uint32_t dfi_warn_bit0_cnt; /* DFI update error has occurred */ ++}; ++ ++struct ddr_crc_err { ++ uint32_t crc_crit_bit1_cnt; /* CA Parity or a CRC error happened during CRC ++ Retry. */ ++ uint32_t crc_crit_bit0_cnt; /* CRC error occurred on the write data bus */ ++}; ++ ++struct ddr_userif_err { ++ uint32_t ++ userif_crit_bit2_cnt; /* Error occurred on the port command channel. */ ++ uint32_t userif_crit_bit1_cnt; /* Multiple accesses outside the defined ++ PHYSICAL memory space have occurred. */ ++ uint32_t userif_crit_bit0_cnt; /* A Memory access outside the defined PHYSICAL ++ memory space has occurred */ ++}; ++ ++struct ddr_ecc_err { ++ uint32_t ecc_warn_bit6_cnt; /* One or more ECC writeback commands ++ could not be executed */ ++ uint32_t ecc_crit_bit3_cnt; /* Multiple uncorrectable ECC events ++ have been detected */ ++ uint32_t ecc_crit_bit2_cnt; /* A uncorrectable ECC event has been detected */ ++ uint32_t ecc_crit_bit8_cnt; /* An ECC correctable error has been detected in a ++ scrubbing read operation */ ++ uint32_t ecc_warn_bit1_cnt; /* Multiple correctable ECC events ++ have been detected */ ++ uint32_t ecc_warn_bit0_cnt; /* A correctable ECC event has been detected */ ++}; ++ ++struct ddr_controller_errors { ++ struct ddr_parity_err parity; ++ struct ddr_dfi_err dfi; ++ struct ddr_crc_err crc; ++ struct ddr_userif_err userif; ++ struct ddr_ecc_err ecc; ++}; ++ ++struct cxl_get_ddr_ecc_err_info_out { ++ struct ddr_controller_errors ddr_ctrl_err[DDR_MAX_SUBSYS]; ++} __attribute__((packed)); ++ ++void display_error_count(struct ddr_controller_errors *ddr_ctrl_err, ddr_subsys ddr_id); ++ ++void display_error_count(struct ddr_controller_errors *ddr_ctrl_err, ddr_subsys ddr_id) { ++ if (ddr_ctrl_err[ddr_id].parity.parity_crit_bit2_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: Parity error on the address/control bus " ++ "(parity_crit_bit2_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].parity.parity_crit_bit2_cnt); ++ } ++ if (ddr_ctrl_err[ddr_id].parity.parity_crit_bit1_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: Overlapping write data parity error " ++ "(parity_crit_bit1_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].parity.parity_crit_bit1_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].parity.parity_crit_bit0_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: Write data parity error " ++ "(parity_crit_bit0_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].parity.parity_crit_bit0_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].dfi.dfi_crit_bit5_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: DFI tINIT_COMPLETE value has timed out " ++ "(dfi_crit_bit5_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].dfi.dfi_crit_bit5_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].dfi.dfi_crit_bit2_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL : Error received from the PHY on the DFI bus " ++ "(dfi_crit_bit2_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].dfi.dfi_crit_bit2_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].dfi.dfi_warn_bit1_cnt) { ++ fprintf(stdout, ++ "DDR-%d: WARN: DFI PHY Master Interface error has occurred " ++ "(dfi_warn_bit1_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].dfi.dfi_warn_bit1_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].dfi.dfi_warn_bit0_cnt) { ++ fprintf(stdout, ++ "DDR-%d: WARN: DFI update error has occurred " ++ "(dfi_warn_bit0_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].dfi.dfi_warn_bit0_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].crc.crc_crit_bit1_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: CA Parity or a CRC error happened during CRC Retry " ++ "(crc_crit_bit1_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].crc.crc_crit_bit1_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].crc.crc_crit_bit0_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: CRC error occurred on the write data bus " ++ "(crc_crit_bit0_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].crc.crc_crit_bit0_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].userif.userif_crit_bit2_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: Error occurred on the port command channel " ++ "(userif_crit_bit2_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].userif.userif_crit_bit2_cnt); ++ } ++ if (ddr_ctrl_err[ddr_id].userif.userif_crit_bit1_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: Multiple accesses outside the defined PHYSICAL " ++ "memory space have occurred " ++ "(userif_crit_bit1_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].userif.userif_crit_bit1_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].userif.userif_crit_bit0_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: A Memory access outside the defined PHYSICAL " ++ "memory space has occurred " ++ "(userif_crit_bit0_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].userif.userif_crit_bit0_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit6_cnt) { ++ fprintf(stdout, ++ "DDR-%d: WARN: One or more ECC writeback commands " ++ "could not be executed " ++ "(ecc_warn_bit6_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit6_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit3_cnt) { ++ fprintf(stdout, ++ "DDR-%d:FATAL: Multiple uncorrectable ECC events have been detected " ++ "(ecc_crit_bit3_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit3_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit2_cnt) { ++ fprintf(stdout, ++ "DDR-%d: FATAL: A uncorrectable ECC event has been detected " ++ "(ecc_crit_bit2_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit2_cnt); ++ } ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit8_cnt) { ++ fprintf(stdout, ++ "DDR-%d: CRIT: An ECC correctable error has been detected " ++ "in a scrubbing read operation " ++ "(ecc_crit_bit8_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_crit_bit8_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit1_cnt) { ++ fprintf(stdout, ++ "DDR-%d: WARN: Multiple correctable ECC events have been detected " ++ "(ecc_warn_bit1_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit1_cnt); ++ } ++ ++ if (ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit0_cnt) { ++ fprintf(stdout, ++ "DDR-%d: WARN: A correctable ECC event has been detected " ++ "(ecc_warn_bit0_cnt= %u)\n", ++ ddr_id, ++ ddr_ctrl_err[ddr_id].ecc.ecc_warn_bit0_cnt); ++ } ++} ++ ++CXL_EXPORT int cxl_memdev_get_ddr_ecc_err_info(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_get_ddr_ecc_err_info_out *get_ddr_ecc_err_info_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_DDR_ECC_ERR_INFO_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_DDR_ECC_ERR_INFO) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_DDR_ECC_ERR_INFO); ++ return -EINVAL; ++ } ++ get_ddr_ecc_err_info_out = (void *)cmd->send_cmd->out.payload; ++ display_error_count(get_ddr_ecc_err_info_out->ddr_ctrl_err, DDR_CTRL0); ++ display_error_count(get_ddr_ecc_err_info_out->ddr_ctrl_err, DDR_CTRL1); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_START_DDR_ECC_SCRUB CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_START_DDR_ECC_SCRUB_OPCODE 0xFB15 ++ ++CXL_EXPORT int cxl_memdev_start_ddr_ecc_scrub(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_START_DDR_ECC_SCRUB_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_START_DDR_ECC_SCRUB) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_START_DDR_ECC_SCRUB); ++ return -EINVAL; ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_ECC_SCRUB_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_ECC_SCRUB_STATUS_OPCODE 0xFB16 ++ ++ ++struct cxl_ddr_ecc_scrub_status_out { ++ int ecc_scrub_status[DDR_MAX_SUBSYS]; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_ddr_ecc_scrub_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_ecc_scrub_status_out *ddr_ecc_scrub_status_out; ++ int rc = 0; ++ int subsys; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_ECC_SCRUB_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_ECC_SCRUB_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_ECC_SCRUB_STATUS); ++ return -EINVAL; ++ } ++ ddr_ecc_scrub_status_out = (void *)cmd->send_cmd->out.payload; ++ for(subsys = DDR_CTRL0; subsys < DDR_MAX_SUBSYS; subsys++) ++ { ++ fprintf(stdout, "DDR-%d %s\n", subsys, ddr_ecc_scrub_status_out->ecc_scrub_status[subsys] ? ++ "ECC SCRUB IS IN PROGRESS" : "DDR SCRUB IS NOT RUNNING/FINISHED"); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR CONTINUOUS SCRUB STATUS */ ++ ++#define CXL_MEM_COMMAND_ID_DDR_CONT_SCRUB_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_CONT_SCRUB_STATUS_OPCODE 0xFB28 ++ ++struct cxl_ddr_cont_scrub_status_out { ++ uint32_t cont_scrub_status; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_cont_scrub_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_cont_scrub_status_out *ddr_cont_scrub_status_out; ++ int rc = 0; ++ int subsys; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_CONT_SCRUB_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_CONT_SCRUB_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_CONT_SCRUB_STATUS); ++ return -EINVAL; ++ } ++ ddr_cont_scrub_status_out = (void *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "%s\n", ddr_cont_scrub_status_out->cont_scrub_status ? ++ "CONTINUOUS SCRUB IS ON" : "CONTINUOUS SCRUB IS OFF"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR CONTINUOUS SCRUB SET */ ++#define CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET_OPCODE 0xFB29 ++#define CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET_PAYLOAD_IN_SIZE 4 ++ ++struct cxl_mbox_ddr_cont_scrub_set_in { ++ uint32_t cont_scrub_status; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_cont_scrub_set(struct cxl_memdev *memdev, uint32_t cont_scrub_status) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_ddr_cont_scrub_set_in *ddr_cont_scrub_set_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_cont_scrub_set_in = (void *) cmd->send_cmd->in.payload; ++ ++ ddr_cont_scrub_set_in->cont_scrub_status = cont_scrub_status; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_CONT_SRUB_SET); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_INIT_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_INIT_STATUS_OPCODE 0xFB17 ++ ++typedef enum { ++ DDR_INIT_INPROGRESS = 0, ++ DDR_INIT_PASSED = 1, ++ DDR_INIT_FAILED = -1, ++ DDR_INIT_FAILED_NO_CH0_DIMM0 = -2, ++ DDR_INIT_FAILED_UNKNOWN_DIMM = -3, ++} ddr_status; ++ ++typedef enum { ++ CH_0 = 0, ++ CH_1 = 1, ++ CH_NA = -1, ++} f_channel_id; ++ ++struct ddr_init_boot_status { ++ int8_t ddr_init_status; ++ int8_t failed_channel_id; ++ char failed_dimm_silk_screen; ++}; ++ ++struct cxl_ddr_init_status_out { ++struct ddr_init_boot_status init_status; ++} __packed; ++ ++CXL_EXPORT int cxl_memdev_ddr_init_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_init_status_out *ddr_init_status_out; ++ int rc = 0; ++ int8_t status; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_INIT_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_INIT_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_INIT_STATUS); ++ return -EINVAL; ++ } ++ ddr_init_status_out = (void *)cmd->send_cmd->out.payload; ++ status = ddr_init_status_out->init_status.ddr_init_status; ++ switch (status) ++ { ++ case DDR_INIT_INPROGRESS: ++ fprintf(stdout, "DDR INIT IS IN PROGRESS\n"); ++ break; ++ case DDR_INIT_PASSED: ++ fprintf(stdout, "DDR INIT PASSED\n"); ++ break; ++ case DDR_INIT_FAILED: ++ fprintf(stdout, "DDR INIT FAILED for CH:%d DIMM:%c\n", ++ ddr_init_status_out->init_status.failed_channel_id, ++ ddr_init_status_out->init_status.failed_dimm_silk_screen); ++ ++ fprintf(stdout, "RECOVERY REMEDY: REPLACE CH:%d DIMM:%c and RE-TRY\n", ++ ddr_init_status_out->init_status.failed_channel_id, ++ ddr_init_status_out->init_status.failed_dimm_silk_screen); ++ break; ++ case DDR_INIT_FAILED_NO_CH0_DIMM0: ++ fprintf(stdout, "DDR INIT FAILED. CH:%d DIMM:%c is NOT PLUGGED IN\n", ++ ddr_init_status_out->init_status.failed_channel_id, ++ ddr_init_status_out->init_status.failed_dimm_silk_screen); ++ ++ fprintf(stdout, "RECOVERY REMEDY: PLUG IN CH:%d DIMM:%c\n", ++ ddr_init_status_out->init_status.failed_channel_id, ++ ddr_init_status_out->init_status.failed_dimm_silk_screen); ++ break; ++ case DDR_INIT_FAILED_UNKNOWN_DIMM: ++ fprintf(stdout, "DDR INIT FAILED. UN-SUPPORTED/UNKNOWN DIMM\n"); ++ fprintf(stdout, "RECOVERY REMEDY: PLUG IN SUPPORTED DIMMs\n"); ++ break; ++ default: ++ fprintf(stdout, "DDR INIT STATUS invalid\n"); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++struct cxl_cmd_membridge_stats_out { ++ // mem transaction counters ++ uint64_t m2s_req_count; ++ uint64_t m2s_rwd_count; ++ uint64_t s2m_drs_count; ++ uint64_t s2m_ndr_count; ++ // HPA logs for poison & out-of-range ++ uint64_t rwd_first_poison_hpa_log; ++ uint64_t rwd_latest_poison_hpa_log; ++ uint64_t req_first_hpa_log; ++ uint64_t rwd_first_hpa_log; ++ // correctible errors counters ++ uint32_t mst_m2s_req_corr_err_count; ++ uint32_t mst_m2s_rwd_corr_err_count; ++ // membridge fifo full/empty status ++ uint32_t fifo_full_status; ++ uint32_t fifo_empty_status; ++ // credit counters ++ uint8_t m2s_rwd_credit_count; ++ uint8_t m2s_req_credit_count; ++ uint8_t s2m_ndr_credit_count; ++ uint8_t s2m_drc_credit_count; ++ // rx state machine status 0 ++ uint8_t rx_fsm_status_rx_deinit; ++ uint8_t rx_fsm_status_m2s_req; ++ uint8_t rx_fsm_status_m2s_rwd; ++ uint8_t rx_fsm_status_ddr0_ar_req; ++ uint8_t rx_fsm_status_ddr0_aw_req; ++ uint8_t rx_fsm_status_ddr0_w_req; ++ // rx state machine status 1 ++ uint8_t rx_fsm_status_ddr1_ar_req; ++ uint8_t rx_fsm_status_ddr1_aw_req; ++ uint8_t rx_fsm_status_ddr1_w_req; ++ // tx state machine status 0 ++ uint8_t tx_fsm_status_tx_deinit; ++ uint8_t tx_fsm_status_s2m_ndr; ++ uint8_t tx_fsm_status_s2m_drc; ++ // stat QoS TEL ++ uint8_t stat_qos_tel_dev_load_read; ++ uint8_t stat_qos_tel_dev_load_type2_read; ++ uint8_t stat_qos_tel_dev_load_write; ++ uint8_t resvd; ++} __attribute__((packed)); ++ ++#define CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_STATS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_STATS_OPCODE 0xFB18 ++ ++CXL_EXPORT int cxl_memdev_get_cxl_membridge_stats(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_cmd_membridge_stats_out *stats; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_STATS_OPCODE); ++ ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ ++ if (rc != 0) { ++ fprintf(stderr, "%s: Read failed, firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_STATS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_GET_CXL_MEMBRIDGE_STATS); ++ return -EINVAL; ++ } ++ ++ stats = (void *)cmd->send_cmd->out.payload; ++ // print membridge statistics info ++ fprintf(stderr, "m2s_req_count: %lu\n", stats->m2s_req_count); ++ fprintf(stderr, "m2s_rwd_count: %lu\n", stats->m2s_rwd_count); ++ fprintf(stderr, "s2m_drs_count: %lu\n", stats->s2m_drs_count); ++ fprintf(stderr, "s2m_ndr_count: %lu\n", stats->s2m_ndr_count); ++ fprintf(stderr, "rwd_first_poison_hpa: 0x%lx\n", stats->rwd_first_poison_hpa_log); ++ fprintf(stderr, "rwd_latest_poison_hpa: 0x%lx\n", stats->rwd_latest_poison_hpa_log); ++ fprintf(stderr, "req_first_hpa_log: 0x%lx\n", stats->req_first_hpa_log); ++ fprintf(stderr, "rwd_first_hpa_log: 0x%lx\n", (u64)stats->rwd_first_hpa_log); ++ fprintf(stderr, "m2s_req_corr_err_count: %u\n", stats->mst_m2s_req_corr_err_count); ++ fprintf(stderr, "m2s_rwd_corr_err_count: %u\n", stats->mst_m2s_rwd_corr_err_count); ++ fprintf(stderr, "fifo_full_status: 0x%x\n", stats->fifo_full_status); ++ fprintf(stderr, "fifo_empty_status: 0x%x\n", stats->fifo_empty_status); ++ fprintf(stderr, "m2s_rwd_credit_count: %u\n", stats->m2s_rwd_credit_count); ++ fprintf(stderr, "m2s_req_credit_count: %u\n", stats->m2s_req_credit_count); ++ fprintf(stderr, "s2m_ndr_credit_count: %u\n", stats->s2m_ndr_credit_count); ++ fprintf(stderr, "s2m_drc_credit_count: %u\n", stats->s2m_drc_credit_count); ++ fprintf(stderr, "rx_status_rx_deinit: 0x%x\n", stats->rx_fsm_status_rx_deinit); ++ fprintf(stderr, "rx_status_m2s_req: 0x%x\n", stats->rx_fsm_status_m2s_req); ++ fprintf(stderr, "rx_status_m2s_rwd: 0x%x\n", stats->rx_fsm_status_m2s_rwd); ++ fprintf(stderr, "rx_status_ddr0_ar_req: 0x%x\n", stats->rx_fsm_status_ddr0_ar_req); ++ fprintf(stderr, "rx_status_ddr0_aw_req: 0x%x\n", stats->rx_fsm_status_ddr0_aw_req); ++ fprintf(stderr, "rx_status_ddr0_w_req: 0x%x\n", stats->rx_fsm_status_ddr0_w_req); ++ fprintf(stderr, "rx_status_ddr1_ar_req: 0x%x\n", stats->rx_fsm_status_ddr1_ar_req); ++ fprintf(stderr, "rx_status_ddr1_aw_req: 0x%x\n", stats->rx_fsm_status_ddr1_aw_req); ++ fprintf(stderr, "rx_status_ddr1_w_req: 0x%x\n", stats->rx_fsm_status_ddr1_w_req); ++ fprintf(stderr, "tx_status_tx_deinit: 0x%x\n", stats->tx_fsm_status_tx_deinit); ++ fprintf(stderr, "tx_status_s2m_ndr: 0x%x\n", stats->tx_fsm_status_s2m_ndr); ++ fprintf(stderr, "tx_status_s2m_drc: 0x%x\n", stats->tx_fsm_status_s2m_drc); ++ fprintf(stderr, "qos_tel_dev_load_read: %u\n", stats->stat_qos_tel_dev_load_read); ++ fprintf(stderr, "qos_tel_dev_load_type2_read:%u\n", stats->stat_qos_tel_dev_load_type2_read); ++ fprintf(stderr, "qos_tel_dev_load_write: %u\n", stats->stat_qos_tel_dev_load_write); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_TRIGGER_COREDUMP CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_TRIGGER_COREDUMP_OPCODE 0xFB1A ++ ++CXL_EXPORT int cxl_memdev_trigger_coredump(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_TRIGGER_COREDUMP_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_TRIGGER_COREDUMP) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_TRIGGER_COREDUMP); ++ return -EINVAL; ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_ERR_INJ_EN CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_ERR_INJ_EN_OPCODE 0xFB19 ++ ++ ++struct cxl_ddr_err_inj_en_in { ++ uint32_t ddr_id; ++ uint32_t err_type; ++ uint64_t ecc_fwc_mask; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_ddr_err_inj_en(struct cxl_memdev *memdev, u32 ddr_id, u32 err_type, u64 ecc_fwc_mask) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_err_inj_en_in *ddr_err_inj_en_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_ERR_INJ_EN_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_err_inj_en_in = (void *) cmd->send_cmd->in.payload; ++ ddr_err_inj_en_in->ddr_id = ddr_id; ++ ddr_err_inj_en_in->err_type = err_type; ++ ddr_err_inj_en_in->ecc_fwc_mask = ecc_fwc_mask; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_ERR_INJ_EN) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_ERR_INJ_EN); ++ return -EINVAL; ++ } ++ fprintf(stderr, "Error injection enabled on DDR%d\n", ddr_id); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_DDR_DIMM_LEVEL_TRAINING_STATUS CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_DIMM_LEVEL_TRAINING_STATUS_OPCODE 0xFB20 ++ ++#define DDR_MAX_CS 4 ++#define DDR_CS_DEVICE_MAX 18 ++#define DDR_REG_MAX_NIBBLE 9 ++#define DDR_MAX_SLICE 9 ++#define DDR_MAX_SLICE_BIT 8 ++ ++struct ddr_phy_pll_status { ++ uint32_t bs0_status; ++ uint32_t bs1_status; ++}; ++ ++struct ddr_wr_levelling_status { ++ uint32_t lower_nibble_err[DDR_REG_MAX_NIBBLE]; ++ uint32_t upper_nibble_err[DDR_REG_MAX_NIBBLE]; ++}; ++ ++struct ddr_read_gate_training_status { ++ uint32_t lower_nibble_min_err[DDR_REG_MAX_NIBBLE]; ++ uint32_t lower_nibble_max_err[DDR_REG_MAX_NIBBLE]; ++ uint32_t upper_nibble_min_err[DDR_REG_MAX_NIBBLE]; ++ uint32_t upper_nibble_max_err[DDR_REG_MAX_NIBBLE]; ++}; ++ ++struct ddr_margin_vref_data { ++ float lower_nibble_vref_low_volt[DDR_REG_MAX_NIBBLE]; ++ float lower_nibble_vref_high_volt[DDR_REG_MAX_NIBBLE]; ++ float upper_nibble_vref_low_volt[DDR_REG_MAX_NIBBLE]; ++ float upper_nibble_vref_high_volt[DDR_REG_MAX_NIBBLE]; ++}; ++ ++struct ddr_margin_write_dq_vref_data { ++ float vref_low_volt[DDR_CS_DEVICE_MAX]; ++ float vref_high_volt[DDR_CS_DEVICE_MAX]; ++}; ++ ++struct ddr_margin_write_dq_vref_data_cs { ++ float vref_low_volt_cs[DDR_MAX_CS][DDR_CS_DEVICE_MAX]; ++ float vref_high_volt_cs[DDR_MAX_CS][DDR_CS_DEVICE_MAX]; ++}; ++ ++struct ddr_margin_rdlvl_delay_dqs_rise_data { ++ uint32_t te_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ uint32_t le_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float te_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float le_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++}; ++ ++struct ddr_margin_rdlvl_delay_dqs_fall_data { ++ uint32_t te_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ uint32_t le_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float te_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float le_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++}; ++ ++struct ddr_margin_wrdqlvl_delay_data { ++ uint32_t te_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ uint32_t le_delay_data[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float te_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++ float le_delay_time[DDR_MAX_SLICE][DDR_MAX_SLICE_BIT]; ++}; ++ ++struct ddr_dimm_training_status { ++ struct ddr_phy_pll_status phy_pll_status; ++ struct ddr_wr_levelling_status wr_levl_status; ++ struct ddr_read_gate_training_status rd_gate_tr_status; ++ struct ddr_margin_vref_data vref_data; ++ struct ddr_margin_write_dq_vref_data wdq_vref_data; ++ struct ddr_margin_write_dq_vref_data_cs wdq_vref_data_cs; ++ struct ddr_margin_rdlvl_delay_dqs_rise_data rddqslvl_rise_data; ++ struct ddr_margin_rdlvl_delay_dqs_fall_data rddqslvl_fall_data; ++ struct ddr_margin_wrdqlvl_delay_data wrdqlvl_delay_data; ++ uint32_t err_status; ++}; ++ ++struct cxl_ddr_dimm_level_training_status_out { ++ struct ddr_dimm_training_status dimm_training_status[2]; ++} __attribute__((packed)); ++ ++void print_ddr_training_status(uint32_t instance, struct ddr_dimm_training_status *dimm_tr_status); ++void print_read_gate_training_status(uint32_t instance, struct ddr_read_gate_training_status* rd_gate_tr_status); ++void print_write_levelling_status(uint32_t instance, struct ddr_wr_levelling_status* wr_levl_status); ++void print_ddr_phy_pll_status(uint32_t instance, struct ddr_phy_pll_status* phy_pll_status); ++void print_ddr_training_status(uint32_t instance, struct ddr_dimm_training_status *dimm_tr_status); ++void print_margin_vref_low_high(uint32_t instance, struct ddr_dimm_training_status *dimm_tr_status); ++void print_margin_rdlvl_delay_window(int instance, struct ddr_dimm_training_status *dimm_tr_status); ++void print_margin_wrdqlvl_delay_window(int instance, struct ddr_dimm_training_status *dimm_tr_status); ++void print_err_status(int instance, struct ddr_dimm_training_status *dimm_tr_status); ++ ++/* DDR phy pll status */ ++void print_ddr_phy_pll_status(uint32_t instance, struct ddr_phy_pll_status* phy_pll_status) { ++ uint32_t read_data; ++ ++ read_data = phy_pll_status->bs0_status; ++ fprintf(stdout, "DDR%d PHY PLL Status: \n", instance); ++ fprintf(stdout, "\tOBS0: \n"); ++ fprintf(stdout, "\t\tPLL Lock Status = %d \n", (read_data & 1)); ++ fprintf(stdout, "\t\tReady = %d \n", ((read_data & 0x2) >> 1)); ++ fprintf(stdout, "\t\tLock assert count = 0x%x \n", ((read_data & 0x7F8) >> 3)); ++ ++ read_data = phy_pll_status->bs1_status; ++ fprintf(stdout, "\tOBS1: \n"); ++ fprintf(stdout, "\t\tPLL Lock Status = %d \n", (read_data & 1)); ++ fprintf(stdout, "\t\tReady = %d \n", ((read_data & 0x2) >> 1)); ++ fprintf(stdout, "\t\tLock assert count = 0x%x \n\n", ((read_data & 0x7F8) >> 3)); ++} ++ ++void print_write_levelling_status(uint32_t instance, struct ddr_wr_levelling_status* wr_levl_status) { ++ uint32_t read_data = 0; ++ int i = 0; ++ ++ fprintf(stdout, "\t\tBYTE# \t\t\t\t 0 \t 1 \t 2 \t 3 \t 4 \t 5 \t 6 \t 7 \t 8\n"); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t\tLOWER NIBBLE ERROR FLAG \t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x1000)>> 12); ++ read_data = wr_levl_status->lower_nibble_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x1000)>> 12); ++ ++ i = 0; ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t\tUPPER NIBBLE ERROR FLAG \t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x4000)>> 14); ++ read_data = wr_levl_status->upper_nibble_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x4000)>> 14); ++} ++ ++void print_read_gate_training_status(uint32_t instance, struct ddr_read_gate_training_status* rd_gate_tr_status) ++{ ++ uint32_t read_data = 0; ++ int i = 0; ++ ++ fprintf(stdout, "\t\tBYTE# \t\t\t\t 0 \t 1 \t 2 \t 3 \t 4 \t 5 \t 6 \t 7 \t 8\n"); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t\tLOWER NIBBLE MIN ERROR \t\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x80)>> 7); ++ read_data = rd_gate_tr_status->lower_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x80)>> 7); ++ ++ i = 0; ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t\tLOWER NIBBLE MAX ERROR \t\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x100)>> 8); ++ read_data = rd_gate_tr_status->lower_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x100)>> 8); ++ ++ i = 0; ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t\tUPPER NIBBLE MIN ERROR \t\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x200)>> 9); ++ read_data = rd_gate_tr_status->upper_nibble_min_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x200)>> 9); ++ ++ i = 0; ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t\tUPPER NIBBLE MAX ERROR \t\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d", (read_data & 0x400)>> 10); ++ read_data = rd_gate_tr_status->upper_nibble_max_err[i++]; ++ fprintf(stdout, "\t %d\n", (read_data & 0x400)>> 10); ++} ++ ++void print_ddr_training_status(uint32_t instance, struct ddr_dimm_training_status *dimm_tr_status) { ++ fprintf(stdout, "DDR%d TRAINING STATUS: \n", instance); ++ fprintf(stdout, "\tWRITE LEVELLING STATUS: \n"); ++ print_write_levelling_status(instance, &dimm_tr_status->wr_levl_status); ++ fprintf(stdout, "\n\tREAD GATE TRAINING STATUS: \n"); ++ print_read_gate_training_status(instance, &dimm_tr_status->rd_gate_tr_status); ++} ++ ++void print_margin_vref_low_high(uint32_t instance, struct ddr_dimm_training_status *dimm_tr_status) ++{ ++ int i = 0, j = 0; ++ float vref_low_volt, vref_high_volt; ++ ++ fprintf(stdout, "DDR%d MARGIN VALUES:\n", instance); ++ fprintf(stdout, "\tREAD LEVEL VREF: \n"); ++ fprintf(stdout, "\t\t VREF_LOW VREF_LOW_VOLT(mV) VREF_HIGH VREF_HIGH_VOLT(mV) VREF_MARGIN(mV)\n"); ++ for(i=0; i<9; i++) ++ { ++ fprintf(stdout, "\t\tSlice%d Lower Nibble: ", i); ++ vref_low_volt = dimm_tr_status->vref_data.lower_nibble_vref_low_volt[i]; ++ fprintf(stdout, "%04.2f ", vref_low_volt); ++ vref_high_volt = dimm_tr_status->vref_data.lower_nibble_vref_high_volt[i]; ++ fprintf(stdout, "%04.2f ", vref_high_volt); ++ fprintf(stdout, "%0.2f\n", (vref_high_volt - vref_low_volt)); ++ ++ fprintf(stdout, "\t\tSlice%d Upper Nibble: ", i); ++ vref_low_volt = dimm_tr_status->vref_data.upper_nibble_vref_low_volt[i]; ++ fprintf(stdout, "%04.2f ", vref_low_volt); ++ vref_high_volt = dimm_tr_status->vref_data.upper_nibble_vref_high_volt[i]; ++ fprintf(stdout, "%04.2f ", vref_high_volt); ++ fprintf(stdout, "%0.2f\n\n", (vref_high_volt - vref_low_volt)); ++ } ++ fprintf(stdout, "\tWRITE DQ LEVEL VREF: \n"); ++ fprintf(stdout, "\t\t VREF_LOW VREF_LOW_VOLT(mV) VREF_HIGH VREF_HIGH_VOLT(mV) VREF_MARGIN(mV)\n"); ++ for(i=0; i<18; i++) ++ { ++ fprintf(stdout, "\t\tCS0 Device%d :\t", i); ++ vref_low_volt = dimm_tr_status->wdq_vref_data.vref_low_volt[i]; ++ fprintf(stdout, "%04.1f ", vref_low_volt); ++ vref_high_volt = dimm_tr_status->wdq_vref_data.vref_high_volt[i]; ++ fprintf(stdout, "%04.1f ", vref_high_volt); ++ fprintf(stdout, "%0.1f \n", (vref_high_volt - vref_low_volt)); ++ } ++ for(j=1; j<4; j++) ++ { ++ for(i=0; i<18; i++) ++ { ++ fprintf(stdout, "\t\tCS%d Device%d :\t", j, i); ++ vref_low_volt = dimm_tr_status->wdq_vref_data_cs.vref_low_volt_cs[j][i]; ++ fprintf(stdout, "%04.1f ", vref_low_volt); ++ vref_high_volt = dimm_tr_status->wdq_vref_data_cs.vref_high_volt_cs[j][i]; ++ fprintf(stdout, "%04.1f ", vref_high_volt); ++ fprintf(stdout, "%0.1f \n", (vref_high_volt - vref_low_volt)); ++ } ++ fprintf(stdout, "\n"); ++ } ++} ++ ++void print_margin_rdlvl_delay_window(int instance, struct ddr_dimm_training_status *dimm_tr_status) ++{ ++ uint32_t te_delay_data = 0, le_delay_data = 0; ++ int i = 0, j = 0; ++ float te_delay_time, le_delay_time; ++ ++ printf("DDR%d Margin Delays: \n", instance); ++ printf("\tREAD DQSLEVEL RISE DELAY WINDOW: \n"); ++ printf("\t\t TE_DATA TE_DELAY(ns) LE_DATA LE_DELAY(ns) RD_RISE_DELAY(ns)\n"); ++ for(j=0; j<9; j++) ++ { ++ for(i=0; i<8; i++) ++ { ++ printf("\t\tSLICE%d BIT%d ", j, i); ++ te_delay_data = dimm_tr_status->rddqslvl_rise_data.te_delay_data[j][i]; ++ printf("%d ", te_delay_data); ++ te_delay_time = dimm_tr_status->rddqslvl_rise_data.te_delay_time[j][i]; ++ printf("%0.03f ", te_delay_time); ++ le_delay_data = dimm_tr_status->rddqslvl_rise_data.le_delay_data[j][i]; ++ printf("%02d ", le_delay_data); ++ le_delay_time = dimm_tr_status->rddqslvl_rise_data.le_delay_time[j][i]; ++ printf("%0.03f ", le_delay_time); ++ printf("%0.03f\n", (te_delay_time - le_delay_time)); ++ } ++ printf("\n"); ++ } ++ printf("\tREAD DQSLEVEL FALL DELAY WINDOW: \n"); ++ printf("\t\t TE_DATA TE_DELAY(ns) LE_DATA LE_DELAY(ns) RD_FALL_DELAY(ns)\n"); ++ for(j=0; j<9; j++) ++ { ++ for(i=0; i<8; i++) ++ { ++ printf("\t\tSLICE%d BIT%d ", j, i); ++ te_delay_data = dimm_tr_status->rddqslvl_fall_data.te_delay_data[j][i]; ++ printf("%d ", te_delay_data); ++ te_delay_time = dimm_tr_status->rddqslvl_fall_data.te_delay_time[j][i]; ++ printf("%0.03f ", te_delay_time); ++ le_delay_data = dimm_tr_status->rddqslvl_fall_data.le_delay_data[j][i]; ++ printf("%02d ", le_delay_data); ++ le_delay_time = dimm_tr_status->rddqslvl_fall_data.le_delay_time[j][i]; ++ printf("%0.03f ", le_delay_time); ++ printf("%0.03f\n", (te_delay_time - le_delay_time)); ++ } ++ printf("\n"); ++ } ++} ++ ++void print_margin_wrdqlvl_delay_window(int instance, struct ddr_dimm_training_status *dimm_tr_status) ++{ ++ uint32_t te_delay_data = 0, le_delay_data = 0; ++ int i = 0, j = 0; ++ float te_delay_time, le_delay_time; ++ ++ printf("\tWRITE DQLEVEL DELAY WINDOW: \n"); ++ printf("\t\t TE_DATA TE_DELAY(ns) LE_DATA LE_DELAY(ns) WRDQLVL_DELAY(ns)\n"); ++ for(j=0; j<9; j++) ++ { ++ for(i=0; i<8; i++) ++ { ++ printf("\t\tSLICE%d BIT%d ", j, i); ++ te_delay_data = dimm_tr_status->wrdqlvl_delay_data.te_delay_data[j][i]; ++ printf("%d ", te_delay_data); ++ te_delay_time = dimm_tr_status->wrdqlvl_delay_data.te_delay_time[j][i]; ++ printf("%0.03f ", te_delay_time); ++ le_delay_data = dimm_tr_status->wrdqlvl_delay_data.le_delay_data[j][i]; ++ printf("%02d ", le_delay_data); ++ le_delay_time = dimm_tr_status->wrdqlvl_delay_data.le_delay_time[j][i]; ++ printf("%0.03f ", le_delay_time); ++ printf("%0.03f\n", (te_delay_time - le_delay_time)); ++ } ++ printf("\n"); ++ } ++} ++ ++void print_err_status(int instance, struct ddr_dimm_training_status *dimm_tr_status) ++{ ++ uint32_t read_data = dimm_tr_status->err_status; ++ ++ fprintf(stdout, "DIMM %d Tranining status\n", instance); ++ fprintf(stdout, "\tWRLVL_ERR = %d\n", (read_data>>4)&0x1); ++ fprintf(stdout, "\tGTLVL_ERR = %d\n", (read_data>>3)&0x1); ++ fprintf(stdout, "\tRDLVL_ERR = %d\n", (read_data>>2)&0x1); ++ fprintf(stdout, "\tWDQLVL_ERR = %d\n", (read_data>>5)&0x1); ++ fprintf(stdout, "\tCA PARTIY ERR = %d\n", (read_data>>1)&0x1); ++} ++ ++CXL_EXPORT int cxl_memdev_ddr_dimm_level_training_status(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_dimm_level_training_status_out *dimm_tr_status; ++ int rc = 0; ++ int i; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_DIMM_LEVEL_TRAINING_STATUS_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_DIMM_LEVEL_TRAINING_STATUS) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_DIMM_LEVEL_TRAINING_STATUS); ++ return -EINVAL; ++ } ++ dimm_tr_status = (void *)cmd->send_cmd->out.payload; ++ for (i = DDR_CTRL0; i < DDR_MAX_SUBSYS; i++) { ++ fprintf(stdout, "dimm:%d level training status\n", i); ++ print_ddr_phy_pll_status(i, &dimm_tr_status->dimm_training_status[i].phy_pll_status); ++ print_ddr_training_status(i, &dimm_tr_status->dimm_training_status[i]); ++ print_margin_vref_low_high(i, &dimm_tr_status->dimm_training_status[i]); ++ print_margin_rdlvl_delay_window(i, &dimm_tr_status->dimm_training_status[i]); ++ print_margin_wrdqlvl_delay_window(i, &dimm_tr_status->dimm_training_status[i]); ++ print_err_status(i, &dimm_tr_status->dimm_training_status[i]); ++ } ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR PARAM SET */ ++#define CXL_MEM_COMMAND_ID_DDR_PARAM_SET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_PARAM_SET_OPCODE 0xFB1E ++#define CXL_MEM_COMMAND_ID_DDR_PARAM_SET_PAYLOAD_IN_SIZE 4 ++ ++struct ddr_interleave_options { ++ uint8_t ddr_interleave_sz; ++ uint8_t ddr_interleave_ctrl_choice; ++} __attribute__((packed)); ++ ++ ++struct cxl_mbox_ddr_param_set_in { ++ struct ddr_interleave_options ddr_inter; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_param_set(struct cxl_memdev *memdev, u32 ddr_interleave_sz, ++ u32 ddr_interleave_ctrl_choice) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_ddr_param_set_in *ddr_param_set_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_PARAM_SET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_PARAM_SET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ ddr_param_set_in = (void *) cmd->send_cmd->in.payload; ++ ++ ddr_param_set_in->ddr_inter.ddr_interleave_sz = ddr_interleave_sz; ++ ddr_param_set_in->ddr_inter.ddr_interleave_ctrl_choice = ddr_interleave_ctrl_choice; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_PARAM_SET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_PARAM_SET); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR PARAM GET */ ++#define CXL_MEM_COMMAND_ID_CXL_DDR_PARAM_GET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CXL_DDR_PARAM_GET_OPCODE 0xFB1F ++ ++struct cxl_ddr_param_get_out ++{ ++ struct ddr_interleave_options ddr_inter; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_ddr_param_get(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_ddr_param_get_out *ddr_param_get_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CXL_DDR_PARAM_GET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CXL_DDR_PARAM_GET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_CXL_DDR_PARAM_GET); ++ return -EINVAL; ++ } ++ ++ ddr_param_get_out = (struct cxl_ddr_param_get_out *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "ddr_interleave_sz: %d\n", ddr_param_get_out->ddr_inter.ddr_interleave_sz); ++ fprintf(stdout, "ddr_interleave_ctrl_choice: %d\n", ddr_param_get_out->ddr_inter.ddr_interleave_ctrl_choice); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* CORE VOLTAGE SET */ ++#define CXL_MEM_COMMAND_ID_CORE_VOLT_SET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CORE_VOLT_SET_OPCODE 0xFB26 ++#define CXL_MEM_COMMAND_ID_CORE_VOLT_SET_PAYLOAD_IN_SIZE 4 ++ ++struct cxl_mbox_core_volt_set_in { ++ float core_volt; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_core_volt_set(struct cxl_memdev *memdev, float core_volt) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_core_volt_set_in *core_volt_set_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CORE_VOLT_SET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_CORE_VOLT_SET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ core_volt_set_in = (void *) cmd->send_cmd->in.payload; ++ ++ core_volt_set_in->core_volt = core_volt; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CORE_VOLT_SET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_CORE_VOLT_SET); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* CORE VOLTAGE GET */ ++#define CXL_MEM_COMMAND_ID_CXL_CORE_VOLT_GET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CXL_CORE_VOLT_GET_OPCODE 0xFB27 ++ ++struct cxl_core_volt_get_out ++{ ++ float core_volt; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_core_volt_get(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_core_volt_get_out *core_volt_get_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CXL_CORE_VOLT_GET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CXL_CORE_VOLT_GET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_CXL_CORE_VOLT_GET); ++ return -EINVAL; ++ } ++ ++ core_volt_get_out = (struct cxl_core_volt_get_out *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "Core Voltage: %f V\n", core_volt_get_out->core_volt); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++#define CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL_OPCODE 0xFB21 ++#define CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL_PAYLOAD_IN_SIZE 4 ++ ++struct cxl_mbox_oem_err_inj_viral_in { ++ u32 viral_type; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_oem_err_inj_viral(struct cxl_memdev *memdev, ++ u32 viral_type) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_oem_err_inj_viral_in *err_inj_viral_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ err_inj_viral_in = (void *) cmd->send_cmd->in.payload; ++ ++ err_inj_viral_in->viral_type = viral_type; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_OEM_ERR_INJ_VIRAL); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++#define CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON_OPCODE 0xFB22 ++#define CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON_PAYLOAD_IN_SIZE 8 ++ ++struct cxl_mbox_err_inj_ll_poison_in { ++ u32 en_dis; ++ u32 ll_err_type; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_err_inj_ll_poison(struct cxl_memdev *memdev, ++ u32 en_dis, u32 ll_err_type) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_err_inj_ll_poison_in *err_inj_ll_poison_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ err_inj_ll_poison_in = (void *) cmd->send_cmd->in.payload; ++ ++ err_inj_ll_poison_in->ll_err_type = ll_err_type; ++ err_inj_ll_poison_in->en_dis = en_dis; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_ERR_INJ_LL_POISON); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++#define CXL_MEM_COMMAND_ID_PCI_ERR_INJ CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_PCI_ERR_INJ_OPCODE 0xFB25 ++#define CXL_MEM_COMMAND_ID_PCI_ERR_INJ_PAYLOAD_IN_SIZE 24 ++ ++struct cxl_mbox_pci_err_inj_in { ++ u32 en_dis; ++ u32 err_type; ++ u32 err_subtype; ++ u32 count; ++ u32 opt_param1; ++ u32 opt_param2; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_pci_err_inj(struct cxl_memdev *memdev, ++ u32 en_dis, ++ u32 err_type, ++ u32 err_subtype, ++ u32 count, ++ u32 opt_param1, ++ u32 opt_param2) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_pci_err_inj_in *pci_err_inj_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_PCI_ERR_INJ_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_PCI_ERR_INJ_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ pci_err_inj_in = (void *) cmd->send_cmd->in.payload; ++ pci_err_inj_in->en_dis = en_dis; ++ pci_err_inj_in->err_type = err_type; ++ pci_err_inj_in->err_subtype = err_subtype; ++ pci_err_inj_in->count = count; ++ pci_err_inj_in->opt_param1 = opt_param1; ++ pci_err_inj_in->opt_param2 = opt_param2; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_PCI_ERR_INJ) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_PCI_ERR_INJ); ++ return -EINVAL; ++ } ++ ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++#define CXL_MEM_COMMAND_ID_READ_LTSSM_STATES CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_READ_LTSSM_STATES_OPCODE 0xFB01 ++#define LTSSM_DUMP_SIZE 0x200 ++#define LTSSM_EXPECTED_STATE 0x11 ++#define LTSSM_STATE_DUMP_COUNT_MAX (LTSSM_DUMP_SIZE / 4) ++ ++struct cxl_mbox_read_ltssm_states_out { ++ uint32_t ltssm_states[LTSSM_STATE_DUMP_COUNT_MAX]; ++} __attribute__((packed)); ++ ++ ++CXL_EXPORT int cxl_memdev_read_ltssm_states(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_read_ltssm_states_out *read_ltssm_states; ++ uint32_t *ltssm_val; ++ uint32_t offset = 0; ++ uint32_t curr_state; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_READ_LTSSM_STATES_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d:\n%s\n", ++ cxl_memdev_get_devname(memdev), rc, DEVICE_ERRORS[rc]); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_READ_LTSSM_STATES) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, CXL_MEM_COMMAND_ID_READ_LTSSM_STATES); ++ return -EINVAL; ++ } ++ ++ read_ltssm_states = (struct cxl_mbox_read_ltssm_states_out*)cmd->send_cmd->out.payload; ++ fprintf(stdout, "LTSSM STATE CHANGES\n"); ++ ltssm_val = read_ltssm_states->ltssm_states; ++ if ((ltssm_val[offset] == ltssm_val[offset + 1]) && (ltssm_val[offset + 1] == 0x0)) { ++ fprintf(stdout, "ltssm state changes are not collected\n"); ++ goto out; ++ } ++ while (offset < LTSSM_STATE_DUMP_COUNT_MAX) { ++ if ((ltssm_val[offset] == ltssm_val[offset + 1]) && (ltssm_val[offset + 1] == 0x0)) ++ break; ++ curr_state = ltssm_val[offset++]; ++ fprintf(stdout, ++ "ltssm state val = 0x%x, %s\n", ++ curr_state, ++ ltssm_state_name[curr_state]); ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++ return 0; ++} ++ ++/* DDR PAGE SELECT SET */ ++#define CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET_OPCODE 0xFB2A ++#define CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET_PAYLOAD_IN_SIZE 4 ++ ++struct page_policy_selection { ++ uint8_t page_policy_reg_val; ++} __attribute__((packed)) page_policy_select; ++ ++ ++struct cxl_mbox_handle_page_selection_in { ++ struct page_policy_selection pp_select; ++} __attribute__((packed)); ++ ++ ++ ++CXL_EXPORT int cxl_memdev_ddr_page_select_set(struct cxl_memdev *memdev, ++ u32 page_select_option) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_handle_page_selection_in *handle_page_selection_in; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* update payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ handle_page_selection_in = (void *) cmd->send_cmd->in.payload; ++ ++ handle_page_selection_in->pp_select.page_policy_reg_val = page_select_option; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_DDR_PAGE_SELECT_SET); ++ return -EINVAL; ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} ++ ++/* DDR PAGE SELECT GET */ ++#define CXL_MEM_COMMAND_ID_CXL_DDR_PAGE_SELECT_GET CXL_MEM_COMMAND_ID_RAW ++#define CXL_MEM_COMMAND_ID_CXL_DDR_PAGE_SELECT_GET_OPCODE 0xFB2B ++ ++struct cxl_mbox_handle_page_selection_out { ++ struct page_policy_selection pp_select; ++} __attribute__((packed)); ++ ++CXL_EXPORT int cxl_memdev_ddr_page_select_get(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ struct cxl_mem_query_commands *query; ++ struct cxl_command_info *cinfo; ++ struct cxl_mbox_handle_page_selection_out *handle_page_selection_out; ++ int rc = 0; ++ ++ cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_CXL_DDR_PAGE_SELECT_GET_OPCODE); ++ if (!cmd) { ++ fprintf(stderr, "%s: cxl_cmd_new_raw returned Null output\n", ++ cxl_memdev_get_devname(memdev)); ++ return -ENOMEM; ++ } ++ ++ query = cmd->query_cmd; ++ cinfo = &query->commands[cmd->query_idx]; ++ ++ /* used to force correct payload size */ ++ cinfo->size_in = CXL_MEM_COMMAND_ID_LOG_INFO_PAYLOAD_IN_SIZE; ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ fprintf(stderr, "%s: cmd submission failed: %d (%s)\n", ++ cxl_memdev_get_devname(memdev), rc, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ fprintf(stderr, "%s: firmware status: %d\n", ++ cxl_memdev_get_devname(memdev), rc); ++ goto out; ++ } ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_CXL_DDR_PAGE_SELECT_GET) { ++ fprintf(stderr, "%s: invalid command id 0x%x (expecting 0x%x)\n", ++ cxl_memdev_get_devname(memdev), cmd->send_cmd->id, ++ CXL_MEM_COMMAND_ID_CXL_DDR_PAGE_SELECT_GET); ++ return -EINVAL; ++ } ++ ++ handle_page_selection_out = (struct cxl_mbox_handle_page_selection_out *)cmd->send_cmd->out.payload; ++ fprintf(stdout, "Page_Policy_Reg_Value is selected for %s\n", (handle_page_selection_out->pp_select.page_policy_reg_val)?"open":"close"); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return rc; ++} +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index aaf2d65..e36b0d4 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -70,7 +70,7 @@ global: + cxl_memdev_get_lsa; + cxl_memdev_cmd_identify; + cxl_memdev_get_supported_logs; +- cxl_memdev_get_cel_log; ++ cxl_memdev_get_log; + cxl_memdev_get_event_interrupt_policy; + cxl_memdev_set_event_interrupt_policy; + cxl_memdev_get_timestamp; +@@ -169,4 +169,48 @@ global: + cxl_memdev_dimm_spd_read; + cxl_memdev_ddr_training_status; + cxl_memdev_dimm_slot_info; ++ cxl_memdev_pmic_vtmon_info; ++ cxl_memdev_ddr_margin_run; ++ cxl_memdev_ddr_margin_status; ++ cxl_memdev_ddr_margin_get; ++ cxl_memdev_ddr_stats_run; ++ cxl_memdev_ddr_stats_status; ++ cxl_memdev_ddr_stats_get; ++ cxl_memdev_reboot_mode_set; ++ cxl_memdev_curr_cxl_boot_mode_get; ++ cxl_memdev_pcie_eye_run; ++ cxl_memdev_pcie_eye_status; ++ cxl_memdev_pcie_eye_get_sw; ++ cxl_memdev_pcie_eye_get_hw; ++ cxl_memdev_pcie_eye_get_sw_ber; ++ cxl_memdev_get_cxl_link_status; ++ cxl_memdev_get_device_info; ++ cxl_memdev_read_ddr_temp; ++ cxl_memdev_cxl_hpa_to_dpa; ++ cxl_memdev_get_cxl_membridge_errors; ++ cxl_memdev_get_ddr_bw; ++ cxl_memdev_get_ddr_latency; ++ cxl_memdev_i2c_read; ++ cxl_memdev_i2c_write; ++ cxl_memdev_get_ddr_ecc_err_info; ++ cxl_memdev_start_ddr_ecc_scrub; ++ cxl_memdev_ddr_ecc_scrub_status; ++ cxl_memdev_ddr_cont_scrub_status; ++ cxl_memdev_ddr_cont_scrub_set; ++ cxl_memdev_ddr_init_status; ++ cxl_memdev_get_cxl_membridge_stats; ++ cxl_memdev_trigger_coredump; ++ cxl_memdev_ddr_err_inj_en; ++ cxl_memdev_ddr_err_inj_en; ++ cxl_memdev_ddr_dimm_level_training_status; ++ cxl_memdev_ddr_param_set; ++ cxl_memdev_ddr_param_get; ++ cxl_memdev_core_volt_set; ++ cxl_memdev_core_volt_get; ++ cxl_memdev_oem_err_inj_viral; ++ cxl_memdev_err_inj_ll_poison; ++ cxl_memdev_pci_err_inj; ++ cxl_memdev_read_ltssm_states; ++ cxl_memdev_ddr_page_select_set; ++ cxl_memdev_ddr_page_select_get; + } LIBCXL_3; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 6583af5..4084daa 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -3,6 +3,7 @@ + #ifndef _LIBCXL_H_ + #define _LIBCXL_H_ + ++#include + #include + #include + #include +@@ -57,13 +58,13 @@ int cxl_memdev_set_lsa(struct cxl_memdev *memdev, void *buf, size_t length, + size_t offset); + int cxl_memdev_cmd_identify(struct cxl_memdev *memdev); + int cxl_memdev_device_info_get(struct cxl_memdev *memdev); +-int cxl_memdev_get_fw_info(struct cxl_memdev *memdev); ++int cxl_memdev_get_fw_info(struct cxl_memdev *memdev, bool is_os_img); + int cxl_memdev_transfer_fw(struct cxl_memdev *memdev, u8 action, + u8 slot, u32 offset, int size, unsigned char *data, u32 transfer_fw_opcode); + int cxl_memdev_activate_fw(struct cxl_memdev *memdev, u8 action, + u8 slot); + int cxl_memdev_get_supported_logs(struct cxl_memdev *memdev); +-int cxl_memdev_get_cel_log(struct cxl_memdev *memdev, const char *uuid); ++int cxl_memdev_get_log(struct cxl_memdev *memdev, const char *uuid, const unsigned int data_size); + int cxl_memdev_get_event_interrupt_policy(struct cxl_memdev *memdev); + int cxl_memdev_set_event_interrupt_policy(struct cxl_memdev *memdev, u32 int_policy); + int cxl_memdev_get_timestamp(struct cxl_memdev *memdev); +@@ -236,6 +237,51 @@ int cxl_memdev_dimm_spd_read(struct cxl_memdev *memdev, u32 spd_id, + u32 offset, u32 num_bytes); + int cxl_memdev_ddr_training_status(struct cxl_memdev *memdev); + int cxl_memdev_dimm_slot_info(struct cxl_memdev *memdev); ++int cxl_memdev_pmic_vtmon_info(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_margin_run(struct cxl_memdev *memdev, u8 slice_num, u8 rd_wr_margin, u8 ddr_id); ++int cxl_memdev_ddr_margin_status(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_margin_get(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_stats_run(struct cxl_memdev *memdev, u8 ddr_id, ++ u32 monitor_time, u32 loop_count); ++int cxl_memdev_ddr_stats_status(struct cxl_memdev *memdev, int* run_status, uint32_t* loop_count); ++int cxl_memdev_ddr_stats_get(struct cxl_memdev *memdev); ++int cxl_memdev_reboot_mode_set(struct cxl_memdev *memdev, u8 reboot_mode); ++int cxl_memdev_curr_cxl_boot_mode_get(struct cxl_memdev *memdev); ++int cxl_memdev_pcie_eye_run(struct cxl_memdev *memdev, u8 lane, u8 sw_scan, u8 ber); ++int cxl_memdev_pcie_eye_status(struct cxl_memdev *memdev); ++int cxl_memdev_pcie_eye_get_sw(struct cxl_memdev *memdev, uint offset); ++int cxl_memdev_pcie_eye_get_hw(struct cxl_memdev *memdev); ++int cxl_memdev_pcie_eye_get_sw_ber(struct cxl_memdev *memdev); ++int cxl_memdev_get_cxl_link_status(struct cxl_memdev *memdev); ++int cxl_memdev_get_device_info(struct cxl_memdev *memdev); ++int cxl_memdev_read_ddr_temp(struct cxl_memdev *memdev); ++int cxl_memdev_cxl_hpa_to_dpa(struct cxl_memdev *memdev, u64 hpa_address); ++int cxl_memdev_get_cxl_membridge_errors(struct cxl_memdev *memdev); ++int cxl_memdev_get_ddr_bw(struct cxl_memdev *memdev, u32 timeout, u32 iterations); ++int cxl_memdev_get_ddr_latency(struct cxl_memdev *memdev, u32 measure_time); ++int cxl_memdev_i2c_read(struct cxl_memdev *memdev, u16 slave_addr, u8 reg_addr, u8 num_bytes); ++int cxl_memdev_i2c_write(struct cxl_memdev *memdev, u16 slave_addr, u8 reg_addr, u8 data); ++int cxl_memdev_get_ddr_ecc_err_info(struct cxl_memdev *memdev); ++int cxl_memdev_start_ddr_ecc_scrub(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_ecc_scrub_status(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_cont_scrub_status(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_cont_scrub_set(struct cxl_memdev *memdev, u32 cont_scrub_status); ++int cxl_memdev_ddr_init_status(struct cxl_memdev *memdev); ++int cxl_memdev_get_cxl_membridge_stats(struct cxl_memdev *memdev); ++int cxl_memdev_trigger_coredump(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_err_inj_en(struct cxl_memdev *memdev, u32 ddr_id, u32 err_type, u64 ecc_fwc_mask); ++int cxl_memdev_ddr_dimm_level_training_status(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_param_set(struct cxl_memdev *memdev, u32 ddr_interleave_sz, ++ u32 ddr_interleave_ctrl_choice); ++int cxl_memdev_ddr_param_get(struct cxl_memdev *memdev); ++int cxl_memdev_core_volt_set(struct cxl_memdev *memdev, float core_volt); ++int cxl_memdev_core_volt_get(struct cxl_memdev *memdev); ++int cxl_memdev_oem_err_inj_viral(struct cxl_memdev *memdev, u32 viral_type); ++int cxl_memdev_err_inj_ll_poison(struct cxl_memdev *memdev, u32 en_dis, u32 ll_err_type); ++int cxl_memdev_pci_err_inj(struct cxl_memdev *memdev, u32 en_dis, u32 type, u32 err, u32 count, u32 opt1, u32 opt2); ++int cxl_memdev_read_ltssm_states(struct cxl_memdev *memdev); ++int cxl_memdev_ddr_page_select_set(struct cxl_memdev *memdev, u32 page_select_option); ++int cxl_memdev_ddr_page_select_get(struct cxl_memdev *memdev); + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +diff --git a/cxl/memdev.c b/cxl/memdev.c +index fba1f75..2ba16d4 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -54,6 +54,10 @@ OPT_UINTEGER('s', "size", ¶m.len, "number of label bytes to operate"), \ + OPT_UINTEGER('O', "offset", ¶m.offset, \ + "offset into the label area to start operation") + ++u64 hpa_address; ++#define HPA_OPTIONS() \ ++OPT_U64('h', "hpa", &hpa_address, "host physical address") ++ + static const struct option read_options[] = { + BASE_OPTIONS(), + LABEL_OPTIONS(), +@@ -92,9 +96,17 @@ static struct _log_uuid { + OPT_STRING('l', "log_uuid", &log_uuid.uuid, "log-uuid", \ + "CEL Log UUID") + +-static const struct option cmd_get_cel_log_options[] = { ++static struct _log_size { ++ u32 size; ++} log_size; ++ ++#define LOG_SIZE_OPTIONS() \ ++OPT_UINTEGER('s', "log_size", &log_size.size, "log-size") ++ ++static const struct option cmd_get_log_options[] = { + BASE_OPTIONS(), + LOG_UUID_OPTIONS(), ++ LOG_SIZE_OPTIONS(), + OPT_END(), + }; + +@@ -146,6 +158,13 @@ static struct _update_fw_params { + bool verbose; + } update_fw_params; + ++static struct _fw_img_params { ++ bool is_os; ++} fw_img_params; ++ ++#define FW_IMG_OPTIONS() \ ++OPT_BOOLEAN('z', "osimage", &fw_img_params.is_os, "select OS(a.k.a boot1) image") ++ + #define UPDATE_FW_OPTIONS() \ + OPT_FILENAME('f', "file", &update_fw_params.filepath, "rom-file", \ + "filepath to read ROM for firmware update"), \ +@@ -156,6 +175,7 @@ OPT_BOOLEAN('m', "mock", &update_fw_params.mock, "For testing purposes. Mock tra + static const struct option cmd_update_fw_options[] = { + BASE_OPTIONS(), + UPDATE_FW_OPTIONS(), ++ FW_IMG_OPTIONS(), + OPT_END(), + }; + +@@ -172,6 +192,7 @@ static const struct option cmd_device_info_get_options[] = { + + static const struct option cmd_get_fw_info_options[] = { + BASE_OPTIONS(), ++ FW_IMG_OPTIONS(), + OPT_END(), + }; + +@@ -1909,6 +1930,403 @@ static const struct option cmd_dimm_slot_info_options[] = { + OPT_END(), + }; + ++static const struct option cmd_pmic_vtmon_info_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_margin_run_params { ++ u32 slice_num; ++ u32 rd_wr_margin; ++ u32 ddr_id; ++ bool verbose; ++} ddr_margin_run_params; ++ ++#define DDR_MARGIN_RUN_OPTIONS() \ ++OPT_UINTEGER('s', "slice_num", &ddr_margin_run_params.slice_num, "SLICE NUMBER"), \ ++OPT_UINTEGER('m', "rd_wr_margin", &ddr_margin_run_params.rd_wr_margin, "RD/WR MARGIN"), \ ++OPT_UINTEGER('i', "ddr_id", &ddr_margin_run_params.ddr_id, "DDR ID") ++ ++static const struct option cmd_ddr_margin_run_options[] = { ++ BASE_OPTIONS(), ++ DDR_MARGIN_RUN_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_margin_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_margin_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_stats_run_params { ++ u32 ddr_id; ++ u32 monitor_time; ++ u32 loop_count; ++ bool verbose; ++} ddr_stats_run_params; ++ ++#define DDR_STATS_RUN_OPTIONS() \ ++OPT_UINTEGER('i', "ddr_id", &ddr_stats_run_params.ddr_id, "DDR ID"), \ ++OPT_UINTEGER('m', "monitor_time", &ddr_stats_run_params.monitor_time, "MOINTOR TIME MSEC"), \ ++OPT_UINTEGER('n', "loop_count", &ddr_stats_run_params.loop_count, "NUM ITERATION") ++ ++static const struct option cmd_ddr_stats_run_options[] = { ++ BASE_OPTIONS(), ++ DDR_STATS_RUN_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_stats_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _reboot_mode_set_params { ++ u32 reboot_mode; ++ bool verbose; ++} reboot_mode_set_params; ++ ++#define REBOOT_MODE_SET_OPTIONS() \ ++OPT_UINTEGER('m', "reboot_mode", &reboot_mode_set_params.reboot_mode, "0:CXL-IO-MEM or 0xCE:CXL-IO") ++ ++static const struct option cmd_reboot_mode_set_options[] = { ++ BASE_OPTIONS(), ++ REBOOT_MODE_SET_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_curr_cxl_boot_mode_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _pcie_eye_run_params { ++ u32 lane; ++ u32 sw_scan; ++ u32 ber; ++ bool verbose; ++} pcie_eye_run_params; ++ ++#define PCIE_EYE_RUN_OPTIONS() \ ++OPT_UINTEGER('l', "lane", &pcie_eye_run_params.lane, "LANE ID"), \ ++OPT_UINTEGER('s', "sw_scan", &pcie_eye_run_params.sw_scan, "SW SCAN"), \ ++OPT_UINTEGER('b', "ber", &pcie_eye_run_params.ber, "BER") ++ ++static const struct option cmd_pcie_eye_run_options[] = { ++ BASE_OPTIONS(), ++ PCIE_EYE_RUN_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_pcie_eye_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _pcie_eye_get_params { ++ u32 sw_scan; ++ u32 ber; ++ bool verbose; ++} pcie_eye_get_params; ++ ++#define PCIE_EYE_GET_OPTIONS() \ ++OPT_UINTEGER('s', "sw_scan", &pcie_eye_get_params.sw_scan, "SW SCAN"), \ ++OPT_UINTEGER('b', "ber", &pcie_eye_get_params.ber, "BER") ++ ++static const struct option cmd_pcie_eye_get_options[] = { ++ BASE_OPTIONS(), ++ PCIE_EYE_GET_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_get_cxl_link_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_get_device_info_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_read_ddr_temp_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_cxl_hpa_to_dpa_options[] = { ++ BASE_OPTIONS(), ++ HPA_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_get_cxl_membridge_errors_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _get_ddr_bw_params { ++ u32 timeout; ++ u32 iterations; ++ bool verbose; ++} get_ddr_bw_params; ++ ++#define GET_DDR_BW_OPTIONS() \ ++OPT_UINTEGER('t', "temeout", &get_ddr_bw_params.timeout, "Timeout"), \ ++OPT_UINTEGER('i', "iterations", &get_ddr_bw_params.iterations, "No Iterations") ++ ++static const struct option cmd_get_ddr_bw_options[] = { ++ BASE_OPTIONS(), ++ GET_DDR_BW_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _get_ddr_latency_params { ++ u32 measure_time; ++ bool verbose; ++} get_ddr_latency_params; ++ ++#define GET_DDR_LATENCY_OPTIONS() \ ++OPT_UINTEGER('t', "measure time", &get_ddr_latency_params.measure_time, "Measure Time in msec") ++ ++static const struct option cmd_get_ddr_latency_options[] = { ++ BASE_OPTIONS(), ++ GET_DDR_LATENCY_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _i2c_read_params { ++ u32 slave_addr; ++ u32 reg_addr; ++ u32 num_bytes; ++ bool verbose; ++} i2c_read_params; ++ ++#define I2C_READ_OPTIONS() \ ++OPT_UINTEGER('s', "slave_addr", &i2c_read_params.slave_addr, "Slave addr"), \ ++OPT_UINTEGER('r', "reg_addr", &i2c_read_params.reg_addr, "Reg addr"), \ ++OPT_UINTEGER('n', "num_bytes", &i2c_read_params.num_bytes, "Number of bytes") ++ ++static const struct option cmd_i2c_read_options[] = { ++ BASE_OPTIONS(), ++ I2C_READ_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _i2c_write_params { ++ u32 slave_addr; ++ u32 reg_addr; ++ u32 data; ++ bool verbose; ++} i2c_write_params; ++ ++#define I2C_WRITE_OPTIONS() \ ++OPT_UINTEGER('s', "slave_addr", &i2c_write_params.slave_addr, "Slave addr"), \ ++OPT_UINTEGER('r', "reg_addr", &i2c_write_params.reg_addr, "Reg addr"), \ ++OPT_UINTEGER('d', "data", &i2c_write_params.data, "Data") ++ ++static const struct option cmd_i2c_write_options[] = { ++ BASE_OPTIONS(), ++ I2C_WRITE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_get_ddr_ecc_err_info_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_start_ddr_ecc_scrub_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_ecc_scrub_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_cont_scrub_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_cont_scrub_set_params { ++ u32 cont_scrub_status; ++} ddr_cont_scrub_set_params; ++ ++#define DDR_CONT_SCRUB_SET_OPTIONS() \ ++OPT_UINTEGER('i', "cont_scrub_status", &ddr_cont_scrub_set_params.cont_scrub_status, "Continuous Scrub ON:1 OFF: 0") ++ ++static const struct option cmd_ddr_cont_scrub_set_options[] = { ++ BASE_OPTIONS(), ++ DDR_CONT_SCRUB_SET_OPTIONS(), ++ OPT_END(), ++}; ++ ++ ++static const struct option cmd_ddr_init_status_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_get_cxl_membridge_stats_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_trigger_coredump_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_err_inj_en_params { ++ u32 ddr_id; ++ u32 err_type; ++ u64 ecc_fwc_mask; ++ bool verbose; ++} ddr_err_inj_en_params; ++ ++#define DDR_ERR_INJ_EN_OPTIONS() \ ++OPT_UINTEGER('d', "ddr_id", &ddr_err_inj_en_params.ddr_id, "ddr id <0-DDR_CTRL0,1-DDR_CTRL1>"), \ ++OPT_UINTEGER('t', "err_type", &ddr_err_inj_en_params.err_type, "error type\n\t\t\t0: AXI bus parity READ ADDR\n\t\t\t1: AXI bus parity WRITE ADDR\n\t\t\t2: AXI bus parity WRITE DATA\n\t\t\t3: CA bus parity\n\t\t\t4: ECC correctable\n\t\t\t5: ECC uncorrectable\n\t\t\t6: ECC SCRUB"), \ ++OPT_U64('m', "ecc_fwc_mask", &ddr_err_inj_en_params.ecc_fwc_mask, "ecc fwc mask <35bit value, upto two bit set for correctable ecc error\n\t\t\tAtleast 4bits for uncoorectable ecc errors\n>") ++ ++static const struct option cmd_ddr_err_inj_en_options[] = { ++ BASE_OPTIONS(), ++ DDR_ERR_INJ_EN_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_dimm_level_training_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_set_params { ++ u32 ddr_interleave_sz; ++ u32 ddr_interleave_ctrl_choice; ++} ddr_set_params; ++ ++#define DDR_PARAM_SET_OPTIONS() \ ++ OPT_UINTEGER('m', "ddr_interleave_sz", &ddr_set_params.ddr_interleave_sz, "Intereleave SZ is: 2 pow m. Input the value of m as the Size"), \ ++ OPT_UINTEGER('n', "ddr_interleave_ctrl_choice", &ddr_set_params.ddr_interleave_ctrl_choice, "CTRL Choice: 1=DDR0 2=DDR1 3= DDR0 and DDR1") ++ ++static const struct option cmd_ddr_param_set_options[] = { ++ BASE_OPTIONS(), ++ DDR_PARAM_SET_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_param_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _ddr_core_volt_set_params { ++ u32 val1; ++ u32 val2; ++ u32 val3; ++} ddr_core_volt_set_params; ++ ++#define CORE_VOLT_SET_OPTIONS() \ ++OPT_UINTEGER('i', "core_volt_val1", &ddr_core_volt_set_params.val1, "CORE Voltage val1.val2 val3"), \ ++OPT_UINTEGER('m', "core_volt_val2", &ddr_core_volt_set_params.val2, "CORE Voltage val1.val2 val3"), \ ++OPT_UINTEGER('n', "core_volt_val3", &ddr_core_volt_set_params.val3, "CORE Voltage val1.val2 val3") ++ ++static const struct option cmd_core_volt_set_options[] = { ++ BASE_OPTIONS(), ++ CORE_VOLT_SET_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_core_volt_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _oem_err_inj_viral_params { ++ u32 viral_type; ++ bool verbose; ++} oem_err_inj_viral_params; ++ ++#define OEM_ERR_INJ_VIRAL_OPTIONS() \ ++OPT_UINTEGER('l', "viral_type", &oem_err_inj_viral_params.viral_type, "viral_type") ++ ++static const struct option cmd_oem_err_inj_viral_options[] = { ++ BASE_OPTIONS(), ++ OEM_ERR_INJ_VIRAL_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _err_inj_ll_poison_params { ++ u32 en_dis; ++ u32 ll_err_type; ++ bool verbose; ++} err_inj_ll_poison_params; ++ ++#define ERR_INJ_LL_POISON_OPTIONS() \ ++OPT_UINTEGER('e', "en_dis", &err_inj_ll_poison_params.en_dis, "enable_disable 0=dis,1=en"), \ ++OPT_UINTEGER('l', "ll_err_type", &err_inj_ll_poison_params.ll_err_type, "link level err type 0=mem-poison") ++ ++static const struct option cmd_err_inj_ll_poison_options[] = { ++ BASE_OPTIONS(), ++ ERR_INJ_LL_POISON_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct _pci_err_in_params { ++ u32 en_dis; ++ u32 err_type; ++ u32 err_subtype; ++ u32 count; ++ u32 opt_param1; ++ u32 opt_param2; ++ bool verbose; ++} pci_err_inj_params; ++ ++#define PCI_ERR_INJ_OPTIONS() \ ++OPT_UINTEGER('e', "en_dis", &pci_err_inj_params.en_dis, "enable_disable:\n\t0=disable inj\n\t1=enable inj"), \ ++OPT_UINTEGER('l', "err_type", &pci_err_inj_params.err_type, "err inj type Group:\n\t0:CRC ERR\n\t1:SEQ NUM ERR\n\t2:DLLP ERR\n\t3:SYMBOL ERR\n\t4:FC CREDIT ERR\n\t5:Special TLP ERR\n"), \ ++OPT_UINTEGER('s', "err_subtype", &pci_err_inj_params.err_subtype, "err inj sub-type:\n\tGroup-0:\n\t\t0 = TX_TLP_LCRC_ERR\n\t\t1 = TX_16B_CRC_ERR_ACK_NAK_DLLP\n\t\t2 = TX_16B_CRC_ERR_UPD_FC\n\t\t3 = TX_TLP_ECRC_ERR\n\t\t4 = TX_FCRC_ERR_TLP\n\t\t5 = TX_PARITY_TSOS_ERR\n\t\t6 = TX_PARITY_SKPOS_ERR\n\t\t8 = RX_LCRC_ERR\n\t\t11= RX_ECRC_ERR\n\n\tGroup-1:\n\t\t0 = TLP_ERR_SEQNUM\n\t\t1 = ACK_NAK_DLLP_ERR_SEQNUM\n\n\tGroup-2:\n\t\t0 = ACK_NACK_DLLP\n\t\t1 = UPD_FC_DLLP\n\t\t2 = NAK_DLLP\n\n\tGroup-3:\n\t\t0 = RSVD_OR_INVRT_SYNC_HDR\n\t\t1 = COM_PAD_TS1\n\t\t2 = COM_PAD_TS2\n\t\t3 = COM_FTS\n\t\t4 = COM_IDL\n\t\t5 = END_EDB\n\t\t6 = STP_SDP\n\t\t7 = COM_SKP\n\n\tGroup-4:\n\t\t0 = POSTED_TLP_HDR\n\t\t1 = NON_POSTED_TLP_HDR\n\t\t2 = CMPL_TLP_HDR\n\t\t4 = POSTED_TLP_DATA\n\t\t5 = NON_POSTED_TLP_DATA\n\n\tGroup-5:\n\t\t0 = DUPLICATE_DLLP\n\t\t1 = NULLIFIED_TLP\n"), \ ++OPT_UINTEGER('c', "count", &pci_err_inj_params.count, "err inj count:\n\t1-255: count of err to inject\n\t0: continuous inj until disable\n"), \ ++OPT_UINTEGER('x', "opt1", &pci_err_inj_params.opt_param1, "opt1: Optional Extra args1\n\tFor Group-1:Bad Sequence Number(2s compliment in hex): Min:0x1001, Max:0xfff\n\tFor Group-4:Bad update-FC credit val(2s compliment in hex): Min:0x1001, Max:0xfff\n\tFor other Groups: Pass value '0'\n"), \ ++OPT_UINTEGER('y', "opt2", &pci_err_inj_params.opt_param2, "opt2: Optional Extra args2\n\tGroup-4::Target VC_NUMBER: Min:0, Max:7\n\tFor other Groups:Pass value '0'\n") ++ ++static const struct option cmd_pci_err_inj_options[] = { ++ BASE_OPTIONS(), ++ PCI_ERR_INJ_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_read_ltssm_states_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static struct page_policy_selection { ++ int page_policy_reg_val; ++} page_policy_select; ++ ++#define DDR_PAGE_SELECT_SET_OPTIONS() \ ++ OPT_INTEGER('p', "page_policy_reg_val", &page_policy_select.page_policy_reg_val, "Value for page policy selection") ++ ++static const struct option cmd_ddr_page_select_set_options[] = { ++ BASE_OPTIONS(), ++ DDR_PAGE_SELECT_SET_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option cmd_ddr_page_select_get_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ + static int action_cmd_clear_event_records(struct cxl_memdev *memdev, struct action_context *actx) + { + u16 record_handle; +@@ -2133,10 +2551,16 @@ static int action_cmd_update_fw(struct cxl_memdev *memdev, struct action_context + } + + offset = 0; +- if (update_fw_params.hbo) { +- opcode = 0xCD01; // Pioneer vendor opcode for hbo-transfer-fw ++ ++ if (fw_img_params.is_os) { ++ printf("firmware update selected for OS Image\n"); ++ opcode = 0xCD04; // Vistara opcode for OS(boot1) image update + } else { +- opcode = 0x0201; // Spec defined transfer-fw ++ if (update_fw_params.hbo) { ++ opcode = 0xCD01; // Pioneer vendor opcode for hbo-transfer-fw ++ } else { ++ opcode = 0x0201; // Spec defined transfer-fw ++ } + } + + for (int i = 0; i < num_blocks; i++) +@@ -2248,15 +2672,15 @@ static int action_cmd_set_event_interrupt_policy(struct cxl_memdev *memdev, stru + return cxl_memdev_set_event_interrupt_policy(memdev, interrupt_policy_params.policy); + } + +-static int action_cmd_get_cel_log(struct cxl_memdev *memdev, struct action_context *actx) ++static int action_cmd_get_log(struct cxl_memdev *memdev, struct action_context *actx) + { + if (cxl_memdev_is_active(memdev)) { +- fprintf(stderr, "%s: memdev active, get_cel_log\n", ++ fprintf(stderr, "%s: memdev active, get_log\n", + cxl_memdev_get_devname(memdev)); + return -EBUSY; + } + +- return cxl_memdev_get_cel_log(memdev, log_uuid.uuid); ++ return cxl_memdev_get_log(memdev, log_uuid.uuid, log_size.size); + } + + static int action_cmd_get_supported_logs(struct cxl_memdev *memdev, struct action_context *actx) +@@ -2358,7 +2782,7 @@ static int action_cmd_get_fw_info(struct cxl_memdev *memdev, struct action_conte + return -EBUSY; + } + +- return cxl_memdev_get_fw_info(memdev); ++ return cxl_memdev_get_fw_info(memdev, fw_img_params.is_os); + } + + static int action_cmd_activate_fw(struct cxl_memdev *memdev, struct action_context *actx) +@@ -3463,32 +3887,594 @@ static int action_cmd_dimm_slot_info(struct cxl_memdev *memdev, struct action_co + return cxl_memdev_dimm_slot_info(memdev); + } + +-static int action_write(struct cxl_memdev *memdev, struct action_context *actx) ++static int action_cmd_pmic_vtmon_info(struct cxl_memdev *memdev, struct action_context *actx) + { +- size_t size = param.len, read_len; +- unsigned char *buf; +- int rc; ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort pmic_vtmon_info\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } + +- if (cxl_memdev_is_active(memdev)) { +- fprintf(stderr, "%s is active, abort label write\n", +- cxl_memdev_get_devname(memdev)); +- return -EBUSY; +- } ++ return cxl_memdev_pmic_vtmon_info(memdev); ++} + +- if (!size) { +- size_t lsa_size = cxl_memdev_get_lsa_size(memdev); ++static int action_cmd_ddr_margin_run(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_margin_run\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } + +- fseek(actx->f_in, 0L, SEEK_END); +- size = ftell(actx->f_in); +- fseek(actx->f_in, 0L, SEEK_SET); ++ return cxl_memdev_ddr_margin_run(memdev, ddr_margin_run_params.slice_num, ++ ddr_margin_run_params.rd_wr_margin, ++ ddr_margin_run_params.ddr_id); ++} + +- if (size > lsa_size) { +- fprintf(stderr, +- "File size (%zu) greater than LSA size (%zu), aborting\n", +- size, lsa_size); +- return -EINVAL; +- } +- } ++static int action_cmd_ddr_margin_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_margin_status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_margin_status(memdev); ++} ++ ++static int action_cmd_ddr_margin_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ int rc = 0; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_margin_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ rc = cxl_memdev_ddr_margin_get(memdev); ++ if(rc) ++ { ++ fprintf(stderr, ++ "ddr_margin_get read failed"); ++ goto abort; ++ } ++ ++abort: ++ return rc; ++} ++ ++static int action_cmd_ddr_stats_run(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_stats_run\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_stats_run(memdev, ddr_stats_run_params.ddr_id, ++ ddr_stats_run_params.monitor_time, ++ ddr_stats_run_params.loop_count); ++} ++ ++static int action_cmd_ddr_stats_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ int rc = 0; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_stats_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ rc = cxl_memdev_ddr_stats_get(memdev); ++ if(rc) ++ { ++ fprintf(stderr, ++ "ddr_stats_get read failed"); ++ goto abort; ++ } ++ ++abort: ++ return rc; ++} ++ ++static int action_cmd_reboot_mode_set(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort reboot mode set\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_reboot_mode_set(memdev, reboot_mode_set_params.reboot_mode); ++} ++ ++static int action_cmd_curr_cxl_boot_mode_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ int rc = 0; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort curr cxl boot mode get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ rc = cxl_memdev_curr_cxl_boot_mode_get(memdev); ++ if(rc) ++ { ++ fprintf(stderr, ++ "curr_cxl_boot_mode_get failed"); ++ goto abort; ++ } ++ ++abort: ++ return rc; ++} ++ ++static int action_cmd_pcie_eye_run(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort pcie_eye_run\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_pcie_eye_run(memdev, pcie_eye_run_params.lane, ++ pcie_eye_run_params.sw_scan, ++ pcie_eye_run_params.ber); ++} ++ ++static int action_cmd_pcie_eye_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort pcie_eye_status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_pcie_eye_status(memdev); ++} ++ ++static int action_cmd_pcie_eye_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ #define NUM_EYESCOPE_VERT_VALS 511 ++ #define TOTAL_EYESCOPE_VERT_VALS ((NUM_EYESCOPE_VERT_VALS * 2) + 1) ++ #define VERT_SKIP 15 ++ ++ int rc = 0; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort pcie_eye_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ if(pcie_eye_get_params.sw_scan) { ++ for (int i = 0; i < TOTAL_EYESCOPE_VERT_VALS; i += VERT_SKIP) { ++ rc = cxl_memdev_pcie_eye_get_sw(memdev, i); ++ if (rc != 0) ++ { ++ fprintf(stderr, ++ "pcie_eye_get read failed or sw_scan not enabled\n"); ++ goto abort; ++ } ++ } ++ if(pcie_eye_get_params.ber) { ++ rc = cxl_memdev_pcie_eye_get_sw_ber(memdev); ++ if (rc != 0) ++ { ++ fprintf(stderr, ++ "pcie_eye_get read failed OR BER is not enabled\n"); ++ goto abort; ++ } ++ } ++ } else { ++ rc = cxl_memdev_pcie_eye_get_hw(memdev); ++ if(rc) ++ { ++ fprintf(stderr, ++ "pcie_eye_get read failed hw scan not enabled"); ++ goto abort; ++ } ++ } ++abort: ++ return rc; ++} ++ ++static int action_cmd_get_cxl_link_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort get_cxl_link_status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_cxl_link_status(memdev); ++} ++ ++static int action_cmd_get_device_info(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort get_device_info\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_device_info(memdev); ++} ++ ++static int action_cmd_read_ddr_temp(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort read_ddr_temp\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_read_ddr_temp(memdev); ++} ++ ++static int action_cmd_cxl_hpa_to_dpa(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort hpa to dpa\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_cxl_hpa_to_dpa(memdev, hpa_address); ++} ++ ++static int action_cmd_get_cxl_membridge_errors(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort cxl membridge errors\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_cxl_membridge_errors(memdev); ++} ++ ++static int action_cmd_get_ddr_bw(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort get_ddr_bw\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_ddr_bw(memdev, get_ddr_bw_params.timeout, get_ddr_bw_params.iterations); ++} ++ ++static int action_cmd_get_ddr_latency(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort get_ddr_latency\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_ddr_latency(memdev, get_ddr_latency_params.measure_time); ++} ++ ++static int action_cmd_i2c_read(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort i2c_read\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_i2c_read(memdev, i2c_read_params.slave_addr, i2c_read_params.reg_addr, i2c_read_params.num_bytes); ++} ++ ++static int action_cmd_i2c_write(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort i2c_write\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_i2c_write(memdev, i2c_write_params.slave_addr, i2c_write_params.reg_addr, i2c_write_params.data); ++} ++ ++static int action_cmd_get_ddr_ecc_err_info(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort get-ddr-ecc-err-info\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_get_ddr_ecc_err_info(memdev); ++} ++ ++static int action_cmd_start_ddr_ecc_scrub(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort start-ddr-ecc-scrub\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_start_ddr_ecc_scrub(memdev); ++} ++ ++static int action_cmd_ddr_ecc_scrub_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-ecc-scrub-status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_ecc_scrub_status(memdev); ++} ++ ++static int action_cmd_ddr_cont_scrub_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-cont-scrub-status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_cont_scrub_status(memdev); ++} ++ ++static int action_cmd_ddr_cont_scrub_set(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-cont-scrub-set\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_cont_scrub_set(memdev, ++ ddr_cont_scrub_set_params.cont_scrub_status); ++} ++ ++static int action_cmd_ddr_init_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-init-status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ return cxl_memdev_ddr_init_status(memdev); ++} ++ ++static int action_cmd_get_cxl_membridge_stats(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort cxl membridge stats\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ return cxl_memdev_get_cxl_membridge_stats(memdev); ++} ++ ++static int action_cmd_trigger_coredump(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-ecc-scrub-status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_trigger_coredump(memdev); ++} ++ ++static int action_cmd_ddr_err_inj_en(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-err-inj-en\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_err_inj_en(memdev, ddr_err_inj_en_params.ddr_id, ddr_err_inj_en_params.err_type, ddr_err_inj_en_params.ecc_fwc_mask); ++} ++ ++static int action_cmd_ddr_dimm_level_training_status(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr-dimm-level-training-status\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_dimm_level_training_status(memdev); ++} ++ ++static int action_cmd_ddr_param_set(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_param_set\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_param_set(memdev, ddr_set_params.ddr_interleave_sz, ++ ddr_set_params.ddr_interleave_ctrl_choice); ++} ++ ++static int action_cmd_ddr_param_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_param_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_param_get(memdev); ++} ++ ++static int action_cmd_core_volt_set(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ float volt; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort core_volt_set\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ volt = ddr_core_volt_set_params.val1 + (ddr_core_volt_set_params.val2 / 10.0) + ++ (ddr_core_volt_set_params.val3 / 100.0); ++ ++ return cxl_memdev_core_volt_set(memdev, volt); ++} ++ ++static int action_cmd_core_volt_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort core_volt_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_core_volt_get(memdev); ++} ++ ++static int action_cmd_oem_err_inj_viral(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort oem_err_inj_viral\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_oem_err_inj_viral(memdev, oem_err_inj_viral_params.viral_type); ++} ++ ++static int action_cmd_err_inj_ll_poison(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort err_inj_ll_poison\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_err_inj_ll_poison(memdev, err_inj_ll_poison_params.en_dis, ++ err_inj_ll_poison_params.ll_err_type); ++} ++ ++static int action_cmd_pci_err_inj(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort err_inj_ll_poison\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_pci_err_inj(memdev, pci_err_inj_params.en_dis, ++ pci_err_inj_params.err_type, ++ pci_err_inj_params.err_subtype, ++ pci_err_inj_params.count, ++ pci_err_inj_params.opt_param1, ++ pci_err_inj_params.opt_param2); ++} ++ ++static int action_cmd_read_ltssm_states(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort read-ltssm-state-changes\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_read_ltssm_states(memdev); ++} ++ ++ ++static int action_cmd_ddr_page_select_set(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_param_set\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_page_select_set(memdev, page_policy_select.page_policy_reg_val); ++} ++ ++static int action_cmd_ddr_page_select_get(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s: memdev active, abort ddr_param_get\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_ddr_page_select_get(memdev); ++} ++ ++ ++static int action_write(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ size_t size = param.len, read_len; ++ unsigned char *buf; ++ int rc; ++ ++ if (cxl_memdev_is_active(memdev)) { ++ fprintf(stderr, "%s is active, abort label write\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ if (!size) { ++ size_t lsa_size = cxl_memdev_get_lsa_size(memdev); ++ ++ fseek(actx->f_in, 0L, SEEK_END); ++ size = ftell(actx->f_in); ++ fseek(actx->f_in, 0L, SEEK_SET); ++ ++ if (size > lsa_size) { ++ fprintf(stderr, ++ "File size (%zu) greater than LSA size (%zu), aborting\n", ++ size, lsa_size); ++ return -EINVAL; ++ } ++ } + + buf = calloc(1, size); + if (!buf) +@@ -3707,10 +4693,10 @@ int cmd_get_supported_logs(int argc, const char **argv, struct cxl_ctx *ctx) + return rc >= 0 ? 0 : EXIT_FAILURE; + } + +-int cmd_get_cel_log(int argc, const char **argv, struct cxl_ctx *ctx) ++int cmd_get_log(int argc, const char **argv, struct cxl_ctx *ctx) + { +- int rc = memdev_action(argc, argv, ctx, action_cmd_get_cel_log, cmd_get_cel_log_options, +- "cxl get-cel-log [..] []"); ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_log, cmd_get_log_options, ++ "cxl get-log [..] []"); + + return rc >= 0 ? 0 : EXIT_FAILURE; + } +@@ -4528,3 +5514,321 @@ int cmd_dimm_slot_info(int argc, const char **argv, struct cxl_ctx *ctx) + + return rc >= 0 ? 0 : EXIT_FAILURE; + } ++ ++int cmd_pmic_vtmon_info(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_pmic_vtmon_info, cmd_pmic_vtmon_info_options, ++ "cxl pmic-vtmon-info [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_margin_run(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_margin_run, cmd_ddr_margin_run_options, ++ "cxl ddr-margin-run [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_margin_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_margin_status, cmd_ddr_margin_status_options, ++ "cxl ddr-margin-status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_margin_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_margin_get, cmd_ddr_margin_get_options, ++ "cxl ddr-margin-get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_stats_run(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_stats_run, cmd_ddr_stats_run_options, ++ "cxl ddr-stats-run [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_stats_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_stats_get, cmd_ddr_stats_get_options, ++ "cxl ddr-stats-get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_curr_cxl_boot_mode_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_curr_cxl_boot_mode_get, cmd_curr_cxl_boot_mode_get_options, ++ "cxl curr-cxl-boot-mode-get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_reboot_mode_set(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_reboot_mode_set, cmd_reboot_mode_set_options, ++ "cxl reboot-mode-set [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_pcie_eye_run(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_pcie_eye_run, cmd_pcie_eye_run_options, ++ "cxl pcie-eye-run [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_pcie_eye_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_pcie_eye_status, cmd_pcie_eye_status_options, ++ "cxl pcie-eye-status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_pcie_eye_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_pcie_eye_get, cmd_pcie_eye_get_options, ++ "cxl pcie-eye-get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_cxl_link_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_cxl_link_status, cmd_get_cxl_link_status_options, ++ "cxl get_cxl_link_status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_device_info(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_device_info, cmd_get_device_info_options, ++ "cxl get_device_info [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_read_ddr_temp(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_read_ddr_temp, cmd_read_ddr_temp_options, ++ "cxl read_ddr_temp [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_cxl_hpa_to_dpa(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_cxl_hpa_to_dpa, cmd_cxl_hpa_to_dpa_options, ++ "cxl hpa to dpa"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_cxl_membridge_errors(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_cxl_membridge_errors, cmd_get_cxl_membridge_errors_options, ++ "cxl get_cxl_membridge_errors"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_ddr_bw(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_ddr_bw, cmd_get_ddr_bw_options, ++ "cxl get-ddr-bw [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_ddr_latency(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_ddr_latency, cmd_get_ddr_latency_options, ++ "cxl get-ddr-latency [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_i2c_read(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_i2c_read, cmd_i2c_read_options, ++ "cxl i2c-read [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_i2c_write(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_i2c_write, cmd_i2c_write_options, ++ "cxl i2c-write [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_ddr_ecc_err_info(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_ddr_ecc_err_info, cmd_get_ddr_ecc_err_info_options, ++ "cxl get-ddr-ecc-err-info [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_start_ddr_ecc_scrub(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_start_ddr_ecc_scrub, cmd_start_ddr_ecc_scrub_options, ++ "cxl start-ddr-ecc-scrub [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_ecc_scrub_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_ecc_scrub_status, cmd_ddr_ecc_scrub_status_options, ++ "cxl ddr-ecc-scrub-status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_cont_scrub_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_cont_scrub_status, cmd_ddr_cont_scrub_status_options, ++ "cxl ddr-cont-scrub-status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_cont_scrub_set(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_cont_scrub_set, cmd_ddr_cont_scrub_set_options, ++ "cxl core_volt_set [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_init_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_init_status, cmd_ddr_init_status_options, ++ "cxl ddr-init-status [..] []"); ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_get_cxl_membridge_stats(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_get_cxl_membridge_stats, cmd_get_cxl_membridge_stats_options, ++ "cxl get_cxl_membridge_errors"); ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_trigger_coredump(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_trigger_coredump, cmd_trigger_coredump_options, ++ "cxl trigger-coredump [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_err_inj_en(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_err_inj_en, cmd_ddr_err_inj_en_options, ++ "cxl ddr-err-inj-en [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_dimm_level_training_status(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_dimm_level_training_status, cmd_ddr_dimm_level_training_options, ++ "cxl ddr-dimm-level-training-status [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_param_set(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_param_set, cmd_ddr_param_set_options, ++ "cxl ddr_param_set [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_param_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_param_get, cmd_ddr_param_get_options, ++ "cxl ddr_param_get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_core_volt_set(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_core_volt_set, cmd_core_volt_set_options, ++ "cxl core_volt_set [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_core_volt_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_core_volt_get, cmd_core_volt_get_options, ++ "cxl core_volt_get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_oem_err_inj_viral(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_oem_err_inj_viral, cmd_oem_err_inj_viral_options, ++ "cxl oem_err_inj_viral [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_err_inj_ll_poison(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_err_inj_ll_poison, cmd_err_inj_ll_poison_options, ++ "cxl err_inj_ll_poison [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_pci_err_inj(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_pci_err_inj, cmd_pci_err_inj_options, ++ "cxl pci_err_inj [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_read_ltssm_states(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_read_ltssm_states, cmd_read_ltssm_states_options, ++ "cxl read-ltssm-state-changes [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_page_select_set(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_page_select_set, cmd_ddr_page_select_set_options, ++ "cxl ddr-page-select-set < [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_ddr_page_select_get(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int rc = memdev_action(argc, argv, ctx, action_cmd_ddr_page_select_get, cmd_ddr_page_select_get_options, ++ "cxl ddr-page-select-get [..] []"); ++ ++ return rc >= 0 ? 0 : EXIT_FAILURE; ++} diff --git a/SPECS/ndctl.spec b/SPECS/ndctl.spec index 3843277..f9fb0c8 100644 --- a/SPECS/ndctl.spec +++ b/SPECS/ndctl.spec @@ -1,6 +1,6 @@ Name: ndctl Version: 71.1 -Release: 8.1%{?dist} +Release: 8.2%{?dist} Summary: Manage "libnvdimm" subsystem devices (Non-volatile Memory) License: GPLv2 Url: https://github.com/pmem/ndctl @@ -14,6 +14,7 @@ Patch0: elake-diff-ea014c0-3ff031d.patch Patch1: elake-c5d4db-7fcbe3.patch # Another `git diff` patch Patch2: elake-diff-bafc29a0-df411ebe.patch +Patch3: facebookexperimental-diff-ffc9245-40fc652.patch %else Patch0: 0003-ndctl-test-Fix-btt-expect-table-compile-warning.patch Patch1: 0004-ndctl-test-Cleanup-unnecessary-out-label.patch @@ -410,6 +411,9 @@ make check %changelog +* Sun May 19 2024 Anita Zhang - 71.1-8.2 +- Patch ffc9245..40fc652 from facebookexperimental/ndctl for hs+fb + * Fri Aug 04 2023 Anita Zhang - 71.1-8.1 - Sync 71.1-8 from c9 branch. - Patch bafc29a0..df411ebe from elake/ndctl fork for hs+fb