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, &currentLinkState);
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, &currentLinkState);
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                                      &currentTxSublinkState,
416                                      &currentTxSublinkSubState);
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                                          &currentTxSublinkState,
442                                          &currentTxSublinkSubState);
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                                      &currentRxSublinkState,
479                                      &currentRxSublinkSubState);
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                                          &currentRxSublinkState,
505                                          &currentRxSublinkSubState);
506     }
507 
508     return NVL_SUCCESS;
509 }
510