2007-01-18 22:04:14 -05:00
/*
2008-10-13 18:47:30 -07:00
* Copyright ( c ) 2005 - 2008 Chelsio , Inc . All rights reserved .
2007-01-18 22:04:14 -05:00
*
2007-01-30 19:44:35 -08:00
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
2007-01-18 22:04:14 -05:00
*
2007-01-30 19:44:35 -08:00
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
2007-01-18 22:04:14 -05:00
*/
# include "common.h"
# include "regs.h"
2008-10-08 17:40:07 -07:00
enum {
2007-01-18 22:04:14 -05:00
AEL100X_TX_CONFIG1 = 0xc002 ,
AEL1002_PWR_DOWN_HI = 0xc011 ,
AEL1002_PWR_DOWN_LO = 0xc012 ,
AEL1002_XFI_EQL = 0xc015 ,
AEL1002_LB_EN = 0xc017 ,
2008-10-08 17:40:07 -07:00
AEL_OPT_SETTINGS = 0xc017 ,
AEL_I2C_CTRL = 0xc30a ,
AEL_I2C_DATA = 0xc30b ,
AEL_I2C_STAT = 0xc30c ,
AEL2005_GPIO_CTRL = 0xc214 ,
AEL2005_GPIO_STAT = 0xc215 ,
2009-05-29 12:52:44 +00:00
AEL2020_GPIO_INTR = 0xc103 , /* Latch High (LH) */
AEL2020_GPIO_CTRL = 0xc108 , /* Store Clear (SC) */
AEL2020_GPIO_STAT = 0xc10c , /* Read Only (RO) */
AEL2020_GPIO_CFG = 0xc110 , /* Read Write (RW) */
AEL2020_GPIO_SDA = 0 , /* IN: i2c serial data */
AEL2020_GPIO_MODDET = 1 , /* IN: Module Detect */
AEL2020_GPIO_0 = 3 , /* IN: unassigned */
AEL2020_GPIO_1 = 2 , /* OUT: unassigned */
AEL2020_GPIO_LSTAT = AEL2020_GPIO_1 , /* wired to link status LED */
2008-10-08 17:40:07 -07:00
} ;
enum { edc_none , edc_sr , edc_twinax } ;
/* PHY module I2C device address */
2009-05-29 12:52:44 +00:00
enum {
MODULE_DEV_ADDR = 0xa0 ,
SFF_DEV_ADDR = 0xa2 ,
} ;
/* PHY transceiver type */
enum {
phy_transtype_unknown = 0 ,
phy_transtype_sfp = 3 ,
phy_transtype_xfp = 6 ,
} ;
2008-10-08 17:40:07 -07:00
# define AEL2005_MODDET_IRQ 4
struct reg_val {
unsigned short mmd_addr ;
unsigned short reg_addr ;
unsigned short clear_bits ;
unsigned short set_bits ;
2007-01-18 22:04:14 -05:00
} ;
2008-10-08 17:40:07 -07:00
static int set_phy_regs ( struct cphy * phy , const struct reg_val * rv )
{
int err ;
for ( err = 0 ; rv - > mmd_addr & & ! err ; rv + + ) {
if ( rv - > clear_bits = = 0xffff )
2009-04-29 08:07:20 +00:00
err = t3_mdio_write ( phy , rv - > mmd_addr , rv - > reg_addr ,
rv - > set_bits ) ;
2008-10-08 17:40:07 -07:00
else
err = t3_mdio_change_bits ( phy , rv - > mmd_addr ,
rv - > reg_addr , rv - > clear_bits ,
rv - > set_bits ) ;
}
return err ;
}
2007-01-18 22:04:14 -05:00
static void ael100x_txon ( struct cphy * phy )
{
2009-04-29 08:07:20 +00:00
int tx_on_gpio =
phy - > mdio . prtad = = 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL ;
2007-01-18 22:04:14 -05:00
msleep ( 100 ) ;
t3_set_reg_field ( phy - > adapter , A_T3DBG_GPIO_EN , 0 , tx_on_gpio ) ;
msleep ( 30 ) ;
}
2009-05-29 12:52:44 +00:00
/*
* Read an 8 - bit word from a device attached to the PHY ' s i2c bus .
*/
static int ael_i2c_rd ( struct cphy * phy , int dev_addr , int word_addr )
{
int i , err ;
unsigned int stat , data ;
err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL_I2C_CTRL ,
( dev_addr < < 8 ) | ( 1 < < 8 ) | word_addr ) ;
if ( err )
return err ;
for ( i = 0 ; i < 200 ; i + + ) {
msleep ( 1 ) ;
err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL_I2C_STAT , & stat ) ;
if ( err )
return err ;
if ( ( stat & 3 ) = = 1 ) {
err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL_I2C_DATA ,
& data ) ;
if ( err )
return err ;
return data > > 8 ;
}
}
CH_WARN ( phy - > adapter , " PHY %u i2c read of dev.addr %#x.%#x timed out \n " ,
phy - > mdio . prtad , dev_addr , word_addr ) ;
return - ETIMEDOUT ;
}
2007-01-18 22:04:14 -05:00
static int ael1002_power_down ( struct cphy * phy , int enable )
{
int err ;
2009-04-29 08:07:20 +00:00
err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , MDIO_PMA_TXDIS , ! ! enable ) ;
2007-01-18 22:04:14 -05:00
if ( ! err )
2009-04-29 08:07:20 +00:00
err = mdio_set_flag ( & phy - > mdio , phy - > mdio . prtad ,
MDIO_MMD_PMAPMD , MDIO_CTRL1 ,
MDIO_CTRL1_LPOWER , enable ) ;
2007-01-18 22:04:14 -05:00
return err ;
}
static int ael1002_reset ( struct cphy * phy , int wait )
{
int err ;
if ( ( err = ael1002_power_down ( phy , 0 ) ) | |
2009-04-29 08:07:20 +00:00
( err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL100X_TX_CONFIG1 , 1 ) ) | |
( err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL1002_PWR_DOWN_HI , 0 ) ) | |
( err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL1002_PWR_DOWN_LO , 0 ) ) | |
( err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL1002_XFI_EQL , 0x18 ) ) | |
( err = t3_mdio_change_bits ( phy , MDIO_MMD_PMAPMD , AEL1002_LB_EN ,
2007-01-18 22:04:14 -05:00
0 , 1 < < 5 ) ) )
return err ;
return 0 ;
}
static int ael1002_intr_noop ( struct cphy * phy )
{
return 0 ;
}
2008-10-08 17:40:07 -07:00
/*
* Get link status for a 10 GBASE - R device .
*/
static int get_link_status_r ( struct cphy * phy , int * link_ok , int * speed ,
int * duplex , int * fc )
2007-01-18 22:04:14 -05:00
{
if ( link_ok ) {
2008-10-08 17:40:07 -07:00
unsigned int stat0 , stat1 , stat2 ;
2009-04-29 08:07:20 +00:00
int err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD ,
MDIO_PMA_RXDET , & stat0 ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
2009-04-29 08:07:20 +00:00
err = t3_mdio_read ( phy , MDIO_MMD_PCS ,
MDIO_PCS_10GBRT_STAT1 , & stat1 ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
2009-04-29 08:07:20 +00:00
err = t3_mdio_read ( phy , MDIO_MMD_PHYXS ,
MDIO_PHYXS_LNSTAT , & stat2 ) ;
2007-01-18 22:04:14 -05:00
if ( err )
return err ;
2008-10-08 17:40:07 -07:00
* link_ok = ( stat0 & stat1 & ( stat2 > > 12 ) ) & 1 ;
2007-01-18 22:04:14 -05:00
}
if ( speed )
* speed = SPEED_10000 ;
if ( duplex )
* duplex = DUPLEX_FULL ;
return 0 ;
}
static struct cphy_ops ael1002_ops = {
. reset = ael1002_reset ,
. intr_enable = ael1002_intr_noop ,
. intr_disable = ael1002_intr_noop ,
. intr_clear = ael1002_intr_noop ,
. intr_handler = ael1002_intr_noop ,
2008-10-08 17:40:07 -07:00
. get_link_status = get_link_status_r ,
2007-01-18 22:04:14 -05:00
. power_down = ael1002_power_down ,
2009-04-29 08:07:20 +00:00
. mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS ,
2007-01-18 22:04:14 -05:00
} ;
2008-10-08 17:38:01 -07:00
int t3_ael1002_phy_prep ( struct cphy * phy , struct adapter * adapter ,
int phy_addr , const struct mdio_ops * mdio_ops )
2007-01-18 22:04:14 -05:00
{
2008-10-08 17:38:29 -07:00
cphy_init ( phy , adapter , phy_addr , & ael1002_ops , mdio_ops ,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE ,
" 10GBASE-R " ) ;
2007-01-18 22:04:14 -05:00
ael100x_txon ( phy ) ;
2008-10-08 17:38:01 -07:00
return 0 ;
2007-01-18 22:04:14 -05:00
}
static int ael1006_reset ( struct cphy * phy , int wait )
{
2009-04-29 08:07:20 +00:00
return t3_phy_reset ( phy , MDIO_MMD_PMAPMD , wait ) ;
2007-01-18 22:04:14 -05:00
}
static struct cphy_ops ael1006_ops = {
. reset = ael1006_reset ,
2008-10-08 17:39:31 -07:00
. intr_enable = t3_phy_lasi_intr_enable ,
. intr_disable = t3_phy_lasi_intr_disable ,
. intr_clear = t3_phy_lasi_intr_clear ,
. intr_handler = t3_phy_lasi_intr_handler ,
2008-10-08 17:40:07 -07:00
. get_link_status = get_link_status_r ,
2009-07-07 19:48:53 +00:00
. power_down = ael1002_power_down ,
2009-04-29 08:07:20 +00:00
. mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS ,
2007-01-18 22:04:14 -05:00
} ;
2008-10-08 17:38:01 -07:00
int t3_ael1006_phy_prep ( struct cphy * phy , struct adapter * adapter ,
int phy_addr , const struct mdio_ops * mdio_ops )
2007-01-18 22:04:14 -05:00
{
2008-10-08 17:38:29 -07:00
cphy_init ( phy , adapter , phy_addr , & ael1006_ops , mdio_ops ,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE ,
" 10GBASE-SR " ) ;
2007-01-18 22:04:14 -05:00
ael100x_txon ( phy ) ;
2008-10-08 17:38:01 -07:00
return 0 ;
2007-01-18 22:04:14 -05:00
}
2009-05-29 12:52:44 +00:00
/*
* Decode our module type .
*/
static int ael2xxx_get_module_type ( struct cphy * phy , int delay_ms )
{
int v ;
if ( delay_ms )
msleep ( delay_ms ) ;
/* see SFF-8472 for below */
v = ael_i2c_rd ( phy , MODULE_DEV_ADDR , 3 ) ;
if ( v < 0 )
return v ;
if ( v = = 0x10 )
return phy_modtype_sr ;
if ( v = = 0x20 )
return phy_modtype_lr ;
if ( v = = 0x40 )
return phy_modtype_lrm ;
v = ael_i2c_rd ( phy , MODULE_DEV_ADDR , 6 ) ;
if ( v < 0 )
return v ;
if ( v ! = 4 )
goto unknown ;
v = ael_i2c_rd ( phy , MODULE_DEV_ADDR , 10 ) ;
if ( v < 0 )
return v ;
if ( v & 0x80 ) {
v = ael_i2c_rd ( phy , MODULE_DEV_ADDR , 0x12 ) ;
if ( v < 0 )
return v ;
return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax ;
}
unknown :
return phy_modtype_unknown ;
}
/*
* Code to support the Aeluros / NetLogic 2005 10 Gb PHY .
*/
2008-10-08 17:40:07 -07:00
static int ael2005_setup_sr_edc ( struct cphy * phy )
{
static struct reg_val regs [ ] = {
2009-04-29 08:07:20 +00:00
{ MDIO_MMD_PMAPMD , 0xc003 , 0xffff , 0x181 } ,
{ MDIO_MMD_PMAPMD , 0xc010 , 0xffff , 0x448a } ,
{ MDIO_MMD_PMAPMD , 0xc04a , 0xffff , 0x5200 } ,
2008-10-08 17:40:07 -07:00
{ 0 , 0 , 0 , 0 }
} ;
2009-07-07 19:49:09 +00:00
2008-10-08 17:40:07 -07:00
int i , err ;
err = set_phy_regs ( phy , regs ) ;
if ( err )
return err ;
msleep ( 50 ) ;
2009-07-07 19:49:09 +00:00
if ( phy - > priv ! = edc_sr )
err = t3_get_edc_fw ( phy , EDC_OPT_AEL2005 ,
EDC_OPT_AEL2005_SIZE ) ;
if ( err )
return err ;
for ( i = 0 ; i < EDC_OPT_AEL2005_SIZE / sizeof ( u16 ) & & ! err ; i + = 2 )
err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD ,
phy - > phy_cache [ i ] ,
phy - > phy_cache [ i + 1 ] ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
phy - > priv = edc_sr ;
return err ;
}
static int ael2005_setup_twinax_edc ( struct cphy * phy , int modtype )
{
static struct reg_val regs [ ] = {
2009-04-29 08:07:20 +00:00
{ MDIO_MMD_PMAPMD , 0xc04a , 0xffff , 0x5a00 } ,
2008-10-08 17:40:07 -07:00
{ 0 , 0 , 0 , 0 }
} ;
static struct reg_val preemphasis [ ] = {
2009-04-29 08:07:20 +00:00
{ MDIO_MMD_PMAPMD , 0xc014 , 0xffff , 0xfe16 } ,
{ MDIO_MMD_PMAPMD , 0xc015 , 0xffff , 0xa000 } ,
2008-10-08 17:40:07 -07:00
{ 0 , 0 , 0 , 0 }
} ;
int i , err ;
err = set_phy_regs ( phy , regs ) ;
if ( ! err & & modtype = = phy_modtype_twinax_long )
err = set_phy_regs ( phy , preemphasis ) ;
if ( err )
return err ;
msleep ( 50 ) ;
2009-07-07 19:49:09 +00:00
if ( phy - > priv ! = edc_twinax )
err = t3_get_edc_fw ( phy , EDC_TWX_AEL2005 ,
EDC_TWX_AEL2005_SIZE ) ;
if ( err )
return err ;
for ( i = 0 ; i < EDC_TWX_AEL2005_SIZE / sizeof ( u16 ) & & ! err ; i + = 2 )
err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD ,
phy - > phy_cache [ i ] ,
phy - > phy_cache [ i + 1 ] ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
phy - > priv = edc_twinax ;
return err ;
}
2009-05-29 12:52:44 +00:00
static int ael2005_get_module_type ( struct cphy * phy , int delay_ms )
2008-10-08 17:40:07 -07:00
{
int v ;
unsigned int stat ;
2009-04-29 08:07:20 +00:00
v = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_CTRL , & stat ) ;
2008-10-08 17:40:07 -07:00
if ( v )
return v ;
if ( stat & ( 1 < < 8 ) ) /* module absent */
return phy_modtype_none ;
2009-05-29 12:52:44 +00:00
return ael2xxx_get_module_type ( phy , delay_ms ) ;
2008-10-08 17:40:07 -07:00
}
static int ael2005_intr_enable ( struct cphy * phy )
{
2009-04-29 08:07:20 +00:00
int err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_CTRL , 0x200 ) ;
2008-10-08 17:40:07 -07:00
return err ? err : t3_phy_lasi_intr_enable ( phy ) ;
}
static int ael2005_intr_disable ( struct cphy * phy )
{
2009-04-29 08:07:20 +00:00
int err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_CTRL , 0x100 ) ;
2008-10-08 17:40:07 -07:00
return err ? err : t3_phy_lasi_intr_disable ( phy ) ;
}
static int ael2005_intr_clear ( struct cphy * phy )
{
2009-04-29 08:07:20 +00:00
int err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_CTRL , 0xd00 ) ;
2008-10-08 17:40:07 -07:00
return err ? err : t3_phy_lasi_intr_clear ( phy ) ;
}
static int ael2005_reset ( struct cphy * phy , int wait )
{
static struct reg_val regs0 [ ] = {
2009-04-29 08:07:20 +00:00
{ MDIO_MMD_PMAPMD , 0xc001 , 0 , 1 < < 5 } ,
{ MDIO_MMD_PMAPMD , 0xc017 , 0 , 1 < < 5 } ,
{ MDIO_MMD_PMAPMD , 0xc013 , 0xffff , 0xf341 } ,
{ MDIO_MMD_PMAPMD , 0xc210 , 0xffff , 0x8000 } ,
{ MDIO_MMD_PMAPMD , 0xc210 , 0xffff , 0x8100 } ,
{ MDIO_MMD_PMAPMD , 0xc210 , 0xffff , 0x8000 } ,
{ MDIO_MMD_PMAPMD , 0xc210 , 0xffff , 0 } ,
2008-10-08 17:40:07 -07:00
{ 0 , 0 , 0 , 0 }
} ;
static struct reg_val regs1 [ ] = {
2009-04-29 08:07:20 +00:00
{ MDIO_MMD_PMAPMD , 0xca00 , 0xffff , 0x0080 } ,
{ MDIO_MMD_PMAPMD , 0xca12 , 0xffff , 0 } ,
2008-10-08 17:40:07 -07:00
{ 0 , 0 , 0 , 0 }
} ;
2009-02-14 11:16:19 +00:00
int err ;
unsigned int lasi_ctrl ;
2008-10-08 17:40:07 -07:00
2009-05-19 13:22:30 +00:00
err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , MDIO_PMA_LASI_CTRL ,
& lasi_ctrl ) ;
2008-10-08 17:40:07 -07:00
if ( err )
return err ;
2009-04-29 08:07:20 +00:00
err = t3_phy_reset ( phy , MDIO_MMD_PMAPMD , 0 ) ;
2008-10-08 17:40:07 -07:00
if ( err )
return err ;
msleep ( 125 ) ;
phy - > priv = edc_none ;
err = set_phy_regs ( phy , regs0 ) ;
if ( err )
return err ;
msleep ( 50 ) ;
2009-05-29 12:52:44 +00:00
err = ael2005_get_module_type ( phy , 0 ) ;
2008-10-08 17:40:07 -07:00
if ( err < 0 )
return err ;
phy - > modtype = err ;
if ( err = = phy_modtype_twinax | | err = = phy_modtype_twinax_long )
err = ael2005_setup_twinax_edc ( phy , err ) ;
else
err = ael2005_setup_sr_edc ( phy ) ;
if ( err )
return err ;
err = set_phy_regs ( phy , regs1 ) ;
if ( err )
return err ;
/* reset wipes out interrupts, reenable them if they were on */
if ( lasi_ctrl & 1 )
err = ael2005_intr_enable ( phy ) ;
return err ;
}
static int ael2005_intr_handler ( struct cphy * phy )
{
unsigned int stat ;
int ret , edc_needed , cause = 0 ;
2009-04-29 08:07:20 +00:00
ret = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_STAT , & stat ) ;
2008-10-08 17:40:07 -07:00
if ( ret )
return ret ;
if ( stat & AEL2005_MODDET_IRQ ) {
2009-04-29 08:07:20 +00:00
ret = t3_mdio_write ( phy , MDIO_MMD_PMAPMD , AEL2005_GPIO_CTRL ,
0xd00 ) ;
2008-10-08 17:40:07 -07:00
if ( ret )
return ret ;
/* modules have max 300 ms init time after hot plug */
2009-05-29 12:52:44 +00:00
ret = ael2005_get_module_type ( phy , 300 ) ;
2008-10-08 17:40:07 -07:00
if ( ret < 0 )
return ret ;
phy - > modtype = ret ;
if ( ret = = phy_modtype_none )
edc_needed = phy - > priv ; /* on unplug retain EDC */
else if ( ret = = phy_modtype_twinax | |
ret = = phy_modtype_twinax_long )
edc_needed = edc_twinax ;
else
edc_needed = edc_sr ;
if ( edc_needed ! = phy - > priv ) {
ret = ael2005_reset ( phy , 0 ) ;
return ret ? ret : cphy_cause_module_change ;
}
cause = cphy_cause_module_change ;
}
ret = t3_phy_lasi_intr_handler ( phy ) ;
if ( ret < 0 )
return ret ;
ret | = cause ;
return ret ? ret : cphy_cause_link_change ;
}
static struct cphy_ops ael2005_ops = {
. reset = ael2005_reset ,
. intr_enable = ael2005_intr_enable ,
. intr_disable = ael2005_intr_disable ,
. intr_clear = ael2005_intr_clear ,
. intr_handler = ael2005_intr_handler ,
. get_link_status = get_link_status_r ,
. power_down = ael1002_power_down ,
2009-04-29 08:07:20 +00:00
. mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS ,
2008-10-08 17:40:07 -07:00
} ;
int t3_ael2005_phy_prep ( struct cphy * phy , struct adapter * adapter ,
int phy_addr , const struct mdio_ops * mdio_ops )
{
cphy_init ( phy , adapter , phy_addr , & ael2005_ops , mdio_ops ,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_IRQ , " 10GBASE-R " ) ;
msleep ( 125 ) ;
2009-04-29 08:07:20 +00:00
return t3_mdio_change_bits ( phy , MDIO_MMD_PMAPMD , AEL_OPT_SETTINGS , 0 ,
2008-10-08 17:40:07 -07:00
1 < < 5 ) ;
}
2009-05-29 12:52:44 +00:00
/*
* Setup EDC and other parameters for operation with an optical module .
*/
static int ael2020_setup_sr_edc ( struct cphy * phy )
{
static struct reg_val regs [ ] = {
/* set CDR offset to 10 */
{ MDIO_MMD_PMAPMD , 0xcc01 , 0xffff , 0x488a } ,
/* adjust 10G RX bias current */
{ MDIO_MMD_PMAPMD , 0xcb1b , 0xffff , 0x0200 } ,
{ MDIO_MMD_PMAPMD , 0xcb1c , 0xffff , 0x00f0 } ,
{ MDIO_MMD_PMAPMD , 0xcc06 , 0xffff , 0x00e0 } ,
/* end */
{ 0 , 0 , 0 , 0 }
} ;
int err ;
err = set_phy_regs ( phy , regs ) ;
msleep ( 50 ) ;
if ( err )
return err ;
phy - > priv = edc_sr ;
return 0 ;
}
/*
* Setup EDC and other parameters for operation with an TWINAX module .
*/
static int ael2020_setup_twinax_edc ( struct cphy * phy , int modtype )
{
/* set uC to 40MHz */
static struct reg_val uCclock40MHz [ ] = {
{ MDIO_MMD_PMAPMD , 0xff28 , 0xffff , 0x4001 } ,
{ MDIO_MMD_PMAPMD , 0xff2a , 0xffff , 0x0002 } ,
{ 0 , 0 , 0 , 0 }
} ;
/* activate uC clock */
static struct reg_val uCclockActivate [ ] = {
{ MDIO_MMD_PMAPMD , 0xd000 , 0xffff , 0x5200 } ,
{ 0 , 0 , 0 , 0 }
} ;
/* set PC to start of SRAM and activate uC */
static struct reg_val uCactivate [ ] = {
{ MDIO_MMD_PMAPMD , 0xd080 , 0xffff , 0x0100 } ,
{ MDIO_MMD_PMAPMD , 0xd092 , 0xffff , 0x0000 } ,
{ 0 , 0 , 0 , 0 }
} ;
int i , err ;
/* set uC clock and activate it */
err = set_phy_regs ( phy , uCclock40MHz ) ;
msleep ( 500 ) ;
if ( err )
return err ;
err = set_phy_regs ( phy , uCclockActivate ) ;
msleep ( 500 ) ;
if ( err )
return err ;
2009-07-07 19:49:09 +00:00
if ( phy - > priv ! = edc_twinax )
err = t3_get_edc_fw ( phy , EDC_TWX_AEL2020 ,
EDC_TWX_AEL2020_SIZE ) ;
if ( err )
return err ;
for ( i = 0 ; i < EDC_TWX_AEL2020_SIZE / sizeof ( u16 ) & & ! err ; i + = 2 )
err = t3_mdio_write ( phy , MDIO_MMD_PMAPMD ,
phy - > phy_cache [ i ] ,
phy - > phy_cache [ i + 1 ] ) ;
2009-05-29 12:52:44 +00:00
/* activate uC */
err = set_phy_regs ( phy , uCactivate ) ;
if ( ! err )
phy - > priv = edc_twinax ;
return err ;
}
/*
* Return Module Type .
*/
static int ael2020_get_module_type ( struct cphy * phy , int delay_ms )
{
int v ;
unsigned int stat ;
v = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL2020_GPIO_STAT , & stat ) ;
if ( v )
return v ;
if ( stat & ( 0x1 < < ( AEL2020_GPIO_MODDET * 4 ) ) ) {
/* module absent */
return phy_modtype_none ;
}
return ael2xxx_get_module_type ( phy , delay_ms ) ;
}
/*
* Enable PHY interrupts . We enable " Module Detection " interrupts ( on any
* state transition ) and then generic Link Alarm Status Interrupt ( LASI ) .
*/
static int ael2020_intr_enable ( struct cphy * phy )
{
2009-07-07 19:48:43 +00:00
struct reg_val regs [ ] = {
/* output Module's Loss Of Signal (LOS) to LED */
{ MDIO_MMD_PMAPMD , AEL2020_GPIO_CFG + AEL2020_GPIO_LSTAT ,
0xffff , 0x4 } ,
{ MDIO_MMD_PMAPMD , AEL2020_GPIO_CTRL ,
0xffff , 0x8 < < ( AEL2020_GPIO_LSTAT * 4 ) } ,
/* enable module detect status change interrupts */
{ MDIO_MMD_PMAPMD , AEL2020_GPIO_CTRL ,
0xffff , 0x2 < < ( AEL2020_GPIO_MODDET * 4 ) } ,
/* end */
{ 0 , 0 , 0 , 0 }
} ;
int err , link_ok = 0 ;
/* set up "link status" LED and enable module change interrupts */
err = set_phy_regs ( phy , regs ) ;
if ( err )
return err ;
err = get_link_status_r ( phy , & link_ok , NULL , NULL , NULL ) ;
if ( err )
return err ;
if ( link_ok )
t3_link_changed ( phy - > adapter ,
phy2portid ( phy ) ) ;
err = t3_phy_lasi_intr_enable ( phy ) ;
if ( err )
return err ;
return 0 ;
2009-05-29 12:52:44 +00:00
}
/*
* Disable PHY interrupts . The mirror of the above . . .
*/
static int ael2020_intr_disable ( struct cphy * phy )
{
2009-07-07 19:48:43 +00:00
struct reg_val regs [ ] = {
/* reset "link status" LED to "off" */
{ MDIO_MMD_PMAPMD , AEL2020_GPIO_CTRL ,
0xffff , 0xb < < ( AEL2020_GPIO_LSTAT * 4 ) } ,
/* disable module detect status change interrupts */
{ MDIO_MMD_PMAPMD , AEL2020_GPIO_CTRL ,
0xffff , 0x1 < < ( AEL2020_GPIO_MODDET * 4 ) } ,
/* end */
{ 0 , 0 , 0 , 0 }
} ;
int err ;
/* turn off "link status" LED and disable module change interrupts */
err = set_phy_regs ( phy , regs ) ;
if ( err )
return err ;
return t3_phy_lasi_intr_disable ( phy ) ;
2009-05-29 12:52:44 +00:00
}
/*
* Clear PHY interrupt state .
*/
static int ael2020_intr_clear ( struct cphy * phy )
{
/*
* The GPIO Interrupt register on the AEL2020 is a " Latching High "
* ( LH ) register which is cleared to the current state when it ' s read .
* Thus , we simply read the register and discard the result .
*/
unsigned int stat ;
int err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL2020_GPIO_INTR , & stat ) ;
return err ? err : t3_phy_lasi_intr_clear ( phy ) ;
}
2009-07-07 19:48:43 +00:00
static struct reg_val ael2020_reset_regs [ ] = {
/* Erratum #2: CDRLOL asserted, causing PMA link down status */
{ MDIO_MMD_PMAPMD , 0xc003 , 0xffff , 0x3101 } ,
/* force XAUI to send LF when RX_LOS is asserted */
{ MDIO_MMD_PMAPMD , 0xcd40 , 0xffff , 0x0001 } ,
/* allow writes to transceiver module EEPROM on i2c bus */
{ MDIO_MMD_PMAPMD , 0xff02 , 0xffff , 0x0023 } ,
{ MDIO_MMD_PMAPMD , 0xff03 , 0xffff , 0x0000 } ,
{ MDIO_MMD_PMAPMD , 0xff04 , 0xffff , 0x0000 } ,
/* end */
{ 0 , 0 , 0 , 0 }
} ;
2009-05-29 12:52:44 +00:00
/*
* Reset the PHY and put it into a canonical operating state .
*/
static int ael2020_reset ( struct cphy * phy , int wait )
{
int err ;
unsigned int lasi_ctrl ;
/* grab current interrupt state */
err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , MDIO_PMA_LASI_CTRL ,
& lasi_ctrl ) ;
if ( err )
return err ;
err = t3_phy_reset ( phy , MDIO_MMD_PMAPMD , 125 ) ;
if ( err )
return err ;
msleep ( 100 ) ;
/* basic initialization for all module types */
phy - > priv = edc_none ;
2009-07-07 19:48:43 +00:00
err = set_phy_regs ( phy , ael2020_reset_regs ) ;
2009-05-29 12:52:44 +00:00
if ( err )
return err ;
/* determine module type and perform appropriate initialization */
err = ael2020_get_module_type ( phy , 0 ) ;
if ( err < 0 )
return err ;
phy - > modtype = ( u8 ) err ;
if ( err = = phy_modtype_twinax | | err = = phy_modtype_twinax_long )
err = ael2020_setup_twinax_edc ( phy , err ) ;
else
err = ael2020_setup_sr_edc ( phy ) ;
if ( err )
return err ;
/* reset wipes out interrupts, reenable them if they were on */
if ( lasi_ctrl & 1 )
err = ael2005_intr_enable ( phy ) ;
return err ;
}
/*
* Handle a PHY interrupt .
*/
static int ael2020_intr_handler ( struct cphy * phy )
{
unsigned int stat ;
int ret , edc_needed , cause = 0 ;
ret = t3_mdio_read ( phy , MDIO_MMD_PMAPMD , AEL2020_GPIO_INTR , & stat ) ;
if ( ret )
return ret ;
if ( stat & ( 0x1 < < AEL2020_GPIO_MODDET ) ) {
/* modules have max 300 ms init time after hot plug */
ret = ael2020_get_module_type ( phy , 300 ) ;
if ( ret < 0 )
return ret ;
phy - > modtype = ( u8 ) ret ;
if ( ret = = phy_modtype_none )
edc_needed = phy - > priv ; /* on unplug retain EDC */
else if ( ret = = phy_modtype_twinax | |
ret = = phy_modtype_twinax_long )
edc_needed = edc_twinax ;
else
edc_needed = edc_sr ;
if ( edc_needed ! = phy - > priv ) {
ret = ael2020_reset ( phy , 0 ) ;
return ret ? ret : cphy_cause_module_change ;
}
cause = cphy_cause_module_change ;
}
ret = t3_phy_lasi_intr_handler ( phy ) ;
if ( ret < 0 )
return ret ;
ret | = cause ;
return ret ? ret : cphy_cause_link_change ;
}
static struct cphy_ops ael2020_ops = {
. reset = ael2020_reset ,
. intr_enable = ael2020_intr_enable ,
. intr_disable = ael2020_intr_disable ,
. intr_clear = ael2020_intr_clear ,
. intr_handler = ael2020_intr_handler ,
. get_link_status = get_link_status_r ,
. power_down = ael1002_power_down ,
. mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS ,
} ;
int t3_ael2020_phy_prep ( struct cphy * phy , struct adapter * adapter , int phy_addr ,
const struct mdio_ops * mdio_ops )
{
2009-07-07 19:48:43 +00:00
int err ;
2009-05-29 12:52:44 +00:00
cphy_init ( phy , adapter , phy_addr , & ael2020_ops , mdio_ops ,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_IRQ , " 10GBASE-R " ) ;
msleep ( 125 ) ;
2009-07-07 19:48:43 +00:00
err = set_phy_regs ( phy , ael2020_reset_regs ) ;
if ( err )
return err ;
2009-05-29 12:52:44 +00:00
return 0 ;
}
2008-10-08 17:40:07 -07:00
/*
* Get link status for a 10 GBASE - X device .
*/
static int get_link_status_x ( struct cphy * phy , int * link_ok , int * speed ,
int * duplex , int * fc )
{
if ( link_ok ) {
unsigned int stat0 , stat1 , stat2 ;
2009-04-29 08:07:20 +00:00
int err = t3_mdio_read ( phy , MDIO_MMD_PMAPMD ,
MDIO_PMA_RXDET , & stat0 ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
2009-04-29 08:07:20 +00:00
err = t3_mdio_read ( phy , MDIO_MMD_PCS ,
MDIO_PCS_10GBX_STAT1 , & stat1 ) ;
2008-10-08 17:40:07 -07:00
if ( ! err )
2009-04-29 08:07:20 +00:00
err = t3_mdio_read ( phy , MDIO_MMD_PHYXS ,
MDIO_PHYXS_LNSTAT , & stat2 ) ;
2008-10-08 17:40:07 -07:00
if ( err )
return err ;
* link_ok = ( stat0 & ( stat1 > > 12 ) & ( stat2 > > 12 ) ) & 1 ;
}
if ( speed )
* speed = SPEED_10000 ;
if ( duplex )
* duplex = DUPLEX_FULL ;
return 0 ;
}
2007-01-18 22:04:14 -05:00
static struct cphy_ops qt2045_ops = {
. reset = ael1006_reset ,
2008-10-08 17:39:31 -07:00
. intr_enable = t3_phy_lasi_intr_enable ,
. intr_disable = t3_phy_lasi_intr_disable ,
. intr_clear = t3_phy_lasi_intr_clear ,
. intr_handler = t3_phy_lasi_intr_handler ,
2008-10-08 17:40:07 -07:00
. get_link_status = get_link_status_x ,
2009-07-07 19:48:53 +00:00
. power_down = ael1002_power_down ,
2009-04-29 08:07:20 +00:00
. mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS ,
2007-01-18 22:04:14 -05:00
} ;
2008-10-08 17:38:01 -07:00
int t3_qt2045_phy_prep ( struct cphy * phy , struct adapter * adapter ,
int phy_addr , const struct mdio_ops * mdio_ops )
2007-01-18 22:04:14 -05:00
{
unsigned int stat ;
2008-10-08 17:38:29 -07:00
cphy_init ( phy , adapter , phy_addr , & qt2045_ops , mdio_ops ,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP ,
" 10GBASE-CX4 " ) ;
2007-01-18 22:04:14 -05:00
/*
* Some cards where the PHY is supposed to be at address 0 actually
* have it at 1.
*/
2009-04-29 08:07:20 +00:00
if ( ! phy_addr & &
! t3_mdio_read ( phy , MDIO_MMD_PMAPMD , MDIO_STAT1 , & stat ) & &
2007-01-18 22:04:14 -05:00
stat = = 0xffff )
2009-04-29 08:07:20 +00:00
phy - > mdio . prtad = 1 ;
2008-10-08 17:38:01 -07:00
return 0 ;
2007-01-18 22:04:14 -05:00
}
static int xaui_direct_reset ( struct cphy * phy , int wait )
{
return 0 ;
}
static int xaui_direct_get_link_status ( struct cphy * phy , int * link_ok ,
int * speed , int * duplex , int * fc )
{
if ( link_ok ) {
unsigned int status ;
2009-04-29 08:07:20 +00:00
int prtad = phy - > mdio . prtad ;
2007-01-18 22:04:14 -05:00
status = t3_read_reg ( phy - > adapter ,
2009-04-29 08:07:20 +00:00
XGM_REG ( A_XGM_SERDES_STAT0 , prtad ) ) |
2007-05-30 10:01:39 -07:00
t3_read_reg ( phy - > adapter ,
2009-04-29 08:07:20 +00:00
XGM_REG ( A_XGM_SERDES_STAT1 , prtad ) ) |
2007-05-30 10:01:39 -07:00
t3_read_reg ( phy - > adapter ,
2009-04-29 08:07:20 +00:00
XGM_REG ( A_XGM_SERDES_STAT2 , prtad ) ) |
2007-05-30 10:01:39 -07:00
t3_read_reg ( phy - > adapter ,
2009-04-29 08:07:20 +00:00
XGM_REG ( A_XGM_SERDES_STAT3 , prtad ) ) ;
2007-01-18 22:04:14 -05:00
* link_ok = ! ( status & F_LOWSIG0 ) ;
}
if ( speed )
* speed = SPEED_10000 ;
if ( duplex )
* duplex = DUPLEX_FULL ;
return 0 ;
}
static int xaui_direct_power_down ( struct cphy * phy , int enable )
{
return 0 ;
}
static struct cphy_ops xaui_direct_ops = {
. reset = xaui_direct_reset ,
. intr_enable = ael1002_intr_noop ,
. intr_disable = ael1002_intr_noop ,
. intr_clear = ael1002_intr_noop ,
. intr_handler = ael1002_intr_noop ,
. get_link_status = xaui_direct_get_link_status ,
. power_down = xaui_direct_power_down ,
} ;
2008-10-08 17:38:01 -07:00
int t3_xaui_direct_phy_prep ( struct cphy * phy , struct adapter * adapter ,
int phy_addr , const struct mdio_ops * mdio_ops )
2007-01-18 22:04:14 -05:00
{
2010-04-19 15:32:20 +00:00
cphy_init ( phy , adapter , phy_addr , & xaui_direct_ops , mdio_ops ,
2008-10-08 17:38:29 -07:00
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP ,
" 10GBASE-CX4 " ) ;
2008-10-08 17:38:01 -07:00
return 0 ;
2007-01-18 22:04:14 -05:00
}