2008-04-27 12:55:59 +01:00
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
2009-11-29 15:16:19 +00:00
* Copyright 2006 - 2009 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 .
*/
/*
2009-10-23 08:33:42 +00:00
* Driver for AMCC QT202x SFP + and XFP adapters ; see www . amcc . com for details
2008-04-27 12:55:59 +01:00
*/
# include <linux/timer.h>
# include <linux/delay.h>
# include "efx.h"
# include "mdio_10g.h"
# include "phy.h"
2009-11-29 15:12:08 +00:00
# include "nic.h"
2008-04-27 12:55:59 +01:00
2009-10-23 08:33:42 +00:00
# define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \
MDIO_DEVS_PMAPMD | \
MDIO_DEVS_PHYXS )
2008-04-27 12:55:59 +01:00
2009-10-23 08:33:42 +00:00
# define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \
( 1 < < LOOPBACK_PMAPMD ) | \
2009-11-29 15:08:41 +00:00
( 1 < < LOOPBACK_PHYXS_WS ) )
2008-05-07 13:36:19 +01:00
2008-04-27 12:55:59 +01:00
/****************************************************************************/
/* Quake-specific MDIO registers */
# define MDIO_QUAKE_LED0_REG (0xD006)
2009-02-27 13:07:33 +00:00
/* QT2025C only */
# define PCS_FW_HEARTBEAT_REG 0xd7ee
# define PCS_FW_HEARTB_LBN 0
# define PCS_FW_HEARTB_WIDTH 8
# define PCS_UC8051_STATUS_REG 0xd7fd
# define PCS_UC_STATUS_LBN 0
# define PCS_UC_STATUS_WIDTH 8
# define PCS_UC_STATUS_FW_SAVE 0x20
# define PMA_PMD_FTX_CTRL2_REG 0xc309
# define PMA_PMD_FTX_STATIC_LBN 13
# define PMA_PMD_VEND1_REG 0xc001
# define PMA_PMD_VEND1_LBTXD_LBN 15
# define PCS_VEND1_REG 0xc000
# define PCS_VEND1_LBTXD_LBN 5
2009-10-23 08:33:42 +00:00
void falcon_qt202x_set_led ( struct efx_nic * p , int led , int mode )
2008-04-27 12:55:59 +01:00
{
int addr = MDIO_QUAKE_LED0_REG + led ;
2009-04-29 08:05:08 +00:00
efx_mdio_write ( p , MDIO_MMD_PMAPMD , addr , mode ) ;
2008-04-27 12:55:59 +01:00
}
2009-10-23 08:33:42 +00:00
struct qt202x_phy_data {
2008-09-01 12:48:17 +01:00
enum efx_phy_mode phy_mode ;
2009-12-23 13:47:37 +00:00
bool bug17190_in_bad_state ;
unsigned long bug17190_timer ;
2008-05-07 13:36:19 +01:00
} ;
2009-10-23 08:33:42 +00:00
# define QT2022C2_MAX_RESET_TIME 500
# define QT2022C2_RESET_WAIT 10
2008-04-27 12:55:59 +01:00
2009-12-23 13:47:37 +00:00
# define BUG17190_INTERVAL (2 * HZ)
2009-02-27 13:07:33 +00:00
static int qt2025c_wait_reset ( struct efx_nic * efx )
{
unsigned long timeout = jiffies + 10 * HZ ;
int reg , old_counter = 0 ;
/* Wait for firmware heartbeat to start */
for ( ; ; ) {
int counter ;
2009-04-29 08:05:08 +00:00
reg = efx_mdio_read ( efx , MDIO_MMD_PCS , PCS_FW_HEARTBEAT_REG ) ;
2009-02-27 13:07:33 +00:00
if ( reg < 0 )
return reg ;
counter = ( ( reg > > PCS_FW_HEARTB_LBN ) &
( ( 1 < < PCS_FW_HEARTB_WIDTH ) - 1 ) ) ;
if ( old_counter = = 0 )
old_counter = counter ;
else if ( counter ! = old_counter )
break ;
if ( time_after ( jiffies , timeout ) )
return - ETIMEDOUT ;
msleep ( 10 ) ;
}
/* Wait for firmware status to look good */
for ( ; ; ) {
2009-04-29 08:05:08 +00:00
reg = efx_mdio_read ( efx , MDIO_MMD_PCS , PCS_UC8051_STATUS_REG ) ;
2009-02-27 13:07:33 +00:00
if ( reg < 0 )
return reg ;
if ( ( reg &
( ( 1 < < PCS_UC_STATUS_WIDTH ) - 1 ) < < PCS_UC_STATUS_LBN ) > =
PCS_UC_STATUS_FW_SAVE )
break ;
if ( time_after ( jiffies , timeout ) )
return - ETIMEDOUT ;
msleep ( 100 ) ;
}
return 0 ;
}
2009-12-23 13:47:37 +00:00
static void qt2025c_bug17190_workaround ( struct efx_nic * efx )
{
struct qt202x_phy_data * phy_data = efx - > phy_data ;
/* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD
* layers up , but PCS down ( no block_lock ) . If we notice this state
* persisting for a couple of seconds , we switch PMA / PMD loopback
* briefly on and then off again , which is normally sufficient to
* recover it .
*/
if ( efx - > link_state . up | |
! efx_mdio_links_ok ( efx , MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS ) ) {
phy_data - > bug17190_in_bad_state = false ;
return ;
}
if ( ! phy_data - > bug17190_in_bad_state ) {
phy_data - > bug17190_in_bad_state = true ;
phy_data - > bug17190_timer = jiffies + BUG17190_INTERVAL ;
return ;
}
if ( time_after_eq ( jiffies , phy_data - > bug17190_timer ) ) {
EFX_LOG ( efx , " bashing QT2025C PMA/PMD \n " ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD , MDIO_CTRL1 ,
MDIO_PMA_CTRL1_LOOPBACK , true ) ;
msleep ( 100 ) ;
efx_mdio_set_flag ( efx , MDIO_MMD_PMAPMD , MDIO_CTRL1 ,
MDIO_PMA_CTRL1_LOOPBACK , false ) ;
phy_data - > bug17190_timer = jiffies + BUG17190_INTERVAL ;
}
}
2009-10-23 08:33:42 +00:00
static int qt202x_reset_phy ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
int rc ;
2009-02-27 13:07:33 +00:00
if ( efx - > phy_type = = PHY_TYPE_QT2025C ) {
2009-08-26 08:17:19 +00:00
/* Wait for the reset triggered by falcon_reset_hw()
* to complete */
2009-02-27 13:07:33 +00:00
rc = qt2025c_wait_reset ( efx ) ;
if ( rc < 0 )
goto fail ;
2009-08-26 08:17:19 +00:00
} else {
/* Reset the PHYXS MMD. This is documented as doing
* a complete soft reset . */
rc = efx_mdio_reset_mmd ( efx , MDIO_MMD_PHYXS ,
2009-10-23 08:33:42 +00:00
QT2022C2_MAX_RESET_TIME /
QT2022C2_RESET_WAIT ,
QT2022C2_RESET_WAIT ) ;
2009-08-26 08:17:19 +00:00
if ( rc < 0 )
goto fail ;
2009-02-27 13:07:33 +00:00
}
2008-04-27 12:55:59 +01:00
/* Wait 250ms for the PHY to complete bootup */
msleep ( 250 ) ;
/* Check that all the MMDs we expect are present and responding. We
* expect faults on some if the link is down , but not on the PHY XS */
2009-10-23 08:33:42 +00:00
rc = efx_mdio_check_mmds ( efx , QT202X_REQUIRED_DEVS , MDIO_DEVS_PHYXS ) ;
2008-04-27 12:55:59 +01:00
if ( rc < 0 )
goto fail ;
2009-11-25 16:09:41 +00:00
falcon_board ( efx ) - > type - > init_phy ( efx ) ;
2008-04-27 12:55:59 +01:00
return rc ;
fail :
2009-02-27 13:06:58 +00:00
EFX_ERR ( efx , " PHY reset timed out \n " ) ;
2008-04-27 12:55:59 +01:00
return rc ;
}
2009-11-29 15:08:55 +00:00
static int qt202x_phy_probe ( struct efx_nic * efx )
{
2009-12-23 13:46:36 +00:00
struct qt202x_phy_data * phy_data ;
phy_data = kzalloc ( sizeof ( struct qt202x_phy_data ) , GFP_KERNEL ) ;
if ( ! phy_data )
return - ENOMEM ;
efx - > phy_data = phy_data ;
phy_data - > phy_mode = efx - > phy_mode ;
2009-12-23 13:47:37 +00:00
phy_data - > bug17190_in_bad_state = false ;
phy_data - > bug17190_timer = 0 ;
2009-12-23 13:46:36 +00:00
2009-11-29 15:08:55 +00:00
efx - > mdio . mmds = QT202X_REQUIRED_DEVS ;
efx - > mdio . mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22 ;
efx - > loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS ;
return 0 ;
}
2009-10-23 08:33:42 +00:00
static int qt202x_phy_init ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
2009-11-28 05:34:29 +00:00
u32 devid ;
2008-04-27 12:55:59 +01:00
int rc ;
2009-11-28 05:34:29 +00:00
rc = qt202x_reset_phy ( efx ) ;
if ( rc ) {
EFX_ERR ( efx , " PHY init failed \n " ) ;
return rc ;
}
devid = efx_mdio_read_id ( efx , MDIO_MMD_PHYXS ) ;
2009-02-27 13:07:15 +00:00
EFX_INFO ( efx , " PHY ID reg %x (OUI %06x model %02x revision %x) \n " ,
2009-04-29 08:05:08 +00:00
devid , efx_mdio_id_oui ( devid ) , efx_mdio_id_model ( devid ) ,
efx_mdio_id_rev ( devid ) ) ;
2008-04-27 12:55:59 +01:00
2008-05-07 13:36:19 +01:00
return 0 ;
2008-04-27 12:55:59 +01:00
}
2009-10-23 08:33:42 +00:00
static int qt202x_link_ok ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
2009-10-23 08:33:42 +00:00
return efx_mdio_links_ok ( efx , QT202X_REQUIRED_DEVS ) ;
2008-04-27 12:55:59 +01:00
}
2009-11-28 05:34:05 +00:00
static bool qt202x_phy_poll ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
2009-11-28 05:34:05 +00:00
bool was_up = efx - > link_state . up ;
efx - > link_state . up = qt202x_link_ok ( efx ) ;
efx - > link_state . speed = 10000 ;
efx - > link_state . fd = true ;
efx - > link_state . fc = efx - > wanted_fc ;
2009-12-23 13:47:37 +00:00
if ( efx - > phy_type = = PHY_TYPE_QT2025C )
qt2025c_bug17190_workaround ( efx ) ;
2009-11-28 05:34:05 +00:00
return efx - > link_state . up ! = was_up ;
2008-04-27 12:55:59 +01:00
}
2009-11-29 03:42:41 +00:00
static int qt202x_phy_reconfigure ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
2009-10-23 08:33:42 +00:00
struct qt202x_phy_data * phy_data = efx - > phy_data ;
2008-05-07 13:36:19 +01:00
2009-02-27 13:07:33 +00:00
if ( efx - > phy_type = = PHY_TYPE_QT2025C ) {
/* There are several different register bits which can
* disable TX ( and save power ) on direct - attach cables
* or optical transceivers , varying somewhat between
* firmware versions . Only ' static mode ' appears to
* cover everything . */
2009-04-29 08:05:08 +00:00
mdio_set_flag (
& efx - > mdio , efx - > mdio . prtad , MDIO_MMD_PMAPMD ,
PMA_PMD_FTX_CTRL2_REG , 1 < < PMA_PMD_FTX_STATIC_LBN ,
2009-02-27 13:07:33 +00:00
efx - > phy_mode & PHY_MODE_TX_DISABLED | |
efx - > phy_mode & PHY_MODE_LOW_POWER | |
efx - > loopback_mode = = LOOPBACK_PCS | |
efx - > loopback_mode = = LOOPBACK_PMAPMD ) ;
} else {
/* Reset the PHY when moving from tx off to tx on */
if ( ! ( efx - > phy_mode & PHY_MODE_TX_DISABLED ) & &
( phy_data - > phy_mode & PHY_MODE_TX_DISABLED ) )
2009-10-23 08:33:42 +00:00
qt202x_reset_phy ( efx ) ;
2009-02-27 13:07:33 +00:00
2009-04-29 08:05:08 +00:00
efx_mdio_transmit_disable ( efx ) ;
2009-02-27 13:07:33 +00:00
}
2008-05-07 13:36:19 +01:00
2009-04-29 08:05:08 +00:00
efx_mdio_phy_reconfigure ( efx ) ;
2008-05-07 13:36:19 +01:00
2008-09-01 12:48:17 +01:00
phy_data - > phy_mode = efx - > phy_mode ;
2009-11-29 03:42:41 +00:00
return 0 ;
2008-04-27 12:55:59 +01:00
}
2009-10-23 08:33:42 +00:00
static void qt202x_phy_get_settings ( struct efx_nic * efx , struct ethtool_cmd * ecmd )
2009-04-29 08:05:08 +00:00
{
mdio45_ethtool_gset ( & efx - > mdio , ecmd ) ;
}
2008-04-27 12:55:59 +01:00
2009-12-23 13:46:36 +00:00
static void qt202x_phy_remove ( struct efx_nic * efx )
2008-04-27 12:55:59 +01:00
{
2008-05-07 13:36:19 +01:00
/* Free the context block */
kfree ( efx - > phy_data ) ;
efx - > phy_data = NULL ;
2008-04-27 12:55:59 +01:00
}
2009-10-23 08:33:42 +00:00
struct efx_phy_operations falcon_qt202x_phy_ops = {
2009-11-29 15:08:55 +00:00
. probe = qt202x_phy_probe ,
2009-10-23 08:33:42 +00:00
. init = qt202x_phy_init ,
. reconfigure = qt202x_phy_reconfigure ,
. poll = qt202x_phy_poll ,
2009-12-23 13:46:36 +00:00
. fini = efx_port_dummy_op_void ,
. remove = qt202x_phy_remove ,
2009-10-23 08:33:42 +00:00
. get_settings = qt202x_phy_get_settings ,
2009-04-29 08:05:08 +00:00
. set_settings = efx_mdio_set_settings ,
2008-04-27 12:55:59 +01:00
} ;