| /* |
| * This file is provided under a CDDLv1 license. When using or |
| * redistributing this file, you may do so under this license. |
| * In redistributing this file this license must be included |
| * and no other modification of this header file is permitted. |
| * |
| * CDDL LICENSE SUMMARY |
| * |
| * Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved. |
| * |
| * The contents of this file are subject to the terms of Version |
| * 1.0 of the Common Development and Distribution License (the "License"). |
| * |
| * You should have received a copy of the License with this software. |
| * You can obtain a copy of the License at |
| * http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| */ |
| |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms of the CDDLv1. |
| */ |
| #include "e1000_api.h" |
| |
| #define E1000_FIFO_MULTIPLIER 0x80 |
| #define E1000_FIFO_HDR_SIZE 0x10 |
| #define E1000_FIFO_GRANULARITY 0x10 |
| #define E1000_FIFO_PAD_82547 0x3E0 |
| #define E1000_ERR_FIFO_WRAP 8 |
| |
| #define DSP_RESET_ENABLE 0x0 |
| #define DSP_RESET_DISABLE 0x2 |
| #define E1000_MAX_DSP_RESETS 10 |
| |
| #define E1000_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1)) |
| |
| |
| /* |
| * e1000_ttl_workaround_enabled_82541 - Returns current TTL workaround status |
| * @hw: pointer to the HW structure |
| * |
| * Returns the current status of the TTL workaround, as to whether the |
| * workaround is enabled or disabled. |
| */ |
| bool |
| e1000_ttl_workaround_enabled_82541(struct e1000_hw *hw) |
| { |
| struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541; |
| bool state = false; |
| |
| DEBUGFUNC("e1000_ttl_workaround_enabled_82541"); |
| |
| if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547)) |
| goto out; |
| |
| state = dev_spec->ttl_workaround; |
| |
| out: |
| return (state); |
| } |
| |
| /* |
| * e1000_fifo_workaround_82547 - Workaround for Tx fifo failure |
| * @hw: pointer to the HW structure |
| * @length: length of next outgoing frame |
| * |
| * Returns: E1000_ERR_FIFO_WRAP if the next packet cannot be transmitted yet |
| * E1000_SUCCESS if the next packet can be transmitted |
| * |
| * Workaround for the 82547 Tx fifo failure. |
| */ |
| s32 |
| e1000_fifo_workaround_82547(struct e1000_hw *hw, u16 length) |
| { |
| struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541; |
| u32 tctl; |
| s32 ret_val = E1000_SUCCESS; |
| u16 fifo_pkt_len; |
| |
| DEBUGFUNC("e1000_fifo_workaround_82547"); |
| |
| if (hw->mac.type != e1000_82547) |
| goto out; |
| |
| /* |
| * Get the length as seen by the FIFO of the next real |
| * packet to be transmitted. |
| */ |
| fifo_pkt_len = E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE, |
| E1000_FIFO_GRANULARITY); |
| |
| if (fifo_pkt_len <= (E1000_FIFO_PAD_82547 + E1000_FIFO_HDR_SIZE)) |
| goto out; |
| |
| if ((dev_spec->tx_fifo_head + fifo_pkt_len) < |
| (dev_spec->tx_fifo_size + E1000_FIFO_PAD_82547)) |
| goto out; |
| |
| if (E1000_READ_REG(hw, E1000_TDT(0)) != |
| E1000_READ_REG(hw, E1000_TDH(0))) { |
| ret_val = -E1000_ERR_FIFO_WRAP; |
| goto out; |
| } |
| |
| if (E1000_READ_REG(hw, E1000_TDFT) != E1000_READ_REG(hw, E1000_TDFH)) { |
| ret_val = -E1000_ERR_FIFO_WRAP; |
| goto out; |
| } |
| |
| if (E1000_READ_REG(hw, E1000_TDFTS) != |
| E1000_READ_REG(hw, E1000_TDFHS)) { |
| ret_val = -E1000_ERR_FIFO_WRAP; |
| goto out; |
| } |
| |
| /* Disable the tx unit to avoid further pointer movement */ |
| tctl = E1000_READ_REG(hw, E1000_TCTL); |
| E1000_WRITE_REG(hw, E1000_TCTL, tctl & ~E1000_TCTL_EN); |
| |
| /* Reset the fifo pointers. */ |
| E1000_WRITE_REG(hw, E1000_TDFT, dev_spec->tx_fifo_start); |
| E1000_WRITE_REG(hw, E1000_TDFH, dev_spec->tx_fifo_start); |
| E1000_WRITE_REG(hw, E1000_TDFTS, dev_spec->tx_fifo_start); |
| E1000_WRITE_REG(hw, E1000_TDFHS, dev_spec->tx_fifo_start); |
| |
| /* Re-enabling tx unit */ |
| E1000_WRITE_REG(hw, E1000_TCTL, tctl); |
| E1000_WRITE_FLUSH(hw); |
| |
| dev_spec->tx_fifo_head = 0; |
| |
| out: |
| return (ret_val); |
| } |
| |
| /* |
| * e1000_update_tx_fifo_head - Update Tx fifo head pointer |
| * @hw: pointer to the HW structure |
| * @length: length of next outgoing frame |
| * |
| * Updates the SW calculated Tx FIFO head pointer. |
| */ |
| void |
| e1000_update_tx_fifo_head_82547(struct e1000_hw *hw, u32 length) |
| { |
| struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541; |
| |
| DEBUGFUNC("e1000_update_tx_fifo_head_82547"); |
| |
| if (hw->mac.type != e1000_82547) |
| return; |
| |
| dev_spec->tx_fifo_head += E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE, |
| E1000_FIFO_GRANULARITY); |
| |
| if (dev_spec->tx_fifo_head > dev_spec->tx_fifo_size) |
| dev_spec->tx_fifo_head -= dev_spec->tx_fifo_size; |
| } |
| |
| /* |
| * e1000_set_ttl_workaround_state_82541 - Enable/Disables TTL workaround |
| * @hw: pointer to the HW structure |
| * @state: boolean to enable/disable TTL workaround |
| * |
| * For 82541 or 82547 only silicon, allows the driver to enable/disable the |
| * TTL workaround. |
| */ |
| void |
| e1000_set_ttl_workaround_state_82541(struct e1000_hw *hw, bool state) |
| { |
| struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541; |
| |
| DEBUGFUNC("e1000_set_ttl_workaround_state_82541"); |
| |
| if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547)) |
| return; |
| |
| dev_spec->ttl_workaround = state; |
| } |
| |
| /* |
| * e1000_igp_ttl_workaround_82547 - Workaround for long TTL on 100HD hubs |
| * @hw: pointer to the HW structure |
| * |
| * Returns: E1000_ERR_PHY if fail to read/write the PHY |
| * E1000_SUCCESS in any other case |
| * |
| * This function, specific to 82547 hardware only, needs to be called every |
| * second. It checks if a parallel detect fault has occurred. If a fault |
| * occurred, disable/enable the DSP reset mechanism up to 5 times (once per |
| * second). If link is established, stop the workaround and ensure the DSP |
| * reset is enabled. |
| */ |
| s32 |
| e1000_igp_ttl_workaround_82547(struct e1000_hw *hw) |
| { |
| struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541; |
| s32 ret_val = E1000_SUCCESS; |
| u16 phy_data = 0; |
| u16 dsp_value = DSP_RESET_ENABLE; |
| bool link; |
| |
| DEBUGFUNC("e1000_igp_ttl_workaround_82547"); |
| |
| /* The workaround needed only for B-0 silicon HW */ |
| if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547)) |
| goto out; |
| |
| if (!(e1000_ttl_workaround_enabled_82541(hw))) |
| goto out; |
| |
| /* Check for link first */ |
| ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); |
| if (ret_val) |
| goto out; |
| |
| if (link) { |
| /* |
| * If link is established during the workaround, |
| * the DSP mechanism must be enabled. |
| */ |
| if (dev_spec->dsp_reset_counter) { |
| dev_spec->dsp_reset_counter = 0; |
| dsp_value = DSP_RESET_ENABLE; |
| } else { |
| ret_val = E1000_SUCCESS; |
| goto out; |
| } |
| } else { |
| if (dev_spec->dsp_reset_counter == 0) { |
| /* |
| * Workaround not activated, |
| * check if it needs activation |
| */ |
| ret_val = hw->phy.ops.read_reg(hw, |
| PHY_AUTONEG_EXP, |
| &phy_data); |
| if (ret_val) |
| goto out; |
| /* |
| * Activate the workaround if there was a |
| * parallel detect fault |
| */ |
| if (phy_data & NWAY_ER_PAR_DETECT_FAULT) { |
| dev_spec->dsp_reset_counter++; |
| } else { |
| ret_val = E1000_SUCCESS; |
| goto out; |
| } |
| } |
| |
| /* After 5 times, stop the workaround */ |
| if (dev_spec->dsp_reset_counter > E1000_MAX_DSP_RESETS) { |
| dev_spec->dsp_reset_counter = 0; |
| dsp_value = DSP_RESET_ENABLE; |
| } else { |
| if (dev_spec->dsp_reset_counter) { |
| dsp_value = (dev_spec->dsp_reset_counter & 1) |
| ? DSP_RESET_DISABLE |
| : DSP_RESET_ENABLE; |
| dev_spec->dsp_reset_counter++; |
| } |
| } |
| } |
| |
| ret_val = |
| hw->phy.ops.write_reg(hw, IGP01E1000_PHY_DSP_RESET, dsp_value); |
| |
| out: |
| return (ret_val); |
| } |