2008-04-27 15:55:59 +04:00
/****************************************************************************
2008-12-13 08:50:08 +03:00
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2007 - 2008 Solarflare Communications Inc .
2008-04-27 15:55:59 +04: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 .
*/
# include <linux/delay.h>
2009-01-19 08:50:16 +03:00
# include <linux/rtnetlink.h>
2008-04-27 15:55:59 +04:00
# include <linux/seq_file.h>
# include "efx.h"
# include "mdio_10g.h"
# include "falcon.h"
# include "phy.h"
# include "falcon_hwdefs.h"
# include "boards.h"
2008-12-13 09:00:17 +03:00
# include "workarounds.h"
# include "selftest.h"
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
/* We expect these MMDs to be in the package. SFT9001 also has a
* clause 22 extension MMD , but since it doesn ' t have all the generic
* MMD registers it is pointless to include it here .
*/
2009-04-29 12:05:08 +04:00
# define TENXPRESS_REQUIRED_DEVS (MDIO_DEVS_PMAPMD | \
MDIO_DEVS_PCS | \
MDIO_DEVS_PHYXS | \
MDIO_DEVS_AN )
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
# define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
( 1 < < LOOPBACK_PCS ) | \
( 1 < < LOOPBACK_PMAPMD ) | \
( 1 < < LOOPBACK_NETWORK ) )
# define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \
( 1 < < LOOPBACK_PHYXS ) | \
( 1 < < LOOPBACK_PCS ) | \
( 1 < < LOOPBACK_PMAPMD ) | \
( 1 < < LOOPBACK_NETWORK ) )
2008-05-07 16:36:19 +04:00
2008-04-27 15:55:59 +04:00
/* We complain if we fail to see the link partner as 10G capable this many
* times in a row ( must be > 1 as sampling the autoneg . registers is racy )
*/
# define MAX_BAD_LP_TRIES (5)
/* Extended control register */
2008-12-13 09:00:17 +03:00
# define PMA_PMD_XCONTROL_REG 49152
# define PMA_PMD_EXT_GMII_EN_LBN 1
# define PMA_PMD_EXT_GMII_EN_WIDTH 1
# define PMA_PMD_EXT_CLK_OUT_LBN 2
# define PMA_PMD_EXT_CLK_OUT_WIDTH 1
# define PMA_PMD_LNPGA_POWERDOWN_LBN 8 /* SFX7101 only */
# define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
# define PMA_PMD_EXT_CLK312_LBN 8 /* SFT9001 only */
# define PMA_PMD_EXT_CLK312_WIDTH 1
# define PMA_PMD_EXT_LPOWER_LBN 12
# define PMA_PMD_EXT_LPOWER_WIDTH 1
2009-01-29 20:48:10 +03:00
# define PMA_PMD_EXT_ROBUST_LBN 14
# define PMA_PMD_EXT_ROBUST_WIDTH 1
2008-12-13 09:00:17 +03:00
# define PMA_PMD_EXT_SSR_LBN 15
# define PMA_PMD_EXT_SSR_WIDTH 1
2008-04-27 15:55:59 +04:00
/* extended status register */
2008-12-13 09:00:17 +03:00
# define PMA_PMD_XSTATUS_REG 49153
2009-06-10 09:30:05 +04:00
# define PMA_PMD_XSTAT_MDIX_LBN 14
2008-04-27 15:55:59 +04:00
# define PMA_PMD_XSTAT_FLP_LBN (12)
/* LED control register */
2008-12-13 09:00:17 +03:00
# define PMA_PMD_LED_CTRL_REG 49159
2008-04-27 15:55:59 +04:00
# define PMA_PMA_LED_ACTIVITY_LBN (3)
/* LED function override register */
2008-12-13 09:00:17 +03:00
# define PMA_PMD_LED_OVERR_REG 49161
2008-04-27 15:55:59 +04:00
/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/
# define PMA_PMD_LED_LINK_LBN (0)
# define PMA_PMD_LED_SPEED_LBN (2)
# define PMA_PMD_LED_TX_LBN (4)
# define PMA_PMD_LED_RX_LBN (6)
/* Override settings */
# define PMA_PMD_LED_AUTO (0) /* H/W control */
# define PMA_PMD_LED_ON (1)
# define PMA_PMD_LED_OFF (2)
# define PMA_PMD_LED_FLASH (3)
2008-12-13 08:50:46 +03:00
# define PMA_PMD_LED_MASK 3
2008-04-27 15:55:59 +04:00
/* All LEDs under hardware control */
# define PMA_PMD_LED_FULL_AUTO (0)
/* Green and Amber under hardware control, Red off */
# define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
2008-12-13 09:00:17 +03:00
# define PMA_PMD_SPEED_ENABLE_REG 49192
# define PMA_PMD_100TX_ADV_LBN 1
# define PMA_PMD_100TX_ADV_WIDTH 1
# define PMA_PMD_1000T_ADV_LBN 2
# define PMA_PMD_1000T_ADV_WIDTH 1
# define PMA_PMD_10000T_ADV_LBN 3
# define PMA_PMD_10000T_ADV_WIDTH 1
# define PMA_PMD_SPEED_LBN 4
# define PMA_PMD_SPEED_WIDTH 4
2008-12-27 00:48:00 +03:00
/* Cable diagnostics - SFT9001 only */
# define PMA_PMD_CDIAG_CTRL_REG 49213
# define CDIAG_CTRL_IMMED_LBN 15
# define CDIAG_CTRL_BRK_LINK_LBN 12
# define CDIAG_CTRL_IN_PROG_LBN 11
# define CDIAG_CTRL_LEN_UNIT_LBN 10
# define CDIAG_CTRL_LEN_METRES 1
# define PMA_PMD_CDIAG_RES_REG 49174
# define CDIAG_RES_A_LBN 12
# define CDIAG_RES_B_LBN 8
# define CDIAG_RES_C_LBN 4
# define CDIAG_RES_D_LBN 0
# define CDIAG_RES_WIDTH 4
# define CDIAG_RES_OPEN 2
# define CDIAG_RES_OK 1
# define CDIAG_RES_INVALID 0
/* Set of 4 registers for pairs A-D */
# define PMA_PMD_CDIAG_LEN_REG 49175
2008-12-13 09:00:17 +03:00
/* Serdes control registers - SFT9001 only */
# define PMA_PMD_CSERDES_CTRL_REG 64258
/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */
# define PMA_PMD_CSERDES_DEFAULT 0x000f
/* Misc register defines - SFX7101 only */
# define PCS_CLOCK_CTRL_REG 55297
2008-04-27 15:55:59 +04:00
# define PLL312_RST_N_LBN 2
2008-12-13 09:00:17 +03:00
# define PCS_SOFT_RST2_REG 55302
2008-04-27 15:55:59 +04:00
# define SERDES_RST_N_LBN 13
# define XGXS_RST_N_LBN 12
2008-12-13 09:00:17 +03:00
# define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */
2008-04-27 15:55:59 +04:00
# define CLK312_EN_LBN 3
2008-05-07 16:36:19 +04:00
/* PHYXS registers */
2008-12-13 09:00:17 +03:00
# define PHYXS_XCONTROL_REG 49152
# define PHYXS_RESET_LBN 15
# define PHYXS_RESET_WIDTH 1
2008-05-07 16:36:19 +04:00
# define PHYXS_TEST1 (49162)
# define LOOPBACK_NEAR_LBN (8)
# define LOOPBACK_NEAR_WIDTH (1)
2008-04-27 15:55:59 +04:00
/* Boot status register */
2009-02-27 16:06:45 +03:00
# define PCS_BOOT_STATUS_REG 53248
# define PCS_BOOT_FATAL_ERROR_LBN 0
# define PCS_BOOT_PROGRESS_LBN 1
# define PCS_BOOT_PROGRESS_WIDTH 2
# define PCS_BOOT_PROGRESS_INIT 0
# define PCS_BOOT_PROGRESS_WAIT_MDIO 1
# define PCS_BOOT_PROGRESS_CHECKSUM 2
# define PCS_BOOT_PROGRESS_JUMP 3
# define PCS_BOOT_DOWNLOAD_WAIT_LBN 3
# define PCS_BOOT_CODE_STARTED_LBN 4
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
/* 100M/1G PHY registers */
# define GPHY_XCONTROL_REG 49152
# define GPHY_ISOLATE_LBN 10
# define GPHY_ISOLATE_WIDTH 1
# define GPHY_DUPLEX_LBN 8
# define GPHY_DUPLEX_WIDTH 1
# define GPHY_LOOPBACK_NEAR_LBN 14
# define GPHY_LOOPBACK_NEAR_WIDTH 1
# define C22EXT_STATUS_REG 49153
# define C22EXT_STATUS_LINK_LBN 2
# define C22EXT_STATUS_LINK_WIDTH 1
2009-01-29 20:59:37 +03:00
# define C22EXT_MSTSLV_CTRL 49161
# define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8
# define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9
# define C22EXT_MSTSLV_STATUS 49162
# define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10
# define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11
2008-12-13 09:00:17 +03:00
2008-04-27 15:55:59 +04:00
/* Time to wait between powering down the LNPGA and turning off the power
* rails */
# define LNPGA_PDOWN_WAIT (HZ / 5)
struct tenxpress_phy_data {
2008-05-07 16:36:19 +04:00
enum efx_loopback_mode loopback_mode ;
2008-09-01 15:48:17 +04:00
enum efx_phy_mode phy_mode ;
2008-04-27 15:55:59 +04:00
int bad_lp_tries ;
} ;
2008-12-13 09:00:17 +03:00
static ssize_t show_phy_short_reach ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct efx_nic * efx = pci_get_drvdata ( to_pci_dev ( dev ) ) ;
int reg ;
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD , MDIO_PMA_10GBT_TXPWR ) ;
return sprintf ( buf , " %d \n " , ! ! ( reg & MDIO_PMA_10GBT_TXPWR_SHORT ) ) ;
2008-12-13 09:00:17 +03:00
}
static ssize_t set_phy_short_reach ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct efx_nic * efx = pci_get_drvdata ( to_pci_dev ( dev ) ) ;
rtnl_lock ( ) ;
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD , MDIO_PMA_10GBT_TXPWR ,
MDIO_PMA_10GBT_TXPWR_SHORT ,
count ! = 0 & & * buf ! = ' 0 ' ) ;
2008-12-13 09:00:17 +03:00
efx_reconfigure_port ( efx ) ;
rtnl_unlock ( ) ;
return count ;
}
static DEVICE_ATTR ( phy_short_reach , 0644 , show_phy_short_reach ,
set_phy_short_reach ) ;
2009-02-27 16:06:45 +03:00
int sft9001_wait_boot ( struct efx_nic * efx )
2008-04-27 15:55:59 +04:00
{
2009-02-27 16:06:45 +03:00
unsigned long timeout = jiffies + HZ + 1 ;
2008-04-27 15:55:59 +04:00
int boot_stat ;
2009-02-27 16:06:45 +03:00
for ( ; ; ) {
2009-04-29 12:05:08 +04:00
boot_stat = efx_mdio_read ( efx , MDIO_MMD_PCS ,
PCS_BOOT_STATUS_REG ) ;
2009-02-27 16:06:45 +03:00
if ( boot_stat > = 0 ) {
EFX_LOG ( efx , " PHY boot status = %#x \n " , boot_stat ) ;
switch ( boot_stat &
( ( 1 < < PCS_BOOT_FATAL_ERROR_LBN ) |
( 3 < < PCS_BOOT_PROGRESS_LBN ) |
( 1 < < PCS_BOOT_DOWNLOAD_WAIT_LBN ) |
( 1 < < PCS_BOOT_CODE_STARTED_LBN ) ) ) {
case ( ( 1 < < PCS_BOOT_FATAL_ERROR_LBN ) |
( PCS_BOOT_PROGRESS_CHECKSUM < <
PCS_BOOT_PROGRESS_LBN ) ) :
case ( ( 1 < < PCS_BOOT_FATAL_ERROR_LBN ) |
( PCS_BOOT_PROGRESS_INIT < <
PCS_BOOT_PROGRESS_LBN ) |
( 1 < < PCS_BOOT_DOWNLOAD_WAIT_LBN ) ) :
return - EINVAL ;
case ( ( PCS_BOOT_PROGRESS_WAIT_MDIO < <
PCS_BOOT_PROGRESS_LBN ) |
( 1 < < PCS_BOOT_DOWNLOAD_WAIT_LBN ) ) :
return ( efx - > phy_mode & PHY_MODE_SPECIAL ) ?
0 : - EIO ;
case ( ( PCS_BOOT_PROGRESS_JUMP < <
PCS_BOOT_PROGRESS_LBN ) |
( 1 < < PCS_BOOT_CODE_STARTED_LBN ) ) :
case ( ( PCS_BOOT_PROGRESS_JUMP < <
PCS_BOOT_PROGRESS_LBN ) |
( 1 < < PCS_BOOT_DOWNLOAD_WAIT_LBN ) |
( 1 < < PCS_BOOT_CODE_STARTED_LBN ) ) :
return ( efx - > phy_mode & PHY_MODE_SPECIAL ) ?
- EIO : 0 ;
default :
if ( boot_stat & ( 1 < < PCS_BOOT_FATAL_ERROR_LBN ) )
return - EIO ;
break ;
}
}
if ( time_after_eq ( jiffies , timeout ) )
return - ETIMEDOUT ;
msleep ( 50 ) ;
2008-04-27 15:55:59 +04:00
}
}
static int tenxpress_init ( struct efx_nic * efx )
{
2008-12-13 09:00:17 +03:00
int reg ;
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFX7101 ) {
/* Enable 312.5 MHz clock */
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PCS , PCS_TEST_SELECT_REG ,
1 < < CLK312_EN_LBN ) ;
2008-12-13 09:00:17 +03:00
} else {
/* Enable 312.5 MHz clock and GMII */
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG ) ;
2008-12-13 09:00:17 +03:00
reg | = ( ( 1 < < PMA_PMD_EXT_GMII_EN_LBN ) |
( 1 < < PMA_PMD_EXT_CLK_OUT_LBN ) |
2009-01-29 20:48:10 +03:00
( 1 < < PMA_PMD_EXT_CLK312_LBN ) |
( 1 < < PMA_PMD_EXT_ROBUST_LBN ) ) ;
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG , reg ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_C22EXT ,
GPHY_XCONTROL_REG , 1 < < GPHY_ISOLATE_LBN ,
false ) ;
2008-12-13 09:00:17 +03:00
}
2008-04-27 15:55:59 +04:00
/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFX7101 ) {
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD , PMA_PMD_LED_CTRL_REG ,
1 < < PMA_PMA_LED_ACTIVITY_LBN , true ) ;
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_LED_OVERR_REG ,
PMA_PMD_LED_DEFAULT ) ;
2008-12-13 09:00:17 +03:00
}
2008-04-27 15:55:59 +04:00
2009-02-27 16:06:45 +03:00
return 0 ;
2008-04-27 15:55:59 +04:00
}
static int tenxpress_phy_init ( struct efx_nic * efx )
{
struct tenxpress_phy_data * phy_data ;
int rc = 0 ;
phy_data = kzalloc ( sizeof ( * phy_data ) , GFP_KERNEL ) ;
2008-05-17 00:20:20 +04:00
if ( ! phy_data )
return - ENOMEM ;
2008-04-27 15:55:59 +04:00
efx - > phy_data = phy_data ;
2008-09-01 15:48:17 +04:00
phy_data - > phy_mode = efx - > phy_mode ;
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
if ( ! ( efx - > phy_mode & PHY_MODE_SPECIAL ) ) {
if ( efx - > phy_type = = PHY_TYPE_SFT9001A ) {
int reg ;
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_XCONTROL_REG ) ;
2008-12-13 09:00:17 +03:00
reg | = ( 1 < < PMA_PMD_EXT_SSR_LBN ) ;
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_XCONTROL_REG , reg ) ;
2008-12-13 09:00:17 +03:00
mdelay ( 200 ) ;
}
2008-04-27 15:55:59 +04:00
2009-04-29 12:05:08 +04:00
rc = efx_mdio_wait_reset_mmds ( efx , TENXPRESS_REQUIRED_DEVS ) ;
2008-12-13 09:00:17 +03:00
if ( rc < 0 )
goto fail ;
2009-04-29 12:05:08 +04:00
rc = efx_mdio_check_mmds ( efx , TENXPRESS_REQUIRED_DEVS , 0 ) ;
2008-12-13 09:00:17 +03:00
if ( rc < 0 )
goto fail ;
}
2008-04-27 15:55:59 +04:00
rc = tenxpress_init ( efx ) ;
if ( rc < 0 )
goto fail ;
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFT9001B ) {
rc = device_create_file ( & efx - > pci_dev - > dev ,
& dev_attr_phy_short_reach ) ;
if ( rc )
goto fail ;
}
2008-04-27 15:55:59 +04:00
schedule_timeout_uninterruptible ( HZ / 5 ) ; /* 200ms */
2008-12-13 09:00:17 +03:00
/* Let XGXS and SerDes out of reset */
2008-04-27 15:55:59 +04:00
falcon_reset_xaui ( efx ) ;
return 0 ;
fail :
kfree ( efx - > phy_data ) ;
efx - > phy_data = NULL ;
return rc ;
}
2008-12-13 09:00:17 +03:00
/* Perform a "special software reset" on the PHY. The caller is
* responsible for saving and restoring the PHY hardware registers
* properly , and masking / unmasking LASI */
2008-05-07 16:36:19 +04:00
static int tenxpress_special_reset ( struct efx_nic * efx )
{
int rc , reg ;
2008-09-01 15:49:25 +04:00
/* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so
* a special software reset can glitch the XGMAC sufficiently for stats
2009-01-29 21:00:07 +03:00
* requests to fail . */
efx_stats_disable ( efx ) ;
2008-05-07 16:36:19 +04:00
/* Initiate reset */
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG ) ;
2008-05-07 16:36:19 +04:00
reg | = ( 1 < < PMA_PMD_EXT_SSR_LBN ) ;
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG , reg ) ;
2008-05-07 16:36:19 +04:00
2008-09-01 15:49:25 +04:00
mdelay ( 200 ) ;
2008-05-07 16:36:19 +04:00
/* Wait for the blocks to come out of reset */
2009-04-29 12:05:08 +04:00
rc = efx_mdio_wait_reset_mmds ( efx , TENXPRESS_REQUIRED_DEVS ) ;
2008-05-07 16:36:19 +04:00
if ( rc < 0 )
2009-01-29 21:00:07 +03:00
goto out ;
2008-05-07 16:36:19 +04:00
/* Try and reconfigure the device */
rc = tenxpress_init ( efx ) ;
if ( rc < 0 )
2009-01-29 21:00:07 +03:00
goto out ;
2008-05-07 16:36:19 +04:00
2008-12-13 09:00:17 +03:00
/* Wait for the XGXS state machine to churn */
mdelay ( 10 ) ;
2009-01-29 21:00:07 +03:00
out :
efx_stats_enable ( efx ) ;
2008-09-01 15:49:25 +04:00
return rc ;
2008-05-07 16:36:19 +04:00
}
2008-12-13 09:00:17 +03:00
static void sfx7101_check_bad_lp ( struct efx_nic * efx , bool link_ok )
2008-04-27 15:55:59 +04:00
{
struct tenxpress_phy_data * pd = efx - > phy_data ;
2008-12-13 08:50:46 +03:00
bool bad_lp ;
2008-04-27 15:55:59 +04:00
int reg ;
2008-12-13 08:50:46 +03:00
if ( link_ok ) {
bad_lp = false ;
} else {
/* Check that AN has started but not completed. */
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_AN , MDIO_STAT1 ) ;
if ( ! ( reg & MDIO_AN_STAT1_LPABLE ) )
2008-12-13 08:50:46 +03:00
return ; /* LP status is unknown */
2009-04-29 12:05:08 +04:00
bad_lp = ! ( reg & MDIO_AN_STAT1_COMPLETE ) ;
2008-12-13 08:50:46 +03:00
if ( bad_lp )
pd - > bad_lp_tries + + ;
}
2008-04-27 15:55:59 +04:00
/* Nothing to do if all is well and was previously so. */
2008-12-13 08:50:46 +03:00
if ( ! pd - > bad_lp_tries )
2008-04-27 15:55:59 +04:00
return ;
2008-12-13 08:50:46 +03:00
/* Use the RX (red) LED as an error indicator once we've seen AN
* failure several times in a row , and also log a message . */
if ( ! bad_lp | | pd - > bad_lp_tries = = MAX_BAD_LP_TRIES ) {
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_LED_OVERR_REG ) ;
2008-12-13 08:50:46 +03:00
reg & = ~ ( PMA_PMD_LED_MASK < < PMA_PMD_LED_RX_LBN ) ;
if ( ! bad_lp ) {
reg | = PMA_PMD_LED_OFF < < PMA_PMD_LED_RX_LBN ;
} else {
reg | = PMA_PMD_LED_FLASH < < PMA_PMD_LED_RX_LBN ;
EFX_ERR ( efx , " appears to be plugged into a port "
" that is not 10GBASE-T capable. The PHY "
" supports 10GBASE-T ONLY, so no link can "
" be established \n " ) ;
}
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_LED_OVERR_REG , reg ) ;
2008-12-13 08:50:46 +03:00
pd - > bad_lp_tries = bad_lp ;
2008-04-27 15:55:59 +04:00
}
}
2008-12-13 09:00:17 +03:00
static bool sfx7101_link_ok ( struct efx_nic * efx )
2008-04-27 15:55:59 +04:00
{
2009-04-29 12:05:08 +04:00
return efx_mdio_links_ok ( efx ,
MDIO_DEVS_PMAPMD |
MDIO_DEVS_PCS |
MDIO_DEVS_PHYXS ) ;
2008-12-13 09:00:17 +03:00
}
static bool sft9001_link_ok ( struct efx_nic * efx , struct ethtool_cmd * ecmd )
{
u32 reg ;
2008-12-27 00:46:12 +03:00
if ( efx_phy_mode_disabled ( efx - > phy_mode ) )
2008-12-13 09:00:17 +03:00
return false ;
2008-12-27 00:46:12 +03:00
else if ( efx - > loopback_mode = = LOOPBACK_GPHY )
return true ;
2008-12-13 09:00:17 +03:00
else if ( efx - > loopback_mode )
2009-04-29 12:05:08 +04:00
return efx_mdio_links_ok ( efx ,
MDIO_DEVS_PMAPMD |
MDIO_DEVS_PHYXS ) ;
2008-12-13 09:00:17 +03:00
/* We must use the same definition of link state as LASI,
* otherwise we can miss a link state transition
*/
if ( ecmd - > speed = = 10000 ) {
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_PCS , MDIO_PCS_10GBRT_STAT1 ) ;
return reg & MDIO_PCS_10GBRT_STAT1_BLKLK ;
2008-12-13 09:00:17 +03:00
} else {
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_C22EXT , C22EXT_STATUS_REG ) ;
2008-12-13 09:00:17 +03:00
return reg & ( 1 < < C22EXT_STATUS_LINK_LBN ) ;
}
2008-04-27 15:55:59 +04:00
}
2008-12-13 09:00:17 +03:00
static void tenxpress_ext_loopback ( struct efx_nic * efx )
2008-05-07 16:36:19 +04:00
{
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_PHYXS , PHYXS_TEST1 ,
1 < < LOOPBACK_NEAR_LBN ,
efx - > loopback_mode = = LOOPBACK_PHYXS ) ;
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type ! = PHY_TYPE_SFX7101 )
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_C22EXT , GPHY_XCONTROL_REG ,
1 < < GPHY_LOOPBACK_NEAR_LBN ,
efx - > loopback_mode = = LOOPBACK_GPHY ) ;
2008-12-13 09:00:17 +03:00
}
static void tenxpress_low_power ( struct efx_nic * efx )
{
if ( efx - > phy_type = = PHY_TYPE_SFX7101 )
2009-04-29 12:05:08 +04:00
efx_mdio_set_mmds_lpower (
2008-12-13 09:00:17 +03:00
efx , ! ! ( efx - > phy_mode & PHY_MODE_LOW_POWER ) ,
TENXPRESS_REQUIRED_DEVS ) ;
2008-05-07 16:36:19 +04:00
else
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag (
efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG ,
1 < < PMA_PMD_EXT_LPOWER_LBN ,
2008-12-13 09:00:17 +03:00
! ! ( efx - > phy_mode & PHY_MODE_LOW_POWER ) ) ;
2008-05-07 16:36:19 +04:00
}
2008-04-27 15:55:59 +04:00
static void tenxpress_phy_reconfigure ( struct efx_nic * efx )
{
2008-05-07 16:36:19 +04:00
struct tenxpress_phy_data * phy_data = efx - > phy_data ;
2008-12-13 09:00:17 +03:00
struct ethtool_cmd ecmd ;
2009-01-29 20:49:09 +03:00
bool phy_mode_change , loop_reset ;
2008-05-07 16:36:19 +04:00
2008-12-13 09:00:17 +03:00
if ( efx - > phy_mode & ( PHY_MODE_OFF | PHY_MODE_SPECIAL ) ) {
2008-09-01 15:48:17 +04:00
phy_data - > phy_mode = efx - > phy_mode ;
2008-04-27 15:55:59 +04:00
return ;
2008-09-01 15:48:17 +04:00
}
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
tenxpress_low_power ( efx ) ;
phy_mode_change = ( efx - > phy_mode = = PHY_MODE_NORMAL & &
phy_data - > phy_mode ! = PHY_MODE_NORMAL ) ;
loop_reset = ( LOOPBACK_OUT_OF ( phy_data , efx , efx - > phy_op - > loopbacks ) | |
LOOPBACK_CHANGED ( phy_data , efx , 1 < < LOOPBACK_GPHY ) ) ;
2009-01-29 20:49:09 +03:00
if ( loop_reset | | phy_mode_change ) {
2008-12-13 09:00:17 +03:00
int rc ;
efx - > phy_op - > get_settings ( efx , & ecmd ) ;
if ( loop_reset | | phy_mode_change ) {
tenxpress_special_reset ( efx ) ;
/* Reset XAUI if we were in 10G, and are staying
* in 10 G . If we ' re moving into and out of 10 G
* then xaui will be reset anyway */
if ( EFX_IS10G ( efx ) )
falcon_reset_xaui ( efx ) ;
}
rc = efx - > phy_op - > set_settings ( efx , & ecmd ) ;
WARN_ON ( rc ) ;
2008-05-07 16:36:19 +04:00
}
2009-04-29 12:05:08 +04:00
efx_mdio_transmit_disable ( efx ) ;
efx_mdio_phy_reconfigure ( efx ) ;
2008-12-13 09:00:17 +03:00
tenxpress_ext_loopback ( efx ) ;
2008-05-07 16:36:19 +04:00
phy_data - > loopback_mode = efx - > loopback_mode ;
2008-09-01 15:48:17 +04:00
phy_data - > phy_mode = efx - > phy_mode ;
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFX7101 ) {
efx - > link_speed = 10000 ;
efx - > link_fd = true ;
efx - > link_up = sfx7101_link_ok ( efx ) ;
} else {
efx - > phy_op - > get_settings ( efx , & ecmd ) ;
efx - > link_speed = ecmd . speed ;
efx - > link_fd = ecmd . duplex = = DUPLEX_FULL ;
efx - > link_up = sft9001_link_ok ( efx , & ecmd ) ;
}
2009-04-29 12:05:08 +04:00
efx - > link_fc = efx_mdio_get_pause ( efx ) ;
2008-04-27 15:55:59 +04:00
}
/* Poll PHY for interrupt */
2008-12-13 08:59:24 +03:00
static void tenxpress_phy_poll ( struct efx_nic * efx )
2008-04-27 15:55:59 +04:00
{
struct tenxpress_phy_data * phy_data = efx - > phy_data ;
2009-02-14 14:41:03 +03:00
bool change = false ;
2008-04-27 15:55:59 +04:00
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFX7101 ) {
2009-02-14 14:41:03 +03:00
bool link_ok = sfx7101_link_ok ( efx ) ;
2008-12-13 09:00:17 +03:00
if ( link_ok ! = efx - > link_up ) {
change = true ;
} else {
2009-04-29 12:05:08 +04:00
unsigned int link_fc = efx_mdio_get_pause ( efx ) ;
2008-12-13 09:00:17 +03:00
if ( link_fc ! = efx - > link_fc )
change = true ;
}
sfx7101_check_bad_lp ( efx , link_ok ) ;
2008-12-27 00:46:12 +03:00
} else if ( efx - > loopback_mode ) {
bool link_ok = sft9001_link_ok ( efx , NULL ) ;
if ( link_ok ! = efx - > link_up )
change = true ;
2008-12-13 08:59:24 +03:00
} else {
2009-04-29 12:05:08 +04:00
int status = efx_mdio_read ( efx , MDIO_MMD_PMAPMD ,
2009-05-15 10:06:16 +04:00
MDIO_PMA_LASI_STAT ) ;
if ( status & MDIO_PMA_LASI_LSALARM )
2008-12-13 08:59:24 +03:00
change = true ;
}
2008-04-27 15:55:59 +04:00
2008-12-13 08:59:24 +03:00
if ( change )
2008-12-13 08:50:08 +03:00
falcon_sim_phy_event ( efx ) ;
2008-04-27 15:55:59 +04:00
2008-09-01 15:48:17 +04:00
if ( phy_data - > phy_mode ! = PHY_MODE_NORMAL )
2008-12-13 08:59:24 +03:00
return ;
2008-04-27 15:55:59 +04:00
}
static void tenxpress_phy_fini ( struct efx_nic * efx )
{
int reg ;
2009-01-11 11:18:13 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFT9001B )
2008-12-13 09:00:17 +03:00
device_remove_file ( & efx - > pci_dev - > dev ,
& dev_attr_phy_short_reach ) ;
2009-01-11 11:18:13 +03:00
if ( efx - > phy_type = = PHY_TYPE_SFX7101 ) {
2008-12-13 09:00:17 +03:00
/* Power down the LNPGA */
reg = ( 1 < < PMA_PMD_LNPGA_POWERDOWN_LBN ) ;
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_XCONTROL_REG , reg ) ;
2008-12-13 09:00:17 +03:00
/* Waiting here ensures that the board fini, which can turn
* off the power to the PHY , won ' t get run until the LNPGA
* powerdown has been given long enough to complete . */
schedule_timeout_uninterruptible ( LNPGA_PDOWN_WAIT ) ; /* 200 ms */
}
2008-04-27 15:55:59 +04:00
kfree ( efx - > phy_data ) ;
efx - > phy_data = NULL ;
}
/* Set the RX and TX LEDs and Link LED flashing. The other LEDs
* ( which probably aren ' t wired anyway ) are left in AUTO mode */
2008-09-01 15:46:50 +04:00
void tenxpress_phy_blink ( struct efx_nic * efx , bool blink )
2008-04-27 15:55:59 +04:00
{
int reg ;
if ( blink )
reg = ( PMA_PMD_LED_FLASH < < PMA_PMD_LED_TX_LBN ) |
( PMA_PMD_LED_FLASH < < PMA_PMD_LED_RX_LBN ) |
( PMA_PMD_LED_FLASH < < PMA_PMD_LED_LINK_LBN ) ;
else
reg = PMA_PMD_LED_DEFAULT ;
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_LED_OVERR_REG , reg ) ;
2008-04-27 15:55:59 +04:00
}
2008-12-27 00:48:00 +03:00
static const char * const sfx7101_test_names [ ] = {
2008-12-27 00:47:25 +03:00
" bist "
} ;
static int
2008-12-27 00:48:00 +03:00
sfx7101_run_tests ( struct efx_nic * efx , int * results , unsigned flags )
2008-09-01 15:49:02 +04:00
{
2008-12-27 00:47:25 +03:00
int rc ;
if ( ! ( flags & ETH_TEST_FL_OFFLINE ) )
return 0 ;
2008-09-01 15:49:02 +04:00
/* BIST is automatically run after a special software reset */
2008-12-27 00:47:25 +03:00
rc = tenxpress_special_reset ( efx ) ;
results [ 0 ] = rc ? - 1 : 1 ;
return rc ;
2008-09-01 15:49:02 +04:00
}
2008-12-27 00:48:00 +03:00
static const char * const sft9001_test_names [ ] = {
" bist " ,
" cable.pairA.status " ,
" cable.pairB.status " ,
" cable.pairC.status " ,
" cable.pairD.status " ,
" cable.pairA.length " ,
" cable.pairB.length " ,
" cable.pairC.length " ,
" cable.pairD.length " ,
} ;
static int sft9001_run_tests ( struct efx_nic * efx , int * results , unsigned flags )
{
struct ethtool_cmd ecmd ;
2009-02-27 16:04:07 +03:00
int rc = 0 , rc2 , i , ctrl_reg , res_reg ;
2008-12-27 00:48:00 +03:00
2009-02-27 16:04:07 +03:00
if ( flags & ETH_TEST_FL_OFFLINE )
efx - > phy_op - > get_settings ( efx , & ecmd ) ;
2008-12-27 00:48:00 +03:00
/* Initialise cable diagnostic results to unknown failure */
for ( i = 1 ; i < 9 ; + + i )
results [ i ] = - 1 ;
/* Run cable diagnostics; wait up to 5 seconds for them to complete.
* A cable fault is not a self - test failure , but a timeout is . */
2009-02-27 16:04:07 +03:00
ctrl_reg = ( ( 1 < < CDIAG_CTRL_IMMED_LBN ) |
( CDIAG_CTRL_LEN_METRES < < CDIAG_CTRL_LEN_UNIT_LBN ) ) ;
if ( flags & ETH_TEST_FL_OFFLINE ) {
/* Break the link in order to run full diagnostics. We
* must reset the PHY to resume normal service . */
ctrl_reg | = ( 1 < < CDIAG_CTRL_BRK_LINK_LBN ) ;
}
2009-04-29 12:05:08 +04:00
efx_mdio_write ( efx , MDIO_MMD_PMAPMD , PMA_PMD_CDIAG_CTRL_REG ,
ctrl_reg ) ;
2008-12-27 00:48:00 +03:00
i = 0 ;
2009-04-29 12:05:08 +04:00
while ( efx_mdio_read ( efx , MDIO_MMD_PMAPMD , PMA_PMD_CDIAG_CTRL_REG ) &
2008-12-27 00:48:00 +03:00
( 1 < < CDIAG_CTRL_IN_PROG_LBN ) ) {
if ( + + i = = 50 ) {
rc = - ETIMEDOUT ;
2009-02-27 16:04:07 +03:00
goto out ;
2008-12-27 00:48:00 +03:00
}
msleep ( 100 ) ;
}
2009-04-29 12:05:08 +04:00
res_reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD , PMA_PMD_CDIAG_RES_REG ) ;
2008-12-27 00:48:00 +03:00
for ( i = 0 ; i < 4 ; i + + ) {
int pair_res =
( res_reg > > ( CDIAG_RES_A_LBN - i * CDIAG_RES_WIDTH ) )
& ( ( 1 < < CDIAG_RES_WIDTH ) - 1 ) ;
2009-04-29 12:05:08 +04:00
int len_reg = efx_mdio_read ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_CDIAG_LEN_REG + i ) ;
2008-12-27 00:48:00 +03:00
if ( pair_res = = CDIAG_RES_OK )
results [ 1 + i ] = 1 ;
else if ( pair_res = = CDIAG_RES_INVALID )
results [ 1 + i ] = - 1 ;
else
results [ 1 + i ] = - pair_res ;
if ( pair_res ! = CDIAG_RES_INVALID & &
pair_res ! = CDIAG_RES_OPEN & &
len_reg ! = 0xffff )
results [ 5 + i ] = len_reg ;
}
2009-02-27 16:04:07 +03:00
out :
if ( flags & ETH_TEST_FL_OFFLINE ) {
/* Reset, running the BIST and then resuming normal service. */
rc2 = tenxpress_special_reset ( efx ) ;
results [ 0 ] = rc2 ? - 1 : 1 ;
if ( ! rc )
rc = rc2 ;
rc2 = efx - > phy_op - > set_settings ( efx , & ecmd ) ;
if ( ! rc )
rc = rc2 ;
}
2008-12-27 00:48:00 +03:00
return rc ;
}
2009-01-29 20:59:37 +03:00
static void
tenxpress_get_settings ( struct efx_nic * efx , struct ethtool_cmd * ecmd )
2008-12-13 08:50:46 +03:00
{
2009-01-29 20:59:37 +03:00
u32 adv = 0 , lpa = 0 ;
2008-12-13 08:50:46 +03:00
int reg ;
2008-12-13 09:00:17 +03:00
if ( efx - > phy_type ! = PHY_TYPE_SFX7101 ) {
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_C22EXT , C22EXT_MSTSLV_CTRL ) ;
2009-01-29 20:59:37 +03:00
if ( reg & ( 1 < < C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN ) )
adv | = ADVERTISED_1000baseT_Full ;
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_C22EXT , C22EXT_MSTSLV_STATUS ) ;
2009-01-29 20:59:37 +03:00
if ( reg & ( 1 < < C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN ) )
2008-12-13 09:00:17 +03:00
lpa | = ADVERTISED_1000baseT_Half ;
2009-01-29 20:59:37 +03:00
if ( reg & ( 1 < < C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN ) )
2008-12-13 09:00:17 +03:00
lpa | = ADVERTISED_1000baseT_Full ;
}
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_AN , MDIO_AN_10GBT_CTRL ) ;
if ( reg & MDIO_AN_10GBT_CTRL_ADV10G )
2009-01-29 20:59:37 +03:00
adv | = ADVERTISED_10000baseT_Full ;
2009-04-29 12:05:08 +04:00
reg = efx_mdio_read ( efx , MDIO_MMD_AN , MDIO_AN_10GBT_STAT ) ;
if ( reg & MDIO_AN_10GBT_STAT_LP10G )
2008-12-13 08:50:46 +03:00
lpa | = ADVERTISED_10000baseT_Full ;
2009-04-29 12:05:08 +04:00
mdio45_ethtool_gset_npage ( & efx - > mdio , ecmd , adv , lpa ) ;
2008-12-13 09:00:17 +03:00
2009-06-10 09:30:05 +04:00
if ( efx - > phy_type ! = PHY_TYPE_SFX7101 ) {
2009-01-29 20:59:37 +03:00
ecmd - > supported | = ( SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full ) ;
2009-06-10 09:30:05 +04:00
if ( ecmd - > speed ! = SPEED_10000 ) {
ecmd - > eth_tp_mdix =
( efx_mdio_read ( efx , MDIO_MMD_PMAPMD ,
PMA_PMD_XSTATUS_REG ) &
( 1 < < PMA_PMD_XSTAT_MDIX_LBN ) )
? ETH_TP_MDI_X : ETH_TP_MDI ;
}
}
2009-01-29 20:49:09 +03:00
/* In loopback, the PHY automatically brings up the correct interface,
* but doesn ' t advertise the correct speed . So override it */
if ( efx - > loopback_mode = = LOOPBACK_GPHY )
ecmd - > speed = SPEED_1000 ;
2009-01-29 20:59:37 +03:00
else if ( LOOPBACK_MASK ( efx ) & efx - > phy_op - > loopbacks )
2009-01-29 20:49:09 +03:00
ecmd - > speed = SPEED_10000 ;
2008-12-13 08:50:46 +03:00
}
2009-01-29 20:59:37 +03:00
static int tenxpress_set_settings ( struct efx_nic * efx , struct ethtool_cmd * ecmd )
2008-12-13 09:00:17 +03:00
{
2009-01-29 20:59:37 +03:00
if ( ! ecmd - > autoneg )
return - EINVAL ;
2008-12-13 09:00:17 +03:00
2009-04-29 12:05:08 +04:00
return efx_mdio_set_settings ( efx , ecmd ) ;
2008-12-13 09:00:17 +03:00
}
2009-01-29 20:59:37 +03:00
static void sfx7101_set_npage_adv ( struct efx_nic * efx , u32 advertising )
2008-12-13 09:00:17 +03:00
{
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_AN , MDIO_AN_10GBT_CTRL ,
MDIO_AN_10GBT_CTRL_ADV10G ,
advertising & ADVERTISED_10000baseT_Full ) ;
2008-12-13 09:00:17 +03:00
}
2009-01-29 20:59:37 +03:00
static void sft9001_set_npage_adv ( struct efx_nic * efx , u32 advertising )
2008-12-13 09:00:17 +03:00
{
2009-04-29 12:05:08 +04:00
efx_mdio_set_flag ( efx , MDIO_MMD_C22EXT , C22EXT_MSTSLV_CTRL ,
1 < < C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN ,
advertising & ADVERTISED_1000baseT_Full ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_AN , MDIO_AN_10GBT_CTRL ,
MDIO_AN_10GBT_CTRL_ADV10G ,
advertising & ADVERTISED_10000baseT_Full ) ;
2008-12-13 09:00:17 +03:00
}
struct efx_phy_operations falcon_sfx7101_phy_ops = {
2008-12-13 08:50:08 +03:00
. macs = EFX_XMAC ,
2008-04-27 15:55:59 +04:00
. init = tenxpress_phy_init ,
. reconfigure = tenxpress_phy_reconfigure ,
2008-12-13 08:59:24 +03:00
. poll = tenxpress_phy_poll ,
2008-04-27 15:55:59 +04:00
. fini = tenxpress_phy_fini ,
2008-12-13 08:59:24 +03:00
. clear_interrupt = efx_port_dummy_op_void ,
2009-01-29 20:59:37 +03:00
. get_settings = tenxpress_get_settings ,
. set_settings = tenxpress_set_settings ,
. set_npage_adv = sfx7101_set_npage_adv ,
2008-12-27 00:48:00 +03:00
. num_tests = ARRAY_SIZE ( sfx7101_test_names ) ,
. test_names = sfx7101_test_names ,
. run_tests = sfx7101_run_tests ,
2008-04-27 15:55:59 +04:00
. mmds = TENXPRESS_REQUIRED_DEVS ,
2008-12-13 09:00:17 +03:00
. loopbacks = SFX7101_LOOPBACKS ,
} ;
struct efx_phy_operations falcon_sft9001_phy_ops = {
. macs = EFX_GMAC | EFX_XMAC ,
. init = tenxpress_phy_init ,
. reconfigure = tenxpress_phy_reconfigure ,
. poll = tenxpress_phy_poll ,
. fini = tenxpress_phy_fini ,
. clear_interrupt = efx_port_dummy_op_void ,
2009-01-29 20:59:37 +03:00
. get_settings = tenxpress_get_settings ,
. set_settings = tenxpress_set_settings ,
. set_npage_adv = sft9001_set_npage_adv ,
2008-12-27 00:48:00 +03:00
. num_tests = ARRAY_SIZE ( sft9001_test_names ) ,
. test_names = sft9001_test_names ,
. run_tests = sft9001_run_tests ,
2008-12-13 09:00:17 +03:00
. mmds = TENXPRESS_REQUIRED_DEVS ,
. loopbacks = SFT9001_LOOPBACKS ,
2008-04-27 15:55:59 +04:00
} ;