xref: /illumos-gate/usr/src/uts/common/io/e1000g/e1000g_workarounds.c (revision 00277c9e43668ff248a12ee635ce125957750373)
1 /*
2  * This file is provided under a CDDLv1 license.  When using or
3  * redistributing this file, you may do so under this license.
4  * In redistributing this file this license must be included
5  * and no other modification of this header file is permitted.
6  *
7  * CDDL LICENSE SUMMARY
8  *
9  * Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved.
10  *
11  * The contents of this file are subject to the terms of Version
12  * 1.0 of the Common Development and Distribution License (the "License").
13  *
14  * You should have received a copy of the License with this software.
15  * You can obtain a copy of the License at
16  *	http://www.opensolaris.org/os/licensing.
17  * See the License for the specific language governing permissions
18  * and limitations under the License.
19  */
20 
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms of the CDDLv1.
24  */
25 #include "e1000_api.h"
26 
27 /*
28  * e1000_ttl_workaround_enabled_82541 - Returns current TTL workaround status
29  * @hw: pointer to the HW structure
30  *
31  * Returns the current status of the TTL workaround, as to whether the
32  * workaround is enabled or disabled.
33  */
34 bool
35 e1000_ttl_workaround_enabled_82541(struct e1000_hw *hw)
36 {
37 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
38 	bool state = false;
39 
40 	DEBUGFUNC("e1000_ttl_workaround_enabled_82541");
41 
42 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
43 		goto out;
44 
45 	state = dev_spec->ttl_workaround;
46 
47 out:
48 	return (state);
49 }
50 
51 /*
52  * e1000_fifo_workaround_82547 - Workaround for Tx fifo failure
53  * @hw: pointer to the HW structure
54  * @length: length of next outgoing frame
55  *
56  * Returns: E1000_ERR_FIFO_WRAP if the next packet cannot be transmitted yet
57  *	E1000_SUCCESS if the next packet can be transmitted
58  *
59  * Workaround for the 82547 Tx fifo failure.
60  */
61 s32
62 e1000_fifo_workaround_82547(struct e1000_hw *hw, u16 length)
63 {
64 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
65 	u32 tctl;
66 	s32 ret_val = E1000_SUCCESS;
67 	u16 fifo_pkt_len;
68 
69 	DEBUGFUNC("e1000_fifo_workaround_82547");
70 
71 	if (hw->mac.type != e1000_82547)
72 		goto out;
73 
74 	/*
75 	 * Get the length as seen by the FIFO of the next real
76 	 * packet to be transmitted.
77 	 */
78 	fifo_pkt_len = E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
79 	    E1000_FIFO_GRANULARITY);
80 
81 	if (fifo_pkt_len <= (E1000_FIFO_PAD_82547 + E1000_FIFO_HDR_SIZE))
82 		goto out;
83 
84 	if ((dev_spec->tx_fifo_head + fifo_pkt_len) <
85 	    (dev_spec->tx_fifo_size + E1000_FIFO_PAD_82547))
86 		goto out;
87 
88 	if (E1000_READ_REG(hw, E1000_TDT(0)) !=
89 	    E1000_READ_REG(hw, E1000_TDH(0))) {
90 		ret_val = -E1000_ERR_FIFO_WRAP;
91 		goto out;
92 	}
93 
94 	if (E1000_READ_REG(hw, E1000_TDFT) != E1000_READ_REG(hw, E1000_TDFH)) {
95 		ret_val = -E1000_ERR_FIFO_WRAP;
96 		goto out;
97 	}
98 
99 	if (E1000_READ_REG(hw, E1000_TDFTS) !=
100 	    E1000_READ_REG(hw, E1000_TDFHS)) {
101 		ret_val = -E1000_ERR_FIFO_WRAP;
102 		goto out;
103 	}
104 
105 	/* Disable the tx unit to avoid further pointer movement */
106 	tctl = E1000_READ_REG(hw, E1000_TCTL);
107 	E1000_WRITE_REG(hw, E1000_TCTL, tctl & ~E1000_TCTL_EN);
108 
109 	/* Reset the fifo pointers. */
110 	E1000_WRITE_REG(hw, E1000_TDFT, dev_spec->tx_fifo_start);
111 	E1000_WRITE_REG(hw, E1000_TDFH, dev_spec->tx_fifo_start);
112 	E1000_WRITE_REG(hw, E1000_TDFTS, dev_spec->tx_fifo_start);
113 	E1000_WRITE_REG(hw, E1000_TDFHS, dev_spec->tx_fifo_start);
114 
115 	/* Re-enabling tx unit */
116 	E1000_WRITE_REG(hw, E1000_TCTL, tctl);
117 	E1000_WRITE_FLUSH(hw);
118 
119 	dev_spec->tx_fifo_head = 0;
120 
121 out:
122 	return (ret_val);
123 }
124 
125 /*
126  * e1000_update_tx_fifo_head - Update Tx fifo head pointer
127  * @hw: pointer to the HW structure
128  * @length: length of next outgoing frame
129  *
130  * Updates the SW calculated Tx FIFO head pointer.
131  */
132 void
133 e1000_update_tx_fifo_head_82547(struct e1000_hw *hw, u32 length)
134 {
135 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
136 
137 	DEBUGFUNC("e1000_update_tx_fifo_head_82547");
138 
139 	if (hw->mac.type != e1000_82547)
140 		return;
141 
142 	dev_spec->tx_fifo_head += E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
143 	    E1000_FIFO_GRANULARITY);
144 
145 	if (dev_spec->tx_fifo_head > dev_spec->tx_fifo_size)
146 		dev_spec->tx_fifo_head -= dev_spec->tx_fifo_size;
147 }
148 
149 /*
150  * e1000_set_ttl_workaround_state_82541 - Enable/Disables TTL workaround
151  * @hw: pointer to the HW structure
152  * @state: boolean to enable/disable TTL workaround
153  *
154  * For 82541 or 82547 only silicon, allows the driver to enable/disable the
155  * TTL workaround.
156  */
157 void
158 e1000_set_ttl_workaround_state_82541(struct e1000_hw *hw, bool state)
159 {
160 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
161 
162 	DEBUGFUNC("e1000_set_ttl_workaround_state_82541");
163 
164 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
165 		return;
166 
167 	dev_spec->ttl_workaround = state;
168 }
169 
170 /*
171  * e1000_igp_ttl_workaround_82547 - Workaround for long TTL on 100HD hubs
172  * @hw: pointer to the HW structure
173  *
174  * Returns: E1000_ERR_PHY if fail to read/write the PHY
175  *          E1000_SUCCESS in any other case
176  *
177  * This function, specific to 82547 hardware only, needs to be called every
178  * second.  It checks if a parallel detect fault has occurred.  If a fault
179  * occurred, disable/enable the DSP reset mechanism up to 5 times (once per
180  * second).  If link is established, stop the workaround and ensure the DSP
181  * reset is enabled.
182  */
183 s32
184 e1000_igp_ttl_workaround_82547(struct e1000_hw *hw)
185 {
186 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
187 	s32 ret_val = E1000_SUCCESS;
188 	u16 phy_data = 0;
189 	u16 dsp_value = DSP_RESET_ENABLE;
190 	bool link;
191 
192 	DEBUGFUNC("e1000_igp_ttl_workaround_82547");
193 
194 	/* The workaround needed only for B-0 silicon HW */
195 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
196 		goto out;
197 
198 	if (!(e1000_ttl_workaround_enabled_82541(hw)))
199 		goto out;
200 
201 	/* Check for link first */
202 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
203 	if (ret_val)
204 		goto out;
205 
206 	if (link) {
207 		/*
208 		 * If link is established during the workaround,
209 		 * the DSP mechanism must be enabled.
210 		 */
211 		if (dev_spec->dsp_reset_counter) {
212 			dev_spec->dsp_reset_counter = 0;
213 			dsp_value = DSP_RESET_ENABLE;
214 		} else {
215 			ret_val = E1000_SUCCESS;
216 			goto out;
217 		}
218 	} else {
219 		if (dev_spec->dsp_reset_counter == 0) {
220 			/*
221 			 * Workaround not activated,
222 			 * check if it needs activation
223 			 */
224 			ret_val = hw->phy.ops.read_reg(hw,
225 			    PHY_AUTONEG_EXP,
226 			    &phy_data);
227 			if (ret_val)
228 				goto out;
229 			/*
230 			 * Activate the workaround if there was a
231 			 * parallel detect fault
232 			 */
233 			if (phy_data & NWAY_ER_PAR_DETECT_FAULT) {
234 				dev_spec->dsp_reset_counter++;
235 			} else {
236 				ret_val = E1000_SUCCESS;
237 				goto out;
238 			}
239 		}
240 
241 		/* After 5 times, stop the workaround */
242 		if (dev_spec->dsp_reset_counter > E1000_MAX_DSP_RESETS) {
243 			dev_spec->dsp_reset_counter = 0;
244 			dsp_value = DSP_RESET_ENABLE;
245 		} else {
246 			if (dev_spec->dsp_reset_counter) {
247 				dsp_value = (dev_spec->dsp_reset_counter & 1)
248 				    ? DSP_RESET_DISABLE
249 				    : DSP_RESET_ENABLE;
250 				dev_spec->dsp_reset_counter++;
251 			}
252 		}
253 	}
254 
255 	ret_val =
256 	    hw->phy.ops.write_reg(hw, IGP01E1000_PHY_DSP_RESET, dsp_value);
257 
258 out:
259 	return (ret_val);
260 }
261