1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2019-2020 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "nvlink.h"
25 #include "nvlink_export.h"
26 #include "nvlink_os.h"
27 #include "../nvlink_ctx.h"
28 #include "../nvlink_helper.h"
29
30 /**
31 * For the given link, check whether the link state is at the requested state.
32 *
33 * @param[in] link NVLink link pointer
34 * @param[in] linkState Target Link State
35 *
36 * return NV_TRUE if the link is in the given state
37 */
38 NvBool
nvlink_core_check_link_state(nvlink_link * link,NvU64 linkState)39 nvlink_core_check_link_state
40 (
41 nvlink_link *link,
42 NvU64 linkState
43 )
44 {
45 NvU64 crntDlLinkMode = NVLINK_LINKSTATE_OFF;
46 NvU64 crntTlLinkMode = NVLINK_LINKSTATE_OFF;
47 NvlStatus status = NVL_SUCCESS;
48
49 if (link == NULL)
50 {
51 return NV_FALSE;
52 }
53
54 switch (linkState)
55 {
56 case NVLINK_LINKSTATE_OFF:
57 case NVLINK_LINKSTATE_RESET:
58 case NVLINK_LINKSTATE_SAFE:
59 case NVLINK_LINKSTATE_HS:
60 {
61 status = link->link_handlers->get_dl_link_mode(link, &crntDlLinkMode);
62 if (status != NVL_SUCCESS)
63 {
64 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
65 "%s: Unable to get DL link mode for %s:%s\n",
66 __FUNCTION__, link->dev->deviceName, link->linkName));
67 return NV_FALSE;
68 }
69
70 if (crntDlLinkMode == linkState)
71 {
72 return NV_TRUE;
73 }
74 break;
75 }
76 case NVLINK_LINKSTATE_ALI:
77 {
78 status = link->link_handlers->get_tl_link_mode(link, &crntTlLinkMode);
79 if (status != NVL_SUCCESS)
80 {
81 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
82 "%s: Unable to get TL link mode for %s:%s\n",
83 __FUNCTION__, link->dev->deviceName, link->linkName));
84 return NV_FALSE;
85 }
86
87 status = link->link_handlers->get_dl_link_mode(link, &crntDlLinkMode);
88 if (status != NVL_SUCCESS)
89 {
90 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
91 "%s: Unable to get DL link mode for %s:%s\n",
92 __FUNCTION__, link->dev->deviceName, link->linkName));
93 return NV_FALSE;
94 }
95
96 if (crntTlLinkMode == NVLINK_LINKSTATE_HS &&
97 (crntDlLinkMode == NVLINK_LINKSTATE_HS ||
98 crntDlLinkMode == NVLINK_LINKSTATE_SLEEP))
99 {
100 return NV_TRUE;
101 }
102 break;
103 }
104 case NVLINK_LINKSTATE_SLEEP:
105 case NVLINK_LINKSTATE_ACTIVE_PENDING:
106 {
107 status = link->link_handlers->get_tl_link_mode(link, &crntTlLinkMode);
108 if (status != NVL_SUCCESS)
109 {
110 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
111 "%s: Unable to get TL link mode for %s:%s\n",
112 __FUNCTION__, link->dev->deviceName, link->linkName));
113 return NV_FALSE;
114 }
115
116 if (crntTlLinkMode == linkState)
117 {
118 return NV_TRUE;
119 }
120 break;
121 }
122 }
123
124 // return false for default case or the states are not matching
125 return NV_FALSE;
126 }
127
128 /**
129 * For the given link, check whether the tx sublink state is at the
130 * requested state.
131 *
132 * @param[in] link NVLink link pointer
133 * @param[in] txSublinkState Target Tx Sublink State
134 *
135 * return NV_TRUE if the tx sublink is in the given state
136 */
137 NvBool
nvlink_core_check_tx_sublink_state(nvlink_link * link,NvU64 txSublinkState)138 nvlink_core_check_tx_sublink_state
139 (
140 nvlink_link *link,
141 NvU64 txSublinkState
142 )
143 {
144 NvlStatus status = NVL_SUCCESS;
145
146 NvU64 crntTxSublinkMode = NVLINK_SUBLINK_STATE_TX_OFF;
147 NvU32 crntTxSublinkSubMode = NVLINK_SUBLINK_SUBSTATE_TX_STABLE;
148
149 if (link == NULL)
150 {
151 return NV_FALSE;
152 }
153
154 status = link->link_handlers->get_tx_mode(link,
155 &crntTxSublinkMode,
156 &crntTxSublinkSubMode);
157 if (status != NVL_SUCCESS)
158 {
159 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
160 "%s: Unable to get TX sublink mode for %s:%s\n",
161 __FUNCTION__, link->dev->deviceName, link->linkName));
162 return NV_FALSE;
163 }
164
165 switch (txSublinkState)
166 {
167 case NVLINK_SUBLINK_STATE_TX_OFF:
168 {
169 if (crntTxSublinkMode == NVLINK_SUBLINK_STATE_TX_OFF)
170 {
171 return NV_TRUE;
172 }
173 break;
174 }
175 case NVLINK_SUBLINK_STATE_TX_SAFE:
176 {
177 if (crntTxSublinkMode == NVLINK_SUBLINK_STATE_TX_SAFE)
178 {
179 return NV_TRUE;
180 }
181 break;
182 }
183 case NVLINK_SUBLINK_STATE_TX_HS:
184 {
185 if ((crntTxSublinkMode == NVLINK_SUBLINK_STATE_TX_HS) ||
186 (crntTxSublinkMode == NVLINK_SUBLINK_STATE_TX_SINGLE_LANE))
187 {
188 return NV_TRUE;
189 }
190 break;
191 }
192 }
193
194 // return false for default case or the states are not matching
195 return NV_FALSE;
196 }
197
198 /**
199 * For the given link, check whether the rx sublink state is at the
200 * requested state.
201 *
202 * @param[in] link NVLink link pointer
203 * @param[in] rxSublinkState Target Rx Sublink State
204 *
205 * return NV_TRUE if the rx sublink is in the given state
206 */
207 NvBool
nvlink_core_check_rx_sublink_state(nvlink_link * link,NvU64 rxSublinkState)208 nvlink_core_check_rx_sublink_state
209 (
210 nvlink_link *link,
211 NvU64 rxSublinkState
212 )
213 {
214 NvlStatus status = NVL_SUCCESS;
215
216 NvU64 crntRxSublinkMode = NVLINK_SUBLINK_STATE_RX_OFF;
217 NvU32 crntRxSublinkSubMode = NVLINK_SUBLINK_SUBSTATE_RX_STABLE;
218
219 if (link == NULL)
220 {
221 return NV_FALSE;
222 }
223
224 status = link->link_handlers->get_rx_mode(link,
225 &crntRxSublinkMode,
226 &crntRxSublinkSubMode);
227 if (status != NVL_SUCCESS)
228 {
229 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
230 "%s: Unable to get TX sublink mode for %s:%s\n",
231 __FUNCTION__, link->dev->deviceName, link->linkName));
232 return NV_FALSE;
233 }
234
235 switch (rxSublinkState)
236 {
237 case NVLINK_SUBLINK_STATE_RX_OFF:
238 {
239 if (crntRxSublinkMode == NVLINK_SUBLINK_STATE_RX_OFF)
240 {
241 return NV_TRUE;
242 }
243 break;
244 }
245 case NVLINK_SUBLINK_STATE_RX_SAFE:
246 {
247 if (crntRxSublinkMode == NVLINK_SUBLINK_STATE_RX_SAFE)
248 {
249 return NV_TRUE;
250 }
251 break;
252 }
253 case NVLINK_SUBLINK_STATE_RX_HS:
254 {
255 if ((crntRxSublinkMode == NVLINK_SUBLINK_STATE_RX_HS) ||
256 (crntRxSublinkMode == NVLINK_SUBLINK_STATE_RX_SINGLE_LANE))
257 {
258 return NV_TRUE;
259 }
260 break;
261 }
262 }
263
264 // return false for default case or the states are not matching
265 return NV_FALSE;
266 }
267
268 /**
269 * Poll the link to reach the specified state upto the given timeout.
270 * Link state transition is considered failed once timeout occurs.
271 *
272 * @param[in] link NVLink link pointer
273 * @param[in] linkState Target Link state
274 * @param[in] timeout Timeout
275 *
276 * return NVL_SUCCESS if the link transitioned to the target state
277 */
278 NvlStatus
nvlink_core_poll_link_state(nvlink_link * link,NvU64 linkState,NvU32 timeout)279 nvlink_core_poll_link_state
280 (
281 nvlink_link *link,
282 NvU64 linkState,
283 NvU32 timeout
284 )
285 {
286 NvU64 currentLinkState = ~0;
287
288 if (link == NULL)
289 {
290 return NVL_BAD_ARGS;
291 }
292
293 link->link_handlers->get_dl_link_mode(link, ¤tLinkState);
294
295 while (currentLinkState != linkState)
296 {
297 nvlink_sleep(1);
298
299 timeout--;
300
301 if (!timeout)
302 {
303 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
304 "%s: Timeout occured while polling on link.\n",
305 __FUNCTION__));
306
307 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
308 "%s: Link info: device: %s link: %s link state "
309 "expected: 0x%08llx actual: 0x%08llx.\n",
310 __FUNCTION__, link->dev->deviceName, link->linkName,
311 linkState, currentLinkState));
312
313 return NVL_ERR_INVALID_STATE;
314 }
315
316 link->link_handlers->get_dl_link_mode(link, ¤tLinkState);
317 }
318
319 return NVL_SUCCESS;
320 }
321
322 /**
323 * Poll for a given timeout period for the sublink to reach a given
324 * state. Sublink state transition is considered as failed once the
325 * timeout occurs
326 *
327 * @param[in] localTxSubLink Local NVLink pointer
328 * @param[in] localTxSubLinkState Local Tx Sublink State
329 * @param[in] localTxSubLinkSubState Local Tx Sublink Substate
330 * @param[in] remoteRxSubLink Remote NVLink pointer
331 * @param[in] remoteRxSubLinkState Remote Rx Sublink State
332 * @param[in] remoteRxSubLinkSubState Remote Rx Sublink Substate
333 * @param[in] timeout Timeout
334 *
335 * return NVL_SUCCESS is the sublink transitioned to the given state
336 */
337 NvlStatus
nvlink_core_poll_sublink_state(nvlink_link * localTxSubLink,NvU64 localTxSubLinkState,NvU32 localTxSubLinkSubState,nvlink_link * remoteRxSubLink,NvU64 remoteRxSubLinkState,NvU32 remoteRxSubLinkSubState,NvU32 timeout)338 nvlink_core_poll_sublink_state
339 (
340 nvlink_link *localTxSubLink,
341 NvU64 localTxSubLinkState,
342 NvU32 localTxSubLinkSubState,
343 nvlink_link *remoteRxSubLink,
344 NvU64 remoteRxSubLinkState,
345 NvU32 remoteRxSubLinkSubState,
346 NvU32 timeout
347 )
348 {
349 NvlStatus status = NVL_SUCCESS;
350
351 if ((localTxSubLink == NULL) || (remoteRxSubLink == NULL))
352 {
353 return NVL_BAD_ARGS;
354 }
355
356 // check for tx sublink if a valid link is specified
357 if (localTxSubLink)
358 {
359 status = nvlink_core_poll_tx_sublink_state(localTxSubLink,
360 localTxSubLinkState,
361 localTxSubLinkSubState,
362 timeout);
363 if (status != NVL_SUCCESS)
364 {
365 // polling on tx sublink failed. skip any rx polling
366 return status;
367 }
368 }
369
370 //
371 // check for rx sublink if a valid link is specified and no
372 // timeout on tx sublink (if it was specified)
373 //
374 if (remoteRxSubLink)
375 {
376 status = nvlink_core_poll_rx_sublink_state(remoteRxSubLink,
377 remoteRxSubLinkState,
378 remoteRxSubLinkSubState,
379 timeout);
380 }
381
382 return status;
383 }
384
385 /**
386 * Poll for the tx sublink to reach the specified state upto the given
387 * timeout. Sublink state transition is considered failed once timeout
388 * occurs.
389 *
390 * @param[in] link NVLink pointer
391 * @param[in] txSublinkState Tx Sublink State
392 * @param[in] txSublinkSubState Tx Sublink Substate
393 * @param[in] timeout Timeout
394 *
395 * return NVL_SUCCESS if the tx sublink transitioned to the target state
396 */
397 NvlStatus
nvlink_core_poll_tx_sublink_state(nvlink_link * link,NvU64 txSublinkState,NvU32 txSublinkSubState,NvU32 timeout)398 nvlink_core_poll_tx_sublink_state
399 (
400 nvlink_link *link,
401 NvU64 txSublinkState,
402 NvU32 txSublinkSubState,
403 NvU32 timeout
404 )
405 {
406 NvU64 currentTxSublinkState = ~0;
407 NvU32 currentTxSublinkSubState = ~0;
408
409 if (link == NULL)
410 {
411 return NVL_BAD_ARGS;
412 }
413
414 link->link_handlers->get_tx_mode(link,
415 ¤tTxSublinkState,
416 ¤tTxSublinkSubState);
417
418 while (!((currentTxSublinkState == txSublinkState) &&
419 (currentTxSublinkSubState == txSublinkSubState)))
420 {
421 nvlink_sleep(1);
422
423 timeout--;
424
425 if (!timeout)
426 {
427 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
428 "%s: Timeout occured while polling on link.\n",
429 __FUNCTION__));
430
431 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
432 "%s: Link info: device: %s link: %s txsublink state"
433 " expected: 0x%08llx actual: 0x%08llx.\n",
434 __FUNCTION__, link->dev->deviceName, link->linkName,
435 txSublinkState, currentTxSublinkState));
436
437 return NVL_ERR_INVALID_STATE;
438 }
439
440 link->link_handlers->get_tx_mode(link,
441 ¤tTxSublinkState,
442 ¤tTxSublinkSubState);
443 }
444
445 return NVL_SUCCESS;
446 }
447
448 /**
449 * Poll for the rx sublink to reach the specified state upto the given
450 * timeout. Sublink state transition is considered failed once timeout
451 * occurs.
452 *
453 * @param[in] link NVLink pointer
454 * @param[in] rxSublinkState Rx Sublink State
455 * @param[in] rxSublinkSubState Rx Sublink Substate
456 * @param[in] timeout Timeout
457 *
458 * return NVL_SUCCESS if the rx sublink transitioned to the target state
459 */
460 NvlStatus
nvlink_core_poll_rx_sublink_state(nvlink_link * link,NvU64 rxSublinkState,NvU32 rxSublinkSubState,NvU32 timeout)461 nvlink_core_poll_rx_sublink_state
462 (
463 nvlink_link *link,
464 NvU64 rxSublinkState,
465 NvU32 rxSublinkSubState,
466 NvU32 timeout
467 )
468 {
469 NvU64 currentRxSublinkState = ~0;
470 NvU32 currentRxSublinkSubState = ~0;
471
472 if (link == NULL)
473 {
474 return NVL_BAD_ARGS;
475 }
476
477 link->link_handlers->get_rx_mode(link,
478 ¤tRxSublinkState,
479 ¤tRxSublinkSubState);
480
481 while (!((currentRxSublinkState == rxSublinkState) &&
482 (currentRxSublinkSubState == rxSublinkSubState)))
483 {
484 nvlink_sleep(1);
485
486 timeout--;
487
488 if (!timeout)
489 {
490 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
491 "%s: Timeout occured while polling on link.\n",
492 __FUNCTION__));
493
494 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
495 "%s: Link info: device: %s link: %s rxsublink state "
496 "expected: 0x%08llx actual: 0x%08llx.\n",
497 __FUNCTION__, link->dev->deviceName, link->linkName,
498 rxSublinkState, currentRxSublinkState));
499
500 return NVL_ERR_INVALID_STATE;
501 }
502
503 link->link_handlers->get_rx_mode(link,
504 ¤tRxSublinkState,
505 ¤tRxSublinkSubState);
506 }
507
508 return NVL_SUCCESS;
509 }
510