lm_mcp.c revision d14abf155341d55053c76eeec58b787a456b753b
/*******************************************************************************
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2014 QLogic Corporation
* The contents of this file are subject to the terms of the
* QLogic End User License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing permissions
* and limitations under the License.
*
*
* Module Description:
*
*
* History:
* 11/26/07 Alon Elhanani Inception.
******************************************************************************/
#include "lm5710.h"
#include "license.h"
#include "mcp_shmem.h"
#include "debug.h"
/**
* Waits for MCP_ONE_TIMEOUT or MCP_ONE_TIMEOUT*10,
* depending on the HW type.
*
* @param pdev
*/
static __inline void lm_mcp_wait_one (
)
{
/* special handling for emulation and FPGA,
wait 10 times longer */
if (CHIP_REV_IS_SLOW(pdev)) {
} else {
}
}
#if !defined(b710)
/**
* Prepare CLP to MCP reset.
*
* @param pdev Device handle
* @param magic_val Old value of `magic' bit.
*/
void lm_clp_reset_prep(
)
{
/* Do some magic... */
}
/**
* Restore the value of the `magic' bit.
*
* @param pdev Device handle.
* @param magic_val Old value of the `magic' bit.
*/
void lm_clp_reset_done(
)
{
/* Restore the `magic' bit value... */
}
#endif // !b710
)
{
}
/**
* @Description
* Prepares for MCP reset: takes care of CLP configurations
* (saves it aside to resotre later) .
*
* @param pdev
* @param magic_val Old value of 'magic' bit.
*/
{
/* Set `magic' bit in order to save MF config */
if (!CHIP_IS_E1(pdev))
{
}
/* Get shmem offset */
/* Clear validity map flags */
if( shmem > 0 )
{
}
return LM_STATUS_SUCCESS;
}
{
u32_t shmem_sig_timeout = 0;
u32_t validity_offset = 0;
#ifdef _VBD_CMD_
return LM_STATUS_SUCCESS;
#endif
/* Get shmem offset */
if( shmem == 0 ) {
goto exit_lbl;
}
ASSERT_STATIC(0 != MCP_ONE_TIMEOUT);
if (CHIP_REV_IS_EMUL(pdev))
else
/* Wait for MCP to come up */
{
/* TBD: its best to check validity map of last port. currently checks on port 0. */
DbgMessage(pdev, INFORM, "shmem 0x%x validity map(0x%x)=0x%x\n", shmem, shmem + validity_offset, val);
/* check that shared memory is valid. */
break;
}
}
/* Check that shared memory is valid. This indicates that MCP is up. */
{
goto exit_lbl;
}
if (!CHIP_IS_E1(pdev))
{
/* Restore `magic' bit value */
}
return lm_status;
}
)
{
/* wait up to 3 seconds to get all locks. Whatsoever, reset mcp afterwards */
do {
/* Reset the MCP */
/* release the locks taken */
// No need to wait here a minimum time, since the mcp_comp will
// returns only when mcp is ready.
return lm_status;
}
//acquire split MCP access lock register
{
//Adjust timeout for our emulation needs
val_rd = 0;
//acquire lock using mcpr_access_lock SPLIT register
for(j = 0; j < cnt*10; j++)
{
{
break;
}
}
{
}
else
{
DbgBreakMsg("Cannot get access to nvram interface.\n");
}
return lm_status;
}
//Release split MCP access lock register
void
{
//This is only a sanity check, can remove later in free build.
val = 0;
//release mcpr_access_lock SPLIT register
} /* release_nvram_lock */
/*******************************************************************************
* Description:
* sends the mcp a keepalive to known registers
* Return:
******************************************************************************/
{
{
return LM_STATUS_INVALID_PARAMETER ;
}
{
return LM_STATUS_SUCCESS ;
}
{
}
&mcp_pulse);
/* The delta between driver pulse and mcp response
* should be 1 (before mcp response) or 0 (after mcp response)
*/
{
return LM_STATUS_FAILURE ;
}
return LM_STATUS_SUCCESS ;
}
/*******************************************************************************
* Description:
* Set driver pulse to MCP to always alive
* Return:
******************************************************************************/
{
{
return;
}
{
return ;
}
// Reset the MCP pulse to always alive
}
// entry that represents a function in the loader objcet
typedef struct _lm_loader_func_entry_t
{
// global object represents MCP - should be one per CHIP (boards)
typedef struct _lm_loader_path_obj_t
{
typedef struct _lm_loader_obj_t
{
} lm_loader_obj_t ;
lm_loader_obj_t g_lm_loader = {0};
// TRUE if the function is first on the port
// TRUE if the function is last on the port
( ( ( FUNC_ID(_pdev) == (_port_idx+0) ) ? TRUE : (FALSE == g_lm_loader.path_arr[_path_idx].func_arr[(_port_idx+0)].b_loaded) ) && \
( ( FUNC_ID(_pdev) == (_port_idx+2) ) ? TRUE : (FALSE == g_lm_loader.path_arr[_path_idx].func_arr[(_port_idx+2)].b_loaded) ) && \
( ( FUNC_ID(_pdev) == (_port_idx+4) ) ? TRUE : (FALSE == g_lm_loader.path_arr[_path_idx].func_arr[(_port_idx+4)].b_loaded) ) && \
( ( FUNC_ID(_pdev) == (_port_idx+6) ) ? TRUE : (_port_idx == 0)?(FALSE == g_lm_loader.path_arr[_path_idx].func_arr[6].b_loaded):(FALSE == g_lm_loader.path_arr[_path_idx].func_arr[7].b_loaded) ) )
#define LM_LOADER_IS_FIRST_ON_COMMON(_pdev,_path_idx) (LM_LOADER_IS_FIRST_ON_PORT(_pdev,_path_idx,0) && LM_LOADER_IS_FIRST_ON_PORT(_pdev,_path_idx,1))
#define LM_LOADER_IS_LAST_ON_COMMON(_pdev,_path_idx) (LM_LOADER_IS_LAST_ON_PORT(_pdev,_path_idx,0) && LM_LOADER_IS_LAST_ON_PORT(_pdev,_path_idx,1))
#define LM_LOADER_IS_FIRST_ON_CHIP(_pdev) (LM_LOADER_IS_FIRST_ON_COMMON(_pdev,0) && LM_LOADER_IS_FIRST_ON_COMMON(_pdev,1))
#define LM_LOADER_IS_LAST_ON_CHIP(_pdev) (LM_LOADER_IS_LAST_ON_COMMON(_pdev,0) && LM_LOADER_IS_LAST_ON_COMMON(_pdev,1))
// Accessed only with lock!
// TRUE if any device is currently locked
/*
*Function Name:lm_loader_opcode_to_mcp_msg
*
*Parameters:
* b_lock - true if it is lock false if unlock
*Description:
* LM_LOADER_OPCODE_XXX-->DRV_MSG_CODE_XXX
*Returns:
*
*/
{
switch(opcode)
{
case LM_LOADER_OPCODE_LOAD:
break;
break;
break;
break;
default:
DbgBreakIf(1) ;
break;
}
return mcp_msg ;
}
/*
*Function Name:mcp_resp_to_lm_loader_resp
*
*Parameters:
*
*Description:
* Translates mcp response to loader response FW_MSG_CODE_DRV_XXX->LM_LOADER_RESPONSE_XX
*Returns:
*
*/
{
switch(mcp_resp)
{
break;
break;
break;
break;
break;
break;
break;
break;
break;
default:
DbgBreakIf(1) ;
break;
}
return resp ;
}
// TBD - should it be the only indication??
#define IS_MCP_ON(_pdev) ( TEST_MODE_NO_MCP != GET_FLAGS(_pdev->params.test_mode, TEST_MODE_NO_MCP ) )
/*
*Function Name:lm_loader_lock
*
*Parameters:
*
*Description:
*Returns:
*
*/
{
{
// in case it is load (and not unload)
// send mfw LFA param
if ( DRV_MSG_CODE_LOAD_REQ == mcp_msg )
{
// in case BFS, set FORCE_LFA flag on
{
}
}
else if (is_suspend)
{
}
//we do this with no locks because acquiring the loader lock may take a long time (e.g in case another function takes a
//long time to initialize we will only get a response from the MCP when it's done). We don't need a lock because interrupts
//are disabled at this point and we won't get any IOCTLs.
lm_status = lm_mcp_cmd_send_recieve_non_atomic( pdev, lm_mcp_mb_header, mcp_msg, param, MCP_CMD_DEFAULT_TIMEOUT, &fw_resp ) ;
if ( LM_STATUS_SUCCESS == lm_status )
{
}
}
else // MCP_SIM
{
if( ERR_IF(PORT_ID(pdev) > 1) || ERR_IF(( FUNC_ID(pdev)) >= ARRSIZE(g_lm_loader.path_arr[PATH_ID(pdev)].func_arr)) )
{
DbgBreakMsg("Invalid PORT_ID/FUNC_ID\n");
return resp ;
}
do
{
{
}
else
{
// we'll release the lock when we are finish the work
break;
}
}while(1) ;
// Verify no one hold the lock, if so - it's a bug!
// mark our current function id as owner
switch( opcode )
{
case LM_LOADER_OPCODE_LOAD:
if( LM_LOADER_IS_FIRST_ON_CHIP(pdev) )
{
}
{
}
{
}
else
{
}
break;
{
}
{
}
else
{
}
break;
default:
DbgBreakIf(1) ;
break;
} // switch
} // MCP_SIM
return resp ;
}
/*
*Function Name:lm_loader_unlock
*
*Parameters:
*
*Description:
*Returns:
*
*/
lm_loader_response lm_loader_unlock( struct _lm_device_t *pdev, lm_loader_opcode opcode, OPTIONAL const u32_t* IN p_param )
{
{
return resp ;
}
{
//we do this with no locks because acquiring the loader lock may take a long time (e.g in case another function takes a
//long time to initialize we will only get a response from the MCP when it's done). We don't need a lock because interrupts
//are disabled at this point and we won't get any IOCTLs.
lm_status = lm_mcp_cmd_send_recieve_non_atomic(pdev, lm_mcp_mb_header, mcp_msg, param, MCP_CMD_DEFAULT_TIMEOUT, &fw_resp ) ;
if ( LM_STATUS_SUCCESS == lm_status )
{
}
}
else // MCP_SIM
{
// Verify current function id is the owner
switch( opcode )
{
case LM_LOADER_OPCODE_LOAD:
b_new_state = TRUE ;
break;
b_new_state = FALSE ;
break;
default:
DbgBreakIf(1) ;
break;
} // switch
// verify new state differs than current
// assign new state
// mark we don't own the lock anymore
} // MCP_SIM
return resp ;
}
/* Used for simulating a mcp reset where the mcp no longer knows the state of the uploaded drivers... */
{
}
/*
*Function Name:lm_mcp_cmd_init
*
*Parameters:
*
*Description:
* initiate sequence of mb + verify boot code version
*Returns:
*
*/
{
u8_t func_mb_id = 0;
{
return LM_STATUS_FAILURE ;
}
// we are on NO_MCP mode - nothing to do
{
return LM_STATUS_SUCCESS ;
}
//validtae bc version
if (bc_rev < BC_REV_SUPPORTED)
{
DbgBreakMsg("Please upgrade the bootcode version.\n");
// TODO add event log
return LM_STATUS_INVALID_PARAMETER;
}
// enable optic module verification according to BC version
{
}
{
SET_FLAGS(pdev->params.link.feature_config_flags, ELINK_FEATURE_CONFIG_BC_SUPPORTS_DUAL_PHY_OPT_MDL_VRFY);
}
{
}
{
SET_FLAGS(pdev->params.link.feature_config_flags, ELINK_FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED);
}
if (bc_rev >= REQ_BC_VER_4_MT_SUPPORTED)
{
}
// regular MCP mode
// read first seq number from shared memory
// read current mcp_pulse value
return LM_STATUS_SUCCESS;
}
{
u32_t minmax_param = 0;
//if in no MCP mode, don't do anything
if(!lm_is_mcp_detected(pdev))
{
return LM_STATUS_SUCCESS;
}
//if bootcode is less then REQ_BC_VER_4_SET_MF_BW, fail
if( bc_rev < REQ_BC_VER_4_SET_MF_BW )
{
return LM_STATUS_INVALID_PARAMETER;
}
//if not E2 or not MF mode, fail
{
return LM_STATUS_INVALID_PARAMETER;
}
//if the parameters are not valid, fail
if (max_bw > 100)
{
return LM_STATUS_INVALID_PARAMETER;
}
//we use FUNC_MF_CFG_MIN_BW_SHIFT because the param structure is supposed to
//be equivalent for this opcode and for the DCC opcode, but there is no define
//for this opcode.
//call lm_mcp_cmd_send_recieve with DRV_MSG_CODE_SET_MF_BW opcode and the parameter
lm_mcp_cmd_send_recieve(pdev, lm_mcp_mb_header, DRV_MSG_CODE_SET_MF_BW, minmax_param, MCP_CMD_DEFAULT_TIMEOUT, &resp);
//make sure that the response is FW_MSG_CODE_SET_MF_BW_SENT
if(resp != FW_MSG_CODE_SET_MF_BW_SENT)
{
return LM_STATUS_FAILURE;
}
//return what lm_mcp_cmd_send_recieve returned
return lm_status;
}
/*
*Function Name:lm_mcp_cmd_send
*
*Parameters:
*
*Description:
* send
*Returns:
*
*/
lm_status_t lm_mcp_cmd_send( struct _lm_device_t *pdev, lm_mcp_mb_type mb_type, u32_t drv_msg, u32_t param )
{
DbgMessage(pdev, INFORMi , "### mcp_cmd_send mb_type=0x%x drv_msg=0x%x param=0x%x\n", mb_type, drv_msg, param );
// we are on NO_MCP mode - nothing to do
{
return LM_STATUS_SUCCESS ;
}
switch( mb_type )
{
case lm_mcp_mb_header:
/* Write the parameter to the mcp */
if (p_seq)
{
}
break;
case lm_mcp_mb_pulse:
break;
case lm_mcp_mb_param:
default:
break;
}
{
return LM_STATUS_INVALID_PARAMETER ;
}
// incremant sequence
++(*p_seq);
// prepare message
return LM_STATUS_SUCCESS ;
}
/*
*Function Name:lm_mcp_cmd_response
*
*Parameters:
* TBD - add timeout value
*Description:
* assumption - only one request can be sent simultaneously
*Returns:
*
*/
{
DbgMessage(pdev, INFORMi, "### mcp_cmd_response mb_type=0x%x drv_msg=0x%x\n", mcp_mb_type, drv_msg );
{
return LM_STATUS_FAILURE ;
}
switch( mcp_mb_type )
{
case lm_mcp_mb_header:
break;
// TBD - is it needed ??
case lm_mcp_mb_pulse:
break;
case lm_mcp_mb_param:
default:
break;
}
{
return LM_STATUS_INVALID_PARAMETER ;
}
// Wait for reply 5 sec per unloading function
//TODO exponential back off
{
{
break;
}
}
return lm_status ;
}
{
if( LM_STATUS_SUCCESS != lm_status )
{
DbgMessage(pdev, FATAL, "mcp_cmd_send_and_recieve: mcp_cmd_send drv_msg=0x%x failed. lm_status=0x%x mcp_check=0x%x\n", drv_msg, lm_status, val);
DbgBreakMsg("mcp_cmd_send_and_recieve: mcp_cmd_send failed!\n");
return lm_status;
}
if( LM_STATUS_SUCCESS != lm_status )
{
DbgMessage(pdev, FATAL, "mcp_cmd_send_and_recieve: mcp_cmd_response drv_msg=0x%x failed. lm_status=0x%x mcp_check=0x%x\n", drv_msg, lm_status, val);
DbgBreakMsg("mcp_cmd_send_and_recieve: mcp_cmd_response failed!\n");
return lm_status;
}
return LM_STATUS_SUCCESS;
}
/*
*Function Name:lm_mcp_cmd_send_recieve
*
*Parameters:
*
*Description:
*
*Returns: lm_status_t
*
*/
{
lm_status = lm_mcp_cmd_send_recieve_non_atomic(pdev, mcp_mb_type, drv_msg, param, timeout, p_fw_resp);
return lm_status ;
}
// check if mcp program counter is advancing, In case it doesn't return the value in case it does, return 0
{
u32_t i = 0 ;
for( i = 0; i<4; i++ )
{
{
return 0; // OK
}
}
return reg; // mcp is hang on this value as program counter!
}
/**lm_mcp_cli_idx_to_drv_cap_flag
* Get the flag to set in drv_capabilities_flag for a given LM
* client.
*
* @param cli_id the LM client index.
*
* @return u32_t the appropriate flag for cli_id, or 0 if there
* is no matching flag.
*/
{
switch(cli_id)
{
case LM_CLI_IDX_NDIS:
return DRV_FLAGS_CAPABILITIES_LOADED_L2;
case LM_CLI_IDX_ISCSI:
case LM_CLI_IDX_FCOE:
case LM_CLI_IDX_MAX://may happen for UM clients that have no matching LM client, such as diag.
return 0;
case LM_CLI_IDX_FWD://fallthrough - this client has no bind/unbind flow and no matching UM client
case LM_CLI_IDX_OOO://fallthrough - this client has no bind/unbind flow and no matching UM client
default:
DbgBreakMsg("Invalid client type");
return 0;
}
}
void lm_mcp_indicate_client_imp(struct _lm_device_t *pdev, IN const lm_cli_idx_t cli_id, IN const u8_t b_bind )
{
u32_t drv_cap_shmem = 0;
if (CHIP_IS_E1x(pdev) ||
{
return;
}
if (0 == drv_cap_client)
{
//this is a client that does not require updating the SHMEM
return;
}
if( b_bind )
{
}
else
{
}
}
{
}
{
}