Commit 8bef0b6b authored by Scott Feldman's avatar Scott Feldman Committed by Stephen Hemminger

[e1000] add ethtool flow control support

* Add ethtool flow control support
parent fdbeedb8
...@@ -180,6 +180,7 @@ struct e1000_adapter { ...@@ -180,6 +180,7 @@ struct e1000_adapter {
spinlock_t stats_lock; spinlock_t stats_lock;
atomic_t irq_sem; atomic_t irq_sem;
struct work_struct tx_timeout_task; struct work_struct tx_timeout_task;
uint8_t fc_autoneg;
struct timer_list blink_timer; struct timer_list blink_timer;
unsigned long led_status; unsigned long led_status;
......
...@@ -190,6 +190,55 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) ...@@ -190,6 +190,55 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
return 0; return 0;
} }
static int
e1000_ethtool_gpause(struct e1000_adapter *adapter,
struct ethtool_pauseparam *epause)
{
struct e1000_hw *hw = &adapter->hw;
epause->autoneg =
(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
if(hw->fc == e1000_fc_rx_pause)
epause->rx_pause = 1;
else if(hw->fc == e1000_fc_tx_pause)
epause->tx_pause = 1;
else if(hw->fc == e1000_fc_full) {
epause->rx_pause = 1;
epause->tx_pause = 1;
}
return 0;
}
static int
e1000_ethtool_spause(struct e1000_adapter *adapter,
struct ethtool_pauseparam *epause)
{
struct e1000_hw *hw = &adapter->hw;
adapter->fc_autoneg = epause->autoneg;
if(epause->rx_pause && epause->tx_pause)
hw->fc = e1000_fc_full;
else if(epause->rx_pause && !epause->tx_pause)
hw->fc = e1000_fc_rx_pause;
else if(!epause->rx_pause && epause->tx_pause)
hw->fc = e1000_fc_tx_pause;
else if(!epause->rx_pause && !epause->tx_pause)
hw->fc = e1000_fc_none;
hw->original_fc = hw->fc;
if(netif_running(adapter->netdev)) {
e1000_down(adapter);
e1000_up(adapter);
} else
e1000_reset(adapter);
return 0;
}
static void static void
e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter, e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
struct ethtool_drvinfo *drvinfo) struct ethtool_drvinfo *drvinfo)
...@@ -1449,6 +1498,19 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) ...@@ -1449,6 +1498,19 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
addr += offsetof(struct ethtool_eeprom, data); addr += offsetof(struct ethtool_eeprom, data);
return e1000_ethtool_seeprom(adapter, &eeprom, addr); return e1000_ethtool_seeprom(adapter, &eeprom, addr);
} }
case ETHTOOL_GPAUSEPARAM: {
struct ethtool_pauseparam epause = {ETHTOOL_GPAUSEPARAM};
e1000_ethtool_gpause(adapter, &epause);
if(copy_to_user(addr, &epause, sizeof(epause)))
return -EFAULT;
return 0;
}
case ETHTOOL_SPAUSEPARAM: {
struct ethtool_pauseparam epause;
if(copy_from_user(&epause, addr, sizeof(epause)))
return -EFAULT;
return e1000_ethtool_spause(adapter, &epause);
}
case ETHTOOL_GSTATS: { case ETHTOOL_GSTATS: {
struct { struct {
struct ethtool_stats eth_stats; struct ethtool_stats eth_stats;
......
...@@ -39,7 +39,6 @@ static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw); ...@@ -39,7 +39,6 @@ static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw);
static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw); static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw);
static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw); static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw);
static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw); static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw);
static int32_t e1000_force_mac_fc(struct e1000_hw *hw);
static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl); static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl); static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count); static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count);
...@@ -1629,7 +1628,7 @@ e1000_config_mac_to_phy(struct e1000_hw *hw) ...@@ -1629,7 +1628,7 @@ e1000_config_mac_to_phy(struct e1000_hw *hw)
* by the PHY rather than the MAC. Software must also configure these * by the PHY rather than the MAC. Software must also configure these
* bits when link is forced on a fiber connection. * bits when link is forced on a fiber connection.
*****************************************************************************/ *****************************************************************************/
static int32_t int32_t
e1000_force_mac_fc(struct e1000_hw *hw) e1000_force_mac_fc(struct e1000_hw *hw)
{ {
uint32_t ctrl; uint32_t ctrl;
...@@ -1682,7 +1681,7 @@ e1000_force_mac_fc(struct e1000_hw *hw) ...@@ -1682,7 +1681,7 @@ e1000_force_mac_fc(struct e1000_hw *hw)
ctrl &= (~E1000_CTRL_TFCE); ctrl &= (~E1000_CTRL_TFCE);
E1000_WRITE_REG(hw, CTRL, ctrl); E1000_WRITE_REG(hw, CTRL, ctrl);
return 0; return E1000_SUCCESS;
} }
/****************************************************************************** /******************************************************************************
......
...@@ -264,6 +264,7 @@ int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw); ...@@ -264,6 +264,7 @@ int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
int32_t e1000_check_for_link(struct e1000_hw *hw); int32_t e1000_check_for_link(struct e1000_hw *hw);
int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex); int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
int32_t e1000_wait_autoneg(struct e1000_hw *hw); int32_t e1000_wait_autoneg(struct e1000_hw *hw);
int32_t e1000_force_mac_fc(struct e1000_hw *hw);
/* PHY */ /* PHY */
int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data); int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
......
...@@ -140,7 +140,7 @@ E1000_PARAM(FlowControl, "Flow Control setting"); ...@@ -140,7 +140,7 @@ E1000_PARAM(FlowControl, "Flow Control setting");
* Valid Range: 0, 1 * Valid Range: 0, 1
* - 0 - disables all checksum offload * - 0 - disables all checksum offload
* - 1 - enables receive IP/TCP/UDP checksum offload * - 1 - enables receive IP/TCP/UDP checksum offload
* on 82543 based NICs * on 82543 and newer -based NICs
* *
* Default Value: 1 * Default Value: 1
*/ */
...@@ -602,7 +602,7 @@ e1000_check_copper_options(struct e1000_adapter *adapter) ...@@ -602,7 +602,7 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
switch (speed + dplx) { switch (speed + dplx) {
case 0: case 0:
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET) if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET)
printk(KERN_INFO printk(KERN_INFO
"Speed and duplex autonegotiation enabled\n"); "Speed and duplex autonegotiation enabled\n");
...@@ -610,14 +610,14 @@ e1000_check_copper_options(struct e1000_adapter *adapter) ...@@ -610,14 +610,14 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
case HALF_DUPLEX: case HALF_DUPLEX:
printk(KERN_INFO "Half Duplex specified without Speed\n"); printk(KERN_INFO "Half Duplex specified without Speed\n");
printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n"); printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_10_HALF | adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
ADVERTISE_100_HALF; ADVERTISE_100_HALF;
break; break;
case FULL_DUPLEX: case FULL_DUPLEX:
printk(KERN_INFO "Full Duplex specified without Speed\n"); printk(KERN_INFO "Full Duplex specified without Speed\n");
printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n"); printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_10_FULL | adapter->hw.autoneg_advertised = ADVERTISE_10_FULL |
ADVERTISE_100_FULL | ADVERTISE_100_FULL |
ADVERTISE_1000_FULL; ADVERTISE_1000_FULL;
...@@ -625,38 +625,38 @@ e1000_check_copper_options(struct e1000_adapter *adapter) ...@@ -625,38 +625,38 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
case SPEED_10: case SPEED_10:
printk(KERN_INFO "10 Mbps Speed specified without Duplex\n"); printk(KERN_INFO "10 Mbps Speed specified without Duplex\n");
printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n"); printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_10_HALF | adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
ADVERTISE_10_FULL; ADVERTISE_10_FULL;
break; break;
case SPEED_10 + HALF_DUPLEX: case SPEED_10 + HALF_DUPLEX:
printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n"); printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n");
adapter->hw.autoneg = 0; adapter->hw.autoneg = adapter->fc_autoneg = 0;
adapter->hw.forced_speed_duplex = e1000_10_half; adapter->hw.forced_speed_duplex = e1000_10_half;
adapter->hw.autoneg_advertised = 0; adapter->hw.autoneg_advertised = 0;
break; break;
case SPEED_10 + FULL_DUPLEX: case SPEED_10 + FULL_DUPLEX:
printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n"); printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n");
adapter->hw.autoneg = 0; adapter->hw.autoneg = adapter->fc_autoneg = 0;
adapter->hw.forced_speed_duplex = e1000_10_full; adapter->hw.forced_speed_duplex = e1000_10_full;
adapter->hw.autoneg_advertised = 0; adapter->hw.autoneg_advertised = 0;
break; break;
case SPEED_100: case SPEED_100:
printk(KERN_INFO "100 Mbps Speed specified without Duplex\n"); printk(KERN_INFO "100 Mbps Speed specified without Duplex\n");
printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n"); printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_100_HALF | adapter->hw.autoneg_advertised = ADVERTISE_100_HALF |
ADVERTISE_100_FULL; ADVERTISE_100_FULL;
break; break;
case SPEED_100 + HALF_DUPLEX: case SPEED_100 + HALF_DUPLEX:
printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n"); printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n");
adapter->hw.autoneg = 0; adapter->hw.autoneg = adapter->fc_autoneg = 0;
adapter->hw.forced_speed_duplex = e1000_100_half; adapter->hw.forced_speed_duplex = e1000_100_half;
adapter->hw.autoneg_advertised = 0; adapter->hw.autoneg_advertised = 0;
break; break;
case SPEED_100 + FULL_DUPLEX: case SPEED_100 + FULL_DUPLEX:
printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n"); printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n");
adapter->hw.autoneg = 0; adapter->hw.autoneg = adapter->fc_autoneg = 0;
adapter->hw.forced_speed_duplex = e1000_100_full; adapter->hw.forced_speed_duplex = e1000_100_full;
adapter->hw.autoneg_advertised = 0; adapter->hw.autoneg_advertised = 0;
break; break;
...@@ -664,20 +664,20 @@ e1000_check_copper_options(struct e1000_adapter *adapter) ...@@ -664,20 +664,20 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n"); printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n");
printk(KERN_INFO printk(KERN_INFO
"Using Autonegotiation at 1000 Mbps Full Duplex only\n"); "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
break; break;
case SPEED_1000 + HALF_DUPLEX: case SPEED_1000 + HALF_DUPLEX:
printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n"); printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n");
printk(KERN_INFO printk(KERN_INFO
"Using Autonegotiation at 1000 Mbps Full Duplex only\n"); "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
break; break;
case SPEED_1000 + FULL_DUPLEX: case SPEED_1000 + FULL_DUPLEX:
printk(KERN_INFO printk(KERN_INFO
"Using Autonegotiation at 1000 Mbps Full Duplex only\n"); "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
adapter->hw.autoneg = 1; adapter->hw.autoneg = adapter->fc_autoneg = 1;
adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment