xref: /netbsd-src/sys/dev/pci/igc/igc_phy.c (revision fb38d839b48b9b6204dbbee1672454d6e719ba01)
1*fb38d839Srin /*	$NetBSD: igc_phy.c,v 1.2 2023/10/04 07:35:27 rin Exp $	*/
2d0d8f2a5Srin /*	$OpenBSD: igc_phy.c,v 1.3 2023/02/03 11:31:52 mbuhl Exp $	*/
3d0d8f2a5Srin /*-
4d0d8f2a5Srin  * Copyright 2021 Intel Corp
5d0d8f2a5Srin  * Copyright 2021 Rubicon Communications, LLC (Netgate)
6d0d8f2a5Srin  * SPDX-License-Identifier: BSD-3-Clause
7d0d8f2a5Srin  */
8d0d8f2a5Srin 
9*fb38d839Srin #include <sys/cdefs.h>
10*fb38d839Srin __KERNEL_RCSID(0, "$NetBSD: igc_phy.c,v 1.2 2023/10/04 07:35:27 rin Exp $");
11*fb38d839Srin 
12*fb38d839Srin #include <dev/pci/igc/igc_api.h>
13*fb38d839Srin #include <dev/mii/mii.h>
14d0d8f2a5Srin 
15d0d8f2a5Srin /**
16d0d8f2a5Srin  *  igc_init_phy_ops_generic - Initialize PHY function pointers
17d0d8f2a5Srin  *  @hw: pointer to the HW structure
18d0d8f2a5Srin  *
19d0d8f2a5Srin  *  Setups up the function pointers to no-op functions
20d0d8f2a5Srin  **/
21d0d8f2a5Srin void
igc_init_phy_ops_generic(struct igc_hw * hw)22d0d8f2a5Srin igc_init_phy_ops_generic(struct igc_hw *hw)
23d0d8f2a5Srin {
24d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
25d0d8f2a5Srin 	DEBUGFUNC("igc_init_phy_ops_generic");
26d0d8f2a5Srin 
27d0d8f2a5Srin 	/* Initialize function pointers */
28d0d8f2a5Srin 	phy->ops.init_params = igc_null_ops_generic;
29d0d8f2a5Srin 	phy->ops.acquire = igc_null_ops_generic;
30d0d8f2a5Srin 	phy->ops.check_reset_block = igc_null_ops_generic;
31d0d8f2a5Srin 	phy->ops.force_speed_duplex = igc_null_ops_generic;
32d0d8f2a5Srin 	phy->ops.get_info = igc_null_ops_generic;
33d0d8f2a5Srin 	phy->ops.set_page = igc_null_set_page;
34d0d8f2a5Srin 	phy->ops.read_reg = igc_null_read_reg;
35d0d8f2a5Srin 	phy->ops.read_reg_locked = igc_null_read_reg;
36d0d8f2a5Srin 	phy->ops.read_reg_page = igc_null_read_reg;
37d0d8f2a5Srin 	phy->ops.release = igc_null_phy_generic;
38d0d8f2a5Srin 	phy->ops.reset = igc_null_ops_generic;
39d0d8f2a5Srin 	phy->ops.set_d0_lplu_state = igc_null_lplu_state;
40d0d8f2a5Srin 	phy->ops.set_d3_lplu_state = igc_null_lplu_state;
41d0d8f2a5Srin 	phy->ops.write_reg = igc_null_write_reg;
42d0d8f2a5Srin 	phy->ops.write_reg_locked = igc_null_write_reg;
43d0d8f2a5Srin 	phy->ops.write_reg_page = igc_null_write_reg;
44d0d8f2a5Srin 	phy->ops.power_up = igc_null_phy_generic;
45d0d8f2a5Srin 	phy->ops.power_down = igc_null_phy_generic;
46d0d8f2a5Srin }
47d0d8f2a5Srin 
48d0d8f2a5Srin /**
49d0d8f2a5Srin  *  igc_null_set_page - No-op function, return 0
50d0d8f2a5Srin  *  @hw: pointer to the HW structure
51d0d8f2a5Srin  *  @data: dummy variable
52d0d8f2a5Srin  **/
53d0d8f2a5Srin int
igc_null_set_page(struct igc_hw IGC_UNUSEDARG * hw,uint16_t IGC_UNUSEDARG data)54d0d8f2a5Srin igc_null_set_page(struct igc_hw IGC_UNUSEDARG *hw, uint16_t IGC_UNUSEDARG data)
55d0d8f2a5Srin {
56d0d8f2a5Srin 	DEBUGFUNC("igc_null_set_page");
57d0d8f2a5Srin 	return IGC_SUCCESS;
58d0d8f2a5Srin }
59d0d8f2a5Srin 
60d0d8f2a5Srin /**
61d0d8f2a5Srin  *  igc_null_read_reg - No-op function, return 0
62d0d8f2a5Srin  *  @hw: pointer to the HW structure
63d0d8f2a5Srin  *  @offset: dummy variable
64d0d8f2a5Srin  *  @data: dummy variable
65d0d8f2a5Srin  **/
66d0d8f2a5Srin int
igc_null_read_reg(struct igc_hw IGC_UNUSEDARG * hw,uint32_t IGC_UNUSEDARG offset,uint16_t IGC_UNUSEDARG * data)67d0d8f2a5Srin igc_null_read_reg(struct igc_hw IGC_UNUSEDARG *hw,
68d0d8f2a5Srin     uint32_t IGC_UNUSEDARG offset, uint16_t IGC_UNUSEDARG *data)
69d0d8f2a5Srin {
70d0d8f2a5Srin 	DEBUGFUNC("igc_null_read_reg");
71d0d8f2a5Srin 	return IGC_SUCCESS;
72d0d8f2a5Srin }
73d0d8f2a5Srin 
74d0d8f2a5Srin /**
75d0d8f2a5Srin  *  igc_null_phy_generic - No-op function, return void
76d0d8f2a5Srin  *  @hw: pointer to the HW structure
77d0d8f2a5Srin  **/
78d0d8f2a5Srin void
igc_null_phy_generic(struct igc_hw IGC_UNUSEDARG * hw)79d0d8f2a5Srin igc_null_phy_generic(struct igc_hw IGC_UNUSEDARG *hw)
80d0d8f2a5Srin {
81d0d8f2a5Srin 	DEBUGFUNC("igc_null_phy_generic");
82d0d8f2a5Srin 	return;
83d0d8f2a5Srin }
84d0d8f2a5Srin 
85d0d8f2a5Srin /**
86d0d8f2a5Srin  *  igc_null_lplu_state - No-op function, return 0
87d0d8f2a5Srin  *  @hw: pointer to the HW structure
88d0d8f2a5Srin  *  @active: dummy variable
89d0d8f2a5Srin  **/
90d0d8f2a5Srin int
igc_null_lplu_state(struct igc_hw IGC_UNUSEDARG * hw,bool IGC_UNUSEDARG active)91d0d8f2a5Srin igc_null_lplu_state(struct igc_hw IGC_UNUSEDARG *hw, bool IGC_UNUSEDARG active)
92d0d8f2a5Srin {
93d0d8f2a5Srin 	DEBUGFUNC("igc_null_lplu_state");
94d0d8f2a5Srin 	return IGC_SUCCESS;
95d0d8f2a5Srin }
96d0d8f2a5Srin 
97d0d8f2a5Srin /**
98d0d8f2a5Srin  *  igc_null_write_reg - No-op function, return 0
99d0d8f2a5Srin  *  @hw: pointer to the HW structure
100d0d8f2a5Srin  *  @offset: dummy variable
101d0d8f2a5Srin  *  @data: dummy variable
102d0d8f2a5Srin  **/
103d0d8f2a5Srin int
igc_null_write_reg(struct igc_hw IGC_UNUSEDARG * hw,uint32_t IGC_UNUSEDARG offset,uint16_t IGC_UNUSEDARG data)104d0d8f2a5Srin igc_null_write_reg(struct igc_hw IGC_UNUSEDARG *hw,
105d0d8f2a5Srin     uint32_t IGC_UNUSEDARG offset, uint16_t IGC_UNUSEDARG data)
106d0d8f2a5Srin {
107d0d8f2a5Srin 	DEBUGFUNC("igc_null_write_reg");
108d0d8f2a5Srin 	return IGC_SUCCESS;
109d0d8f2a5Srin }
110d0d8f2a5Srin 
111d0d8f2a5Srin /**
112d0d8f2a5Srin  *  igc_check_reset_block_generic - Check if PHY reset is blocked
113d0d8f2a5Srin  *  @hw: pointer to the HW structure
114d0d8f2a5Srin  *
115d0d8f2a5Srin  *  Read the PHY management control register and check whether a PHY reset
116d0d8f2a5Srin  *  is blocked.  If a reset is not blocked return IGC_SUCCESS, otherwise
117d0d8f2a5Srin  *  return IGC_BLK_PHY_RESET (12).
118d0d8f2a5Srin  **/
119d0d8f2a5Srin int
igc_check_reset_block_generic(struct igc_hw * hw)120d0d8f2a5Srin igc_check_reset_block_generic(struct igc_hw *hw)
121d0d8f2a5Srin {
122d0d8f2a5Srin 	uint32_t manc;
123d0d8f2a5Srin 
124d0d8f2a5Srin 	DEBUGFUNC("igc_check_reset_block");
125d0d8f2a5Srin 
126d0d8f2a5Srin 	manc = IGC_READ_REG(hw, IGC_MANC);
127d0d8f2a5Srin 
128d0d8f2a5Srin 	return (manc & IGC_MANC_BLK_PHY_RST_ON_IDE) ?
129d0d8f2a5Srin 	    IGC_BLK_PHY_RESET : IGC_SUCCESS;
130d0d8f2a5Srin }
131d0d8f2a5Srin 
132d0d8f2a5Srin /**
133d0d8f2a5Srin  *  igc_get_phy_id - Retrieve the PHY ID and revision
134d0d8f2a5Srin  *  @hw: pointer to the HW structure
135d0d8f2a5Srin  *
136d0d8f2a5Srin  *  Reads the PHY registers and stores the PHY ID and possibly the PHY
137d0d8f2a5Srin  *  revision in the hardware structure.
138d0d8f2a5Srin  **/
139d0d8f2a5Srin int
igc_get_phy_id(struct igc_hw * hw)140d0d8f2a5Srin igc_get_phy_id(struct igc_hw *hw)
141d0d8f2a5Srin {
142d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
143d0d8f2a5Srin 	uint16_t phy_id;
144d0d8f2a5Srin 	int ret_val = IGC_SUCCESS;
145d0d8f2a5Srin 
146d0d8f2a5Srin 	DEBUGFUNC("igc_get_phy_id");
147d0d8f2a5Srin 
148d0d8f2a5Srin 	if (!phy->ops.read_reg)
149d0d8f2a5Srin 		return IGC_SUCCESS;
150d0d8f2a5Srin 
151d0d8f2a5Srin 	ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
152d0d8f2a5Srin 	if (ret_val)
153d0d8f2a5Srin 		return ret_val;
154d0d8f2a5Srin 
155d0d8f2a5Srin 	phy->id = (uint32_t)(phy_id << 16);
156d0d8f2a5Srin 	DELAY(200);
157d0d8f2a5Srin 	ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
158d0d8f2a5Srin 	if (ret_val)
159d0d8f2a5Srin 		return ret_val;
160d0d8f2a5Srin 
161d0d8f2a5Srin 	phy->id |= (uint32_t)(phy_id & PHY_REVISION_MASK);
162d0d8f2a5Srin 	phy->revision = (uint32_t)(phy_id & ~PHY_REVISION_MASK);
163d0d8f2a5Srin 
164d0d8f2a5Srin 	return IGC_SUCCESS;
165d0d8f2a5Srin }
166d0d8f2a5Srin 
167d0d8f2a5Srin /**
168d0d8f2a5Srin  *  igc_read_phy_reg_mdic - Read MDI control register
169d0d8f2a5Srin  *  @hw: pointer to the HW structure
170d0d8f2a5Srin  *  @offset: register offset to be read
171d0d8f2a5Srin  *  @data: pointer to the read data
172d0d8f2a5Srin  *
173d0d8f2a5Srin  *  Reads the MDI control register in the PHY at offset and stores the
174d0d8f2a5Srin  *  information read to data.
175d0d8f2a5Srin  **/
176d0d8f2a5Srin int
igc_read_phy_reg_mdic(struct igc_hw * hw,uint32_t offset,uint16_t * data)177d0d8f2a5Srin igc_read_phy_reg_mdic(struct igc_hw *hw, uint32_t offset, uint16_t *data)
178d0d8f2a5Srin {
179d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
180d0d8f2a5Srin 	uint32_t i, mdic = 0;
181d0d8f2a5Srin 
182d0d8f2a5Srin 	DEBUGFUNC("igc_read_phy_reg_mdic");
183d0d8f2a5Srin 
184d0d8f2a5Srin 	if (offset > MAX_PHY_REG_ADDRESS) {
185d0d8f2a5Srin 		DEBUGOUT1("PHY Address %d is out of range\n", offset);
186d0d8f2a5Srin 		return -IGC_ERR_PARAM;
187d0d8f2a5Srin 	}
188d0d8f2a5Srin 
189d0d8f2a5Srin 	/* Set up Op-code, Phy Address, and register offset in the MDI
190d0d8f2a5Srin 	 * Control register.  The MAC will take care of interfacing with the
191d0d8f2a5Srin 	 * PHY to retrieve the desired data.
192d0d8f2a5Srin 	 */
193d0d8f2a5Srin 	mdic = ((offset << IGC_MDIC_REG_SHIFT) |
194d0d8f2a5Srin 	    (phy->addr << IGC_MDIC_PHY_SHIFT) | (IGC_MDIC_OP_READ));
195d0d8f2a5Srin 
196d0d8f2a5Srin 	IGC_WRITE_REG(hw, IGC_MDIC, mdic);
197d0d8f2a5Srin 
198d0d8f2a5Srin 	/* Poll the ready bit to see if the MDI read completed
199d0d8f2a5Srin 	 * Increasing the time out as testing showed failures with
200d0d8f2a5Srin 	 * the lower time out
201d0d8f2a5Srin 	 */
202d0d8f2a5Srin 	for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) {
203d0d8f2a5Srin 		DELAY(50);
204d0d8f2a5Srin 		mdic = IGC_READ_REG(hw, IGC_MDIC);
205d0d8f2a5Srin 		if (mdic & IGC_MDIC_READY)
206d0d8f2a5Srin 			break;
207d0d8f2a5Srin 	}
208d0d8f2a5Srin 	if (!(mdic & IGC_MDIC_READY)) {
209d0d8f2a5Srin 		DEBUGOUT("MDI Read did not complete\n");
210d0d8f2a5Srin 		return -IGC_ERR_PHY;
211d0d8f2a5Srin 	}
212d0d8f2a5Srin 	if (mdic & IGC_MDIC_ERROR) {
213d0d8f2a5Srin 		DEBUGOUT("MDI Error\n");
214d0d8f2a5Srin 		return -IGC_ERR_PHY;
215d0d8f2a5Srin 	}
216d0d8f2a5Srin 	if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset) {
217d0d8f2a5Srin 		DEBUGOUT2("MDI Read offset error - requested %d, returned %d\n",
218d0d8f2a5Srin 		    offset, (mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT);
219d0d8f2a5Srin 		return -IGC_ERR_PHY;
220d0d8f2a5Srin 	}
221d0d8f2a5Srin 	*data = (uint16_t)mdic;
222d0d8f2a5Srin 
223d0d8f2a5Srin 	return IGC_SUCCESS;
224d0d8f2a5Srin }
225d0d8f2a5Srin 
226d0d8f2a5Srin /**
227d0d8f2a5Srin  *  igc_write_phy_reg_mdic - Write MDI control register
228d0d8f2a5Srin  *  @hw: pointer to the HW structure
229d0d8f2a5Srin  *  @offset: register offset to write to
230d0d8f2a5Srin  *  @data: data to write to register at offset
231d0d8f2a5Srin  *
232d0d8f2a5Srin  *  Writes data to MDI control register in the PHY at offset.
233d0d8f2a5Srin  **/
234d0d8f2a5Srin int
igc_write_phy_reg_mdic(struct igc_hw * hw,uint32_t offset,uint16_t data)235d0d8f2a5Srin igc_write_phy_reg_mdic(struct igc_hw *hw, uint32_t offset, uint16_t data)
236d0d8f2a5Srin {
237d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
238d0d8f2a5Srin 	uint32_t i, mdic = 0;
239d0d8f2a5Srin 
240d0d8f2a5Srin 	DEBUGFUNC("igc_write_phy_reg_mdic");
241d0d8f2a5Srin 
242d0d8f2a5Srin 	if (offset > MAX_PHY_REG_ADDRESS) {
243d0d8f2a5Srin 		DEBUGOUT1("PHY Address %d is out of range\n", offset);
244d0d8f2a5Srin 		return -IGC_ERR_PARAM;
245d0d8f2a5Srin 	}
246d0d8f2a5Srin 
247d0d8f2a5Srin 	/* Set up Op-code, Phy Address, and register offset in the MDI
248d0d8f2a5Srin 	 * Control register.  The MAC will take care of interfacing with the
249d0d8f2a5Srin 	 * PHY to retrieve the desired data.
250d0d8f2a5Srin 	 */
251d0d8f2a5Srin 	mdic = (((uint32_t)data) | (offset << IGC_MDIC_REG_SHIFT) |
252d0d8f2a5Srin 	    (phy->addr << IGC_MDIC_PHY_SHIFT) | (IGC_MDIC_OP_WRITE));
253d0d8f2a5Srin 
254d0d8f2a5Srin 	IGC_WRITE_REG(hw, IGC_MDIC, mdic);
255d0d8f2a5Srin 
256d0d8f2a5Srin 	/* Poll the ready bit to see if the MDI read completed
257d0d8f2a5Srin 	 * Increasing the time out as testing showed failures with
258d0d8f2a5Srin 	 * the lower time out
259d0d8f2a5Srin 	 */
260d0d8f2a5Srin 	for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) {
261d0d8f2a5Srin 		DELAY(50);
262d0d8f2a5Srin 		mdic = IGC_READ_REG(hw, IGC_MDIC);
263d0d8f2a5Srin 		if (mdic & IGC_MDIC_READY)
264d0d8f2a5Srin 			break;
265d0d8f2a5Srin 	}
266d0d8f2a5Srin 	if (!(mdic & IGC_MDIC_READY)) {
267d0d8f2a5Srin 		DEBUGOUT("MDI Write did not complete\n");
268d0d8f2a5Srin 		return -IGC_ERR_PHY;
269d0d8f2a5Srin 	}
270d0d8f2a5Srin 	if (mdic & IGC_MDIC_ERROR) {
271d0d8f2a5Srin 		DEBUGOUT("MDI Error\n");
272d0d8f2a5Srin 		return -IGC_ERR_PHY;
273d0d8f2a5Srin 	}
274d0d8f2a5Srin 	if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset)
275d0d8f2a5Srin 		return -IGC_ERR_PHY;
276d0d8f2a5Srin 
277d0d8f2a5Srin 	return IGC_SUCCESS;
278d0d8f2a5Srin }
279d0d8f2a5Srin 
280d0d8f2a5Srin /**
281d0d8f2a5Srin  *  igc_phy_setup_autoneg - Configure PHY for auto-negotiation
282d0d8f2a5Srin  *  @hw: pointer to the HW structure
283d0d8f2a5Srin  *
284d0d8f2a5Srin  *  Reads the MII auto-neg advertisement register and/or the 1000T control
285d0d8f2a5Srin  *  register and if the PHY is already setup for auto-negotiation, then
286d0d8f2a5Srin  *  return successful.  Otherwise, setup advertisement and flow control to
287d0d8f2a5Srin  *  the appropriate values for the wanted auto-negotiation.
288d0d8f2a5Srin  **/
289*fb38d839Srin static int
igc_phy_setup_autoneg(struct igc_hw * hw)290d0d8f2a5Srin igc_phy_setup_autoneg(struct igc_hw *hw)
291d0d8f2a5Srin {
292d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
293d0d8f2a5Srin 	uint16_t mii_autoneg_adv_reg;
294d0d8f2a5Srin 	uint16_t mii_1000t_ctrl_reg = 0;
295d0d8f2a5Srin 	uint16_t aneg_multigbt_an_ctrl = 0;
296d0d8f2a5Srin 	int ret_val;
297d0d8f2a5Srin 
298d0d8f2a5Srin 	DEBUGFUNC("igc_phy_setup_autoneg");
299d0d8f2a5Srin 
300d0d8f2a5Srin 	phy->autoneg_advertised &= phy->autoneg_mask;
301d0d8f2a5Srin 
302d0d8f2a5Srin 	/* Read the MII Auto-Neg Advertisement Register (Address 4). */
303d0d8f2a5Srin 	ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
304d0d8f2a5Srin 	if (ret_val)
305d0d8f2a5Srin 		return ret_val;
306d0d8f2a5Srin 
307d0d8f2a5Srin 	if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
308d0d8f2a5Srin 		/* Read the MII 1000Base-T Control Register (Address 9). */
309d0d8f2a5Srin 		ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL,
310d0d8f2a5Srin 		    &mii_1000t_ctrl_reg);
311d0d8f2a5Srin 		if (ret_val)
312d0d8f2a5Srin 			return ret_val;
313d0d8f2a5Srin 	}
314d0d8f2a5Srin 
315d0d8f2a5Srin 	if (phy->autoneg_mask & ADVERTISE_2500_FULL) {
316d0d8f2a5Srin 		/* Read the MULTI GBT AN Control Register - reg 7.32 */
317d0d8f2a5Srin 		ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<
318d0d8f2a5Srin 		    MMD_DEVADDR_SHIFT) | ANEG_MULTIGBT_AN_CTRL,
319d0d8f2a5Srin 		    &aneg_multigbt_an_ctrl);
320d0d8f2a5Srin 		if (ret_val)
321d0d8f2a5Srin 			return ret_val;
322d0d8f2a5Srin 	}
323d0d8f2a5Srin 
324d0d8f2a5Srin 	/* Need to parse both autoneg_advertised and fc and set up
325d0d8f2a5Srin 	 * the appropriate PHY registers.  First we will parse for
326d0d8f2a5Srin 	 * autoneg_advertised software override.  Since we can advertise
327d0d8f2a5Srin 	 * a plethora of combinations, we need to check each bit
328d0d8f2a5Srin 	 * individually.
329d0d8f2a5Srin 	 */
330d0d8f2a5Srin 
331d0d8f2a5Srin 	/* First we clear all the 10/100 mb speed bits in the Auto-Neg
332d0d8f2a5Srin 	 * Advertisement Register (Address 4) and the 1000 mb speed bits in
333d0d8f2a5Srin 	 * the  1000Base-T Control Register (Address 9).
334d0d8f2a5Srin 	 */
335d0d8f2a5Srin 	mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | NWAY_AR_100TX_HD_CAPS |
336d0d8f2a5Srin 	    NWAY_AR_10T_FD_CAPS | NWAY_AR_10T_HD_CAPS);
337d0d8f2a5Srin 	mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
338d0d8f2a5Srin 
339d0d8f2a5Srin 	DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised);
340d0d8f2a5Srin 
341d0d8f2a5Srin 	/* Do we want to advertise 10 Mb Half Duplex? */
342d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
343d0d8f2a5Srin 		DEBUGOUT("Advertise 10mb Half duplex\n");
344d0d8f2a5Srin 		mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
345d0d8f2a5Srin 	}
346d0d8f2a5Srin 
347d0d8f2a5Srin 	/* Do we want to advertise 10 Mb Full Duplex? */
348d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
349d0d8f2a5Srin 		DEBUGOUT("Advertise 10mb Full duplex\n");
350d0d8f2a5Srin 		mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
351d0d8f2a5Srin 	}
352d0d8f2a5Srin 
353d0d8f2a5Srin 	/* Do we want to advertise 100 Mb Half Duplex? */
354d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
355d0d8f2a5Srin 		DEBUGOUT("Advertise 100mb Half duplex\n");
356d0d8f2a5Srin 		mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
357d0d8f2a5Srin 	}
358d0d8f2a5Srin 
359d0d8f2a5Srin 	/* Do we want to advertise 100 Mb Full Duplex? */
360d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
361d0d8f2a5Srin 		DEBUGOUT("Advertise 100mb Full duplex\n");
362d0d8f2a5Srin 		mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
363d0d8f2a5Srin 	}
364d0d8f2a5Srin 
365d0d8f2a5Srin 	/* We do not allow the Phy to advertise 1000 Mb Half Duplex */
366d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_1000_HALF)
367d0d8f2a5Srin 		DEBUGOUT("Advertise 1000mb Half duplex request denied!\n");
368d0d8f2a5Srin 
369d0d8f2a5Srin 	/* Do we want to advertise 1000 Mb Full Duplex? */
370d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
371d0d8f2a5Srin 		DEBUGOUT("Advertise 1000mb Full duplex\n");
372d0d8f2a5Srin 		mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
373d0d8f2a5Srin 	}
374d0d8f2a5Srin 
375d0d8f2a5Srin 	/* We do not allow the Phy to advertise 2500 Mb Half Duplex */
376d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_2500_HALF)
377d0d8f2a5Srin 		DEBUGOUT("Advertise 2500mb Half duplex request denied!\n");
378d0d8f2a5Srin 
379d0d8f2a5Srin 	/* Do we want to advertise 2500 Mb Full Duplex? */
380d0d8f2a5Srin 	if (phy->autoneg_advertised & ADVERTISE_2500_FULL) {
381d0d8f2a5Srin 		DEBUGOUT("Advertise 2500mb Full duplex\n");
382d0d8f2a5Srin 		aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS;
383d0d8f2a5Srin 	} else
384d0d8f2a5Srin 		aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS;
385d0d8f2a5Srin 
386d0d8f2a5Srin 	/* Check for a software override of the flow control settings, and
387d0d8f2a5Srin 	 * setup the PHY advertisement registers accordingly.  If
388d0d8f2a5Srin 	 * auto-negotiation is enabled, then software will have to set the
389d0d8f2a5Srin 	 * "PAUSE" bits to the correct value in the Auto-Negotiation
390d0d8f2a5Srin 	 * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-
391d0d8f2a5Srin 	 * negotiation.
392d0d8f2a5Srin 	 *
393d0d8f2a5Srin 	 * The possible values of the "fc" parameter are:
394d0d8f2a5Srin 	 *      0:  Flow control is completely disabled
395d0d8f2a5Srin 	 *      1:  Rx flow control is enabled (we can receive pause frames
396d0d8f2a5Srin 	 *          but not send pause frames).
397d0d8f2a5Srin 	 *      2:  Tx flow control is enabled (we can send pause frames
398d0d8f2a5Srin 	 *          but we do not support receiving pause frames).
399d0d8f2a5Srin 	 *      3:  Both Rx and Tx flow control (symmetric) are enabled.
400d0d8f2a5Srin 	 *  other:  No software override.  The flow control configuration
401d0d8f2a5Srin 	 *          in the EEPROM is used.
402d0d8f2a5Srin 	 */
403d0d8f2a5Srin 	switch (hw->fc.current_mode) {
404d0d8f2a5Srin 	case igc_fc_none:
405d0d8f2a5Srin 		/* Flow control (Rx & Tx) is completely disabled by a
406d0d8f2a5Srin 		 * software over-ride.
407d0d8f2a5Srin 		 */
408d0d8f2a5Srin 		mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
409d0d8f2a5Srin 		break;
410d0d8f2a5Srin 	case igc_fc_rx_pause:
411d0d8f2a5Srin 		/* Rx Flow control is enabled, and Tx Flow control is
412d0d8f2a5Srin 		 * disabled, by a software over-ride.
413d0d8f2a5Srin 		 *
414d0d8f2a5Srin 		 * Since there really isn't a way to advertise that we are
415d0d8f2a5Srin 		 * capable of Rx Pause ONLY, we will advertise that we
416d0d8f2a5Srin 		 * support both symmetric and asymmetric Rx PAUSE.  Later
417d0d8f2a5Srin 		 * (in igc_config_fc_after_link_up) we will disable the
418d0d8f2a5Srin 		 * hw's ability to send PAUSE frames.
419d0d8f2a5Srin 		 */
420d0d8f2a5Srin 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
421d0d8f2a5Srin 		break;
422d0d8f2a5Srin 	case igc_fc_tx_pause:
423d0d8f2a5Srin 		/* Tx Flow control is enabled, and Rx Flow control is
424d0d8f2a5Srin 		 * disabled, by a software over-ride.
425d0d8f2a5Srin 		 */
426d0d8f2a5Srin 		mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
427d0d8f2a5Srin 		mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
428d0d8f2a5Srin 		break;
429d0d8f2a5Srin 	case igc_fc_full:
430d0d8f2a5Srin 		/* Flow control (both Rx and Tx) is enabled by a software
431d0d8f2a5Srin 		 * over-ride.
432d0d8f2a5Srin 		 */
433d0d8f2a5Srin 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
434d0d8f2a5Srin 		break;
435d0d8f2a5Srin 	default:
436d0d8f2a5Srin 		DEBUGOUT("Flow control param set incorrectly\n");
437d0d8f2a5Srin 		return -IGC_ERR_CONFIG;
438d0d8f2a5Srin 	}
439d0d8f2a5Srin 
440d0d8f2a5Srin 	ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
441d0d8f2a5Srin 	if (ret_val)
442d0d8f2a5Srin 		return ret_val;
443d0d8f2a5Srin 
444d0d8f2a5Srin 	DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
445d0d8f2a5Srin 
446d0d8f2a5Srin 	if (phy->autoneg_mask & ADVERTISE_1000_FULL)
447d0d8f2a5Srin 		ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,
448d0d8f2a5Srin 		    mii_1000t_ctrl_reg);
449d0d8f2a5Srin 
450d0d8f2a5Srin 	if (phy->autoneg_mask & ADVERTISE_2500_FULL)
451d0d8f2a5Srin 		ret_val = phy->ops.write_reg(hw,
452d0d8f2a5Srin 		    (STANDARD_AN_REG_MASK << MMD_DEVADDR_SHIFT) |
453d0d8f2a5Srin 		    ANEG_MULTIGBT_AN_CTRL, aneg_multigbt_an_ctrl);
454d0d8f2a5Srin 
455d0d8f2a5Srin 	return ret_val;
456d0d8f2a5Srin }
457d0d8f2a5Srin 
458d0d8f2a5Srin /**
459d0d8f2a5Srin  *  igc_copper_link_autoneg - Setup/Enable autoneg for copper link
460d0d8f2a5Srin  *  @hw: pointer to the HW structure
461d0d8f2a5Srin  *
462d0d8f2a5Srin  *  Performs initial bounds checking on autoneg advertisement parameter, then
463d0d8f2a5Srin  *  configure to advertise the full capability.  Setup the PHY to autoneg
464d0d8f2a5Srin  *  and restart the negotiation process between the link partner.  If
465d0d8f2a5Srin  *  autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
466d0d8f2a5Srin  **/
467*fb38d839Srin static int
igc_copper_link_autoneg(struct igc_hw * hw)468d0d8f2a5Srin igc_copper_link_autoneg(struct igc_hw *hw)
469d0d8f2a5Srin {
470d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
471d0d8f2a5Srin 	uint16_t phy_ctrl;
472d0d8f2a5Srin 	int ret_val;
473d0d8f2a5Srin 
474d0d8f2a5Srin 	DEBUGFUNC("igc_copper_link_autoneg");
475d0d8f2a5Srin 
476d0d8f2a5Srin 	/* Perform some bounds checking on the autoneg advertisement
477d0d8f2a5Srin 	 * parameter.
478d0d8f2a5Srin 	 */
479d0d8f2a5Srin 	phy->autoneg_advertised &= phy->autoneg_mask;
480d0d8f2a5Srin 
481d0d8f2a5Srin 	/* If autoneg_advertised is zero, we assume it was not defaulted
482d0d8f2a5Srin 	 * by the calling code so we set to advertise full capability.
483d0d8f2a5Srin 	 */
484d0d8f2a5Srin 	if (!phy->autoneg_advertised)
485d0d8f2a5Srin 		phy->autoneg_advertised = phy->autoneg_mask;
486d0d8f2a5Srin 
487d0d8f2a5Srin 	DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
488d0d8f2a5Srin 	ret_val = igc_phy_setup_autoneg(hw);
489d0d8f2a5Srin 	if (ret_val) {
490d0d8f2a5Srin 		DEBUGOUT("Error Setting up Auto-Negotiation\n");
491d0d8f2a5Srin 		return ret_val;
492d0d8f2a5Srin 	}
493d0d8f2a5Srin 	DEBUGOUT("Restarting Auto-Neg\n");
494d0d8f2a5Srin 
495d0d8f2a5Srin 	/* Restart auto-negotiation by setting the Auto Neg Enable bit and
496d0d8f2a5Srin 	 * the Auto Neg Restart bit in the PHY control register.
497d0d8f2a5Srin 	 */
498d0d8f2a5Srin 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
499d0d8f2a5Srin 	if (ret_val)
500d0d8f2a5Srin 		return ret_val;
501d0d8f2a5Srin 
502d0d8f2a5Srin 	phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
503d0d8f2a5Srin 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
504d0d8f2a5Srin 	if (ret_val)
505d0d8f2a5Srin 		return ret_val;
506d0d8f2a5Srin 
507d0d8f2a5Srin 	/* Does the user want to wait for Auto-Neg to complete here, or
508d0d8f2a5Srin 	 * check at a later time (for example, callback routine).
509d0d8f2a5Srin 	 */
510d0d8f2a5Srin 	if (phy->autoneg_wait_to_complete) {
511d0d8f2a5Srin 		ret_val = igc_wait_autoneg(hw);
512d0d8f2a5Srin 		if (ret_val)
513d0d8f2a5Srin 			return ret_val;
514d0d8f2a5Srin 	}
515d0d8f2a5Srin 
516d0d8f2a5Srin 	hw->mac.get_link_status = true;
517d0d8f2a5Srin 
518d0d8f2a5Srin 	return ret_val;
519d0d8f2a5Srin }
520d0d8f2a5Srin 
521d0d8f2a5Srin /**
522d0d8f2a5Srin  *  igc_setup_copper_link_generic - Configure copper link settings
523d0d8f2a5Srin  *  @hw: pointer to the HW structure
524d0d8f2a5Srin  *
525d0d8f2a5Srin  *  Calls the appropriate function to configure the link for auto-neg or forced
526d0d8f2a5Srin  *  speed and duplex.  Then we check for link, once link is established calls
527d0d8f2a5Srin  *  to configure collision distance and flow control are called.  If link is
528d0d8f2a5Srin  *  not established, we return -IGC_ERR_PHY (-2).
529d0d8f2a5Srin  **/
530d0d8f2a5Srin int
igc_setup_copper_link_generic(struct igc_hw * hw)531d0d8f2a5Srin igc_setup_copper_link_generic(struct igc_hw *hw)
532d0d8f2a5Srin {
533d0d8f2a5Srin 	int ret_val;
534d0d8f2a5Srin 	bool link;
535d0d8f2a5Srin 
536d0d8f2a5Srin 	DEBUGFUNC("igc_setup_copper_link_generic");
537d0d8f2a5Srin 
538d0d8f2a5Srin 	if (hw->mac.autoneg) {
539d0d8f2a5Srin 		/* Setup autoneg and flow control advertisement and perform
540d0d8f2a5Srin 		 * autonegotiation.
541d0d8f2a5Srin 		 */
542d0d8f2a5Srin 		ret_val = igc_copper_link_autoneg(hw);
543d0d8f2a5Srin 		if (ret_val)
544d0d8f2a5Srin 			return ret_val;
545d0d8f2a5Srin 	} else {
546d0d8f2a5Srin 		/* PHY will be set to 10H, 10F, 100H or 100F
547d0d8f2a5Srin 		 * depending on user settings.
548d0d8f2a5Srin 		 */
549d0d8f2a5Srin 		DEBUGOUT("Forcing Speed and Duplex\n");
550d0d8f2a5Srin 		ret_val = hw->phy.ops.force_speed_duplex(hw);
551d0d8f2a5Srin 		if (ret_val) {
552d0d8f2a5Srin 			DEBUGOUT("Error Forcing Speed and Duplex\n");
553d0d8f2a5Srin 			return ret_val;
554d0d8f2a5Srin 		}
555d0d8f2a5Srin 	}
556d0d8f2a5Srin 
557d0d8f2a5Srin 	/* Check link status. Wait up to 100 microseconds for link to become
558d0d8f2a5Srin 	 * valid.
559d0d8f2a5Srin 	 */
560d0d8f2a5Srin 	ret_val = igc_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10,
561d0d8f2a5Srin 	    &link);
562d0d8f2a5Srin 	if (ret_val)
563d0d8f2a5Srin 		return ret_val;
564d0d8f2a5Srin 
565d0d8f2a5Srin 	if (link) {
566d0d8f2a5Srin 		DEBUGOUT("Valid link established!!!\n");
567d0d8f2a5Srin 		hw->mac.ops.config_collision_dist(hw);
568d0d8f2a5Srin 		ret_val = igc_config_fc_after_link_up_generic(hw);
569d0d8f2a5Srin 	} else
570d0d8f2a5Srin 		DEBUGOUT("Unable to establish link!!!\n");
571d0d8f2a5Srin 
572d0d8f2a5Srin 	return ret_val;
573d0d8f2a5Srin }
574d0d8f2a5Srin 
575d0d8f2a5Srin /**
576d0d8f2a5Srin  *  igc_check_downshift_generic - Checks whether a downshift in speed occurred
577d0d8f2a5Srin  *  @hw: pointer to the HW structure
578d0d8f2a5Srin  *
579d0d8f2a5Srin  *  Success returns 0, Failure returns 1
580d0d8f2a5Srin  *
581d0d8f2a5Srin  *  A downshift is detected by querying the PHY link health.
582d0d8f2a5Srin  **/
583d0d8f2a5Srin int
igc_check_downshift_generic(struct igc_hw * hw)584d0d8f2a5Srin igc_check_downshift_generic(struct igc_hw *hw)
585d0d8f2a5Srin {
586d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
587d0d8f2a5Srin 	int ret_val;
588d0d8f2a5Srin 
589d0d8f2a5Srin 	DEBUGFUNC("igc_check_downshift_generic");
590d0d8f2a5Srin 
591d0d8f2a5Srin 	switch (phy->type) {
592d0d8f2a5Srin 	case igc_phy_i225:
593d0d8f2a5Srin 	default:
594d0d8f2a5Srin 		/* speed downshift not supported */
595d0d8f2a5Srin 		phy->speed_downgraded = false;
596d0d8f2a5Srin 		return IGC_SUCCESS;
597d0d8f2a5Srin 	}
598d0d8f2a5Srin 
599d0d8f2a5Srin 	return ret_val;
600d0d8f2a5Srin }
601d0d8f2a5Srin 
602d0d8f2a5Srin /**
603d0d8f2a5Srin  *  igc_wait_autoneg - Wait for auto-neg completion
604d0d8f2a5Srin  *  @hw: pointer to the HW structure
605d0d8f2a5Srin  *
606d0d8f2a5Srin  *  Waits for auto-negotiation to complete or for the auto-negotiation time
607d0d8f2a5Srin  *  limit to expire, which ever happens first.
608d0d8f2a5Srin  **/
609d0d8f2a5Srin int
igc_wait_autoneg(struct igc_hw * hw)610d0d8f2a5Srin igc_wait_autoneg(struct igc_hw *hw)
611d0d8f2a5Srin {
612d0d8f2a5Srin 	uint16_t i, phy_status;
613d0d8f2a5Srin 	int ret_val = IGC_SUCCESS;
614d0d8f2a5Srin 
615d0d8f2a5Srin 	DEBUGFUNC("igc_wait_autoneg");
616d0d8f2a5Srin 
617d0d8f2a5Srin 	if (!hw->phy.ops.read_reg)
618d0d8f2a5Srin 		return IGC_SUCCESS;
619d0d8f2a5Srin 
620d0d8f2a5Srin 	/* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */
621d0d8f2a5Srin 	for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) {
622*fb38d839Srin 		ret_val = hw->phy.ops.read_reg(hw, MII_BMSR, &phy_status);
623d0d8f2a5Srin 		if (ret_val)
624d0d8f2a5Srin 			break;
625*fb38d839Srin 		ret_val = hw->phy.ops.read_reg(hw, MII_BMSR, &phy_status);
626d0d8f2a5Srin 		if (ret_val)
627d0d8f2a5Srin 			break;
628d0d8f2a5Srin 		if (phy_status & MII_SR_AUTONEG_COMPLETE)
629d0d8f2a5Srin 			break;
630d0d8f2a5Srin 		msec_delay(100);
631d0d8f2a5Srin 	}
632d0d8f2a5Srin 
633d0d8f2a5Srin 	/* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
634d0d8f2a5Srin 	 * has completed.
635d0d8f2a5Srin 	 */
636d0d8f2a5Srin 	return ret_val;
637d0d8f2a5Srin }
638d0d8f2a5Srin 
639d0d8f2a5Srin /**
640d0d8f2a5Srin  *  igc_phy_has_link_generic - Polls PHY for link
641d0d8f2a5Srin  *  @hw: pointer to the HW structure
642d0d8f2a5Srin  *  @iterations: number of times to poll for link
643d0d8f2a5Srin  *  @usec_interval: delay between polling attempts
644d0d8f2a5Srin  *  @success: pointer to whether polling was successful or not
645d0d8f2a5Srin  *
646d0d8f2a5Srin  *  Polls the PHY status register for link, 'iterations' number of times.
647d0d8f2a5Srin  **/
648d0d8f2a5Srin int
igc_phy_has_link_generic(struct igc_hw * hw,uint32_t iterations,uint32_t usec_interval,bool * success)649d0d8f2a5Srin igc_phy_has_link_generic(struct igc_hw *hw, uint32_t iterations,
650d0d8f2a5Srin     uint32_t usec_interval, bool *success)
651d0d8f2a5Srin {
652d0d8f2a5Srin 	uint16_t i, phy_status;
653d0d8f2a5Srin 	int ret_val = IGC_SUCCESS;
654d0d8f2a5Srin 
655d0d8f2a5Srin 	DEBUGFUNC("igc_phy_has_link_generic");
656d0d8f2a5Srin 
657d0d8f2a5Srin 	if (!hw->phy.ops.read_reg)
658d0d8f2a5Srin 		return IGC_SUCCESS;
659d0d8f2a5Srin 
660d0d8f2a5Srin 	for (i = 0; i < iterations; i++) {
661*fb38d839Srin 		/* Some PHYs require the MII_BMSR register to be read
662d0d8f2a5Srin 		 * twice due to the link bit being sticky.  No harm doing
663d0d8f2a5Srin 		 * it across the board.
664d0d8f2a5Srin 		 */
665*fb38d839Srin 		ret_val = hw->phy.ops.read_reg(hw, MII_BMSR, &phy_status);
666d0d8f2a5Srin 		if (ret_val) {
667d0d8f2a5Srin 			/* If the first read fails, another entity may have
668d0d8f2a5Srin 			 * ownership of the resources, wait and try again to
669d0d8f2a5Srin 			 * see if they have relinquished the resources yet.
670d0d8f2a5Srin 			 */
671d0d8f2a5Srin 			if (usec_interval >= 1000)
672d0d8f2a5Srin 				msec_delay(usec_interval/1000);
673d0d8f2a5Srin 			else
674d0d8f2a5Srin 				DELAY(usec_interval);
675d0d8f2a5Srin 		}
676*fb38d839Srin 		ret_val = hw->phy.ops.read_reg(hw, MII_BMSR, &phy_status);
677d0d8f2a5Srin 		if (ret_val)
678d0d8f2a5Srin 			break;
679d0d8f2a5Srin 		if (phy_status & MII_SR_LINK_STATUS)
680d0d8f2a5Srin 			break;
681d0d8f2a5Srin 		if (usec_interval >= 1000)
682d0d8f2a5Srin 			msec_delay(usec_interval/1000);
683d0d8f2a5Srin 		else
684d0d8f2a5Srin 			DELAY(usec_interval);
685d0d8f2a5Srin 	}
686d0d8f2a5Srin 
687d0d8f2a5Srin 	*success = (i < iterations);
688d0d8f2a5Srin 
689d0d8f2a5Srin 	return ret_val;
690d0d8f2a5Srin }
691d0d8f2a5Srin 
692d0d8f2a5Srin /**
693d0d8f2a5Srin  *  igc_phy_hw_reset_generic - PHY hardware reset
694d0d8f2a5Srin  *  @hw: pointer to the HW structure
695d0d8f2a5Srin  *
696d0d8f2a5Srin  *  Verify the reset block is not blocking us from resetting.  Acquire
697d0d8f2a5Srin  *  semaphore (if necessary) and read/set/write the device control reset
698d0d8f2a5Srin  *  bit in the PHY.  Wait the appropriate delay time for the device to
699d0d8f2a5Srin  *  reset and release the semaphore (if necessary).
700d0d8f2a5Srin  **/
701d0d8f2a5Srin int
igc_phy_hw_reset_generic(struct igc_hw * hw)702d0d8f2a5Srin igc_phy_hw_reset_generic(struct igc_hw *hw)
703d0d8f2a5Srin {
704d0d8f2a5Srin 	struct igc_phy_info *phy = &hw->phy;
705d0d8f2a5Srin 	uint32_t ctrl, timeout = 10000, phpm = 0;
706d0d8f2a5Srin 	int ret_val;
707d0d8f2a5Srin 
708d0d8f2a5Srin 	DEBUGFUNC("igc_phy_hw_reset_generic");
709d0d8f2a5Srin 
710d0d8f2a5Srin 	if (phy->ops.check_reset_block) {
711d0d8f2a5Srin 		ret_val = phy->ops.check_reset_block(hw);
712d0d8f2a5Srin 		if (ret_val)
713d0d8f2a5Srin 			return IGC_SUCCESS;
714d0d8f2a5Srin 	}
715d0d8f2a5Srin 
716d0d8f2a5Srin 	ret_val = phy->ops.acquire(hw);
717d0d8f2a5Srin 	if (ret_val)
718d0d8f2a5Srin 		return ret_val;
719d0d8f2a5Srin 
720d0d8f2a5Srin 	phpm = IGC_READ_REG(hw, IGC_I225_PHPM);
721d0d8f2a5Srin 
722d0d8f2a5Srin 	ctrl = IGC_READ_REG(hw, IGC_CTRL);
723d0d8f2a5Srin 	IGC_WRITE_REG(hw, IGC_CTRL, ctrl | IGC_CTRL_PHY_RST);
724d0d8f2a5Srin 	IGC_WRITE_FLUSH(hw);
725d0d8f2a5Srin 
726d0d8f2a5Srin 	DELAY(phy->reset_delay_us);
727d0d8f2a5Srin 
728d0d8f2a5Srin 	IGC_WRITE_REG(hw, IGC_CTRL, ctrl);
729d0d8f2a5Srin 	IGC_WRITE_FLUSH(hw);
730d0d8f2a5Srin 
731d0d8f2a5Srin 	DELAY(150);
732d0d8f2a5Srin 
733d0d8f2a5Srin 	do {
734d0d8f2a5Srin 		phpm = IGC_READ_REG(hw, IGC_I225_PHPM);
735d0d8f2a5Srin 		timeout--;
736d0d8f2a5Srin 		DELAY(1);
737d0d8f2a5Srin 	} while (!(phpm & IGC_I225_PHPM_RST_COMPL) && timeout);
738d0d8f2a5Srin 
739d0d8f2a5Srin 	if (!timeout)
740d0d8f2a5Srin 		DEBUGOUT("Timeout expired after a phy reset\n");
741d0d8f2a5Srin 
742d0d8f2a5Srin 	phy->ops.release(hw);
743d0d8f2a5Srin 
744d0d8f2a5Srin 	return ret_val;
745d0d8f2a5Srin }
746d0d8f2a5Srin 
747d0d8f2a5Srin /**
748d0d8f2a5Srin  * igc_power_up_phy_copper - Restore copper link in case of PHY power down
749d0d8f2a5Srin  * @hw: pointer to the HW structure
750d0d8f2a5Srin  *
751d0d8f2a5Srin  * In the case of a PHY power down to save power, or to turn off link during a
752d0d8f2a5Srin  * driver unload, or wake on lan is not enabled, restore the link to previous
753d0d8f2a5Srin  * settings.
754d0d8f2a5Srin  **/
755d0d8f2a5Srin void
igc_power_up_phy_copper(struct igc_hw * hw)756d0d8f2a5Srin igc_power_up_phy_copper(struct igc_hw *hw)
757d0d8f2a5Srin {
758d0d8f2a5Srin 	uint16_t mii_reg = 0;
759d0d8f2a5Srin 
760d0d8f2a5Srin 	/* The PHY will retain its settings across a power down/up cycle */
761d0d8f2a5Srin 	hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
762d0d8f2a5Srin 	mii_reg &= ~MII_CR_POWER_DOWN;
763d0d8f2a5Srin 	hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
764d0d8f2a5Srin 	DELAY(300);
765d0d8f2a5Srin }
766d0d8f2a5Srin 
767d0d8f2a5Srin /**
768d0d8f2a5Srin  * igc_power_down_phy_copper - Restore copper link in case of PHY power down
769d0d8f2a5Srin  * @hw: pointer to the HW structure
770d0d8f2a5Srin  *
771d0d8f2a5Srin  * In the case of a PHY power down to save power, or to turn off link during a
772d0d8f2a5Srin  * driver unload, or wake on lan is not enabled, restore the link to previous
773d0d8f2a5Srin  * settings.
774d0d8f2a5Srin  **/
775d0d8f2a5Srin void
igc_power_down_phy_copper(struct igc_hw * hw)776d0d8f2a5Srin igc_power_down_phy_copper(struct igc_hw *hw)
777d0d8f2a5Srin {
778d0d8f2a5Srin 	uint16_t mii_reg = 0;
779d0d8f2a5Srin 
780d0d8f2a5Srin 	/* The PHY will retain its settings across a power down/up cycle */
781d0d8f2a5Srin 	hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
782d0d8f2a5Srin 	mii_reg |= MII_CR_POWER_DOWN;
783d0d8f2a5Srin 	hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
784d0d8f2a5Srin 	msec_delay(1);
785d0d8f2a5Srin }
786d0d8f2a5Srin 
787d0d8f2a5Srin /**
788d0d8f2a5Srin  *  igc_write_phy_reg_gpy - Write GPY PHY register
789d0d8f2a5Srin  *  @hw: pointer to the HW structure
790d0d8f2a5Srin  *  @offset: register offset to write to
791d0d8f2a5Srin  *  @data: data to write at register offset
792d0d8f2a5Srin  *
793d0d8f2a5Srin  *  Acquires semaphore, if necessary, then writes the data to PHY register
794d0d8f2a5Srin  *  at the offset.  Release any acquired semaphores before exiting.
795d0d8f2a5Srin  **/
796d0d8f2a5Srin int
igc_write_phy_reg_gpy(struct igc_hw * hw,uint32_t offset,uint16_t data)797d0d8f2a5Srin igc_write_phy_reg_gpy(struct igc_hw *hw, uint32_t offset, uint16_t data)
798d0d8f2a5Srin {
799d0d8f2a5Srin 	uint8_t dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
800d0d8f2a5Srin 	int ret_val;
801d0d8f2a5Srin 
802d0d8f2a5Srin 	DEBUGFUNC("igc_write_phy_reg_gpy");
803d0d8f2a5Srin 
804d0d8f2a5Srin 	offset = offset & GPY_REG_MASK;
805d0d8f2a5Srin 
806d0d8f2a5Srin 	if (!dev_addr) {
807d0d8f2a5Srin 		ret_val = hw->phy.ops.acquire(hw);
808d0d8f2a5Srin 		if (ret_val)
809d0d8f2a5Srin 			return ret_val;
810d0d8f2a5Srin 		ret_val = igc_write_phy_reg_mdic(hw, offset, data);
811d0d8f2a5Srin 		if (ret_val)
812d0d8f2a5Srin 			return ret_val;
813d0d8f2a5Srin 		hw->phy.ops.release(hw);
814d0d8f2a5Srin 	} else {
815d0d8f2a5Srin 		ret_val = igc_write_xmdio_reg(hw, (uint16_t)offset, dev_addr,
816d0d8f2a5Srin 		    data);
817d0d8f2a5Srin 	}
818d0d8f2a5Srin 
819d0d8f2a5Srin 	return ret_val;
820d0d8f2a5Srin }
821d0d8f2a5Srin 
822d0d8f2a5Srin /**
823d0d8f2a5Srin  *  igc_read_phy_reg_gpy - Read GPY PHY register
824d0d8f2a5Srin  *  @hw: pointer to the HW structure
825d0d8f2a5Srin  *  @offset: lower half is register offset to read to
826d0d8f2a5Srin  *     upper half is MMD to use.
827d0d8f2a5Srin  *  @data: data to read at register offset
828d0d8f2a5Srin  *
829d0d8f2a5Srin  *  Acquires semaphore, if necessary, then reads the data in the PHY register
830d0d8f2a5Srin  *  at the offset.  Release any acquired semaphores before exiting.
831d0d8f2a5Srin  **/
832d0d8f2a5Srin int
igc_read_phy_reg_gpy(struct igc_hw * hw,uint32_t offset,uint16_t * data)833d0d8f2a5Srin igc_read_phy_reg_gpy(struct igc_hw *hw, uint32_t offset, uint16_t *data)
834d0d8f2a5Srin {
835d0d8f2a5Srin 	uint8_t dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
836d0d8f2a5Srin 	int ret_val;
837d0d8f2a5Srin 
838d0d8f2a5Srin 	DEBUGFUNC("igc_read_phy_reg_gpy");
839d0d8f2a5Srin 
840d0d8f2a5Srin 	offset = offset & GPY_REG_MASK;
841d0d8f2a5Srin 
842d0d8f2a5Srin 	if (!dev_addr) {
843d0d8f2a5Srin 		ret_val = hw->phy.ops.acquire(hw);
844d0d8f2a5Srin 		if (ret_val)
845d0d8f2a5Srin 			return ret_val;
846d0d8f2a5Srin 		ret_val = igc_read_phy_reg_mdic(hw, offset, data);
847d0d8f2a5Srin 		if (ret_val)
848d0d8f2a5Srin 			return ret_val;
849d0d8f2a5Srin 		hw->phy.ops.release(hw);
850d0d8f2a5Srin 	} else {
851d0d8f2a5Srin 		ret_val = igc_read_xmdio_reg(hw, (uint16_t)offset, dev_addr,
852d0d8f2a5Srin 		    data);
853d0d8f2a5Srin 	}
854d0d8f2a5Srin 
855d0d8f2a5Srin 	return ret_val;
856d0d8f2a5Srin }
857d0d8f2a5Srin 
858d0d8f2a5Srin /**
859d0d8f2a5Srin  *  __igc_access_xmdio_reg - Read/write XMDIO register
860d0d8f2a5Srin  *  @hw: pointer to the HW structure
861d0d8f2a5Srin  *  @address: XMDIO address to program
862d0d8f2a5Srin  *  @dev_addr: device address to program
863d0d8f2a5Srin  *  @data: pointer to value to read/write from/to the XMDIO address
864d0d8f2a5Srin  *  @read: boolean flag to indicate read or write
865d0d8f2a5Srin  **/
866*fb38d839Srin static int
__igc_access_xmdio_reg(struct igc_hw * hw,uint16_t address,uint8_t dev_addr,uint16_t * data,bool read)867d0d8f2a5Srin __igc_access_xmdio_reg(struct igc_hw *hw, uint16_t address, uint8_t dev_addr,
868d0d8f2a5Srin     uint16_t *data, bool read)
869d0d8f2a5Srin {
870d0d8f2a5Srin 	int ret_val;
871d0d8f2a5Srin 
872d0d8f2a5Srin 	DEBUGFUNC("__igc_access_xmdio_reg");
873d0d8f2a5Srin 
874d0d8f2a5Srin 	ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr);
875d0d8f2a5Srin 	if (ret_val)
876d0d8f2a5Srin 		return ret_val;
877d0d8f2a5Srin 
878d0d8f2a5Srin 	ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address);
879d0d8f2a5Srin 	if (ret_val)
880d0d8f2a5Srin 		return ret_val;
881d0d8f2a5Srin 
882d0d8f2a5Srin 	ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA |
883d0d8f2a5Srin 	    dev_addr);
884d0d8f2a5Srin 	if (ret_val)
885d0d8f2a5Srin 		return ret_val;
886d0d8f2a5Srin 
887d0d8f2a5Srin 	if (read)
888d0d8f2a5Srin 		ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data);
889d0d8f2a5Srin 	else
890d0d8f2a5Srin 		ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data);
891d0d8f2a5Srin 	if (ret_val)
892d0d8f2a5Srin 		return ret_val;
893d0d8f2a5Srin 
894d0d8f2a5Srin 	/* Recalibrate the device back to 0 */
895d0d8f2a5Srin 	ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0);
896d0d8f2a5Srin 	if (ret_val)
897d0d8f2a5Srin 		return ret_val;
898d0d8f2a5Srin 
899d0d8f2a5Srin 	return ret_val;
900d0d8f2a5Srin }
901d0d8f2a5Srin 
902d0d8f2a5Srin /**
903d0d8f2a5Srin  *  igc_read_xmdio_reg - Read XMDIO register
904d0d8f2a5Srin  *  @hw: pointer to the HW structure
905d0d8f2a5Srin  *  @addr: XMDIO address to program
906d0d8f2a5Srin  *  @dev_addr: device address to program
907d0d8f2a5Srin  *  @data: value to be read from the EMI address
908d0d8f2a5Srin  **/
909d0d8f2a5Srin int
igc_read_xmdio_reg(struct igc_hw * hw,uint16_t addr,uint8_t dev_addr,uint16_t * data)910d0d8f2a5Srin igc_read_xmdio_reg(struct igc_hw *hw, uint16_t addr, uint8_t dev_addr,
911d0d8f2a5Srin     uint16_t *data)
912d0d8f2a5Srin {
913d0d8f2a5Srin 	DEBUGFUNC("igc_read_xmdio_reg");
914d0d8f2a5Srin 
915d0d8f2a5Srin 	return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true);
916d0d8f2a5Srin }
917d0d8f2a5Srin 
918d0d8f2a5Srin /**
919d0d8f2a5Srin  *  igc_write_xmdio_reg - Write XMDIO register
920d0d8f2a5Srin  *  @hw: pointer to the HW structure
921d0d8f2a5Srin  *  @addr: XMDIO address to program
922d0d8f2a5Srin  *  @dev_addr: device address to program
923d0d8f2a5Srin  *  @data: value to be written to the XMDIO address
924d0d8f2a5Srin  **/
925d0d8f2a5Srin int
igc_write_xmdio_reg(struct igc_hw * hw,uint16_t addr,uint8_t dev_addr,uint16_t data)926d0d8f2a5Srin igc_write_xmdio_reg(struct igc_hw *hw, uint16_t addr, uint8_t dev_addr,
927d0d8f2a5Srin     uint16_t data)
928d0d8f2a5Srin {
929d0d8f2a5Srin 	DEBUGFUNC("igc_write_xmdio_reg");
930d0d8f2a5Srin 
931d0d8f2a5Srin 	return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false);
932d0d8f2a5Srin }
933