From 2f2d9d67dc6b6da958c51ca432cd4c8f84534fba Mon Sep 17 00:00:00 2001 From: BenReed161 Date: Mon, 31 Mar 2025 15:55:46 -0700 Subject: [PATCH 1/6] Add support for Gen5 loopback commands Update loopback set function sub command loopback type to follow the gen 5 spec. Add gen5 speed to list of supported loopback speeds in help message. Update the loopback code to add a new function to handle gen 5 loopback seperatly due to the changes compared to gen4. Added check for only gen4 args on a gen5 system. Added arguements for the parallel option and the external option and added additional information in the help option to show the arguement supports gen 4 / gen 5. --- cli/diag.c | 59 ++++++++++++++----- inc/switchtec/diag.h | 5 ++ inc/switchtec/switchtec.h | 10 +++- lib/diag.c | 119 +++++++++++++++++++++++++++++++++----- 4 files changed, 159 insertions(+), 34 deletions(-) diff --git a/cli/diag.c b/cli/diag.c index f02ec339..831224de 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -1462,6 +1462,7 @@ static const struct argconfig_choice loopback_ltssm_speeds[] = { {"GEN2", SWITCHTEC_DIAG_LTSSM_GEN2, "GEN2 LTSSM Speed"}, {"GEN3", SWITCHTEC_DIAG_LTSSM_GEN3, "GEN3 LTSSM Speed"}, {"GEN4", SWITCHTEC_DIAG_LTSSM_GEN4, "GEN4 LTSSM Speed"}, + {"GEN5", SWITCHTEC_DIAG_LTSSM_GEN5, "GEN5 LTSSM Speed"}, {} }; @@ -1481,10 +1482,17 @@ static int print_loopback_mode(struct switchtec_dev *dev, int port_id) if (!enable) b += snprintf(&buf[b], sizeof(buf) - b, "DISABLED, "); - if (enable & SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX) - b += snprintf(&buf[b], sizeof(buf) - b, "RX->TX, "); - if (enable & SWITCHTEC_DIAG_LOOPBACK_TX_TO_RX) - b += snprintf(&buf[b], sizeof(buf) - b, "TX->RX, "); + if (switchtec_is_gen5(dev)) { + if (enable & SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX) + b += snprintf(&buf[b], sizeof(buf) - b, "PARALLEL, "); + if (enable & SWITCHTEC_DIAG_LOOPBACK_TX_TO_RX) + b += snprintf(&buf[b], sizeof(buf) - b, "EXTERNAL, "); + } else { + if (enable & SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX) + b += snprintf(&buf[b], sizeof(buf) - b, "RX->TX, "); + if (enable & SWITCHTEC_DIAG_LOOPBACK_TX_TO_RX) + b += snprintf(&buf[b], sizeof(buf) - b, "TX->RX, "); + } if (enable & SWITCHTEC_DIAG_LOOPBACK_LTSSM) b += snprintf(&buf[b], sizeof(buf) - b, "LTSSM, "); @@ -1519,6 +1527,8 @@ static int loopback(int argc, char **argv) int disable; int enable_tx_to_rx; int enable_rx_to_tx; + int enable_parallel; + int enable_external; int enable_ltssm; int speed; } cfg = { @@ -1533,25 +1543,36 @@ static int loopback(int argc, char **argv) {"disable", 'd', "", CFG_NONE, &cfg.disable, no_argument, "Disable all loopback modes"}, {"ltssm", 'l', "", CFG_NONE, &cfg.enable_ltssm, no_argument, - "Enable LTSSM loopback mode"}, - {"rx-to-tx", 'r', "", CFG_NONE, &cfg.enable_rx_to_tx, no_argument, - "Enable RX->TX loopback mode"}, - {"tx-to-rx", 't', "", CFG_NONE, &cfg.enable_tx_to_rx, no_argument, - "Enable TX->RX loopback mode"}, - {"speed", 's', "GEN", CFG_CHOICES, &cfg.speed, required_argument, - "LTSSM Speed (if enabling the LTSSM loopback mode), default: GEN4", + "Enable LTSSM loopback mode (Gen 4 / Gen 5)"}, + {"rx-to-tx", 'r', "", CFG_NONE, &cfg.enable_rx_to_tx, + no_argument, "Enable RX->TX loopback mode (Gen 4)"}, + {"tx-to-rx", 't', "", CFG_NONE, &cfg.enable_tx_to_rx, + no_argument, "Enable TX->RX loopback mode (Gen 4)"}, + {"parallel", 'P', "", CFG_NONE, &cfg.enable_parallel, + no_argument, "Enable parallel datapath loopback mode in SERDES digital layer (Gen 5)"}, + {"external", 'e', "", CFG_NONE, &cfg.enable_external, + no_argument, "Enable external datapath loopback mode in physical layer (Gen 5)"}, + {"speed", 's', "GEN", CFG_CHOICES, &cfg.speed, + required_argument, "LTSSM Speed (if enabling the LTSSM loopback mode), default: GEN4", .choices = loopback_ltssm_speeds}, {NULL}}; argconfig_parse(argc, argv, CMD_DESC_LOOPBACK, opts, &cfg, sizeof(cfg)); + if ((cfg.enable_external || cfg.enable_parallel) && + (cfg.enable_rx_to_tx || cfg.enable_tx_to_rx)) { + fprintf(stderr, "Cannot enable both Gen4 and Gen5 loopback settings. Use \'--help\' to see full list and support for each.\n"); + return -1; + } + if (cfg.port_id < 0) { fprintf(stderr, "Must specify -p / --port_id\n"); return -1; } if (cfg.disable && (cfg.enable_rx_to_tx || cfg.enable_tx_to_rx || - cfg.enable_ltssm)) { + cfg.enable_ltssm || cfg.enable_external || + cfg.enable_parallel)) { fprintf(stderr, "Must not specify -d / --disable with an enable flag\n"); return -1; @@ -1562,7 +1583,7 @@ static int loopback(int argc, char **argv) return ret; if (cfg.disable || cfg.enable_rx_to_tx || cfg.enable_tx_to_rx || - cfg.enable_ltssm) { + cfg.enable_ltssm || cfg.enable_external || cfg.enable_parallel) { if (cfg.enable_rx_to_tx) enable |= SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX; if (cfg.enable_tx_to_rx) @@ -1570,8 +1591,16 @@ static int loopback(int argc, char **argv) if (cfg.enable_ltssm) enable |= SWITCHTEC_DIAG_LOOPBACK_LTSSM; - ret = switchtec_diag_loopback_set(cfg.dev, cfg.port_id, - enable, cfg.speed); + if (switchtec_is_gen5(cfg.dev)) { + if (cfg.enable_rx_to_tx || cfg.enable_tx_to_rx) { + fprintf(stderr, "Cannot enable Gen 4 settings \'-r\' \'--rx-to-tx\' or \'-t\' \'--tx-to-rx\' on Gen 5 system. \n"); + return -1; + } + } + ret = switchtec_diag_loopback_set(cfg.dev, cfg.port_id, enable, + cfg.enable_parallel, + cfg.enable_external, + cfg.enable_ltssm, cfg.speed); if (ret) { switchtec_perror("loopback_set"); return -1; diff --git a/inc/switchtec/diag.h b/inc/switchtec/diag.h index e2f25b8c..587cdfd3 100644 --- a/inc/switchtec/diag.h +++ b/inc/switchtec/diag.h @@ -139,6 +139,11 @@ enum switchtec_diag_loopback_type { DIAG_LOOPBACK_TX_TO_RX = 1, }; +enum switchtec_diag_loopback_type_gen5 { + DIAG_LOOPBACK_PARALEL_DATAPATH = 5, + DIAG_LOOPBACK_EXTERNAL_DATAPATH = 6, +}; + struct switchtec_diag_loopback_in { uint8_t sub_cmd; uint8_t port_id; diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index b8454221..e119c573 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -1173,6 +1173,7 @@ enum switchtec_diag_ltssm_speed { SWITCHTEC_DIAG_LTSSM_GEN2 = 1, SWITCHTEC_DIAG_LTSSM_GEN3 = 2, SWITCHTEC_DIAG_LTSSM_GEN4 = 3, + SWITCHTEC_DIAG_LTSSM_GEN5 = 4, }; enum switchtec_diag_end { @@ -1205,10 +1206,13 @@ int switchtec_diag_eye_fetch(struct switchtec_dev *dev, double *pixels, size_t pixel_cnt, int *lane_id); int switchtec_diag_eye_cancel(struct switchtec_dev *dev); -int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, - int enable, enum switchtec_diag_ltssm_speed ltssm_speed); +int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, + int enable, int enable_parallel, + int enable_external, int enable_ltssm, + enum switchtec_diag_ltssm_speed ltssm_speed); int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, - int *enabled, enum switchtec_diag_ltssm_speed *ltssm_speed); + int *enabled, + enum switchtec_diag_ltssm_speed *ltssm_speed); int switchtec_diag_pattern_gen_set(struct switchtec_dev *dev, int port_id, enum switchtec_diag_pattern type); int switchtec_diag_pattern_gen_get(struct switchtec_dev *dev, int port_id, diff --git a/lib/diag.c b/lib/diag.c index 22879f50..a644abae 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -322,18 +322,60 @@ int switchtec_diag_eye_cancel(struct switchtec_dev *dev) return ret; } -/** - * @brief Setup Loopback Mode - * @param[in] dev Switchtec device handle - * @param[in] port_id Physical port ID - * @param[in] enable Any enum switchtec_diag_loopback_enable flags - * or'd together to enable specific loopback modes - * @param[in] ltssm_speed LTSSM loopback max speed - * - * @return 0 on success, error code on failure - */ -int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, - int enable, enum switchtec_diag_ltssm_speed ltssm_speed) +static int switchtec_diag_loopback_set_gen5(struct switchtec_dev *dev, + int port_id, int enable_parallel, + int enable_external, + int enable_ltssm, + enum switchtec_diag_ltssm_speed + ltssm_speed) +{ + struct switchtec_diag_loopback_in int_in = { + .sub_cmd = MRPC_LOOPBACK_SET_INT_LOOPBACK, + .port_id = port_id, + .enable = 1, + }; + struct switchtec_diag_loopback_ltssm_in ltssm_in = { + .sub_cmd = MRPC_LOOPBACK_SET_LTSSM_LOOPBACK, + .port_id = port_id, + .enable = enable_ltssm, + .speed = ltssm_speed, + }; + int ret; + + if (enable_ltssm && !(enable_external || enable_parallel)) { + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, <ssm_in, + sizeof(ltssm_in), NULL, 0); + if (ret) + return ret; + } else { + int_in.type = DIAG_LOOPBACK_PARALEL_DATAPATH; + int_in.enable = enable_parallel; + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, &int_in, + sizeof(int_in), NULL, 0); + if (ret) + return ret; + if (!enable_parallel) { + int_in.type = DIAG_LOOPBACK_EXTERNAL_DATAPATH; + int_in.enable = enable_external; + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, &int_in, + sizeof(int_in), NULL, 0); + if (ret) + return ret; + } + + ltssm_in.enable = enable_ltssm; + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, <ssm_in, + sizeof(ltssm_in), NULL, 0); + if (ret) + return ret; + } + return 0; +} + +static int switchtec_diag_loopback_set_gen4(struct switchtec_dev *dev, + int port_id, int enable, + enum switchtec_diag_ltssm_speed + ltssm_speed) { struct switchtec_diag_loopback_in int_in = { .sub_cmd = MRPC_LOOPBACK_SET_INT_LOOPBACK, @@ -372,6 +414,42 @@ int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, return 0; } +/** + * @brief Setup Loopback Mode + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[in] enable Enable bitmap - Gen 4 + * @param[in] enable_parallel Enable the parallel SERDES loopback - Gen 5 + * @param[in] enable_external Enable the external physical loopback - Gen 5 + * @param[in] enable_ltssm Enable the ltssm loopback + * @param[in] ltssm_speed LTSSM loopback max speed + * + * @return 0 on success, error code on failure + */ +int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, + int enable, int enable_parallel, + int enable_external, int enable_ltssm, + enum switchtec_diag_ltssm_speed ltssm_speed) +{ + int ret = 0; + if (switchtec_is_gen5(dev)) { + ret = switchtec_diag_loopback_set_gen5(dev, port_id, + enable_parallel, + enable_external, + enable_ltssm, + ltssm_speed); + if (ret) + return ret; + } + else { + ret = switchtec_diag_loopback_set_gen4(dev, port_id, enable, + ltssm_speed); + if (ret) + return ret; + } + return 0; +} + /** * @brief Setup Loopback Mode * @param[in] dev Switchtec device handle @@ -382,13 +460,13 @@ int switchtec_diag_loopback_set(struct switchtec_dev *dev, int port_id, * * @return 0 on succes, error code on failure */ -int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, - int *enabled, enum switchtec_diag_ltssm_speed *ltssm_speed) +int switchtec_diag_loopback_get(struct switchtec_dev *dev, + int port_id, int *enabled, + enum switchtec_diag_ltssm_speed *ltssm_speed) { struct switchtec_diag_loopback_in int_in = { .sub_cmd = MRPC_LOOPBACK_GET_INT_LOOPBACK, .port_id = port_id, - .type = DIAG_LOOPBACK_RX_TO_TX, }; struct switchtec_diag_loopback_ltssm_in lt_in = { .sub_cmd = MRPC_LOOPBACK_GET_LTSSM_LOOPBACK, @@ -398,6 +476,11 @@ int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, struct switchtec_diag_loopback_ltssm_out lt_out; int ret, en = 0; + if (switchtec_is_gen5(dev)) + int_in.type = DIAG_LOOPBACK_PARALEL_DATAPATH; + else + int_in.type = DIAG_LOOPBACK_RX_TO_TX; + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, &int_in, sizeof(int_in), &int_out, sizeof(int_out)); if (ret) @@ -406,7 +489,11 @@ int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, if (int_out.enabled) en |= SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX; - int_in.type = DIAG_LOOPBACK_TX_TO_RX; + if (switchtec_is_gen5(dev)) + int_in.type = DIAG_LOOPBACK_EXTERNAL_DATAPATH; + else + int_in.type = DIAG_LOOPBACK_TX_TO_RX; + ret = switchtec_cmd(dev, MRPC_INT_LOOPBACK, &int_in, sizeof(int_in), &int_out, sizeof(int_out)); if (ret) From 6328dc5b1b00430211600df393e7efa990d4cdd0 Mon Sep 17 00:00:00 2001 From: Mohamed Ismail Abdul Date: Mon, 15 Apr 2024 12:06:14 -0700 Subject: [PATCH 2/6] Add AER event generate commands Add the AER event generate commands in the diag library. This command allow the generation of AER events without the use of data. Update to support additional events, (prev. only supports the generation of error events associated with Corrected Error (CE).) --- cli/diag.c | 37 +++++++++++++++++++++++++++++++++++++ inc/switchtec/diag.h | 13 +++++++++++++ inc/switchtec/switchtec.h | 2 ++ lib/diag.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/cli/diag.c b/cli/diag.c index 831224de..40d46ce0 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -2158,6 +2158,42 @@ static int tlp_inject (int argc, char **argv) return 0; } +#define CMD_DESC_AER_EVENT_GEN "Generate an AER Error Event" + +static int aer_event_gen(int argc, char **argv) +{ + int ret; + static struct { + struct switchtec_dev *dev; + int port_id; + int aer_error_id; + int trigger_event; + } cfg = {}; + + const struct argconfig_options opts[] = { + DEVICE_OPTION, + {"port", 'p', "", CFG_NONNEGATIVE, &cfg.port_id, + required_argument, "port ID"}, + {"ce_event", 'e', "", CFG_NONNEGATIVE, &cfg.aer_error_id, + required_argument, "aer CE event - 0,6,7,8,12,14,15"}, + {"trigger", 't', "", CFG_NONNEGATIVE, &cfg.trigger_event, + required_argument, "trigger event (only CE events supported-0x1)"}, + {NULL}}; + + argconfig_parse(argc, argv, CMD_DESC_AER_EVENT_GEN, opts, &cfg, + sizeof(cfg)); + + ret = switchtec_aer_event_gen(cfg.dev, cfg.port_id, cfg.aer_error_id, + cfg.trigger_event); + + if (ret != 0) { + switchtec_perror("aer event generation"); + return 1; + } + + return 0; +} + static const struct cmd commands[] = { CMD(crosshair, CMD_DESC_CROSS_HAIR), CMD(eye, CMD_DESC_EYE), @@ -2172,6 +2208,7 @@ static const struct cmd commands[] = { CMD(refclk, CMD_DESC_REF_CLK), CMD(ltssm_log, CMD_DESC_LTSSM_LOG), CMD(tlp_inject, CMD_TLP_INJECT), + CMD(aer_event_gen, CMD_DESC_AER_EVENT_GEN), {} }; diff --git a/inc/switchtec/diag.h b/inc/switchtec/diag.h index 587cdfd3..70aeaa98 100644 --- a/inc/switchtec/diag.h +++ b/inc/switchtec/diag.h @@ -294,5 +294,18 @@ struct switchtec_tlp_inject_in { uint32_t raw_tlp_data[SWITCHTEC_DIAG_MAX_TLP_DWORDS]; }; +enum switchtec_aer_event_gen_result { + AER_EVENT_GEN_SUCCESS = 0, + AER_EVENT_GEN_FAIL = 1, +}; + +struct switchtec_aer_event_gen_in { + uint8_t sub_cmd; + uint8_t phys_port_id; + uint8_t reserved[2]; + uint32_t err_mask; + uint32_t hdr_log[4]; +}; + #endif /**@}*/ diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index e119c573..00f405de 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -1251,6 +1251,8 @@ int switchtec_diag_ltssm_log(struct switchtec_dev *dev, struct switchtec_diag_ltssm_log *log_data); int switchtec_tlp_inject(struct switchtec_dev * dev, int port_id, int tlp_type, int tlp_length, int ecrc, uint32_t * raw_tlp_data); +int switchtec_aer_event_gen(struct switchtec_dev *dev, int port_id, + int aer_error_id, int trigger_event); #ifdef __cplusplus } #endif diff --git a/lib/diag.c b/lib/diag.c index a644abae..ebeb658e 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -1322,4 +1322,33 @@ int switchtec_tlp_inject(struct switchtec_dev * dev, int port_id, int tlp_type, return ret; } +/** + * @brief Call the aer event gen function to generate AER events + * @param[in] dev Switchtec device handle + * @param[in] port Switchtec Port + * @param[in] aer_error_id aer error bit + * @param[out] trigger_event One of the trigger events + * + */ +int switchtec_aer_event_gen(struct switchtec_dev *dev, int port_id, + int aer_error_id, int trigger_event) +{ + uint32_t output; + int ret_val; + + struct switchtec_aer_event_gen_in sub_cmd_id = { + .sub_cmd = trigger_event, + .phys_port_id = port_id, + .err_mask = (1 << aer_error_id), + .hdr_log[0] = 0, + .hdr_log[1] = 0, + .hdr_log[2] = 0, + .hdr_log[3] = 0 + }; + + ret_val = switchtec_cmd(dev, MRPC_AER_GEN, &sub_cmd_id, + sizeof(sub_cmd_id), &output, sizeof(output)); + return ret_val; +} + /**@}*/ From 3beebe7ed5211139f7b2f2e6397db90330e48bbf Mon Sep 17 00:00:00 2001 From: Chetana Date: Fri, 19 Apr 2024 13:25:52 -0700 Subject: [PATCH 3/6] Add Link error injection commands Add new link error injection commands introduced with gen5 allows the user to inject a link error into a physical port. List of Errors: - DLLP - DLLP CRC - TLP LCRC - TLP seq num err - ACK/NAK err - Credit timeout err Include the inject header file for switchtec library to invoke the link inject error options. --- cli/diag.c | 155 +++++++++++++++++++++++++++++++++++ inc/switchtec/inject.h | 79 ++++++++++++++++++ inc/switchtec/mrpc.h | 7 ++ inc/switchtec/switchtec.h | 17 ++++ lib/diag.c | 166 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 424 insertions(+) create mode 100644 inc/switchtec/inject.h diff --git a/cli/diag.c b/cli/diag.c index 40d46ce0..ee9740fe 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -2194,6 +2194,160 @@ static int aer_event_gen(int argc, char **argv) return 0; } +#define CMD_DESC_LNKERR_INJECT "Inject a link error" + +static int linkerr_inject(int argc, char ** argv) +{ + int ret = 0; + uint32_t * dllp_data_dword = NULL; + int num_dwords = 0; + static struct { + struct switchtec_dev *dev; + int inject_dllp; + int inject_dllp_crc; + int inject_tlp_lcrc; + int inject_tlp_seq; + int inject_nack; + int inject_cto; + uint8_t enable; + uint8_t count; + uint16_t dllp_rate; + uint16_t tlp_rate; + uint16_t seq_num; + char * dllp_data; + int phy_port; + } cfg = {}; + + const struct argconfig_options opts[] = { + DEVICE_OPTION, + {"dllp", 'd', "", CFG_NONE, &cfg.inject_dllp, + no_argument, "Inject a DLLP"}, + {"dllp-crc", 'D', "", CFG_NONE, &cfg.inject_dllp_crc, + no_argument, "Inject a DLLP CRC error"}, + {"tlp-lcrc", 'l', "", CFG_NONE, &cfg.inject_tlp_lcrc, + no_argument, "Inject a TLP LCRC error"}, + {"tlp-seq", 's', "", CFG_NONE, &cfg.inject_tlp_seq, + no_argument, "Inject a TLP Sequence Number error"}, + {"nack", 'n', "", CFG_NONE, &cfg.inject_nack, no_argument, + "Inject an ACK to NACK error"}, + {"cto", 't', "", CFG_NONE, &cfg.inject_cto, no_argument, + "Inject a TLP Credit Timeout"}, + {"port", 'p', "", CFG_NONNEGATIVE, &cfg.phy_port, + required_argument, "physical port ID, default: port 0"}, + {"enable", 'e', "", CFG_NONNEGATIVE, &cfg.enable, + required_argument, "enable DLLP CRC Error Injection or TLP LCRC Error Injection, default: 0"}, + {"data", 'i', "", CFG_STRING, &cfg.dllp_data, required_argument, + "DLLP data to inject, a single dword in hex prefixed with \"0x\""}, + {"seq_num", 'S', "", CFG_NONNEGATIVE, &cfg.seq_num, + required_argument, "sequence number of ACK to be replaced by NACK (0-4095)"}, + {"count", 'c', "", CFG_NONNEGATIVE, &cfg.count, + required_argument, "number of times to replace ACK with NACK (0-255)"}, + {"dllp-crc-rate", 'r', "", CFG_NONNEGATIVE, &cfg.dllp_rate, + required_argument, "valid range (0-4096). errors are injected at intervals of rate x 256 x clk "}, + {"tlp-lcrc-rate", 'R', "", CFG_NONNEGATIVE, &cfg.tlp_rate, + required_argument, "valid range (0-7). Ex. rate = 1 -> every other TLP has an error"}, + {NULL} + }; + + argconfig_parse(argc, argv, CMD_DESC_LNKERR_INJECT, opts, &cfg, + sizeof(cfg)); + + uint8_t *ptr = (uint8_t *)&cfg + 5; + int total_en = 0; + for (size_t i = 0; i < 6; i++) { + ptr += 3; + if (ptr[i] == 1) + total_en++; + } + if (total_en > 1) { + fprintf(stderr, "Cannot enable more than one link error injection command at a time.\n"); + return -1; + } + if (total_en == 0) { + fprintf(stderr, "Must enable one link error injection command.\n"); + return -1; + } + + if (cfg.enable && !(cfg.inject_dllp_crc || cfg.inject_tlp_lcrc)) + printf("Ignoring -e enable flag, not valid for the currently selected command.\n"); + if (!cfg.inject_nack && cfg.count) + printf("Ignoring -c flag, not valid for the currently selected command.\n"); + if (!cfg.inject_nack && cfg.seq_num) + printf("Ignoring -S flag, not valid for the currently selected command.\n"); + if (!cfg.inject_tlp_lcrc && cfg.tlp_rate) + printf("Ignoring -R flag, not valid for the currently selected command.\n"); + if (!cfg.inject_dllp_crc && cfg.dllp_rate) + printf("Ignoring -r flag, not valid for the currently selected command.\n"); + if (!cfg.inject_dllp && cfg.dllp_data) { + printf("Ignoring -i flag, not valid for the currently selected command.\n"); + } else if (cfg.inject_dllp && cfg.dllp_data) { + ret = convert_str_to_dwords(cfg.dllp_data, &dllp_data_dword, + &num_dwords); + if (ret) { + fprintf(stderr, "Error with DLLP data provided\n"); + return -1; + } + if (num_dwords == 0) { + fprintf(stderr, "Must provide a single valid DLLP data dword\n"); + free(dllp_data_dword); + return -1; + } + } + + if (cfg.dllp_rate && cfg.tlp_rate) { + fprintf(stderr, "Cannot enable both rate configurations.\n"); + return -1; + } + + if (cfg.inject_dllp) { + ret = switchtec_inject_err_dllp(cfg.dev, cfg.phy_port, + cfg.dllp_data != NULL ? *dllp_data_dword : 0); + free(dllp_data_dword); + } + if (cfg.inject_dllp_crc) { + if (cfg.dllp_rate > 4096) { + fprintf(stderr, "DLLP CRC rate out of range. Valid range is 0-4095.\n"); + return -1; + } + ret = switchtec_inject_err_dllp_crc(cfg.dev, cfg.phy_port, + cfg.enable, cfg.dllp_rate); + } + if (cfg.inject_tlp_lcrc) { + if (cfg.tlp_rate > 7) { + fprintf(stderr, "TLP LCRC rate out of range. Valid range is 0-7.\n"); + return -1; + } + ret = switchtec_inject_err_tlp_lcrc(cfg.dev, cfg.phy_port, + cfg.enable, cfg.tlp_rate); + if (ret) + return -1; + } + if (cfg.inject_tlp_seq) + ret = switchtec_inject_err_tlp_seq_num(cfg.dev, cfg.phy_port); + if (cfg.inject_nack) { + if (cfg.seq_num > 4095) { + fprintf(stderr, "Sequence number out of range. Valid range is 0-4095).\n"); + return -1; + } + if (cfg.count > 255) { + fprintf(stderr, "Count out of range. Valid range is 0-255\n"); + return -1; + } + ret = switchtec_inject_err_ack_nack(cfg.dev, cfg.phy_port, + cfg.seq_num, cfg.count); + } + if (cfg.inject_cto) { + if (!switchtec_is_gen5(cfg.dev)) { + fprintf(stderr, "Credit timeout error injection is only supported on Gen5.\n"); + return -1; + } + ret = switchtec_inject_err_cto(cfg.dev, cfg.phy_port); + } + + switchtec_perror("linkerr-inject"); + return ret; +} + static const struct cmd commands[] = { CMD(crosshair, CMD_DESC_CROSS_HAIR), CMD(eye, CMD_DESC_EYE), @@ -2209,6 +2363,7 @@ static const struct cmd commands[] = { CMD(ltssm_log, CMD_DESC_LTSSM_LOG), CMD(tlp_inject, CMD_TLP_INJECT), CMD(aer_event_gen, CMD_DESC_AER_EVENT_GEN), + CMD(linkerr_inject, CMD_DESC_LNKERR_INJECT), {} }; diff --git a/inc/switchtec/inject.h b/inc/switchtec/inject.h new file mode 100644 index 00000000..b542a3e7 --- /dev/null +++ b/inc/switchtec/inject.h @@ -0,0 +1,79 @@ +/* + * Microsemi Switchtec(tm) PCIe Management Library + * Copyright (c) 2025, Microsemi Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include + +struct switchtec_lnkerr_dllp_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t resvd[2]; + uint32_t data; +}; + +struct switchtec_lnkerr_dllp_crc_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t enable; + uint8_t resvd1; + uint16_t rate; + uint8_t resvd2[2]; +}; + +struct switchtec_lnkerr_tlp_lcrc_gen5_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t enable; + uint8_t resvd1; + uint8_t rate; + uint8_t resvd[3]; +}; + +struct switchtec_lnkerr_tlp_lcrc_gen4_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t enable; + uint8_t rate; +}; + +struct switchtec_lnkerr_tlp_seqn_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t resvd[2]; +}; + +struct switchtec_lnkerr_ack_nack_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t resvd1[2]; + uint16_t seq_num; + uint8_t count; + uint8_t resvd2; +}; + +struct switchtec_lnkerr_cto_in { + uint8_t subcmd; + uint8_t phys_port_id; + uint8_t resvd[2]; +}; diff --git a/inc/switchtec/mrpc.h b/inc/switchtec/mrpc.h index 9f873e8e..39217d49 100644 --- a/inc/switchtec/mrpc.h +++ b/inc/switchtec/mrpc.h @@ -283,6 +283,13 @@ enum mrpc_sub_cmd { MRPC_LTMON_LOG_DUMP_GEN4 = 15, MRPC_LTMON_GET_STATUS_GEN5 = 20, MRPC_LTMON_LOG_DUMP_GEN5 = 21, + + MRPC_ERR_INJ_DLLP = 0, + MRPC_ERR_INJ_DLLP_CRC = 1, + MRPC_ERR_INJ_TLP_LCRC = 2, + MRPC_ERR_INJ_TLP_SEQ = 3, + MRPC_ERR_INJ_ACK_NACK = 4, + MRPC_ERR_INJ_CTO = 5, }; #endif diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index 00f405de..bc1ce0b3 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -35,6 +35,7 @@ #include "portable.h" #include "registers.h" #include "utils.h" +#include "inject.h" #include #include @@ -423,6 +424,22 @@ int switchtec_calc_lane_mask(struct switchtec_dev *dev, int phys_port_id, int lane_id, int num_lanes, int *lane_mask, struct switchtec_status *port); +/** + * @brief Return Link error injection command outputs for DLLP, DLLP_CRC, + * LCRC, SEQ_NUM, ACK_NACK, CTO. + */ + +int switchtec_inject_err_dllp(struct switchtec_dev *dev, int phys_port_id, + int data); +int switchtec_inject_err_dllp_crc(struct switchtec_dev *dev, int phys_port_id, + int enable, uint16_t rate); +int switchtec_inject_err_tlp_lcrc(struct switchtec_dev *dev, int phys_port_id, + int enable, uint8_t rate); +int switchtec_inject_err_tlp_seq_num(struct switchtec_dev *dev, int phys_port_id); +int switchtec_inject_err_ack_nack(struct switchtec_dev *dev, int phys_port_id, + uint16_t seq_num, uint8_t count); +int switchtec_inject_err_cto(struct switchtec_dev *dev, int phys_port_id); + /** * @brief Return whether a Switchtec device is a Gen 3 device. */ diff --git a/lib/diag.c b/lib/diag.c index ebeb658e..fdf87b5a 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -1351,4 +1351,170 @@ int switchtec_aer_event_gen(struct switchtec_dev *dev, int port_id, return ret_val; } +/** + * @brief Inject a DLLP into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phys_port_id Physical port id + * @param[in] data DLLP data + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_dllp(struct switchtec_dev *dev, int phys_port_id, + int data) +{ + uint32_t output; + + struct switchtec_lnkerr_dllp_in cmd = { + .subcmd = MRPC_ERR_INJ_DLLP, + .phys_port_id = phys_port_id, + .data = data, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +/** + * @brief Inject a DLLP CRC error into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phys_port_id Physical port id + * @param[in] enable Enable DLLP CRC error injection + * @param[in] rate Rate of the error injection + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_dllp_crc(struct switchtec_dev *dev, + int phys_port_id, int enable, + uint16_t rate) +{ + uint32_t output; + + struct switchtec_lnkerr_dllp_crc_in cmd = { + .subcmd = MRPC_ERR_INJ_DLLP_CRC, + .phys_port_id = phys_port_id, + .enable = enable, + .rate = rate, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +static int switchtec_inject_err_tlp_lcrc_gen4(struct switchtec_dev *dev, + int phys_port_id, int enable, + uint8_t rate) +{ + uint32_t output; + + struct switchtec_lnkerr_tlp_lcrc_gen4_in cmd = { + .subcmd = MRPC_ERR_INJ_TLP_LCRC, + .phys_port_id = phys_port_id, + .enable = enable, + .rate = rate, + }; + printf("enable: %d\n", enable); + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +static int switchtec_inject_err_tlp_lcrc_gen5(struct switchtec_dev *dev, + int phys_port_id, int enable, + uint8_t rate) +{ + uint32_t output; + + struct switchtec_lnkerr_tlp_lcrc_gen5_in cmd = { + .subcmd = MRPC_ERR_INJ_TLP_LCRC, + .phys_port_id = phys_port_id, + .enable = enable, + .rate = rate, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +/** + * @brief Inject a TLP LCRC error into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phy_port Physical port id + * @param[in] rate Rate of the error injection + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_tlp_lcrc(struct switchtec_dev *dev, int phy_port, + int enable, uint8_t rate) +{ + int ret; + if (switchtec_is_gen4(dev)) { + ret = switchtec_inject_err_tlp_lcrc_gen4(dev, phy_port, enable, rate); + return ret; + } else if (switchtec_is_gen5(dev)) { + ret = switchtec_inject_err_tlp_lcrc_gen5(dev, phy_port, enable, rate); + return ret; + } + fprintf(stderr, "The TLP LCRC is not supported for Gen3 switches.\n"); + return -1; +} + +/** + * @brief Inject a TLP Sequence Number error into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phys_port_id Physical port id + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_tlp_seq_num(struct switchtec_dev *dev, int phys_port_id) +{ + uint32_t output; + + struct switchtec_lnkerr_tlp_seqn_in cmd = { + .subcmd = MRPC_ERR_INJ_TLP_SEQ, + .phys_port_id = phys_port_id, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +/** + * @brief Inject an ACK to NACK error into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phys_port_id Physical port id + * @param[in] seq_num Sequence Number of ACK to be changed to a NACK (0-4095) + * @param[in] count Number of times to replace ACK with NACK (0-255) + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_ack_nack(struct switchtec_dev *dev, int phys_port_id, + uint16_t seq_num, uint8_t count) +{ + uint32_t output; + + struct switchtec_lnkerr_ack_nack_in cmd = { + .subcmd = MRPC_ERR_INJ_ACK_NACK, + .phys_port_id = phys_port_id, + .seq_num = seq_num, + .count = count, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + +/** + * @brief Inject Credit Timeout error into a physical port + * @param[in] dev Switchtec device handle + * @param[in] phys_port_id Physical port id + * @return 0 on success, or a negative value on failure + */ +int switchtec_inject_err_cto(struct switchtec_dev *dev, int phys_port_id) +{ + uint32_t output; + + struct switchtec_lnkerr_cto_in cmd = { + .subcmd = MRPC_ERR_INJ_CTO, + .phys_port_id = phys_port_id, + }; + + return switchtec_cmd(dev, MRPC_MRPC_ERR_INJ, &cmd, + sizeof(cmd), &output, sizeof(output)); +} + /**@}*/ From 70eaa456184cd1288a89f05abf06d75be1fe6ebb Mon Sep 17 00:00:00 2001 From: Mohamed Ismail Abdul Date: Tue, 13 Aug 2024 15:47:47 -0700 Subject: [PATCH 4/6] Add support for Gen5 Port Eq Dump commands Add support for Gen 5 for existing port eq commands. Port Equalization commands supported with the changes 1. port-eq-txcoeff 2. port-eq-txfslf 3. port-eq-txtable Change port eq txfslf to print the width of the current port instead of the negotiated link width to match tx coeff command for gen5. This change is to match the MRPC gen5 spec. (The -P (previous) equalization values are not currently supported in the Gen5 version due to firmware change requirement.) --- cli/diag.c | 11 +- inc/switchtec/mrpc.h | 6 + inc/switchtec/switchtec.h | 86 +++++++++- lib/diag.c | 349 +++++++++++++++++++++++++++++++++++--- 4 files changed, 423 insertions(+), 29 deletions(-) diff --git a/cli/diag.c b/cli/diag.c index ee9740fe..6acf4e89 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -1823,7 +1823,7 @@ static int port_eq_txcoeff(int argc, char **argv) int i, ret; const struct argconfig_options opts[] = { - DEVICE_OPTION, FAR_END_OPTION, PORT_OPTION, PREV_OPTION, {} + DEVICE_OPTION, FAR_END_OPTION, PORT_OPTION, PREV_OPTION, {NULL} }; ret = diag_parse_common_cfg(argc, argv, CMD_DESC_PORT_EQ_TXCOEFF, @@ -1857,7 +1857,7 @@ static int port_eq_txfslf(int argc, char **argv) { struct diag_common_cfg cfg = DEFAULT_DIAG_COMMON_CFG; struct switchtec_port_eq_tx_fslf data; - int i, ret; + int i, ret, lnk_width; const struct argconfig_options opts[] = { DEVICE_OPTION, FAR_END_OPTION, PORT_OPTION, PREV_OPTION, {} @@ -1873,7 +1873,12 @@ static int port_eq_txfslf(int argc, char **argv) cfg.prev ? "(Previous Link-Up)" : ""); printf("Lane FS LF\n"); - for (i = 0; i < cfg.port.neg_lnk_width; i++) { + if (switchtec_is_gen5(cfg.dev)) + lnk_width = cfg.port.cfg_lnk_width; + else + lnk_width = cfg.port.neg_lnk_width; + + for (i = 0; i < lnk_width; i++) { ret = switchtec_diag_port_eq_tx_fslf(cfg.dev, cfg.port_id, i, cfg.end, cfg.link, &data); if (ret) { diff --git a/inc/switchtec/mrpc.h b/inc/switchtec/mrpc.h index 39217d49..3e94fef7 100644 --- a/inc/switchtec/mrpc.h +++ b/inc/switchtec/mrpc.h @@ -245,6 +245,12 @@ enum mrpc_sub_cmd { MRPC_PORT_EQ_LOCAL_TX_FSLF_DUMP = 3, MRPC_PORT_EQ_FAR_END_TX_FSLF_DUMP = 4, + MRPC_GEN5_PORT_EQ_LOCAL_TX_COEFF_DUMP = 9, + MRPC_GEN5_PORT_EQ_FAR_END_TX_COEFF_DUMP = 10, + MRPC_GEN5_PORT_EQ_LOCAL_TX_FSLF_DUMP = 11, + MRPC_GEN5_PORT_EQ_FAR_END_TX_FSLF_DUMP = 12, + MRPC_GEN5_PORT_EQ_FAR_END_TX_EQ_TABLE_DUMP = 15, + MRPC_EXT_RCVR_OBJ_DUMP_RCVR_EXT = 0, MRPC_EXT_RCVR_OBJ_DUMP_RCVR_EXT_PREV = 1, MRPC_EXT_RCVR_OBJ_DUMP_PREV = 2, diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index bc1ce0b3..a25b492e 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -1130,6 +1130,50 @@ struct switchtec_port_eq_coeff { } cursors[16]; }; +struct switchtec_rem_port_eq_coeff { + uint8_t lane_cnt; + uint8_t reserved[3]; + struct { + uint8_t is_coef; + uint8_t pre; + uint8_t post; + uint8_t preset; + } cursors[16]; +}; + +enum lane_eq_dump_type_enum { + LANE_EQ_DUMP_TYPE_CURR, //!< Current settings + LANE_EQ_DUMP_TYPE_PREV, //!< Previous link-up settings + LANE_EQ_DUMP_TYPE_NUM //!< Total number of dump types +}; + +enum link_rate_enum { + PCIE_LINK_RATE_INV = 0, //!< Invalid + PCIE_LINK_RATE_GEN1 = 1, //!< Gen1, supports 2.5GT/s + PCIE_LINK_RATE_GEN2 = 2, //!< Gen2, supports 2.5GT/s, 5GT/s + PCIE_LINK_RATE_GEN3 = 3, //!< Gen3, supports 2.5GT/s, 5GT/s, 8GT/s + PCIE_LINK_RATE_GEN4 = 4, //!< Gen4, supports 2.5GT/s, 5GT/s, 8GT/s, 16GT/s + PCIE_LINK_RATE_GEN5 = 5, //!< Gen5, supports 2.5GT/s, 5GT/s, 8GT/s, 16GT/s, 32GT/s + PCIE_LINK_RATE_NUM = 5 //!< Total number of PCIe generations +}; + +struct switchtec_port_eq_coeff_in { + uint8_t cmd; + uint8_t op_type; + uint8_t phys_port_id; + uint8_t lane_id; + uint8_t dump_type; + uint8_t prev_rate; + uint8_t reserved[2]; +}; + +struct switchtec_port_eq_table_in { + uint8_t sub_cmd; + uint8_t port_id; + uint8_t dump_type; + uint8_t prev_rate; +}; + struct switchtec_port_eq_table { int lane_id; int step_cnt; @@ -1146,11 +1190,42 @@ struct switchtec_port_eq_table { } steps[126]; }; +struct switchtec_gen5_port_eq_table { + uint8_t lane_id; + uint8_t step_cnt; + uint8_t reserved[2]; + + struct { + uint8_t pre_cursor; + uint8_t post_cursor; + uint16_t reserved_0; + uint8_t reserved_1; + uint8_t error_status; + uint8_t active_status; + uint8_t speed; + } steps[25]; +}; + struct switchtec_port_eq_tx_fslf { int fs; int lf; }; +struct switchtec_port_eq_tx_fslf_out { + uint8_t fs; + uint8_t lf; + uint8_t reserved[2]; +}; + +struct switchtec_port_eq_tx_fslf_in { + uint8_t sub_cmd; + uint8_t port_id; + uint8_t lane_id; + uint8_t dump_type; + uint8_t prev_rate; + uint8_t reserved[3]; +}; + struct switchtec_rcvr_ext { int ctle2_rx_mode; int dtclk_5; @@ -1250,15 +1325,16 @@ int switchtec_diag_rcvr_ext(struct switchtec_dev *dev, int port_id, struct switchtec_rcvr_ext *res); int switchtec_diag_port_eq_tx_coeff(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_end end, enum switchtec_diag_link link, - struct switchtec_port_eq_coeff *res); + enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_coeff *res); int switchtec_diag_port_eq_tx_table(struct switchtec_dev *dev, int port_id, enum switchtec_diag_link link, struct switchtec_port_eq_table *res); int switchtec_diag_port_eq_tx_fslf(struct switchtec_dev *dev, int port_id, - int lane_id, enum switchtec_diag_end end, - enum switchtec_diag_link link, - struct switchtec_port_eq_tx_fslf *res); + int lane_id, enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_tx_fslf *res); int switchtec_diag_perm_table(struct switchtec_dev *dev, struct switchtec_mrpc table[MRPC_MAX_ID]); diff --git a/lib/diag.c b/lib/diag.c index fdf87b5a..e1f83198 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -710,7 +710,7 @@ int switchtec_diag_rcvr_obj(struct switchtec_dev *dev, int port_id, } /** - * @brief Get the port equalization TX coefficients + * @brief Get the Gen5 port equalization TX coefficients * @param[in] dev Switchtec device handle * @param[in] port_id Physical port ID * @param[in] end Get coefficents for the Local or the Far End @@ -718,9 +718,118 @@ int switchtec_diag_rcvr_obj(struct switchtec_dev *dev, int port_id, * * @return 0 on success, error code on failure */ -int switchtec_diag_port_eq_tx_coeff(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_end end, enum switchtec_diag_link link, - struct switchtec_port_eq_coeff *res) +static int switchtec_gen5_diag_port_eq_tx_coeff(struct switchtec_dev *dev, + int port_id, + enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_coeff + *res) +{ + struct switchtec_port_eq_coeff *loc_out; + struct switchtec_rem_port_eq_coeff *rem_out; + struct switchtec_port_eq_coeff_in *in; + uint8_t *buf; + uint32_t buf_size; + uint32_t in_size = sizeof(struct switchtec_port_eq_coeff_in); + uint32_t out_size = 0; + int ret = 0; + int i; + + if (!res) { + fprintf(stderr, "Error inval output buffer\n"); + errno = -EINVAL; + return -1; + } + + buf_size = in_size; + if (end == SWITCHTEC_DIAG_LOCAL) { + buf_size += sizeof(struct switchtec_port_eq_coeff); + out_size = sizeof(struct switchtec_port_eq_coeff); + } else if (end == SWITCHTEC_DIAG_FAR_END) { + buf_size += sizeof(struct switchtec_rem_port_eq_coeff); + out_size = sizeof(struct switchtec_rem_port_eq_coeff); + } else { + fprintf(stderr, "Error inval end option\n"); + errno = -EINVAL; + } + buf = (uint8_t *)malloc(buf_size); + if (!buf) { + fprintf(stderr, "Error in buffer alloc\n"); + errno = -ENOMEM; + return -1; + } + + in = (struct switchtec_port_eq_coeff_in *)buf; + in->op_type = DIAG_PORT_EQ_STATUS_OP_PER_PORT; + in->phys_port_id = port_id; + in->lane_id = 0; + in->dump_type = LANE_EQ_DUMP_TYPE_CURR; + + if (link == SWITCHTEC_DIAG_LINK_PREVIOUS) { + in->dump_type = LANE_EQ_DUMP_TYPE_PREV; + in->prev_rate = PCIE_LINK_RATE_GEN5; + } + + if (end == SWITCHTEC_DIAG_LOCAL) { + in->cmd = MRPC_GEN5_PORT_EQ_LOCAL_TX_COEFF_DUMP; + loc_out = (struct switchtec_port_eq_coeff *)&buf[in_size]; + ret = switchtec_cmd(dev, MRPC_PORT_EQ_STATUS, in, in_size, + loc_out, out_size); + if (ret) { + fprintf(stderr, "Error in switchtec cmd:%d\n", ret); + goto end; + } + } else if (end == SWITCHTEC_DIAG_FAR_END) { + in->cmd = MRPC_GEN5_PORT_EQ_FAR_END_TX_COEFF_DUMP; + rem_out = (struct switchtec_rem_port_eq_coeff *)&buf[in_size]; + ret = switchtec_cmd(dev, MRPC_PORT_EQ_STATUS, in, in_size, + rem_out, out_size); + if (ret) { + fprintf(stderr, "Error in switchtec cmd:%d\n", ret); + goto end; + } + } else { + fprintf(stderr, "Error inval end request\n"); + errno = -EINVAL; + goto end; + } + + if (end == SWITCHTEC_DIAG_LOCAL) { + res->lane_cnt = loc_out->lane_cnt + 1; + for (i = 0; i < res->lane_cnt; i++) { + res->cursors[i].pre = loc_out->cursors[i].pre; + res->cursors[i].post = loc_out->cursors[i].post; + } + } else { + res->lane_cnt = rem_out->lane_cnt + 1; + for (i = 0; i < res->lane_cnt; i++) { + res->cursors[i].pre = rem_out->cursors[i].pre; + res->cursors[i].post = rem_out->cursors[i].post; + } + } + +end: + if (buf) + free(buf); + + return ret; +} + +/** + * @brief Get the Gen4 port equalization TX coefficients + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[in] end Get coefficents for the Local or the Far End + * @param[out] res Resulting port equalization coefficients + * + * @return 0 on success, error code on failure + */ +static int switchtec_gen4_diag_port_eq_tx_coeff(struct switchtec_dev *dev, + int port_id, + enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_coeff + *res) { struct switchtec_diag_port_eq_status_out out = {}; struct switchtec_diag_port_eq_status_in in = { @@ -773,16 +882,101 @@ int switchtec_diag_port_eq_tx_coeff(struct switchtec_dev *dev, int port_id, } /** - * @brief Get the far end TX equalization table + * @brief Get the port equalization TX coefficients * @param[in] dev Switchtec device handle * @param[in] port_id Physical port ID - * @param[out] res Resulting port equalization table + * @param[in] end Get coefficents for the Local or the Far End + * @param[out] res Resulting port equalization coefficients * * @return 0 on success, error code on failure */ -int switchtec_diag_port_eq_tx_table(struct switchtec_dev *dev, int port_id, +int switchtec_diag_port_eq_tx_coeff(struct switchtec_dev *dev, int port_id, + enum switchtec_diag_end end, enum switchtec_diag_link link, - struct switchtec_port_eq_table *res) + struct switchtec_port_eq_coeff *res) +{ + int ret = -1; + + if (switchtec_is_gen5(dev)) + ret = switchtec_gen5_diag_port_eq_tx_coeff(dev, port_id, end, + link, res); + else if (switchtec_is_gen4(dev)) + ret = switchtec_gen4_diag_port_eq_tx_coeff(dev, port_id, end, + link, res); + + return ret; +} + +/** + * @brief Get the Gen5 far end TX equalization table + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[out] res Resulting port equalization table + * + * @return 0 on success, error code on failure + */ +static int switchtec_gen5_diag_port_eq_tx_table(struct switchtec_dev *dev, + int port_id, + enum switchtec_diag_link link, + struct switchtec_port_eq_table + *res) +{ + struct switchtec_gen5_port_eq_table out = {}; + struct switchtec_port_eq_table_in in = { + .sub_cmd = MRPC_GEN5_PORT_EQ_FAR_END_TX_EQ_TABLE_DUMP, + port_id = port_id, + }; + int ret, i; + + if (!res) { + errno = -EINVAL; + return -1; + } + + in.dump_type = LANE_EQ_DUMP_TYPE_CURR; + in.prev_rate = 0; + + if (link == SWITCHTEC_DIAG_LINK_PREVIOUS) { + in.dump_type = LANE_EQ_DUMP_TYPE_PREV; + in.prev_rate = PCIE_LINK_RATE_GEN5; + } + + ret = switchtec_cmd(dev, MRPC_PORT_EQ_STATUS, &in, + sizeof(struct switchtec_port_eq_table_in), + &out, sizeof(struct switchtec_gen5_port_eq_table)); + if (ret) + return -1; + + res->lane_id = out.lane_id; + res->step_cnt = out.step_cnt; + + for (i = 0; i < res->step_cnt; i++) { + res->steps[i].pre_cursor = out.steps[i].pre_cursor; + res->steps[i].post_cursor = out.steps[i].post_cursor; + res->steps[i].fom = 0; + res->steps[i].pre_cursor_up = 0; + res->steps[i].post_cursor_up = 0; + res->steps[i].error_status = out.steps[i].error_status; + res->steps[i].active_status = out.steps[i].active_status; + res->steps[i].speed = out.steps[i].speed; + } + + return 0; +} + +/** + * @brief Get the Gen4 far end TX equalization table + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[out] res Resulting port equalization table + * + * @return 0 on success, error code on failure + */ +static int switchtec_gen4_diag_port_eq_tx_table(struct switchtec_dev *dev, + int port_id, + enum switchtec_diag_link link, + struct switchtec_port_eq_table + *res) { struct switchtec_diag_port_eq_table_out out = {}; struct switchtec_diag_port_eq_status_in2 in = { @@ -817,21 +1011,45 @@ int switchtec_diag_port_eq_tx_table(struct switchtec_dev *dev, int port_id, res->lane_id = out.lane_id; res->step_cnt = out.step_cnt; for (i = 0; i < res->step_cnt; i++) { - res->steps[i].pre_cursor = out.steps[i].pre_cursor; - res->steps[i].post_cursor = out.steps[i].post_cursor; - res->steps[i].fom = out.steps[i].fom; - res->steps[i].pre_cursor_up = out.steps[i].pre_cursor_up; + res->steps[i].pre_cursor = out.steps[i].pre_cursor; + res->steps[i].post_cursor = out.steps[i].post_cursor; + res->steps[i].fom = out.steps[i].fom; + res->steps[i].pre_cursor_up = out.steps[i].pre_cursor_up; res->steps[i].post_cursor_up = out.steps[i].post_cursor_up; - res->steps[i].error_status = out.steps[i].error_status; - res->steps[i].active_status = out.steps[i].active_status; - res->steps[i].speed = out.steps[i].speed; + res->steps[i].error_status = out.steps[i].error_status; + res->steps[i].active_status = out.steps[i].active_status; + res->steps[i].speed = out.steps[i].speed; } return 0; } /** - * @brief Get the equalization FS/LF + * @brief Get the far end TX equalization table + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[out] res Resulting port equalization table + * + * @return 0 on success, error code on failure + */ +int switchtec_diag_port_eq_tx_table(struct switchtec_dev *dev, int port_id, + enum switchtec_diag_link link, + struct switchtec_port_eq_table *res) +{ + int ret = -1; + + if (switchtec_is_gen5(dev)) + ret = switchtec_gen5_diag_port_eq_tx_table(dev, port_id, link, + res); + else if (switchtec_is_gen4(dev)) + ret = switchtec_gen4_diag_port_eq_tx_table(dev, port_id, link, + res); + + return ret; +} + +/** + * @brief Get the Gen5 equalization FS/LF * @param[in] dev Switchtec device handle * @param[in] port_id Physical port ID * @param[in] lane_id Physical port ID @@ -840,10 +1058,70 @@ int switchtec_diag_port_eq_tx_table(struct switchtec_dev *dev, int port_id, * * @return 0 on success, error code on failure */ -int switchtec_diag_port_eq_tx_fslf(struct switchtec_dev *dev, int port_id, - int lane_id, enum switchtec_diag_end end, - enum switchtec_diag_link link, - struct switchtec_port_eq_tx_fslf *res) +static int switchtec_gen5_diag_port_eq_tx_fslf(struct switchtec_dev *dev, + int port_id, int lane_id, + enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_tx_fslf + *res) +{ + struct switchtec_port_eq_tx_fslf_in in = {}; + struct switchtec_port_eq_tx_fslf_out out = {}; + int ret; + + if (!res) { + errno = -EINVAL; + return -1; + } + + in.port_id = port_id; + in.lane_id = lane_id; + + + if (end == SWITCHTEC_DIAG_LOCAL) { + in.sub_cmd = MRPC_GEN5_PORT_EQ_LOCAL_TX_FSLF_DUMP; + } else if (end == SWITCHTEC_DIAG_FAR_END) { + in.sub_cmd = MRPC_GEN5_PORT_EQ_FAR_END_TX_FSLF_DUMP; + } else { + errno = -EINVAL; + return -1; + } + + if (link == SWITCHTEC_DIAG_LINK_CURRENT) { + in.dump_type = LANE_EQ_DUMP_TYPE_CURR; + } else { + in.dump_type = LANE_EQ_DUMP_TYPE_PREV; + in.prev_rate = PCIE_LINK_RATE_GEN5; + } + + ret = switchtec_cmd(dev, MRPC_PORT_EQ_STATUS, &in, + sizeof(struct switchtec_port_eq_tx_fslf_in), &out, + sizeof(struct switchtec_port_eq_tx_fslf_out)); + if (ret) + return -1; + + res->fs = out.fs; + res->lf = out.lf; + + return 0; +} + +/** + * @brief Get the Gen4 equalization FS/LF + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID + * @param[in] lane_id Physical port ID + * @param[in] end Get coefficents for the Local or the Far End + * @param[out] res Resulting FS/LF values + * + * @return 0 on success, error code on failure + */ +static int switchtec_gen4_diag_port_eq_tx_fslf(struct switchtec_dev *dev, + int port_id, int lane_id, + enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_tx_fslf + *res) { struct switchtec_diag_port_eq_tx_fslf_out out = {}; struct switchtec_diag_port_eq_status_in2 in = { @@ -893,9 +1171,38 @@ int switchtec_diag_port_eq_tx_fslf(struct switchtec_dev *dev, int port_id, } /** - * @brief Get the Extended Receiver Object + * @brief Get the equalization FS/LF * @param[in] dev Switchtec device handle * @param[in] port_id Physical port ID + * @param[in] lane_id Physical port ID + * @param[in] end Get coefficents for the Local or the Far End + * @param[out] res Resulting FS/LF values + * + * @return 0 on success, error code on failure + */ +int switchtec_diag_port_eq_tx_fslf(struct switchtec_dev *dev, int port_id, + int lane_id, enum switchtec_diag_end end, + enum switchtec_diag_link link, + struct switchtec_port_eq_tx_fslf *res) +{ + int ret = -1; + + if (switchtec_is_gen5(dev)) + ret = switchtec_gen5_diag_port_eq_tx_fslf(dev, port_id, + lane_id, end, + link, res); + else if (switchtec_is_gen4(dev)) + ret = switchtec_gen4_diag_port_eq_tx_fslf(dev, port_id, + lane_id, end, + link, res); + + return ret; +} + +/** + * @brief Get the Extended Receiver Object + * @param[in] dev Switchtec device handle + * @param[in] port_id Physical port ID * @param[in] lane_id Lane ID * @param[in] link Current or previous link-up * @param[out] res Resulting receiver object From f0aed950c304a2641a36af3714a692ccf60619d6 Mon Sep 17 00:00:00 2001 From: Chetana Date: Mon, 16 Sep 2024 12:25:35 -0700 Subject: [PATCH 5/6] Add support for Gen5 Eye capture Updated eye command gen5 mrpc. Add gen5 eye mrpc calls to original switchtec user eye command and added gen5 detection as part of the function. Added additional API function for calling the gen5 read functionality for reading the status since the gen5 eye observe has no output unlike gen4 to denote the status. --- cli/diag.c | 128 +++++++++++++++++++++++++---- cli/graph.c | 50 ++++++++++++ cli/graph.h | 7 +- inc/switchtec/diag.h | 9 +++ inc/switchtec/mrpc.h | 6 +- inc/switchtec/switchtec.h | 37 ++++++++- lib/diag.c | 165 +++++++++++++++++++++++++++++++------- 7 files changed, 352 insertions(+), 50 deletions(-) diff --git a/cli/diag.c b/cli/diag.c index 6acf4e89..a9378892 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -325,7 +326,9 @@ static void print_eye_csv(FILE *f, struct range *X, struct range *Y, int x, y, i, j = 0; fprintf(f, "%s\n", title); - fprintf(f, "interval_ms, %d\n", interval); + + if (interval > -1) + fprintf(f, "interval_ms, %d\n", interval); for_range(x, X) fprintf(f, ", %d", x); @@ -1190,7 +1193,7 @@ static double *eye_observe_dev(struct switchtec_dev *dev, int port_id, goto out_err; } - ret = switchtec_diag_eye_start(dev, lane_mask, X, Y, interval); + ret = switchtec_diag_eye_start(dev, lane_mask, X, Y, interval, 0); if (ret) { switchtec_perror("eye_start"); goto out_err; @@ -1249,7 +1252,8 @@ static double *eye_observe_dev(struct switchtec_dev *dev, int port_id, static int eye_graph(enum output_format fmt, struct range *X, struct range *Y, double *pixels, const char *title, - struct switchtec_diag_cross_hair *ch) + struct switchtec_diag_cross_hair *ch, + struct switchtec_dev *dev) { size_t pixel_cnt = RANGE_CNT(X) * RANGE_CNT(Y); int data[pixel_cnt], shades[pixel_cnt]; @@ -1277,14 +1281,77 @@ static int eye_graph(enum output_format fmt, struct range *X, struct range *Y, } if (fmt == FMT_TEXT) { - graph_draw_text(X, Y, data, title, 'T', 'V'); + if (switchtec_is_gen5(dev)) + graph_draw_text_no_invert(X, Y, data, title, 'P', 'B'); + else + graph_draw_text(X, Y, data, title, 'T', 'V'); if (status_ptr) printf("\n %s\n", status_ptr); return 0; } - return graph_draw_win(X, Y, data, shades, title, 'T', 'V', - status_ptr, NULL, NULL); + if (switchtec_is_gen5(dev)) + return graph_draw_win(X, Y, data, shades, title, 'P', 'B', + status_ptr, NULL, NULL); + else + return graph_draw_win(X, Y, data, shades, title, 'T', 'V', + status_ptr, NULL, NULL); +} + +static double *eye_capture_dev_gen5(struct switchtec_dev *dev, + int port_id, int lane_id, int num_lanes, + int capture_depth, int* num_phases, int* gen) +{ + int bin, j, ret, first_lane, num_phases_l, stride; + int lane_mask[4] = {}; + struct switchtec_status sw_status; + double tmp[60]; + double* ber_data = NULL; + + ret = switchtec_calc_lane_mask(dev, port_id, lane_id, num_lanes, + lane_mask, &sw_status); + if (ret < 0) { + switchtec_perror("Invalid lane"); + return NULL; + } + + ret = switchtec_diag_eye_start(dev, lane_mask, NULL, NULL, 0, + capture_depth); + if (ret) { + switchtec_perror("eye_run"); + return NULL; + } + + first_lane = switchtec_calc_lane_id(dev, port_id, lane_id, NULL); + for (j = 0; j < num_lanes; j++) { + for (bin = 0; bin < 64; bin++) { + ret = switchtec_diag_eye_read(dev, first_lane + j, bin, + &num_phases_l, tmp); + if (ret) { + switchtec_perror("eye_read"); + if (ber_data) + free(ber_data); + return NULL; + } + + if (!ber_data) { + stride = 64 * num_phases_l; + ber_data = calloc(num_lanes * stride, + sizeof(double)); + if (!ber_data) { + perror("allocating BER data"); + return NULL; + } + } + + memcpy(&ber_data[(j * stride) + (bin * num_phases_l)], + tmp, num_phases_l * sizeof(double)); + } + } + + *gen = sw_status.link_rate; + *num_phases = num_phases_l; + return ber_data; } #define CMD_DESC_EYE "Capture PCIe Eye Errors" @@ -1294,13 +1361,14 @@ static int eye(int argc, char **argv) struct switchtec_diag_cross_hair ch = {}, *ch_ptr = NULL; char title[128], subtitle[50]; double *pixels = NULL; - int ret, gen; + int num_phases, ret, gen; static struct { struct switchtec_dev *dev; int fmt; int port_id; int lane_id; + int capture_depth; int num_lanes; int mode; struct range x_range, y_range; @@ -1313,6 +1381,7 @@ static int eye(int argc, char **argv) .fmt = FMT_DEFAULT, .port_id = -1, .lane_id = 0, + .capture_depth = 24, .num_lanes = 1, .mode = SWITCHTEC_DIAG_EYE_RAW, .x_range.start = 0, @@ -1327,7 +1396,7 @@ static int eye(int argc, char **argv) DEVICE_OPTION_OPTIONAL, {"crosshair", 'C', "FILE", CFG_FILE_R, &cfg.crosshair_file, required_argument, - "optionally, superimpose a crosshair CSV onto the result"}, + "optionally, superimpose a crosshair CSV onto the result (Not supported in Gen 5)"}, {"format", 'f', "FMT", CFG_CHOICES, &cfg.fmt, required_argument, "output format (default: " FMT_DEFAULT_STR ")", .choices=output_fmt_choices}, @@ -1357,12 +1426,24 @@ static int eye(int argc, char **argv) required_argument, "voltage step (default: 5)"}, {"interval", 'i', "NUM", CFG_NONNEGATIVE, &cfg.step_interval, required_argument, "step interval in ms (default: 1ms)"}, + {"capture-depth", 'd', "NUM", CFG_POSITIVE, &cfg.capture_depth, + required_argument, "capture depth (6 to 40; default: 24)"}, {NULL}}; argconfig_parse(argc, argv, CMD_DESC_EYE, opts, &cfg, sizeof(cfg)); + if (cfg.dev != NULL && switchtec_is_gen5(cfg.dev)) { + cfg.y_range.start = 0; + cfg.y_range.end = 63; + cfg.y_range.step = 1; + } + if (cfg.crosshair_file) { + if (switchtec_is_gen5(cfg.dev)) { + fprintf(stderr, "Crosshair superimpose not suppored in Gen 5\n"); + return -1; + } ret = load_crosshair_csv(cfg.crosshair_file, &ch, subtitle, sizeof(subtitle)); if (ret) { @@ -1373,11 +1454,11 @@ static int eye(int argc, char **argv) ch_ptr = &ch; } - + if (cfg.plot_file) { pixels = load_eye_csv(cfg.plot_file, &cfg.x_range, - &cfg.y_range, subtitle, sizeof(subtitle), - &cfg.step_interval); + &cfg.y_range, subtitle, sizeof(subtitle), + &cfg.step_interval); if (!pixels) { fprintf(stderr, "Unable to parse CSV file: %s\n", cfg.plot_filename); @@ -1433,12 +1514,25 @@ static int eye(int argc, char **argv) } if (!pixels) { - pixels = eye_observe_dev(cfg.dev, cfg.port_id, cfg.lane_id, - cfg.num_lanes, cfg.mode, cfg.step_interval, - &cfg.x_range, &cfg.y_range, &gen); - if (!pixels) - return -1; + if (switchtec_is_gen5(cfg.dev)) { + pixels = eye_capture_dev_gen5(cfg.dev, cfg.port_id, + cfg.lane_id, cfg.num_lanes, + cfg.capture_depth, + &num_phases, &gen); + if (!pixels) + return -1; + cfg.x_range.end = num_phases - 1; + } + else { + pixels = eye_observe_dev(cfg.dev, cfg.port_id, + cfg.lane_id, cfg.num_lanes, + cfg.mode, cfg.step_interval, + &cfg.x_range, &cfg.y_range, + &gen); + if (!pixels) + return -1; + } eye_set_title(title, cfg.port_id, cfg.lane_id, gen); } @@ -1451,7 +1545,7 @@ static int eye(int argc, char **argv) } ret = eye_graph(cfg.fmt, &cfg.x_range, &cfg.y_range, pixels, title, - ch_ptr); + ch_ptr, cfg.dev); free(pixels); return ret; diff --git a/cli/graph.c b/cli/graph.c index 95609ded..47eaeda5 100644 --- a/cli/graph.c +++ b/cli/graph.c @@ -390,6 +390,16 @@ void graph_init(void) #endif /* defined(HAVE_LIBCURSES) || defined(HAVE_LIBNCURSES) */ +int graph_draw_win_no_invert(struct range *X, struct range *Y, int *data, + int *shades, const char *title, char x_title, + char y_title, char *status, graph_anim_fn *anim, + void *opaque) +{ + graph_draw_text_no_invert(X, Y, data, title, x_title, y_title); + return 0; +} + + void graph_draw_text(struct range *X, struct range *Y, int *data, const char *title, char x_title, char y_title) { @@ -428,3 +438,43 @@ void graph_draw_text(struct range *X, struct range *Y, int *data, j--; } } + +void graph_draw_text_no_invert(struct range *X, struct range *Y, int *data, + const char *title, char x_title, char y_title) +{ + int stride = RANGE_CNT(X); + int x, y, i = RANGE_CNT(Y) - 1; + int j = Y->start; + int space_ch; + + printf(" %s\n\n", title); + + printf(" "); + for_range(x, X) + printf("%d ", x / 10); + printf("\n"); + + printf(" "); + for_range(x, X) + printf("%d ", x % 10); + printf("\n\n"); + + for_range(y, Y) { + printf("%5d ", y); + i = 0; + for_range(x, X) { + space_ch = ' '; + if (data[j * stride + i] == GRAPH_TEXT_HLINE || + data[j * stride + i] == GRAPH_TEXT_PLUS) + space_ch = GRAPH_TEXT_HLINE; + else if (data[j * stride + i] == '-' || + data[j * stride + i] == '+') + space_ch = '-'; + + printf("%lc%lc", data[j * stride + i], space_ch); + i++; + } + printf("\n"); + j++; + } +} diff --git a/cli/graph.h b/cli/graph.h index 1c0f209f..ee37b176 100644 --- a/cli/graph.h +++ b/cli/graph.h @@ -68,5 +68,10 @@ void graph_draw_text(struct range *X, struct range *Y, int *data, int graph_draw_win(struct range *X, struct range *Y, int *data, int *shades, const char *title, char x_title, char y_title, char *status, graph_anim_fn *anim, void *opaque); - +void graph_draw_text_no_invert(struct range *X, struct range *Y, int *data, + const char *title, char x_title, char y_title); +int graph_draw_win_no_invert(struct range *X, struct range *Y, int *data, + int *shades, const char *title, char x_title, + char y_title, char *status, graph_anim_fn *anim, + void *opaque); #endif diff --git a/inc/switchtec/diag.h b/inc/switchtec/diag.h index 70aeaa98..9a345fc2 100644 --- a/inc/switchtec/diag.h +++ b/inc/switchtec/diag.h @@ -242,6 +242,15 @@ struct switchtec_diag_port_eye_fetch { }; }; + +struct switchtec_gen5_diag_eye_run_in { + uint8_t sub_cmd; + uint8_t capture_depth; + uint8_t timeout_disable; + uint8_t resvd1; + uint32_t lane_mask[4]; +}; + struct switchtec_diag_cross_hair_in { uint8_t sub_cmd; uint8_t lane_id; diff --git a/inc/switchtec/mrpc.h b/inc/switchtec/mrpc.h index 3e94fef7..1bc22e7b 100644 --- a/inc/switchtec/mrpc.h +++ b/inc/switchtec/mrpc.h @@ -124,8 +124,8 @@ enum mrpc_cmd { MRPC_SN_VER_GET_GEN5 = 0x119, MRPC_DBG_UNLOCK_GEN5 = 0x11A, MRPC_BOOTUP_RESUME_GEN5 = 0x11B, + MRPC_GEN5_EYE_CAPTURE = 0x142, MRPC_FTDC_LOG_DUMP = 0x147, - MRPC_MAX_ID = 0x148, }; @@ -280,6 +280,10 @@ enum mrpc_sub_cmd { MRPC_EYE_OBSERVE_SET_DATA_MODE = 3, MRPC_EYE_OBSERVE_GET_DATA_MODE = 4, + MRPC_EYE_CAP_RUN_GEN5 = 0, + MRPC_EYE_CAP_STATUS_GEN5 = 1, + MRPC_EYE_CAP_READ_GEN5 = 2, + MRPC_CROSS_HAIR_ENABLE = 0, MRPC_CROSS_HAIR_DISABLE = 1, MRPC_CROSS_HAIR_GET = 2, diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index a25b492e..4bcc5cfa 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -1244,6 +1244,30 @@ enum switchtec_diag_eye_data_mode { SWITCHTEC_DIAG_EYE_RATIO, }; +struct switchtec_gen5_diag_eye_status_in { + uint8_t sub_cmd; + uint8_t resvd1[3]; +}; + +struct switchtec_gen5_diag_eye_status_out { + uint8_t status; + uint8_t resvd1[3]; +}; + +struct switchtec_gen5_diag_eye_read_in { + uint8_t sub_cmd; + uint8_t lane_id; + uint8_t bin; + uint8_t resvd1; +}; + +struct switchtec_gen5_diag_eye_read_out { + uint8_t num_phases; + uint8_t resvd1[3]; + uint32_t resvd2; + uint64_t ber_data[60]; +}; + enum switchtec_diag_loopback_enable { SWITCHTEC_DIAG_LOOPBACK_RX_TO_TX = 1 << 0, SWITCHTEC_DIAG_LOOPBACK_TX_TO_RX = 1 << 1, @@ -1278,6 +1302,15 @@ enum switchtec_diag_link { SWITCHTEC_DIAG_LINK_PREVIOUS, }; +enum switchtec_gen5_diag_eye_status { + SWITCHTEC_GEN5_DIAG_EYE_STATUS_IDLE = 0, + SWITCHTEC_GEN5_DIAG_EYE_STATUS_PENDING = 1, + SWITCHTEC_GEN5_DIAG_EYE_STATUS_IN_PROGRESS = 2, + SWITCHTEC_GEN5_DIAG_EYE_STATUS_DONE = 3, + SWITCHTEC_GEN5_DIAG_EYE_STATUS_TIMEOUT = 4, + SWITCHTEC_GEN5_DIAG_EYE_STATUS_ERROR = 5, +}; + struct switchtec_diag_ltssm_log { unsigned int timestamp; float link_rate; @@ -1291,9 +1324,11 @@ int switchtec_diag_cross_hair_get(struct switchtec_dev *dev, int start_lane_id, int switchtec_diag_eye_set_mode(struct switchtec_dev *dev, enum switchtec_diag_eye_data_mode mode); +int switchtec_diag_eye_read(struct switchtec_dev *dev, int lane_id, int bin, + int* num_phases, double* ber_data); int switchtec_diag_eye_start(struct switchtec_dev *dev, int lane_mask[4], struct range *x_range, struct range *y_range, - int step_interval); + int step_interval, int capture_depth); int switchtec_diag_eye_fetch(struct switchtec_dev *dev, double *pixels, size_t pixel_cnt, int *lane_id); int switchtec_diag_eye_cancel(struct switchtec_dev *dev); diff --git a/lib/diag.c b/lib/diag.c index e1f83198..2444376b 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -128,6 +128,43 @@ int switchtec_diag_cross_hair_get(struct switchtec_dev *dev, int start_lane_id, return 0; } +static int switchtec_diag_eye_status_gen5(struct switchtec_dev *dev) +{ + int ret; + int eye_status; + + struct switchtec_gen5_diag_eye_status_in in = { + .sub_cmd = MRPC_EYE_CAP_STATUS_GEN5, + }; + struct switchtec_gen5_diag_eye_status_out out; + + do { + ret = switchtec_cmd(dev, MRPC_GEN5_EYE_CAPTURE, &in, sizeof(in), + &out, sizeof(out)); + if (ret) { + switchtec_perror("eye_status"); + return -1; + } + eye_status = out.status; + usleep(200000); + } while (eye_status == SWITCHTEC_GEN5_DIAG_EYE_STATUS_IN_PROGRESS || + eye_status == SWITCHTEC_GEN5_DIAG_EYE_STATUS_PENDING); + + switch (eye_status) { + case SWITCHTEC_GEN5_DIAG_EYE_STATUS_IDLE: + switchtec_perror("Eye capture idle"); + case SWITCHTEC_GEN5_DIAG_EYE_STATUS_DONE: + return 0; + case SWITCHTEC_GEN5_DIAG_EYE_STATUS_TIMEOUT: + switchtec_perror("Eye capture timeout"); + case SWITCHTEC_GEN5_DIAG_EYE_STATUS_ERROR: + switchtec_perror("Eye capture error"); + return -1; + } + switchtec_perror("Unknown eye capture state"); + return -1; +} + static int switchtec_diag_eye_status(int status) { switch (status) { @@ -144,8 +181,23 @@ static int switchtec_diag_eye_status(int status) } } -static int switchtec_diag_eye_cmd(struct switchtec_dev *dev, void *in, - size_t size) +static int switchtec_diag_eye_cmd_gen5(struct switchtec_dev *dev, void *in, + size_t size) +{ + int ret; + + ret = switchtec_cmd(dev, MRPC_GEN5_EYE_CAPTURE, in, size, + NULL, 0); + if (ret) + return ret; + + usleep(200000); + + return switchtec_diag_eye_status_gen5(dev); +} + +static int switchtec_diag_eye_cmd_gen4(struct switchtec_dev *dev, void *in, + size_t size) { struct switchtec_diag_port_eye_cmd out; int ret; @@ -174,7 +226,45 @@ int switchtec_diag_eye_set_mode(struct switchtec_dev *dev, .data_mode = mode, }; - return switchtec_diag_eye_cmd(dev, &in, sizeof(in)); + return switchtec_diag_eye_cmd_gen4(dev, &in, sizeof(in)); +} + +/** + * @brief Start a PCIe Eye Read Gen5 + * @param[in] dev Switchtec device handle + * @param[in] lane_id lane_id + * @param[in] bin bin + * @param[in] num_phases pointer to the number of phases + * @param[in] ber_data pointer to the Ber data + * + * @return 0 on success, error code on failure + */ +int switchtec_diag_eye_read(struct switchtec_dev *dev, int lane_id, + int bin, int* num_phases, double* ber_data) +{ + if (dev) { + fprintf(stderr, "Eye read not supported on Gen 4 switches.\n"); + return -1; + } + struct switchtec_gen5_diag_eye_read_in in = { + .sub_cmd = MRPC_EYE_CAP_READ_GEN5, + .lane_id = lane_id, + .bin = bin, + }; + struct switchtec_gen5_diag_eye_read_out out; + int i, ret; + + ret = switchtec_cmd(dev, MRPC_GEN5_EYE_CAPTURE, &in, sizeof(in), + &out, sizeof(out)); + if (ret) + return ret; + + *num_phases = out.num_phases; + + for(i = 0; i < out.num_phases; i++) + ber_data[i] = le64toh(out.ber_data[i]) / 281474976710656.; + + return ret; } /** @@ -191,33 +281,48 @@ int switchtec_diag_eye_set_mode(struct switchtec_dev *dev, */ int switchtec_diag_eye_start(struct switchtec_dev *dev, int lane_mask[4], struct range *x_range, struct range *y_range, - int step_interval) + int step_interval, int capture_depth) { - int err; - int ret; - struct switchtec_diag_port_eye_start in = { - .sub_cmd = MRPC_EYE_OBSERVE_START, - .lane_mask[0] = lane_mask[0], - .lane_mask[1] = lane_mask[1], - .lane_mask[2] = lane_mask[2], - .lane_mask[3] = lane_mask[3], - .x_start = x_range->start, - .y_start = y_range->start, - .x_end = x_range->end, - .y_end = y_range->end, - .x_step = x_range->step, - .y_step = y_range->step, - .step_interval = step_interval, - }; - - ret = switchtec_diag_eye_cmd(dev, &in, sizeof(in)); - - /* Add delay so hardware has enough time to start */ - err = errno; - usleep(200000); - errno = err; - - return ret; + int err, ret; + if (switchtec_is_gen5(dev)) { + struct switchtec_gen5_diag_eye_run_in in = { + .sub_cmd = MRPC_EYE_CAP_RUN_GEN5, + .capture_depth = capture_depth, + .timeout_disable = 1, + .lane_mask[0] = lane_mask[0], + .lane_mask[1] = lane_mask[1], + .lane_mask[2] = lane_mask[2], + .lane_mask[3] = lane_mask[3], + }; + + ret = switchtec_diag_eye_cmd_gen5(dev, &in, sizeof(in)); + err = errno; + errno = err; + return ret; + } else { + struct switchtec_diag_port_eye_start in = { + .sub_cmd = MRPC_EYE_OBSERVE_START, + .lane_mask[0] = lane_mask[0], + .lane_mask[1] = lane_mask[1], + .lane_mask[2] = lane_mask[2], + .lane_mask[3] = lane_mask[3], + .x_start = x_range->start, + .y_start = y_range->start, + .x_end = x_range->end, + .y_end = y_range->end, + .x_step = x_range->step, + .y_step = y_range->step, + .step_interval = step_interval, + }; + + ret = switchtec_diag_eye_cmd_gen4(dev, &in, sizeof(in)); + /* Add delay so hardware has enough time to start */ + err = errno; + usleep(200000); + errno = err; + return ret; + } + return -1; } static uint64_t hi_lo_to_uint64(uint32_t lo, uint32_t hi) @@ -312,7 +417,7 @@ int switchtec_diag_eye_cancel(struct switchtec_dev *dev) .sub_cmd = MRPC_EYE_OBSERVE_CANCEL, }; - ret = switchtec_diag_eye_cmd(dev, &in, sizeof(in)); + ret = switchtec_diag_eye_cmd_gen4(dev, &in, sizeof(in)); /* Add delay so hardware can stop completely */ err = errno; From f91084e17b99f3f804769b1717e5abba56601818 Mon Sep 17 00:00:00 2001 From: BenReed161 Date: Mon, 12 May 2025 12:01:37 -0700 Subject: [PATCH 6/6] Add support for Gen5 pattern mon/gen Add support for the MRPC pattern generator and monitor set/get commands. Sets the sub command ID to the correct version depending on gen. New command line arg with the pattern gen function for the link speed that the pattern generator command needs in the gen5 spec. --- cli/diag.c | 35 ++++++++++++++++++++++++++++++----- inc/switchtec/mrpc.h | 1 + inc/switchtec/switchtec.h | 22 +++++++++++++++++----- lib/diag.c | 12 ++++++++---- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/cli/diag.c b/cli/diag.c index a9378892..78ad1212 100644 --- a/cli/diag.c +++ b/cli/diag.c @@ -1714,6 +1714,14 @@ static const struct argconfig_choice pattern_types[] = { {} }; +static const struct argconfig_choice pat_gen_link_speeds[] = { + {"GEN1", SWITCHTEC_DIAG_PAT_LINK_GEN1, "GEN1 Pattern Generator Speed"}, + {"GEN2", SWITCHTEC_DIAG_PAT_LINK_GEN2, "GEN2 Pattern Generator Speed"}, + {"GEN3", SWITCHTEC_DIAG_PAT_LINK_GEN3, "GEN3 Pattern Generator Speed"}, + {"GEN4", SWITCHTEC_DIAG_PAT_LINK_GEN4, "GEN4 Pattern Generator Speed"}, + {"GEN5", SWITCHTEC_DIAG_PAT_LINK_GEN5, "GEN5 Pattern Generator Speed"}, +}; + static const char *pattern_to_str(enum switchtec_diag_pattern type) { const struct argconfig_choice *s; @@ -1739,7 +1747,8 @@ static int print_pattern_mode(struct switchtec_dev *dev, return -1; } - ret = switchtec_diag_pattern_mon_get(dev, port_id, 0, &mon_pat, &err_cnt); + ret = switchtec_diag_pattern_mon_get(dev, port_id, 0, &mon_pat, + &err_cnt); if (ret) { switchtec_perror("pattern_mon_get"); return -1; @@ -1759,11 +1768,15 @@ static int print_pattern_mode(struct switchtec_dev *dev, for (lane_id = 1; lane_id < port->cfg_lnk_width; lane_id++) { ret = switchtec_diag_pattern_mon_get(dev, port_id, lane_id, NULL, &err_cnt); - printf(" Lane %-2d Errors: 0x%llx\n", lane_id, - err_cnt); - if (ret) { + if (ret == 0x70b02) { + printf(" Lane %d has the pattern monitor disabled.\n", + lane_id); + } else if (ret) { switchtec_perror("pattern_mon_get"); return -1; + } else { + printf(" Lane %-2d Errors: 0x%llx\n", + lane_id, err_cnt); } } } @@ -1786,7 +1799,9 @@ static int pattern(int argc, char **argv) int monitor; int pattern; int inject_errs; + int link_speed; } cfg = { + .link_speed = SWITCHTEC_DIAG_PAT_LINK_DISABLED, .port_id = -1, .pattern = SWITCHTEC_DIAG_PATTERN_PRBS_31, }; @@ -1808,6 +1823,10 @@ static int pattern(int argc, char **argv) required_argument, "pattern to generate or monitor for (default: PRBS31)", .choices = pattern_types}, + {"speed", 's', "SPEED", CFG_CHOICES, &cfg.link_speed, + required_argument, + "link speed that applies to the pattern generator (default: GEN1)", + .choices = pat_gen_link_speeds}, {NULL}}; argconfig_parse(argc, argv, CMD_DESC_PATTERN, opts, &cfg, sizeof(cfg)); @@ -1823,6 +1842,12 @@ static int pattern(int argc, char **argv) return -1; } + if (cfg.link_speed && cfg.monitor) { + fprintf(stderr, + "Cannot enable link speed -s / --speed on pattern monitor\n"); + return -1; + } + ret = get_port(cfg.dev, cfg.port_id, &cfg.port); if (ret) return ret; @@ -1844,7 +1869,7 @@ static int pattern(int argc, char **argv) if (cfg.generate) { ret = switchtec_diag_pattern_gen_set(cfg.dev, cfg.port_id, - cfg.pattern); + cfg.pattern, cfg.link_speed); if (ret) { switchtec_perror("pattern_gen_set"); return -1; diff --git a/inc/switchtec/mrpc.h b/inc/switchtec/mrpc.h index 1bc22e7b..e329fc8a 100644 --- a/inc/switchtec/mrpc.h +++ b/inc/switchtec/mrpc.h @@ -273,6 +273,7 @@ enum mrpc_sub_cmd { MRPC_PAT_GEN_GET_MON = 7, MRPC_PAT_GEN_SET_MON = 8, MRPC_PAT_GEN_INJ_ERR = 9, + MRPC_PAT_GEN_SET_GEN_GEN5 = 10, MRPC_EYE_OBSERVE_START = 0, MRPC_EYE_OBSERVE_FETCH = 1, diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index 4bcc5cfa..1b070c68 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -1284,6 +1284,15 @@ enum switchtec_diag_pattern { SWITCHTEC_DIAG_PATTERN_PRBS_DISABLED, }; +enum switchtec_diag_pattern_link_rate { + SWITCHTEC_DIAG_PAT_LINK_DISABLED = 0, + SWITCHTEC_DIAG_PAT_LINK_GEN1 = 1, + SWITCHTEC_DIAG_PAT_LINK_GEN2 = 2, + SWITCHTEC_DIAG_PAT_LINK_GEN3 = 3, + SWITCHTEC_DIAG_PAT_LINK_GEN4 = 4, + SWITCHTEC_DIAG_PAT_LINK_GEN5 = 5, +}; + enum switchtec_diag_ltssm_speed { SWITCHTEC_DIAG_LTSSM_GEN1 = 0, SWITCHTEC_DIAG_LTSSM_GEN2 = 1, @@ -1341,14 +1350,17 @@ int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, int *enabled, enum switchtec_diag_ltssm_speed *ltssm_speed); int switchtec_diag_pattern_gen_set(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_pattern type); + enum switchtec_diag_pattern type, + enum switchtec_diag_pattern_link_rate + link_speed); int switchtec_diag_pattern_gen_get(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_pattern *type); + enum switchtec_diag_pattern *type); int switchtec_diag_pattern_mon_set(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_pattern type); + enum switchtec_diag_pattern type); int switchtec_diag_pattern_mon_get(struct switchtec_dev *dev, int port_id, - int lane_id, enum switchtec_diag_pattern *type, - unsigned long long *err_cnt); + int lane_id, + enum switchtec_diag_pattern *type, + unsigned long long *err_cnt); int switchtec_diag_pattern_inject(struct switchtec_dev *dev, int port_id, unsigned int err_cnt); diff --git a/lib/diag.c b/lib/diag.c index 2444376b..cb6894c7 100644 --- a/lib/diag.c +++ b/lib/diag.c @@ -633,13 +633,17 @@ int switchtec_diag_loopback_get(struct switchtec_dev *dev, * @return 0 on success, error code on failure */ int switchtec_diag_pattern_gen_set(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_pattern type) + enum switchtec_diag_pattern type, + enum switchtec_diag_pattern_link_rate link_speed) { struct switchtec_diag_pat_gen_in in = { .sub_cmd = MRPC_PAT_GEN_SET_GEN, .port_id = port_id, .pattern_type = type, + .lane_id = link_speed }; + if (switchtec_is_gen5(dev)) + in.sub_cmd = MRPC_PAT_GEN_SET_GEN_GEN5; return switchtec_cmd(dev, MRPC_PAT_GEN, &in, sizeof(in), NULL, 0); } @@ -653,7 +657,7 @@ int switchtec_diag_pattern_gen_set(struct switchtec_dev *dev, int port_id, * @return 0 on success, error code on failure */ int switchtec_diag_pattern_gen_get(struct switchtec_dev *dev, int port_id, - enum switchtec_diag_pattern *type) + enum switchtec_diag_pattern *type) { struct switchtec_diag_pat_gen_in in = { .sub_cmd = MRPC_PAT_GEN_GET_GEN, @@ -703,8 +707,8 @@ int switchtec_diag_pattern_mon_set(struct switchtec_dev *dev, int port_id, * @return 0 on success, error code on failure */ int switchtec_diag_pattern_mon_get(struct switchtec_dev *dev, int port_id, - int lane_id, enum switchtec_diag_pattern *type, - unsigned long long *err_cnt) + int lane_id, enum switchtec_diag_pattern *type, + unsigned long long *err_cnt) { struct switchtec_diag_pat_gen_in in = { .sub_cmd = MRPC_PAT_GEN_GET_MON,