175eba5b6SRobert Mustacchi /*
275eba5b6SRobert Mustacchi  * This file is provided under a CDDLv1 license.  When using or
375eba5b6SRobert Mustacchi  * redistributing this file, you may do so under this license.
475eba5b6SRobert Mustacchi  * In redistributing this file this license must be included
575eba5b6SRobert Mustacchi  * and no other modification of this header file is permitted.
675eba5b6SRobert Mustacchi  *
775eba5b6SRobert Mustacchi  * CDDL LICENSE SUMMARY
875eba5b6SRobert Mustacchi  *
975eba5b6SRobert Mustacchi  * Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved.
1075eba5b6SRobert Mustacchi  *
1175eba5b6SRobert Mustacchi  * The contents of this file are subject to the terms of Version
1275eba5b6SRobert Mustacchi  * 1.0 of the Common Development and Distribution License (the "License").
1375eba5b6SRobert Mustacchi  *
1475eba5b6SRobert Mustacchi  * You should have received a copy of the License with this software.
1575eba5b6SRobert Mustacchi  * You can obtain a copy of the License at
1675eba5b6SRobert Mustacchi  *	http://www.opensolaris.org/os/licensing.
1775eba5b6SRobert Mustacchi  * See the License for the specific language governing permissions
1875eba5b6SRobert Mustacchi  * and limitations under the License.
1975eba5b6SRobert Mustacchi  */
2075eba5b6SRobert Mustacchi 
2175eba5b6SRobert Mustacchi /*
2275eba5b6SRobert Mustacchi  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2375eba5b6SRobert Mustacchi  * Use is subject to license terms of the CDDLv1.
2475eba5b6SRobert Mustacchi  */
2575eba5b6SRobert Mustacchi #include "e1000_api.h"
2675eba5b6SRobert Mustacchi 
27*49b78600SRobert Mustacchi #define	E1000_FIFO_MULTIPLIER			0x80
28*49b78600SRobert Mustacchi #define	E1000_FIFO_HDR_SIZE			0x10
29*49b78600SRobert Mustacchi #define	E1000_FIFO_GRANULARITY			0x10
30*49b78600SRobert Mustacchi #define	E1000_FIFO_PAD_82547			0x3E0
31*49b78600SRobert Mustacchi #define	E1000_ERR_FIFO_WRAP			8
32*49b78600SRobert Mustacchi 
33*49b78600SRobert Mustacchi #define	DSP_RESET_ENABLE			0x0
34*49b78600SRobert Mustacchi #define	DSP_RESET_DISABLE			0x2
35*49b78600SRobert Mustacchi #define	E1000_MAX_DSP_RESETS			10
36*49b78600SRobert Mustacchi 
37*49b78600SRobert Mustacchi #define	E1000_ROUNDUP(size, unit)	(((size) + (unit) - 1) & ~((unit) - 1))
38*49b78600SRobert Mustacchi 
39*49b78600SRobert Mustacchi 
4075eba5b6SRobert Mustacchi /*
4175eba5b6SRobert Mustacchi  * e1000_ttl_workaround_enabled_82541 - Returns current TTL workaround status
4275eba5b6SRobert Mustacchi  * @hw: pointer to the HW structure
4375eba5b6SRobert Mustacchi  *
4475eba5b6SRobert Mustacchi  * Returns the current status of the TTL workaround, as to whether the
4575eba5b6SRobert Mustacchi  * workaround is enabled or disabled.
4675eba5b6SRobert Mustacchi  */
4775eba5b6SRobert Mustacchi bool
e1000_ttl_workaround_enabled_82541(struct e1000_hw * hw)4875eba5b6SRobert Mustacchi e1000_ttl_workaround_enabled_82541(struct e1000_hw *hw)
4975eba5b6SRobert Mustacchi {
5075eba5b6SRobert Mustacchi 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
5175eba5b6SRobert Mustacchi 	bool state = false;
5275eba5b6SRobert Mustacchi 
5375eba5b6SRobert Mustacchi 	DEBUGFUNC("e1000_ttl_workaround_enabled_82541");
5475eba5b6SRobert Mustacchi 
5575eba5b6SRobert Mustacchi 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
5675eba5b6SRobert Mustacchi 		goto out;
5775eba5b6SRobert Mustacchi 
5875eba5b6SRobert Mustacchi 	state = dev_spec->ttl_workaround;
5975eba5b6SRobert Mustacchi 
6075eba5b6SRobert Mustacchi out:
6175eba5b6SRobert Mustacchi 	return (state);
6275eba5b6SRobert Mustacchi }
6375eba5b6SRobert Mustacchi 
6475eba5b6SRobert Mustacchi /*
6575eba5b6SRobert Mustacchi  * e1000_fifo_workaround_82547 - Workaround for Tx fifo failure
6675eba5b6SRobert Mustacchi  * @hw: pointer to the HW structure
6775eba5b6SRobert Mustacchi  * @length: length of next outgoing frame
6875eba5b6SRobert Mustacchi  *
6975eba5b6SRobert Mustacchi  * Returns: E1000_ERR_FIFO_WRAP if the next packet cannot be transmitted yet
7075eba5b6SRobert Mustacchi  *	E1000_SUCCESS if the next packet can be transmitted
7175eba5b6SRobert Mustacchi  *
7275eba5b6SRobert Mustacchi  * Workaround for the 82547 Tx fifo failure.
7375eba5b6SRobert Mustacchi  */
7475eba5b6SRobert Mustacchi s32
e1000_fifo_workaround_82547(struct e1000_hw * hw,u16 length)7575eba5b6SRobert Mustacchi e1000_fifo_workaround_82547(struct e1000_hw *hw, u16 length)
7675eba5b6SRobert Mustacchi {
7775eba5b6SRobert Mustacchi 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
7875eba5b6SRobert Mustacchi 	u32 tctl;
7975eba5b6SRobert Mustacchi 	s32 ret_val = E1000_SUCCESS;
8075eba5b6SRobert Mustacchi 	u16 fifo_pkt_len;
8175eba5b6SRobert Mustacchi 
8275eba5b6SRobert Mustacchi 	DEBUGFUNC("e1000_fifo_workaround_82547");
8375eba5b6SRobert Mustacchi 
8475eba5b6SRobert Mustacchi 	if (hw->mac.type != e1000_82547)
8575eba5b6SRobert Mustacchi 		goto out;
8675eba5b6SRobert Mustacchi 
8775eba5b6SRobert Mustacchi 	/*
8875eba5b6SRobert Mustacchi 	 * Get the length as seen by the FIFO of the next real
8975eba5b6SRobert Mustacchi 	 * packet to be transmitted.
9075eba5b6SRobert Mustacchi 	 */
9175eba5b6SRobert Mustacchi 	fifo_pkt_len = E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
9275eba5b6SRobert Mustacchi 	    E1000_FIFO_GRANULARITY);
9375eba5b6SRobert Mustacchi 
9475eba5b6SRobert Mustacchi 	if (fifo_pkt_len <= (E1000_FIFO_PAD_82547 + E1000_FIFO_HDR_SIZE))
9575eba5b6SRobert Mustacchi 		goto out;
9675eba5b6SRobert Mustacchi 
9775eba5b6SRobert Mustacchi 	if ((dev_spec->tx_fifo_head + fifo_pkt_len) <
9875eba5b6SRobert Mustacchi 	    (dev_spec->tx_fifo_size + E1000_FIFO_PAD_82547))
9975eba5b6SRobert Mustacchi 		goto out;
10075eba5b6SRobert Mustacchi 
10175eba5b6SRobert Mustacchi 	if (E1000_READ_REG(hw, E1000_TDT(0)) !=
10275eba5b6SRobert Mustacchi 	    E1000_READ_REG(hw, E1000_TDH(0))) {
10375eba5b6SRobert Mustacchi 		ret_val = -E1000_ERR_FIFO_WRAP;
10475eba5b6SRobert Mustacchi 		goto out;
10575eba5b6SRobert Mustacchi 	}
10675eba5b6SRobert Mustacchi 
10775eba5b6SRobert Mustacchi 	if (E1000_READ_REG(hw, E1000_TDFT) != E1000_READ_REG(hw, E1000_TDFH)) {
10875eba5b6SRobert Mustacchi 		ret_val = -E1000_ERR_FIFO_WRAP;
10975eba5b6SRobert Mustacchi 		goto out;
11075eba5b6SRobert Mustacchi 	}
11175eba5b6SRobert Mustacchi 
11275eba5b6SRobert Mustacchi 	if (E1000_READ_REG(hw, E1000_TDFTS) !=
11375eba5b6SRobert Mustacchi 	    E1000_READ_REG(hw, E1000_TDFHS)) {
11475eba5b6SRobert Mustacchi 		ret_val = -E1000_ERR_FIFO_WRAP;
11575eba5b6SRobert Mustacchi 		goto out;
11675eba5b6SRobert Mustacchi 	}
11775eba5b6SRobert Mustacchi 
11875eba5b6SRobert Mustacchi 	/* Disable the tx unit to avoid further pointer movement */
11975eba5b6SRobert Mustacchi 	tctl = E1000_READ_REG(hw, E1000_TCTL);
12075eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TCTL, tctl & ~E1000_TCTL_EN);
12175eba5b6SRobert Mustacchi 
12275eba5b6SRobert Mustacchi 	/* Reset the fifo pointers. */
12375eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TDFT, dev_spec->tx_fifo_start);
12475eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TDFH, dev_spec->tx_fifo_start);
12575eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TDFTS, dev_spec->tx_fifo_start);
12675eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TDFHS, dev_spec->tx_fifo_start);
12775eba5b6SRobert Mustacchi 
12875eba5b6SRobert Mustacchi 	/* Re-enabling tx unit */
12975eba5b6SRobert Mustacchi 	E1000_WRITE_REG(hw, E1000_TCTL, tctl);
13075eba5b6SRobert Mustacchi 	E1000_WRITE_FLUSH(hw);
13175eba5b6SRobert Mustacchi 
13275eba5b6SRobert Mustacchi 	dev_spec->tx_fifo_head = 0;
13375eba5b6SRobert Mustacchi 
13475eba5b6SRobert Mustacchi out:
13575eba5b6SRobert Mustacchi 	return (ret_val);
13675eba5b6SRobert Mustacchi }
13775eba5b6SRobert Mustacchi 
13875eba5b6SRobert Mustacchi /*
13975eba5b6SRobert Mustacchi  * e1000_update_tx_fifo_head - Update Tx fifo head pointer
14075eba5b6SRobert Mustacchi  * @hw: pointer to the HW structure
14175eba5b6SRobert Mustacchi  * @length: length of next outgoing frame
14275eba5b6SRobert Mustacchi  *
14375eba5b6SRobert Mustacchi  * Updates the SW calculated Tx FIFO head pointer.
14475eba5b6SRobert Mustacchi  */
14575eba5b6SRobert Mustacchi void
e1000_update_tx_fifo_head_82547(struct e1000_hw * hw,u32 length)14675eba5b6SRobert Mustacchi e1000_update_tx_fifo_head_82547(struct e1000_hw *hw, u32 length)
14775eba5b6SRobert Mustacchi {
14875eba5b6SRobert Mustacchi 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
14975eba5b6SRobert Mustacchi 
15075eba5b6SRobert Mustacchi 	DEBUGFUNC("e1000_update_tx_fifo_head_82547");
15175eba5b6SRobert Mustacchi 
15275eba5b6SRobert Mustacchi 	if (hw->mac.type != e1000_82547)
15375eba5b6SRobert Mustacchi 		return;
15475eba5b6SRobert Mustacchi 
15575eba5b6SRobert Mustacchi 	dev_spec->tx_fifo_head += E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
15675eba5b6SRobert Mustacchi 	    E1000_FIFO_GRANULARITY);
15775eba5b6SRobert Mustacchi 
15875eba5b6SRobert Mustacchi 	if (dev_spec->tx_fifo_head > dev_spec->tx_fifo_size)
15975eba5b6SRobert Mustacchi 		dev_spec->tx_fifo_head -= dev_spec->tx_fifo_size;
16075eba5b6SRobert Mustacchi }
16175eba5b6SRobert Mustacchi 
16275eba5b6SRobert Mustacchi /*
16375eba5b6SRobert Mustacchi  * e1000_set_ttl_workaround_state_82541 - Enable/Disables TTL workaround
16475eba5b6SRobert Mustacchi  * @hw: pointer to the HW structure
16575eba5b6SRobert Mustacchi  * @state: boolean to enable/disable TTL workaround
16675eba5b6SRobert Mustacchi  *
16775eba5b6SRobert Mustacchi  * For 82541 or 82547 only silicon, allows the driver to enable/disable the
16875eba5b6SRobert Mustacchi  * TTL workaround.
16975eba5b6SRobert Mustacchi  */
17075eba5b6SRobert Mustacchi void
e1000_set_ttl_workaround_state_82541(struct e1000_hw * hw,bool state)17175eba5b6SRobert Mustacchi e1000_set_ttl_workaround_state_82541(struct e1000_hw *hw, bool state)
17275eba5b6SRobert Mustacchi {
17375eba5b6SRobert Mustacchi 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
17475eba5b6SRobert Mustacchi 
17575eba5b6SRobert Mustacchi 	DEBUGFUNC("e1000_set_ttl_workaround_state_82541");
17675eba5b6SRobert Mustacchi 
17775eba5b6SRobert Mustacchi 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
17875eba5b6SRobert Mustacchi 		return;
17975eba5b6SRobert Mustacchi 
18075eba5b6SRobert Mustacchi 	dev_spec->ttl_workaround = state;
18175eba5b6SRobert Mustacchi }
18275eba5b6SRobert Mustacchi 
18375eba5b6SRobert Mustacchi /*
18475eba5b6SRobert Mustacchi  * e1000_igp_ttl_workaround_82547 - Workaround for long TTL on 100HD hubs
18575eba5b6SRobert Mustacchi  * @hw: pointer to the HW structure
18675eba5b6SRobert Mustacchi  *
18775eba5b6SRobert Mustacchi  * Returns: E1000_ERR_PHY if fail to read/write the PHY
18875eba5b6SRobert Mustacchi  *          E1000_SUCCESS in any other case
18975eba5b6SRobert Mustacchi  *
19075eba5b6SRobert Mustacchi  * This function, specific to 82547 hardware only, needs to be called every
19175eba5b6SRobert Mustacchi  * second.  It checks if a parallel detect fault has occurred.  If a fault
19275eba5b6SRobert Mustacchi  * occurred, disable/enable the DSP reset mechanism up to 5 times (once per
19375eba5b6SRobert Mustacchi  * second).  If link is established, stop the workaround and ensure the DSP
19475eba5b6SRobert Mustacchi  * reset is enabled.
19575eba5b6SRobert Mustacchi  */
19675eba5b6SRobert Mustacchi s32
e1000_igp_ttl_workaround_82547(struct e1000_hw * hw)19775eba5b6SRobert Mustacchi e1000_igp_ttl_workaround_82547(struct e1000_hw *hw)
19875eba5b6SRobert Mustacchi {
19975eba5b6SRobert Mustacchi 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
20075eba5b6SRobert Mustacchi 	s32 ret_val = E1000_SUCCESS;
20175eba5b6SRobert Mustacchi 	u16 phy_data = 0;
20275eba5b6SRobert Mustacchi 	u16 dsp_value = DSP_RESET_ENABLE;
20375eba5b6SRobert Mustacchi 	bool link;
20475eba5b6SRobert Mustacchi 
20575eba5b6SRobert Mustacchi 	DEBUGFUNC("e1000_igp_ttl_workaround_82547");
20675eba5b6SRobert Mustacchi 
20775eba5b6SRobert Mustacchi 	/* The workaround needed only for B-0 silicon HW */
20875eba5b6SRobert Mustacchi 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
20975eba5b6SRobert Mustacchi 		goto out;
21075eba5b6SRobert Mustacchi 
21175eba5b6SRobert Mustacchi 	if (!(e1000_ttl_workaround_enabled_82541(hw)))
21275eba5b6SRobert Mustacchi 		goto out;
21375eba5b6SRobert Mustacchi 
21475eba5b6SRobert Mustacchi 	/* Check for link first */
21575eba5b6SRobert Mustacchi 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
21675eba5b6SRobert Mustacchi 	if (ret_val)
21775eba5b6SRobert Mustacchi 		goto out;
21875eba5b6SRobert Mustacchi 
21975eba5b6SRobert Mustacchi 	if (link) {
22075eba5b6SRobert Mustacchi 		/*
22175eba5b6SRobert Mustacchi 		 * If link is established during the workaround,
22275eba5b6SRobert Mustacchi 		 * the DSP mechanism must be enabled.
22375eba5b6SRobert Mustacchi 		 */
22475eba5b6SRobert Mustacchi 		if (dev_spec->dsp_reset_counter) {
22575eba5b6SRobert Mustacchi 			dev_spec->dsp_reset_counter = 0;
22675eba5b6SRobert Mustacchi 			dsp_value = DSP_RESET_ENABLE;
22775eba5b6SRobert Mustacchi 		} else {
22875eba5b6SRobert Mustacchi 			ret_val = E1000_SUCCESS;
22975eba5b6SRobert Mustacchi 			goto out;
23075eba5b6SRobert Mustacchi 		}
23175eba5b6SRobert Mustacchi 	} else {
23275eba5b6SRobert Mustacchi 		if (dev_spec->dsp_reset_counter == 0) {
23375eba5b6SRobert Mustacchi 			/*
23475eba5b6SRobert Mustacchi 			 * Workaround not activated,
23575eba5b6SRobert Mustacchi 			 * check if it needs activation
23675eba5b6SRobert Mustacchi 			 */
23775eba5b6SRobert Mustacchi 			ret_val = hw->phy.ops.read_reg(hw,
23875eba5b6SRobert Mustacchi 			    PHY_AUTONEG_EXP,
23975eba5b6SRobert Mustacchi 			    &phy_data);
24075eba5b6SRobert Mustacchi 			if (ret_val)
24175eba5b6SRobert Mustacchi 				goto out;
24275eba5b6SRobert Mustacchi 			/*
24375eba5b6SRobert Mustacchi 			 * Activate the workaround if there was a
24475eba5b6SRobert Mustacchi 			 * parallel detect fault
24575eba5b6SRobert Mustacchi 			 */
24675eba5b6SRobert Mustacchi 			if (phy_data & NWAY_ER_PAR_DETECT_FAULT) {
24775eba5b6SRobert Mustacchi 				dev_spec->dsp_reset_counter++;
24875eba5b6SRobert Mustacchi 			} else {
24975eba5b6SRobert Mustacchi 				ret_val = E1000_SUCCESS;
25075eba5b6SRobert Mustacchi 				goto out;
25175eba5b6SRobert Mustacchi 			}
25275eba5b6SRobert Mustacchi 		}
25375eba5b6SRobert Mustacchi 
25475eba5b6SRobert Mustacchi 		/* After 5 times, stop the workaround */
25575eba5b6SRobert Mustacchi 		if (dev_spec->dsp_reset_counter > E1000_MAX_DSP_RESETS) {
25675eba5b6SRobert Mustacchi 			dev_spec->dsp_reset_counter = 0;
25775eba5b6SRobert Mustacchi 			dsp_value = DSP_RESET_ENABLE;
25875eba5b6SRobert Mustacchi 		} else {
25975eba5b6SRobert Mustacchi 			if (dev_spec->dsp_reset_counter) {
26075eba5b6SRobert Mustacchi 				dsp_value = (dev_spec->dsp_reset_counter & 1)
26175eba5b6SRobert Mustacchi 				    ? DSP_RESET_DISABLE
26275eba5b6SRobert Mustacchi 				    : DSP_RESET_ENABLE;
26375eba5b6SRobert Mustacchi 				dev_spec->dsp_reset_counter++;
26475eba5b6SRobert Mustacchi 			}
26575eba5b6SRobert Mustacchi 		}
26675eba5b6SRobert Mustacchi 	}
26775eba5b6SRobert Mustacchi 
26875eba5b6SRobert Mustacchi 	ret_val =
26975eba5b6SRobert Mustacchi 	    hw->phy.ops.write_reg(hw, IGP01E1000_PHY_DSP_RESET, dsp_value);
27075eba5b6SRobert Mustacchi 
27175eba5b6SRobert Mustacchi out:
27275eba5b6SRobert Mustacchi 	return (ret_val);
27375eba5b6SRobert Mustacchi }
274