aboutsummaryrefslogtreecommitdiffstats
path: root/lmr/lmr.h
blob: f728fca78d1e8ab9d0ea8d00ccf103a1a67d7e8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
 *	The PCI Utilities -- Margining utility main header
 *
 *	Copyright (c) 2023 KNS Group LLC (YADRO)
 *
 *	Can be freely distributed and used under the terms of the GNU GPL v2+.
 *
 *	SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef _LMR_H
#define _LMR_H

#include <stdbool.h>

#include "pciutils.h"
#include "lib/bitops.h"

#define MARGIN_STEP_MS 1000

#define MARGIN_TIM_MIN       20
#define MARGIN_TIM_RECOMMEND 30
#define MARGIN_VOLT_MIN      50

enum margin_hw { MARGIN_HW_DEFAULT, MARGIN_ICE_LAKE_RC };

/* PCI Device wrapper for margining functions */
struct margin_dev {
  struct pci_dev *dev;
  int lmr_cap_addr;
  u8 width;
  u8 retimers_n;
  u8 link_speed;

  enum margin_hw hw;

  /* Saved Device settings to restore after margining */
  u8 aspm;
  bool hasd; // Hardware Autonomous Speed Disable
  bool hawd; // Hardware Autonomous Width Disable
};

struct margin_link {
  struct margin_dev down_port;
  struct margin_dev up_port;
};

/* Specification Revision 5.0 Table 8-11 */
struct margin_params {
  bool ind_error_sampler;
  bool sample_report_method;
  bool ind_left_right_tim;
  bool ind_up_down_volt;
  bool volt_support;

  u8 max_lanes;

  u8 timing_steps;
  u8 timing_offset;

  u8 volt_steps;
  u8 volt_offset;

  u8 sample_rate_v;
  u8 sample_rate_t;
};

/* Step Margin Execution Status - Step command response */
enum margin_step_exec_sts {
  MARGIN_NAK = 0, // NAK/Set up for margin
  MARGIN_LIM,     // Too many errors (device limit)
  MARGIN_THR      // Test threshold has been reached
};

enum margin_dir { VOLT_UP = 0, VOLT_DOWN, TIM_LEFT, TIM_RIGHT };

/* Margining results of one lane of the receiver */
struct margin_res_lane {
  u8 lane;
  u8 steps[4];
  enum margin_step_exec_sts statuses[4];
};

/* Reason not to run margining test on the Link/Receiver */
enum margin_test_status {
  MARGIN_TEST_OK = 0,
  MARGIN_TEST_READY_BIT,
  MARGIN_TEST_CAPS,

  // Couldn't run test
  MARGIN_TEST_PREREQS,
  MARGIN_TEST_ARGS_LANES,
  MARGIN_TEST_ARGS_RECVS,
  MARGIN_TEST_ASPM
};

/* All lanes Receiver results */
struct margin_results {
  u8 recvn; // Receiver Number
  struct margin_params params;
  bool lane_reversal;
  u8 link_speed;

  enum margin_test_status test_status;

  /* Used to convert steps to physical quantity.
     Calculated from MaxOffset and NumSteps     */
  double tim_coef;
  double volt_coef;

  bool tim_off_reported;
  bool volt_off_reported;

  u8 lanes_n;
  struct margin_res_lane *lanes;
};

/* pcilmr arguments */
struct margin_args {
  u8 steps_t;        // 0 == use NumTimingSteps
  u8 steps_v;        // 0 == use NumVoltageSteps
  u8 parallel_lanes; // [1; MaxLanes + 1]
  u8 error_limit;    // [0; 63]
  u8 recvs[6];       // Receivers Numbers
  u8 recvs_n;        // 0 == margin all available receivers
  u8 lanes[32];      // Lanes to Margin
  u8 lanes_n;        // 0 == margin all available lanes
  bool run_margin;   // Or print params only
  u8 verbosity;      // 0 - basic;
                     // 1 - add info about remaining time and lanes in progress during margining

  u64 *steps_utility; // For ETA logging
};

/* Receiver structure */
struct margin_recv {
  struct margin_dev *dev;
  u8 recvn; // Receiver Number
  bool lane_reversal;
  struct margin_params *params;

  u8 parallel_lanes;
  u8 error_limit;
};

struct margin_lanes_data {
  struct margin_recv *recv;

  struct margin_res_lane *results;
  u8 *lanes_numbers;
  u8 lanes_n;

  bool ind;
  enum margin_dir dir;

  u8 steps_lane_done;
  u8 steps_lane_total;
  u64 *steps_utility;

  u8 verbosity;
};

/* margin_hw */

/* Verify that devices form the link with 16 GT/s or 32 GT/s data rate */
bool margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port);

/* Check Margining Ready bit from Margining Port Status Register */
bool margin_check_ready_bit(struct pci_dev *dev);

/* Verify link and fill wrappers */
bool margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port,
                      struct margin_link *wrappers);

/* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */
bool margin_prep_link(struct margin_link *link);

/* Restore ASPM, Hardware Autonomous Speed/Width settings */
void margin_restore_link(struct margin_link *link);

/* margin */

/* Fill margin_params without calling other functions */
bool margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
                        struct margin_params *params);

enum margin_test_status margin_process_args(struct margin_dev *dev, struct margin_args *args);

/* Awaits that args are prepared through process_args.
   Returns number of margined Receivers through recvs_n */
struct margin_results *margin_test_link(struct margin_link *link, struct margin_args *args,
                                        u8 *recvs_n);

void margin_free_results(struct margin_results *results, u8 results_n);

/* margin_log */

extern bool margin_global_logging;
extern bool margin_print_domain;

void margin_log(char *format, ...);

/* b:d.f -> b:d.f */
void margin_log_bdfs(struct pci_dev *down_port, struct pci_dev *up_port);

/* Print Link header (bdfs, width, speed) */
void margin_log_link(struct margin_link *link);

void margin_log_params(struct margin_params *params);

/* Print receiver number */
void margin_log_recvn(struct margin_recv *recv);

/* Print full info from Receiver struct */
void margin_log_receiver(struct margin_recv *recv);

/* Margining in progress log */
void margin_log_margining(struct margin_lanes_data arg);

void margin_log_hw_quirks(struct margin_recv *recv);

/* margin_results */

void margin_results_print_brief(struct margin_results *results, u8 recvs_n);

void margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
                             struct pci_dev *up_port);

#endif