dect
/
linux-2.6
Archived
13
0
Fork 0

igb: VFTA Table Fix for i350 devices

Due to a hardware problem, writes to the VFTA register can
theoretically fail. Although the likelihood of this is very low.
This patch adds a shadow vfta in the adapter struct for reading
and adds new write functions for these devices to work around the problem.

Signed-off-by: Carolyn Wyborny <carolyn.wyborny@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Carolyn Wyborny 2011-10-14 00:13:49 +00:00 committed by Jeff Kirsher
parent b6e0c419f0
commit 1128c756be
5 changed files with 65 additions and 4 deletions

View File

@ -1051,7 +1051,10 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
/* Disabling VLAN filtering */
hw_dbg("Initializing the IEEE VLAN\n");
igb_clear_vfta(hw);
if (hw->mac.type == e1000_i350)
igb_clear_vfta_i350(hw);
else
igb_clear_vfta(hw);
/* Setup the receive address */
igb_init_rx_addrs(hw, rar_count);

View File

@ -117,6 +117,50 @@ static void igb_write_vfta(struct e1000_hw *hw, u32 offset, u32 value)
wrfl();
}
/* Due to a hw errata, if the host tries to configure the VFTA register
* while performing queries from the BMC or DMA, then the VFTA in some
* cases won't be written.
*/
/**
* igb_clear_vfta_i350 - Clear VLAN filter table
* @hw: pointer to the HW structure
*
* Clears the register array which contains the VLAN filter table by
* setting all the values to 0.
**/
void igb_clear_vfta_i350(struct e1000_hw *hw)
{
u32 offset;
int i;
for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
for (i = 0; i < 10; i++)
array_wr32(E1000_VFTA, offset, 0);
wrfl();
}
}
/**
* igb_write_vfta_i350 - Write value to VLAN filter table
* @hw: pointer to the HW structure
* @offset: register offset in VLAN filter table
* @value: register value written to VLAN filter table
*
* Writes value at the given offset in the register array which stores
* the VLAN filter table.
**/
void igb_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value)
{
int i;
for (i = 0; i < 10; i++)
array_wr32(E1000_VFTA, offset, value);
wrfl();
}
/**
* igb_init_rx_addrs - Initialize receive address's
* @hw: pointer to the HW structure
@ -155,9 +199,12 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
{
u32 index = (vid >> E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK;
u32 mask = 1 << (vid & E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
u32 vfta = array_rd32(E1000_VFTA, index);
u32 vfta;
struct igb_adapter *adapter = hw->back;
s32 ret_val = 0;
vfta = adapter->shadow_vfta[index];
/* bit was set/cleared before we started */
if ((!!(vfta & mask)) == add) {
ret_val = -E1000_ERR_CONFIG;
@ -167,8 +214,11 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add)
else
vfta &= ~mask;
}
igb_write_vfta(hw, index, vfta);
if (hw->mac.type == e1000_i350)
igb_write_vfta_i350(hw, index, vfta);
else
igb_write_vfta(hw, index, vfta);
adapter->shadow_vfta[index] = vfta;
return ret_val;
}

View File

@ -60,6 +60,7 @@ s32 igb_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg,
void igb_clear_hw_cntrs_base(struct e1000_hw *hw);
void igb_clear_vfta(struct e1000_hw *hw);
void igb_clear_vfta_i350(struct e1000_hw *hw);
s32 igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add);
void igb_config_collision_dist(struct e1000_hw *hw);
void igb_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);

View File

@ -363,6 +363,7 @@ struct igb_adapter {
u32 rss_queues;
u32 wvbr;
int node;
u32 *shadow_vfta;
};
#define IGB_FLAG_HAS_MSI (1 << 0)

View File

@ -2206,6 +2206,7 @@ static void __devexit igb_remove(struct pci_dev *pdev)
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
kfree(adapter->shadow_vfta);
free_netdev(netdev);
pci_disable_pcie_error_reporting(pdev);
@ -2438,6 +2439,11 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter)
((adapter->rss_queues > 1) && (adapter->vfs_allocated_count > 6)))
adapter->flags |= IGB_FLAG_QUEUE_PAIRS;
/* Setup and initialize a copy of the hw vlan table array */
adapter->shadow_vfta = kzalloc(sizeof(u32) *
E1000_VLAN_FILTER_TBL_SIZE,
GFP_ATOMIC);
/* This call may decrease the number of queues */
if (igb_init_interrupt_scheme(adapter)) {
dev_err(&pdev->dev, "Unable to allocate memory for queues\n");