diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_mac.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_mac.c | 104 | 
1 files changed, 104 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c index 5eeb4c8caf4a..12aa6b5fcb5d 100644 --- a/drivers/net/ethernet/intel/igc/igc_mac.c +++ b/drivers/net/ethernet/intel/igc/igc_mac.c @@ -784,3 +784,107 @@ bool igc_enable_mng_pass_thru(struct igc_hw *hw)  out:  	return ret_val;  } + +/** + *  igc_hash_mc_addr - Generate a multicast hash value + *  @hw: pointer to the HW structure + *  @mc_addr: pointer to a multicast address + * + *  Generates a multicast address hash value which is used to determine + *  the multicast filter table array address and new table value.  See + *  igc_mta_set() + **/ +static u32 igc_hash_mc_addr(struct igc_hw *hw, u8 *mc_addr) +{ +	u32 hash_value, hash_mask; +	u8 bit_shift = 0; + +	/* Register count multiplied by bits per register */ +	hash_mask = (hw->mac.mta_reg_count * 32) - 1; + +	/* For a mc_filter_type of 0, bit_shift is the number of left-shifts +	 * where 0xFF would still fall within the hash mask. +	 */ +	while (hash_mask >> bit_shift != 0xFF) +		bit_shift++; + +	/* The portion of the address that is used for the hash table +	 * is determined by the mc_filter_type setting. +	 * The algorithm is such that there is a total of 8 bits of shifting. +	 * The bit_shift for a mc_filter_type of 0 represents the number of +	 * left-shifts where the MSB of mc_addr[5] would still fall within +	 * the hash_mask.  Case 0 does this exactly.  Since there are a total +	 * of 8 bits of shifting, then mc_addr[4] will shift right the +	 * remaining number of bits. Thus 8 - bit_shift.  The rest of the +	 * cases are a variation of this algorithm...essentially raising the +	 * number of bits to shift mc_addr[5] left, while still keeping the +	 * 8-bit shifting total. +	 * +	 * For example, given the following Destination MAC Address and an +	 * MTA register count of 128 (thus a 4096-bit vector and 0xFFF mask), +	 * we can see that the bit_shift for case 0 is 4.  These are the hash +	 * values resulting from each mc_filter_type... +	 * [0] [1] [2] [3] [4] [5] +	 * 01  AA  00  12  34  56 +	 * LSB                 MSB +	 * +	 * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563 +	 * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6 +	 * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163 +	 * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634 +	 */ +	switch (hw->mac.mc_filter_type) { +	default: +	case 0: +		break; +	case 1: +		bit_shift += 1; +		break; +	case 2: +		bit_shift += 2; +		break; +	case 3: +		bit_shift += 4; +		break; +	} + +	hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) | +				  (((u16)mc_addr[5]) << bit_shift))); + +	return hash_value; +} + +/** + *  igc_update_mc_addr_list - Update Multicast addresses + *  @hw: pointer to the HW structure + *  @mc_addr_list: array of multicast addresses to program + *  @mc_addr_count: number of multicast addresses to program + * + *  Updates entire Multicast Table Array. + *  The caller must have a packed mc_addr_list of multicast addresses. + **/ +void igc_update_mc_addr_list(struct igc_hw *hw, +			     u8 *mc_addr_list, u32 mc_addr_count) +{ +	u32 hash_value, hash_bit, hash_reg; +	int i; + +	/* clear mta_shadow */ +	memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + +	/* update mta_shadow from mc_addr_list */ +	for (i = 0; (u32)i < mc_addr_count; i++) { +		hash_value = igc_hash_mc_addr(hw, mc_addr_list); + +		hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); +		hash_bit = hash_value & 0x1F; + +		hw->mac.mta_shadow[hash_reg] |= BIT(hash_bit); +		mc_addr_list += ETH_ALEN; +	} + +	/* replace the entire MTA table */ +	for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) +		array_wr32(IGC_MTA, i, hw->mac.mta_shadow[i]); +	wrfl(); +}  |