xref: /illumos-gate/usr/src/uts/common/io/e1000g/e1000g_workarounds.c (revision 49b7860084dbba18bc00b29413d6182197f9fe93)
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 #define	E1000_FIFO_MULTIPLIER			0x80
28 #define	E1000_FIFO_HDR_SIZE			0x10
29 #define	E1000_FIFO_GRANULARITY			0x10
30 #define	E1000_FIFO_PAD_82547			0x3E0
31 #define	E1000_ERR_FIFO_WRAP			8
32 
33 #define	DSP_RESET_ENABLE			0x0
34 #define	DSP_RESET_DISABLE			0x2
35 #define	E1000_MAX_DSP_RESETS			10
36 
37 #define	E1000_ROUNDUP(size, unit)	(((size) + (unit) - 1) & ~((unit) - 1))
38 
39 
40 /*
41  * e1000_ttl_workaround_enabled_82541 - Returns current TTL workaround status
42  * @hw: pointer to the HW structure
43  *
44  * Returns the current status of the TTL workaround, as to whether the
45  * workaround is enabled or disabled.
46  */
47 bool
e1000_ttl_workaround_enabled_82541(struct e1000_hw * hw)48 e1000_ttl_workaround_enabled_82541(struct e1000_hw *hw)
49 {
50 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
51 	bool state = false;
52 
53 	DEBUGFUNC("e1000_ttl_workaround_enabled_82541");
54 
55 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
56 		goto out;
57 
58 	state = dev_spec->ttl_workaround;
59 
60 out:
61 	return (state);
62 }
63 
64 /*
65  * e1000_fifo_workaround_82547 - Workaround for Tx fifo failure
66  * @hw: pointer to the HW structure
67  * @length: length of next outgoing frame
68  *
69  * Returns: E1000_ERR_FIFO_WRAP if the next packet cannot be transmitted yet
70  *	E1000_SUCCESS if the next packet can be transmitted
71  *
72  * Workaround for the 82547 Tx fifo failure.
73  */
74 s32
e1000_fifo_workaround_82547(struct e1000_hw * hw,u16 length)75 e1000_fifo_workaround_82547(struct e1000_hw *hw, u16 length)
76 {
77 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
78 	u32 tctl;
79 	s32 ret_val = E1000_SUCCESS;
80 	u16 fifo_pkt_len;
81 
82 	DEBUGFUNC("e1000_fifo_workaround_82547");
83 
84 	if (hw->mac.type != e1000_82547)
85 		goto out;
86 
87 	/*
88 	 * Get the length as seen by the FIFO of the next real
89 	 * packet to be transmitted.
90 	 */
91 	fifo_pkt_len = E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
92 	    E1000_FIFO_GRANULARITY);
93 
94 	if (fifo_pkt_len <= (E1000_FIFO_PAD_82547 + E1000_FIFO_HDR_SIZE))
95 		goto out;
96 
97 	if ((dev_spec->tx_fifo_head + fifo_pkt_len) <
98 	    (dev_spec->tx_fifo_size + E1000_FIFO_PAD_82547))
99 		goto out;
100 
101 	if (E1000_READ_REG(hw, E1000_TDT(0)) !=
102 	    E1000_READ_REG(hw, E1000_TDH(0))) {
103 		ret_val = -E1000_ERR_FIFO_WRAP;
104 		goto out;
105 	}
106 
107 	if (E1000_READ_REG(hw, E1000_TDFT) != E1000_READ_REG(hw, E1000_TDFH)) {
108 		ret_val = -E1000_ERR_FIFO_WRAP;
109 		goto out;
110 	}
111 
112 	if (E1000_READ_REG(hw, E1000_TDFTS) !=
113 	    E1000_READ_REG(hw, E1000_TDFHS)) {
114 		ret_val = -E1000_ERR_FIFO_WRAP;
115 		goto out;
116 	}
117 
118 	/* Disable the tx unit to avoid further pointer movement */
119 	tctl = E1000_READ_REG(hw, E1000_TCTL);
120 	E1000_WRITE_REG(hw, E1000_TCTL, tctl & ~E1000_TCTL_EN);
121 
122 	/* Reset the fifo pointers. */
123 	E1000_WRITE_REG(hw, E1000_TDFT, dev_spec->tx_fifo_start);
124 	E1000_WRITE_REG(hw, E1000_TDFH, dev_spec->tx_fifo_start);
125 	E1000_WRITE_REG(hw, E1000_TDFTS, dev_spec->tx_fifo_start);
126 	E1000_WRITE_REG(hw, E1000_TDFHS, dev_spec->tx_fifo_start);
127 
128 	/* Re-enabling tx unit */
129 	E1000_WRITE_REG(hw, E1000_TCTL, tctl);
130 	E1000_WRITE_FLUSH(hw);
131 
132 	dev_spec->tx_fifo_head = 0;
133 
134 out:
135 	return (ret_val);
136 }
137 
138 /*
139  * e1000_update_tx_fifo_head - Update Tx fifo head pointer
140  * @hw: pointer to the HW structure
141  * @length: length of next outgoing frame
142  *
143  * Updates the SW calculated Tx FIFO head pointer.
144  */
145 void
e1000_update_tx_fifo_head_82547(struct e1000_hw * hw,u32 length)146 e1000_update_tx_fifo_head_82547(struct e1000_hw *hw, u32 length)
147 {
148 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
149 
150 	DEBUGFUNC("e1000_update_tx_fifo_head_82547");
151 
152 	if (hw->mac.type != e1000_82547)
153 		return;
154 
155 	dev_spec->tx_fifo_head += E1000_ROUNDUP(length + E1000_FIFO_HDR_SIZE,
156 	    E1000_FIFO_GRANULARITY);
157 
158 	if (dev_spec->tx_fifo_head > dev_spec->tx_fifo_size)
159 		dev_spec->tx_fifo_head -= dev_spec->tx_fifo_size;
160 }
161 
162 /*
163  * e1000_set_ttl_workaround_state_82541 - Enable/Disables TTL workaround
164  * @hw: pointer to the HW structure
165  * @state: boolean to enable/disable TTL workaround
166  *
167  * For 82541 or 82547 only silicon, allows the driver to enable/disable the
168  * TTL workaround.
169  */
170 void
e1000_set_ttl_workaround_state_82541(struct e1000_hw * hw,bool state)171 e1000_set_ttl_workaround_state_82541(struct e1000_hw *hw, bool state)
172 {
173 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
174 
175 	DEBUGFUNC("e1000_set_ttl_workaround_state_82541");
176 
177 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
178 		return;
179 
180 	dev_spec->ttl_workaround = state;
181 }
182 
183 /*
184  * e1000_igp_ttl_workaround_82547 - Workaround for long TTL on 100HD hubs
185  * @hw: pointer to the HW structure
186  *
187  * Returns: E1000_ERR_PHY if fail to read/write the PHY
188  *          E1000_SUCCESS in any other case
189  *
190  * This function, specific to 82547 hardware only, needs to be called every
191  * second.  It checks if a parallel detect fault has occurred.  If a fault
192  * occurred, disable/enable the DSP reset mechanism up to 5 times (once per
193  * second).  If link is established, stop the workaround and ensure the DSP
194  * reset is enabled.
195  */
196 s32
e1000_igp_ttl_workaround_82547(struct e1000_hw * hw)197 e1000_igp_ttl_workaround_82547(struct e1000_hw *hw)
198 {
199 	struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
200 	s32 ret_val = E1000_SUCCESS;
201 	u16 phy_data = 0;
202 	u16 dsp_value = DSP_RESET_ENABLE;
203 	bool link;
204 
205 	DEBUGFUNC("e1000_igp_ttl_workaround_82547");
206 
207 	/* The workaround needed only for B-0 silicon HW */
208 	if ((hw->mac.type != e1000_82541) && (hw->mac.type != e1000_82547))
209 		goto out;
210 
211 	if (!(e1000_ttl_workaround_enabled_82541(hw)))
212 		goto out;
213 
214 	/* Check for link first */
215 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
216 	if (ret_val)
217 		goto out;
218 
219 	if (link) {
220 		/*
221 		 * If link is established during the workaround,
222 		 * the DSP mechanism must be enabled.
223 		 */
224 		if (dev_spec->dsp_reset_counter) {
225 			dev_spec->dsp_reset_counter = 0;
226 			dsp_value = DSP_RESET_ENABLE;
227 		} else {
228 			ret_val = E1000_SUCCESS;
229 			goto out;
230 		}
231 	} else {
232 		if (dev_spec->dsp_reset_counter == 0) {
233 			/*
234 			 * Workaround not activated,
235 			 * check if it needs activation
236 			 */
237 			ret_val = hw->phy.ops.read_reg(hw,
238 			    PHY_AUTONEG_EXP,
239 			    &phy_data);
240 			if (ret_val)
241 				goto out;
242 			/*
243 			 * Activate the workaround if there was a
244 			 * parallel detect fault
245 			 */
246 			if (phy_data & NWAY_ER_PAR_DETECT_FAULT) {
247 				dev_spec->dsp_reset_counter++;
248 			} else {
249 				ret_val = E1000_SUCCESS;
250 				goto out;
251 			}
252 		}
253 
254 		/* After 5 times, stop the workaround */
255 		if (dev_spec->dsp_reset_counter > E1000_MAX_DSP_RESETS) {
256 			dev_spec->dsp_reset_counter = 0;
257 			dsp_value = DSP_RESET_ENABLE;
258 		} else {
259 			if (dev_spec->dsp_reset_counter) {
260 				dsp_value = (dev_spec->dsp_reset_counter & 1)
261 				    ? DSP_RESET_DISABLE
262 				    : DSP_RESET_ENABLE;
263 				dev_spec->dsp_reset_counter++;
264 			}
265 		}
266 	}
267 
268 	ret_val =
269 	    hw->phy.ops.write_reg(hw, IGP01E1000_PHY_DSP_RESET, dsp_value);
270 
271 out:
272 	return (ret_val);
273 }
274