2008-04-27 15:55:59 +04: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 .
*/
/*
* Driver for XFP optical PHYs ( plus some support specific to the Quake 2032 )
* See www . amcc . com for details ( search for qt2032 )
*/
# include <linux/timer.h>
# include <linux/delay.h>
# include "efx.h"
# include "gmii.h"
# include "mdio_10g.h"
# include "xenpack.h"
# include "phy.h"
# include "mac.h"
# define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \
MDIO_MMDREG_DEVS0_PMAPMD | \
MDIO_MMDREG_DEVS0_PHYXS )
2008-05-07 16:36:19 +04:00
# define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \
( 1 < < LOOPBACK_PMAPMD ) | \
( 1 < < LOOPBACK_NETWORK ) )
2008-04-27 15:55:59 +04:00
/****************************************************************************/
/* Quake-specific MDIO registers */
# define MDIO_QUAKE_LED0_REG (0xD006)
void xfp_set_led ( struct efx_nic * p , int led , int mode )
{
int addr = MDIO_QUAKE_LED0_REG + led ;
mdio_clause45_write ( p , p - > mii . phy_id , MDIO_MMD_PMAPMD , addr ,
mode ) ;
}
2008-05-07 16:36:19 +04:00
struct xfp_phy_data {
2008-09-01 15:48:17 +04:00
enum efx_phy_mode phy_mode ;
2008-05-07 16:36:19 +04:00
} ;
2008-04-27 15:55:59 +04:00
# define XFP_MAX_RESET_TIME 500
# define XFP_RESET_WAIT 10
/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing
* a complete soft reset .
*/
static int xfp_reset_phy ( struct efx_nic * efx )
{
int rc ;
rc = mdio_clause45_reset_mmd ( efx , MDIO_MMD_PHYXS ,
XFP_MAX_RESET_TIME / XFP_RESET_WAIT ,
XFP_RESET_WAIT ) ;
if ( rc < 0 )
goto fail ;
/* 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 */
rc = mdio_clause45_check_mmds ( efx , XFP_REQUIRED_DEVS ,
MDIO_MMDREG_DEVS0_PHYXS ) ;
if ( rc < 0 )
goto fail ;
efx - > board_info . init_leds ( efx ) ;
return rc ;
fail :
EFX_ERR ( efx , " XFP: reset timed out! \n " ) ;
return rc ;
}
static int xfp_phy_init ( struct efx_nic * efx )
{
2008-05-07 16:36:19 +04:00
struct xfp_phy_data * phy_data ;
2008-04-27 15:55:59 +04:00
u32 devid = mdio_clause45_read_id ( efx , MDIO_MMD_PHYXS ) ;
int rc ;
2008-05-07 16:36:19 +04:00
phy_data = kzalloc ( sizeof ( struct xfp_phy_data ) , GFP_KERNEL ) ;
2008-05-17 00:20:20 +04:00
if ( ! phy_data )
return - ENOMEM ;
2008-05-17 00:20:00 +04:00
efx - > phy_data = phy_data ;
2008-05-07 16:36:19 +04:00
2008-04-27 15:55:59 +04:00
EFX_INFO ( efx , " XFP: PHY ID reg %x (OUI %x model %x revision "
" %x) \n " , devid , MDIO_ID_OUI ( devid ) , MDIO_ID_MODEL ( devid ) ,
MDIO_ID_REV ( devid ) ) ;
2008-09-01 15:48:17 +04:00
phy_data - > phy_mode = efx - > phy_mode ;
2008-05-07 16:36:19 +04:00
2008-04-27 15:55:59 +04:00
rc = xfp_reset_phy ( efx ) ;
EFX_INFO ( efx , " XFP: PHY init %s. \n " ,
rc ? " failed " : " successful " ) ;
2008-05-07 16:36:19 +04:00
if ( rc < 0 )
goto fail ;
2008-04-27 15:55:59 +04:00
2008-05-07 16:36:19 +04:00
return 0 ;
fail :
kfree ( efx - > phy_data ) ;
efx - > phy_data = NULL ;
2008-04-27 15:55:59 +04:00
return rc ;
}
static void xfp_phy_clear_interrupt ( struct efx_nic * efx )
{
xenpack_clear_lasi_irqs ( efx ) ;
}
static int xfp_link_ok ( struct efx_nic * efx )
{
return mdio_clause45_links_ok ( efx , XFP_REQUIRED_DEVS ) ;
}
static int xfp_phy_check_hw ( struct efx_nic * efx )
{
int rc = 0 ;
int link_up = xfp_link_ok ( efx ) ;
/* Simulate a PHY event if link state has changed */
if ( link_up ! = efx - > link_up )
falcon_xmac_sim_phy_event ( efx ) ;
2008-11-04 23:34:56 +03:00
rc = efx - > board_info . monitor ( efx ) ;
if ( rc ) {
struct xfp_phy_data * phy_data = efx - > phy_data ;
EFX_ERR ( efx , " XFP sensor alert; putting PHY into low power \n " ) ;
efx - > phy_mode | = PHY_MODE_LOW_POWER ;
mdio_clause45_set_mmds_lpower ( efx , 1 , XFP_REQUIRED_DEVS ) ;
phy_data - > phy_mode | = PHY_MODE_LOW_POWER ;
}
2008-04-27 15:55:59 +04:00
return rc ;
}
static void xfp_phy_reconfigure ( struct efx_nic * efx )
{
2008-05-07 16:36:19 +04:00
struct xfp_phy_data * phy_data = efx - > phy_data ;
/* Reset the PHY when moving from tx off to tx on */
2008-09-01 15:48:17 +04:00
if ( ! ( efx - > phy_mode & PHY_MODE_TX_DISABLED ) & &
( phy_data - > phy_mode & PHY_MODE_TX_DISABLED ) )
2008-05-07 16:36:19 +04:00
xfp_reset_phy ( efx ) ;
mdio_clause45_transmit_disable ( efx ) ;
mdio_clause45_phy_reconfigure ( efx ) ;
2008-09-01 15:48:17 +04:00
phy_data - > phy_mode = efx - > phy_mode ;
2008-04-27 15:55:59 +04:00
efx - > link_up = xfp_link_ok ( efx ) ;
efx - > link_options = GM_LPA_10000FULL ;
}
static void xfp_phy_fini ( struct efx_nic * efx )
{
/* Clobber the LED if it was blinking */
2008-09-01 15:46:50 +04:00
efx - > board_info . blink ( efx , false ) ;
2008-05-07 16:36:19 +04:00
/* Free the context block */
kfree ( efx - > phy_data ) ;
efx - > phy_data = NULL ;
2008-04-27 15:55:59 +04:00
}
struct efx_phy_operations falcon_xfp_phy_ops = {
. init = xfp_phy_init ,
. reconfigure = xfp_phy_reconfigure ,
. check_hw = xfp_phy_check_hw ,
. fini = xfp_phy_fini ,
. clear_interrupt = xfp_phy_clear_interrupt ,
. mmds = XFP_REQUIRED_DEVS ,
2008-05-07 16:36:19 +04:00
. loopbacks = XFP_LOOPBACKS ,
2008-04-27 15:55:59 +04:00
} ;