lm_phy.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/15/01 Hav Khauv Inception.
******************************************************************************/
#include "lm5710.h"
#include "phy_reg.h"
#include "license.h"
#include "mcp_shmem.h"
#include "lm_stats.h"
#include "577xx_int_offsets.h"
/***********************************************************/
/* CLC - Common Link Component API */
/***********************************************************/
/* Driver needs to redefine the cps_cb_st_ptr ( CPS CallBack Struct Pointer ) with its own */
#if defined(ELINK_DEBUG) && !defined(__SunOS)
{
}
{
}
{
}
{
}
#endif /* ELINK_DEBUG */
{
}
{
}
/* wb_write - pointer to 2 32 bits vars to be passed to the DMAE*/
{
}
{
}
/* mode - 0( LOW ) /1(HIGH)*/
{
}
{
}
{
return val;
}
{
}
{
#define MAX_WAIT_INTERVAL 50
if( 0 == wait_itr )
{
wait_time = microsecond ;
wait_itr = 1;
}
{
}
}
{
&fw_resp );
return fw_resp;
}
{
#ifdef DOS
#endif // DOS
}
{
switch( elink_log_id )
{
break;
break;
break;
break;
break;
default:
break;
} // elink_log_id switch
}
{
}
{
}
/*******************************************************************************
* Macros.
******************************************************************************/
#define MDIO_INDIRECT_REG_ADDR 0x1f
#define MDIO_ACCESS_TIMEOUT 1000
{\
case ELINK_STATUS_OK:\
break;\
case ELINK_STATUS_TIMEOUT:\
break;\
case ELINK_STATUS_NO_LINK:\
break;\
case ELINK_STATUS_ERROR:\
default:\
break;\
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
{
}
{
if(!(tmp & EMAC_MDIO_COMM_START_BUSY))
{
break;
}
}
if(tmp & EMAC_MDIO_COMM_START_BUSY)
{
DbgBreakMsg("Write phy register failed\n");
}
else
{
}
{
}
port*0x18, 0);
return lm_status;
} /* lm_mwrite */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
{
}
{
if(!(val & EMAC_MDIO_COMM_START_BUSY))
{
break;
}
}
if(val & EMAC_MDIO_COMM_START_BUSY)
{
DbgBreakMsg("Read phy register failed\n");
val = 0;
}
else
{
}
{
}
port*0x18, 0);
return lm_status;
} /* lm_mread */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
return lm_status;
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
return lm_status;
}
{
if (addr > 0x1f)
{
DbgBreakMsg("lm_set_phy_addr: error addr not valid\n");
return LM_STATUS_FAILURE;
}
return LM_STATUS_SUCCESS;
}
/*
*Function Name: lm_get_speed_real_from_elink_line_speed
*
*Parameters: IN line speed (from elink)
*
*Description:
*
*Returns: "real speed" in mbps units
*
*/
{
u32_t real_speed = 0;
{
// probably we get here from ioc_get_driver_info in case of no link
// we return 0 in that case
return 0;
}
switch(line_speed)
{
case ELINK_SPEED_10:
real_speed = 10;
break;
case ELINK_SPEED_100:
real_speed = 100;
break;
case ELINK_SPEED_1000:
real_speed = 1000;
break;
case ELINK_SPEED_2500:
real_speed = 2500;
break;
case ELINK_SPEED_10000:
real_speed = 10000;
break;
case ELINK_SPEED_20000:
real_speed = 20000;
break;
default:
DbgBreakIf(1);
break;
}
return real_speed;
}
/*
*Function Name: lm_get_speed_medium_from_elink_line_speed
*
*Parameters: IN line speed (from elink)
*
*Description:
*
*Returns: "medium" translation to LM units
*
*/
{
switch(line_speed)
{
case ELINK_SPEED_10:
break;
case ELINK_SPEED_100:
break;
case ELINK_SPEED_1000:
break;
case ELINK_SPEED_2500:
break;
case ELINK_SPEED_10000:
break;
case ELINK_SPEED_20000:
break;
default:
DbgBreakIf(1);
break;
}
return medium;
}
{
u32_t port_default_cfg = 0;
if(!CHIP_IS_E3(pdev))
{
return PORT_SPEED_10G;
}
{
return PORT_SPEED_10G;
}
LM_SHMEM_READ(pdev,OFFSETOF(shmem_region_t,dev_info.port_hw_config[PORT_ID(pdev)].default_cfg),&port_default_cfg);
{
return PORT_SPEED_1G;
}
else
{
return PORT_SPEED_10G;
}
}
/*
*Function Name: lm_loopback_req_meduim_convert
*
*Parameters: IN req_medium as received from upper layer
*
*Description: convert the req_meduim (recieved from diag driver / BMAPI) to relevant type according to the chip
* this is a little bit conusing since we override the value recieved by a new value
* but we need to do it for backward compatbiality.
*Returns: "medium" translation to LM units
*
*/
lm_medium_t lm_loopback_req_medium_convert( IN struct _lm_device_t *pdev, IN const lm_medium_t req_medium )
{
u32_t default_cfg = 0;
// Assumption bxdiag always send the following for each test type:
// LOOPBACK_TYPE_MAC --> LM_MEDIUM_TYPE_BMAC_LOOPBACK/LM_MEDIUM_TYPE_UMAC_LOOPBACK/LM_MEDIUM_TYPE_XMAC_LOOPBACK (bxdiag 7.0.1 only, never gold...)
// LOOPBACK_TYPE_PHY --> LM_MEDIUM_TYPE_XGXS_10_LOOPBACK
// Here, we'll "translate" the LM_MEDIUM_TYPE_XXX so it will work correctly in BCM578xx
if( CHIP_IS_E3(pdev) )
{
LM_SHMEM_READ(pdev,OFFSETOF(shmem_region_t,dev_info.port_hw_config[PORT_ID(pdev)].default_cfg),&default_cfg);
}
switch(req_medium)
{
// MAC loopback test
if( CHIP_IS_E3(pdev) )
{
{
}
else
{
}
}
else
{
}
break;
// PHY loopback test
if( CHIP_IS_E3(pdev) )
{
switch(default_cfg)
{
break;
break;
default:
break;
}
}
else
{
}
break;
default:
break;
}
return ret_medium;
}
{
{
return;
}
// link status
{
}
else
{
// if we are in multifunction mode and function is disabled indicate OS link down (unless loopback medium is set)
// Note that the CLC link is up so pmf handling is still going on
if (IS_MULTI_VNIC(pdev) && (GET_FLAGS(pdev->hw_info.mf_info.func_mf_cfg, FUNC_MF_CFG_FUNC_DISABLED)) &&
{
}
else
{
//in NIV mode, link_status is modified only from lm_niv_vif_set or from the FUNCTION_UPDATE completion(for loopback)
if(!IS_MF_AFEX_MODE(pdev))
{
}
}
// get speed
// get duplex
{
}
// get flow_control
{
}
{
}
// get EEE state
{
}
else
{
}
if (IS_MULTI_VNIC(pdev))
{
(real_speed *100),
if (real_speed > max_bw_in_100Mbps)
{
if (max_bw_in_100Mbps)
{
}
else
{
// in case the pdev->params.max_bw[VNIC_ID(pdev)] = 0
}
}
}
}
}
{
u32_t i = 0;
// inform all other port vnics not ourself
for( i=0; i<4 ;i++ )
{
{
}
}
}
void
{
{
return;
}
// notify stats
}
/**
* @description
* Configure cmng the firmware to the right CMNG values if this
*
* @note This function must be called under PHY_LOCK
* @param pdev
*/
{
u32_t port_speed = 0;
/* fairness is only supported for vnics in the meantime... */
if ((!IS_MULTI_VNIC(pdev)) ||
{
return;
}
{
// in case we are not PMF we still want to run this code in AFEX mode.
return;
}
}
{
{
}
}
void lm_link_fill_reported_data( IN lm_device_t *pdev, OUT lm_reported_link_params_t *lm_reported_link_params )
{
lm_reported_link_params->eee_policy = (u8_t)pdev->vars.eee_policy; // one of PORT_FEAT_CFG_EEE_POWER_MODE_*
}
// This function is called due to link change attention for none pmf it gets the link status from the shmem
{
// get current link params into current_link_params
/* Don't report link down again (if it is already down) */
{
b_indicate = FALSE;
}
else
{
// Don't report exact same link status twice
b_indicate = ( FALSE == mm_memcmp( ¤t_link_params, &pdev->vars.last_reported_link_params, sizeof(current_link_params)) );
}
{
// link up
// dropless flow control
{
{
pause_ena = 1;
}
LM_INTMEM_WRITE16(pdev,USTORM_ETH_PAUSE_ENABLED_OFFSET(PORT_ID(pdev)), pause_ena, BAR_USTRORM_INTMEM);
}
// indicate link up - except if we're in NIV mode where we wait for the VIF-SET/enable command from the MCP.
if( IS_MF_AFEX_MODE(pdev) )
{
b_indicate = FALSE;
}
// indicate link up
if( b_indicate )
{
DbgMessage(pdev, WARN, "lm_link_update: indicate link %d 0x%x \n",pdev->vars.link_status,pdev->vars.medium);
}
// notify stats
}
else
{ // link down
// indicate link down
// indicate link down
if( b_indicate )
{
DbgMessage(pdev, WARN, "lm_link_update: indicate link %d 0x%x \n",pdev->vars.link_status,pdev->vars.medium);
}
}
// notify othres funcs
{
}
}
// This function is called due to link change interrupt for the relevant function
// NOTE: this function must be called under phy lock
{
{
DbgBreakIf(!pdev);
return LM_STATUS_FAILURE;
}
// notify stats
{
}
// increment link_chng_cnt counter to indicate there was some link change.
return LM_STATUS_SUCCESS;
}
{
{
}
else
{
}
}
{
{
}
else
{
}
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
u8_t i = 0;
{
return LM_STATUS_INVALID_PARAMETER;
}
{
case PHY_PRIORITY_MODE_HW_DEF:
break;
i = ELINK_EXT_PHY1;
while (i < ELINK_MAX_PHYS)
{
{
break;
}
i++;
}
break;
case PHY_PRIORITY_MODE_SERDES:
i = ELINK_EXT_PHY1;
while (i < ELINK_MAX_PHYS)
{
{
break;
}
i++;
}
break;
case PHY_PRIORITY_MODE_HW_PIN:
break;
default:
DbgBreak();
break;
}
return lm_status;
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
// Get speed from shared memory not registry - if mcp is detected...
if(pdev->hw_info.mcp_detected && ((speed == LM_MEDIUM_SPEED_HARDWARE_DEFAULT) || (IS_MULTI_VNIC(pdev))))
{
DbgMessage(pdev, WARN, "lm_init_phy: pdev->hw_info.link_config[phy_num] = 0x%x\n",pdev->hw_info.link_config[phy_num]);
{
break;
break;
break;
break;
break;
break;
break;
break;
break;
default:
//Follow Teton solution:We need to do this because Microsoft's definition
// is not complete, like speed 2.5gb or some other speeds.
break;
}
}
if ( duplex == LM_MEDIUM_HALF_DUPLEX)
{
}
switch (speed)
{
case LM_MEDIUM_SPEED_AUTONEG:
break;
case LM_MEDIUM_SPEED_10MBPS:
break;
case LM_MEDIUM_SPEED_100MBPS:
break;
case LM_MEDIUM_SPEED_1000MBPS:
break;
case LM_MEDIUM_SPEED_2500MBPS:
break;
case LM_MEDIUM_SPEED_10GBPS:
break;
case LM_MEDIUM_SPEED_20GBPS:
break;
default:
return LM_STATUS_INVALID_PARAMETER;
}
if (flow_control == LM_FLOW_CONTROL_NONE)
{
}
else if (flow_control & LM_FLOW_CONTROL_AUTO_PAUSE)
{
}
else
{
/* Under flow control reporting mode we */
if ((speed == LM_MEDIUM_SPEED_AUTONEG) &&
{
}
else
{
{
}
{
}
}
}
return LM_STATUS_SUCCESS;
}
/**
* @Description
* this function sets the flow control auto negotiation
* advertise parameter.
*
* @param pdev
* @param flow_control
*/
{
/* There are two cases where we will set flow control auto adv to TX only.
* a certain threshold. (mtu_above_th)
* 2. cq CQ57772, required only under special registry key, in which we want the flow control displayed
* in gui (i.e. received by ioctl) to show the resolved flow control (after auto negotiation) and not
* the requested flow control (in case forced force control is used). For this purpose, if we're in auto-neg
* and a forced flow control was requested, we set the request flow control to auto (later on in set_link_parameters)
* if forced TX is requested, we se the adv to tx only..(report_mode_tx_only)
*/
mtu_above_thr = CHIP_IS_E1x(pdev) && !IS_MULTI_VNIC(pdev) && (pdev->params.mtu_max > LM_MTU_FLOW_CTRL_TX_THR);
report_mode_tx_only = (pdev->params.flow_control_reporting_mode == LM_FLOW_CONTROL_REPORTING_MODE_ENABLED) &&
if (mtu_above_thr || report_mode_tx_only)
{
}
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
u8_t i = 0;
lm_medium_t speed = 0;
{
return LM_STATUS_SUCCESS;
}
//fill clc params
{
DbgBreakIf(!pdev) ;
return LM_STATUS_FAILURE;
}
{
{
case 0x4f70:
case 0x4375:
{
{
// The relevant ssids are from SINGLE_MEDIA board type, so only EXT_PHY1 needs to be set.
SET_FLAGS(pdev->params.link.feature_config_flags, ELINK_FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED);
}
}
break;
default:
break;
}
}
/* Set req_fc_auto_adv */
for (i = 0 ; i < 6 ; i++)
{
}
if (LM_SWCFG_HW_DEF == sw_config )
{
{
}
else
{
}
}
#ifndef EDIAG
{
case LM_AUTOGREEEN_NVRAM:
// Use whatever is configured in the NVRAM
break;
case LM_AUTOGREEEN_DISABLED:
break;
case LM_AUTOGREEEN_ENABLED:
{
case LM_EEE_CONTROL_HIGH: // enable EEE mode, advertise "AGGRESSIVE" (Registry: MaxPowerSave)
break;
case LM_EEE_CONTROL_MED: // enable EEE mode, advertise "BALANCED" (Registry: Balance)
break;
case LM_EEE_CONTROL_LOW: // enable EEE mode, advertise "LOW_LATENCY" (Registry: MaxPerformace)
break;
case LM_EEE_CONTROL_NVRAM: // use NVRAM value
break;
default:
// break here if illegal value was read from registry (CHK version only).
DbgBreakIf(1);
break;
}
break;
default:
}
#endif
switch (sw_config)
{
// TODO change to shmem defines
case LM_SWCFG_1G:
break;
case LM_SWCFG_10G:
break;
default:
DbgBreakIf(1);
break;
}
// Override setting if dual media and phy type specified from miniport
if ((ELINK_DUAL_MEDIA(link)) &&
((type == LM_MEDIUM_TYPE_SERDES) ||
(type == LM_MEDIUM_TYPE_XGXS)))
{
}
if (LM_STATUS_SUCCESS == lm_status) {
if (ELINK_DUAL_MEDIA(link))
{
}
} else {
return lm_status;
}
/* If 10G is requested and it is blocked on this KR, issue event log */
{
if( LM_MEDIUM_SPEED_10GBPS == speed )
{
// block this request (elink does not support it) & log
return LM_STATUS_SUCCESS;
}
}
switch (type)
{
break;
// for bacs PHY loopback test set speed to 10G.
// Otherwise do not overwrite the speed
{
{
}
else
{
}
}
break;
break;
break;
{
}
{
}
else
{
}
// TBD: Dual Media ext PHY loopback test for second ext PHY ?
break;
break;
break;
break;
default:
break;
}
// Handle dual media boards, if phy type specified from miniport
if (ELINK_DUAL_MEDIA(link))
{
switch (type)
{
case LM_MEDIUM_TYPE_SERDES:
i = ELINK_EXT_PHY1;
while (i < ELINK_MAX_PHYS)
{
{
lm_set_phy_selection(pdev, i);
break;
}
i++;
}
break;
case LM_MEDIUM_TYPE_XGXS:
i = ELINK_EXT_PHY1;
while (i < ELINK_MAX_PHYS)
{
{
lm_set_phy_selection(pdev, i);
break;
}
i++;
}
break;
case LM_MEDIUM_AUTO_DETECT:
break;
// Do nothing.
break;
default:
DbgBreak();
break;
}
}
{
{
}
{
{
}
}
}
else
{
}
// Emulation FPGA or LOOPBACK non pmf in multi vnic mode link might be up now
return LM_STATUS_SUCCESS;
} /* lm_init_phy */
#ifndef EDIAG
/*
* \brief query i2c information if exists and write it to 3rd party known place
*
* \param pdev
*
* \return lm_status_t
*
*/
{
u8_t ext_phy_type = 0;
u8_t sff8472_comp = 0;
if( !b_need_refresh )
{
// that means we need nothing here...
return lm_status;
}
// Check which PHY controls the SFP+ module
{
{
// Capture A0 section + static part of A2 section only once if A2 is supportd
{
elink_status = elink_read_sfp_module_eeprom( &pdev->params.link.phy[ext_phy_type], // ELINK_INT_PHY, ELINK_EXT_PHY1, ELINK_EXT_PHY2
0,
{
// Set same status to A2 section and quit as A0 is mandatory
break; // Quit the loop
}
// Check if the module is compliant with SFF8472, meaning it supports A2 section.
if ( (!sff8472_comp) ||
{
// Release the HW LOCK
// Set A2 section query status to NOT SUPPORTED and quit
// Exit loop
break;
}
elink_status = elink_read_sfp_module_eeprom( &pdev->params.link.phy[ext_phy_type], // ELINK_INT_PHY, ELINK_EXT_PHY1, ELINK_EXT_PHY2
{
break; // no use continue if we didn't get A2 data
}
} // !ELINK_STATUS_OK
/* Avoid reading A2 section if the module doesn't support SFF8472. */
{
break;
}
// Capture the dynamic part of A2
elink_status = elink_read_sfp_module_eeprom( &pdev->params.link.phy[ext_phy_type], // ELINK_INT_PHY, ELINK_EXT_PHY1, ELINK_EXT_PHY2
// Calculate and validate I2C section checksum
if( ELINK_STATUS_OK == elink_status )
{
if( ELINK_STATUS_OK != elink_status )
{
}
}
// only one sfp+ module is expected on board so we exit the ext_phy_type loop
break;
} // if( ELINK_ETH_PHY_SFPP_10G_FIBER == ...
} // for "ext_phy_type"
// it means that there is a need to write otherwise we even didn't enter the loop
// so the registry write is redundent.
{
}
return lm_status;
} /* lm_link_i2c_update */
#endif
/**
* @Description
* This function is called periodically, every time the link
* timer expires, it's main purpose is to call elink under
* appropriate locks to perform any periodic tasks
* @assumptions:
* called under UM_PHY_LOCK!
*
* @param pdev
*
* @return lm_status_t
*/
{
if (CHIP_REV_IS_SLOW(pdev))
{
return LM_STATUS_SUCCESS;
}
{
#ifndef EDIAG
#endif
}
return LM_STATUS_SUCCESS;
}
/*
*Function Name:lm_get_external_phy_fw_version
*
*Parameters:
*
*Description:
* Funciton should be called under PHY_LOCK
*Returns:
*
*/
u8_t * sz_version,
{
{
return LM_STATUS_INVALID_PARAMETER;
}
// reset the returned value to zero
*sz_version = '\0';
if (elink_status == ELINK_STATUS_OK)
{
// Update internal hw_info structure for debugging purpose
{
}
return LM_STATUS_SUCCESS ;
}
else
{
return LM_STATUS_FAILURE;
}
}
/*
*Function Name:lm_update_external_phy_fw_prepare
*
*Parameters:
*
*Description:
*
*Returns:
*
*/
{
do
{
if (!CHIP_IS_E1x(pdev))
{
}
if( ELINK_STATUS_OK != elink_status )
{
break;
}
if( ELINK_STATUS_OK != elink_status )
{
break;
}
} while(0);
if( ELINK_STATUS_OK != elink_status )
{
goto _exit;
}
{
{
}
break;
default:
break;
}
return lm_status;
}
/*
*Function Name:lm_update_external_phy_fw_reinit
*
*Parameters:
*
*Description:
*
*Returns:
*
*/
{
// Emulation FPGA or LOOPBACK non pmf in multi vnic mode link might be up now
if( LM_STATUS_SUCCESS == lm_status )
{
// in case success -reset version
}
return lm_status;
}
/*
*Function Name:lm_update_external_phy_fw_done
*
*Parameters:
*
*Description:
*
*Returns:
*
*/
{
u8_t ext_phy_addr = 0;
{
break;
default:
break;
}
if( b_exit )
{
return lm_status ;
}
/* DSP Remove Download Mode */
/* wait 0.5 sec to allow it to run */
return lm_status;
}
{
{
return LM_STATUS_SUCCESS;
}
// Get speed from registry not shared memory - if mcp is not detected...
if(!pdev->hw_info.mcp_detected || ((speed != LM_MEDIUM_SPEED_HARDWARE_DEFAULT) && (!IS_MULTI_VNIC(pdev))))
{
switch (speed)
{
case LM_MEDIUM_SPEED_AUTONEG:
case LM_MEDIUM_SPEED_10MBPS:
case LM_MEDIUM_SPEED_100MBPS:
case LM_MEDIUM_SPEED_1000MBPS:
case LM_MEDIUM_SPEED_2500MBPS:
case LM_MEDIUM_SPEED_10GBPS:
case LM_MEDIUM_SPEED_20GBPS:
break;
default:
}
}
return lm_status;
}