/*
 * SPDX-FileCopyrightText: Copyright (c) 2017-2020 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: MIT
 *
 * 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.
 */

#ifndef _NVLINK_HELPER_H_
#define _NVLINK_HELPER_H_

//
// fabric node id will be used as MSB 16 bits of the link token value to 
// generate a unique token for discovering connections
//
#define NVLINK_FABRIC_NODE_ID_MASK 0xFFFF
#define NVLINK_FABRIC_NODE_ID_POS  48

/**
 * Check if the device type is supported
 */
NvBool nvlink_core_is_supported_device_type(NvU32 devType);

/**
 * Get the link and sublink states for the endpoint
 */
void nvlink_core_get_endpoint_state(nvlink_link *link, nvlink_link_state *linkState);

/**
 * Get the nvlink_device * from the PCI DBDF
 */
void nvlink_core_get_device_by_devinfo(nvlink_device_info *devInfo, nvlink_device **dev);

/**
 * Get the nvlink_link * from the PCI DBDF and link#
 */
void nvlink_core_get_link_by_endpoint(nvlink_endpoint *endPoint, nvlink_link **link);

/**
 * Given the nvlink_link ptr, copy the endpoint details for the link
 */
void nvlink_core_copy_endpoint_info(nvlink_link *connLink, nvlink_endpoint *endPointInfo);

/**
 * Given the nvlink_device ptr, copy the device details
 */
void nvlink_core_copy_device_info(nvlink_device *tmpDev, nvlink_detailed_dev_info *devInfo);


/************************************************************************************************/
/****************************** NVLink initialization functions *********************************/
/************************************************************************************************/

/**
 * Kick-off INITPHASE5 on the given array of links
 */
