diff --git a/cli/diag.c b/cli/diag.c index f02ec339..78ad1212 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; @@ -1462,6 +1556,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 +1576,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 +1621,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 +1637,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 +1677,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 +1685,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; @@ -1591,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; @@ -1616,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; @@ -1636,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); } } } @@ -1663,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, }; @@ -1685,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)); @@ -1700,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; @@ -1721,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; @@ -1794,7 +1942,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, @@ -1828,7 +1976,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, {} @@ -1844,7 +1992,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) { @@ -2129,6 +2282,196 @@ 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; +} + +#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), @@ -2143,6 +2486,8 @@ 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), + CMD(linkerr_inject, CMD_DESC_LNKERR_INJECT), {} }; 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 e2f25b8c..9a345fc2 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; @@ -237,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; @@ -289,5 +303,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/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..e329fc8a 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, }; @@ -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, @@ -267,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, @@ -274,6 +281,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, @@ -283,6 +294,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 b8454221..1b070c68 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. */ @@ -1113,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; @@ -1129,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; @@ -1152,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, @@ -1168,11 +1284,21 @@ 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, SWITCHTEC_DIAG_LTSSM_GEN3 = 2, SWITCHTEC_DIAG_LTSSM_GEN4 = 3, + SWITCHTEC_DIAG_LTSSM_GEN5 = 4, }; enum switchtec_diag_end { @@ -1185,6 +1311,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; @@ -1198,26 +1333,34 @@ 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); -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); + 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); @@ -1229,15 +1372,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]); @@ -1247,6 +1391,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 22879f50..cb6894c7 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; @@ -322,18 +427,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 +519,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 +565,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 +581,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 +594,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) @@ -441,13 +633,17 @@ int switchtec_diag_loopback_get(struct switchtec_dev *dev, int port_id, * @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); } @@ -461,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, @@ -511,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, @@ -623,7 +819,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 @@ -631,9 +827,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 = { @@ -686,16 +991,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 = { @@ -730,21 +1120,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 @@ -753,10 +1167,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 = { @@ -806,9 +1280,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 @@ -1235,4 +1738,199 @@ 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; +} + +/** + * @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)); +} + /**@}*/