2010-09-21 01:44:11 +00:00
/*
* Copyright ( C ) 1999 - 2010 Intel Corporation .
* Copyright ( C ) 2010 OKI SEMICONDUCTOR Co . , LTD .
*
* This code was derived from the Intel e1000e Linux driver .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include "pch_gbe.h"
# define OPTION_UNSET -1
# define OPTION_DISABLED 0
# define OPTION_ENABLED 1
/**
* TxDescriptors - Transmit Descriptor Count
* @ Valid Range : PCH_GBE_MIN_TXD - PCH_GBE_MAX_TXD
* @ Default Value : PCH_GBE_DEFAULT_TXD
*/
static int TxDescriptors = OPTION_UNSET ;
module_param ( TxDescriptors , int , 0 ) ;
MODULE_PARM_DESC ( TxDescriptors , " Number of transmit descriptors " ) ;
/**
* RxDescriptors - Receive Descriptor Count
* @ Valid Range : PCH_GBE_MIN_RXD - PCH_GBE_MAX_RXD
* @ Default Value : PCH_GBE_DEFAULT_RXD
*/
static int RxDescriptors = OPTION_UNSET ;
module_param ( RxDescriptors , int , 0 ) ;
MODULE_PARM_DESC ( RxDescriptors , " Number of receive descriptors " ) ;
/**
* Speed - User Specified Speed Override
* @ Valid Range : 0 , 10 , 100 , 1000
* - 0 : auto - negotiate at all supported speeds
* - 10 : only link at 10 Mbps
* - 100 : only link at 100 Mbps
* - 1000 : only link at 1000 Mbps
* @ Default Value : 0
*/
static int Speed = OPTION_UNSET ;
module_param ( Speed , int , 0 ) ;
MODULE_PARM_DESC ( Speed , " Speed setting " ) ;
/**
* Duplex - User Specified Duplex Override
* @ Valid Range : 0 - 2
* - 0 : auto - negotiate for duplex
* - 1 : only link at half duplex
* - 2 : only link at full duplex
* @ Default Value : 0
*/
static int Duplex = OPTION_UNSET ;
module_param ( Duplex , int , 0 ) ;
MODULE_PARM_DESC ( Duplex , " Duplex setting " ) ;
# define HALF_DUPLEX 1
# define FULL_DUPLEX 2
/**
* AutoNeg - Auto - negotiation Advertisement Override
* @ Valid Range : 0x01 - 0x0F , 0x20 - 0x2F
*
* The AutoNeg value is a bit mask describing which speed and duplex
* combinations should be advertised during auto - negotiation .
* The supported speed and duplex modes are listed below
*
* Bit 7 6 5 4 3 2 1 0
* Speed ( Mbps ) N / A N / A 1000 N / A 100 100 10 10
* Duplex Full Full Half Full Half
*
* @ Default Value : 0x2F ( copper )
*/
static int AutoNeg = OPTION_UNSET ;
module_param ( AutoNeg , int , 0 ) ;
MODULE_PARM_DESC ( AutoNeg , " Advertised auto-negotiation setting " ) ;
# define PHY_ADVERTISE_10_HALF 0x0001
# define PHY_ADVERTISE_10_FULL 0x0002
# define PHY_ADVERTISE_100_HALF 0x0004
# define PHY_ADVERTISE_100_FULL 0x0008
# define PHY_ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */
# define PHY_ADVERTISE_1000_FULL 0x0020
# define PCH_AUTONEG_ADVERTISE_DEFAULT 0x2F
/**
* FlowControl - User Specified Flow Control Override
* @ Valid Range : 0 - 3
* - 0 : No Flow Control
* - 1 : Rx only , respond to PAUSE frames but do not generate them
* - 2 : Tx only , generate PAUSE frames but ignore them on receive
* - 3 : Full Flow Control Support
* @ Default Value : Read flow control settings from the EEPROM
*/
static int FlowControl = OPTION_UNSET ;
module_param ( FlowControl , int , 0 ) ;
MODULE_PARM_DESC ( FlowControl , " Flow Control setting " ) ;
/*
* XsumRX - Receive Checksum Offload Enable / Disable
* @ Valid Range : 0 , 1
* - 0 : disables all checksum offload
* - 1 : enables receive IP / TCP / UDP checksum offload
* @ Default Value : PCH_GBE_DEFAULT_RX_CSUM
*/
static int XsumRX = OPTION_UNSET ;
module_param ( XsumRX , int , 0 ) ;
MODULE_PARM_DESC ( XsumRX , " Disable or enable Receive Checksum offload " ) ;
# define PCH_GBE_DEFAULT_RX_CSUM true /* trueorfalse */
/*
* XsumTX - Transmit Checksum Offload Enable / Disable
* @ Valid Range : 0 , 1
* - 0 : disables all checksum offload
* - 1 : enables transmit IP / TCP / UDP checksum offload
* @ Default Value : PCH_GBE_DEFAULT_TX_CSUM
*/
static int XsumTX = OPTION_UNSET ;
module_param ( XsumTX , int , 0 ) ;
MODULE_PARM_DESC ( XsumTX , " Disable or enable Transmit Checksum offload " ) ;
# define PCH_GBE_DEFAULT_TX_CSUM true /* trueorfalse */
/**
* pch_gbe_option - Force the MAC ' s flow control settings
* @ hw : Pointer to the HW structure
* Returns
* 0 : Successful .
* Negative value : Failed .
*/
struct pch_gbe_option {
enum { enable_option , range_option , list_option } type ;
char * name ;
char * err ;
int def ;
union {
struct { /* range_option info */
int min ;
int max ;
} r ;
struct { /* list_option info */
int nr ;
const struct pch_gbe_opt_list { int i ; char * str ; } * p ;
} l ;
} arg ;
} ;
static const struct pch_gbe_opt_list speed_list [ ] = {
{ 0 , " " } ,
{ SPEED_10 , " " } ,
{ SPEED_100 , " " } ,
{ SPEED_1000 , " " }
} ;
static const struct pch_gbe_opt_list dplx_list [ ] = {
{ 0 , " " } ,
{ HALF_DUPLEX , " " } ,
{ FULL_DUPLEX , " " }
} ;
static const struct pch_gbe_opt_list an_list [ ] =
# define AA "AutoNeg advertising "
{ { 0x01 , AA " 10/HD " } ,
{ 0x02 , AA " 10/FD " } ,
{ 0x03 , AA " 10/FD, 10/HD " } ,
{ 0x04 , AA " 100/HD " } ,
{ 0x05 , AA " 100/HD, 10/HD " } ,
{ 0x06 , AA " 100/HD, 10/FD " } ,
{ 0x07 , AA " 100/HD, 10/FD, 10/HD " } ,
{ 0x08 , AA " 100/FD " } ,
{ 0x09 , AA " 100/FD, 10/HD " } ,
{ 0x0a , AA " 100/FD, 10/FD " } ,
{ 0x0b , AA " 100/FD, 10/FD, 10/HD " } ,
{ 0x0c , AA " 100/FD, 100/HD " } ,
{ 0x0d , AA " 100/FD, 100/HD, 10/HD " } ,
{ 0x0e , AA " 100/FD, 100/HD, 10/FD " } ,
{ 0x0f , AA " 100/FD, 100/HD, 10/FD, 10/HD " } ,
{ 0x20 , AA " 1000/FD " } ,
{ 0x21 , AA " 1000/FD, 10/HD " } ,
{ 0x22 , AA " 1000/FD, 10/FD " } ,
{ 0x23 , AA " 1000/FD, 10/FD, 10/HD " } ,
{ 0x24 , AA " 1000/FD, 100/HD " } ,
{ 0x25 , AA " 1000/FD, 100/HD, 10/HD " } ,
{ 0x26 , AA " 1000/FD, 100/HD, 10/FD " } ,
{ 0x27 , AA " 1000/FD, 100/HD, 10/FD, 10/HD " } ,
{ 0x28 , AA " 1000/FD, 100/FD " } ,
{ 0x29 , AA " 1000/FD, 100/FD, 10/HD " } ,
{ 0x2a , AA " 1000/FD, 100/FD, 10/FD " } ,
{ 0x2b , AA " 1000/FD, 100/FD, 10/FD, 10/HD " } ,
{ 0x2c , AA " 1000/FD, 100/FD, 100/HD " } ,
{ 0x2d , AA " 1000/FD, 100/FD, 100/HD, 10/HD " } ,
{ 0x2e , AA " 1000/FD, 100/FD, 100/HD, 10/FD " } ,
{ 0x2f , AA " 1000/FD, 100/FD, 100/HD, 10/FD, 10/HD " }
} ;
static const struct pch_gbe_opt_list fc_list [ ] = {
{ PCH_GBE_FC_NONE , " Flow Control Disabled " } ,
{ PCH_GBE_FC_RX_PAUSE , " Flow Control Receive Only " } ,
{ PCH_GBE_FC_TX_PAUSE , " Flow Control Transmit Only " } ,
{ PCH_GBE_FC_FULL , " Flow Control Enabled " }
} ;
/**
* pch_gbe_validate_option - Validate option
* @ value : value
* @ opt : option
* @ adapter : Board private structure
* Returns
* 0 : Successful .
* Negative value : Failed .
*/
static int pch_gbe_validate_option ( int * value ,
const struct pch_gbe_option * opt ,
struct pch_gbe_adapter * adapter )
{
if ( * value = = OPTION_UNSET ) {
* value = opt - > def ;
return 0 ;
}
switch ( opt - > type ) {
case enable_option :
switch ( * value ) {
case OPTION_ENABLED :
pr_debug ( " %s Enabled \n " , opt - > name ) ;
return 0 ;
case OPTION_DISABLED :
pr_debug ( " %s Disabled \n " , opt - > name ) ;
return 0 ;
}
break ;
case range_option :
if ( * value > = opt - > arg . r . min & & * value < = opt - > arg . r . max ) {
pr_debug ( " %s set to %i \n " , opt - > name , * value ) ;
return 0 ;
}
break ;
case list_option : {
int i ;
const struct pch_gbe_opt_list * ent ;
for ( i = 0 ; i < opt - > arg . l . nr ; i + + ) {
ent = & opt - > arg . l . p [ i ] ;
if ( * value = = ent - > i ) {
if ( ent - > str [ 0 ] ! = ' \0 ' )
pr_debug ( " %s \n " , ent - > str ) ;
return 0 ;
}
}
}
break ;
default :
BUG ( ) ;
}
pr_debug ( " Invalid %s value specified (%i) %s \n " ,
opt - > name , * value , opt - > err ) ;
* value = opt - > def ;
return - 1 ;
}
/**
* pch_gbe_check_copper_options - Range Checking for Link Options , Copper Version
* @ adapter : Board private structure
*/
static void pch_gbe_check_copper_options ( struct pch_gbe_adapter * adapter )
{
struct pch_gbe_hw * hw = & adapter - > hw ;
int speed , dplx ;
{ /* Speed */
static const struct pch_gbe_option opt = {
. type = list_option ,
. name = " Speed " ,
. err = " parameter ignored " ,
. def = 0 ,
. arg = { . l = { . nr = ( int ) ARRAY_SIZE ( speed_list ) ,
. p = speed_list } }
} ;
speed = Speed ;
pch_gbe_validate_option ( & speed , & opt , adapter ) ;
}
{ /* Duplex */
static const struct pch_gbe_option opt = {
. type = list_option ,
. name = " Duplex " ,
. err = " parameter ignored " ,
. def = 0 ,
. arg = { . l = { . nr = ( int ) ARRAY_SIZE ( dplx_list ) ,
. p = dplx_list } }
} ;
dplx = Duplex ;
pch_gbe_validate_option ( & dplx , & opt , adapter ) ;
}
{ /* Autoneg */
static const struct pch_gbe_option opt = {
. type = list_option ,
. name = " AutoNeg " ,
. err = " parameter ignored " ,
. def = PCH_AUTONEG_ADVERTISE_DEFAULT ,
. arg = { . l = { . nr = ( int ) ARRAY_SIZE ( an_list ) ,
. p = an_list } }
} ;
if ( speed | | dplx ) {
pr_debug ( " AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored \n " ) ;
hw - > phy . autoneg_advertised = opt . def ;
} else {
hw - > phy . autoneg_advertised = AutoNeg ;
pch_gbe_validate_option (
( int * ) ( & hw - > phy . autoneg_advertised ) ,
& opt , adapter ) ;
}
}
switch ( speed + dplx ) {
case 0 :
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
if ( ( speed | | dplx ) )
pr_debug ( " Speed and duplex autonegotiation enabled \n " ) ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case HALF_DUPLEX :
pr_debug ( " Half Duplex specified without Speed \n " ) ;
pr_debug ( " Using Autonegotiation at Half Duplex only \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
hw - > phy . autoneg_advertised = PHY_ADVERTISE_10_HALF |
PHY_ADVERTISE_100_HALF ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case FULL_DUPLEX :
pr_debug ( " Full Duplex specified without Speed \n " ) ;
pr_debug ( " Using Autonegotiation at Full Duplex only \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
hw - > phy . autoneg_advertised = PHY_ADVERTISE_10_FULL |
PHY_ADVERTISE_100_FULL |
PHY_ADVERTISE_1000_FULL ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_FULL ;
break ;
case SPEED_10 :
pr_debug ( " 10 Mbps Speed specified without Duplex \n " ) ;
pr_debug ( " Using Autonegotiation at 10 Mbps only \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
hw - > phy . autoneg_advertised = PHY_ADVERTISE_10_HALF |
PHY_ADVERTISE_10_FULL ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case SPEED_10 + HALF_DUPLEX :
pr_debug ( " Forcing to 10 Mbps Half Duplex \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 0 ;
hw - > phy . autoneg_advertised = 0 ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case SPEED_10 + FULL_DUPLEX :
pr_debug ( " Forcing to 10 Mbps Full Duplex \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 0 ;
hw - > phy . autoneg_advertised = 0 ;
hw - > mac . link_speed = SPEED_10 ;
hw - > mac . link_duplex = DUPLEX_FULL ;
break ;
case SPEED_100 :
pr_debug ( " 100 Mbps Speed specified without Duplex \n " ) ;
pr_debug ( " Using Autonegotiation at 100 Mbps only \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
hw - > phy . autoneg_advertised = PHY_ADVERTISE_100_HALF |
PHY_ADVERTISE_100_FULL ;
hw - > mac . link_speed = SPEED_100 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case SPEED_100 + HALF_DUPLEX :
pr_debug ( " Forcing to 100 Mbps Half Duplex \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 0 ;
hw - > phy . autoneg_advertised = 0 ;
hw - > mac . link_speed = SPEED_100 ;
hw - > mac . link_duplex = DUPLEX_HALF ;
break ;
case SPEED_100 + FULL_DUPLEX :
pr_debug ( " Forcing to 100 Mbps Full Duplex \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 0 ;
hw - > phy . autoneg_advertised = 0 ;
hw - > mac . link_speed = SPEED_100 ;
hw - > mac . link_duplex = DUPLEX_FULL ;
break ;
case SPEED_1000 :
pr_debug ( " 1000 Mbps Speed specified without Duplex \n " ) ;
goto full_duplex_only ;
case SPEED_1000 + HALF_DUPLEX :
pr_debug ( " Half Duplex is not supported at 1000 Mbps \n " ) ;
/* fall through */
case SPEED_1000 + FULL_DUPLEX :
full_duplex_only :
pr_debug ( " Using Autonegotiation at 1000 Mbps Full Duplex only \n " ) ;
hw - > mac . autoneg = hw - > mac . fc_autoneg = 1 ;
hw - > phy . autoneg_advertised = PHY_ADVERTISE_1000_FULL ;
hw - > mac . link_speed = SPEED_1000 ;
hw - > mac . link_duplex = DUPLEX_FULL ;
break ;
default :
BUG ( ) ;
}
}
/**
* pch_gbe_check_options - Range Checking for Command Line Parameters
* @ adapter : Board private structure
*/
void pch_gbe_check_options ( struct pch_gbe_adapter * adapter )
{
struct pch_gbe_hw * hw = & adapter - > hw ;
2011-04-19 01:56:12 +00:00
struct net_device * dev = adapter - > netdev ;
int val ;
2010-09-21 01:44:11 +00:00
{ /* Transmit Descriptor Count */
static const struct pch_gbe_option opt = {
. type = range_option ,
. name = " Transmit Descriptors " ,
. err = " using default of "
__MODULE_STRING ( PCH_GBE_DEFAULT_TXD ) ,
. def = PCH_GBE_DEFAULT_TXD ,
2010-11-29 06:18:07 +00:00
. arg = { . r = { . min = PCH_GBE_MIN_TXD ,
. max = PCH_GBE_MAX_TXD } }
2010-09-21 01:44:11 +00:00
} ;
struct pch_gbe_tx_ring * tx_ring = adapter - > tx_ring ;
tx_ring - > count = TxDescriptors ;
pch_gbe_validate_option ( & tx_ring - > count , & opt , adapter ) ;
tx_ring - > count = roundup ( tx_ring - > count ,
PCH_GBE_TX_DESC_MULTIPLE ) ;
}
{ /* Receive Descriptor Count */
static const struct pch_gbe_option opt = {
. type = range_option ,
. name = " Receive Descriptors " ,
. err = " using default of "
__MODULE_STRING ( PCH_GBE_DEFAULT_RXD ) ,
. def = PCH_GBE_DEFAULT_RXD ,
2010-11-29 06:18:07 +00:00
. arg = { . r = { . min = PCH_GBE_MIN_RXD ,
. max = PCH_GBE_MAX_RXD } }
2010-09-21 01:44:11 +00:00
} ;
struct pch_gbe_rx_ring * rx_ring = adapter - > rx_ring ;
rx_ring - > count = RxDescriptors ;
pch_gbe_validate_option ( & rx_ring - > count , & opt , adapter ) ;
rx_ring - > count = roundup ( rx_ring - > count ,
PCH_GBE_RX_DESC_MULTIPLE ) ;
}
{ /* Checksum Offload Enable/Disable */
static const struct pch_gbe_option opt = {
. type = enable_option ,
. name = " Checksum Offload " ,
. err = " defaulting to Enabled " ,
. def = PCH_GBE_DEFAULT_RX_CSUM
} ;
2011-04-19 01:56:12 +00:00
val = XsumRX ;
pch_gbe_validate_option ( & val , & opt , adapter ) ;
if ( ! val )
dev - > features & = ~ NETIF_F_RXCSUM ;
2010-09-21 01:44:11 +00:00
}
{ /* Checksum Offload Enable/Disable */
static const struct pch_gbe_option opt = {
. type = enable_option ,
. name = " Checksum Offload " ,
. err = " defaulting to Enabled " ,
. def = PCH_GBE_DEFAULT_TX_CSUM
} ;
2011-04-19 01:56:12 +00:00
val = XsumTX ;
pch_gbe_validate_option ( & val , & opt , adapter ) ;
if ( ! val )
dev - > features & = ~ NETIF_F_ALL_CSUM ;
2010-09-21 01:44:11 +00:00
}
{ /* Flow Control */
static const struct pch_gbe_option opt = {
. type = list_option ,
. name = " Flow Control " ,
. err = " reading default settings from EEPROM " ,
. def = PCH_GBE_FC_DEFAULT ,
. arg = { . l = { . nr = ( int ) ARRAY_SIZE ( fc_list ) ,
. p = fc_list } }
} ;
hw - > mac . fc = FlowControl ;
pch_gbe_validate_option ( ( int * ) ( & hw - > mac . fc ) ,
& opt , adapter ) ;
}
pch_gbe_check_copper_options ( adapter ) ;
}