2008-04-27 12:55:59 +01:00
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
2011-02-25 00:01:34 +00:00
* Copyright 2006 - 2011 Solarflare Communications Inc .
2008-04-27 12:55:59 +01:00
*
* 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"
2009-01-29 17:49:09 +00:00
# include "workarounds.h"
2008-04-27 12:55:59 +01:00
2009-04-29 08:05:08 +00:00
unsigned efx_mdio_id_oui ( u32 id )
2009-02-27 13:07:15 +00:00
{
unsigned oui = 0 ;
int i ;
/* The bits of the OUI are designated a..x, with a=0 and b variable.
* In the id register c is the MSB but the OUI is conventionally
* written as bytes h . . a , p . . i , x . . q . Reorder the bits accordingly . */
for ( i = 0 ; i < 22 ; + + i )
if ( id & ( 1 < < ( i + 10 ) ) )
oui | = 1 < < ( i ^ 7 ) ;
return oui ;
}
2009-04-29 08:05:08 +00:00
int efx_mdio_reset_mmd ( struct efx_nic * port , int mmd ,
2008-04-27 12:55:59 +01:00
int spins , int spintime )
{
u32 ctrl ;
/* Catch callers passing values in the wrong units (or just silly) */
EFX_BUG_ON_PARANOID ( spins * spintime > = 5000 ) ;
2009-04-29 08:05:08 +00:00
efx_mdio_write ( port , mmd , MDIO_CTRL1 , MDIO_CTRL1_RESET ) ;
2008-04-27 12:55:59 +01:00
/* Wait for the reset bit to clear. */
do {
msleep ( spintime ) ;
2009-04-29 08:05:08 +00:00
ctrl = efx_mdio_read ( port , mmd , MDIO_CTRL1 ) ;
2008-04-27 12:55:59 +01:00
spins - - ;
2009-04-29 08:05:08 +00:00
} while ( spins & & ( ctrl & MDIO_CTRL1_RESET ) ) ;
2008-04-27 12:55:59 +01:00
return spins ? spins : - ETIMEDOUT ;
}
2011-02-24 23:59:15 +00:00
static int efx_mdio_check_mmd ( struct efx_nic * efx , int mmd )
2008-04-27 12:55:59 +01:00
{
int status ;
2008-12-12 21:50:46 -08:00
if ( mmd ! = MDIO_MMD_AN ) {
/* Read MMD STATUS2 to check it is responding. */
2009-04-29 08:05:08 +00:00
status = efx_mdio_read ( efx , mmd , MDIO_STAT2 ) ;
if ( ( status & MDIO_STAT2_DEVPRST ) ! = MDIO_STAT2_DEVPRST_VAL ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" PHY MMD %d not responding. \n " , mmd ) ;
2008-12-12 21:50:46 -08:00
return - EIO ;
}
2008-04-27 12:55:59 +01:00
}
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
2009-04-29 08:05:08 +00:00
int efx_mdio_wait_reset_mmds ( struct efx_nic * efx , unsigned int mmd_mask )
2008-04-27 12:55:59 +01:00
{
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 ) {
2009-04-29 08:05:08 +00:00
stat = efx_mdio_read ( efx , mmd , MDIO_CTRL1 ) ;
2008-04-27 12:55:59 +01:00
if ( stat < 0 ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" failed to read status of "
" MMD %d \n " , mmd ) ;
2008-04-27 12:55:59 +01:00
return - EIO ;
}
2009-04-29 08:05:08 +00:00
if ( stat & MDIO_CTRL1_RESET )
2008-04-27 12:55:59 +01:00
in_reset | = ( 1 < < mmd ) ;
}
mask = mask > > 1 ;
mmd + + ;
}
if ( ! in_reset )
break ;
tries - - ;
msleep ( spintime ) ;
}
if ( in_reset ! = 0 ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" not all MMDs came out of reset in time. "
" MMDs still in reset: %x \n " , in_reset ) ;
2008-04-27 12:55:59 +01:00
rc = - ETIMEDOUT ;
}
return rc ;
}
2011-02-24 23:59:15 +00:00
int efx_mdio_check_mmds ( struct efx_nic * efx , unsigned int mmd_mask )
2008-04-27 12:55:59 +01:00
{
2009-04-29 08:05:08 +00:00
int mmd = 0 , probe_mmd , devs1 , devs2 ;
2008-12-12 21:44:14 -08:00
u32 devices ;
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 . */
2009-04-29 08:05:08 +00:00
probe_mmd = ( mmd_mask & MDIO_DEVS_PHYXS ) ? MDIO_MMD_PHYXS :
2008-04-27 12:55:59 +01:00
__ffs ( mmd_mask ) ;
/* Check all the expected MMDs are present */
2009-04-29 08:05:08 +00:00
devs1 = efx_mdio_read ( efx , probe_mmd , MDIO_DEVS1 ) ;
devs2 = efx_mdio_read ( efx , probe_mmd , MDIO_DEVS2 ) ;
if ( devs1 < 0 | | devs2 < 0 ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" failed to read devices present \n " ) ;
2008-04-27 12:55:59 +01:00
return - EIO ;
}
2009-04-29 08:05:08 +00:00
devices = devs1 | ( devs2 < < 16 ) ;
2008-04-27 12:55:59 +01:00
if ( ( devices & mmd_mask ) ! = mmd_mask ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" required MMDs not present: got %x, wanted %x \n " ,
devices , mmd_mask ) ;
2008-04-27 12:55:59 +01:00
return - ENODEV ;
}
2010-06-23 11:30:07 +00:00
netif_vdbg ( efx , hw , efx - > net_dev , " Devices present: %x \n " , devices ) ;
2008-04-27 12:55:59 +01:00
/* Check all required MMDs are responding and happy. */
while ( mmd_mask ) {
2011-02-24 23:59:15 +00:00
if ( ( mmd_mask & 1 ) & & efx_mdio_check_mmd ( efx , mmd ) )
return - EIO ;
2008-04-27 12:55:59 +01:00
mmd_mask = mmd_mask > > 1 ;
mmd + + ;
}
return 0 ;
}
2009-04-29 08:05:08 +00:00
bool efx_mdio_links_ok ( struct efx_nic * efx , unsigned int mmd_mask )
2008-04-27 12:55:59 +01:00
{
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 ;
2009-11-29 15:08:41 +00:00
else if ( LOOPBACK_MASK ( efx ) & LOOPBACKS_WS )
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 )
2009-04-29 08:05:08 +00:00
mmd_mask & = ~ ( MDIO_DEVS_PHYXS |
MDIO_DEVS_PCS |
MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN ) ;
2009-01-29 17:51:15 +00:00
else if ( efx - > loopback_mode = = LOOPBACK_PCS )
2009-04-29 08:05:08 +00:00
mmd_mask & = ~ ( MDIO_DEVS_PCS |
MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN ) ;
2008-05-07 13:36:19 +01:00
else if ( efx - > loopback_mode = = LOOPBACK_PMAPMD )
2009-04-29 08:05:08 +00:00
mmd_mask & = ~ ( MDIO_DEVS_PMAPMD |
MDIO_DEVS_AN ) ;
2009-01-29 17:51:15 +00:00
2009-04-29 08:05:08 +00:00
return mdio45_links_ok ( & efx - > mdio , mmd_mask ) ;
2008-04-27 12:55:59 +01:00
}
2009-04-29 08:05:08 +00:00
void efx_mdio_transmit_disable ( struct efx_nic * efx )
2008-05-07 13:36:19 +01:00
{
2009-04-29 08:05:08 +00:00
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD ,
MDIO_PMA_TXDIS , MDIO_PMD_TXDIS_GLOBAL ,
efx - > phy_mode & PHY_MODE_TX_DISABLED ) ;
2008-05-07 13:36:19 +01:00
}
2009-04-29 08:05:08 +00:00
void efx_mdio_phy_reconfigure ( struct efx_nic * efx )
2008-05-07 13:36:19 +01:00
{
2009-04-29 08:05:08 +00:00
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD ,
MDIO_CTRL1 , MDIO_PMA_CTRL1_LOOPBACK ,
efx - > loopback_mode = = LOOPBACK_PMAPMD ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_PCS ,
MDIO_CTRL1 , MDIO_PCS_CTRL1_LOOPBACK ,
efx - > loopback_mode = = LOOPBACK_PCS ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_PHYXS ,
MDIO_CTRL1 , MDIO_PHYXS_CTRL1_LOOPBACK ,
2009-11-29 15:08:41 +00:00
efx - > loopback_mode = = LOOPBACK_PHYXS_WS ) ;
2008-05-07 13:36:19 +01:00
}
2009-04-29 08:05:08 +00:00
static void efx_mdio_set_mmd_lpower ( struct efx_nic * efx ,
int lpower , int mmd )
2008-11-04 20:34:56 +00:00
{
2009-04-29 08:05:08 +00:00
int stat = efx_mdio_read ( efx , mmd , MDIO_STAT1 ) ;
2008-11-04 20:34:56 +00:00
2010-06-23 11:30:07 +00:00
netif_vdbg ( efx , drv , efx - > net_dev , " Setting low power mode for MMD %d to %d \n " ,
2008-11-04 20:34:56 +00:00
mmd , lpower ) ;
2009-04-29 08:05:08 +00:00
if ( stat & MDIO_STAT1_LPOWERABLE ) {
efx_mdio_set_flag ( efx , mmd , MDIO_CTRL1 ,
MDIO_CTRL1_LPOWER , lpower ) ;
2008-11-04 20:34:56 +00:00
}
}
2009-04-29 08:05:08 +00:00
void efx_mdio_set_mmds_lpower ( struct efx_nic * efx ,
int low_power , unsigned int mmd_mask )
2008-11-04 20:34:56 +00:00
{
int mmd = 0 ;
2009-04-29 08:05:08 +00:00
mmd_mask & = ~ MDIO_DEVS_AN ;
2008-11-04 20:34:56 +00:00
while ( mmd_mask ) {
if ( mmd_mask & 1 )
2009-04-29 08:05:08 +00:00
efx_mdio_set_mmd_lpower ( efx , low_power , mmd ) ;
2008-11-04 20:34:56 +00:00
mmd_mask = ( mmd_mask > > 1 ) ;
mmd + + ;
}
}
2008-12-12 21:50:46 -08:00
/**
2009-04-29 08:05:08 +00:00
* efx_mdio_set_settings - Set ( some of ) the PHY settings over MDIO .
2008-04-27 12:55:59 +01:00
* @ efx : Efx NIC
2012-01-05 17:19:45 +00:00
* @ ecmd : New settings
2008-04-27 12:55:59 +01:00
*/
2009-04-29 08:05:08 +00:00
int efx_mdio_set_settings ( struct efx_nic * efx , struct ethtool_cmd * ecmd )
2008-04-27 12:55:59 +01:00
{
2011-04-27 18:32:38 +00:00
struct ethtool_cmd prev = { . cmd = ETHTOOL_GSET } ;
2008-12-12 21:50:46 -08:00
efx - > phy_op - > get_settings ( efx , & prev ) ;
if ( ecmd - > advertising = = prev . advertising & &
2011-04-27 18:32:38 +00:00
ethtool_cmd_speed ( ecmd ) = = ethtool_cmd_speed ( & prev ) & &
2008-12-12 21:50:46 -08:00
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 */
2009-10-23 08:33:27 +00:00
if ( ! ecmd - > autoneg | |
( ecmd - > advertising | SUPPORTED_Autoneg ) & ~ prev . supported )
2008-12-12 21:50:46 -08:00
return - EINVAL ;
2009-11-29 03:42:41 +00:00
efx_link_set_advertising ( efx , ecmd - > advertising | ADVERTISED_Autoneg ) ;
efx_mdio_an_reconfigure ( efx ) ;
return 0 ;
}
/**
* efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation
* @ efx : Efx NIC
*/
void efx_mdio_an_reconfigure ( struct efx_nic * efx )
{
int reg ;
WARN_ON ( ! ( efx - > mdio . mmds & MDIO_DEVS_AN ) ) ;
2009-10-23 08:33:27 +00:00
/* Set up the base page */
2010-09-22 10:00:11 +00:00
reg = ADVERTISE_CSMA | ADVERTISE_RESV ;
2009-11-29 03:42:41 +00:00
if ( efx - > link_advertising & ADVERTISED_Pause )
reg | = ADVERTISE_PAUSE_CAP ;
if ( efx - > link_advertising & ADVERTISED_Asym_Pause )
reg | = ADVERTISE_PAUSE_ASYM ;
2009-10-23 08:33:27 +00:00
efx_mdio_write ( efx , MDIO_MMD_AN , MDIO_AN_ADVERTISE , reg ) ;
2010-09-22 10:00:11 +00:00
/* Set up the (extended) next page */
efx - > phy_op - > set_npage_adv ( efx , efx - > link_advertising ) ;
2009-10-23 08:33:27 +00:00
/* Enable and restart AN */
reg = efx_mdio_read ( efx , MDIO_MMD_AN , MDIO_CTRL1 ) ;
2010-09-22 10:00:11 +00:00
reg | = MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART | MDIO_AN_CTRL1_XNP ;
2009-10-23 08:33:27 +00:00
efx_mdio_write ( efx , MDIO_MMD_AN , MDIO_CTRL1 , reg ) ;
2008-12-12 21:50:46 -08:00
}
2011-05-17 17:53:22 -04:00
u8 efx_mdio_get_pause ( struct efx_nic * efx )
2008-12-12 21:50:46 -08:00
{
2009-10-23 08:33:17 +00:00
BUILD_BUG_ON ( EFX_FC_AUTO & ( EFX_FC_RX | EFX_FC_TX ) ) ;
2008-12-12 21:50:46 -08:00
2009-10-23 08:33:17 +00:00
if ( ! ( efx - > wanted_fc & EFX_FC_AUTO ) )
2008-12-12 21:50:46 -08:00
return efx - > wanted_fc ;
2009-10-23 08:33:17 +00:00
WARN_ON ( ! ( efx - > mdio . mmds & MDIO_DEVS_AN ) ) ;
return mii_resolve_flowctrl_fdx (
mii_advertise_flowctrl ( efx - > wanted_fc ) ,
efx_mdio_read ( efx , MDIO_MMD_AN , MDIO_AN_LPA ) ) ;
2008-04-27 12:55:59 +01:00
}
2010-02-03 09:30:50 +00:00
int efx_mdio_test_alive ( struct efx_nic * efx )
{
int rc ;
int devad = __ffs ( efx - > mdio . mmds ) ;
u16 physid1 , physid2 ;
mutex_lock ( & efx - > mac_lock ) ;
physid1 = efx_mdio_read ( efx , devad , MDIO_DEVID1 ) ;
physid2 = efx_mdio_read ( efx , devad , MDIO_DEVID2 ) ;
if ( ( physid1 = = 0x0000 ) | | ( physid1 = = 0xffff ) | |
( physid2 = = 0x0000 ) | | ( physid2 = = 0xffff ) ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , hw , efx - > net_dev ,
" no MDIO PHY present with ID %d \n " , efx - > mdio . prtad ) ;
2010-02-03 09:30:50 +00:00
rc = - EINVAL ;
} else {
2011-02-24 23:59:15 +00:00
rc = efx_mdio_check_mmds ( efx , efx - > mdio . mmds ) ;
2010-02-03 09:30:50 +00:00
}
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
}