xref: /onnv-gate/usr/src/uts/common/io/igb/igb_phy.c (revision 10319:0355b7a83c0d)
15779Sxy150489 /*
25779Sxy150489  * CDDL HEADER START
35779Sxy150489  *
48571SChenlu.Chen@Sun.COM  * Copyright(c) 2007-2009 Intel Corporation. All rights reserved.
55779Sxy150489  * The contents of this file are subject to the terms of the
65779Sxy150489  * Common Development and Distribution License (the "License").
75779Sxy150489  * You may not use this file except in compliance with the License.
85779Sxy150489  *
95779Sxy150489  * You can obtain a copy of the license at:
105779Sxy150489  *	http://www.opensolaris.org/os/licensing.
115779Sxy150489  * See the License for the specific language governing permissions
125779Sxy150489  * and limitations under the License.
135779Sxy150489  *
145779Sxy150489  * When using or redistributing this file, you may do so under the
155779Sxy150489  * License only. No other modification of this header is permitted.
165779Sxy150489  *
175779Sxy150489  * If applicable, add the following below this CDDL HEADER, with the
185779Sxy150489  * fields enclosed by brackets "[]" replaced with your own identifying
195779Sxy150489  * information: Portions Copyright [yyyy] [name of copyright owner]
205779Sxy150489  *
215779Sxy150489  * CDDL HEADER END
225779Sxy150489  */
235779Sxy150489 
245779Sxy150489 /*
258571SChenlu.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
265779Sxy150489  * Use is subject to license terms of the CDDL.
275779Sxy150489  */
285779Sxy150489 
29*10319SJason.Xu@Sun.COM /* IntelVersion: 1.140 v2-9-8_2009-6-12 */
305779Sxy150489 
315779Sxy150489 #include "igb_api.h"
325779Sxy150489 
338571SChenlu.Chen@Sun.COM static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw);
345779Sxy150489 
355779Sxy150489 /* Cable length tables */
365779Sxy150489 static const u16 e1000_m88_cable_length_table[] =
375779Sxy150489 	{ 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
385779Sxy150489 
395779Sxy150489 #define	M88E1000_CABLE_LENGTH_TABLE_SIZE \
405779Sxy150489 	(sizeof (e1000_m88_cable_length_table) / \
415779Sxy150489 	sizeof (e1000_m88_cable_length_table[0]))
425779Sxy150489 
435779Sxy150489 static const u16 e1000_igp_2_cable_length_table[] =
445779Sxy150489 	{ 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
455779Sxy150489 	0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41,
465779Sxy150489 	6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61,
475779Sxy150489 	21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82,
485779Sxy150489 	40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104,
495779Sxy150489 	60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121,
505779Sxy150489 	83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
515779Sxy150489 	104, 109, 114, 118, 121, 124};
525779Sxy150489 
535779Sxy150489 #define	IGP02E1000_CABLE_LENGTH_TABLE_SIZE \
545779Sxy150489 	(sizeof (e1000_igp_2_cable_length_table) / \
555779Sxy150489 	sizeof (e1000_igp_2_cable_length_table[0]))
565779Sxy150489 
575779Sxy150489 /*
588571SChenlu.Chen@Sun.COM  * e1000_init_phy_ops_generic - Initialize PHY function pointers
598571SChenlu.Chen@Sun.COM  * @hw: pointer to the HW structure
608571SChenlu.Chen@Sun.COM  *
618571SChenlu.Chen@Sun.COM  * Setups up the function pointers to no-op functions
628571SChenlu.Chen@Sun.COM  */
638571SChenlu.Chen@Sun.COM void
648571SChenlu.Chen@Sun.COM e1000_init_phy_ops_generic(struct e1000_hw *hw)
658571SChenlu.Chen@Sun.COM {
668571SChenlu.Chen@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
678571SChenlu.Chen@Sun.COM 	DEBUGFUNC("e1000_init_phy_ops_generic");
688571SChenlu.Chen@Sun.COM 
698571SChenlu.Chen@Sun.COM 	/* Initialize function pointers */
708571SChenlu.Chen@Sun.COM 	phy->ops.init_params = e1000_null_ops_generic;
718571SChenlu.Chen@Sun.COM 	phy->ops.acquire = e1000_null_ops_generic;
728571SChenlu.Chen@Sun.COM 	phy->ops.check_polarity = e1000_null_ops_generic;
738571SChenlu.Chen@Sun.COM 	phy->ops.check_reset_block = e1000_null_ops_generic;
748571SChenlu.Chen@Sun.COM 	phy->ops.commit = e1000_null_ops_generic;
758571SChenlu.Chen@Sun.COM 	phy->ops.force_speed_duplex = e1000_null_ops_generic;
768571SChenlu.Chen@Sun.COM 	phy->ops.get_cfg_done = e1000_null_ops_generic;
778571SChenlu.Chen@Sun.COM 	phy->ops.get_cable_length = e1000_null_ops_generic;
788571SChenlu.Chen@Sun.COM 	phy->ops.get_info = e1000_null_ops_generic;
798571SChenlu.Chen@Sun.COM 	phy->ops.read_reg = e1000_null_read_reg;
808571SChenlu.Chen@Sun.COM 	phy->ops.release = e1000_null_phy_generic;
818571SChenlu.Chen@Sun.COM 	phy->ops.reset = e1000_null_ops_generic;
828571SChenlu.Chen@Sun.COM 	phy->ops.set_d0_lplu_state = e1000_null_lplu_state;
838571SChenlu.Chen@Sun.COM 	phy->ops.set_d3_lplu_state = e1000_null_lplu_state;
848571SChenlu.Chen@Sun.COM 	phy->ops.write_reg = e1000_null_write_reg;
858571SChenlu.Chen@Sun.COM 	phy->ops.power_up = e1000_null_phy_generic;
868571SChenlu.Chen@Sun.COM 	phy->ops.power_down = e1000_null_phy_generic;
878571SChenlu.Chen@Sun.COM }
888571SChenlu.Chen@Sun.COM 
898571SChenlu.Chen@Sun.COM /*
908571SChenlu.Chen@Sun.COM  * e1000_null_read_reg - No-op function, return 0
918571SChenlu.Chen@Sun.COM  * @hw: pointer to the HW structure
928571SChenlu.Chen@Sun.COM  */
938571SChenlu.Chen@Sun.COM s32
948571SChenlu.Chen@Sun.COM e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data)
958571SChenlu.Chen@Sun.COM {
968571SChenlu.Chen@Sun.COM 	DEBUGFUNC("e1000_null_read_reg");
978571SChenlu.Chen@Sun.COM 	UNREFERENCED_3PARAMETER(hw, offset, data);
988571SChenlu.Chen@Sun.COM 	return (E1000_SUCCESS);
998571SChenlu.Chen@Sun.COM }
1008571SChenlu.Chen@Sun.COM 
1018571SChenlu.Chen@Sun.COM /*
1028571SChenlu.Chen@Sun.COM  * e1000_null_phy_generic - No-op function, return void
1038571SChenlu.Chen@Sun.COM  * @hw: pointer to the HW structure
1048571SChenlu.Chen@Sun.COM  */
1058571SChenlu.Chen@Sun.COM void
1068571SChenlu.Chen@Sun.COM e1000_null_phy_generic(struct e1000_hw *hw)
1078571SChenlu.Chen@Sun.COM {
1088571SChenlu.Chen@Sun.COM 	DEBUGFUNC("e1000_null_phy_generic");
1098571SChenlu.Chen@Sun.COM 	UNREFERENCED_1PARAMETER(hw);
1108571SChenlu.Chen@Sun.COM }
1118571SChenlu.Chen@Sun.COM 
1128571SChenlu.Chen@Sun.COM /*
1138571SChenlu.Chen@Sun.COM  * e1000_null_lplu_state - No-op function, return 0
1148571SChenlu.Chen@Sun.COM  * @hw: pointer to the HW structure
1158571SChenlu.Chen@Sun.COM  */
1168571SChenlu.Chen@Sun.COM s32
1178571SChenlu.Chen@Sun.COM e1000_null_lplu_state(struct e1000_hw *hw, bool active)
1188571SChenlu.Chen@Sun.COM {
1198571SChenlu.Chen@Sun.COM 	DEBUGFUNC("e1000_null_lplu_state");
1208571SChenlu.Chen@Sun.COM 	UNREFERENCED_2PARAMETER(hw, active);
1218571SChenlu.Chen@Sun.COM 	return (E1000_SUCCESS);
1228571SChenlu.Chen@Sun.COM }
1238571SChenlu.Chen@Sun.COM 
1248571SChenlu.Chen@Sun.COM /*
1258571SChenlu.Chen@Sun.COM  * e1000_null_write_reg - No-op function, return 0
1268571SChenlu.Chen@Sun.COM  * @hw: pointer to the HW structure
1278571SChenlu.Chen@Sun.COM  */
1288571SChenlu.Chen@Sun.COM s32
1298571SChenlu.Chen@Sun.COM e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data)
1308571SChenlu.Chen@Sun.COM {
1318571SChenlu.Chen@Sun.COM 	DEBUGFUNC("e1000_null_write_reg");
1328571SChenlu.Chen@Sun.COM 	UNREFERENCED_3PARAMETER(hw, offset, data);
1338571SChenlu.Chen@Sun.COM 	return (E1000_SUCCESS);
1348571SChenlu.Chen@Sun.COM }
1358571SChenlu.Chen@Sun.COM 
1368571SChenlu.Chen@Sun.COM /*
1375779Sxy150489  * e1000_check_reset_block_generic - Check if PHY reset is blocked
1385779Sxy150489  * @hw: pointer to the HW structure
1395779Sxy150489  *
1405779Sxy150489  * Read the PHY management control register and check whether a PHY reset
1415779Sxy150489  * is blocked.  If a reset is not blocked return E1000_SUCCESS, otherwise
1425779Sxy150489  * return E1000_BLK_PHY_RESET (12).
1435779Sxy150489  */
1445779Sxy150489 s32
1455779Sxy150489 e1000_check_reset_block_generic(struct e1000_hw *hw)
1465779Sxy150489 {
1475779Sxy150489 	u32 manc;
1485779Sxy150489 
1495779Sxy150489 	DEBUGFUNC("e1000_check_reset_block");
1505779Sxy150489 
1515779Sxy150489 	manc = E1000_READ_REG(hw, E1000_MANC);
1525779Sxy150489 
1535779Sxy150489 	return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
1545779Sxy150489 	    E1000_BLK_PHY_RESET : E1000_SUCCESS;
1555779Sxy150489 }
1565779Sxy150489 
1575779Sxy150489 /*
1585779Sxy150489  * e1000_get_phy_id - Retrieve the PHY ID and revision
1595779Sxy150489  * @hw: pointer to the HW structure
1605779Sxy150489  *
1615779Sxy150489  * Reads the PHY registers and stores the PHY ID and possibly the PHY
1625779Sxy150489  * revision in the hardware structure.
1635779Sxy150489  */
1645779Sxy150489 s32
1655779Sxy150489 e1000_get_phy_id(struct e1000_hw *hw)
1665779Sxy150489 {
1675779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
1685779Sxy150489 	s32 ret_val = E1000_SUCCESS;
1695779Sxy150489 	u16 phy_id;
1705779Sxy150489 
1715779Sxy150489 	DEBUGFUNC("e1000_get_phy_id");
1725779Sxy150489 
1738571SChenlu.Chen@Sun.COM 	if (!(phy->ops.read_reg))
1748571SChenlu.Chen@Sun.COM 		goto out;
1758571SChenlu.Chen@Sun.COM 
1768571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
1775779Sxy150489 	if (ret_val)
1785779Sxy150489 		goto out;
1795779Sxy150489 
1805779Sxy150489 	phy->id = (u32)(phy_id << 16);
1815779Sxy150489 	usec_delay(20);
1828571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
1835779Sxy150489 	if (ret_val)
1845779Sxy150489 		goto out;
1855779Sxy150489 
1865779Sxy150489 	phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
1875779Sxy150489 	phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
1885779Sxy150489 
1895779Sxy150489 out:
1905779Sxy150489 	return (ret_val);
1915779Sxy150489 }
1925779Sxy150489 
1935779Sxy150489 /*
1945779Sxy150489  * e1000_phy_reset_dsp_generic - Reset PHY DSP
1955779Sxy150489  * @hw: pointer to the HW structure
1965779Sxy150489  *
1975779Sxy150489  * Reset the digital signal processor.
1985779Sxy150489  */
1995779Sxy150489 s32
2005779Sxy150489 e1000_phy_reset_dsp_generic(struct e1000_hw *hw)
2015779Sxy150489 {
2028571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
2035779Sxy150489 
2045779Sxy150489 	DEBUGFUNC("e1000_phy_reset_dsp_generic");
2055779Sxy150489 
2068571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.write_reg))
2078571SChenlu.Chen@Sun.COM 		goto out;
2088571SChenlu.Chen@Sun.COM 
2098571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1);
2105779Sxy150489 	if (ret_val)
2115779Sxy150489 		goto out;
2125779Sxy150489 
2138571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0);
2145779Sxy150489 
2155779Sxy150489 out:
2165779Sxy150489 	return (ret_val);
2175779Sxy150489 }
2185779Sxy150489 
2195779Sxy150489 /*
2205779Sxy150489  * e1000_read_phy_reg_mdic - Read MDI control register
2215779Sxy150489  * @hw: pointer to the HW structure
2225779Sxy150489  * @offset: register offset to be read
2235779Sxy150489  * @data: pointer to the read data
2245779Sxy150489  *
2258571SChenlu.Chen@Sun.COM  * Reads the MDI control register in the PHY at offset and stores the
2265779Sxy150489  * information read to data.
2275779Sxy150489  */
2285779Sxy150489 s32
2295779Sxy150489 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
2305779Sxy150489 {
2315779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
2325779Sxy150489 	u32 i, mdic = 0;
2335779Sxy150489 	s32 ret_val = E1000_SUCCESS;
2345779Sxy150489 
2355779Sxy150489 	DEBUGFUNC("e1000_read_phy_reg_mdic");
2365779Sxy150489 
2375779Sxy150489 	/*
2385779Sxy150489 	 * Set up Op-code, Phy Address, and register offset in the MDI
2395779Sxy150489 	 * Control register.  The MAC will take care of interfacing with the
2405779Sxy150489 	 * PHY to retrieve the desired data.
2415779Sxy150489 	 */
2425779Sxy150489 	mdic = ((offset << E1000_MDIC_REG_SHIFT) |
2435779Sxy150489 	    (phy->addr << E1000_MDIC_PHY_SHIFT) |
2445779Sxy150489 	    (E1000_MDIC_OP_READ));
2455779Sxy150489 
2465779Sxy150489 	E1000_WRITE_REG(hw, E1000_MDIC, mdic);
2475779Sxy150489 
2485779Sxy150489 	/*
2495779Sxy150489 	 * Poll the ready bit to see if the MDI read completed
2505779Sxy150489 	 * Increasing the time out as testing showed failures with
2515779Sxy150489 	 * the lower time out
2525779Sxy150489 	 */
2535779Sxy150489 	for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
2545779Sxy150489 		usec_delay(50);
2555779Sxy150489 		mdic = E1000_READ_REG(hw, E1000_MDIC);
2565779Sxy150489 		if (mdic & E1000_MDIC_READY)
2575779Sxy150489 			break;
2585779Sxy150489 	}
2595779Sxy150489 	if (!(mdic & E1000_MDIC_READY)) {
2605779Sxy150489 		DEBUGOUT("MDI Read did not complete\n");
2615779Sxy150489 		ret_val = -E1000_ERR_PHY;
2625779Sxy150489 		goto out;
2635779Sxy150489 	}
2645779Sxy150489 	if (mdic & E1000_MDIC_ERROR) {
2655779Sxy150489 		DEBUGOUT("MDI Error\n");
2665779Sxy150489 		ret_val = -E1000_ERR_PHY;
2675779Sxy150489 		goto out;
2685779Sxy150489 	}
2695779Sxy150489 	*data = (u16) mdic;
2705779Sxy150489 
2715779Sxy150489 out:
2725779Sxy150489 	return (ret_val);
2735779Sxy150489 }
2745779Sxy150489 
2755779Sxy150489 /*
2765779Sxy150489  * e1000_write_phy_reg_mdic - Write MDI control register
2775779Sxy150489  * @hw: pointer to the HW structure
2785779Sxy150489  * @offset: register offset to write to
2795779Sxy150489  * @data: data to write to register at offset
2805779Sxy150489  *
2815779Sxy150489  * Writes data to MDI control register in the PHY at offset.
2825779Sxy150489  */
2835779Sxy150489 s32
2845779Sxy150489 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
2855779Sxy150489 {
2865779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
2875779Sxy150489 	u32 i, mdic = 0;
2885779Sxy150489 	s32 ret_val = E1000_SUCCESS;
2895779Sxy150489 
2905779Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_mdic");
2915779Sxy150489 
2925779Sxy150489 	/*
2935779Sxy150489 	 * Set up Op-code, Phy Address, and register offset in the MDI
2945779Sxy150489 	 * Control register.  The MAC will take care of interfacing with the
2955779Sxy150489 	 * PHY to retrieve the desired data.
2965779Sxy150489 	 */
2975779Sxy150489 	mdic = (((u32)data) |
2985779Sxy150489 	    (offset << E1000_MDIC_REG_SHIFT) |
2995779Sxy150489 	    (phy->addr << E1000_MDIC_PHY_SHIFT) |
3005779Sxy150489 	    (E1000_MDIC_OP_WRITE));
3015779Sxy150489 
3025779Sxy150489 	E1000_WRITE_REG(hw, E1000_MDIC, mdic);
3035779Sxy150489 
3045779Sxy150489 	/*
3055779Sxy150489 	 * Poll the ready bit to see if the MDI read completed
3065779Sxy150489 	 * Increasing the time out as testing showed failures with
3075779Sxy150489 	 * the lower time out
3085779Sxy150489 	 */
3095779Sxy150489 	for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
3105779Sxy150489 		usec_delay(50);
3115779Sxy150489 		mdic = E1000_READ_REG(hw, E1000_MDIC);
3125779Sxy150489 		if (mdic & E1000_MDIC_READY)
3135779Sxy150489 			break;
3145779Sxy150489 	}
3155779Sxy150489 	if (!(mdic & E1000_MDIC_READY)) {
3165779Sxy150489 		DEBUGOUT("MDI Write did not complete\n");
3175779Sxy150489 		ret_val = -E1000_ERR_PHY;
3185779Sxy150489 		goto out;
3195779Sxy150489 	}
3205779Sxy150489 	if (mdic & E1000_MDIC_ERROR) {
3215779Sxy150489 		DEBUGOUT("MDI Error\n");
3225779Sxy150489 		ret_val = -E1000_ERR_PHY;
3235779Sxy150489 		goto out;
3245779Sxy150489 	}
3255779Sxy150489 
3265779Sxy150489 out:
3275779Sxy150489 	return (ret_val);
3285779Sxy150489 }
3295779Sxy150489 
3305779Sxy150489 /*
3315779Sxy150489  * e1000_read_phy_reg_m88 - Read m88 PHY register
3325779Sxy150489  * @hw: pointer to the HW structure
3335779Sxy150489  * @offset: register offset to be read
3345779Sxy150489  * @data: pointer to the read data
3355779Sxy150489  *
3365779Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
3375779Sxy150489  * and storing the retrieved information in data.  Release any acquired
3385779Sxy150489  * semaphores before exiting.
3395779Sxy150489  */
3405779Sxy150489 s32
3415779Sxy150489 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data)
3425779Sxy150489 {
3438571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
3445779Sxy150489 
3455779Sxy150489 	DEBUGFUNC("e1000_read_phy_reg_m88");
3465779Sxy150489 
3478571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
3488571SChenlu.Chen@Sun.COM 		goto out;
3498571SChenlu.Chen@Sun.COM 
3508571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
3515779Sxy150489 	if (ret_val)
3525779Sxy150489 		goto out;
3535779Sxy150489 
3545779Sxy150489 	ret_val = e1000_read_phy_reg_mdic(hw,
3555779Sxy150489 	    MAX_PHY_REG_ADDRESS & offset, data);
3565779Sxy150489 
3578571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
3585779Sxy150489 
3595779Sxy150489 out:
3605779Sxy150489 	return (ret_val);
3615779Sxy150489 }
3625779Sxy150489 
3635779Sxy150489 /*
3645779Sxy150489  * e1000_write_phy_reg_m88 - Write m88 PHY register
3655779Sxy150489  * @hw: pointer to the HW structure
3665779Sxy150489  * @offset: register offset to write to
3675779Sxy150489  * @data: data to write at register offset
3685779Sxy150489  *
3695779Sxy150489  * Acquires semaphore, if necessary, then writes the data to PHY register
3705779Sxy150489  * at the offset.  Release any acquired semaphores before exiting.
3715779Sxy150489  */
3725779Sxy150489 s32
3735779Sxy150489 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data)
3745779Sxy150489 {
3758571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
3765779Sxy150489 
3775779Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_m88");
3785779Sxy150489 
3798571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
3808571SChenlu.Chen@Sun.COM 		goto out;
3818571SChenlu.Chen@Sun.COM 
3828571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
3835779Sxy150489 	if (ret_val)
3845779Sxy150489 		goto out;
3855779Sxy150489 
3865779Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw,
3875779Sxy150489 	    MAX_PHY_REG_ADDRESS & offset, data);
3885779Sxy150489 
3898571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
3905779Sxy150489 
3915779Sxy150489 out:
3925779Sxy150489 	return (ret_val);
3935779Sxy150489 }
3945779Sxy150489 
3955779Sxy150489 /*
3965779Sxy150489  * e1000_read_phy_reg_igp - Read igp PHY register
3975779Sxy150489  * @hw: pointer to the HW structure
3985779Sxy150489  * @offset: register offset to be read
3995779Sxy150489  * @data: pointer to the read data
4005779Sxy150489  *
4015779Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
4025779Sxy150489  * and storing the retrieved information in data.  Release any acquired
4035779Sxy150489  * semaphores before exiting.
4045779Sxy150489  */
4055779Sxy150489 s32
4065779Sxy150489 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
4075779Sxy150489 {
4088571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
4095779Sxy150489 
4105779Sxy150489 	DEBUGFUNC("e1000_read_phy_reg_igp");
4115779Sxy150489 
4128571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
4138571SChenlu.Chen@Sun.COM 		goto out;
4148571SChenlu.Chen@Sun.COM 
4158571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
4165779Sxy150489 	if (ret_val)
4175779Sxy150489 		goto out;
4185779Sxy150489 
4195779Sxy150489 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
4205779Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw,
4215779Sxy150489 		    IGP01E1000_PHY_PAGE_SELECT, (u16)offset);
4225779Sxy150489 		if (ret_val) {
4238571SChenlu.Chen@Sun.COM 			hw->phy.ops.release(hw);
4245779Sxy150489 			goto out;
4255779Sxy150489 		}
4265779Sxy150489 	}
4275779Sxy150489 
4285779Sxy150489 	ret_val = e1000_read_phy_reg_mdic(hw,
4295779Sxy150489 	    MAX_PHY_REG_ADDRESS & offset, data);
4305779Sxy150489 
4318571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
4325779Sxy150489 
4335779Sxy150489 out:
4345779Sxy150489 	return (ret_val);
4355779Sxy150489 }
4365779Sxy150489 
4375779Sxy150489 /*
4385779Sxy150489  * e1000_write_phy_reg_igp - Write igp PHY register
4395779Sxy150489  * @hw: pointer to the HW structure
4405779Sxy150489  * @offset: register offset to write to
4415779Sxy150489  * @data: data to write at register offset
4425779Sxy150489  *
4435779Sxy150489  * Acquires semaphore, if necessary, then writes the data to PHY register
4445779Sxy150489  * at the offset.  Release any acquired semaphores before exiting.
4455779Sxy150489  */
4465779Sxy150489 s32
4475779Sxy150489 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
4485779Sxy150489 {
4498571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
4505779Sxy150489 
4515779Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_igp");
4525779Sxy150489 
4538571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
4548571SChenlu.Chen@Sun.COM 		goto out;
4558571SChenlu.Chen@Sun.COM 
4568571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
4575779Sxy150489 	if (ret_val)
4585779Sxy150489 		goto out;
4595779Sxy150489 
4605779Sxy150489 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
4615779Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw,
4625779Sxy150489 		    IGP01E1000_PHY_PAGE_SELECT, (u16)offset);
4635779Sxy150489 		if (ret_val) {
4648571SChenlu.Chen@Sun.COM 			hw->phy.ops.release(hw);
4655779Sxy150489 			goto out;
4665779Sxy150489 		}
4675779Sxy150489 	}
4685779Sxy150489 
4695779Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw,
4705779Sxy150489 	    MAX_PHY_REG_ADDRESS & offset, data);
4715779Sxy150489 
4728571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
4735779Sxy150489 
4745779Sxy150489 out:
4755779Sxy150489 	return (ret_val);
4765779Sxy150489 }
4775779Sxy150489 
4785779Sxy150489 /*
4795779Sxy150489  * e1000_read_kmrn_reg_generic - Read kumeran register
4805779Sxy150489  * @hw: pointer to the HW structure
4815779Sxy150489  * @offset: register offset to be read
4825779Sxy150489  * @data: pointer to the read data
4835779Sxy150489  *
4845779Sxy150489  * Acquires semaphore, if necessary.  Then reads the PHY register at offset
4855779Sxy150489  * using the kumeran interface.  The information retrieved is stored in data.
4865779Sxy150489  * Release any acquired semaphores before exiting.
4875779Sxy150489  */
4885779Sxy150489 s32
4895779Sxy150489 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data)
4905779Sxy150489 {
4915779Sxy150489 	u32 kmrnctrlsta;
4928571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
4935779Sxy150489 
4945779Sxy150489 	DEBUGFUNC("e1000_read_kmrn_reg_generic");
4955779Sxy150489 
4968571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
4978571SChenlu.Chen@Sun.COM 		goto out;
4988571SChenlu.Chen@Sun.COM 
4998571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
5005779Sxy150489 	if (ret_val)
5015779Sxy150489 		goto out;
5025779Sxy150489 
5035779Sxy150489 	kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
5045779Sxy150489 	    E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
5055779Sxy150489 	E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
5065779Sxy150489 
5075779Sxy150489 	usec_delay(2);
5085779Sxy150489 
5095779Sxy150489 	kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA);
5105779Sxy150489 	*data = (u16)kmrnctrlsta;
5115779Sxy150489 
5128571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
5135779Sxy150489 
5145779Sxy150489 out:
5155779Sxy150489 	return (ret_val);
5165779Sxy150489 }
5175779Sxy150489 
5185779Sxy150489 /*
5195779Sxy150489  * e1000_write_kmrn_reg_generic - Write kumeran register
5205779Sxy150489  * @hw: pointer to the HW structure
5215779Sxy150489  * @offset: register offset to write to
5225779Sxy150489  * @data: data to write at register offset
5235779Sxy150489  *
5245779Sxy150489  * Acquires semaphore, if necessary.  Then write the data to PHY register
5255779Sxy150489  * at the offset using the kumeran interface.  Release any acquired semaphores
5265779Sxy150489  * before exiting.
5275779Sxy150489  */
5285779Sxy150489 s32
5295779Sxy150489 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data)
5305779Sxy150489 {
5315779Sxy150489 	u32 kmrnctrlsta;
5328571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
5335779Sxy150489 
5345779Sxy150489 	DEBUGFUNC("e1000_write_kmrn_reg_generic");
5355779Sxy150489 
5368571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.acquire))
5378571SChenlu.Chen@Sun.COM 		goto out;
5388571SChenlu.Chen@Sun.COM 
5398571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
5405779Sxy150489 	if (ret_val)
5415779Sxy150489 		goto out;
5425779Sxy150489 
5435779Sxy150489 	kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
5445779Sxy150489 	    E1000_KMRNCTRLSTA_OFFSET) | data;
5455779Sxy150489 	E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
5465779Sxy150489 
5475779Sxy150489 	usec_delay(2);
5488571SChenlu.Chen@Sun.COM 	hw->phy.ops.release(hw);
5495779Sxy150489 
5505779Sxy150489 out:
5515779Sxy150489 	return (ret_val);
5525779Sxy150489 }
5535779Sxy150489 
5545779Sxy150489 /*
5555779Sxy150489  * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link
5565779Sxy150489  * @hw: pointer to the HW structure
5575779Sxy150489  *
5585779Sxy150489  * Sets up MDI/MDI-X and polarity for m88 PHY's.  If necessary, transmit clock
5595779Sxy150489  * and downshift values are set also.
5605779Sxy150489  */
5615779Sxy150489 s32
5625779Sxy150489 e1000_copper_link_setup_m88(struct e1000_hw *hw)
5635779Sxy150489 {
5645779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
5655779Sxy150489 	s32 ret_val;
5665779Sxy150489 	u16 phy_data;
5675779Sxy150489 
5685779Sxy150489 	DEBUGFUNC("e1000_copper_link_setup_m88");
5695779Sxy150489 
5705779Sxy150489 	if (phy->reset_disable) {
5715779Sxy150489 		ret_val = E1000_SUCCESS;
5725779Sxy150489 		goto out;
5735779Sxy150489 	}
5745779Sxy150489 
5755779Sxy150489 	/* Enable CRS on TX. This must be set for half-duplex operation. */
5768571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
5775779Sxy150489 	if (ret_val)
5785779Sxy150489 		goto out;
5795779Sxy150489 
5808571SChenlu.Chen@Sun.COM 	phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
5815779Sxy150489 
5825779Sxy150489 	/*
5835779Sxy150489 	 * Options:
5845779Sxy150489 	 *   MDI/MDI-X = 0 (default)
5855779Sxy150489 	 *   0 - Auto for all speeds
5865779Sxy150489 	 *   1 - MDI mode
5875779Sxy150489 	 *   2 - MDI-X mode
5885779Sxy150489 	 *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
5895779Sxy150489 	 */
5905779Sxy150489 	phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
5915779Sxy150489 
5925779Sxy150489 	switch (phy->mdix) {
5935779Sxy150489 		case 1:
5945779Sxy150489 			phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
5955779Sxy150489 			break;
5965779Sxy150489 		case 2:
5975779Sxy150489 			phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
5985779Sxy150489 			break;
5995779Sxy150489 		case 3:
6005779Sxy150489 			phy_data |= M88E1000_PSCR_AUTO_X_1000T;
6015779Sxy150489 			break;
6025779Sxy150489 		case 0:
6035779Sxy150489 		default:
6045779Sxy150489 			phy_data |= M88E1000_PSCR_AUTO_X_MODE;
6055779Sxy150489 			break;
6065779Sxy150489 	}
6075779Sxy150489 
6085779Sxy150489 	/*
6095779Sxy150489 	 * Options:
6105779Sxy150489 	 *   disable_polarity_correction = 0 (default)
6115779Sxy150489 	 *	Automatic Correction for Reversed Cable Polarity
6125779Sxy150489 	 *   0 - Disabled
6135779Sxy150489 	 *   1 - Enabled
6145779Sxy150489 	 */
6155779Sxy150489 	phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
6165779Sxy150489 	if (phy->disable_polarity_correction == 1)
6175779Sxy150489 		phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
6185779Sxy150489 
6198571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
6205779Sxy150489 	if (ret_val)
6215779Sxy150489 		goto out;
6225779Sxy150489 
6238571SChenlu.Chen@Sun.COM 	if (phy->revision < E1000_REVISION_4) {
6245779Sxy150489 		/*
6255779Sxy150489 		 * Force TX_CLK in the Extended PHY Specific Control Register
6265779Sxy150489 		 * to 25MHz clock.
6275779Sxy150489 		 */
6288571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
6298571SChenlu.Chen@Sun.COM 		    &phy_data);
6305779Sxy150489 		if (ret_val)
6315779Sxy150489 			goto out;
6325779Sxy150489 
6335779Sxy150489 		phy_data |= M88E1000_EPSCR_TX_CLK_25;
6345779Sxy150489 
6355779Sxy150489 		if ((phy->revision == E1000_REVISION_2) &&
6365779Sxy150489 		    (phy->id == M88E1111_I_PHY_ID)) {
6375779Sxy150489 			/* 82573L PHY - set the downshift counter to 5x. */
6385779Sxy150489 			phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK;
6395779Sxy150489 			phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X;
6405779Sxy150489 		} else {
6415779Sxy150489 			/* Configure Master and Slave downshift values */
6425779Sxy150489 			phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
6435779Sxy150489 			    M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
6445779Sxy150489 			phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
6455779Sxy150489 			    M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
6465779Sxy150489 		}
6478571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
6488571SChenlu.Chen@Sun.COM 		    phy_data);
6495779Sxy150489 		if (ret_val)
6505779Sxy150489 			goto out;
6515779Sxy150489 	}
6525779Sxy150489 
6535779Sxy150489 	/* Commit the changes. */
6548571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.commit(hw);
6555779Sxy150489 	if (ret_val) {
6565779Sxy150489 		DEBUGOUT("Error committing the PHY changes\n");
6575779Sxy150489 		goto out;
6585779Sxy150489 	}
6595779Sxy150489 
6605779Sxy150489 out:
6615779Sxy150489 	return (ret_val);
6625779Sxy150489 }
6635779Sxy150489 
6645779Sxy150489 /*
6655779Sxy150489  * e1000_copper_link_setup_igp - Setup igp PHY's for copper link
6665779Sxy150489  * @hw: pointer to the HW structure
6675779Sxy150489  *
6685779Sxy150489  * Sets up LPLU, MDI/MDI-X, polarity, Smartspeed and Master/Slave config for
6695779Sxy150489  * igp PHY's.
6705779Sxy150489  */
6715779Sxy150489 s32
6725779Sxy150489 e1000_copper_link_setup_igp(struct e1000_hw *hw)
6735779Sxy150489 {
6745779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
6755779Sxy150489 	s32 ret_val;
6765779Sxy150489 	u16 data;
6775779Sxy150489 
6785779Sxy150489 	DEBUGFUNC("e1000_copper_link_setup_igp");
6795779Sxy150489 
6805779Sxy150489 	if (phy->reset_disable) {
6815779Sxy150489 		ret_val = E1000_SUCCESS;
6825779Sxy150489 		goto out;
6835779Sxy150489 	}
6845779Sxy150489 
6858571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.reset(hw);
6865779Sxy150489 	if (ret_val) {
6875779Sxy150489 		DEBUGOUT("Error resetting the PHY.\n");
6885779Sxy150489 		goto out;
6895779Sxy150489 	}
6905779Sxy150489 
6918571SChenlu.Chen@Sun.COM 	/*
6928571SChenlu.Chen@Sun.COM 	 * Wait 100ms for MAC to configure PHY from NVM settings, to avoid
6938571SChenlu.Chen@Sun.COM 	 * timeout issues when LFS is enabled.
6948571SChenlu.Chen@Sun.COM 	 */
6958571SChenlu.Chen@Sun.COM 	msec_delay(100);
6965779Sxy150489 
6975779Sxy150489 	/*
6985779Sxy150489 	 * The NVM settings will configure LPLU in D3 for
6995779Sxy150489 	 * non-IGP1 PHYs.
7005779Sxy150489 	 */
7015779Sxy150489 	if (phy->type == e1000_phy_igp) {
7025779Sxy150489 		/* disable lplu d3 during driver init */
7038571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.set_d3_lplu_state(hw, false);
7045779Sxy150489 		if (ret_val) {
7055779Sxy150489 			DEBUGOUT("Error Disabling LPLU D3\n");
7065779Sxy150489 			goto out;
7075779Sxy150489 		}
7085779Sxy150489 	}
7095779Sxy150489 
7105779Sxy150489 	/* disable lplu d0 during driver init */
7118571SChenlu.Chen@Sun.COM 	if (hw->phy.ops.set_d0_lplu_state) {
7128571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.set_d0_lplu_state(hw, false);
7138571SChenlu.Chen@Sun.COM 		if (ret_val) {
7148571SChenlu.Chen@Sun.COM 			DEBUGOUT("Error Disabling LPLU D0\n");
7158571SChenlu.Chen@Sun.COM 			goto out;
7168571SChenlu.Chen@Sun.COM 		}
7175779Sxy150489 	}
7185779Sxy150489 	/* Configure mdi-mdix settings */
7198571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data);
7205779Sxy150489 	if (ret_val)
7215779Sxy150489 		goto out;
7225779Sxy150489 
7235779Sxy150489 	data &= ~IGP01E1000_PSCR_AUTO_MDIX;
7245779Sxy150489 
7255779Sxy150489 	switch (phy->mdix) {
7265779Sxy150489 	case 1:
7275779Sxy150489 		data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
7285779Sxy150489 		break;
7295779Sxy150489 	case 2:
7305779Sxy150489 		data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
7315779Sxy150489 		break;
7325779Sxy150489 	case 0:
7335779Sxy150489 	default:
7345779Sxy150489 		data |= IGP01E1000_PSCR_AUTO_MDIX;
7355779Sxy150489 		break;
7365779Sxy150489 	}
7378571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, data);
7385779Sxy150489 	if (ret_val)
7395779Sxy150489 		goto out;
7405779Sxy150489 
7415779Sxy150489 	/* set auto-master slave resolution settings */
7425779Sxy150489 	if (hw->mac.autoneg) {
7435779Sxy150489 		/*
7445779Sxy150489 		 * when autonegotiation advertisement is only 1000Mbps then we
7455779Sxy150489 		 * should disable SmartSpeed and enable Auto MasterSlave
7465779Sxy150489 		 * resolution as hardware default.
7475779Sxy150489 		 */
7485779Sxy150489 		if (phy->autoneg_advertised == ADVERTISE_1000_FULL) {
7495779Sxy150489 			/* Disable SmartSpeed */
7508571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.read_reg(hw,
7515779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG, &data);
7525779Sxy150489 			if (ret_val)
7535779Sxy150489 				goto out;
7545779Sxy150489 
7555779Sxy150489 			data &= ~IGP01E1000_PSCFR_SMART_SPEED;
7568571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.write_reg(hw,
7575779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG, data);
7585779Sxy150489 			if (ret_val)
7595779Sxy150489 				goto out;
7605779Sxy150489 
7615779Sxy150489 			/* Set auto Master/Slave resolution process */
7628571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data);
7635779Sxy150489 			if (ret_val)
7645779Sxy150489 				goto out;
7655779Sxy150489 
7665779Sxy150489 			data &= ~CR_1000T_MS_ENABLE;
7678571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data);
7685779Sxy150489 			if (ret_val)
7695779Sxy150489 				goto out;
7705779Sxy150489 		}
7715779Sxy150489 
7728571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data);
7735779Sxy150489 		if (ret_val)
7745779Sxy150489 			goto out;
7755779Sxy150489 
7765779Sxy150489 		/* load defaults for future use */
7775779Sxy150489 		phy->original_ms_type = (data & CR_1000T_MS_ENABLE) ?
7785779Sxy150489 		    ((data & CR_1000T_MS_VALUE) ?
7795779Sxy150489 		    e1000_ms_force_master :
7805779Sxy150489 		    e1000_ms_force_slave) :
7815779Sxy150489 		    e1000_ms_auto;
7825779Sxy150489 
7835779Sxy150489 		switch (phy->ms_type) {
7845779Sxy150489 		case e1000_ms_force_master:
7855779Sxy150489 			data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
7865779Sxy150489 			break;
7875779Sxy150489 		case e1000_ms_force_slave:
7885779Sxy150489 			data |= CR_1000T_MS_ENABLE;
7895779Sxy150489 			data &= ~(CR_1000T_MS_VALUE);
7905779Sxy150489 			break;
7915779Sxy150489 		case e1000_ms_auto:
7925779Sxy150489 			data &= ~CR_1000T_MS_ENABLE;
7935779Sxy150489 		default:
7945779Sxy150489 			break;
7955779Sxy150489 		}
7968571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data);
7975779Sxy150489 		if (ret_val)
7985779Sxy150489 			goto out;
7995779Sxy150489 	}
8005779Sxy150489 
8015779Sxy150489 out:
8025779Sxy150489 	return (ret_val);
8035779Sxy150489 }
8045779Sxy150489 
8055779Sxy150489 /*
8065779Sxy150489  * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link
8075779Sxy150489  * @hw: pointer to the HW structure
8085779Sxy150489  *
8095779Sxy150489  * Performs initial bounds checking on autoneg advertisement parameter, then
8105779Sxy150489  * configure to advertise the full capability.  Setup the PHY to autoneg
8115779Sxy150489  * and restart the negotiation process between the link partner.  If
8125779Sxy150489  * autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
8135779Sxy150489  */
8145779Sxy150489 s32
8155779Sxy150489 e1000_copper_link_autoneg(struct e1000_hw *hw)
8165779Sxy150489 {
8175779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
8185779Sxy150489 	s32 ret_val;
8195779Sxy150489 	u16 phy_ctrl;
8205779Sxy150489 
8215779Sxy150489 	DEBUGFUNC("e1000_copper_link_autoneg");
8225779Sxy150489 
8235779Sxy150489 	/*
8245779Sxy150489 	 * Perform some bounds checking on the autoneg advertisement
8255779Sxy150489 	 * parameter.
8265779Sxy150489 	 */
8275779Sxy150489 	phy->autoneg_advertised &= phy->autoneg_mask;
8285779Sxy150489 
8295779Sxy150489 	/*
8305779Sxy150489 	 * If autoneg_advertised is zero, we assume it was not defaulted
8315779Sxy150489 	 * by the calling code so we set to advertise full capability.
8325779Sxy150489 	 */
8335779Sxy150489 	if (phy->autoneg_advertised == 0)
8345779Sxy150489 		phy->autoneg_advertised = phy->autoneg_mask;
8355779Sxy150489 
8365779Sxy150489 	DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
8375779Sxy150489 	ret_val = e1000_phy_setup_autoneg(hw);
8385779Sxy150489 	if (ret_val) {
8395779Sxy150489 		DEBUGOUT("Error Setting up Auto-Negotiation\n");
8405779Sxy150489 		goto out;
8415779Sxy150489 	}
8425779Sxy150489 	DEBUGOUT("Restarting Auto-Neg\n");
8435779Sxy150489 
8445779Sxy150489 	/*
8455779Sxy150489 	 * Restart auto-negotiation by setting the Auto Neg Enable bit and
8465779Sxy150489 	 * the Auto Neg Restart bit in the PHY control register.
8475779Sxy150489 	 */
8488571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
8495779Sxy150489 	if (ret_val)
8505779Sxy150489 		goto out;
8515779Sxy150489 
8525779Sxy150489 	phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
8538571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
8545779Sxy150489 	if (ret_val)
8555779Sxy150489 		goto out;
8565779Sxy150489 
8575779Sxy150489 	/*
8585779Sxy150489 	 * Does the user want to wait for Auto-Neg to complete here, or
8595779Sxy150489 	 * check at a later time (for example, callback routine).
8605779Sxy150489 	 */
8615779Sxy150489 	if (phy->autoneg_wait_to_complete) {
8628571SChenlu.Chen@Sun.COM 		ret_val = hw->mac.ops.wait_autoneg(hw);
8635779Sxy150489 		if (ret_val) {
8645779Sxy150489 			DEBUGOUT("Error while waiting for "
8655779Sxy150489 			    "autoneg to complete\n");
8665779Sxy150489 			goto out;
8675779Sxy150489 		}
8685779Sxy150489 	}
8695779Sxy150489 
8708571SChenlu.Chen@Sun.COM 	hw->mac.get_link_status = true;
8715779Sxy150489 
8725779Sxy150489 out:
8735779Sxy150489 	return (ret_val);
8745779Sxy150489 }
8755779Sxy150489 
8765779Sxy150489 /*
8775779Sxy150489  * e1000_phy_setup_autoneg - Configure PHY for auto-negotiation
8785779Sxy150489  * @hw: pointer to the HW structure
8795779Sxy150489  *
8805779Sxy150489  * Reads the MII auto-neg advertisement register and/or the 1000T control
8815779Sxy150489  * register and if the PHY is already setup for auto-negotiation, then
8825779Sxy150489  * return successful.  Otherwise, setup advertisement and flow control to
8835779Sxy150489  * the appropriate values for the wanted auto-negotiation.
8845779Sxy150489  */
8855779Sxy150489 s32
8865779Sxy150489 e1000_phy_setup_autoneg(struct e1000_hw *hw)
8875779Sxy150489 {
8885779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
8895779Sxy150489 	s32 ret_val;
8905779Sxy150489 	u16 mii_autoneg_adv_reg;
8915779Sxy150489 	u16 mii_1000t_ctrl_reg = 0;
8925779Sxy150489 
8935779Sxy150489 	DEBUGFUNC("e1000_phy_setup_autoneg");
8945779Sxy150489 
8955779Sxy150489 	phy->autoneg_advertised &= phy->autoneg_mask;
8965779Sxy150489 
8975779Sxy150489 	/* Read the MII Auto-Neg Advertisement Register (Address 4). */
8988571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
8995779Sxy150489 	if (ret_val)
9005779Sxy150489 		goto out;
9015779Sxy150489 
9025779Sxy150489 	if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
9035779Sxy150489 		/* Read the MII 1000Base-T Control Register (Address 9). */
9048571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL,
9058571SChenlu.Chen@Sun.COM 		    &mii_1000t_ctrl_reg);
9065779Sxy150489 		if (ret_val)
9075779Sxy150489 			goto out;
9085779Sxy150489 	}
9095779Sxy150489 
9105779Sxy150489 	/*
9115779Sxy150489 	 * Need to parse both autoneg_advertised and fc and set up
9125779Sxy150489 	 * the appropriate PHY registers.  First we will parse for
9135779Sxy150489 	 * autoneg_advertised software override.  Since we can advertise
9145779Sxy150489 	 * a plethora of combinations, we need to check each bit
9155779Sxy150489 	 * individually.
9165779Sxy150489 	 */
9175779Sxy150489 
9185779Sxy150489 	/*
9195779Sxy150489 	 * First we clear all the 10/100 mb speed bits in the Auto-Neg
9205779Sxy150489 	 * Advertisement Register (Address 4) and the 1000 mb speed bits in
9215779Sxy150489 	 * the  1000Base-T Control Register (Address 9).
9225779Sxy150489 	 */
9235779Sxy150489 	mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS |
9245779Sxy150489 	    NWAY_AR_100TX_HD_CAPS |
9255779Sxy150489 	    NWAY_AR_10T_FD_CAPS   |
9265779Sxy150489 	    NWAY_AR_10T_HD_CAPS);
9275779Sxy150489 	mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
9285779Sxy150489 
9295779Sxy150489 	DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised);
9305779Sxy150489 
9315779Sxy150489 	/* Do we want to advertise 10 Mb Half Duplex? */
9325779Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
9335779Sxy150489 		DEBUGOUT("Advertise 10mb Half duplex\n");
9345779Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
9355779Sxy150489 	}
9365779Sxy150489 
9375779Sxy150489 	/* Do we want to advertise 10 Mb Full Duplex? */
9385779Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
9395779Sxy150489 		DEBUGOUT("Advertise 10mb Full duplex\n");
9405779Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
9415779Sxy150489 	}
9425779Sxy150489 
9435779Sxy150489 	/* Do we want to advertise 100 Mb Half Duplex? */
9445779Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
9455779Sxy150489 		DEBUGOUT("Advertise 100mb Half duplex\n");
9465779Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
9475779Sxy150489 	}
9485779Sxy150489 
9495779Sxy150489 	/* Do we want to advertise 100 Mb Full Duplex? */
9505779Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
9515779Sxy150489 		DEBUGOUT("Advertise 100mb Full duplex\n");
9525779Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
9535779Sxy150489 	}
9545779Sxy150489 
9555779Sxy150489 	/* We do not allow the Phy to advertise 1000 Mb Half Duplex */
9568571SChenlu.Chen@Sun.COM 	if (phy->autoneg_advertised & ADVERTISE_1000_HALF)
9575779Sxy150489 		DEBUGOUT("Advertise 1000mb Half duplex request denied!\n");
9585779Sxy150489 
9595779Sxy150489 	/* Do we want to advertise 1000 Mb Full Duplex? */
9605779Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
9615779Sxy150489 		DEBUGOUT("Advertise 1000mb Full duplex\n");
9625779Sxy150489 		mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
9635779Sxy150489 	}
9645779Sxy150489 
9655779Sxy150489 	/*
9665779Sxy150489 	 * Check for a software override of the flow control settings, and
9675779Sxy150489 	 * setup the PHY advertisement registers accordingly.  If
9685779Sxy150489 	 * auto-negotiation is enabled, then software will have to set the
9695779Sxy150489 	 * "PAUSE" bits to the correct value in the Auto-Negotiation
9705779Sxy150489 	 * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-
9715779Sxy150489 	 * negotiation.
9725779Sxy150489 	 *
9735779Sxy150489 	 * The possible values of the "fc" parameter are:
9745779Sxy150489 	 * 0:	Flow control is completely disabled
9755779Sxy150489 	 * 1:	Rx flow control is enabled (we can receive pause frames
9765779Sxy150489 	 *	but not send pause frames).
9775779Sxy150489 	 * 2:	Tx flow control is enabled (we can send pause frames
9785779Sxy150489 	 *	but we do not support receiving pause frames).
9795779Sxy150489 	 * 3:	Both Rx and Tx flow control (symmetric) are enabled.
9805779Sxy150489 	 * other: No software override.  The flow control configuration
9815779Sxy150489 	 *	in the EEPROM is used.
9825779Sxy150489 	 */
9838571SChenlu.Chen@Sun.COM 	switch (hw->fc.current_mode) {
9845779Sxy150489 	case e1000_fc_none:
9855779Sxy150489 		/*
9865779Sxy150489 		 * Flow control (Rx & Tx) is completely disabled by a
9875779Sxy150489 		 * software over-ride.
9885779Sxy150489 		 */
9895779Sxy150489 		mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
9905779Sxy150489 		break;
9915779Sxy150489 	case e1000_fc_rx_pause:
9925779Sxy150489 		/*
9935779Sxy150489 		 * Rx Flow control is enabled, and Tx Flow control is
9945779Sxy150489 		 * disabled, by a software over-ride.
9955779Sxy150489 		 *
9965779Sxy150489 		 * Since there really isn't a way to advertise that we are
9975779Sxy150489 		 * capable of Rx Pause ONLY, we will advertise that we
9985779Sxy150489 		 * support both symmetric and asymmetric Rx PAUSE.  Later
9995779Sxy150489 		 * (in e1000_config_fc_after_link_up) we will disable the
10005779Sxy150489 		 * hw's ability to send PAUSE frames.
10015779Sxy150489 		 */
10025779Sxy150489 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
10035779Sxy150489 		break;
10045779Sxy150489 	case e1000_fc_tx_pause:
10055779Sxy150489 		/*
10065779Sxy150489 		 * Tx Flow control is enabled, and Rx Flow control is
10075779Sxy150489 		 * disabled, by a software over-ride.
10085779Sxy150489 		 */
10095779Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
10105779Sxy150489 		mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
10115779Sxy150489 		break;
10125779Sxy150489 	case e1000_fc_full:
10135779Sxy150489 		/*
10145779Sxy150489 		 * Flow control (both Rx and Tx) is enabled by a software
10155779Sxy150489 		 * over-ride.
10165779Sxy150489 		 */
10175779Sxy150489 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
10185779Sxy150489 		break;
10195779Sxy150489 	default:
10205779Sxy150489 		DEBUGOUT("Flow control param set incorrectly\n");
10215779Sxy150489 		ret_val = -E1000_ERR_CONFIG;
10225779Sxy150489 		goto out;
10235779Sxy150489 	}
10245779Sxy150489 
10258571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
10265779Sxy150489 	if (ret_val)
10275779Sxy150489 		goto out;
10285779Sxy150489 
10295779Sxy150489 	DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
10305779Sxy150489 
10315779Sxy150489 	if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
10328571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw,
10335779Sxy150489 		    PHY_1000T_CTRL, mii_1000t_ctrl_reg);
10345779Sxy150489 		if (ret_val)
10355779Sxy150489 			goto out;
10365779Sxy150489 	}
10375779Sxy150489 
10385779Sxy150489 out:
10395779Sxy150489 	return (ret_val);
10405779Sxy150489 }
10415779Sxy150489 
10425779Sxy150489 /*
10435779Sxy150489  * e1000_setup_copper_link_generic - Configure copper link settings
10445779Sxy150489  * @hw: pointer to the HW structure
10455779Sxy150489  *
10465779Sxy150489  * Calls the appropriate function to configure the link for auto-neg or forced
10475779Sxy150489  * speed and duplex.  Then we check for link, once link is established calls
10485779Sxy150489  * to configure collision distance and flow control are called.  If link is
10495779Sxy150489  * not established, we return -E1000_ERR_PHY (-2).
10505779Sxy150489  */
10515779Sxy150489 s32
10525779Sxy150489 e1000_setup_copper_link_generic(struct e1000_hw *hw)
10535779Sxy150489 {
10545779Sxy150489 	s32 ret_val;
10555779Sxy150489 	bool link;
10565779Sxy150489 
10575779Sxy150489 	DEBUGFUNC("e1000_setup_copper_link_generic");
10585779Sxy150489 
10595779Sxy150489 	if (hw->mac.autoneg) {
10605779Sxy150489 		/*
10615779Sxy150489 		 * Setup autoneg and flow control advertisement and perform
10625779Sxy150489 		 * autonegotiation.
10635779Sxy150489 		 */
10645779Sxy150489 		ret_val = e1000_copper_link_autoneg(hw);
10655779Sxy150489 		if (ret_val)
10665779Sxy150489 			goto out;
10675779Sxy150489 	} else {
10685779Sxy150489 		/*
10695779Sxy150489 		 * PHY will be set to 10H, 10F, 100H or 100F
10705779Sxy150489 		 * depending on user settings.
10715779Sxy150489 		 */
10725779Sxy150489 		DEBUGOUT("Forcing Speed and Duplex\n");
10738571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.force_speed_duplex(hw);
10745779Sxy150489 		if (ret_val) {
10755779Sxy150489 			DEBUGOUT("Error Forcing Speed and Duplex\n");
10765779Sxy150489 			goto out;
10775779Sxy150489 		}
10785779Sxy150489 	}
10795779Sxy150489 
10805779Sxy150489 	/*
10815779Sxy150489 	 * Check link status. Wait up to 100 microseconds for link to become
10825779Sxy150489 	 * valid.
10835779Sxy150489 	 */
10845779Sxy150489 	ret_val = e1000_phy_has_link_generic(hw,
10855779Sxy150489 	    COPPER_LINK_UP_LIMIT,
10865779Sxy150489 	    10,
10875779Sxy150489 	    &link);
10885779Sxy150489 	if (ret_val)
10895779Sxy150489 		goto out;
10905779Sxy150489 
10915779Sxy150489 	if (link) {
10925779Sxy150489 		DEBUGOUT("Valid link established!!!\n");
10935779Sxy150489 		e1000_config_collision_dist_generic(hw);
10945779Sxy150489 		ret_val = e1000_config_fc_after_link_up_generic(hw);
10955779Sxy150489 	} else {
10965779Sxy150489 		DEBUGOUT("Unable to establish link!!!\n");
10975779Sxy150489 	}
10985779Sxy150489 
10995779Sxy150489 out:
11005779Sxy150489 	return (ret_val);
11015779Sxy150489 }
11025779Sxy150489 
11035779Sxy150489 /*
11045779Sxy150489  * e1000_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY
11055779Sxy150489  * @hw: pointer to the HW structure
11065779Sxy150489  *
11075779Sxy150489  * Calls the PHY setup function to force speed and duplex.  Clears the
11085779Sxy150489  * auto-crossover to force MDI manually.  Waits for link and returns
11095779Sxy150489  * successful if link up is successful, else -E1000_ERR_PHY (-2).
11105779Sxy150489  */
11115779Sxy150489 s32
11125779Sxy150489 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw)
11135779Sxy150489 {
11145779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
11155779Sxy150489 	s32 ret_val;
11165779Sxy150489 	u16 phy_data;
11175779Sxy150489 	bool link;
11185779Sxy150489 
11195779Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_igp");
11205779Sxy150489 
11218571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
11225779Sxy150489 	if (ret_val)
11235779Sxy150489 		goto out;
11245779Sxy150489 
11255779Sxy150489 	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
11265779Sxy150489 
11278571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
11285779Sxy150489 	if (ret_val)
11295779Sxy150489 		goto out;
11305779Sxy150489 
11315779Sxy150489 	/*
11325779Sxy150489 	 * Clear Auto-Crossover to force MDI manually.  IGP requires MDI
11335779Sxy150489 	 * forced whenever speed and duplex are forced.
11345779Sxy150489 	 */
11358571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data);
11365779Sxy150489 	if (ret_val)
11375779Sxy150489 		goto out;
11385779Sxy150489 
11395779Sxy150489 	phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
11405779Sxy150489 	phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
11415779Sxy150489 
11428571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data);
11435779Sxy150489 	if (ret_val)
11445779Sxy150489 		goto out;
11455779Sxy150489 
11465779Sxy150489 	DEBUGOUT1("IGP PSCR: %X\n", phy_data);
11475779Sxy150489 
11485779Sxy150489 	usec_delay(1);
11495779Sxy150489 
11505779Sxy150489 	if (phy->autoneg_wait_to_complete) {
11515779Sxy150489 		DEBUGOUT("Waiting for forced speed/duplex link on IGP phy.\n");
11525779Sxy150489 
11535779Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
11545779Sxy150489 		    PHY_FORCE_LIMIT,
11555779Sxy150489 		    100000,
11565779Sxy150489 		    &link);
11575779Sxy150489 		if (ret_val)
11585779Sxy150489 			goto out;
11595779Sxy150489 
11608571SChenlu.Chen@Sun.COM 		if (!link)
11615779Sxy150489 			DEBUGOUT("Link taking longer than expected.\n");
11625779Sxy150489 
11635779Sxy150489 		/* Try once more */
11645779Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
11655779Sxy150489 		    PHY_FORCE_LIMIT,
11665779Sxy150489 		    100000,
11675779Sxy150489 		    &link);
11685779Sxy150489 		if (ret_val)
11695779Sxy150489 			goto out;
11705779Sxy150489 	}
11715779Sxy150489 
11725779Sxy150489 out:
11735779Sxy150489 	return (ret_val);
11745779Sxy150489 }
11755779Sxy150489 
11765779Sxy150489 /*
11775779Sxy150489  * e1000_phy_force_speed_duplex_m88 - Force speed/duplex for m88 PHY
11785779Sxy150489  * @hw: pointer to the HW structure
11795779Sxy150489  *
11805779Sxy150489  * Calls the PHY setup function to force speed and duplex.  Clears the
11815779Sxy150489  * auto-crossover to force MDI manually.  Resets the PHY to commit the
11825779Sxy150489  * changes.  If time expires while waiting for link up, we reset the DSP.
11835779Sxy150489  * After reset, TX_CLK and CRS on Tx must be set.  Return successful upon
11845779Sxy150489  * successful completion, else return corresponding error code.
11855779Sxy150489  */
11865779Sxy150489 s32
11875779Sxy150489 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw)
11885779Sxy150489 {
11895779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
11905779Sxy150489 	s32 ret_val;
11915779Sxy150489 	u16 phy_data;
11925779Sxy150489 	bool link;
11935779Sxy150489 
11945779Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_m88");
11955779Sxy150489 
11965779Sxy150489 	/*
11975779Sxy150489 	 * Clear Auto-Crossover to force MDI manually.  M88E1000 requires MDI
11985779Sxy150489 	 * forced whenever speed and duplex are forced.
11995779Sxy150489 	 */
12008571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
12015779Sxy150489 	if (ret_val)
12025779Sxy150489 		goto out;
12035779Sxy150489 
12045779Sxy150489 	phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
12058571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
12065779Sxy150489 	if (ret_val)
12075779Sxy150489 		goto out;
12085779Sxy150489 
12095779Sxy150489 	DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data);
12105779Sxy150489 
12118571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
12125779Sxy150489 	if (ret_val)
12135779Sxy150489 		goto out;
12145779Sxy150489 
12155779Sxy150489 	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
12165779Sxy150489 
12178571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
12185779Sxy150489 	if (ret_val)
12195779Sxy150489 		goto out;
12205779Sxy150489 
12218571SChenlu.Chen@Sun.COM 	/* Reset the phy to commit changes. */
12228571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.commit(hw);
12238571SChenlu.Chen@Sun.COM 	if (ret_val)
12248571SChenlu.Chen@Sun.COM 		goto out;
12255779Sxy150489 
12265779Sxy150489 	if (phy->autoneg_wait_to_complete) {
12275779Sxy150489 		DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n");
12285779Sxy150489 
12298571SChenlu.Chen@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
12308571SChenlu.Chen@Sun.COM 		    100000, &link);
12315779Sxy150489 		if (ret_val)
12325779Sxy150489 			goto out;
12335779Sxy150489 
12345779Sxy150489 		if (!link) {
12355779Sxy150489 			/*
12365779Sxy150489 			 * We didn't get link.
12375779Sxy150489 			 * Reset the DSP and cross our fingers.
12385779Sxy150489 			 */
12398571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.write_reg(hw,
12405779Sxy150489 			    M88E1000_PHY_PAGE_SELECT,
12415779Sxy150489 			    0x001d);
12425779Sxy150489 			if (ret_val)
12435779Sxy150489 				goto out;
12445779Sxy150489 			ret_val = e1000_phy_reset_dsp_generic(hw);
12455779Sxy150489 			if (ret_val)
12465779Sxy150489 				goto out;
12475779Sxy150489 		}
12485779Sxy150489 
12495779Sxy150489 		/* Try once more */
12508571SChenlu.Chen@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
12518571SChenlu.Chen@Sun.COM 		    100000, &link);
12525779Sxy150489 		if (ret_val)
12535779Sxy150489 			goto out;
12545779Sxy150489 	}
12555779Sxy150489 
12568571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
12575779Sxy150489 	if (ret_val)
12585779Sxy150489 		goto out;
12595779Sxy150489 
12605779Sxy150489 	/*
12615779Sxy150489 	 * Resetting the phy means we need to re-force TX_CLK in the
12625779Sxy150489 	 * Extended PHY Specific Control Register to 25MHz clock from
12635779Sxy150489 	 * the reset value of 2.5MHz.
12645779Sxy150489 	 */
12655779Sxy150489 	phy_data |= M88E1000_EPSCR_TX_CLK_25;
12668571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data);
12675779Sxy150489 	if (ret_val)
12685779Sxy150489 		goto out;
12695779Sxy150489 
12705779Sxy150489 	/*
12715779Sxy150489 	 * In addition, we must re-enable CRS on Tx for both half and full
12725779Sxy150489 	 * duplex.
12735779Sxy150489 	 */
12748571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
12755779Sxy150489 	if (ret_val)
12765779Sxy150489 		goto out;
12775779Sxy150489 
12785779Sxy150489 	phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
12798571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
12805779Sxy150489 
12815779Sxy150489 out:
12825779Sxy150489 	return (ret_val);
12835779Sxy150489 }
12845779Sxy150489 
12855779Sxy150489 /*
1286*10319SJason.Xu@Sun.COM  * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex
1287*10319SJason.Xu@Sun.COM  * @hw: pointer to the HW structure
1288*10319SJason.Xu@Sun.COM  *
1289*10319SJason.Xu@Sun.COM  * Forces the speed and duplex settings of the PHY.
1290*10319SJason.Xu@Sun.COM  * This is a function pointer entry point only called by
1291*10319SJason.Xu@Sun.COM  * PHY setup routines.
1292*10319SJason.Xu@Sun.COM  */
1293*10319SJason.Xu@Sun.COM s32
1294*10319SJason.Xu@Sun.COM e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw)
1295*10319SJason.Xu@Sun.COM {
1296*10319SJason.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
1297*10319SJason.Xu@Sun.COM 	s32 ret_val;
1298*10319SJason.Xu@Sun.COM 	u16 data;
1299*10319SJason.Xu@Sun.COM 	bool link;
1300*10319SJason.Xu@Sun.COM 
1301*10319SJason.Xu@Sun.COM 	DEBUGFUNC("e1000_phy_force_speed_duplex_ife");
1302*10319SJason.Xu@Sun.COM 
1303*10319SJason.Xu@Sun.COM 	if (phy->type != e1000_phy_ife) {
1304*10319SJason.Xu@Sun.COM 		ret_val = e1000_phy_force_speed_duplex_igp(hw);
1305*10319SJason.Xu@Sun.COM 		goto out;
1306*10319SJason.Xu@Sun.COM 	}
1307*10319SJason.Xu@Sun.COM 
1308*10319SJason.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data);
1309*10319SJason.Xu@Sun.COM 	if (ret_val)
1310*10319SJason.Xu@Sun.COM 		goto out;
1311*10319SJason.Xu@Sun.COM 
1312*10319SJason.Xu@Sun.COM 	e1000_phy_force_speed_duplex_setup(hw, &data);
1313*10319SJason.Xu@Sun.COM 
1314*10319SJason.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data);
1315*10319SJason.Xu@Sun.COM 	if (ret_val)
1316*10319SJason.Xu@Sun.COM 		goto out;
1317*10319SJason.Xu@Sun.COM 
1318*10319SJason.Xu@Sun.COM 	/* Disable MDI-X support for 10/100 */
1319*10319SJason.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
1320*10319SJason.Xu@Sun.COM 	if (ret_val)
1321*10319SJason.Xu@Sun.COM 		goto out;
1322*10319SJason.Xu@Sun.COM 
1323*10319SJason.Xu@Sun.COM 	data &= ~IFE_PMC_AUTO_MDIX;
1324*10319SJason.Xu@Sun.COM 	data &= ~IFE_PMC_FORCE_MDIX;
1325*10319SJason.Xu@Sun.COM 
1326*10319SJason.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data);
1327*10319SJason.Xu@Sun.COM 	if (ret_val)
1328*10319SJason.Xu@Sun.COM 		goto out;
1329*10319SJason.Xu@Sun.COM 
1330*10319SJason.Xu@Sun.COM 	DEBUGOUT1("IFE PMC: %X\n", data);
1331*10319SJason.Xu@Sun.COM 
1332*10319SJason.Xu@Sun.COM 	usec_delay(1);
1333*10319SJason.Xu@Sun.COM 
1334*10319SJason.Xu@Sun.COM 	if (phy->autoneg_wait_to_complete) {
1335*10319SJason.Xu@Sun.COM 		DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n");
1336*10319SJason.Xu@Sun.COM 
1337*10319SJason.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
1338*10319SJason.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
1339*10319SJason.Xu@Sun.COM 		if (ret_val)
1340*10319SJason.Xu@Sun.COM 			goto out;
1341*10319SJason.Xu@Sun.COM 
1342*10319SJason.Xu@Sun.COM 		if (!link)
1343*10319SJason.Xu@Sun.COM 			DEBUGOUT("Link taking longer than expected.\n");
1344*10319SJason.Xu@Sun.COM 
1345*10319SJason.Xu@Sun.COM 		/* Try once more */
1346*10319SJason.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
1347*10319SJason.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
1348*10319SJason.Xu@Sun.COM 		if (ret_val)
1349*10319SJason.Xu@Sun.COM 			goto out;
1350*10319SJason.Xu@Sun.COM 	}
1351*10319SJason.Xu@Sun.COM 
1352*10319SJason.Xu@Sun.COM out:
1353*10319SJason.Xu@Sun.COM 	return (ret_val);
1354*10319SJason.Xu@Sun.COM }
1355*10319SJason.Xu@Sun.COM /*
13565779Sxy150489  * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex
13575779Sxy150489  * @hw: pointer to the HW structure
13585779Sxy150489  * @phy_ctrl: pointer to current value of PHY_CONTROL
13595779Sxy150489  *
13605779Sxy150489  * Forces speed and duplex on the PHY by doing the following: disable flow
13615779Sxy150489  * control, force speed/duplex on the MAC, disable auto speed detection,
13625779Sxy150489  * disable auto-negotiation, configure duplex, configure speed, configure
13635779Sxy150489  * the collision distance, write configuration to CTRL register.  The
13645779Sxy150489  * caller must write to the PHY_CONTROL register for these settings to
13655779Sxy150489  * take affect.
13665779Sxy150489  */
13675779Sxy150489 void
13685779Sxy150489 e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl)
13695779Sxy150489 {
13705779Sxy150489 	struct e1000_mac_info *mac = &hw->mac;
13715779Sxy150489 	u32 ctrl;
13725779Sxy150489 
13735779Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_setup");
13745779Sxy150489 
13755779Sxy150489 	/* Turn off flow control when forcing speed/duplex */
13768571SChenlu.Chen@Sun.COM 	hw->fc.current_mode = e1000_fc_none;
13775779Sxy150489 
13785779Sxy150489 	/* Force speed/duplex on the mac */
13795779Sxy150489 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
13805779Sxy150489 	ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
13815779Sxy150489 	ctrl &= ~E1000_CTRL_SPD_SEL;
13825779Sxy150489 
13835779Sxy150489 	/* Disable Auto Speed Detection */
13845779Sxy150489 	ctrl &= ~E1000_CTRL_ASDE;
13855779Sxy150489 
13865779Sxy150489 	/* Disable autoneg on the phy */
13875779Sxy150489 	*phy_ctrl &= ~MII_CR_AUTO_NEG_EN;
13885779Sxy150489 
13895779Sxy150489 	/* Forcing Full or Half Duplex? */
13905779Sxy150489 	if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) {
13915779Sxy150489 		ctrl &= ~E1000_CTRL_FD;
13925779Sxy150489 		*phy_ctrl &= ~MII_CR_FULL_DUPLEX;
13935779Sxy150489 		DEBUGOUT("Half Duplex\n");
13945779Sxy150489 	} else {
13955779Sxy150489 		ctrl |= E1000_CTRL_FD;
13965779Sxy150489 		*phy_ctrl |= MII_CR_FULL_DUPLEX;
13975779Sxy150489 		DEBUGOUT("Full Duplex\n");
13985779Sxy150489 	}
13995779Sxy150489 
14005779Sxy150489 	/* Forcing 10mb or 100mb? */
14015779Sxy150489 	if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) {
14025779Sxy150489 		ctrl |= E1000_CTRL_SPD_100;
14035779Sxy150489 		*phy_ctrl |= MII_CR_SPEED_100;
14045779Sxy150489 		*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10);
14055779Sxy150489 		DEBUGOUT("Forcing 100mb\n");
14065779Sxy150489 	} else {
14075779Sxy150489 		ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
14085779Sxy150489 		/* LINTED */
14095779Sxy150489 		*phy_ctrl |= MII_CR_SPEED_10;
14105779Sxy150489 		*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
14115779Sxy150489 		DEBUGOUT("Forcing 10mb\n");
14125779Sxy150489 	}
14135779Sxy150489 
14145779Sxy150489 	e1000_config_collision_dist_generic(hw);
14155779Sxy150489 
14165779Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
14175779Sxy150489 }
14185779Sxy150489 
14195779Sxy150489 /*
14205779Sxy150489  * e1000_set_d3_lplu_state_generic - Sets low power link up state for D3
14215779Sxy150489  * @hw: pointer to the HW structure
14225779Sxy150489  * @active: boolean used to enable/disable lplu
14235779Sxy150489  *
14245779Sxy150489  * Success returns 0, Failure returns 1
14255779Sxy150489  *
14265779Sxy150489  * The low power link up (lplu) state is set to the power management level D3
14275779Sxy150489  * and SmartSpeed is disabled when active is true, else clear lplu for D3
14285779Sxy150489  * and enable Smartspeed.  LPLU and Smartspeed are mutually exclusive.  LPLU
14295779Sxy150489  * is used during Dx states where the power conservation is most important.
14305779Sxy150489  * During driver activity, SmartSpeed should be enabled so performance is
14315779Sxy150489  * maintained.
14325779Sxy150489  */
14335779Sxy150489 s32
14345779Sxy150489 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active)
14355779Sxy150489 {
14365779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
14378571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
14385779Sxy150489 	u16 data;
14395779Sxy150489 
14405779Sxy150489 	DEBUGFUNC("e1000_set_d3_lplu_state_generic");
14415779Sxy150489 
14428571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.read_reg))
14438571SChenlu.Chen@Sun.COM 		goto out;
14448571SChenlu.Chen@Sun.COM 
14458571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data);
14465779Sxy150489 	if (ret_val)
14475779Sxy150489 		goto out;
14485779Sxy150489 
14495779Sxy150489 	if (!active) {
14505779Sxy150489 		data &= ~IGP02E1000_PM_D3_LPLU;
14518571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw,
14525779Sxy150489 		    IGP02E1000_PHY_POWER_MGMT,
14535779Sxy150489 		    data);
14545779Sxy150489 		if (ret_val)
14555779Sxy150489 			goto out;
14565779Sxy150489 		/*
14575779Sxy150489 		 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
14585779Sxy150489 		 * during Dx states where the power conservation is most
14595779Sxy150489 		 * important.  During driver activity we should enable
14605779Sxy150489 		 * SmartSpeed, so performance is maintained.
14615779Sxy150489 		 */
14625779Sxy150489 		if (phy->smart_speed == e1000_smart_speed_on) {
14638571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.read_reg(hw,
14645779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
14655779Sxy150489 			    &data);
14665779Sxy150489 			if (ret_val)
14675779Sxy150489 				goto out;
14685779Sxy150489 
14695779Sxy150489 			data |= IGP01E1000_PSCFR_SMART_SPEED;
14708571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.write_reg(hw,
14715779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
14725779Sxy150489 			    data);
14735779Sxy150489 			if (ret_val)
14745779Sxy150489 				goto out;
14755779Sxy150489 		} else if (phy->smart_speed == e1000_smart_speed_off) {
14768571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.read_reg(hw,
14775779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
14785779Sxy150489 			    &data);
14795779Sxy150489 			if (ret_val)
14805779Sxy150489 				goto out;
14815779Sxy150489 
14825779Sxy150489 			data &= ~IGP01E1000_PSCFR_SMART_SPEED;
14838571SChenlu.Chen@Sun.COM 			ret_val = phy->ops.write_reg(hw,
14845779Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
14855779Sxy150489 			    data);
14865779Sxy150489 			if (ret_val)
14875779Sxy150489 				goto out;
14885779Sxy150489 		}
14895779Sxy150489 	} else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) ||
14905779Sxy150489 	    (phy->autoneg_advertised == E1000_ALL_NOT_GIG) ||
14915779Sxy150489 	    (phy->autoneg_advertised == E1000_ALL_10_SPEED)) {
14925779Sxy150489 		data |= IGP02E1000_PM_D3_LPLU;
14938571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw,
14945779Sxy150489 		    IGP02E1000_PHY_POWER_MGMT,
14955779Sxy150489 		    data);
14965779Sxy150489 		if (ret_val)
14975779Sxy150489 			goto out;
14985779Sxy150489 
14995779Sxy150489 		/* When LPLU is enabled, we should disable SmartSpeed */
15008571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw,
15015779Sxy150489 		    IGP01E1000_PHY_PORT_CONFIG,
15025779Sxy150489 		    &data);
15035779Sxy150489 		if (ret_val)
15045779Sxy150489 			goto out;
15055779Sxy150489 
15065779Sxy150489 		data &= ~IGP01E1000_PSCFR_SMART_SPEED;
15078571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.write_reg(hw,
15085779Sxy150489 		    IGP01E1000_PHY_PORT_CONFIG,
15095779Sxy150489 		    data);
15105779Sxy150489 	}
15115779Sxy150489 
15125779Sxy150489 out:
15135779Sxy150489 	return (ret_val);
15145779Sxy150489 }
15155779Sxy150489 
15165779Sxy150489 /*
15178571SChenlu.Chen@Sun.COM  * e1000_check_downshift_generic - Checks whether a downshift in speed occurred
15185779Sxy150489  * @hw: pointer to the HW structure
15195779Sxy150489  *
15205779Sxy150489  * Success returns 0, Failure returns 1
15215779Sxy150489  *
15225779Sxy150489  * A downshift is detected by querying the PHY link health.
15235779Sxy150489  */
15245779Sxy150489 s32
15255779Sxy150489 e1000_check_downshift_generic(struct e1000_hw *hw)
15265779Sxy150489 {
15275779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
15285779Sxy150489 	s32 ret_val;
15295779Sxy150489 	u16 phy_data, offset, mask;
15305779Sxy150489 
15315779Sxy150489 	DEBUGFUNC("e1000_check_downshift_generic");
15325779Sxy150489 
15335779Sxy150489 	switch (phy->type) {
15345779Sxy150489 	case e1000_phy_m88:
15355779Sxy150489 	case e1000_phy_gg82563:
15365779Sxy150489 		offset	= M88E1000_PHY_SPEC_STATUS;
15375779Sxy150489 		mask	= M88E1000_PSSR_DOWNSHIFT;
15385779Sxy150489 		break;
15395779Sxy150489 	case e1000_phy_igp_2:
15405779Sxy150489 	case e1000_phy_igp:
15415779Sxy150489 	case e1000_phy_igp_3:
15425779Sxy150489 		offset	= IGP01E1000_PHY_LINK_HEALTH;
15435779Sxy150489 		mask	= IGP01E1000_PLHR_SS_DOWNGRADE;
15445779Sxy150489 		break;
15455779Sxy150489 	default:
15465779Sxy150489 		/* speed downshift not supported */
15478571SChenlu.Chen@Sun.COM 		phy->speed_downgraded = false;
15485779Sxy150489 		ret_val = E1000_SUCCESS;
15495779Sxy150489 		goto out;
15505779Sxy150489 	}
15515779Sxy150489 
15528571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
15535779Sxy150489 
15545779Sxy150489 	if (!ret_val)
15558571SChenlu.Chen@Sun.COM 		phy->speed_downgraded = (phy_data & mask) ? true : false;
15565779Sxy150489 
15575779Sxy150489 out:
15585779Sxy150489 	return (ret_val);
15595779Sxy150489 }
15605779Sxy150489 
15615779Sxy150489 /*
15625779Sxy150489  * e1000_check_polarity_m88 - Checks the polarity.
15635779Sxy150489  * @hw: pointer to the HW structure
15645779Sxy150489  *
15655779Sxy150489  * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
15665779Sxy150489  *
15675779Sxy150489  * Polarity is determined based on the PHY specific status register.
15685779Sxy150489  */
15695779Sxy150489 s32
15705779Sxy150489 e1000_check_polarity_m88(struct e1000_hw *hw)
15715779Sxy150489 {
15725779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
15735779Sxy150489 	s32 ret_val;
15745779Sxy150489 	u16 data;
15755779Sxy150489 
15765779Sxy150489 	DEBUGFUNC("e1000_check_polarity_m88");
15775779Sxy150489 
15788571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &data);
15795779Sxy150489 
15805779Sxy150489 	if (!ret_val)
15815779Sxy150489 		phy->cable_polarity = (data & M88E1000_PSSR_REV_POLARITY)
15825779Sxy150489 		    ? e1000_rev_polarity_reversed
15835779Sxy150489 		    : e1000_rev_polarity_normal;
15845779Sxy150489 
15855779Sxy150489 	return (ret_val);
15865779Sxy150489 }
15875779Sxy150489 
15885779Sxy150489 /*
15895779Sxy150489  * e1000_check_polarity_igp - Checks the polarity.
15905779Sxy150489  * @hw: pointer to the HW structure
15915779Sxy150489  *
15925779Sxy150489  * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
15935779Sxy150489  *
15945779Sxy150489  * Polarity is determined based on the PHY port status register, and the
15955779Sxy150489  * current speed (since there is no polarity at 100Mbps).
15965779Sxy150489  */
15975779Sxy150489 s32
15985779Sxy150489 e1000_check_polarity_igp(struct e1000_hw *hw)
15995779Sxy150489 {
16005779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
16015779Sxy150489 	s32 ret_val;
16025779Sxy150489 	u16 data, offset, mask;
16035779Sxy150489 
16045779Sxy150489 	DEBUGFUNC("e1000_check_polarity_igp");
16055779Sxy150489 
16065779Sxy150489 	/*
16075779Sxy150489 	 * Polarity is determined based on the speed of
16085779Sxy150489 	 * our connection.
16095779Sxy150489 	 */
16108571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data);
16115779Sxy150489 	if (ret_val)
16125779Sxy150489 		goto out;
16135779Sxy150489 
16145779Sxy150489 	if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
16155779Sxy150489 	    IGP01E1000_PSSR_SPEED_1000MBPS) {
16165779Sxy150489 		offset	= IGP01E1000_PHY_PCS_INIT_REG;
16175779Sxy150489 		mask	= IGP01E1000_PHY_POLARITY_MASK;
16185779Sxy150489 	} else {
16195779Sxy150489 		/*
16205779Sxy150489 		 * This really only applies to 10Mbps since
16215779Sxy150489 		 * there is no polarity for 100Mbps (always 0).
16225779Sxy150489 		 */
16235779Sxy150489 		offset	= IGP01E1000_PHY_PORT_STATUS;
16245779Sxy150489 		mask	= IGP01E1000_PSSR_POLARITY_REVERSED;
16255779Sxy150489 	}
16265779Sxy150489 
16278571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, offset, &data);
16285779Sxy150489 
16295779Sxy150489 	if (!ret_val)
16305779Sxy150489 		phy->cable_polarity = (data & mask)
16315779Sxy150489 		    ? e1000_rev_polarity_reversed
16325779Sxy150489 		    : e1000_rev_polarity_normal;
16335779Sxy150489 
16345779Sxy150489 out:
16355779Sxy150489 	return (ret_val);
16365779Sxy150489 }
16375779Sxy150489 
16385779Sxy150489 /*
1639*10319SJason.Xu@Sun.COM  * e1000_check_polarity_ife - Check cable polarity for IFE PHY
1640*10319SJason.Xu@Sun.COM  * @hw: pointer to the HW structure
1641*10319SJason.Xu@Sun.COM  *
1642*10319SJason.Xu@Sun.COM  * Polarity is determined on the polarity reversal feature being enabled.
1643*10319SJason.Xu@Sun.COM  */
1644*10319SJason.Xu@Sun.COM s32
1645*10319SJason.Xu@Sun.COM e1000_check_polarity_ife(struct e1000_hw *hw)
1646*10319SJason.Xu@Sun.COM {
1647*10319SJason.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
1648*10319SJason.Xu@Sun.COM 	s32 ret_val;
1649*10319SJason.Xu@Sun.COM 	u16 phy_data, offset, mask;
1650*10319SJason.Xu@Sun.COM 
1651*10319SJason.Xu@Sun.COM 	DEBUGFUNC("e1000_check_polarity_ife");
1652*10319SJason.Xu@Sun.COM 
1653*10319SJason.Xu@Sun.COM 	/*
1654*10319SJason.Xu@Sun.COM 	 * Polarity is determined based on the reversal feature being enabled.
1655*10319SJason.Xu@Sun.COM 	 */
1656*10319SJason.Xu@Sun.COM 	if (phy->polarity_correction) {
1657*10319SJason.Xu@Sun.COM 		offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
1658*10319SJason.Xu@Sun.COM 		mask = IFE_PESC_POLARITY_REVERSED;
1659*10319SJason.Xu@Sun.COM 	} else {
1660*10319SJason.Xu@Sun.COM 		offset = IFE_PHY_SPECIAL_CONTROL;
1661*10319SJason.Xu@Sun.COM 		mask = IFE_PSC_FORCE_POLARITY;
1662*10319SJason.Xu@Sun.COM 	}
1663*10319SJason.Xu@Sun.COM 
1664*10319SJason.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
1665*10319SJason.Xu@Sun.COM 
1666*10319SJason.Xu@Sun.COM 	if (!ret_val)
1667*10319SJason.Xu@Sun.COM 		phy->cable_polarity = (phy_data & mask)
1668*10319SJason.Xu@Sun.COM 		    ? e1000_rev_polarity_reversed
1669*10319SJason.Xu@Sun.COM 		    : e1000_rev_polarity_normal;
1670*10319SJason.Xu@Sun.COM 
1671*10319SJason.Xu@Sun.COM 	return (ret_val);
1672*10319SJason.Xu@Sun.COM }
1673*10319SJason.Xu@Sun.COM /*
16748571SChenlu.Chen@Sun.COM  * e1000_wait_autoneg_generic - Wait for auto-neg completion
16755779Sxy150489  * @hw: pointer to the HW structure
16765779Sxy150489  *
16775779Sxy150489  * Waits for auto-negotiation to complete or for the auto-negotiation time
16785779Sxy150489  * limit to expire, which ever happens first.
16795779Sxy150489  */
16805779Sxy150489 s32
16815779Sxy150489 e1000_wait_autoneg_generic(struct e1000_hw *hw)
16825779Sxy150489 {
16835779Sxy150489 	s32 ret_val = E1000_SUCCESS;
16845779Sxy150489 	u16 i, phy_status;
16855779Sxy150489 
16865779Sxy150489 	DEBUGFUNC("e1000_wait_autoneg_generic");
16875779Sxy150489 
16888571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.read_reg))
16898571SChenlu.Chen@Sun.COM 		return (E1000_SUCCESS);
16908571SChenlu.Chen@Sun.COM 
16915779Sxy150489 	/* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */
16925779Sxy150489 	for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) {
16938571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
16945779Sxy150489 		if (ret_val)
16955779Sxy150489 			break;
16968571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
16975779Sxy150489 		if (ret_val)
16985779Sxy150489 			break;
16995779Sxy150489 		if (phy_status & MII_SR_AUTONEG_COMPLETE)
17005779Sxy150489 			break;
17015779Sxy150489 		msec_delay(100);
17025779Sxy150489 	}
17035779Sxy150489 
17045779Sxy150489 	/*
17055779Sxy150489 	 * PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
17065779Sxy150489 	 * has completed.
17075779Sxy150489 	 */
17085779Sxy150489 	return (ret_val);
17095779Sxy150489 }
17105779Sxy150489 
17115779Sxy150489 /*
17125779Sxy150489  * e1000_phy_has_link_generic - Polls PHY for link
17135779Sxy150489  * @hw: pointer to the HW structure
17145779Sxy150489  * @iterations: number of times to poll for link
17155779Sxy150489  * @usec_interval: delay between polling attempts
17165779Sxy150489  * @success: pointer to whether polling was successful or not
17175779Sxy150489  *
17185779Sxy150489  * Polls the PHY status register for link, 'iterations' number of times.
17195779Sxy150489  */
17205779Sxy150489 s32
17215779Sxy150489 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
17225779Sxy150489     u32 usec_interval, bool *success)
17235779Sxy150489 {
17245779Sxy150489 	s32 ret_val = E1000_SUCCESS;
17255779Sxy150489 	u16 i, phy_status;
17265779Sxy150489 
17275779Sxy150489 	DEBUGFUNC("e1000_phy_has_link_generic");
17285779Sxy150489 
17298571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.read_reg))
17308571SChenlu.Chen@Sun.COM 		return (E1000_SUCCESS);
17318571SChenlu.Chen@Sun.COM 
17325779Sxy150489 	for (i = 0; i < iterations; i++) {
17335779Sxy150489 		/*
17345779Sxy150489 		 * Some PHYs require the PHY_STATUS register to be read
17355779Sxy150489 		 * twice due to the link bit being sticky.  No harm doing
17365779Sxy150489 		 * it across the board.
17375779Sxy150489 		 */
17388571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
1739*10319SJason.Xu@Sun.COM 		if (ret_val) {
1740*10319SJason.Xu@Sun.COM 			/*
1741*10319SJason.Xu@Sun.COM 			 * If the first read fails, another entity may have
1742*10319SJason.Xu@Sun.COM 			 * ownership of the resources, wait and try again to
1743*10319SJason.Xu@Sun.COM 			 * see if they have relinquished the resources yet.
1744*10319SJason.Xu@Sun.COM 			 */
1745*10319SJason.Xu@Sun.COM 			usec_delay(usec_interval);
1746*10319SJason.Xu@Sun.COM 			ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
1747*10319SJason.Xu@Sun.COM 			    &phy_status);
1748*10319SJason.Xu@Sun.COM 		}
17495779Sxy150489 		if (ret_val)
17505779Sxy150489 			break;
17515779Sxy150489 		if (phy_status & MII_SR_LINK_STATUS)
17525779Sxy150489 			break;
17535779Sxy150489 		if (usec_interval >= 1000)
17545779Sxy150489 			msec_delay_irq(usec_interval/1000);
17555779Sxy150489 		else
17565779Sxy150489 			usec_delay(usec_interval);
17575779Sxy150489 	}
17585779Sxy150489 
17598571SChenlu.Chen@Sun.COM 	*success = (i < iterations) ? true : false;
17605779Sxy150489 
17615779Sxy150489 	return (ret_val);
17625779Sxy150489 }
17635779Sxy150489 
17645779Sxy150489 /*
17655779Sxy150489  * e1000_get_cable_length_m88 - Determine cable length for m88 PHY
17665779Sxy150489  * @hw: pointer to the HW structure
17675779Sxy150489  *
17685779Sxy150489  * Reads the PHY specific status register to retrieve the cable length
17695779Sxy150489  * information.  The cable length is determined by averaging the minimum and
17705779Sxy150489  * maximum values to get the "average" cable length.  The m88 PHY has four
17715779Sxy150489  * possible cable length values, which are:
17725779Sxy150489  *	Register Value		Cable Length
17735779Sxy150489  *	0			< 50 meters
17745779Sxy150489  *	1			50 - 80 meters
17755779Sxy150489  *	2			80 - 110 meters
17765779Sxy150489  *	3			110 - 140 meters
17775779Sxy150489  *	4			> 140 meters
17785779Sxy150489  */
17795779Sxy150489 s32
17805779Sxy150489 e1000_get_cable_length_m88(struct e1000_hw *hw)
17815779Sxy150489 {
17825779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
17835779Sxy150489 	s32 ret_val;
17845779Sxy150489 	u16 phy_data, index;
17855779Sxy150489 
17865779Sxy150489 	DEBUGFUNC("e1000_get_cable_length_m88");
17875779Sxy150489 
17888571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
17895779Sxy150489 	if (ret_val)
17905779Sxy150489 		goto out;
17915779Sxy150489 
17925779Sxy150489 	index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
17935779Sxy150489 	    M88E1000_PSSR_CABLE_LENGTH_SHIFT;
17948571SChenlu.Chen@Sun.COM 	if (index < (M88E1000_CABLE_LENGTH_TABLE_SIZE + 1)) {
1795*10319SJason.Xu@Sun.COM 		ret_val = E1000_ERR_PHY;
1796*10319SJason.Xu@Sun.COM 		goto out;
1797*10319SJason.Xu@Sun.COM 	}
17985779Sxy150489 
1799*10319SJason.Xu@Sun.COM 	phy->min_cable_length = e1000_m88_cable_length_table[index];
1800*10319SJason.Xu@Sun.COM 	phy->max_cable_length = e1000_m88_cable_length_table[index+1];
1801*10319SJason.Xu@Sun.COM 
1802*10319SJason.Xu@Sun.COM 	phy->cable_length = (phy->min_cable_length +
1803*10319SJason.Xu@Sun.COM 	    phy->max_cable_length) / 2;
18045779Sxy150489 
18055779Sxy150489 out:
18065779Sxy150489 	return (ret_val);
18075779Sxy150489 }
18085779Sxy150489 
18095779Sxy150489 /*
18105779Sxy150489  * e1000_get_cable_length_igp_2 - Determine cable length for igp2 PHY
18115779Sxy150489  * @hw: pointer to the HW structure
18125779Sxy150489  *
18135779Sxy150489  * The automatic gain control (agc) normalizes the amplitude of the
18145779Sxy150489  * received signal, adjusting for the attenuation produced by the
18158571SChenlu.Chen@Sun.COM  * cable.  By reading the AGC registers, which represent the
18168571SChenlu.Chen@Sun.COM  * combination of coarse and fine gain value, the value can be put
18175779Sxy150489  * into a lookup table to obtain the approximate cable length
18185779Sxy150489  * for each channel.
18195779Sxy150489  */
18205779Sxy150489 s32
18215779Sxy150489 e1000_get_cable_length_igp_2(struct e1000_hw *hw)
18225779Sxy150489 {
18235779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
18245779Sxy150489 	s32 ret_val = E1000_SUCCESS;
18255779Sxy150489 	u16 phy_data, i, agc_value = 0;
18265779Sxy150489 	u16 cur_agc_index, max_agc_index = 0;
18275779Sxy150489 	u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1;
18285779Sxy150489 	u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] =
18295779Sxy150489 		{IGP02E1000_PHY_AGC_A,
18305779Sxy150489 		IGP02E1000_PHY_AGC_B,
18315779Sxy150489 		IGP02E1000_PHY_AGC_C,
18325779Sxy150489 		IGP02E1000_PHY_AGC_D};
18335779Sxy150489 
18345779Sxy150489 	DEBUGFUNC("e1000_get_cable_length_igp_2");
18355779Sxy150489 
18365779Sxy150489 	/* Read the AGC registers for all channels */
18375779Sxy150489 	for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) {
18388571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &phy_data);
18395779Sxy150489 		if (ret_val)
18405779Sxy150489 			goto out;
18415779Sxy150489 
18425779Sxy150489 		/*
18435779Sxy150489 		 * Getting bits 15:9, which represent the combination of
18448571SChenlu.Chen@Sun.COM 		 * coarse and fine gain values.  The result is a number
18455779Sxy150489 		 * that can be put into the lookup table to obtain the
18465779Sxy150489 		 * approximate cable length.
18475779Sxy150489 		 */
18485779Sxy150489 		cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) &
18495779Sxy150489 		    IGP02E1000_AGC_LENGTH_MASK;
18505779Sxy150489 
18515779Sxy150489 		/* Array index bound check. */
18525779Sxy150489 		if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) ||
18535779Sxy150489 		    (cur_agc_index == 0)) {
18545779Sxy150489 			ret_val = -E1000_ERR_PHY;
18555779Sxy150489 			goto out;
18565779Sxy150489 		}
18575779Sxy150489 
18585779Sxy150489 		/* Remove min & max AGC values from calculation. */
18595779Sxy150489 		if (e1000_igp_2_cable_length_table[min_agc_index] >
18605779Sxy150489 		    e1000_igp_2_cable_length_table[cur_agc_index])
18615779Sxy150489 			min_agc_index = cur_agc_index;
18625779Sxy150489 		if (e1000_igp_2_cable_length_table[max_agc_index] <
18635779Sxy150489 		    e1000_igp_2_cable_length_table[cur_agc_index])
18645779Sxy150489 			max_agc_index = cur_agc_index;
18655779Sxy150489 
18665779Sxy150489 		agc_value += e1000_igp_2_cable_length_table[cur_agc_index];
18675779Sxy150489 	}
18685779Sxy150489 
18695779Sxy150489 	agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] +
18705779Sxy150489 	    e1000_igp_2_cable_length_table[max_agc_index]);
18715779Sxy150489 	agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2);
18725779Sxy150489 
18735779Sxy150489 	/* Calculate cable length with the error range of +/- 10 meters. */
18745779Sxy150489 	phy->min_cable_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ?
18755779Sxy150489 	    (agc_value - IGP02E1000_AGC_RANGE) : 0;
18765779Sxy150489 	phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE;
18775779Sxy150489 
18785779Sxy150489 	phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
18795779Sxy150489 
18805779Sxy150489 out:
18815779Sxy150489 	return (ret_val);
18825779Sxy150489 }
18835779Sxy150489 
18845779Sxy150489 /*
18855779Sxy150489  * e1000_get_phy_info_m88 - Retrieve PHY information
18865779Sxy150489  * @hw: pointer to the HW structure
18875779Sxy150489  *
18885779Sxy150489  * Valid for only copper links.  Read the PHY status register (sticky read)
18895779Sxy150489  * to verify that link is up.  Read the PHY special control register to
18905779Sxy150489  * determine the polarity and 10base-T extended distance.  Read the PHY
18915779Sxy150489  * special status register to determine MDI/MDIx and current speed.  If
18925779Sxy150489  * speed is 1000, then determine cable length, local and remote receiver.
18935779Sxy150489  */
18945779Sxy150489 s32
18955779Sxy150489 e1000_get_phy_info_m88(struct e1000_hw *hw)
18965779Sxy150489 {
18975779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
18985779Sxy150489 	s32  ret_val;
18995779Sxy150489 	u16 phy_data;
19005779Sxy150489 	bool link;
19015779Sxy150489 
19025779Sxy150489 	DEBUGFUNC("e1000_get_phy_info_m88");
19035779Sxy150489 
19045779Sxy150489 	if (hw->phy.media_type != e1000_media_type_copper) {
19055779Sxy150489 		DEBUGOUT("Phy info is only valid for copper media\n");
19065779Sxy150489 		ret_val = -E1000_ERR_CONFIG;
19075779Sxy150489 		goto out;
19085779Sxy150489 	}
19095779Sxy150489 
19105779Sxy150489 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
19115779Sxy150489 	if (ret_val)
19125779Sxy150489 		goto out;
19135779Sxy150489 
19145779Sxy150489 	if (!link) {
19155779Sxy150489 		DEBUGOUT("Phy info is only valid if link is up\n");
19165779Sxy150489 		ret_val = -E1000_ERR_CONFIG;
19175779Sxy150489 		goto out;
19185779Sxy150489 	}
19195779Sxy150489 
19208571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
19215779Sxy150489 	if (ret_val)
19225779Sxy150489 		goto out;
19235779Sxy150489 
19245779Sxy150489 	phy->polarity_correction = (phy_data & M88E1000_PSCR_POLARITY_REVERSAL)
19258571SChenlu.Chen@Sun.COM 	    ? true : false;
19265779Sxy150489 
19275779Sxy150489 	ret_val = e1000_check_polarity_m88(hw);
19285779Sxy150489 	if (ret_val)
19295779Sxy150489 		goto out;
19305779Sxy150489 
19318571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
19325779Sxy150489 	if (ret_val)
19335779Sxy150489 		goto out;
19345779Sxy150489 
19358571SChenlu.Chen@Sun.COM 	phy->is_mdix = (phy_data & M88E1000_PSSR_MDIX) ? true : false;
19365779Sxy150489 
19375779Sxy150489 	if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) {
19388571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.get_cable_length(hw);
19395779Sxy150489 		if (ret_val)
19405779Sxy150489 			goto out;
19415779Sxy150489 
19428571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data);
19435779Sxy150489 		if (ret_val)
19445779Sxy150489 			goto out;
19455779Sxy150489 
19465779Sxy150489 		phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS)
19475779Sxy150489 		    ? e1000_1000t_rx_status_ok
19485779Sxy150489 		    : e1000_1000t_rx_status_not_ok;
19495779Sxy150489 
19505779Sxy150489 		phy->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS)
19515779Sxy150489 		    ? e1000_1000t_rx_status_ok
19525779Sxy150489 		    : e1000_1000t_rx_status_not_ok;
19535779Sxy150489 	} else {
19545779Sxy150489 		/* Set values to "undefined" */
19555779Sxy150489 		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
19565779Sxy150489 		phy->local_rx = e1000_1000t_rx_status_undefined;
19575779Sxy150489 		phy->remote_rx = e1000_1000t_rx_status_undefined;
19585779Sxy150489 	}
19595779Sxy150489 
19605779Sxy150489 out:
19615779Sxy150489 	return (ret_val);
19625779Sxy150489 }
19635779Sxy150489 
19645779Sxy150489 /*
19655779Sxy150489  * e1000_get_phy_info_igp - Retrieve igp PHY information
19665779Sxy150489  * @hw: pointer to the HW structure
19675779Sxy150489  *
19685779Sxy150489  * Read PHY status to determine if link is up.  If link is up, then
19695779Sxy150489  * set/determine 10base-T extended distance and polarity correction.  Read
19705779Sxy150489  * PHY port status to determine MDI/MDIx and speed.  Based on the speed,
19715779Sxy150489  * determine on the cable length, local and remote receiver.
19725779Sxy150489  */
19735779Sxy150489 s32
19745779Sxy150489 e1000_get_phy_info_igp(struct e1000_hw *hw)
19755779Sxy150489 {
19765779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
19775779Sxy150489 	s32 ret_val;
19785779Sxy150489 	u16 data;
19795779Sxy150489 	bool link;
19805779Sxy150489 
19815779Sxy150489 	DEBUGFUNC("e1000_get_phy_info_igp");
19825779Sxy150489 
19835779Sxy150489 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
19845779Sxy150489 	if (ret_val)
19855779Sxy150489 		goto out;
19865779Sxy150489 
19875779Sxy150489 	if (!link) {
19885779Sxy150489 		DEBUGOUT("Phy info is only valid if link is up\n");
19895779Sxy150489 		ret_val = -E1000_ERR_CONFIG;
19905779Sxy150489 		goto out;
19915779Sxy150489 	}
19925779Sxy150489 
19938571SChenlu.Chen@Sun.COM 	phy->polarity_correction = true;
19945779Sxy150489 
19955779Sxy150489 	ret_val = e1000_check_polarity_igp(hw);
19965779Sxy150489 	if (ret_val)
19975779Sxy150489 		goto out;
19985779Sxy150489 
19998571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data);
20005779Sxy150489 	if (ret_val)
20015779Sxy150489 		goto out;
20025779Sxy150489 
20038571SChenlu.Chen@Sun.COM 	phy->is_mdix = (data & IGP01E1000_PSSR_MDIX) ? true : false;
20045779Sxy150489 
20055779Sxy150489 	if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
20065779Sxy150489 	    IGP01E1000_PSSR_SPEED_1000MBPS) {
20078571SChenlu.Chen@Sun.COM 		ret_val = hw->phy.ops.get_cable_length(hw);
20085779Sxy150489 		if (ret_val)
20095779Sxy150489 			goto out;
20105779Sxy150489 
20118571SChenlu.Chen@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
20125779Sxy150489 		if (ret_val)
20135779Sxy150489 			goto out;
20145779Sxy150489 
20155779Sxy150489 		phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
20165779Sxy150489 		    ? e1000_1000t_rx_status_ok
20175779Sxy150489 		    : e1000_1000t_rx_status_not_ok;
20185779Sxy150489 
20195779Sxy150489 		phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
20205779Sxy150489 		    ? e1000_1000t_rx_status_ok
20215779Sxy150489 		    : e1000_1000t_rx_status_not_ok;
20225779Sxy150489 	} else {
20235779Sxy150489 		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
20245779Sxy150489 		phy->local_rx = e1000_1000t_rx_status_undefined;
20255779Sxy150489 		phy->remote_rx = e1000_1000t_rx_status_undefined;
20265779Sxy150489 	}
20275779Sxy150489 
20285779Sxy150489 out:
20295779Sxy150489 	return (ret_val);
20305779Sxy150489 }
20315779Sxy150489 
20325779Sxy150489 /*
20335779Sxy150489  * e1000_phy_sw_reset_generic - PHY software reset
20345779Sxy150489  * @hw: pointer to the HW structure
20355779Sxy150489  *
20365779Sxy150489  * Does a software reset of the PHY by reading the PHY control register and
20375779Sxy150489  * setting/write the control register reset bit to the PHY.
20385779Sxy150489  */
20395779Sxy150489 s32
20405779Sxy150489 e1000_phy_sw_reset_generic(struct e1000_hw *hw)
20415779Sxy150489 {
20428571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
20435779Sxy150489 	u16 phy_ctrl;
20445779Sxy150489 
20455779Sxy150489 	DEBUGFUNC("e1000_phy_sw_reset_generic");
20465779Sxy150489 
20478571SChenlu.Chen@Sun.COM 	if (!(hw->phy.ops.read_reg))
20488571SChenlu.Chen@Sun.COM 		goto out;
20498571SChenlu.Chen@Sun.COM 
20508571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
20515779Sxy150489 	if (ret_val)
20525779Sxy150489 		goto out;
20535779Sxy150489 
20545779Sxy150489 	phy_ctrl |= MII_CR_RESET;
20558571SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
20565779Sxy150489 	if (ret_val)
20575779Sxy150489 		goto out;
20585779Sxy150489 
20595779Sxy150489 	usec_delay(1);
20605779Sxy150489 
20615779Sxy150489 out:
20625779Sxy150489 	return (ret_val);
20635779Sxy150489 }
20645779Sxy150489 
20655779Sxy150489 /*
20665779Sxy150489  * e1000_phy_hw_reset_generic - PHY hardware reset
20675779Sxy150489  * @hw: pointer to the HW structure
20685779Sxy150489  *
20695779Sxy150489  * Verify the reset block is not blocking us from resetting.  Acquire
20705779Sxy150489  * semaphore (if necessary) and read/set/write the device control reset
20715779Sxy150489  * bit in the PHY.  Wait the appropriate delay time for the device to
20728571SChenlu.Chen@Sun.COM  * reset and release the semaphore (if necessary).
20735779Sxy150489  */
20745779Sxy150489 s32
20755779Sxy150489 e1000_phy_hw_reset_generic(struct e1000_hw *hw)
20765779Sxy150489 {
20775779Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
20788571SChenlu.Chen@Sun.COM 	s32 ret_val = E1000_SUCCESS;
20795779Sxy150489 	u32 ctrl;
20805779Sxy150489 
20815779Sxy150489 	DEBUGFUNC("e1000_phy_hw_reset_generic");
20825779Sxy150489 
20838571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.check_reset_block(hw);
20845779Sxy150489 	if (ret_val) {
20855779Sxy150489 		ret_val = E1000_SUCCESS;
20865779Sxy150489 		goto out;
20875779Sxy150489 	}
20885779Sxy150489 
20898571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.acquire(hw);
20905779Sxy150489 	if (ret_val)
20915779Sxy150489 		goto out;
20925779Sxy150489 
20935779Sxy150489 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
20945779Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST);
20955779Sxy150489 	E1000_WRITE_FLUSH(hw);
20965779Sxy150489 
20975779Sxy150489 	usec_delay(phy->reset_delay_us);
20985779Sxy150489 
20995779Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
21005779Sxy150489 	E1000_WRITE_FLUSH(hw);
21015779Sxy150489 
21025779Sxy150489 	usec_delay(150);
21035779Sxy150489 
21048571SChenlu.Chen@Sun.COM 	phy->ops.release(hw);
21055779Sxy150489 
21068571SChenlu.Chen@Sun.COM 	ret_val = phy->ops.get_cfg_done(hw);
21075779Sxy150489 
21085779Sxy150489 out:
21095779Sxy150489 	return (ret_val);
21105779Sxy150489 }
21115779Sxy150489 
21125779Sxy150489 /*
21135779Sxy150489  * e1000_get_cfg_done_generic - Generic configuration done
21145779Sxy150489  * @hw: pointer to the HW structure
21155779Sxy150489  *
21165779Sxy150489  * Generic function to wait 10 milli-seconds for configuration to complete
21175779Sxy150489  * and return success.
21185779Sxy150489  */
21195779Sxy150489 s32
21205779Sxy150489 e1000_get_cfg_done_generic(struct e1000_hw *hw)
21215779Sxy150489 {
21225779Sxy150489 	DEBUGFUNC("e1000_get_cfg_done_generic");
21238571SChenlu.Chen@Sun.COM 	UNREFERENCED_1PARAMETER(hw);
21245779Sxy150489 
21255779Sxy150489 	msec_delay_irq(10);
21265779Sxy150489 
21275779Sxy150489 	return (E1000_SUCCESS);
21285779Sxy150489 }
21295779Sxy150489 
21305779Sxy150489 /*
21315779Sxy150489  * e1000_phy_init_script_igp3 - Inits the IGP3 PHY
21325779Sxy150489  * @hw: pointer to the HW structure
21335779Sxy150489  *
21345779Sxy150489  * Initializes a Intel Gigabit PHY3 when an EEPROM is not present.
21355779Sxy150489  */
21365779Sxy150489 s32
21375779Sxy150489 e1000_phy_init_script_igp3(struct e1000_hw *hw)
21385779Sxy150489 {
21395779Sxy150489 	DEBUGOUT("Running IGP 3 PHY init script\n");
21405779Sxy150489 
21415779Sxy150489 	/* PHY init IGP 3 */
21425779Sxy150489 	/* Enable rise/fall, 10-mode work in class-A */
21438571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2F5B, 0x9018);
21445779Sxy150489 	/* Remove all caps from Replica path filter */
21458571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2F52, 0x0000);
21465779Sxy150489 	/* Bias trimming for ADC, AFE and Driver (Default) */
21478571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2FB1, 0x8B24);
21485779Sxy150489 	/* Increase Hybrid poly bias */
21498571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2FB2, 0xF8F0);
21508571SChenlu.Chen@Sun.COM 	/* Add 4% to Tx amplitude in Gig mode */
21518571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2010, 0x10B0);
21525779Sxy150489 	/* Disable trimming (TTT) */
21538571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2011, 0x0000);
21545779Sxy150489 	/* Poly DC correction to 94.6% + 2% for all channels */
21558571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x20DD, 0x249A);
21565779Sxy150489 	/* ABS DC correction to 95.9% */
21578571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x20DE, 0x00D3);
21585779Sxy150489 	/* BG temp curve trim */
21598571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x28B4, 0x04CE);
21605779Sxy150489 	/* Increasing ADC OPAMP stage 1 currents to max */
21618571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x2F70, 0x29E4);
21625779Sxy150489 	/* Force 1000 ( required for enabling PHY regs configuration) */
21638571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x0000, 0x0140);
21645779Sxy150489 	/* Set upd_freq to 6 */
21658571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F30, 0x1606);
21665779Sxy150489 	/* Disable NPDFE */
21678571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F31, 0xB814);
21685779Sxy150489 	/* Disable adaptive fixed FFE (Default) */
21698571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F35, 0x002A);
21705779Sxy150489 	/* Enable FFE hysteresis */
21718571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F3E, 0x0067);
21725779Sxy150489 	/* Fixed FFE for short cable lengths */
21738571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F54, 0x0065);
21745779Sxy150489 	/* Fixed FFE for medium cable lengths */
21758571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F55, 0x002A);
21765779Sxy150489 	/* Fixed FFE for long cable lengths */
21778571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F56, 0x002A);
21785779Sxy150489 	/* Enable Adaptive Clip Threshold */
21798571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F72, 0x3FB0);
21805779Sxy150489 	/* AHT reset limit to 1 */
21818571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F76, 0xC0FF);
21825779Sxy150489 	/* Set AHT master delay to 127 msec */
21838571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F77, 0x1DEC);
21845779Sxy150489 	/* Set scan bits for AHT */
21858571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F78, 0xF9EF);
21865779Sxy150489 	/* Set AHT Preset bits */
21878571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1F79, 0x0210);
21885779Sxy150489 	/* Change integ_factor of channel A to 3 */
21898571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1895, 0x0003);
21905779Sxy150489 	/* Change prop_factor of channels BCD to 8 */
21918571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1796, 0x0008);
21925779Sxy150489 	/* Change cg_icount + enable integbp for channels BCD */
21938571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1798, 0xD008);
21945779Sxy150489 	/*
21955779Sxy150489 	 * Change cg_icount + enable integbp + change prop_factor_master
21965779Sxy150489 	 * to 8 for channel A
21975779Sxy150489 	 */
21988571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x1898, 0xD918);
21995779Sxy150489 	/* Disable AHT in Slave mode on channel A */
22008571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x187A, 0x0800);
22015779Sxy150489 	/*
22025779Sxy150489 	 * Enable LPLU and disable AN to 1000 in non-D0a states,
22035779Sxy150489 	 * Enable SPD+B2B
22045779Sxy150489 	 */
22058571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x0019, 0x008D);
22065779Sxy150489 	/* Enable restart AN on an1000_dis change */
22078571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x001B, 0x2080);
22085779Sxy150489 	/* Enable wh_fifo read clock in 10/100 modes */
22098571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x0014, 0x0045);
22105779Sxy150489 	/* Restart AN, Speed selection is 1000 */
22118571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, 0x0000, 0x1340);
22125779Sxy150489 
22135779Sxy150489 	return (E1000_SUCCESS);
22145779Sxy150489 }
22155779Sxy150489 
22165779Sxy150489 /*
22175779Sxy150489  * e1000_get_phy_type_from_id - Get PHY type from id
22185779Sxy150489  * @phy_id: phy_id read from the phy
22195779Sxy150489  *
22205779Sxy150489  * Returns the phy type from the id.
22215779Sxy150489  */
22228571SChenlu.Chen@Sun.COM enum e1000_phy_type
22235779Sxy150489 e1000_get_phy_type_from_id(u32 phy_id)
22245779Sxy150489 {
22258571SChenlu.Chen@Sun.COM 	enum e1000_phy_type phy_type = e1000_phy_unknown;
22265779Sxy150489 
22275779Sxy150489 	switch (phy_id)	{
22285779Sxy150489 	case M88E1000_I_PHY_ID:
22295779Sxy150489 	case M88E1000_E_PHY_ID:
22305779Sxy150489 	case M88E1111_I_PHY_ID:
22315779Sxy150489 	case M88E1011_I_PHY_ID:
22325779Sxy150489 		phy_type = e1000_phy_m88;
22335779Sxy150489 		break;
22345779Sxy150489 	case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */
22355779Sxy150489 		phy_type = e1000_phy_igp_2;
22365779Sxy150489 		break;
22375779Sxy150489 	case GG82563_E_PHY_ID:
22385779Sxy150489 		phy_type = e1000_phy_gg82563;
22395779Sxy150489 		break;
22405779Sxy150489 	case IGP03E1000_E_PHY_ID:
22415779Sxy150489 		phy_type = e1000_phy_igp_3;
22425779Sxy150489 		break;
22435779Sxy150489 	case IFE_E_PHY_ID:
22445779Sxy150489 	case IFE_PLUS_E_PHY_ID:
22455779Sxy150489 	case IFE_C_E_PHY_ID:
22465779Sxy150489 		phy_type = e1000_phy_ife;
22475779Sxy150489 		break;
22485779Sxy150489 	default:
22495779Sxy150489 		phy_type = e1000_phy_unknown;
22505779Sxy150489 		break;
22515779Sxy150489 	}
22525779Sxy150489 	return (phy_type);
22535779Sxy150489 }
22545779Sxy150489 
22555779Sxy150489 /*
2256*10319SJason.Xu@Sun.COM  * e1000_determine_phy_address - Determines PHY address.
2257*10319SJason.Xu@Sun.COM  * @hw: pointer to the HW structure
2258*10319SJason.Xu@Sun.COM  *
2259*10319SJason.Xu@Sun.COM  * This uses a trial and error method to loop through possible PHY
2260*10319SJason.Xu@Sun.COM  * addresses. It tests each by reading the PHY ID registers and
2261*10319SJason.Xu@Sun.COM  * checking for a match.
2262*10319SJason.Xu@Sun.COM  */
2263*10319SJason.Xu@Sun.COM s32
2264*10319SJason.Xu@Sun.COM e1000_determine_phy_address(struct e1000_hw *hw)
2265*10319SJason.Xu@Sun.COM {
2266*10319SJason.Xu@Sun.COM 	s32 ret_val = -E1000_ERR_PHY_TYPE;
2267*10319SJason.Xu@Sun.COM 	u32 phy_addr = 0;
2268*10319SJason.Xu@Sun.COM 	u32 i;
2269*10319SJason.Xu@Sun.COM 	enum e1000_phy_type phy_type = e1000_phy_unknown;
2270*10319SJason.Xu@Sun.COM 
2271*10319SJason.Xu@Sun.COM 	hw->phy.id = phy_type;
2272*10319SJason.Xu@Sun.COM 
2273*10319SJason.Xu@Sun.COM 	for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) {
2274*10319SJason.Xu@Sun.COM 		hw->phy.addr = phy_addr;
2275*10319SJason.Xu@Sun.COM 		i = 0;
2276*10319SJason.Xu@Sun.COM 
2277*10319SJason.Xu@Sun.COM 		do {
2278*10319SJason.Xu@Sun.COM 			e1000_get_phy_id(hw);
2279*10319SJason.Xu@Sun.COM 			phy_type = e1000_get_phy_type_from_id(hw->phy.id);
2280*10319SJason.Xu@Sun.COM 
2281*10319SJason.Xu@Sun.COM 			/*
2282*10319SJason.Xu@Sun.COM 			 * If phy_type is valid, break - we found our
2283*10319SJason.Xu@Sun.COM 			 * PHY address
2284*10319SJason.Xu@Sun.COM 			 */
2285*10319SJason.Xu@Sun.COM 			if (phy_type  != e1000_phy_unknown) {
2286*10319SJason.Xu@Sun.COM 				ret_val = E1000_SUCCESS;
2287*10319SJason.Xu@Sun.COM 				goto out;
2288*10319SJason.Xu@Sun.COM 			}
2289*10319SJason.Xu@Sun.COM 			msec_delay(1);
2290*10319SJason.Xu@Sun.COM 			i++;
2291*10319SJason.Xu@Sun.COM 		} while (i < 10);
2292*10319SJason.Xu@Sun.COM 	}
2293*10319SJason.Xu@Sun.COM 
2294*10319SJason.Xu@Sun.COM out:
2295*10319SJason.Xu@Sun.COM 	return (ret_val);
2296*10319SJason.Xu@Sun.COM }
2297*10319SJason.Xu@Sun.COM /*
22985779Sxy150489  * e1000_power_up_phy_copper - Restore copper link in case of PHY power down
22995779Sxy150489  * @hw: pointer to the HW structure
23005779Sxy150489  *
23015779Sxy150489  * In the case of a PHY power down to save power, or to turn off link during a
23025779Sxy150489  * driver unload, or wake on lan is not enabled, restore the link to previous
23035779Sxy150489  * settings.
23045779Sxy150489  */
23055779Sxy150489 void
23065779Sxy150489 e1000_power_up_phy_copper(struct e1000_hw *hw)
23075779Sxy150489 {
23085779Sxy150489 	u16 mii_reg = 0;
23095779Sxy150489 
23105779Sxy150489 	/* The PHY will retain its settings across a power down/up cycle */
23118571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
23125779Sxy150489 	mii_reg &= ~MII_CR_POWER_DOWN;
23138571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
23145779Sxy150489 }
23155779Sxy150489 
23165779Sxy150489 /*
23175779Sxy150489  * e1000_power_down_phy_copper - Restore copper link in case of PHY power down
23185779Sxy150489  * @hw: pointer to the HW structure
23195779Sxy150489  *
23205779Sxy150489  * In the case of a PHY power down to save power, or to turn off link during a
23215779Sxy150489  * driver unload, or wake on lan is not enabled, restore the link to previous
23225779Sxy150489  * settings.
23235779Sxy150489  */
23245779Sxy150489 void
23255779Sxy150489 e1000_power_down_phy_copper(struct e1000_hw *hw)
23265779Sxy150489 {
23275779Sxy150489 	u16 mii_reg = 0;
23285779Sxy150489 
23295779Sxy150489 	/* The PHY will retain its settings across a power down/up cycle */
23308571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
23315779Sxy150489 	mii_reg |= MII_CR_POWER_DOWN;
23328571SChenlu.Chen@Sun.COM 	(void) hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
23335779Sxy150489 	msec_delay(1);
23345779Sxy150489 }
2335