NvlStatus nvlink_core_initphase5(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Kick-off INITPHASE1 on the given array of links
 */
NvlStatus nvlink_core_initphase1(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Kick-off INITRXTERM on the given array of links
 */
NvlStatus nvlink_core_rx_init_term(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Kick-off receiver detect on the given array of links
 */
NvlStatus nvlink_core_set_rx_detect(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Get receiver detect status on the given array of links
 */
NvlStatus nvlink_core_get_rx_detect(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Get Enable TX common mode on the given array of links
 */
NvlStatus nvlink_core_enable_common_mode(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Get Disable TX common mode on the given array of links
 */
NvlStatus nvlink_core_disable_common_mode(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Perform RX calibration on the given array of links
 */
NvlStatus nvlink_core_calibrate_links(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Enable data on the given array of links
 */
NvlStatus nvlink_core_enable_data(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/**
 * Transition to SWCFG on the given array of links
 */
NvlStatus nvlink_core_link_init_async(nvlink_link **links, NvU32 numLinks);

/**
 * Poll on SAFE/SWCFG on the given link
 */
NvlStatus nvlink_core_wait_for_link_init(nvlink_link *link);

/**
 * Initialize all the endpoints from OFF to SWCFG state
 */
void nvlink_core_init_links_from_off_to_swcfg(nvlink_link **pLinks,
                                              NvU32         numLinks,
                                              NvU32         flags);

/*
 * Initialize all the endpoints from OFF to SWCFG state for Non-ALI sequence
 * Used for nvlink 4.0+
 */
void nvlink_core_init_links_from_off_to_swcfg_non_ALI(nvlink_link **pLinks,
                                                     NvU32         numLinks,
                                                     NvU32         flags);
/**
 * Send INITNEGOTIATE command on the given array of links
 */
NvlStatus nvlink_core_initnegotiate(nvlink_link **links, NvU32 numLinks, NvU32 flags);

/*
 * Initialize all the endpoints from OFF to ACTIVE state for ALI sequence
 * Used for nvlink 4.0+
 */
NvlStatus nvlink_core_train_intranode_conns_from_off_to_active_ALI(nvlink_link **pLinks,
                                                                   NvU32         numLinks);
/************************************************************************************************/
/*************************** NVLink topology discovery functions ********************************/
/************************************************************************************************/

/**
 * Generate a discovery token for the given link
 */
NvU64 nvlink_core_get_link_discovery_token(nvlink_link *link);

/**
 * Write the dicovery token for the given link
 */
NvlStatus nvlink_core_write_link_discovery_token(nvlink_link *link, NvU64 token);

/**
 * Read the dicovery token for the given link
 */
NvU64 nvlink_core_read_link_discovery_token(nvlink_link *link);

/**
 * Detect the connection by correlating the tokens
 */
void nvlink_core_correlate_conn_by_token(nvlink_link *srcLink, NvU64 writeToken, NvBool skipReadToken);

/**
 * For a given end of a link, returns the other end its connected to.
 */
void nvlink_core_discover_and_get_remote_end(nvlink_link  *end,
                                             nvlink_link **remote_end,
                                             NvU32         flags);


/************************************************************************************************/
/********************************** NVLink training functions ***********************************/
/************************************************************************************************/

/**
 * Train the internode connection link from SWCFG to ACTIVE
 */
NvlStatus nvlink_core_train_internode_conns_from_swcfg_to_active(nvlink_internode_conn **conns,
                                                                 NvU32                   connCount,
                                                                 NvU32                  *isMasterEnd,
                                                                 NvU32                   flags);

/**
 *  Train the internode connection sublink to enter high speed
 */
NvlStatus nvlink_core_train_internode_conn_sublink_from_safe_to_hs(nvlink_internode_conn *conn,
                                                                   NvU32                  flags);

/**
 * Train a given set of intranode connections from L2 to ACTIVE state
 */
NvlStatus nvlink_core_train_intranode_conns_from_from_L2_to_active(nvlink_intranode_conn **conns,
                                                                   NvU32                   connCount,
                                                                   NvU32                   flags);

/**
 * Train intranode connections associated with a list of links to HS using ALT sequence
 */
NvlStatus nvlink_core_train_intranode_conns_from_swcfg_to_active_ALT(nvlink_intranode_conn **conns,
                                                                     NvU32                   connCount,
                                                                     NvU32                   flags);



/**
 * Train a single intranode connection associated with a list of links to HS using legacy
 * pre-Ampere sequence
 */
NvlStatus nvlink_core_train_intranode_conns_from_swcfg_to_active_legacy(nvlink_intranode_conn **conns,
                                                                        NvU32                   connCount,
                                                                        NvU32                   flags);

/**
 * Train intranode connections associated with a list of links to HS using non-ALI sequence
 * for nvlink 4.0+
 */
NvlStatus nvlink_core_train_intranode_conns_from_swcfg_to_active_non_ALI(nvlink_intranode_conn **conns,
                                                                        NvU32                   connCount,
                                                                        NvU32                   flags);

/**
 * Check to make sure that links are in active and ready for ALI training for nvlink 4.0+
 */
NvlStatus nvlink_core_train_check_link_ready_ALI(nvlink_link **links,
                                                 NvU32         linkCount);

/**
 * Initiate ALI training for nvlink 4.0+
 */
NvlStatus nvlink_core_train_from_off_to_active_ALI(nvlink_link **links,
                                                   NvU32        linkCount);


/************************************************************************************************/
/********************************** NVLink shutdown functions ***********************************/
/************************************************************************************************/

/**
 * [CLEAN SHUTDOWN]
 *
 * Shutdown given intranode connections from active to L2 state
 */
NvlStatus nvlink_core_powerdown_intranode_conns_from_active_to_L2(nvlink_intranode_conn **conns,
                                                                  NvU32                   connCount,
                                                                  NvU32                   flags);

/**
 * [PSEUDO-CLEAN SHUTDOWN]
 *
 * Shutdown the given array of intranode connections from ACTIVE to OFF state
 */
NvlStatus nvlink_core_powerdown_intranode_conns_from_active_to_off(nvlink_intranode_conn **conns,
                                                                   NvU32                   connCount,
                                                                   NvU32                   flags);

/**
 * Power down the given array of intranode connections from ACTIVE to SWCFG state
 */
NvlStatus nvlink_core_powerdown_intranode_conns_from_active_to_swcfg(nvlink_intranode_conn **conns,
                                                                     NvU32                   connCount,
                                                                     NvU32                   flags);

/**
 * Reset the given array of intranode connections
 */
NvlStatus nvlink_core_reset_intranode_conns(nvlink_intranode_conn **conns,
                                            NvU32                   connCount,
                                            NvU32                   flags);

/**
 * Check to make sure that links are in active and ready for ALI training for nvlink 4.0+
 */
NvlStatus nvlink_core_powerdown_floorswept_conns_to_off(nvlink_link **links,
                                       NvU32          numLinks,
                                       NvU32          numIoctrls,
                                       NvU32          numLinksPerIoctrl,
                                       NvU32          numActiveLinksPerIoctrl);


/************************************************************************************************/
/**************************** NVLink connection management functions ****************************/
/************************************************************************************************/

/**
 * For a given link, return the associated internode connection
 */
void nvlink_core_get_internode_conn(nvlink_link            *localLink,
                                    nvlink_internode_conn **conn);

/**
 * Add a new internode connection to the list of connections
 */
NvlStatus nvlink_core_add_internode_conn(nvlink_link                 *localLink,
                                         nvlink_remote_endpoint_info *remoteEndPoint);

/**
 * For a given link, delete the associated internode connection
 */
void nvlink_core_remove_internode_conn(nvlink_link *localLink);

/**
 * For a given link, return the associated intranode connection
 */
void nvlink_core_get_intranode_conn(nvlink_link            *endpoint,
                                    nvlink_intranode_conn **conn);

/**
 * Add a new intranode connection to the list of intranode connections
 */
NvlStatus nvlink_core_add_intranode_conn(nvlink_link *end0, nvlink_link *end1);

/**
 * Remove the connection from the list of intranode connections
 */
void nvlink_core_remove_intranode_conn(nvlink_intranode_conn *conn);

/**
 * Check if the given intranode connection is in the specified mode
 */
NvlStatus nvlink_core_check_intranode_conn_state(nvlink_intranode_conn *conn,
                                                NvU64                  linkMode);

/**
 * Copy the intranode connection's remote endpoint information into the nvlink_conn_info
 * structure passed in
 */
void nvlink_core_copy_intranode_conn_info(nvlink_link      *remote_end,
                                          nvlink_conn_info *conn_info);

/**
 * Copy the internode connection's remote endpoint information into the nvlink_conn_info
 * structure passed in
 */
void nvlink_core_copy_internode_conn_info(nvlink_remote_endpoint_info *remote_end,
                                          nvlink_conn_info            *conn_info);


/************************************************************************************************/
/******************************* NVLink link management functions *******************************/
/************************************************************************************************/

/**
 * For the given link, check whether the link state is at the requested state
 */
NvBool nvlink_core_check_link_state(nvlink_link *link, NvU64 linkState);

/**
 * For the given link, check whether the tx sublink state is at the requested state
 */
NvBool nvlink_core_check_tx_sublink_state(nvlink_link *link, NvU64 txSublinkState);

/**
 * For the given link, check whether the rx sublink state is at the requested state
 */
NvBool nvlink_core_check_rx_sublink_state(nvlink_link *link, NvU64 rxSublinkState);

/**
 * Poll for the link to reach the particular state upto the given timeout. The link
 * state transition is considered failed once timeout occurs
 */
NvlStatus nvlink_core_poll_link_state(nvlink_link *link,
                                      NvU64        linkState,
                                      NvU32        timeout);
/**
 * Poll for a given timeout period for a sublink to reach the particular state. The
 * sublink state transition is considered failed once timeout occurs
 */
NvlStatus nvlink_core_poll_sublink_state(nvlink_link *localTxSubLink,
                                         NvU64        localTxSubLinkState,
                                         NvU32        localTxSubLinkSubtate,
                                         nvlink_link *remoteRxSubLink,
                                         NvU64        remoteRxSubLinkState,
                                         NvU32        remoteRxSubLinkSubstate,
                                         NvU32        timeout);

/**
 * Poll for the tx sublink to reach the specified state upto the given timeout. The
 * sublink state transition is considered failed once timeout occurs
 */
NvlStatus nvlink_core_poll_tx_sublink_state(nvlink_link *link,
                                            NvU64        txSublinkState,
                                            NvU32        txSublinkSubState,
                                            NvU32        timeout);

/**
 * Poll for the rx sublink to reach the specified state upto the given timeout. The
 * sublink state transition is considered failed once timeout occurs
 */
NvlStatus nvlink_core_poll_rx_sublink_state(nvlink_link *link,
                                            NvU64        rxSublinkState,
                                            NvU32        rxSublinkSubState,
                                            NvU32        timeout);

/************************************************************************************************/
/****************** Nvlink print functions for devices/links/connections ************************/
/************************************************************************************************/

void nvlink_core_print_link_state(nvlink_link *link);
void nvlink_core_print_intranode_conn(nvlink_intranode_conn *conn);


#endif //_NVLINK_HELPER_H_
