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