2008-04-27 12:55:59 +01:00
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2006 - 2008 Solarflare Communications Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation , incorporated herein by reference .
*/
/*
* Useful functions for working with MDIO clause 45 PHYs
*/
# include <linux/types.h>
# include <linux/ethtool.h>
# include <linux/delay.h>
# include "net_driver.h"
# include "mdio_10g.h"
# include "boards.h"
2009-01-29 17:49:09 +00:00
# include "workarounds.h"
2008-04-27 12:55:59 +01:00
int mdio_clause45_reset_mmd ( struct efx_nic * port , int mmd ,
int spins , int spintime )
{
u32 ctrl ;
int phy_id = port - > mii . phy_id ;
/* Catch callers passing values in the wrong units (or just silly) */
EFX_BUG_ON_PARANOID ( spins * spintime > = 5000 ) ;
mdio_clause45_write ( port , phy_id , mmd , MDIO_MMDREG_CTRL1 ,
( 1 < < MDIO_MMDREG_CTRL1_RESET_LBN ) ) ;
/* Wait for the reset bit to clear. */
do {
msleep ( spintime ) ;
ctrl = mdio_clause45_read ( port , phy_id , mmd , MDIO_MMDREG_CTRL1 ) ;
spins - - ;
} while ( spins & & ( ctrl & ( 1 < < MDIO_MMDREG_CTRL1_RESET_LBN ) ) ) ;
return spins ? spins : - ETIMEDOUT ;
}
static int mdio_clause45_check_mmd ( struct efx_nic * efx , int mmd ,
int fault_fatal )
{
int status ;
int phy_id = efx - > mii . phy_id ;
2008-05-07 13:36:19 +01:00
if ( LOOPBACK_INTERNAL ( efx ) )
return 0 ;
2008-12-12 21:50:46 -08:00
if ( mmd ! = MDIO_MMD_AN ) {
/* Read MMD STATUS2 to check it is responding. */
status = mdio_clause45_read ( efx , phy_id , mmd ,
MDIO_MMDREG_STAT2 ) ;
if ( ( ( status > > MDIO_MMDREG_STAT2_PRESENT_LBN ) &
( ( 1 < < MDIO_MMDREG_STAT2_PRESENT_WIDTH ) - 1 ) ) ! =
MDIO_MMDREG_STAT2_PRESENT_VAL ) {
EFX_ERR ( efx , " PHY MMD %d not responding. \n " , mmd ) ;
return - EIO ;
}
2008-04-27 12:55:59 +01:00
}
/* Read MMD STATUS 1 to check for fault. */
status = mdio_clause45_read ( efx , phy_id , mmd , MDIO_MMDREG_STAT1 ) ;
if ( ( status & ( 1 < < MDIO_MMDREG_STAT1_FAULT_LBN ) ) ! = 0 ) {
if ( fault_fatal ) {
EFX_ERR ( efx , " PHY MMD %d reporting fatal "
" fault: status %x \n " , mmd , status ) ;
return - EIO ;
} else {
EFX_LOG ( efx , " PHY MMD %d reporting status "
" %x (expected) \n " , mmd , status ) ;
}
}
return 0 ;
}
/* This ought to be ridiculous overkill. We expect it to fail rarely */
# define MDIO45_RESET_TIME 1000 /* ms */
# define MDIO45_RESET_ITERS 100
int mdio_clause45_wait_reset_mmds ( struct efx_nic * efx ,
unsigned int mmd_mask )
{
const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS ;
int tries = MDIO45_RESET_ITERS ;
int rc = 0 ;
int in_reset ;
while ( tries ) {
int mask = mmd_mask ;
int mmd = 0 ;
int stat ;
in_reset = 0 ;
while ( mask ) {
if ( mask & 1 ) {
stat = mdio_clause45_read ( efx ,
efx - > mii . phy_id ,
mmd ,
MDIO_MMDREG_CTRL1 ) ;
if ( stat < 0 ) {
EFX_ERR ( efx , " failed to read status of "
" MMD %d \n " , mmd ) ;
return - EIO ;
}
if ( stat & ( 1 < < MDIO_MMDREG_CTRL1_RESET_LBN ) )
in_reset | = ( 1 < < mmd ) ;
}
mask = mask > > 1 ;
mmd + + ;
}
if ( ! in_reset )
break ;
tries - - ;
msleep ( spintime ) ;
}
if ( in_reset ! = 0 ) {
EFX_ERR ( efx , " not all MMDs came out of reset in time. "
" MMDs still in reset: %x \n " , in_reset ) ;
rc = - ETIMEDOUT ;
}
return rc ;
}
int mdio_clause45_check_mmds ( struct efx_nic * efx ,
unsigned int mmd_mask , unsigned int fatal_mask )
{
2008-12-12 21:44:14 -08:00
u32 devices ;
int mmd = 0 , probe_mmd ;
2008-04-27 12:55:59 +01:00
/* Historically we have probed the PHYXS to find out what devices are
* present , but that doesn ' t work so well if the PHYXS isn ' t expected
* to exist , if so just find the first item in the list supplied . */
2008-12-12 21:44:14 -08:00
probe_mmd = ( mmd_mask & MDIO_MMDREG_DEVS_PHYXS ) ? MDIO_MMD_PHYXS :
2008-04-27 12:55:59 +01:00
__ffs ( mmd_mask ) ;
2008-12-12 21:44:14 -08:00
devices = ( mdio_clause45_read ( efx , efx - > mii . phy_id ,
probe_mmd , MDIO_MMDREG_DEVS0 ) |
mdio_clause45_read ( efx , efx - > mii . phy_id ,
probe_mmd , MDIO_MMDREG_DEVS1 ) < < 16 ) ;
2008-04-27 12:55:59 +01:00
/* Check all the expected MMDs are present */
if ( devices < 0 ) {
EFX_ERR ( efx , " failed to read devices present \n " ) ;
return - EIO ;
}
if ( ( devices & mmd_mask ) ! = mmd_mask ) {
EFX_ERR ( efx , " required MMDs not present: got %x, "
" wanted %x \n " , devices , mmd_mask ) ;
return - ENODEV ;
}
EFX_TRACE ( efx , " Devices present: %x \n " , devices ) ;
/* Check all required MMDs are responding and happy. */
while ( mmd_mask ) {
if ( mmd_mask & 1 ) {
int fault_fatal = fatal_mask & 1 ;
if ( mdio_clause45_check_mmd ( efx , mmd , fault_fatal ) )
return - EIO ;
}
mmd_mask = mmd_mask > > 1 ;
fatal_mask = fatal_mask > > 1 ;
mmd + + ;
}
return 0 ;
}
2008-09-01 12:46:50 +01:00
bool mdio_clause45_links_ok ( struct efx_nic * efx , unsigned int mmd_mask )
2008-04-27 12:55:59 +01:00
{
int phy_id = efx - > mii . phy_id ;
2008-12-26 13:46:12 -08:00
u32 reg ;
2008-09-01 12:46:50 +01:00
bool ok = true ;
2008-04-27 12:55:59 +01:00
int mmd = 0 ;
2008-05-07 13:36:19 +01:00
/* If the port is in loopback, then we should only consider a subset
* of mmd ' s */
if ( LOOPBACK_INTERNAL ( efx ) )
2008-09-01 12:46:50 +01:00
return true ;
2008-05-07 13:36:19 +01:00
else if ( efx - > loopback_mode = = LOOPBACK_NETWORK )
2008-09-01 12:46:50 +01:00
return false ;
2008-09-01 12:48:17 +01:00
else if ( efx_phy_mode_disabled ( efx - > phy_mode ) )
return false ;
2009-01-29 17:51:15 +00:00
else if ( efx - > loopback_mode = = LOOPBACK_PHYXS )
2008-12-12 21:44:14 -08:00
mmd_mask & = ~ ( MDIO_MMDREG_DEVS_PHYXS |
MDIO_MMDREG_DEVS_PCS |
2008-12-12 21:50:46 -08:00
MDIO_MMDREG_DEVS_PMAPMD |
MDIO_MMDREG_DEVS_AN ) ;
2009-01-29 17:51:15 +00:00
else if ( efx - > loopback_mode = = LOOPBACK_PCS )
2008-12-12 21:44:14 -08:00
mmd_mask & = ~ ( MDIO_MMDREG_DEVS_PCS |
2008-12-12 21:50:46 -08:00
MDIO_MMDREG_DEVS_PMAPMD |
MDIO_MMDREG_DEVS_AN ) ;
2008-05-07 13:36:19 +01:00
else if ( efx - > loopback_mode = = LOOPBACK_PMAPMD )
2008-12-12 21:50:46 -08:00
mmd_mask & = ~ ( MDIO_MMDREG_DEVS_PMAPMD |
MDIO_MMDREG_DEVS_AN ) ;
2008-05-07 13:36:19 +01:00
2009-01-29 17:51:15 +00:00
if ( ! mmd_mask ) {
/* Use presence of XGMII faults in leui of link state */
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_PHYXS ,
MDIO_PHYXS_STATUS2 ) ;
return ! ( reg & ( 1 < < MDIO_PHYXS_STATUS2_RX_FAULT_LBN ) ) ;
}
2008-04-27 12:55:59 +01:00
while ( mmd_mask ) {
if ( mmd_mask & 1 ) {
/* Double reads because link state is latched, and a
* read moves the current state into the register */
2008-12-26 13:46:12 -08:00
reg = mdio_clause45_read ( efx , phy_id ,
mmd , MDIO_MMDREG_STAT1 ) ;
reg = mdio_clause45_read ( efx , phy_id ,
mmd , MDIO_MMDREG_STAT1 ) ;
ok = ok & & ( reg & ( 1 < < MDIO_MMDREG_STAT1_LINK_LBN ) ) ;
2008-04-27 12:55:59 +01:00
}
mmd_mask = ( mmd_mask > > 1 ) ;
mmd + + ;
}
return ok ;
}
2008-05-07 13:36:19 +01:00
void mdio_clause45_transmit_disable ( struct efx_nic * efx )
{
2008-12-12 21:48:57 -08:00
mdio_clause45_set_flag ( efx , efx - > mii . phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_TXDIS , MDIO_MMDREG_TXDIS_GLOBAL_LBN ,
efx - > phy_mode & PHY_MODE_TX_DISABLED ) ;
2008-05-07 13:36:19 +01:00
}
void mdio_clause45_phy_reconfigure ( struct efx_nic * efx )
{
int phy_id = efx - > mii . phy_id ;
2008-12-12 21:48:57 -08:00
mdio_clause45_set_flag ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_CTRL1 , MDIO_PMAPMD_CTRL1_LBACK_LBN ,
efx - > loopback_mode = = LOOPBACK_PMAPMD ) ;
mdio_clause45_set_flag ( efx , phy_id , MDIO_MMD_PCS ,
MDIO_MMDREG_CTRL1 , MDIO_MMDREG_CTRL1_LBACK_LBN ,
efx - > loopback_mode = = LOOPBACK_PCS ) ;
mdio_clause45_set_flag ( efx , phy_id , MDIO_MMD_PHYXS ,
MDIO_MMDREG_CTRL1 , MDIO_MMDREG_CTRL1_LBACK_LBN ,
efx - > loopback_mode = = LOOPBACK_NETWORK ) ;
2008-05-07 13:36:19 +01:00
}
2008-11-04 20:34:56 +00:00
static void mdio_clause45_set_mmd_lpower ( struct efx_nic * efx ,
int lpower , int mmd )
{
int phy = efx - > mii . phy_id ;
int stat = mdio_clause45_read ( efx , phy , mmd , MDIO_MMDREG_STAT1 ) ;
EFX_TRACE ( efx , " Setting low power mode for MMD %d to %d \n " ,
mmd , lpower ) ;
if ( stat & ( 1 < < MDIO_MMDREG_STAT1_LPABLE_LBN ) ) {
2008-12-12 21:48:57 -08:00
mdio_clause45_set_flag ( efx , phy , mmd , MDIO_MMDREG_CTRL1 ,
MDIO_MMDREG_CTRL1_LPOWER_LBN , lpower ) ;
2008-11-04 20:34:56 +00:00
}
}
void mdio_clause45_set_mmds_lpower ( struct efx_nic * efx ,
int low_power , unsigned int mmd_mask )
{
int mmd = 0 ;
2008-12-12 21:50:46 -08:00
mmd_mask & = ~ MDIO_MMDREG_DEVS_AN ;
2008-11-04 20:34:56 +00:00
while ( mmd_mask ) {
if ( mmd_mask & 1 )
mdio_clause45_set_mmd_lpower ( efx , low_power , mmd ) ;
mmd_mask = ( mmd_mask > > 1 ) ;
mmd + + ;
}
}
2009-01-29 17:59:37 +00:00
static u32 mdio_clause45_get_an ( struct efx_nic * efx , u16 addr )
2008-12-12 21:50:46 -08:00
{
int phy_id = efx - > mii . phy_id ;
u32 result = 0 ;
int reg ;
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN , addr ) ;
if ( reg & ADVERTISE_10HALF )
result | = ADVERTISED_10baseT_Half ;
if ( reg & ADVERTISE_10FULL )
result | = ADVERTISED_10baseT_Full ;
if ( reg & ADVERTISE_100HALF )
result | = ADVERTISED_100baseT_Half ;
if ( reg & ADVERTISE_100FULL )
result | = ADVERTISED_100baseT_Full ;
return result ;
}
2008-04-27 12:55:59 +01:00
/**
* mdio_clause45_get_settings - Read ( some of ) the PHY settings over MDIO .
* @ efx : Efx NIC
* @ ecmd : Buffer for settings
*
* On return the ' port ' , ' speed ' , ' supported ' and ' advertising ' fields of
2008-12-12 21:50:46 -08:00
* ecmd have been filled out .
2008-04-27 12:55:59 +01:00
*/
void mdio_clause45_get_settings ( struct efx_nic * efx ,
struct ethtool_cmd * ecmd )
{
2008-12-12 21:50:46 -08:00
mdio_clause45_get_settings_ext ( efx , ecmd , 0 , 0 ) ;
}
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
/**
* mdio_clause45_get_settings_ext - Read ( some of ) the PHY settings over MDIO .
* @ efx : Efx NIC
* @ ecmd : Buffer for settings
* @ xnp : Advertised Extended Next Page state
* @ xnp_lpa : Link Partner ' s advertised XNP state
*
* On return the ' port ' , ' speed ' , ' supported ' and ' advertising ' fields of
* ecmd have been filled out .
*/
void mdio_clause45_get_settings_ext ( struct efx_nic * efx ,
struct ethtool_cmd * ecmd ,
2009-01-29 17:59:37 +00:00
u32 npage_adv , u32 npage_lpa )
2008-12-12 21:50:46 -08:00
{
int phy_id = efx - > mii . phy_id ;
int reg ;
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
ecmd - > transceiver = XCVR_INTERNAL ;
ecmd - > phy_address = phy_id ;
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_CTRL2 ) ;
switch ( reg & MDIO_PMAPMD_CTRL2_TYPE_MASK ) {
2008-04-27 12:55:59 +01:00
case MDIO_PMAPMD_CTRL2_10G_BT :
case MDIO_PMAPMD_CTRL2_1G_BT :
case MDIO_PMAPMD_CTRL2_100_BT :
case MDIO_PMAPMD_CTRL2_10_BT :
ecmd - > port = PORT_TP ;
2008-12-12 21:50:46 -08:00
ecmd - > supported = SUPPORTED_TP ;
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_SPEED ) ;
if ( reg & ( 1 < < MDIO_MMDREG_SPEED_10G_LBN ) )
ecmd - > supported | = SUPPORTED_10000baseT_Full ;
if ( reg & ( 1 < < MDIO_MMDREG_SPEED_1000M_LBN ) )
ecmd - > supported | = ( SUPPORTED_1000baseT_Full |
SUPPORTED_1000baseT_Half ) ;
if ( reg & ( 1 < < MDIO_MMDREG_SPEED_100M_LBN ) )
ecmd - > supported | = ( SUPPORTED_100baseT_Full |
SUPPORTED_100baseT_Half ) ;
if ( reg & ( 1 < < MDIO_MMDREG_SPEED_10M_LBN ) )
ecmd - > supported | = ( SUPPORTED_10baseT_Full |
SUPPORTED_10baseT_Half ) ;
ecmd - > advertising = ADVERTISED_TP ;
2008-04-27 12:55:59 +01:00
break ;
2008-12-12 21:50:46 -08:00
/* We represent CX4 as fibre in the absence of anything better */
case MDIO_PMAPMD_CTRL2_10G_CX4 :
/* All the other defined modes are flavours of optical */
2008-04-27 12:55:59 +01:00
default :
ecmd - > port = PORT_FIBRE ;
ecmd - > supported = SUPPORTED_FIBRE ;
ecmd - > advertising = ADVERTISED_FIBRE ;
break ;
}
2008-12-12 21:50:46 -08:00
if ( efx - > phy_op - > mmds & DEV_PRESENT_BIT ( MDIO_MMD_AN ) ) {
ecmd - > supported | = SUPPORTED_Autoneg ;
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 ) ;
if ( reg & BMCR_ANENABLE ) {
ecmd - > autoneg = AUTONEG_ENABLE ;
ecmd - > advertising | =
ADVERTISED_Autoneg |
2009-01-29 17:59:37 +00:00
mdio_clause45_get_an ( efx , MDIO_AN_ADVERTISE ) |
npage_adv ;
2008-12-12 21:50:46 -08:00
} else
ecmd - > autoneg = AUTONEG_DISABLE ;
} else
ecmd - > autoneg = AUTONEG_DISABLE ;
2008-12-26 13:49:25 -08:00
if ( ecmd - > autoneg ) {
/* If AN is complete, report best common mode,
* otherwise report best advertised mode . */
2009-01-29 17:59:37 +00:00
u32 modes = 0 ;
2008-12-26 13:49:25 -08:00
if ( mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_STAT1 ) &
2009-01-29 17:59:37 +00:00
( 1 < < MDIO_AN_STATUS_AN_DONE_LBN ) )
modes = ( ecmd - > advertising &
( mdio_clause45_get_an ( efx , MDIO_AN_LPA ) |
npage_lpa ) ) ;
if ( modes = = 0 )
modes = ecmd - > advertising ;
if ( modes & ADVERTISED_10000baseT_Full ) {
2008-12-12 21:50:46 -08:00
ecmd - > speed = SPEED_10000 ;
ecmd - > duplex = DUPLEX_FULL ;
2009-01-29 17:59:37 +00:00
} else if ( modes & ( ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half ) ) {
2008-12-12 21:50:46 -08:00
ecmd - > speed = SPEED_1000 ;
2009-01-29 17:59:37 +00:00
ecmd - > duplex = ! ! ( modes & ADVERTISED_1000baseT_Full ) ;
} else if ( modes & ( ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half ) ) {
2008-12-12 21:50:46 -08:00
ecmd - > speed = SPEED_100 ;
2009-01-29 17:59:37 +00:00
ecmd - > duplex = ! ! ( modes & ADVERTISED_100baseT_Full ) ;
2008-12-12 21:50:46 -08:00
} else {
ecmd - > speed = SPEED_10 ;
2009-01-29 17:59:37 +00:00
ecmd - > duplex = ! ! ( modes & ADVERTISED_10baseT_Full ) ;
2008-12-12 21:50:46 -08:00
}
} else {
/* Report forced settings */
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_CTRL1 ) ;
ecmd - > speed = ( ( ( reg & BMCR_SPEED1000 ) ? 100 : 1 ) *
( ( reg & BMCR_SPEED100 ) ? 100 : 10 ) ) ;
ecmd - > duplex = ( reg & BMCR_FULLDPLX | |
ecmd - > speed = = SPEED_10000 ) ;
}
2008-04-27 12:55:59 +01:00
}
/**
* mdio_clause45_set_settings - Set ( some of ) the PHY settings over MDIO .
* @ efx : Efx NIC
* @ ecmd : New settings
*/
int mdio_clause45_set_settings ( struct efx_nic * efx ,
struct ethtool_cmd * ecmd )
{
2008-12-12 21:50:46 -08:00
int phy_id = efx - > mii . phy_id ;
struct ethtool_cmd prev ;
u32 required ;
2009-01-29 17:59:37 +00:00
int reg ;
2008-12-12 21:50:46 -08:00
efx - > phy_op - > get_settings ( efx , & prev ) ;
if ( ecmd - > advertising = = prev . advertising & &
ecmd - > speed = = prev . speed & &
ecmd - > duplex = = prev . duplex & &
ecmd - > port = = prev . port & &
ecmd - > autoneg = = prev . autoneg )
2008-04-27 12:55:59 +01:00
return 0 ;
2008-12-12 21:50:46 -08:00
/* We can only change these settings for -T PHYs */
if ( prev . port ! = PORT_TP | | ecmd - > port ! = PORT_TP )
return - EINVAL ;
2009-01-29 17:59:37 +00:00
/* Check that PHY supports these settings */
if ( ecmd - > autoneg ) {
required = SUPPORTED_Autoneg ;
} else if ( ecmd - > duplex ) {
2008-12-12 21:50:46 -08:00
switch ( ecmd - > speed ) {
2009-01-29 17:59:37 +00:00
case SPEED_10 : required = SUPPORTED_10baseT_Full ; break ;
case SPEED_100 : required = SUPPORTED_100baseT_Full ; break ;
default : return - EINVAL ;
2008-12-12 21:50:46 -08:00
}
} else {
switch ( ecmd - > speed ) {
2009-01-29 17:59:37 +00:00
case SPEED_10 : required = SUPPORTED_10baseT_Half ; break ;
case SPEED_100 : required = SUPPORTED_100baseT_Half ; break ;
default : return - EINVAL ;
2008-12-12 21:50:46 -08:00
}
}
required | = ecmd - > advertising ;
if ( required & ~ prev . supported )
return - EINVAL ;
2009-01-29 17:59:37 +00:00
if ( ecmd - > autoneg ) {
bool xnp = ( ecmd - > advertising & ADVERTISED_10000baseT_Full
| | EFX_WORKAROUND_13204 ( efx ) ) ;
/* Set up the base page */
reg = ADVERTISE_CSMA ;
if ( ecmd - > advertising & ADVERTISED_10baseT_Half )
reg | = ADVERTISE_10HALF ;
if ( ecmd - > advertising & ADVERTISED_10baseT_Full )
reg | = ADVERTISE_10FULL ;
if ( ecmd - > advertising & ADVERTISED_100baseT_Half )
reg | = ADVERTISE_100HALF ;
if ( ecmd - > advertising & ADVERTISED_100baseT_Full )
reg | = ADVERTISE_100FULL ;
if ( xnp )
reg | = ADVERTISE_RESV ;
else if ( ecmd - > advertising & ( ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseT_Full ) )
reg | = ADVERTISE_NPAGE ;
reg | = efx_fc_advertise ( efx - > wanted_fc ) ;
mdio_clause45_write ( efx , phy_id , MDIO_MMD_AN ,
MDIO_AN_ADVERTISE , reg ) ;
/* Set up the (extended) next page if necessary */
if ( efx - > phy_op - > set_npage_adv )
efx - > phy_op - > set_npage_adv ( efx , ecmd - > advertising ) ;
2008-12-12 21:50:46 -08:00
2009-01-29 17:59:37 +00:00
/* Enable and restart AN */
2008-12-12 21:50:46 -08:00
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 ) ;
2009-01-29 17:59:37 +00:00
reg | = BMCR_ANENABLE ;
if ( ! ( EFX_WORKAROUND_15195 ( efx ) & &
LOOPBACK_MASK ( efx ) & efx - > phy_op - > loopbacks ) )
reg | = BMCR_ANRESTART ;
2008-12-12 21:50:46 -08:00
if ( xnp )
reg | = 1 < < MDIO_AN_CTRL_XNP_LBN ;
else
reg & = ~ ( 1 < < MDIO_AN_CTRL_XNP_LBN ) ;
mdio_clause45_write ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 , reg ) ;
2009-01-29 17:59:37 +00:00
} else {
/* Disable AN */
mdio_clause45_set_flag ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 ,
__ffs ( BMCR_ANENABLE ) , false ) ;
/* Set the basic control bits */
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_CTRL1 ) ;
reg & = ~ ( BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX |
0x003c ) ;
if ( ecmd - > speed = = SPEED_100 )
reg | = BMCR_SPEED100 ;
if ( ecmd - > duplex )
reg | = BMCR_FULLDPLX ;
mdio_clause45_write ( efx , phy_id , MDIO_MMD_PMAPMD ,
MDIO_MMDREG_CTRL1 , reg ) ;
2008-12-12 21:50:46 -08:00
}
return 0 ;
}
void mdio_clause45_set_pause ( struct efx_nic * efx )
{
int phy_id = efx - > mii . phy_id ;
int reg ;
if ( efx - > phy_op - > mmds & DEV_PRESENT_BIT ( MDIO_MMD_AN ) ) {
/* Set pause capability advertising */
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN ,
MDIO_AN_ADVERTISE ) ;
reg & = ~ ( ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM ) ;
reg | = efx_fc_advertise ( efx - > wanted_fc ) ;
mdio_clause45_write ( efx , phy_id , MDIO_MMD_AN ,
MDIO_AN_ADVERTISE , reg ) ;
/* Restart auto-negotiation */
reg = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 ) ;
if ( reg & BMCR_ANENABLE ) {
reg | = BMCR_ANRESTART ;
mdio_clause45_write ( efx , phy_id , MDIO_MMD_AN ,
MDIO_MMDREG_CTRL1 , reg ) ;
}
}
}
enum efx_fc_type mdio_clause45_get_pause ( struct efx_nic * efx )
{
int phy_id = efx - > mii . phy_id ;
int lpa ;
if ( ! ( efx - > phy_op - > mmds & DEV_PRESENT_BIT ( MDIO_MMD_AN ) ) )
return efx - > wanted_fc ;
lpa = mdio_clause45_read ( efx , phy_id , MDIO_MMD_AN , MDIO_AN_LPA ) ;
return efx_fc_resolve ( efx - > wanted_fc , lpa ) ;
2008-04-27 12:55:59 +01:00
}
2008-12-12 21:48:57 -08:00
void mdio_clause45_set_flag ( struct efx_nic * efx , u8 prt , u8 dev ,
u16 addr , int bit , bool sense )
{
int old_val = mdio_clause45_read ( efx , prt , dev , addr ) ;
int new_val ;
if ( sense )
new_val = old_val | ( 1 < < bit ) ;
else
new_val = old_val & ~ ( 1 < < bit ) ;
if ( old_val ! = new_val )
mdio_clause45_write ( efx , prt , dev , addr , new_val ) ;
}