xref: /onnv-gate/usr/src/uts/common/io/e1000g/e1000_phy.c (revision 11143:f855b68211a7)
14919Sxy150489 /*
24919Sxy150489  * This file is provided under a CDDLv1 license.  When using or
34919Sxy150489  * redistributing this file, you may do so under this license.
44919Sxy150489  * In redistributing this file this license must be included
54919Sxy150489  * and no other modification of this header file is permitted.
64919Sxy150489  *
74919Sxy150489  * CDDL LICENSE SUMMARY
84919Sxy150489  *
98479SChenlu.Chen@Sun.COM  * Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved.
104919Sxy150489  *
114919Sxy150489  * The contents of this file are subject to the terms of Version
124919Sxy150489  * 1.0 of the Common Development and Distribution License (the "License").
134919Sxy150489  *
144919Sxy150489  * You should have received a copy of the License with this software.
154919Sxy150489  * You can obtain a copy of the License at
164919Sxy150489  *	http://www.opensolaris.org/os/licensing.
174919Sxy150489  * See the License for the specific language governing permissions
184919Sxy150489  * and limitations under the License.
194919Sxy150489  */
204919Sxy150489 
214919Sxy150489 /*
228479SChenlu.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234919Sxy150489  * Use is subject to license terms of the CDDLv1.
244919Sxy150489  */
254919Sxy150489 
264919Sxy150489 /*
2711020SMin.Xu@Sun.COM  * IntelVersion: 1.151 v3-1-10-1_2009-9-18_Release14-6
284919Sxy150489  */
294919Sxy150489 #include "e1000_api.h"
304919Sxy150489 
314919Sxy150489 static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg);
328479SChenlu.Chen@Sun.COM static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
338479SChenlu.Chen@Sun.COM     u16 *data, bool read);
344919Sxy150489 
3510680SMin.Xu@Sun.COM static u32 e1000_get_phy_addr_for_hv_page(u32 page);
3610680SMin.Xu@Sun.COM static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
3710680SMin.Xu@Sun.COM     u16 *data, bool read);
3810680SMin.Xu@Sun.COM 
394919Sxy150489 /* Cable length tables */
404919Sxy150489 static const u16 e1000_m88_cable_length_table[] =
414919Sxy150489 	{0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED};
424919Sxy150489 
434919Sxy150489 #define	M88E1000_CABLE_LENGTH_TABLE_SIZE \
444919Sxy150489 	(sizeof (e1000_m88_cable_length_table) / \
454919Sxy150489 	sizeof (e1000_m88_cable_length_table[0]))
464919Sxy150489 
474919Sxy150489 static const u16 e1000_igp_2_cable_length_table[] =
484919Sxy150489 	{0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
494919Sxy150489 	0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41,
504919Sxy150489 	6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61,
514919Sxy150489 	21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82,
524919Sxy150489 	40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104,
534919Sxy150489 	60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121,
544919Sxy150489 	83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
554919Sxy150489 	104, 109, 114, 118, 121, 124};
564919Sxy150489 
574919Sxy150489 #define	IGP02E1000_CABLE_LENGTH_TABLE_SIZE \
584919Sxy150489 	(sizeof (e1000_igp_2_cable_length_table) / \
594919Sxy150489 	sizeof (e1000_igp_2_cable_length_table[0]))
604919Sxy150489 
614919Sxy150489 /*
626735Scc210113  * e1000_init_phy_ops_generic - Initialize PHY function pointers
636735Scc210113  * @hw: pointer to the HW structure
646735Scc210113  *
656735Scc210113  * Setups up the function pointers to no-op functions
666735Scc210113  */
676735Scc210113 void
e1000_init_phy_ops_generic(struct e1000_hw * hw)686735Scc210113 e1000_init_phy_ops_generic(struct e1000_hw *hw)
696735Scc210113 {
706735Scc210113 	struct e1000_phy_info *phy = &hw->phy;
716735Scc210113 	DEBUGFUNC("e1000_init_phy_ops_generic");
726735Scc210113 
736735Scc210113 	/* Initialize function pointers */
746735Scc210113 	phy->ops.init_params = e1000_null_ops_generic;
756735Scc210113 	phy->ops.acquire = e1000_null_ops_generic;
766735Scc210113 	phy->ops.check_polarity = e1000_null_ops_generic;
776735Scc210113 	phy->ops.check_reset_block = e1000_null_ops_generic;
786735Scc210113 	phy->ops.commit = e1000_null_ops_generic;
796735Scc210113 	phy->ops.force_speed_duplex = e1000_null_ops_generic;
806735Scc210113 	phy->ops.get_cfg_done = e1000_null_ops_generic;
816735Scc210113 	phy->ops.get_cable_length = e1000_null_ops_generic;
826735Scc210113 	phy->ops.get_info = e1000_null_ops_generic;
836735Scc210113 	phy->ops.read_reg = e1000_null_read_reg;
8411020SMin.Xu@Sun.COM 	phy->ops.read_reg_locked = e1000_null_read_reg;
856735Scc210113 	phy->ops.release = e1000_null_phy_generic;
866735Scc210113 	phy->ops.reset = e1000_null_ops_generic;
876735Scc210113 	phy->ops.set_d0_lplu_state = e1000_null_lplu_state;
886735Scc210113 	phy->ops.set_d3_lplu_state = e1000_null_lplu_state;
896735Scc210113 	phy->ops.write_reg = e1000_null_write_reg;
9011020SMin.Xu@Sun.COM 	phy->ops.write_reg_locked = e1000_null_write_reg;
916735Scc210113 	phy->ops.power_up = e1000_null_phy_generic;
926735Scc210113 	phy->ops.power_down = e1000_null_phy_generic;
938479SChenlu.Chen@Sun.COM 	phy->ops.cfg_on_link_up = e1000_null_ops_generic;
946735Scc210113 }
956735Scc210113 
966735Scc210113 /*
976735Scc210113  * e1000_null_read_reg - No-op function, return 0
986735Scc210113  * @hw: pointer to the HW structure
996735Scc210113  */
1006735Scc210113 s32
e1000_null_read_reg(struct e1000_hw * hw,u32 offset,u16 * data)1016735Scc210113 e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data)
1026735Scc210113 {
1036735Scc210113 	DEBUGFUNC("e1000_null_read_reg");
1046735Scc210113 	UNREFERENCED_3PARAMETER(hw, offset, data);
1056735Scc210113 	return (E1000_SUCCESS);
1066735Scc210113 }
1076735Scc210113 
1086735Scc210113 /*
1096735Scc210113  * e1000_null_phy_generic - No-op function, return void
1106735Scc210113  * @hw: pointer to the HW structure
1116735Scc210113  */
1126735Scc210113 void
e1000_null_phy_generic(struct e1000_hw * hw)1136735Scc210113 e1000_null_phy_generic(struct e1000_hw *hw)
1146735Scc210113 {
1156735Scc210113 	DEBUGFUNC("e1000_null_phy_generic");
1166735Scc210113 	UNREFERENCED_1PARAMETER(hw);
1176735Scc210113 }
1186735Scc210113 
1196735Scc210113 /*
1206735Scc210113  * e1000_null_lplu_state - No-op function, return 0
1216735Scc210113  * @hw: pointer to the HW structure
1226735Scc210113  */
1236735Scc210113 s32
e1000_null_lplu_state(struct e1000_hw * hw,bool active)1246735Scc210113 e1000_null_lplu_state(struct e1000_hw *hw, bool active)
1256735Scc210113 {
1266735Scc210113 	DEBUGFUNC("e1000_null_lplu_state");
1276735Scc210113 	UNREFERENCED_2PARAMETER(hw, active);
1286735Scc210113 	return (E1000_SUCCESS);
1296735Scc210113 }
1306735Scc210113 
1316735Scc210113 /*
1326735Scc210113  * e1000_null_write_reg - No-op function, return 0
1336735Scc210113  * @hw: pointer to the HW structure
1346735Scc210113  */
1356735Scc210113 s32
e1000_null_write_reg(struct e1000_hw * hw,u32 offset,u16 data)1366735Scc210113 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data)
1376735Scc210113 {
1386735Scc210113 	DEBUGFUNC("e1000_null_write_reg");
1396735Scc210113 	UNREFERENCED_3PARAMETER(hw, offset, data);
1406735Scc210113 	return (E1000_SUCCESS);
1416735Scc210113 }
1426735Scc210113 
1436735Scc210113 /*
1444919Sxy150489  * e1000_check_reset_block_generic - Check if PHY reset is blocked
1454919Sxy150489  * @hw: pointer to the HW structure
1464919Sxy150489  *
1474919Sxy150489  * Read the PHY management control register and check whether a PHY reset
1484919Sxy150489  * is blocked.  If a reset is not blocked return E1000_SUCCESS, otherwise
1494919Sxy150489  * return E1000_BLK_PHY_RESET (12).
1504919Sxy150489  */
1514919Sxy150489 s32
e1000_check_reset_block_generic(struct e1000_hw * hw)1524919Sxy150489 e1000_check_reset_block_generic(struct e1000_hw *hw)
1534919Sxy150489 {
1544919Sxy150489 	u32 manc;
1554919Sxy150489 
1564919Sxy150489 	DEBUGFUNC("e1000_check_reset_block");
1574919Sxy150489 
1584919Sxy150489 	manc = E1000_READ_REG(hw, E1000_MANC);
1594919Sxy150489 
1604919Sxy150489 	return ((manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
1614919Sxy150489 	    E1000_BLK_PHY_RESET : E1000_SUCCESS);
1624919Sxy150489 }
1634919Sxy150489 
1644919Sxy150489 /*
1654919Sxy150489  * e1000_get_phy_id - Retrieve the PHY ID and revision
1664919Sxy150489  * @hw: pointer to the HW structure
1674919Sxy150489  *
1684919Sxy150489  * Reads the PHY registers and stores the PHY ID and possibly the PHY
1694919Sxy150489  * revision in the hardware structure.
1704919Sxy150489  */
1714919Sxy150489 s32
e1000_get_phy_id(struct e1000_hw * hw)1724919Sxy150489 e1000_get_phy_id(struct e1000_hw *hw)
1734919Sxy150489 {
1744919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
1754919Sxy150489 	s32 ret_val = E1000_SUCCESS;
1764919Sxy150489 	u16 phy_id;
17710680SMin.Xu@Sun.COM 	u16 retry_count = 0;
1784919Sxy150489 
1794919Sxy150489 	DEBUGFUNC("e1000_get_phy_id");
1804919Sxy150489 
1816735Scc210113 	if (!(phy->ops.read_reg))
1826735Scc210113 		goto out;
1836735Scc210113 
18410680SMin.Xu@Sun.COM 	while (retry_count < 2) {
18510680SMin.Xu@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
18610680SMin.Xu@Sun.COM 		if (ret_val)
18710680SMin.Xu@Sun.COM 			goto out;
18810680SMin.Xu@Sun.COM 
18910680SMin.Xu@Sun.COM 		phy->id = (u32)(phy_id << 16);
19010680SMin.Xu@Sun.COM 		usec_delay(20);
19110680SMin.Xu@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
19210680SMin.Xu@Sun.COM 		if (ret_val)
19310680SMin.Xu@Sun.COM 			goto out;
19410680SMin.Xu@Sun.COM 
19510680SMin.Xu@Sun.COM 		phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
19610680SMin.Xu@Sun.COM 		phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
19710680SMin.Xu@Sun.COM 
19810680SMin.Xu@Sun.COM 		if (phy->id != 0 && phy->id != PHY_REVISION_MASK)
19910680SMin.Xu@Sun.COM 			goto out;
20010680SMin.Xu@Sun.COM 
20110680SMin.Xu@Sun.COM 		/*
20211020SMin.Xu@Sun.COM 		 * If the PHY ID is still unknown, we may have an 82577
20311020SMin.Xu@Sun.COM 		 * without link.  We will try again after setting Slow MDIC
20411020SMin.Xu@Sun.COM 		 * mode. No harm in trying again in this case since the PHY
20511020SMin.Xu@Sun.COM 		 * ID is unknown at this point anyway.
20610680SMin.Xu@Sun.COM 		 */
20711020SMin.Xu@Sun.COM 		ret_val = phy->ops.acquire(hw);
20811020SMin.Xu@Sun.COM 		if (ret_val)
20911020SMin.Xu@Sun.COM 			goto out;
21010680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
21110680SMin.Xu@Sun.COM 		if (ret_val)
21210680SMin.Xu@Sun.COM 			goto out;
21311020SMin.Xu@Sun.COM 		phy->ops.release(hw);
21410680SMin.Xu@Sun.COM 
21510680SMin.Xu@Sun.COM 		retry_count++;
21610680SMin.Xu@Sun.COM 	}
2174919Sxy150489 out:
21810680SMin.Xu@Sun.COM 	/* Revert to MDIO fast mode, if applicable */
21911020SMin.Xu@Sun.COM 	if (retry_count) {
22011020SMin.Xu@Sun.COM 		ret_val = phy->ops.acquire(hw);
22111020SMin.Xu@Sun.COM 		if (ret_val)
22211020SMin.Xu@Sun.COM 			return (ret_val);
22310680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
22411020SMin.Xu@Sun.COM 		phy->ops.release(hw);
22511020SMin.Xu@Sun.COM 	}
22610680SMin.Xu@Sun.COM 
2274919Sxy150489 	return (ret_val);
2284919Sxy150489 }
2294919Sxy150489 
2304919Sxy150489 /*
2314919Sxy150489  * e1000_phy_reset_dsp_generic - Reset PHY DSP
2324919Sxy150489  * @hw: pointer to the HW structure
2334919Sxy150489  *
2344919Sxy150489  * Reset the digital signal processor.
2354919Sxy150489  */
2364919Sxy150489 s32
e1000_phy_reset_dsp_generic(struct e1000_hw * hw)2374919Sxy150489 e1000_phy_reset_dsp_generic(struct e1000_hw *hw)
2384919Sxy150489 {
2396735Scc210113 	s32 ret_val = E1000_SUCCESS;
2404919Sxy150489 
2414919Sxy150489 	DEBUGFUNC("e1000_phy_reset_dsp_generic");
2424919Sxy150489 
2436735Scc210113 	if (!(hw->phy.ops.write_reg))
2446735Scc210113 		goto out;
2456735Scc210113 
2466735Scc210113 	ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1);
2474919Sxy150489 	if (ret_val)
2484919Sxy150489 		goto out;
2494919Sxy150489 
2506735Scc210113 	ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0);
2514919Sxy150489 
2524919Sxy150489 out:
2534919Sxy150489 	return (ret_val);
2544919Sxy150489 }
2554919Sxy150489 
2564919Sxy150489 /*
2574919Sxy150489  * e1000_read_phy_reg_mdic - Read MDI control register
2584919Sxy150489  * @hw: pointer to the HW structure
2594919Sxy150489  * @offset: register offset to be read
2604919Sxy150489  * @data: pointer to the read data
2614919Sxy150489  *
2626735Scc210113  * Reads the MDI control register in the PHY at offset and stores the
2634919Sxy150489  * information read to data.
2644919Sxy150489  */
2656735Scc210113 s32
e1000_read_phy_reg_mdic(struct e1000_hw * hw,u32 offset,u16 * data)2664919Sxy150489 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
2674919Sxy150489 {
2684919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
2694919Sxy150489 	u32 i, mdic = 0;
2704919Sxy150489 	s32 ret_val = E1000_SUCCESS;
2714919Sxy150489 
2724919Sxy150489 	DEBUGFUNC("e1000_read_phy_reg_mdic");
2734919Sxy150489 
2744919Sxy150489 	/*
2754919Sxy150489 	 * Set up Op-code, Phy Address, and register offset in the MDI Control
2764919Sxy150489 	 * register.  The MAC will take care of interfacing with the PHY to
2774919Sxy150489 	 * retrieve the desired data.
2784919Sxy150489 	 */
2794919Sxy150489 	mdic = ((offset << E1000_MDIC_REG_SHIFT) |
2804919Sxy150489 	    (phy->addr << E1000_MDIC_PHY_SHIFT) |
2814919Sxy150489 	    (E1000_MDIC_OP_READ));
2824919Sxy150489 
2834919Sxy150489 	E1000_WRITE_REG(hw, E1000_MDIC, mdic);
2844919Sxy150489 
2856735Scc210113 	/*
2866735Scc210113 	 * Poll the ready bit to see if the MDI read completed
2876735Scc210113 	 * Increasing the time out as testing showed failures with
2886735Scc210113 	 * the lower time out
2896735Scc210113 	 */
2906735Scc210113 	for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
2914919Sxy150489 		usec_delay(50);
2924919Sxy150489 		mdic = E1000_READ_REG(hw, E1000_MDIC);
2934919Sxy150489 		if (mdic & E1000_MDIC_READY)
2944919Sxy150489 			break;
2954919Sxy150489 	}
2964919Sxy150489 	if (!(mdic & E1000_MDIC_READY)) {
2974919Sxy150489 		DEBUGOUT("MDI Read did not complete\n");
2984919Sxy150489 		ret_val = -E1000_ERR_PHY;
2994919Sxy150489 		goto out;
3004919Sxy150489 	}
3014919Sxy150489 	if (mdic & E1000_MDIC_ERROR) {
3024919Sxy150489 		DEBUGOUT("MDI Error\n");
3034919Sxy150489 		ret_val = -E1000_ERR_PHY;
3044919Sxy150489 		goto out;
3054919Sxy150489 	}
3064919Sxy150489 	*data = (u16)mdic;
3074919Sxy150489 
3084919Sxy150489 out:
3094919Sxy150489 	return (ret_val);
3104919Sxy150489 }
3114919Sxy150489 
3124919Sxy150489 /*
3134919Sxy150489  * e1000_write_phy_reg_mdic - Write MDI control register
3144919Sxy150489  * @hw: pointer to the HW structure
3154919Sxy150489  * @offset: register offset to write to
3164919Sxy150489  * @data: data to write to register at offset
3174919Sxy150489  *
3184919Sxy150489  * Writes data to MDI control register in the PHY at offset.
3194919Sxy150489  */
3206735Scc210113 s32
e1000_write_phy_reg_mdic(struct e1000_hw * hw,u32 offset,u16 data)3214919Sxy150489 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
3224919Sxy150489 {
3234919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
3244919Sxy150489 	u32 i, mdic = 0;
3254919Sxy150489 	s32 ret_val = E1000_SUCCESS;
3264919Sxy150489 
3274919Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_mdic");
3284919Sxy150489 
3294919Sxy150489 	/*
3304919Sxy150489 	 * Set up Op-code, Phy Address, and register offset in the MDI Control
3314919Sxy150489 	 * register.  The MAC will take care of interfacing with the PHY to
3324919Sxy150489 	 * retrieve the desired data.
3334919Sxy150489 	 */
3344919Sxy150489 	mdic = (((u32)data) |
3354919Sxy150489 	    (offset << E1000_MDIC_REG_SHIFT) |
3364919Sxy150489 	    (phy->addr << E1000_MDIC_PHY_SHIFT) |
3374919Sxy150489 	    (E1000_MDIC_OP_WRITE));
3384919Sxy150489 
3394919Sxy150489 	E1000_WRITE_REG(hw, E1000_MDIC, mdic);
3404919Sxy150489 
3416735Scc210113 	/*
3426735Scc210113 	 * Poll the ready bit to see if the MDI read completed
3436735Scc210113 	 * Increasing the time out as testing showed failures with
3446735Scc210113 	 * the lower time out
3456735Scc210113 	 */
3466735Scc210113 	for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
3476735Scc210113 		usec_delay(50);
3484919Sxy150489 		mdic = E1000_READ_REG(hw, E1000_MDIC);
3494919Sxy150489 		if (mdic & E1000_MDIC_READY)
3504919Sxy150489 			break;
3514919Sxy150489 	}
3524919Sxy150489 	if (!(mdic & E1000_MDIC_READY)) {
3534919Sxy150489 		DEBUGOUT("MDI Write did not complete\n");
3544919Sxy150489 		ret_val = -E1000_ERR_PHY;
3554919Sxy150489 		goto out;
3564919Sxy150489 	}
3576735Scc210113 	if (mdic & E1000_MDIC_ERROR) {
3586735Scc210113 		DEBUGOUT("MDI Error\n");
3596735Scc210113 		ret_val = -E1000_ERR_PHY;
3606735Scc210113 		goto out;
3616735Scc210113 	}
3624919Sxy150489 
3634919Sxy150489 out:
3644919Sxy150489 	return (ret_val);
3654919Sxy150489 }
3664919Sxy150489 
3674919Sxy150489 /*
3684919Sxy150489  * e1000_read_phy_reg_m88 - Read m88 PHY register
3694919Sxy150489  * @hw: pointer to the HW structure
3704919Sxy150489  * @offset: register offset to be read
3714919Sxy150489  * @data: pointer to the read data
3724919Sxy150489  *
3734919Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
3744919Sxy150489  * and storing the retrieved information in data.  Release any acquired
3754919Sxy150489  * semaphores before exiting.
3764919Sxy150489  */
3774919Sxy150489 s32
e1000_read_phy_reg_m88(struct e1000_hw * hw,u32 offset,u16 * data)3784919Sxy150489 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data)
3794919Sxy150489 {
3806735Scc210113 	s32 ret_val = E1000_SUCCESS;
3814919Sxy150489 
3824919Sxy150489 	DEBUGFUNC("e1000_read_phy_reg_m88");
3834919Sxy150489 
3846735Scc210113 	if (!(hw->phy.ops.acquire))
3856735Scc210113 		goto out;
3866735Scc210113 
3876735Scc210113 	ret_val = hw->phy.ops.acquire(hw);
3884919Sxy150489 	if (ret_val)
3894919Sxy150489 		goto out;
3904919Sxy150489 
3914919Sxy150489 	ret_val = e1000_read_phy_reg_mdic(hw,
3924919Sxy150489 	    MAX_PHY_REG_ADDRESS & offset,
3934919Sxy150489 	    data);
3944919Sxy150489 
3956735Scc210113 	hw->phy.ops.release(hw);
3964919Sxy150489 
3974919Sxy150489 out:
3984919Sxy150489 	return (ret_val);
3994919Sxy150489 }
4004919Sxy150489 
4014919Sxy150489 /*
4024919Sxy150489  * e1000_write_phy_reg_m88 - Write m88 PHY register
4034919Sxy150489  * @hw: pointer to the HW structure
4044919Sxy150489  * @offset: register offset to write to
4054919Sxy150489  * @data: data to write at register offset
4064919Sxy150489  *
4074919Sxy150489  * Acquires semaphore, if necessary, then writes the data to PHY register
4084919Sxy150489  * at the offset.  Release any acquired semaphores before exiting.
4094919Sxy150489  */
4104919Sxy150489 s32
e1000_write_phy_reg_m88(struct e1000_hw * hw,u32 offset,u16 data)4114919Sxy150489 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data)
4124919Sxy150489 {
4136735Scc210113 	s32 ret_val = E1000_SUCCESS;
4144919Sxy150489 
4154919Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_m88");
4164919Sxy150489 
4176735Scc210113 	if (!(hw->phy.ops.acquire))
4186735Scc210113 		goto out;
4196735Scc210113 
4206735Scc210113 	ret_val = hw->phy.ops.acquire(hw);
4214919Sxy150489 	if (ret_val)
4224919Sxy150489 		goto out;
4234919Sxy150489 
4244919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw,
4254919Sxy150489 	    MAX_PHY_REG_ADDRESS & offset,
4264919Sxy150489 	    data);
4274919Sxy150489 
4286735Scc210113 	hw->phy.ops.release(hw);
4294919Sxy150489 
4304919Sxy150489 out:
4314919Sxy150489 	return (ret_val);
4324919Sxy150489 }
4334919Sxy150489 
4344919Sxy150489 /*
4354919Sxy150489  * e1000_read_phy_reg_igp - Read igp PHY register
4364919Sxy150489  * @hw: pointer to the HW structure
4374919Sxy150489  * @offset: register offset to be read
4384919Sxy150489  * @data: pointer to the read data
43911020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
4404919Sxy150489  *
4414919Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
44211020SMin.Xu@Sun.COM  * and stores the retrieved information in data.  Release any acquired
4434919Sxy150489  * semaphores before exiting.
4444919Sxy150489  */
44511020SMin.Xu@Sun.COM static s32
__e1000_read_phy_reg_igp(struct e1000_hw * hw,u32 offset,u16 * data,bool locked)44611020SMin.Xu@Sun.COM __e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
44711020SMin.Xu@Sun.COM     bool locked)
44811020SMin.Xu@Sun.COM {
44911020SMin.Xu@Sun.COM 	s32 ret_val = E1000_SUCCESS;
45011020SMin.Xu@Sun.COM 
45111020SMin.Xu@Sun.COM 	DEBUGFUNC("__e1000_read_phy_reg_igp");
45211020SMin.Xu@Sun.COM 
45311020SMin.Xu@Sun.COM 	if (!locked) {
45411020SMin.Xu@Sun.COM 		if (!(hw->phy.ops.acquire))
45511020SMin.Xu@Sun.COM 			goto out;
45611020SMin.Xu@Sun.COM 
45711020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
45811020SMin.Xu@Sun.COM 		if (ret_val)
45911020SMin.Xu@Sun.COM 			goto out;
46011020SMin.Xu@Sun.COM 	}
46111020SMin.Xu@Sun.COM 
46211020SMin.Xu@Sun.COM 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
46311020SMin.Xu@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw,
46411020SMin.Xu@Sun.COM 		    IGP01E1000_PHY_PAGE_SELECT, (u16)offset);
46511020SMin.Xu@Sun.COM 		if (ret_val)
46611020SMin.Xu@Sun.COM 			goto release;
46711020SMin.Xu@Sun.COM 	}
46811020SMin.Xu@Sun.COM 
46911020SMin.Xu@Sun.COM 	ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
47011020SMin.Xu@Sun.COM 	    data);
47111020SMin.Xu@Sun.COM 
47211020SMin.Xu@Sun.COM release:
47311020SMin.Xu@Sun.COM 	if (!locked)
47411020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
47511020SMin.Xu@Sun.COM out:
47611020SMin.Xu@Sun.COM 	return (ret_val);
47711020SMin.Xu@Sun.COM }
47811020SMin.Xu@Sun.COM 
47911020SMin.Xu@Sun.COM /*
48011020SMin.Xu@Sun.COM  * e1000_read_phy_reg_igp - Read igp PHY register
48111020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
48211020SMin.Xu@Sun.COM  * @offset: register offset to be read
48311020SMin.Xu@Sun.COM  * @data: pointer to the read data
48411020SMin.Xu@Sun.COM  *
48511020SMin.Xu@Sun.COM  * Acquires semaphore then reads the PHY register at offset and stores the
48611020SMin.Xu@Sun.COM  * retrieved information in data.
48711020SMin.Xu@Sun.COM  * Release the acquired semaphore before exiting.
48811020SMin.Xu@Sun.COM  */
4894919Sxy150489 s32
e1000_read_phy_reg_igp(struct e1000_hw * hw,u32 offset,u16 * data)4904919Sxy150489 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
4914919Sxy150489 {
49211020SMin.Xu@Sun.COM 	return (__e1000_read_phy_reg_igp(hw, offset, data, false));
49311020SMin.Xu@Sun.COM }
49411020SMin.Xu@Sun.COM 
49511020SMin.Xu@Sun.COM /*
49611020SMin.Xu@Sun.COM  * e1000_read_phy_reg_igp_locked - Read igp PHY register
49711020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
49811020SMin.Xu@Sun.COM  * @offset: register offset to be read
49911020SMin.Xu@Sun.COM  * @data: pointer to the read data
50011020SMin.Xu@Sun.COM  *
50111020SMin.Xu@Sun.COM  * Reads the PHY register at offset and stores the retrieved information
50211020SMin.Xu@Sun.COM  * in data.  Assumes semaphore already acquired.
50311020SMin.Xu@Sun.COM  */
50411020SMin.Xu@Sun.COM s32
e1000_read_phy_reg_igp_locked(struct e1000_hw * hw,u32 offset,u16 * data)50511020SMin.Xu@Sun.COM e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data)
50611020SMin.Xu@Sun.COM {
50711020SMin.Xu@Sun.COM 	return (__e1000_read_phy_reg_igp(hw, offset, data, true));
50811020SMin.Xu@Sun.COM }
50911020SMin.Xu@Sun.COM 
51011020SMin.Xu@Sun.COM /*
51111020SMin.Xu@Sun.COM  * e1000_write_phy_reg_igp - Write igp PHY register
51211020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
51311020SMin.Xu@Sun.COM  * @offset: register offset to write to
51411020SMin.Xu@Sun.COM  * @data: data to write at register offset
51511020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
51611020SMin.Xu@Sun.COM  *
51711020SMin.Xu@Sun.COM  * Acquires semaphore, if necessary, then writes the data to PHY register
51811020SMin.Xu@Sun.COM  * at the offset.  Release any acquired semaphores before exiting.
51911020SMin.Xu@Sun.COM  */
52011020SMin.Xu@Sun.COM static s32
__e1000_write_phy_reg_igp(struct e1000_hw * hw,u32 offset,u16 data,bool locked)52111020SMin.Xu@Sun.COM __e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
52211020SMin.Xu@Sun.COM     bool locked)
52311020SMin.Xu@Sun.COM {
5246735Scc210113 	s32 ret_val = E1000_SUCCESS;
5254919Sxy150489 
52611020SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_write_phy_reg_igp");
52711020SMin.Xu@Sun.COM 
52811020SMin.Xu@Sun.COM 	if (!locked) {
52911020SMin.Xu@Sun.COM 		if (!(hw->phy.ops.acquire))
53011020SMin.Xu@Sun.COM 			goto out;
53111020SMin.Xu@Sun.COM 
53211020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
53311020SMin.Xu@Sun.COM 		if (ret_val)
53411020SMin.Xu@Sun.COM 			goto out;
53511020SMin.Xu@Sun.COM 	}
5364919Sxy150489 
5374919Sxy150489 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
5384919Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw,
53911020SMin.Xu@Sun.COM 		    IGP01E1000_PHY_PAGE_SELECT, (u16)offset);
54011020SMin.Xu@Sun.COM 		if (ret_val)
54111020SMin.Xu@Sun.COM 			goto release;
5424919Sxy150489 	}
54311020SMin.Xu@Sun.COM 
54411020SMin.Xu@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
5454919Sxy150489 	    data);
5464919Sxy150489 
54711020SMin.Xu@Sun.COM release:
54811020SMin.Xu@Sun.COM 	if (!locked)
54911020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
5504919Sxy150489 
5514919Sxy150489 out:
5524919Sxy150489 	return (ret_val);
5534919Sxy150489 }
5544919Sxy150489 
5554919Sxy150489 /*
5564919Sxy150489  * e1000_write_phy_reg_igp - Write igp PHY register
5574919Sxy150489  * @hw: pointer to the HW structure
5584919Sxy150489  * @offset: register offset to write to
5594919Sxy150489  * @data: data to write at register offset
5604919Sxy150489  *
56111020SMin.Xu@Sun.COM  * Acquires semaphore then writes the data to PHY register
5624919Sxy150489  * at the offset.  Release any acquired semaphores before exiting.
5634919Sxy150489  */
5644919Sxy150489 s32
e1000_write_phy_reg_igp(struct e1000_hw * hw,u32 offset,u16 data)5654919Sxy150489 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
5664919Sxy150489 {
56711020SMin.Xu@Sun.COM 	return (__e1000_write_phy_reg_igp(hw, offset, data, false));
56811020SMin.Xu@Sun.COM }
56911020SMin.Xu@Sun.COM 
57011020SMin.Xu@Sun.COM /*
57111020SMin.Xu@Sun.COM  * e1000_write_phy_reg_igp_locked - Write igp PHY register
57211020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
57311020SMin.Xu@Sun.COM  * @offset: register offset to write to
57411020SMin.Xu@Sun.COM  * @data: data to write at register offset
57511020SMin.Xu@Sun.COM  *
57611020SMin.Xu@Sun.COM  * Writes the data to PHY register at the offset.
57711020SMin.Xu@Sun.COM  * Assumes semaphore already acquired.
57811020SMin.Xu@Sun.COM  */
57911020SMin.Xu@Sun.COM s32
e1000_write_phy_reg_igp_locked(struct e1000_hw * hw,u32 offset,u16 data)58011020SMin.Xu@Sun.COM e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data)
58111020SMin.Xu@Sun.COM {
58211020SMin.Xu@Sun.COM 	return (__e1000_write_phy_reg_igp(hw, offset, data, true));
5834919Sxy150489 }
5844919Sxy150489 
5854919Sxy150489 /*
58611020SMin.Xu@Sun.COM  * __e1000_read_kmrn_reg - Read kumeran register
5874919Sxy150489  * @hw: pointer to the HW structure
5884919Sxy150489  * @offset: register offset to be read
5894919Sxy150489  * @data: pointer to the read data
59011020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
5914919Sxy150489  *
59211020SMin.Xu@Sun.COM  * Acquires semaphore, if necessary. Then reads the PHY register at offset
59311020SMin.Xu@Sun.COM  * using the kumeran interface. The information retrieved is stored in data.
5944919Sxy150489  * Release any acquired semaphores before exiting.
5954919Sxy150489  */
59611020SMin.Xu@Sun.COM static s32
__e1000_read_kmrn_reg(struct e1000_hw * hw,u32 offset,u16 * data,bool locked)59711020SMin.Xu@Sun.COM __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, bool locked)
5984919Sxy150489 {
5994919Sxy150489 	u32 kmrnctrlsta;
6006735Scc210113 	s32 ret_val = E1000_SUCCESS;
6014919Sxy150489 
60211020SMin.Xu@Sun.COM 	DEBUGFUNC("__e1000_read_kmrn_reg");
60311020SMin.Xu@Sun.COM 
60411020SMin.Xu@Sun.COM 	if (!locked) {
60511020SMin.Xu@Sun.COM 		if (!(hw->phy.ops.acquire))
60611020SMin.Xu@Sun.COM 			goto out;
60711020SMin.Xu@Sun.COM 
60811020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
60911020SMin.Xu@Sun.COM 		if (ret_val)
61011020SMin.Xu@Sun.COM 			goto out;
61111020SMin.Xu@Sun.COM 	}
6124919Sxy150489 
6134919Sxy150489 	kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
6144919Sxy150489 	    E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
6154919Sxy150489 	E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
6164919Sxy150489 
6174919Sxy150489 	usec_delay(2);
6184919Sxy150489 
6194919Sxy150489 	kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA);
6204919Sxy150489 	*data = (u16)kmrnctrlsta;
6214919Sxy150489 
62211020SMin.Xu@Sun.COM 	if (!locked)
62311020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
6244919Sxy150489 
6254919Sxy150489 out:
6264919Sxy150489 	return (ret_val);
6274919Sxy150489 }
6284919Sxy150489 
6294919Sxy150489 /*
63011020SMin.Xu@Sun.COM  * e1000_read_kmrn_reg_generic -  Read kumeran register
63111020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
63211020SMin.Xu@Sun.COM  * @offset: register offset to be read
63311020SMin.Xu@Sun.COM  * @data: pointer to the read data
63411020SMin.Xu@Sun.COM  *
63511020SMin.Xu@Sun.COM  * Acquires semaphore then reads the PHY register at offset using the
63611020SMin.Xu@Sun.COM  * kumeran interface.  The information retrieved is stored in data.
63711020SMin.Xu@Sun.COM  * Release the acquired semaphore before exiting.
63811020SMin.Xu@Sun.COM  */
63911020SMin.Xu@Sun.COM s32
e1000_read_kmrn_reg_generic(struct e1000_hw * hw,u32 offset,u16 * data)64011020SMin.Xu@Sun.COM e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data)
64111020SMin.Xu@Sun.COM {
64211020SMin.Xu@Sun.COM 	return (__e1000_read_kmrn_reg(hw, offset, data, false));
64311020SMin.Xu@Sun.COM }
64411020SMin.Xu@Sun.COM 
64511020SMin.Xu@Sun.COM /*
64611020SMin.Xu@Sun.COM  * e1000_read_kmrn_reg_locked -  Read kumeran register
64711020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
64811020SMin.Xu@Sun.COM  * @offset: register offset to be read
64911020SMin.Xu@Sun.COM  * @data: pointer to the read data
65011020SMin.Xu@Sun.COM  *
65111020SMin.Xu@Sun.COM  * Reads the PHY register at offset using the kumeran interface.  The
65211020SMin.Xu@Sun.COM  * information retrieved is stored in data.
65311020SMin.Xu@Sun.COM  * Assumes semaphore already acquired.
65411020SMin.Xu@Sun.COM  */
65511020SMin.Xu@Sun.COM s32
e1000_read_kmrn_reg_locked(struct e1000_hw * hw,u32 offset,u16 * data)65611020SMin.Xu@Sun.COM e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
65711020SMin.Xu@Sun.COM {
65811020SMin.Xu@Sun.COM 	return (__e1000_read_kmrn_reg(hw, offset, data, true));
65911020SMin.Xu@Sun.COM }
66011020SMin.Xu@Sun.COM 
66111020SMin.Xu@Sun.COM /*
66211020SMin.Xu@Sun.COM  * __e1000_write_kmrn_reg - Write kumeran register
6634919Sxy150489  * @hw: pointer to the HW structure
6644919Sxy150489  * @offset: register offset to write to
6654919Sxy150489  * @data: data to write at register offset
66611020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
6674919Sxy150489  *
6684919Sxy150489  * Acquires semaphore, if necessary.  Then write the data to PHY register
6694919Sxy150489  * at the offset using the kumeran interface.  Release any acquired semaphores
6704919Sxy150489  * before exiting.
6714919Sxy150489  */
67211020SMin.Xu@Sun.COM static s32
__e1000_write_kmrn_reg(struct e1000_hw * hw,u32 offset,u16 data,bool locked)67311020SMin.Xu@Sun.COM __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, bool locked)
6744919Sxy150489 {
6754919Sxy150489 	u32 kmrnctrlsta;
6766735Scc210113 	s32 ret_val = E1000_SUCCESS;
6774919Sxy150489 
6784919Sxy150489 	DEBUGFUNC("e1000_write_kmrn_reg_generic");
6794919Sxy150489 
68011020SMin.Xu@Sun.COM 	if (!locked) {
68111020SMin.Xu@Sun.COM 		if (!(hw->phy.ops.acquire))
68211020SMin.Xu@Sun.COM 			goto out;
68311020SMin.Xu@Sun.COM 
68411020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
68511020SMin.Xu@Sun.COM 		if (ret_val)
68611020SMin.Xu@Sun.COM 			goto out;
68711020SMin.Xu@Sun.COM 	}
6884919Sxy150489 
6894919Sxy150489 	kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
6904919Sxy150489 	    E1000_KMRNCTRLSTA_OFFSET) | data;
6914919Sxy150489 	E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
6924919Sxy150489 
6934919Sxy150489 	usec_delay(2);
69411020SMin.Xu@Sun.COM 
69511020SMin.Xu@Sun.COM 	if (!locked)
69611020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
6974919Sxy150489 
6984919Sxy150489 out:
6994919Sxy150489 	return (ret_val);
7004919Sxy150489 }
7014919Sxy150489 
7024919Sxy150489 /*
70311020SMin.Xu@Sun.COM  * e1000_write_kmrn_reg_generic -  Write kumeran register
70411020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
70511020SMin.Xu@Sun.COM  * @offset: register offset to write to
70611020SMin.Xu@Sun.COM  * @data: data to write at register offset
70711020SMin.Xu@Sun.COM  *
70811020SMin.Xu@Sun.COM  * Acquires semaphore then writes the data to the PHY register at the offset
70911020SMin.Xu@Sun.COM  * using the kumeran interface.  Release the acquired semaphore before exiting.
71011020SMin.Xu@Sun.COM  */
71111020SMin.Xu@Sun.COM s32
e1000_write_kmrn_reg_generic(struct e1000_hw * hw,u32 offset,u16 data)71211020SMin.Xu@Sun.COM e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data)
71311020SMin.Xu@Sun.COM {
71411020SMin.Xu@Sun.COM 	return (__e1000_write_kmrn_reg(hw, offset, data, false));
71511020SMin.Xu@Sun.COM }
71611020SMin.Xu@Sun.COM 
71711020SMin.Xu@Sun.COM /*
71811020SMin.Xu@Sun.COM  * e1000_write_kmrn_reg_locked -  Write kumeran register
71911020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
72011020SMin.Xu@Sun.COM  * @offset: register offset to write to
72111020SMin.Xu@Sun.COM  * @data: data to write at register offset
72211020SMin.Xu@Sun.COM  *
72311020SMin.Xu@Sun.COM  * Write the data to PHY register at the offset using the kumeran interface.
72411020SMin.Xu@Sun.COM  * Assumes semaphore already acquired.
72511020SMin.Xu@Sun.COM  */
72611020SMin.Xu@Sun.COM s32
e1000_write_kmrn_reg_locked(struct e1000_hw * hw,u32 offset,u16 data)72711020SMin.Xu@Sun.COM e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data)
72811020SMin.Xu@Sun.COM {
72911020SMin.Xu@Sun.COM 	return (__e1000_write_kmrn_reg(hw, offset, data, true));
73011020SMin.Xu@Sun.COM }
73111020SMin.Xu@Sun.COM 
73211020SMin.Xu@Sun.COM /*
73310680SMin.Xu@Sun.COM  * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link
73410680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
73510680SMin.Xu@Sun.COM  *
73610680SMin.Xu@Sun.COM  * Sets up Carrier-sense on Transmit and downshift values.
73710680SMin.Xu@Sun.COM  */
73810680SMin.Xu@Sun.COM s32
e1000_copper_link_setup_82577(struct e1000_hw * hw)73910680SMin.Xu@Sun.COM e1000_copper_link_setup_82577(struct e1000_hw *hw)
74010680SMin.Xu@Sun.COM {
74110680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
74210680SMin.Xu@Sun.COM 	s32 ret_val;
74310680SMin.Xu@Sun.COM 	u16 phy_data;
74410680SMin.Xu@Sun.COM 
74510680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_copper_link_setup_82577");
74610680SMin.Xu@Sun.COM 
74710680SMin.Xu@Sun.COM 	if (phy->reset_disable) {
74810680SMin.Xu@Sun.COM 		ret_val = E1000_SUCCESS;
74910680SMin.Xu@Sun.COM 		goto out;
75010680SMin.Xu@Sun.COM 	}
75110680SMin.Xu@Sun.COM 
75210680SMin.Xu@Sun.COM 	/* Enable CRS on TX. This must be set for half-duplex operation. */
75310680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_CFG_REG, &phy_data);
75410680SMin.Xu@Sun.COM 	if (ret_val)
75510680SMin.Xu@Sun.COM 		goto out;
75610680SMin.Xu@Sun.COM 
75710680SMin.Xu@Sun.COM 	phy_data |= I82577_CFG_ASSERT_CRS_ON_TX;
75810680SMin.Xu@Sun.COM 
75910680SMin.Xu@Sun.COM 	/* Enable downshift */
76010680SMin.Xu@Sun.COM 	phy_data |= I82577_CFG_ENABLE_DOWNSHIFT;
76110680SMin.Xu@Sun.COM 
76210680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, I82577_CFG_REG, phy_data);
76310680SMin.Xu@Sun.COM 	if (ret_val)
76410680SMin.Xu@Sun.COM 		goto out;
76510680SMin.Xu@Sun.COM 
76610680SMin.Xu@Sun.COM 	/* Set number of link attempts before downshift */
76710680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_CTRL_REG, &phy_data);
76810680SMin.Xu@Sun.COM 	if (ret_val)
76910680SMin.Xu@Sun.COM 		goto out;
77010680SMin.Xu@Sun.COM 	phy_data &= ~I82577_CTRL_DOWNSHIFT_MASK;
77110680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, I82577_CTRL_REG, phy_data);
77210680SMin.Xu@Sun.COM 
77310680SMin.Xu@Sun.COM out:
77410680SMin.Xu@Sun.COM 	return (ret_val);
77510680SMin.Xu@Sun.COM }
77610680SMin.Xu@Sun.COM 
77710680SMin.Xu@Sun.COM /*
7784919Sxy150489  * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link
7794919Sxy150489  * @hw: pointer to the HW structure
7804919Sxy150489  *
7814919Sxy150489  * Sets up MDI/MDI-X and polarity for m88 PHY's.  If necessary, transmit clock
7824919Sxy150489  * and downshift values are set also.
7834919Sxy150489  */
7844919Sxy150489 s32
e1000_copper_link_setup_m88(struct e1000_hw * hw)7854919Sxy150489 e1000_copper_link_setup_m88(struct e1000_hw *hw)
7864919Sxy150489 {
7874919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
7884919Sxy150489 	s32 ret_val;
7894919Sxy150489 	u16 phy_data;
7904919Sxy150489 
7914919Sxy150489 	DEBUGFUNC("e1000_copper_link_setup_m88");
7924919Sxy150489 
7934919Sxy150489 	if (phy->reset_disable) {
7944919Sxy150489 		ret_val = E1000_SUCCESS;
7954919Sxy150489 		goto out;
7964919Sxy150489 	}
7974919Sxy150489 
7984919Sxy150489 	/* Enable CRS on TX. This must be set for half-duplex operation. */
7996735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
8004919Sxy150489 	if (ret_val)
8014919Sxy150489 		goto out;
8024919Sxy150489 
80310680SMin.Xu@Sun.COM 	/* For BM PHY this bit is downshift enable */
80410680SMin.Xu@Sun.COM 	if (phy->type != e1000_phy_bm)
8054919Sxy150489 		phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
8064919Sxy150489 
8074919Sxy150489 	/*
8084919Sxy150489 	 * Options:
8094919Sxy150489 	 *   MDI/MDI-X = 0 (default)
8104919Sxy150489 	 *   0 - Auto for all speeds
8114919Sxy150489 	 *   1 - MDI mode
8124919Sxy150489 	 *   2 - MDI-X mode
8134919Sxy150489 	 *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
8144919Sxy150489 	 */
8154919Sxy150489 	phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
8164919Sxy150489 
8174919Sxy150489 	switch (phy->mdix) {
8184919Sxy150489 	case 1:
8194919Sxy150489 		phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
8204919Sxy150489 		break;
8214919Sxy150489 	case 2:
8224919Sxy150489 		phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
8234919Sxy150489 		break;
8244919Sxy150489 	case 3:
8254919Sxy150489 		phy_data |= M88E1000_PSCR_AUTO_X_1000T;
8264919Sxy150489 		break;
8274919Sxy150489 	case 0:
8284919Sxy150489 	default:
8294919Sxy150489 		phy_data |= M88E1000_PSCR_AUTO_X_MODE;
8304919Sxy150489 		break;
8314919Sxy150489 	}
8324919Sxy150489 
8334919Sxy150489 	/*
8344919Sxy150489 	 * Options:
8354919Sxy150489 	 *   disable_polarity_correction = 0 (default)
8364919Sxy150489 	 *	Automatic Correction for Reversed Cable Polarity
8374919Sxy150489 	 *   0 - Disabled
8384919Sxy150489 	 *   1 - Enabled
8394919Sxy150489 	 */
8404919Sxy150489 	phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
8414919Sxy150489 	if (phy->disable_polarity_correction == 1)
8424919Sxy150489 		phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
8434919Sxy150489 
8446735Scc210113 	/* Enable downshift on BM (disabled by default) */
8456735Scc210113 	if (phy->type == e1000_phy_bm)
8466735Scc210113 		phy_data |= BME1000_PSCR_ENABLE_DOWNSHIFT;
8476735Scc210113 
8486735Scc210113 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
8494919Sxy150489 	if (ret_val)
8504919Sxy150489 		goto out;
8514919Sxy150489 
8526735Scc210113 	if ((phy->type == e1000_phy_m88) &&
8537607STed.You@Sun.COM 	    (phy->revision < E1000_REVISION_4) &&
8547607STed.You@Sun.COM 	    (phy->id != BME1000_E_PHY_ID_R2)) {
8554919Sxy150489 		/*
8564919Sxy150489 		 * Force TX_CLK in the Extended PHY Specific Control Register
8574919Sxy150489 		 * to 25MHz clock.
8584919Sxy150489 		 */
8596735Scc210113 		ret_val = phy->ops.read_reg(hw,
8604919Sxy150489 		    M88E1000_EXT_PHY_SPEC_CTRL,
8614919Sxy150489 		    &phy_data);
8624919Sxy150489 		if (ret_val)
8634919Sxy150489 			goto out;
8644919Sxy150489 
8654919Sxy150489 		phy_data |= M88E1000_EPSCR_TX_CLK_25;
8664919Sxy150489 
8674919Sxy150489 		if ((phy->revision == E1000_REVISION_2) &&
8684919Sxy150489 		    (phy->id == M88E1111_I_PHY_ID)) {
8694919Sxy150489 			/* 82573L PHY - set the downshift counter to 5x. */
8704919Sxy150489 			phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK;
8714919Sxy150489 			phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X;
8724919Sxy150489 		} else {
8734919Sxy150489 			/* Configure Master and Slave downshift values */
8744919Sxy150489 			phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
8754919Sxy150489 			    M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
8764919Sxy150489 			phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
8774919Sxy150489 			    M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
8784919Sxy150489 		}
8796735Scc210113 		ret_val = phy->ops.write_reg(hw,
8804919Sxy150489 		    M88E1000_EXT_PHY_SPEC_CTRL,
8814919Sxy150489 		    phy_data);
8824919Sxy150489 		if (ret_val)
8834919Sxy150489 			goto out;
8844919Sxy150489 	}
8854919Sxy150489 
8867607STed.You@Sun.COM 	if ((phy->type == e1000_phy_bm) && (phy->id == BME1000_E_PHY_ID_R2)) {
8877607STed.You@Sun.COM 		/* Set PHY page 0, register 29 to 0x0003 */
8887607STed.You@Sun.COM 		ret_val = phy->ops.write_reg(hw, 29, 0x0003);
8897607STed.You@Sun.COM 		if (ret_val)
8907607STed.You@Sun.COM 			goto out;
8917607STed.You@Sun.COM 
8927607STed.You@Sun.COM 		/* Set PHY page 0, register 30 to 0x0000 */
8937607STed.You@Sun.COM 		ret_val = phy->ops.write_reg(hw, 30, 0x0000);
8947607STed.You@Sun.COM 		if (ret_val)
8957607STed.You@Sun.COM 			goto out;
8967607STed.You@Sun.COM 	}
8977607STed.You@Sun.COM 
8984919Sxy150489 	/* Commit the changes. */
8996735Scc210113 	ret_val = phy->ops.commit(hw);
9004919Sxy150489 	if (ret_val) {
9014919Sxy150489 		DEBUGOUT("Error committing the PHY changes\n");
9024919Sxy150489 		goto out;
9034919Sxy150489 	}
9044919Sxy150489 
90510680SMin.Xu@Sun.COM 	if (phy->type == e1000_phy_82578) {
90610680SMin.Xu@Sun.COM 		ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
90710680SMin.Xu@Sun.COM 		    &phy_data);
90810680SMin.Xu@Sun.COM 		if (ret_val)
90910680SMin.Xu@Sun.COM 			goto out;
91010680SMin.Xu@Sun.COM 
91110680SMin.Xu@Sun.COM 		/* 82578 PHY - set the downshift count to 1x. */
91210680SMin.Xu@Sun.COM 		phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE;
91310680SMin.Xu@Sun.COM 		phy_data &= ~I82578_EPSCR_DOWNSHIFT_COUNTER_MASK;
91410680SMin.Xu@Sun.COM 		ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
91510680SMin.Xu@Sun.COM 		    phy_data);
91610680SMin.Xu@Sun.COM 		if (ret_val)
91710680SMin.Xu@Sun.COM 			goto out;
91810680SMin.Xu@Sun.COM }
91910680SMin.Xu@Sun.COM 
9204919Sxy150489 out:
9214919Sxy150489 	return (ret_val);
9224919Sxy150489 }
9234919Sxy150489 
9244919Sxy150489 /*
9254919Sxy150489  * e1000_copper_link_setup_igp - Setup igp PHY's for copper link
9264919Sxy150489  * @hw: pointer to the HW structure
9274919Sxy150489  *
9284919Sxy150489  * Sets up LPLU, MDI/MDI-X, polarity, Smartspeed and Master/Slave config for
9294919Sxy150489  * igp PHY's.
9304919Sxy150489  */
9314919Sxy150489 s32
e1000_copper_link_setup_igp(struct e1000_hw * hw)9324919Sxy150489 e1000_copper_link_setup_igp(struct e1000_hw *hw)
9334919Sxy150489 {
9344919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
9354919Sxy150489 	s32 ret_val;
9364919Sxy150489 	u16 data;
9374919Sxy150489 
9384919Sxy150489 	DEBUGFUNC("e1000_copper_link_setup_igp");
9394919Sxy150489 
9404919Sxy150489 	if (phy->reset_disable) {
9414919Sxy150489 		ret_val = E1000_SUCCESS;
9424919Sxy150489 		goto out;
9434919Sxy150489 	}
9444919Sxy150489 
9457607STed.You@Sun.COM 	ret_val = hw->phy.ops.reset(hw);
9464919Sxy150489 	if (ret_val) {
9474919Sxy150489 		DEBUGOUT("Error resetting the PHY.\n");
9484919Sxy150489 		goto out;
9494919Sxy150489 	}
9504919Sxy150489 
9516735Scc210113 	/*
9526735Scc210113 	 * Wait 100ms for MAC to configure PHY from NVM settings, to avoid
9536735Scc210113 	 * timeout issues when LFS is enabled.
9546735Scc210113 	 */
9556735Scc210113 	msec_delay(100);
9564919Sxy150489 
9574919Sxy150489 	/*
9584919Sxy150489 	 * The NVM settings will configure LPLU in D3 for non-IGP1 PHYs.
9594919Sxy150489 	 */
9604919Sxy150489 	if (phy->type == e1000_phy_igp) {
9614919Sxy150489 		/* disable lplu d3 during driver init */
9627607STed.You@Sun.COM 		ret_val = hw->phy.ops.set_d3_lplu_state(hw, false);
9634919Sxy150489 		if (ret_val) {
9644919Sxy150489 			DEBUGOUT("Error Disabling LPLU D3\n");
9654919Sxy150489 			goto out;
9664919Sxy150489 		}
9674919Sxy150489 	}
9684919Sxy150489 
9694919Sxy150489 	/* disable lplu d0 during driver init */
9707607STed.You@Sun.COM 	if (hw->phy.ops.set_d0_lplu_state) {
9717607STed.You@Sun.COM 		ret_val = hw->phy.ops.set_d0_lplu_state(hw, false);
9727607STed.You@Sun.COM 		if (ret_val) {
9737607STed.You@Sun.COM 			DEBUGOUT("Error Disabling LPLU D0\n");
9747607STed.You@Sun.COM 			goto out;
9757607STed.You@Sun.COM 		}
9764919Sxy150489 	}
9774919Sxy150489 	/* Configure mdi-mdix settings */
9786735Scc210113 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data);
9794919Sxy150489 	if (ret_val)
9804919Sxy150489 		goto out;
9814919Sxy150489 
9824919Sxy150489 	data &= ~IGP01E1000_PSCR_AUTO_MDIX;
9834919Sxy150489 
9844919Sxy150489 	switch (phy->mdix) {
9854919Sxy150489 	case 1:
9864919Sxy150489 		data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
9874919Sxy150489 		break;
9884919Sxy150489 	case 2:
9894919Sxy150489 		data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
9904919Sxy150489 		break;
9914919Sxy150489 	case 0:
9924919Sxy150489 	default:
9934919Sxy150489 		data |= IGP01E1000_PSCR_AUTO_MDIX;
9944919Sxy150489 		break;
9954919Sxy150489 	}
9966735Scc210113 	ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, data);
9974919Sxy150489 	if (ret_val)
9984919Sxy150489 		goto out;
9994919Sxy150489 
10004919Sxy150489 	/* set auto-master slave resolution settings */
10014919Sxy150489 	if (hw->mac.autoneg) {
10024919Sxy150489 		/*
10034919Sxy150489 		 * when autonegotiation advertisement is only 1000Mbps then we
10044919Sxy150489 		 * should disable SmartSpeed and enable Auto MasterSlave
10054919Sxy150489 		 * resolution as hardware default.
10064919Sxy150489 		 */
10074919Sxy150489 		if (phy->autoneg_advertised == ADVERTISE_1000_FULL) {
10084919Sxy150489 			/* Disable SmartSpeed */
10096735Scc210113 			ret_val = phy->ops.read_reg(hw,
10104919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
10114919Sxy150489 			    &data);
10124919Sxy150489 			if (ret_val)
10134919Sxy150489 				goto out;
10144919Sxy150489 
10154919Sxy150489 			data &= ~IGP01E1000_PSCFR_SMART_SPEED;
10166735Scc210113 			ret_val = phy->ops.write_reg(hw,
10174919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
10184919Sxy150489 			    data);
10194919Sxy150489 			if (ret_val)
10204919Sxy150489 				goto out;
10214919Sxy150489 
10224919Sxy150489 			/* Set auto Master/Slave resolution process */
10236735Scc210113 			ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data);
10244919Sxy150489 			if (ret_val)
10254919Sxy150489 				goto out;
10264919Sxy150489 
10274919Sxy150489 			data &= ~CR_1000T_MS_ENABLE;
10286735Scc210113 			ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data);
10294919Sxy150489 			if (ret_val)
10304919Sxy150489 				goto out;
10314919Sxy150489 		}
10324919Sxy150489 
10336735Scc210113 		ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data);
10344919Sxy150489 		if (ret_val)
10354919Sxy150489 			goto out;
10364919Sxy150489 
10374919Sxy150489 		/* load defaults for future use */
10384919Sxy150489 		phy->original_ms_type = (data & CR_1000T_MS_ENABLE) ?
10394919Sxy150489 		    ((data & CR_1000T_MS_VALUE) ?
10404919Sxy150489 		    e1000_ms_force_master :
10414919Sxy150489 		    e1000_ms_force_slave) :
10424919Sxy150489 		    e1000_ms_auto;
10434919Sxy150489 
10444919Sxy150489 		switch (phy->ms_type) {
10454919Sxy150489 		case e1000_ms_force_master:
10464919Sxy150489 			data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
10474919Sxy150489 			break;
10484919Sxy150489 		case e1000_ms_force_slave:
10494919Sxy150489 			data |= CR_1000T_MS_ENABLE;
10504919Sxy150489 			data &= ~(CR_1000T_MS_VALUE);
10514919Sxy150489 			break;
10524919Sxy150489 		case e1000_ms_auto:
10534919Sxy150489 			data &= ~CR_1000T_MS_ENABLE;
10544919Sxy150489 		default:
10554919Sxy150489 			break;
10564919Sxy150489 		}
10576735Scc210113 		ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data);
10584919Sxy150489 		if (ret_val)
10594919Sxy150489 			goto out;
10604919Sxy150489 	}
10614919Sxy150489 
10624919Sxy150489 out:
10634919Sxy150489 	return (ret_val);
10644919Sxy150489 }
10654919Sxy150489 
10664919Sxy150489 /*
10674919Sxy150489  * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link
10684919Sxy150489  * @hw: pointer to the HW structure
10694919Sxy150489  *
10704919Sxy150489  * Performs initial bounds checking on autoneg advertisement parameter, then
10714919Sxy150489  * configure to advertise the full capability.  Setup the PHY to autoneg
10724919Sxy150489  * and restart the negotiation process between the link partner.  If
10736735Scc210113  * autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
10744919Sxy150489  */
10754919Sxy150489 s32
e1000_copper_link_autoneg(struct e1000_hw * hw)10764919Sxy150489 e1000_copper_link_autoneg(struct e1000_hw *hw)
10774919Sxy150489 {
10784919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
10794919Sxy150489 	s32 ret_val;
10804919Sxy150489 	u16 phy_ctrl;
10814919Sxy150489 
10824919Sxy150489 	DEBUGFUNC("e1000_copper_link_autoneg");
10834919Sxy150489 
10844919Sxy150489 	/*
10854919Sxy150489 	 * Perform some bounds checking on the autoneg advertisement
10864919Sxy150489 	 * parameter.
10874919Sxy150489 	 */
10884919Sxy150489 	phy->autoneg_advertised &= phy->autoneg_mask;
10894919Sxy150489 
10904919Sxy150489 	/*
10914919Sxy150489 	 * If autoneg_advertised is zero, we assume it was not defaulted by
10924919Sxy150489 	 * the calling code so we set to advertise full capability.
10934919Sxy150489 	 */
10944919Sxy150489 	if (phy->autoneg_advertised == 0)
10954919Sxy150489 		phy->autoneg_advertised = phy->autoneg_mask;
10964919Sxy150489 
10974919Sxy150489 	DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
10984919Sxy150489 	ret_val = e1000_phy_setup_autoneg(hw);
10994919Sxy150489 	if (ret_val) {
11004919Sxy150489 		DEBUGOUT("Error Setting up Auto-Negotiation\n");
11014919Sxy150489 		goto out;
11024919Sxy150489 	}
11034919Sxy150489 	DEBUGOUT("Restarting Auto-Neg\n");
11044919Sxy150489 
11054919Sxy150489 	/*
11064919Sxy150489 	 * Restart auto-negotiation by setting the Auto Neg Enable bit and the
11074919Sxy150489 	 * Auto Neg Restart bit in the PHY control register.
11084919Sxy150489 	 */
11096735Scc210113 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
11104919Sxy150489 	if (ret_val)
11114919Sxy150489 		goto out;
11124919Sxy150489 
11134919Sxy150489 	phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
11146735Scc210113 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
11154919Sxy150489 	if (ret_val)
11164919Sxy150489 		goto out;
11174919Sxy150489 
11184919Sxy150489 	/*
11194919Sxy150489 	 * Does the user want to wait for Auto-Neg to complete here, or check
11204919Sxy150489 	 * at a later time (for example, callback routine).
11214919Sxy150489 	 */
11226735Scc210113 	if (phy->autoneg_wait_to_complete) {
11237607STed.You@Sun.COM 		ret_val = hw->mac.ops.wait_autoneg(hw);
11244919Sxy150489 		if (ret_val) {
11254919Sxy150489 			DEBUGOUT("Error while waiting for "
11264919Sxy150489 			    "autoneg to complete\n");
11274919Sxy150489 			goto out;
11284919Sxy150489 		}
11294919Sxy150489 	}
11304919Sxy150489 
11317607STed.You@Sun.COM 	hw->mac.get_link_status = true;
11324919Sxy150489 
11334919Sxy150489 out:
11344919Sxy150489 	return (ret_val);
11354919Sxy150489 }
11364919Sxy150489 
11374919Sxy150489 /*
11384919Sxy150489  * e1000_phy_setup_autoneg - Configure PHY for auto-negotiation
11394919Sxy150489  * @hw: pointer to the HW structure
11404919Sxy150489  *
11414919Sxy150489  * Reads the MII auto-neg advertisement register and/or the 1000T control
11424919Sxy150489  * register and if the PHY is already setup for auto-negotiation, then
11434919Sxy150489  * return successful.  Otherwise, setup advertisement and flow control to
11444919Sxy150489  * the appropriate values for the wanted auto-negotiation.
11454919Sxy150489  */
11464919Sxy150489 s32
e1000_phy_setup_autoneg(struct e1000_hw * hw)11474919Sxy150489 e1000_phy_setup_autoneg(struct e1000_hw *hw)
11484919Sxy150489 {
11494919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
11504919Sxy150489 	s32 ret_val;
11514919Sxy150489 	u16 mii_autoneg_adv_reg;
11524919Sxy150489 	u16 mii_1000t_ctrl_reg = 0;
11534919Sxy150489 
11544919Sxy150489 	DEBUGFUNC("e1000_phy_setup_autoneg");
11554919Sxy150489 
11564919Sxy150489 	phy->autoneg_advertised &= phy->autoneg_mask;
11574919Sxy150489 
11584919Sxy150489 	/* Read the MII Auto-Neg Advertisement Register (Address 4). */
11596735Scc210113 	ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
11604919Sxy150489 	if (ret_val)
11614919Sxy150489 		goto out;
11624919Sxy150489 
11634919Sxy150489 	if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
11644919Sxy150489 		/* Read the MII 1000Base-T Control Register (Address 9). */
11656735Scc210113 		ret_val = phy->ops.read_reg(hw,
11664919Sxy150489 		    PHY_1000T_CTRL,
11674919Sxy150489 		    &mii_1000t_ctrl_reg);
11684919Sxy150489 		if (ret_val)
11694919Sxy150489 			goto out;
11704919Sxy150489 	}
11714919Sxy150489 
11724919Sxy150489 	/*
11734919Sxy150489 	 * Need to parse both autoneg_advertised and fc and set up the
11744919Sxy150489 	 * appropriate PHY registers.  First we will parse for
11754919Sxy150489 	 * autoneg_advertised software override.  Since we can advertise a
11764919Sxy150489 	 * plethora of combinations, we need to check each bit individually.
11774919Sxy150489 	 */
11784919Sxy150489 
11794919Sxy150489 	/*
11804919Sxy150489 	 * First we clear all the 10/100 mb speed bits in the Auto-Neg
11814919Sxy150489 	 * Advertisement Register (Address 4) and the 1000 mb speed bits in
11824919Sxy150489 	 * the  1000Base-T Control Register (Address 9).
11834919Sxy150489 	 */
11844919Sxy150489 	mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS |
11854919Sxy150489 	    NWAY_AR_100TX_HD_CAPS |
11864919Sxy150489 	    NWAY_AR_10T_FD_CAPS |
11874919Sxy150489 	    NWAY_AR_10T_HD_CAPS);
11884919Sxy150489 	mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
11894919Sxy150489 
11904919Sxy150489 	DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised);
11914919Sxy150489 
11924919Sxy150489 	/* Do we want to advertise 10 Mb Half Duplex? */
11934919Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
11944919Sxy150489 		DEBUGOUT("Advertise 10mb Half duplex\n");
11954919Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
11964919Sxy150489 	}
11974919Sxy150489 
11984919Sxy150489 	/* Do we want to advertise 10 Mb Full Duplex? */
11994919Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
12004919Sxy150489 		DEBUGOUT("Advertise 10mb Full duplex\n");
12014919Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
12024919Sxy150489 	}
12034919Sxy150489 
12044919Sxy150489 	/* Do we want to advertise 100 Mb Half Duplex? */
12054919Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
12064919Sxy150489 		DEBUGOUT("Advertise 100mb Half duplex\n");
12074919Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
12084919Sxy150489 	}
12094919Sxy150489 
12104919Sxy150489 	/* Do we want to advertise 100 Mb Full Duplex? */
12114919Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
12124919Sxy150489 		DEBUGOUT("Advertise 100mb Full duplex\n");
12134919Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
12144919Sxy150489 	}
12154919Sxy150489 
12164919Sxy150489 	/* We do not allow the Phy to advertise 1000 Mb Half Duplex */
1217*11143SGuoqing.Zhu@Sun.COM 	if (phy->autoneg_advertised & ADVERTISE_1000_HALF) {
1218*11143SGuoqing.Zhu@Sun.COM 		/* EMPTY */
12194919Sxy150489 		DEBUGOUT("Advertise 1000mb Half duplex request denied!\n");
1220*11143SGuoqing.Zhu@Sun.COM 	}
12214919Sxy150489 
12224919Sxy150489 	/* Do we want to advertise 1000 Mb Full Duplex? */
12234919Sxy150489 	if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
12244919Sxy150489 		DEBUGOUT("Advertise 1000mb Full duplex\n");
12254919Sxy150489 		mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
12264919Sxy150489 	}
12274919Sxy150489 
12284919Sxy150489 	/*
12294919Sxy150489 	 * Check for a software override of the flow control settings, and
12304919Sxy150489 	 * setup the PHY advertisement registers accordingly.  If
12314919Sxy150489 	 * auto-negotiation is enabled, then software will have to set the
12324919Sxy150489 	 * "PAUSE" bits to the correct value in the Auto-Negotiation
12334919Sxy150489 	 * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-
12344919Sxy150489 	 * negotiation.
12354919Sxy150489 	 *
12364919Sxy150489 	 * The possible values of the "fc" parameter are:
12374919Sxy150489 	 * 0:	Flow control is completely disabled
12384919Sxy150489 	 * 1:	Rx flow control is enabled (we can receive pause frames
12394919Sxy150489 	 *	but not send pause frames).
12404919Sxy150489 	 * 2:	Tx flow control is enabled (we can send pause frames
12414919Sxy150489 	 *	but we do not support receiving pause frames).
12426735Scc210113 	 * 3:	Both Rx and Tx flow control (symmetric) are enabled.
12434919Sxy150489 	 * other: No software override.  The flow control configuration
12444919Sxy150489 	 *	in the EEPROM is used.
12454919Sxy150489 	 */
12468479SChenlu.Chen@Sun.COM 	switch (hw->fc.current_mode) {
12474919Sxy150489 	case e1000_fc_none:
12484919Sxy150489 		/*
12496735Scc210113 		 * Flow control (Rx & Tx) is completely disabled by a software
12504919Sxy150489 		 * over-ride.
12514919Sxy150489 		 */
12524919Sxy150489 		mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
12534919Sxy150489 		break;
12544919Sxy150489 	case e1000_fc_rx_pause:
12554919Sxy150489 		/*
12566735Scc210113 		 * Rx Flow control is enabled, and Tx Flow control is
12574919Sxy150489 		 * disabled, by a software over-ride.
12584919Sxy150489 		 *
12594919Sxy150489 		 * Since there really isn't a way to advertise that we are
12606735Scc210113 		 * capable of Rx Pause ONLY, we will advertise that we support
12616735Scc210113 		 * both symmetric and asymmetric Rx PAUSE.  Later (in
12624919Sxy150489 		 * e1000_config_fc_after_link_up) we will disable the hw's
12634919Sxy150489 		 * ability to send PAUSE frames.
12644919Sxy150489 		 */
12654919Sxy150489 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
12664919Sxy150489 		break;
12674919Sxy150489 	case e1000_fc_tx_pause:
12684919Sxy150489 		/*
12696735Scc210113 		 * Tx Flow control is enabled, and Rx Flow control is
12704919Sxy150489 		 * disabled, by a software over-ride.
12714919Sxy150489 		 */
12724919Sxy150489 		mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
12734919Sxy150489 		mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
12744919Sxy150489 		break;
12754919Sxy150489 	case e1000_fc_full:
12764919Sxy150489 		/*
12776735Scc210113 		 * Flow control (both Rx and Tx) is enabled by a software
12784919Sxy150489 		 * over-ride.
12794919Sxy150489 		 */
12804919Sxy150489 		mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
12814919Sxy150489 		break;
12824919Sxy150489 	default:
12834919Sxy150489 		DEBUGOUT("Flow control param set incorrectly\n");
12844919Sxy150489 		ret_val = -E1000_ERR_CONFIG;
12854919Sxy150489 		goto out;
12864919Sxy150489 	}
12874919Sxy150489 
12886735Scc210113 	ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
12894919Sxy150489 	if (ret_val)
12904919Sxy150489 		goto out;
12914919Sxy150489 
12924919Sxy150489 	DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
12934919Sxy150489 
12944919Sxy150489 	if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
12956735Scc210113 		ret_val = phy->ops.write_reg(hw,
12964919Sxy150489 		    PHY_1000T_CTRL,
12974919Sxy150489 		    mii_1000t_ctrl_reg);
12984919Sxy150489 		if (ret_val)
12994919Sxy150489 			goto out;
13004919Sxy150489 	}
13014919Sxy150489 
13024919Sxy150489 out:
13034919Sxy150489 	return (ret_val);
13044919Sxy150489 }
13054919Sxy150489 
13064919Sxy150489 /*
13074919Sxy150489  * e1000_setup_copper_link_generic - Configure copper link settings
13084919Sxy150489  * @hw: pointer to the HW structure
13094919Sxy150489  *
13104919Sxy150489  * Calls the appropriate function to configure the link for auto-neg or forced
13114919Sxy150489  * speed and duplex.  Then we check for link, once link is established calls
13124919Sxy150489  * to configure collision distance and flow control are called.  If link is
13134919Sxy150489  * not established, we return -E1000_ERR_PHY (-2).
13144919Sxy150489  */
13154919Sxy150489 s32
e1000_setup_copper_link_generic(struct e1000_hw * hw)13164919Sxy150489 e1000_setup_copper_link_generic(struct e1000_hw *hw)
13174919Sxy150489 {
13184919Sxy150489 	s32 ret_val;
13196735Scc210113 	bool link;
13204919Sxy150489 
13214919Sxy150489 	DEBUGFUNC("e1000_setup_copper_link_generic");
13224919Sxy150489 
13234919Sxy150489 	if (hw->mac.autoneg) {
13244919Sxy150489 		/*
13254919Sxy150489 		 * Setup autoneg and flow control advertisement and perform
13264919Sxy150489 		 * autonegotiation.
13274919Sxy150489 		 */
13284919Sxy150489 		ret_val = e1000_copper_link_autoneg(hw);
13294919Sxy150489 		if (ret_val)
13304919Sxy150489 			goto out;
13314919Sxy150489 	} else {
13324919Sxy150489 		/*
13334919Sxy150489 		 * PHY will be set to 10H, 10F, 100H or 100F depending on user
13344919Sxy150489 		 * settings.
13354919Sxy150489 		 */
13364919Sxy150489 		DEBUGOUT("Forcing Speed and Duplex\n");
13376735Scc210113 		ret_val = hw->phy.ops.force_speed_duplex(hw);
13384919Sxy150489 		if (ret_val) {
13394919Sxy150489 			DEBUGOUT("Error Forcing Speed and Duplex\n");
13404919Sxy150489 			goto out;
13414919Sxy150489 		}
13424919Sxy150489 	}
13434919Sxy150489 
13444919Sxy150489 	/*
13454919Sxy150489 	 * Check link status. Wait up to 100 microseconds for link to become
13464919Sxy150489 	 * valid.
13474919Sxy150489 	 */
13484919Sxy150489 	ret_val = e1000_phy_has_link_generic(hw,
13494919Sxy150489 	    COPPER_LINK_UP_LIMIT,
13504919Sxy150489 	    10,
13514919Sxy150489 	    &link);
13524919Sxy150489 	if (ret_val)
13534919Sxy150489 		goto out;
13544919Sxy150489 
13554919Sxy150489 	if (link) {
13564919Sxy150489 		DEBUGOUT("Valid link established!!!\n");
13574919Sxy150489 		e1000_config_collision_dist_generic(hw);
13584919Sxy150489 		ret_val = e1000_config_fc_after_link_up_generic(hw);
13594919Sxy150489 	} else {
1360*11143SGuoqing.Zhu@Sun.COM 		/* EMPTY */
13614919Sxy150489 		DEBUGOUT("Unable to establish link!!!\n");
13624919Sxy150489 	}
13634919Sxy150489 
13644919Sxy150489 out:
13654919Sxy150489 	return (ret_val);
13664919Sxy150489 }
13674919Sxy150489 
13684919Sxy150489 /*
13694919Sxy150489  * e1000_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY
13704919Sxy150489  * @hw: pointer to the HW structure
13714919Sxy150489  *
13724919Sxy150489  * Calls the PHY setup function to force speed and duplex.  Clears the
13734919Sxy150489  * auto-crossover to force MDI manually.  Waits for link and returns
13744919Sxy150489  * successful if link up is successful, else -E1000_ERR_PHY (-2).
13754919Sxy150489  */
13764919Sxy150489 s32
e1000_phy_force_speed_duplex_igp(struct e1000_hw * hw)13774919Sxy150489 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw)
13784919Sxy150489 {
13794919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
13804919Sxy150489 	s32 ret_val;
13814919Sxy150489 	u16 phy_data;
13826735Scc210113 	bool link;
13834919Sxy150489 
13844919Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_igp");
13854919Sxy150489 
13866735Scc210113 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
13874919Sxy150489 	if (ret_val)
13884919Sxy150489 		goto out;
13894919Sxy150489 
13904919Sxy150489 	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
13914919Sxy150489 
13926735Scc210113 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
13934919Sxy150489 	if (ret_val)
13944919Sxy150489 		goto out;
13954919Sxy150489 
13964919Sxy150489 	/*
13974919Sxy150489 	 * Clear Auto-Crossover to force MDI manually.  IGP requires MDI
13984919Sxy150489 	 * forced whenever speed and duplex are forced.
13994919Sxy150489 	 */
14006735Scc210113 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data);
14014919Sxy150489 	if (ret_val)
14024919Sxy150489 		goto out;
14034919Sxy150489 
14044919Sxy150489 	phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
14054919Sxy150489 	phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
14064919Sxy150489 
14076735Scc210113 	ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data);
14084919Sxy150489 	if (ret_val)
14094919Sxy150489 		goto out;
14104919Sxy150489 
14114919Sxy150489 	DEBUGOUT1("IGP PSCR: %X\n", phy_data);
14124919Sxy150489 
14134919Sxy150489 	usec_delay(1);
14144919Sxy150489 
14156735Scc210113 	if (phy->autoneg_wait_to_complete) {
14164919Sxy150489 		DEBUGOUT("Waiting for forced speed/duplex link on IGP phy.\n");
14174919Sxy150489 
14184919Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
14194919Sxy150489 		    PHY_FORCE_LIMIT,
14204919Sxy150489 		    100000,
14214919Sxy150489 		    &link);
14224919Sxy150489 		if (ret_val)
14234919Sxy150489 			goto out;
14244919Sxy150489 
1425*11143SGuoqing.Zhu@Sun.COM 		if (!link) {
1426*11143SGuoqing.Zhu@Sun.COM 			/* EMPTY */
14274919Sxy150489 			DEBUGOUT("Link taking longer than expected.\n");
1428*11143SGuoqing.Zhu@Sun.COM 		}
14294919Sxy150489 
14304919Sxy150489 		/* Try once more */
14314919Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
14324919Sxy150489 		    PHY_FORCE_LIMIT,
14334919Sxy150489 		    100000,
14344919Sxy150489 		    &link);
14354919Sxy150489 		if (ret_val)
14364919Sxy150489 			goto out;
14374919Sxy150489 	}
14384919Sxy150489 
14394919Sxy150489 out:
14404919Sxy150489 	return (ret_val);
14414919Sxy150489 }
14424919Sxy150489 
14434919Sxy150489 /*
14444919Sxy150489  * e1000_phy_force_speed_duplex_m88 - Force speed/duplex for m88 PHY
14454919Sxy150489  * @hw: pointer to the HW structure
14464919Sxy150489  *
14474919Sxy150489  * Calls the PHY setup function to force speed and duplex.  Clears the
14484919Sxy150489  * auto-crossover to force MDI manually.  Resets the PHY to commit the
14494919Sxy150489  * changes.  If time expires while waiting for link up, we reset the DSP.
14506735Scc210113  * After reset, TX_CLK and CRS on Tx must be set.  Return successful upon
14514919Sxy150489  * successful completion, else return corresponding error code.
14524919Sxy150489  */
14534919Sxy150489 s32
e1000_phy_force_speed_duplex_m88(struct e1000_hw * hw)14544919Sxy150489 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw)
14554919Sxy150489 {
14564919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
14574919Sxy150489 	s32 ret_val;
14584919Sxy150489 	u16 phy_data;
14596735Scc210113 	bool link;
14604919Sxy150489 
14614919Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_m88");
14624919Sxy150489 
14634919Sxy150489 	/*
14644919Sxy150489 	 * Clear Auto-Crossover to force MDI manually.  M88E1000 requires MDI
14654919Sxy150489 	 * forced whenever speed and duplex are forced.
14664919Sxy150489 	 */
14676735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
14684919Sxy150489 	if (ret_val)
14694919Sxy150489 		goto out;
14704919Sxy150489 
14714919Sxy150489 	phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
14726735Scc210113 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
14734919Sxy150489 	if (ret_val)
14744919Sxy150489 		goto out;
14754919Sxy150489 
14764919Sxy150489 	DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data);
14774919Sxy150489 
14786735Scc210113 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
14794919Sxy150489 	if (ret_val)
14804919Sxy150489 		goto out;
14814919Sxy150489 
14824919Sxy150489 	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
14834919Sxy150489 
14846735Scc210113 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
14854919Sxy150489 	if (ret_val)
14864919Sxy150489 		goto out;
14874919Sxy150489 
14888479SChenlu.Chen@Sun.COM 	/* Reset the phy to commit changes. */
14898479SChenlu.Chen@Sun.COM 	ret_val = hw->phy.ops.commit(hw);
14908479SChenlu.Chen@Sun.COM 	if (ret_val)
14918479SChenlu.Chen@Sun.COM 		goto out;
14924919Sxy150489 
14936735Scc210113 	if (phy->autoneg_wait_to_complete) {
14944919Sxy150489 		DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n");
14954919Sxy150489 
14964919Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
14974919Sxy150489 		    PHY_FORCE_LIMIT,
14984919Sxy150489 		    100000,
14994919Sxy150489 		    &link);
15004919Sxy150489 		if (ret_val)
15014919Sxy150489 			goto out;
15024919Sxy150489 
15034919Sxy150489 		if (!link) {
15044919Sxy150489 			/*
15054919Sxy150489 			 * We didn't get link. Reset the DSP and cross our
15064919Sxy150489 			 * fingers.
15074919Sxy150489 			 */
15086735Scc210113 			ret_val = phy->ops.write_reg(hw,
15094919Sxy150489 			    M88E1000_PHY_PAGE_SELECT,
15104919Sxy150489 			    0x001d);
15114919Sxy150489 			if (ret_val)
15124919Sxy150489 				goto out;
15134919Sxy150489 			ret_val = e1000_phy_reset_dsp_generic(hw);
15144919Sxy150489 			if (ret_val)
15154919Sxy150489 				goto out;
15164919Sxy150489 		}
15174919Sxy150489 		/* Try once more */
15184919Sxy150489 		ret_val = e1000_phy_has_link_generic(hw,
15194919Sxy150489 		    PHY_FORCE_LIMIT,
15204919Sxy150489 		    100000,
15214919Sxy150489 		    &link);
15224919Sxy150489 		if (ret_val)
15234919Sxy150489 			goto out;
15244919Sxy150489 	}
15256735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
15264919Sxy150489 	if (ret_val)
15274919Sxy150489 		goto out;
15284919Sxy150489 
15294919Sxy150489 	/*
15304919Sxy150489 	 * Resetting the phy means we need to re-force TX_CLK in the Extended
15314919Sxy150489 	 * PHY Specific Control Register to 25MHz clock from the reset value
15324919Sxy150489 	 * of 2.5MHz.
15334919Sxy150489 	 */
15344919Sxy150489 	phy_data |= M88E1000_EPSCR_TX_CLK_25;
15356735Scc210113 	ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data);
15364919Sxy150489 	if (ret_val)
15374919Sxy150489 		goto out;
15384919Sxy150489 
15394919Sxy150489 	/*
15404919Sxy150489 	 * In addition, we must re-enable CRS on Tx for both half and full
15414919Sxy150489 	 * duplex.
15424919Sxy150489 	 */
15436735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
15444919Sxy150489 	if (ret_val)
15454919Sxy150489 		goto out;
15464919Sxy150489 
15474919Sxy150489 	phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
15486735Scc210113 	ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
15494919Sxy150489 
15504919Sxy150489 out:
15514919Sxy150489 	return (ret_val);
15524919Sxy150489 }
15534919Sxy150489 
15544919Sxy150489 /*
155510680SMin.Xu@Sun.COM  * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex
155610680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
155710680SMin.Xu@Sun.COM  *
155810680SMin.Xu@Sun.COM  * Forces the speed and duplex settings of the PHY.
155910680SMin.Xu@Sun.COM  * This is a function pointer entry point only called by
156010680SMin.Xu@Sun.COM  * PHY setup routines.
156110680SMin.Xu@Sun.COM  */
156210680SMin.Xu@Sun.COM s32
e1000_phy_force_speed_duplex_ife(struct e1000_hw * hw)156310680SMin.Xu@Sun.COM e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw)
156410680SMin.Xu@Sun.COM {
156510680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
156610680SMin.Xu@Sun.COM 	s32 ret_val;
156710680SMin.Xu@Sun.COM 	u16 data;
156810680SMin.Xu@Sun.COM 	bool link;
156910680SMin.Xu@Sun.COM 
157010680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_phy_force_speed_duplex_ife");
157110680SMin.Xu@Sun.COM 
157210680SMin.Xu@Sun.COM 	if (phy->type != e1000_phy_ife) {
157310680SMin.Xu@Sun.COM 		ret_val = e1000_phy_force_speed_duplex_igp(hw);
157410680SMin.Xu@Sun.COM 		goto out;
157510680SMin.Xu@Sun.COM 	}
157610680SMin.Xu@Sun.COM 
157710680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data);
157810680SMin.Xu@Sun.COM 	if (ret_val)
157910680SMin.Xu@Sun.COM 		goto out;
158010680SMin.Xu@Sun.COM 
158110680SMin.Xu@Sun.COM 	e1000_phy_force_speed_duplex_setup(hw, &data);
158210680SMin.Xu@Sun.COM 
158310680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data);
158410680SMin.Xu@Sun.COM 	if (ret_val)
158510680SMin.Xu@Sun.COM 		goto out;
158610680SMin.Xu@Sun.COM 
158710680SMin.Xu@Sun.COM 	/* Disable MDI-X support for 10/100 */
158810680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
158910680SMin.Xu@Sun.COM 	if (ret_val)
159010680SMin.Xu@Sun.COM 		goto out;
159110680SMin.Xu@Sun.COM 
159210680SMin.Xu@Sun.COM 	data &= ~IFE_PMC_AUTO_MDIX;
159310680SMin.Xu@Sun.COM 	data &= ~IFE_PMC_FORCE_MDIX;
159410680SMin.Xu@Sun.COM 
159510680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data);
159610680SMin.Xu@Sun.COM 	if (ret_val)
159710680SMin.Xu@Sun.COM 		goto out;
159810680SMin.Xu@Sun.COM 
159910680SMin.Xu@Sun.COM 	DEBUGOUT1("IFE PMC: %X\n", data);
160010680SMin.Xu@Sun.COM 
160110680SMin.Xu@Sun.COM 	usec_delay(1);
160210680SMin.Xu@Sun.COM 
160310680SMin.Xu@Sun.COM 	if (phy->autoneg_wait_to_complete) {
160410680SMin.Xu@Sun.COM 		DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n");
160510680SMin.Xu@Sun.COM 
160610680SMin.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
160710680SMin.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
160810680SMin.Xu@Sun.COM 		if (ret_val)
160910680SMin.Xu@Sun.COM 			goto out;
161010680SMin.Xu@Sun.COM 
1611*11143SGuoqing.Zhu@Sun.COM 		if (!link) {
1612*11143SGuoqing.Zhu@Sun.COM 			/* EMPTY */
161310680SMin.Xu@Sun.COM 			DEBUGOUT("Link taking longer than expected.\n");
1614*11143SGuoqing.Zhu@Sun.COM 		}
161510680SMin.Xu@Sun.COM 
161610680SMin.Xu@Sun.COM 		/* Try once more */
161710680SMin.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
161810680SMin.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
161910680SMin.Xu@Sun.COM 		if (ret_val)
162010680SMin.Xu@Sun.COM 			goto out;
162110680SMin.Xu@Sun.COM 	}
162210680SMin.Xu@Sun.COM 
162310680SMin.Xu@Sun.COM out:
162410680SMin.Xu@Sun.COM 	return (ret_val);
162510680SMin.Xu@Sun.COM }
162610680SMin.Xu@Sun.COM 
162710680SMin.Xu@Sun.COM /*
16284919Sxy150489  * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex
16294919Sxy150489  * @hw: pointer to the HW structure
16304919Sxy150489  * @phy_ctrl: pointer to current value of PHY_CONTROL
16314919Sxy150489  *
16324919Sxy150489  * Forces speed and duplex on the PHY by doing the following: disable flow
16334919Sxy150489  * control, force speed/duplex on the MAC, disable auto speed detection,
16344919Sxy150489  * disable auto-negotiation, configure duplex, configure speed, configure
16354919Sxy150489  * the collision distance, write configuration to CTRL register.  The
16364919Sxy150489  * caller must write to the PHY_CONTROL register for these settings to
16374919Sxy150489  * take affect.
16384919Sxy150489  */
16394919Sxy150489 void
e1000_phy_force_speed_duplex_setup(struct e1000_hw * hw,u16 * phy_ctrl)16404919Sxy150489 e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl)
16414919Sxy150489 {
16424919Sxy150489 	struct e1000_mac_info *mac = &hw->mac;
16434919Sxy150489 	u32 ctrl;
16444919Sxy150489 
16454919Sxy150489 	DEBUGFUNC("e1000_phy_force_speed_duplex_setup");
16464919Sxy150489 
16474919Sxy150489 	/* Turn off flow control when forcing speed/duplex */
16488479SChenlu.Chen@Sun.COM 	hw->fc.current_mode = e1000_fc_none;
16494919Sxy150489 
16504919Sxy150489 	/* Force speed/duplex on the mac */
16514919Sxy150489 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
16524919Sxy150489 	ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
16534919Sxy150489 	ctrl &= ~E1000_CTRL_SPD_SEL;
16544919Sxy150489 
16554919Sxy150489 	/* Disable Auto Speed Detection */
16564919Sxy150489 	ctrl &= ~E1000_CTRL_ASDE;
16574919Sxy150489 
16584919Sxy150489 	/* Disable autoneg on the phy */
16594919Sxy150489 	*phy_ctrl &= ~MII_CR_AUTO_NEG_EN;
16604919Sxy150489 
16614919Sxy150489 	/* Forcing Full or Half Duplex? */
16624919Sxy150489 	if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) {
16634919Sxy150489 		ctrl &= ~E1000_CTRL_FD;
16644919Sxy150489 		*phy_ctrl &= ~MII_CR_FULL_DUPLEX;
16654919Sxy150489 		DEBUGOUT("Half Duplex\n");
16664919Sxy150489 	} else {
16674919Sxy150489 		ctrl |= E1000_CTRL_FD;
16684919Sxy150489 		*phy_ctrl |= MII_CR_FULL_DUPLEX;
16694919Sxy150489 		DEBUGOUT("Full Duplex\n");
16704919Sxy150489 	}
16714919Sxy150489 
16724919Sxy150489 	/* Forcing 10mb or 100mb? */
16734919Sxy150489 	if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) {
16744919Sxy150489 		ctrl |= E1000_CTRL_SPD_100;
16754919Sxy150489 		*phy_ctrl |= MII_CR_SPEED_100;
16764919Sxy150489 		*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10);
16774919Sxy150489 		DEBUGOUT("Forcing 100mb\n");
16784919Sxy150489 	} else {
16794919Sxy150489 		ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
168010680SMin.Xu@Sun.COM 		/* LINTED */
16814919Sxy150489 		*phy_ctrl |= MII_CR_SPEED_10;
16824919Sxy150489 		*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
16834919Sxy150489 		DEBUGOUT("Forcing 10mb\n");
16844919Sxy150489 	}
16854919Sxy150489 
16864919Sxy150489 	e1000_config_collision_dist_generic(hw);
16874919Sxy150489 
16884919Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
16894919Sxy150489 }
16904919Sxy150489 
16914919Sxy150489 /*
16924919Sxy150489  * e1000_set_d3_lplu_state_generic - Sets low power link up state for D3
16934919Sxy150489  * @hw: pointer to the HW structure
16944919Sxy150489  * @active: boolean used to enable/disable lplu
16954919Sxy150489  *
16964919Sxy150489  * Success returns 0, Failure returns 1
16974919Sxy150489  *
16984919Sxy150489  * The low power link up (lplu) state is set to the power management level D3
16994919Sxy150489  * and SmartSpeed is disabled when active is true, else clear lplu for D3
17004919Sxy150489  * and enable Smartspeed.  LPLU and Smartspeed are mutually exclusive.  LPLU
17014919Sxy150489  * is used during Dx states where the power conservation is most important.
17024919Sxy150489  * During driver activity, SmartSpeed should be enabled so performance is
17034919Sxy150489  * maintained.
17044919Sxy150489  */
17054919Sxy150489 s32
e1000_set_d3_lplu_state_generic(struct e1000_hw * hw,bool active)17066735Scc210113 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active)
17074919Sxy150489 {
17084919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
17096735Scc210113 	s32 ret_val = E1000_SUCCESS;
17104919Sxy150489 	u16 data;
17114919Sxy150489 
17124919Sxy150489 	DEBUGFUNC("e1000_set_d3_lplu_state_generic");
17134919Sxy150489 
17146735Scc210113 	if (!(hw->phy.ops.read_reg))
17156735Scc210113 		goto out;
17166735Scc210113 
17176735Scc210113 	ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data);
17184919Sxy150489 	if (ret_val)
17194919Sxy150489 		goto out;
17204919Sxy150489 
17214919Sxy150489 	if (!active) {
17224919Sxy150489 		data &= ~IGP02E1000_PM_D3_LPLU;
17236735Scc210113 		ret_val = phy->ops.write_reg(hw,
17244919Sxy150489 		    IGP02E1000_PHY_POWER_MGMT,
17254919Sxy150489 		    data);
17264919Sxy150489 		if (ret_val)
17274919Sxy150489 			goto out;
17284919Sxy150489 		/*
17294919Sxy150489 		 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
17304919Sxy150489 		 * during Dx states where the power conservation is most
17314919Sxy150489 		 * important.  During driver activity we should enable
17324919Sxy150489 		 * SmartSpeed, so performance is maintained.
17334919Sxy150489 		 */
17344919Sxy150489 		if (phy->smart_speed == e1000_smart_speed_on) {
17356735Scc210113 			ret_val = phy->ops.read_reg(hw,
17364919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
17374919Sxy150489 			    &data);
17384919Sxy150489 			if (ret_val)
17394919Sxy150489 				goto out;
17404919Sxy150489 
17414919Sxy150489 			data |= IGP01E1000_PSCFR_SMART_SPEED;
17426735Scc210113 			ret_val = phy->ops.write_reg(hw,
17434919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
17444919Sxy150489 			    data);
17454919Sxy150489 			if (ret_val)
17464919Sxy150489 				goto out;
17474919Sxy150489 		} else if (phy->smart_speed == e1000_smart_speed_off) {
17486735Scc210113 			ret_val = phy->ops.read_reg(hw,
17494919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
17504919Sxy150489 			    &data);
17514919Sxy150489 			if (ret_val)
17524919Sxy150489 				goto out;
17534919Sxy150489 
17544919Sxy150489 			data &= ~IGP01E1000_PSCFR_SMART_SPEED;
17556735Scc210113 			ret_val = phy->ops.write_reg(hw,
17564919Sxy150489 			    IGP01E1000_PHY_PORT_CONFIG,
17574919Sxy150489 			    data);
17584919Sxy150489 			if (ret_val)
17594919Sxy150489 				goto out;
17604919Sxy150489 		}
17614919Sxy150489 	} else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) ||
17624919Sxy150489 	    (phy->autoneg_advertised == E1000_ALL_NOT_GIG) ||
17634919Sxy150489 	    (phy->autoneg_advertised == E1000_ALL_10_SPEED)) {
17644919Sxy150489 		data |= IGP02E1000_PM_D3_LPLU;
17656735Scc210113 		ret_val = phy->ops.write_reg(hw,
17664919Sxy150489 		    IGP02E1000_PHY_POWER_MGMT,
17674919Sxy150489 		    data);
17684919Sxy150489 		if (ret_val)
17694919Sxy150489 			goto out;
17704919Sxy150489 
17714919Sxy150489 		/* When LPLU is enabled, we should disable SmartSpeed */
17726735Scc210113 		ret_val = phy->ops.read_reg(hw,
17734919Sxy150489 		    IGP01E1000_PHY_PORT_CONFIG,
17744919Sxy150489 		    &data);
17754919Sxy150489 		if (ret_val)
17764919Sxy150489 			goto out;
17774919Sxy150489 
17784919Sxy150489 		data &= ~IGP01E1000_PSCFR_SMART_SPEED;
17796735Scc210113 		ret_val = phy->ops.write_reg(hw,
17804919Sxy150489 		    IGP01E1000_PHY_PORT_CONFIG,
17814919Sxy150489 		    data);
17824919Sxy150489 	}
17834919Sxy150489 
17844919Sxy150489 out:
17854919Sxy150489 	return (ret_val);
17864919Sxy150489 }
17874919Sxy150489 
17884919Sxy150489 /*
17896735Scc210113  * e1000_check_downshift_generic - Checks whether a downshift in speed occurred
17904919Sxy150489  * @hw: pointer to the HW structure
17914919Sxy150489  *
17924919Sxy150489  * Success returns 0, Failure returns 1
17934919Sxy150489  *
17944919Sxy150489  * A downshift is detected by querying the PHY link health.
17954919Sxy150489  */
17964919Sxy150489 s32
e1000_check_downshift_generic(struct e1000_hw * hw)17974919Sxy150489 e1000_check_downshift_generic(struct e1000_hw *hw)
17984919Sxy150489 {
17994919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
18004919Sxy150489 	s32 ret_val;
18014919Sxy150489 	u16 phy_data, offset, mask;
18024919Sxy150489 
18034919Sxy150489 	DEBUGFUNC("e1000_check_downshift_generic");
18044919Sxy150489 
18054919Sxy150489 	switch (phy->type) {
18064919Sxy150489 	case e1000_phy_m88:
18074919Sxy150489 	case e1000_phy_gg82563:
18084919Sxy150489 	case e1000_phy_bm:
180910680SMin.Xu@Sun.COM 	case e1000_phy_82578:
18104919Sxy150489 		offset = M88E1000_PHY_SPEC_STATUS;
18114919Sxy150489 		mask = M88E1000_PSSR_DOWNSHIFT;
18124919Sxy150489 		break;
18134919Sxy150489 	case e1000_phy_igp_2:
18144919Sxy150489 	case e1000_phy_igp:
18154919Sxy150489 	case e1000_phy_igp_3:
18164919Sxy150489 		offset = IGP01E1000_PHY_LINK_HEALTH;
18174919Sxy150489 		mask = IGP01E1000_PLHR_SS_DOWNGRADE;
18184919Sxy150489 		break;
18194919Sxy150489 	default:
18204919Sxy150489 		/* speed downshift not supported */
18217607STed.You@Sun.COM 		phy->speed_downgraded = false;
18224919Sxy150489 		ret_val = E1000_SUCCESS;
18234919Sxy150489 		goto out;
18244919Sxy150489 	}
18254919Sxy150489 
18266735Scc210113 	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
18274919Sxy150489 
18284919Sxy150489 	if (!ret_val)
18297607STed.You@Sun.COM 		phy->speed_downgraded = (phy_data & mask) ? true : false;
18304919Sxy150489 
18314919Sxy150489 out:
18324919Sxy150489 	return (ret_val);
18334919Sxy150489 }
18344919Sxy150489 
18354919Sxy150489 /*
18364919Sxy150489  * e1000_check_polarity_m88 - Checks the polarity.
18374919Sxy150489  * @hw: pointer to the HW structure
18384919Sxy150489  *
18394919Sxy150489  * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
18404919Sxy150489  *
18414919Sxy150489  * Polarity is determined based on the PHY specific status register.
18424919Sxy150489  */
18434919Sxy150489 s32
e1000_check_polarity_m88(struct e1000_hw * hw)18444919Sxy150489 e1000_check_polarity_m88(struct e1000_hw *hw)
18454919Sxy150489 {
18464919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
18474919Sxy150489 	s32 ret_val;
18484919Sxy150489 	u16 data;
18494919Sxy150489 
18504919Sxy150489 	DEBUGFUNC("e1000_check_polarity_m88");
18514919Sxy150489 
18526735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &data);
18534919Sxy150489 
18544919Sxy150489 	if (!ret_val)
18554919Sxy150489 		phy->cable_polarity = (data & M88E1000_PSSR_REV_POLARITY)
18564919Sxy150489 		    ? e1000_rev_polarity_reversed
18574919Sxy150489 		    : e1000_rev_polarity_normal;
18584919Sxy150489 
18594919Sxy150489 	return (ret_val);
18604919Sxy150489 }
18614919Sxy150489 
18624919Sxy150489 /*
18634919Sxy150489  * e1000_check_polarity_igp - Checks the polarity.
18644919Sxy150489  * @hw: pointer to the HW structure
18654919Sxy150489  *
18664919Sxy150489  * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
18674919Sxy150489  *
18684919Sxy150489  * Polarity is determined based on the PHY port status register, and the
18694919Sxy150489  * current speed (since there is no polarity at 100Mbps).
18704919Sxy150489  */
18714919Sxy150489 s32
e1000_check_polarity_igp(struct e1000_hw * hw)18724919Sxy150489 e1000_check_polarity_igp(struct e1000_hw *hw)
18734919Sxy150489 {
18744919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
18754919Sxy150489 	s32 ret_val;
18764919Sxy150489 	u16 data, offset, mask;
18774919Sxy150489 
18784919Sxy150489 	DEBUGFUNC("e1000_check_polarity_igp");
18794919Sxy150489 
18804919Sxy150489 	/*
18814919Sxy150489 	 * Polarity is determined based on the speed of our connection.
18824919Sxy150489 	 */
18836735Scc210113 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data);
18844919Sxy150489 	if (ret_val)
18854919Sxy150489 		goto out;
18864919Sxy150489 
18874919Sxy150489 	if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
18884919Sxy150489 	    IGP01E1000_PSSR_SPEED_1000MBPS) {
18894919Sxy150489 		offset = IGP01E1000_PHY_PCS_INIT_REG;
18904919Sxy150489 		mask = IGP01E1000_PHY_POLARITY_MASK;
18914919Sxy150489 	} else {
18924919Sxy150489 		/*
18934919Sxy150489 		 * This really only applies to 10Mbps since there is no
18944919Sxy150489 		 * polarity for 100Mbps (always 0).
18954919Sxy150489 		 */
18964919Sxy150489 		offset = IGP01E1000_PHY_PORT_STATUS;
18974919Sxy150489 		mask = IGP01E1000_PSSR_POLARITY_REVERSED;
18984919Sxy150489 	}
18994919Sxy150489 
19006735Scc210113 	ret_val = phy->ops.read_reg(hw, offset, &data);
19014919Sxy150489 
19024919Sxy150489 	if (!ret_val)
19034919Sxy150489 		phy->cable_polarity = (data & mask)
19044919Sxy150489 		    ? e1000_rev_polarity_reversed
19054919Sxy150489 		    : e1000_rev_polarity_normal;
19064919Sxy150489 
19074919Sxy150489 out:
19084919Sxy150489 	return (ret_val);
19094919Sxy150489 }
19104919Sxy150489 
19114919Sxy150489 /*
191210680SMin.Xu@Sun.COM  * e1000_check_polarity_ife - Check cable polarity for IFE PHY
191310680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
191410680SMin.Xu@Sun.COM  *
191510680SMin.Xu@Sun.COM  * Polarity is determined on the polarity reversal feature being enabled.
191610680SMin.Xu@Sun.COM  */
191710680SMin.Xu@Sun.COM s32
e1000_check_polarity_ife(struct e1000_hw * hw)191810680SMin.Xu@Sun.COM e1000_check_polarity_ife(struct e1000_hw *hw)
191910680SMin.Xu@Sun.COM {
192010680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
192110680SMin.Xu@Sun.COM 	s32 ret_val;
192210680SMin.Xu@Sun.COM 	u16 phy_data, offset, mask;
192310680SMin.Xu@Sun.COM 
192410680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_check_polarity_ife");
192510680SMin.Xu@Sun.COM 
192610680SMin.Xu@Sun.COM 	/*
192710680SMin.Xu@Sun.COM 	 * Polarity is determined based on the reversal feature being enabled.
192810680SMin.Xu@Sun.COM 	 */
192910680SMin.Xu@Sun.COM 	if (phy->polarity_correction) {
193010680SMin.Xu@Sun.COM 		offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
193110680SMin.Xu@Sun.COM 		mask = IFE_PESC_POLARITY_REVERSED;
193210680SMin.Xu@Sun.COM 	} else {
193310680SMin.Xu@Sun.COM 		offset = IFE_PHY_SPECIAL_CONTROL;
193410680SMin.Xu@Sun.COM 		mask = IFE_PSC_FORCE_POLARITY;
193510680SMin.Xu@Sun.COM 	}
193610680SMin.Xu@Sun.COM 
193710680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
193810680SMin.Xu@Sun.COM 
193910680SMin.Xu@Sun.COM 	if (!ret_val)
194010680SMin.Xu@Sun.COM 		phy->cable_polarity = (phy_data & mask)
194110680SMin.Xu@Sun.COM 		    ? e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
194210680SMin.Xu@Sun.COM 
194310680SMin.Xu@Sun.COM 	return (ret_val);
194410680SMin.Xu@Sun.COM }
194510680SMin.Xu@Sun.COM 
194610680SMin.Xu@Sun.COM /*
19476735Scc210113  * e1000_wait_autoneg_generic - Wait for auto-neg completion
19484919Sxy150489  * @hw: pointer to the HW structure
19494919Sxy150489  *
19504919Sxy150489  * Waits for auto-negotiation to complete or for the auto-negotiation time
19514919Sxy150489  * limit to expire, which ever happens first.
19524919Sxy150489  */
19534919Sxy150489 s32
e1000_wait_autoneg_generic(struct e1000_hw * hw)19544919Sxy150489 e1000_wait_autoneg_generic(struct e1000_hw *hw)
19554919Sxy150489 {
19564919Sxy150489 	s32 ret_val = E1000_SUCCESS;
19574919Sxy150489 	u16 i, phy_status;
19584919Sxy150489 
19594919Sxy150489 	DEBUGFUNC("e1000_wait_autoneg_generic");
19604919Sxy150489 
19616735Scc210113 	if (!(hw->phy.ops.read_reg))
19626735Scc210113 		return (E1000_SUCCESS);
19636735Scc210113 
19644919Sxy150489 	/* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */
19654919Sxy150489 	for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) {
19666735Scc210113 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
19674919Sxy150489 		if (ret_val)
19684919Sxy150489 			break;
19696735Scc210113 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
19704919Sxy150489 		if (ret_val)
19714919Sxy150489 			break;
19724919Sxy150489 		if (phy_status & MII_SR_AUTONEG_COMPLETE)
19734919Sxy150489 			break;
19744919Sxy150489 		msec_delay(100);
19754919Sxy150489 	}
19764919Sxy150489 
19774919Sxy150489 	/*
19784919Sxy150489 	 * PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation has
19794919Sxy150489 	 * completed.
19804919Sxy150489 	 */
19814919Sxy150489 	return (ret_val);
19824919Sxy150489 }
19834919Sxy150489 
19844919Sxy150489 /*
19854919Sxy150489  * e1000_phy_has_link_generic - Polls PHY for link
19864919Sxy150489  * @hw: pointer to the HW structure
19874919Sxy150489  * @iterations: number of times to poll for link
19884919Sxy150489  * @usec_interval: delay between polling attempts
19894919Sxy150489  * @success: pointer to whether polling was successful or not
19904919Sxy150489  *
19914919Sxy150489  * Polls the PHY status register for link, 'iterations' number of times.
19924919Sxy150489  */
19934919Sxy150489 s32
e1000_phy_has_link_generic(struct e1000_hw * hw,u32 iterations,u32 usec_interval,bool * success)19944919Sxy150489 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
19956735Scc210113     u32 usec_interval, bool *success)
19964919Sxy150489 {
19974919Sxy150489 	s32 ret_val = E1000_SUCCESS;
19984919Sxy150489 	u16 i, phy_status;
19994919Sxy150489 
20004919Sxy150489 	DEBUGFUNC("e1000_phy_has_link_generic");
20014919Sxy150489 
20026735Scc210113 	if (!(hw->phy.ops.read_reg))
20036735Scc210113 		return (E1000_SUCCESS);
20046735Scc210113 
20054919Sxy150489 	for (i = 0; i < iterations; i++) {
20064919Sxy150489 		/*
20074919Sxy150489 		 * Some PHYs require the PHY_STATUS register to be read twice
20084919Sxy150489 		 * due to the link bit being sticky.  No harm doing it across
20094919Sxy150489 		 * the board.
20104919Sxy150489 		 */
20116735Scc210113 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
201210680SMin.Xu@Sun.COM 		if (ret_val) {
201310680SMin.Xu@Sun.COM 			/*
201410680SMin.Xu@Sun.COM 			 * If the first read fails, another entity may have
201510680SMin.Xu@Sun.COM 			 * ownership of the resources, wait and try again to
201610680SMin.Xu@Sun.COM 			 * see if they have relinquished the resources yet.
201710680SMin.Xu@Sun.COM 			 */
201810680SMin.Xu@Sun.COM 			usec_delay(usec_interval);
201910680SMin.Xu@Sun.COM 		}
202011020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
20214919Sxy150489 		if (ret_val)
20224919Sxy150489 			break;
20234919Sxy150489 		if (phy_status & MII_SR_LINK_STATUS)
20244919Sxy150489 			break;
20254919Sxy150489 		if (usec_interval >= 1000)
20264919Sxy150489 			msec_delay_irq(usec_interval / 1000);
20274919Sxy150489 		else
20284919Sxy150489 			usec_delay(usec_interval);
20294919Sxy150489 	}
20304919Sxy150489 
20317607STed.You@Sun.COM 	*success = (i < iterations) ? true : false;
20324919Sxy150489 
20334919Sxy150489 	return (ret_val);
20344919Sxy150489 }
20354919Sxy150489 
20364919Sxy150489 /*
20374919Sxy150489  * e1000_get_cable_length_m88 - Determine cable length for m88 PHY
20384919Sxy150489  * @hw: pointer to the HW structure
20394919Sxy150489  *
20404919Sxy150489  * Reads the PHY specific status register to retrieve the cable length
20414919Sxy150489  * information.  The cable length is determined by averaging the minimum and
20424919Sxy150489  * maximum values to get the "average" cable length.  The m88 PHY has four
20434919Sxy150489  * possible cable length values, which are:
20444919Sxy150489  *	Register Value		Cable Length
20454919Sxy150489  *	0			< 50 meters
20464919Sxy150489  *	1			50 - 80 meters
20474919Sxy150489  *	2			80 - 110 meters
20484919Sxy150489  *	3			110 - 140 meters
20494919Sxy150489  *	4			> 140 meters
20504919Sxy150489  */
20514919Sxy150489 s32
e1000_get_cable_length_m88(struct e1000_hw * hw)20524919Sxy150489 e1000_get_cable_length_m88(struct e1000_hw *hw)
20534919Sxy150489 {
20544919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
20554919Sxy150489 	s32 ret_val;
20564919Sxy150489 	u16 phy_data, index;
20574919Sxy150489 
20584919Sxy150489 	DEBUGFUNC("e1000_get_cable_length_m88");
20594919Sxy150489 
20606735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
20614919Sxy150489 	if (ret_val)
20624919Sxy150489 		goto out;
20634919Sxy150489 
20644919Sxy150489 	index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
20654919Sxy150489 	    M88E1000_PSSR_CABLE_LENGTH_SHIFT;
20668479SChenlu.Chen@Sun.COM 
206710680SMin.Xu@Sun.COM 	if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
20688479SChenlu.Chen@Sun.COM 		ret_val = E1000_ERR_PHY;
206910680SMin.Xu@Sun.COM 		goto out;
20708479SChenlu.Chen@Sun.COM 	}
207110680SMin.Xu@Sun.COM 	phy->min_cable_length = e1000_m88_cable_length_table[index];
207210680SMin.Xu@Sun.COM 	phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
207310680SMin.Xu@Sun.COM 
207410680SMin.Xu@Sun.COM 	phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
20754919Sxy150489 
20764919Sxy150489 out:
20774919Sxy150489 	return (ret_val);
20784919Sxy150489 }
20794919Sxy150489 
20804919Sxy150489 /*
20814919Sxy150489  * e1000_get_cable_length_igp_2 - Determine cable length for igp2 PHY
20824919Sxy150489  * @hw: pointer to the HW structure
20834919Sxy150489  *
20844919Sxy150489  * The automatic gain control (agc) normalizes the amplitude of the
20854919Sxy150489  * received signal, adjusting for the attenuation produced by the
20866735Scc210113  * cable.  By reading the AGC registers, which represent the
20876735Scc210113  * combination of coarse and fine gain value, the value can be put
20884919Sxy150489  * into a lookup table to obtain the approximate cable length
20894919Sxy150489  * for each channel.
20904919Sxy150489  */
20914919Sxy150489 s32
e1000_get_cable_length_igp_2(struct e1000_hw * hw)20924919Sxy150489 e1000_get_cable_length_igp_2(struct e1000_hw *hw)
20934919Sxy150489 {
20944919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
20956735Scc210113 	s32 ret_val = E1000_SUCCESS;
20964919Sxy150489 	u16 phy_data, i, agc_value = 0;
20974919Sxy150489 	u16 cur_agc_index, max_agc_index = 0;
20984919Sxy150489 	u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1;
20994919Sxy150489 	u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] =
21004919Sxy150489 		{IGP02E1000_PHY_AGC_A,
21014919Sxy150489 		IGP02E1000_PHY_AGC_B,
21024919Sxy150489 		IGP02E1000_PHY_AGC_C,
21034919Sxy150489 		IGP02E1000_PHY_AGC_D};
21044919Sxy150489 
21054919Sxy150489 	DEBUGFUNC("e1000_get_cable_length_igp_2");
21064919Sxy150489 
21074919Sxy150489 	/* Read the AGC registers for all channels */
21084919Sxy150489 	for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) {
21096735Scc210113 		ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &phy_data);
21104919Sxy150489 		if (ret_val)
21114919Sxy150489 			goto out;
21124919Sxy150489 
21134919Sxy150489 		/*
21144919Sxy150489 		 * Getting bits 15:9, which represent the combination of
21156735Scc210113 		 * coarse and fine gain values.  The result is a number that
21164919Sxy150489 		 * can be put into the lookup table to obtain the approximate
21174919Sxy150489 		 * cable length.
21184919Sxy150489 		 */
21194919Sxy150489 		cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) &
21204919Sxy150489 		    IGP02E1000_AGC_LENGTH_MASK;
21214919Sxy150489 
21224919Sxy150489 		/* Array index bound check. */
21234919Sxy150489 		if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) ||
21244919Sxy150489 		    (cur_agc_index == 0)) {
21254919Sxy150489 			ret_val = -E1000_ERR_PHY;
21264919Sxy150489 			goto out;
21274919Sxy150489 		}
21284919Sxy150489 
21294919Sxy150489 		/* Remove min & max AGC values from calculation. */
21304919Sxy150489 		if (e1000_igp_2_cable_length_table[min_agc_index] >
21314919Sxy150489 		    e1000_igp_2_cable_length_table[cur_agc_index])
21324919Sxy150489 			min_agc_index = cur_agc_index;
21334919Sxy150489 		if (e1000_igp_2_cable_length_table[max_agc_index] <
21344919Sxy150489 		    e1000_igp_2_cable_length_table[cur_agc_index])
21354919Sxy150489 			max_agc_index = cur_agc_index;
21364919Sxy150489 
21374919Sxy150489 		agc_value += e1000_igp_2_cable_length_table[cur_agc_index];
21384919Sxy150489 	}
21394919Sxy150489 
21404919Sxy150489 	agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] +
21414919Sxy150489 	    e1000_igp_2_cable_length_table[max_agc_index]);
21424919Sxy150489 	agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2);
21434919Sxy150489 
21444919Sxy150489 	/* Calculate cable length with the error range of +/- 10 meters. */
21454919Sxy150489 	phy->min_cable_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ?
21464919Sxy150489 	    (agc_value - IGP02E1000_AGC_RANGE) : 0;
21474919Sxy150489 	phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE;
21484919Sxy150489 
21494919Sxy150489 	phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
21504919Sxy150489 
21514919Sxy150489 out:
21524919Sxy150489 	return (ret_val);
21534919Sxy150489 }
21544919Sxy150489 
21554919Sxy150489 /*
21564919Sxy150489  * e1000_get_phy_info_m88 - Retrieve PHY information
21574919Sxy150489  * @hw: pointer to the HW structure
21584919Sxy150489  *
21594919Sxy150489  * Valid for only copper links.  Read the PHY status register (sticky read)
21604919Sxy150489  * to verify that link is up.  Read the PHY special control register to
21614919Sxy150489  * determine the polarity and 10base-T extended distance.  Read the PHY
21624919Sxy150489  * special status register to determine MDI/MDIx and current speed.  If
21634919Sxy150489  * speed is 1000, then determine cable length, local and remote receiver.
21644919Sxy150489  */
21654919Sxy150489 s32
e1000_get_phy_info_m88(struct e1000_hw * hw)21664919Sxy150489 e1000_get_phy_info_m88(struct e1000_hw *hw)
21674919Sxy150489 {
21684919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
21694919Sxy150489 	s32 ret_val;
21704919Sxy150489 	u16 phy_data;
21716735Scc210113 	bool link;
21724919Sxy150489 
21734919Sxy150489 	DEBUGFUNC("e1000_get_phy_info_m88");
21744919Sxy150489 
21756735Scc210113 	if (hw->phy.media_type != e1000_media_type_copper) {
21764919Sxy150489 		DEBUGOUT("Phy info is only valid for copper media\n");
21774919Sxy150489 		ret_val = -E1000_ERR_CONFIG;
21784919Sxy150489 		goto out;
21794919Sxy150489 	}
21804919Sxy150489 
21814919Sxy150489 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
21824919Sxy150489 	if (ret_val)
21834919Sxy150489 		goto out;
21844919Sxy150489 
21854919Sxy150489 	if (!link) {
21864919Sxy150489 		DEBUGOUT("Phy info is only valid if link is up\n");
21874919Sxy150489 		ret_val = -E1000_ERR_CONFIG;
21884919Sxy150489 		goto out;
21894919Sxy150489 	}
21904919Sxy150489 
21916735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
21924919Sxy150489 	if (ret_val)
21934919Sxy150489 		goto out;
21944919Sxy150489 
21954919Sxy150489 	phy->polarity_correction = (phy_data & M88E1000_PSCR_POLARITY_REVERSAL)
21967607STed.You@Sun.COM 	    ? true
21977607STed.You@Sun.COM 	    : false;
21984919Sxy150489 
21994919Sxy150489 	ret_val = e1000_check_polarity_m88(hw);
22004919Sxy150489 	if (ret_val)
22014919Sxy150489 		goto out;
22024919Sxy150489 
22036735Scc210113 	ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
22044919Sxy150489 	if (ret_val)
22054919Sxy150489 		goto out;
22064919Sxy150489 
22077607STed.You@Sun.COM 	phy->is_mdix = (phy_data & M88E1000_PSSR_MDIX) ? true : false;
22084919Sxy150489 
22094919Sxy150489 	if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) {
22107607STed.You@Sun.COM 		ret_val = hw->phy.ops.get_cable_length(hw);
22114919Sxy150489 		if (ret_val)
22124919Sxy150489 			goto out;
22134919Sxy150489 
22146735Scc210113 		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data);
22154919Sxy150489 		if (ret_val)
22164919Sxy150489 			goto out;
22174919Sxy150489 
22184919Sxy150489 		phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS)
22194919Sxy150489 		    ? e1000_1000t_rx_status_ok
22204919Sxy150489 		    : e1000_1000t_rx_status_not_ok;
22214919Sxy150489 
22224919Sxy150489 		phy->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS)
22234919Sxy150489 		    ? e1000_1000t_rx_status_ok
22244919Sxy150489 		    : e1000_1000t_rx_status_not_ok;
22254919Sxy150489 	} else {
22264919Sxy150489 		/* Set values to "undefined" */
22274919Sxy150489 		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
22284919Sxy150489 		phy->local_rx = e1000_1000t_rx_status_undefined;
22294919Sxy150489 		phy->remote_rx = e1000_1000t_rx_status_undefined;
22304919Sxy150489 	}
22314919Sxy150489 
22324919Sxy150489 out:
22334919Sxy150489 	return (ret_val);
22344919Sxy150489 }
22354919Sxy150489 
22364919Sxy150489 /*
22374919Sxy150489  * e1000_get_phy_info_igp - Retrieve igp PHY information
22384919Sxy150489  * @hw: pointer to the HW structure
22394919Sxy150489  *
22404919Sxy150489  * Read PHY status to determine if link is up.  If link is up, then
22414919Sxy150489  * set/determine 10base-T extended distance and polarity correction.  Read
22424919Sxy150489  * PHY port status to determine MDI/MDIx and speed.  Based on the speed,
22434919Sxy150489  * determine on the cable length, local and remote receiver.
22444919Sxy150489  */
22454919Sxy150489 s32
e1000_get_phy_info_igp(struct e1000_hw * hw)22464919Sxy150489 e1000_get_phy_info_igp(struct e1000_hw *hw)
22474919Sxy150489 {
22484919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
22494919Sxy150489 	s32 ret_val;
22504919Sxy150489 	u16 data;
22516735Scc210113 	bool link;
22524919Sxy150489 
22534919Sxy150489 	DEBUGFUNC("e1000_get_phy_info_igp");
22544919Sxy150489 
22554919Sxy150489 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
22564919Sxy150489 	if (ret_val)
22574919Sxy150489 		goto out;
22584919Sxy150489 
22594919Sxy150489 	if (!link) {
22604919Sxy150489 		DEBUGOUT("Phy info is only valid if link is up\n");
22614919Sxy150489 		ret_val = -E1000_ERR_CONFIG;
22624919Sxy150489 		goto out;
22634919Sxy150489 	}
22644919Sxy150489 
22657607STed.You@Sun.COM 	phy->polarity_correction = true;
22664919Sxy150489 
22674919Sxy150489 	ret_val = e1000_check_polarity_igp(hw);
22684919Sxy150489 	if (ret_val)
22694919Sxy150489 		goto out;
22704919Sxy150489 
22716735Scc210113 	ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data);
22724919Sxy150489 	if (ret_val)
22734919Sxy150489 		goto out;
22744919Sxy150489 
22757607STed.You@Sun.COM 	phy->is_mdix = (data & IGP01E1000_PSSR_MDIX) ? true : false;
22764919Sxy150489 
22774919Sxy150489 	if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
22784919Sxy150489 	    IGP01E1000_PSSR_SPEED_1000MBPS) {
22797607STed.You@Sun.COM 		ret_val = hw->phy.ops.get_cable_length(hw);
22804919Sxy150489 		if (ret_val)
22814919Sxy150489 			goto out;
22824919Sxy150489 
22836735Scc210113 		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
22844919Sxy150489 		if (ret_val)
22854919Sxy150489 			goto out;
22864919Sxy150489 
22874919Sxy150489 		phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
22884919Sxy150489 		    ? e1000_1000t_rx_status_ok
22894919Sxy150489 		    : e1000_1000t_rx_status_not_ok;
22904919Sxy150489 
22914919Sxy150489 		phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
22924919Sxy150489 		    ? e1000_1000t_rx_status_ok
22934919Sxy150489 		    : e1000_1000t_rx_status_not_ok;
22944919Sxy150489 	} else {
22954919Sxy150489 		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
22964919Sxy150489 		phy->local_rx = e1000_1000t_rx_status_undefined;
22974919Sxy150489 		phy->remote_rx = e1000_1000t_rx_status_undefined;
22984919Sxy150489 	}
22994919Sxy150489 
23004919Sxy150489 out:
23014919Sxy150489 	return (ret_val);
23024919Sxy150489 }
23034919Sxy150489 
23044919Sxy150489 /*
23054919Sxy150489  * e1000_phy_sw_reset_generic - PHY software reset
23064919Sxy150489  * @hw: pointer to the HW structure
23074919Sxy150489  *
23084919Sxy150489  * Does a software reset of the PHY by reading the PHY control register and
23094919Sxy150489  * setting/write the control register reset bit to the PHY.
23104919Sxy150489  */
23114919Sxy150489 s32
e1000_phy_sw_reset_generic(struct e1000_hw * hw)23124919Sxy150489 e1000_phy_sw_reset_generic(struct e1000_hw *hw)
23134919Sxy150489 {
23146735Scc210113 	s32 ret_val = E1000_SUCCESS;
23154919Sxy150489 	u16 phy_ctrl;
23164919Sxy150489 
23174919Sxy150489 	DEBUGFUNC("e1000_phy_sw_reset_generic");
23184919Sxy150489 
23196735Scc210113 	if (!(hw->phy.ops.read_reg))
23206735Scc210113 		goto out;
23216735Scc210113 
23226735Scc210113 	ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
23234919Sxy150489 	if (ret_val)
23244919Sxy150489 		goto out;
23254919Sxy150489 
23264919Sxy150489 	phy_ctrl |= MII_CR_RESET;
23276735Scc210113 	ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
23284919Sxy150489 	if (ret_val)
23294919Sxy150489 		goto out;
23304919Sxy150489 
23314919Sxy150489 	usec_delay(1);
23324919Sxy150489 
23334919Sxy150489 out:
23344919Sxy150489 	return (ret_val);
23354919Sxy150489 }
23364919Sxy150489 
23374919Sxy150489 /*
23384919Sxy150489  * e1000_phy_hw_reset_generic - PHY hardware reset
23394919Sxy150489  * @hw: pointer to the HW structure
23404919Sxy150489  *
23414919Sxy150489  * Verify the reset block is not blocking us from resetting.  Acquire
23424919Sxy150489  * semaphore (if necessary) and read/set/write the device control reset
23434919Sxy150489  * bit in the PHY.  Wait the appropriate delay time for the device to
23446735Scc210113  * reset and release the semaphore (if necessary).
23454919Sxy150489  */
23464919Sxy150489 s32
e1000_phy_hw_reset_generic(struct e1000_hw * hw)23474919Sxy150489 e1000_phy_hw_reset_generic(struct e1000_hw *hw)
23484919Sxy150489 {
23494919Sxy150489 	struct e1000_phy_info *phy = &hw->phy;
23506735Scc210113 	s32 ret_val = E1000_SUCCESS;
23514919Sxy150489 	u32 ctrl;
23524919Sxy150489 
23534919Sxy150489 	DEBUGFUNC("e1000_phy_hw_reset_generic");
23544919Sxy150489 
23556735Scc210113 	ret_val = phy->ops.check_reset_block(hw);
23564919Sxy150489 	if (ret_val) {
23574919Sxy150489 		ret_val = E1000_SUCCESS;
23584919Sxy150489 		goto out;
23594919Sxy150489 	}
23604919Sxy150489 
23616735Scc210113 	ret_val = phy->ops.acquire(hw);
23624919Sxy150489 	if (ret_val)
23634919Sxy150489 		goto out;
23644919Sxy150489 
23654919Sxy150489 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
23664919Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST);
23674919Sxy150489 	E1000_WRITE_FLUSH(hw);
23684919Sxy150489 
23694919Sxy150489 	usec_delay(phy->reset_delay_us);
23704919Sxy150489 
23714919Sxy150489 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
23724919Sxy150489 	E1000_WRITE_FLUSH(hw);
23734919Sxy150489 
23744919Sxy150489 	usec_delay(150);
23754919Sxy150489 
23766735Scc210113 	phy->ops.release(hw);
23774919Sxy150489 
23786735Scc210113 	ret_val = phy->ops.get_cfg_done(hw);
23794919Sxy150489 
23804919Sxy150489 out:
23814919Sxy150489 	return (ret_val);
23824919Sxy150489 }
23834919Sxy150489 
23844919Sxy150489 /*
23854919Sxy150489  * e1000_get_cfg_done_generic - Generic configuration done
23864919Sxy150489  * @hw: pointer to the HW structure
23874919Sxy150489  *
23884919Sxy150489  * Generic function to wait 10 milli-seconds for configuration to complete
23894919Sxy150489  * and return success.
23904919Sxy150489  */
23914919Sxy150489 s32
e1000_get_cfg_done_generic(struct e1000_hw * hw)23924919Sxy150489 e1000_get_cfg_done_generic(struct e1000_hw *hw)
23934919Sxy150489 {
23944919Sxy150489 	DEBUGFUNC("e1000_get_cfg_done_generic");
23956735Scc210113 	UNREFERENCED_1PARAMETER(hw);
23964919Sxy150489 
23974919Sxy150489 	msec_delay_irq(10);
23984919Sxy150489 
23994919Sxy150489 	return (E1000_SUCCESS);
24004919Sxy150489 }
24014919Sxy150489 
24024919Sxy150489 /*
24034919Sxy150489  * e1000_phy_init_script_igp3 - Inits the IGP3 PHY
24044919Sxy150489  * @hw: pointer to the HW structure
24054919Sxy150489  *
24064919Sxy150489  * Initializes a Intel Gigabit PHY3 when an EEPROM is not present.
24074919Sxy150489  */
24084919Sxy150489 s32
e1000_phy_init_script_igp3(struct e1000_hw * hw)24094919Sxy150489 e1000_phy_init_script_igp3(struct e1000_hw *hw)
24104919Sxy150489 {
24114919Sxy150489 	DEBUGOUT("Running IGP 3 PHY init script\n");
24124919Sxy150489 
24134919Sxy150489 	/* PHY init IGP 3 */
24144919Sxy150489 	/* Enable rise/fall, 10-mode work in class-A */
24156735Scc210113 	hw->phy.ops.write_reg(hw, 0x2F5B, 0x9018);
24164919Sxy150489 	/* Remove all caps from Replica path filter */
24176735Scc210113 	hw->phy.ops.write_reg(hw, 0x2F52, 0x0000);
24184919Sxy150489 	/* Bias trimming for ADC, AFE and Driver (Default) */
24196735Scc210113 	hw->phy.ops.write_reg(hw, 0x2FB1, 0x8B24);
24204919Sxy150489 	/* Increase Hybrid poly bias */
24216735Scc210113 	hw->phy.ops.write_reg(hw, 0x2FB2, 0xF8F0);
24224919Sxy150489 	/* Add 4% to TX amplitude in Giga mode */
24236735Scc210113 	hw->phy.ops.write_reg(hw, 0x2010, 0x10B0);
24244919Sxy150489 	/* Disable trimming (TTT) */
24256735Scc210113 	hw->phy.ops.write_reg(hw, 0x2011, 0x0000);
24264919Sxy150489 	/* Poly DC correction to 94.6% + 2% for all channels */
24276735Scc210113 	hw->phy.ops.write_reg(hw, 0x20DD, 0x249A);
24284919Sxy150489 	/* ABS DC correction to 95.9% */
24296735Scc210113 	hw->phy.ops.write_reg(hw, 0x20DE, 0x00D3);
24304919Sxy150489 	/* BG temp curve trim */
24316735Scc210113 	hw->phy.ops.write_reg(hw, 0x28B4, 0x04CE);
24324919Sxy150489 	/* Increasing ADC OPAMP stage 1 currents to max */
24336735Scc210113 	hw->phy.ops.write_reg(hw, 0x2F70, 0x29E4);
24344919Sxy150489 	/* Force 1000 ( required for enabling PHY regs configuration) */
24356735Scc210113 	hw->phy.ops.write_reg(hw, 0x0000, 0x0140);
24364919Sxy150489 	/* Set upd_freq to 6 */
24376735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F30, 0x1606);
24384919Sxy150489 	/* Disable NPDFE */
24396735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F31, 0xB814);
24404919Sxy150489 	/* Disable adaptive fixed FFE (Default) */
24416735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F35, 0x002A);
24424919Sxy150489 	/* Enable FFE hysteresis */
24436735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F3E, 0x0067);
24444919Sxy150489 	/* Fixed FFE for short cable lengths */
24456735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F54, 0x0065);
24464919Sxy150489 	/* Fixed FFE for medium cable lengths */
24476735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F55, 0x002A);
24484919Sxy150489 	/* Fixed FFE for long cable lengths */
24496735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F56, 0x002A);
24504919Sxy150489 	/* Enable Adaptive Clip Threshold */
24516735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F72, 0x3FB0);
24524919Sxy150489 	/* AHT reset limit to 1 */
24536735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F76, 0xC0FF);
24544919Sxy150489 	/* Set AHT master delay to 127 msec */
24556735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F77, 0x1DEC);
24564919Sxy150489 	/* Set scan bits for AHT */
24576735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F78, 0xF9EF);
24584919Sxy150489 	/* Set AHT Preset bits */
24596735Scc210113 	hw->phy.ops.write_reg(hw, 0x1F79, 0x0210);
24604919Sxy150489 	/* Change integ_factor of channel A to 3 */
24616735Scc210113 	hw->phy.ops.write_reg(hw, 0x1895, 0x0003);
24624919Sxy150489 	/* Change prop_factor of channels BCD to 8 */
24636735Scc210113 	hw->phy.ops.write_reg(hw, 0x1796, 0x0008);
24644919Sxy150489 	/* Change cg_icount + enable integbp for channels BCD */
24656735Scc210113 	hw->phy.ops.write_reg(hw, 0x1798, 0xD008);
24664919Sxy150489 	/*
24674919Sxy150489 	 * Change cg_icount + enable integbp + change prop_factor_master to 8
24684919Sxy150489 	 * for channel A
24694919Sxy150489 	 */
24706735Scc210113 	hw->phy.ops.write_reg(hw, 0x1898, 0xD918);
24714919Sxy150489 	/* Disable AHT in Slave mode on channel A */
24726735Scc210113 	hw->phy.ops.write_reg(hw, 0x187A, 0x0800);
24734919Sxy150489 	/*
24744919Sxy150489 	 * Enable LPLU and disable AN to 1000 in non-D0a states, Enable
24754919Sxy150489 	 * SPD+B2B
24764919Sxy150489 	 */
24776735Scc210113 	hw->phy.ops.write_reg(hw, 0x0019, 0x008D);
24784919Sxy150489 	/* Enable restart AN on an1000_dis change */
24796735Scc210113 	hw->phy.ops.write_reg(hw, 0x001B, 0x2080);
24804919Sxy150489 	/* Enable wh_fifo read clock in 10/100 modes */
24816735Scc210113 	hw->phy.ops.write_reg(hw, 0x0014, 0x0045);
24824919Sxy150489 	/* Restart AN, Speed selection is 1000 */
24836735Scc210113 	hw->phy.ops.write_reg(hw, 0x0000, 0x1340);
24844919Sxy150489 
24854919Sxy150489 	return (E1000_SUCCESS);
24864919Sxy150489 }
24874919Sxy150489 
24884919Sxy150489 /*
24894919Sxy150489  * e1000_get_phy_type_from_id - Get PHY type from id
24904919Sxy150489  * @phy_id: phy_id read from the phy
24914919Sxy150489  *
24924919Sxy150489  * Returns the phy type from the id.
24934919Sxy150489  */
24948479SChenlu.Chen@Sun.COM enum e1000_phy_type
e1000_get_phy_type_from_id(u32 phy_id)24954919Sxy150489 e1000_get_phy_type_from_id(u32 phy_id)
24964919Sxy150489 {
24978479SChenlu.Chen@Sun.COM 	enum e1000_phy_type phy_type = e1000_phy_unknown;
24984919Sxy150489 
24994919Sxy150489 	switch (phy_id) {
25004919Sxy150489 	case M88E1000_I_PHY_ID:
25014919Sxy150489 	case M88E1000_E_PHY_ID:
25024919Sxy150489 	case M88E1111_I_PHY_ID:
25034919Sxy150489 	case M88E1011_I_PHY_ID:
25044919Sxy150489 		phy_type = e1000_phy_m88;
25054919Sxy150489 		break;
25064919Sxy150489 	case IGP01E1000_I_PHY_ID:	/* IGP 1 & 2 share this */
25074919Sxy150489 		phy_type = e1000_phy_igp_2;
25084919Sxy150489 		break;
25094919Sxy150489 	case GG82563_E_PHY_ID:
25104919Sxy150489 		phy_type = e1000_phy_gg82563;
25114919Sxy150489 		break;
25124919Sxy150489 	case IGP03E1000_E_PHY_ID:
25134919Sxy150489 		phy_type = e1000_phy_igp_3;
25144919Sxy150489 		break;
25154919Sxy150489 	case IFE_E_PHY_ID:
25164919Sxy150489 	case IFE_PLUS_E_PHY_ID:
25174919Sxy150489 	case IFE_C_E_PHY_ID:
25184919Sxy150489 		phy_type = e1000_phy_ife;
25194919Sxy150489 		break;
25204919Sxy150489 	case BME1000_E_PHY_ID:
25216735Scc210113 	case BME1000_E_PHY_ID_R2:
25224919Sxy150489 		phy_type = e1000_phy_bm;
25234919Sxy150489 		break;
252410680SMin.Xu@Sun.COM 	case I82578_E_PHY_ID:
252510680SMin.Xu@Sun.COM 		phy_type = e1000_phy_82578;
252610680SMin.Xu@Sun.COM 		break;
252710680SMin.Xu@Sun.COM 	case I82577_E_PHY_ID:
252810680SMin.Xu@Sun.COM 		phy_type = e1000_phy_82577;
252910680SMin.Xu@Sun.COM 		break;
25304919Sxy150489 	default:
25314919Sxy150489 		phy_type = e1000_phy_unknown;
25324919Sxy150489 		break;
25334919Sxy150489 	}
25344919Sxy150489 	return (phy_type);
25354919Sxy150489 }
25364919Sxy150489 
25374919Sxy150489 /*
25384919Sxy150489  * e1000_determine_phy_address - Determines PHY address.
25394919Sxy150489  * @hw: pointer to the HW structure
25404919Sxy150489  *
25414919Sxy150489  * This uses a trial and error method to loop through possible PHY
25424919Sxy150489  * addresses. It tests each by reading the PHY ID registers and
25434919Sxy150489  * checking for a match.
25444919Sxy150489  */
25454919Sxy150489 s32
e1000_determine_phy_address(struct e1000_hw * hw)25464919Sxy150489 e1000_determine_phy_address(struct e1000_hw *hw)
25474919Sxy150489 {
25484919Sxy150489 	s32 ret_val = -E1000_ERR_PHY_TYPE;
25494919Sxy150489 	u32 phy_addr = 0;
25506735Scc210113 	u32 i;
25518479SChenlu.Chen@Sun.COM 	enum e1000_phy_type phy_type = e1000_phy_unknown;
25524919Sxy150489 
255310680SMin.Xu@Sun.COM 	hw->phy.id = phy_type;
255410680SMin.Xu@Sun.COM 
25556735Scc210113 	for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) {
25564919Sxy150489 		hw->phy.addr = phy_addr;
25576735Scc210113 		i = 0;
25586735Scc210113 
25596735Scc210113 		do {
25607426SChenliang.Xu@Sun.COM 			(void) e1000_get_phy_id(hw);
25616735Scc210113 			phy_type = e1000_get_phy_type_from_id(hw->phy.id);
25624919Sxy150489 
25636735Scc210113 			/*
25646735Scc210113 			 * If phy_type is valid, break - we found our
25656735Scc210113 			 * PHY address
25666735Scc210113 			 */
25676735Scc210113 			if (phy_type != e1000_phy_unknown) {
25686735Scc210113 				ret_val = E1000_SUCCESS;
25696735Scc210113 				goto out;
25706735Scc210113 			}
25716735Scc210113 			msec_delay(1);
25726735Scc210113 			i++;
25736735Scc210113 		} while (i < 10);
25744919Sxy150489 	}
25754919Sxy150489 
25766735Scc210113 out:
25774919Sxy150489 	return (ret_val);
25784919Sxy150489 }
25794919Sxy150489 
25804919Sxy150489 /*
25814919Sxy150489  * e1000_get_phy_addr_for_bm_page - Retrieve PHY page address
25824919Sxy150489  * @page: page to access
25834919Sxy150489  *
25844919Sxy150489  * Returns the phy address for the page requested.
25854919Sxy150489  */
25864919Sxy150489 static u32
e1000_get_phy_addr_for_bm_page(u32 page,u32 reg)25874919Sxy150489 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg)
25884919Sxy150489 {
25894919Sxy150489 	u32 phy_addr = 2;
25904919Sxy150489 
25914919Sxy150489 	if ((page >= 768) || (page == 0 && reg == 25) || (reg == 31))
25924919Sxy150489 		phy_addr = 1;
25934919Sxy150489 
25944919Sxy150489 	return (phy_addr);
25954919Sxy150489 }
25964919Sxy150489 
25974919Sxy150489 /*
25984919Sxy150489  * e1000_write_phy_reg_bm - Write BM PHY register
25994919Sxy150489  * @hw: pointer to the HW structure
26004919Sxy150489  * @offset: register offset to write to
26014919Sxy150489  * @data: data to write at register offset
26024919Sxy150489  *
26034919Sxy150489  * Acquires semaphore, if necessary, then writes the data to PHY register
26044919Sxy150489  * at the offset.  Release any acquired semaphores before exiting.
26054919Sxy150489  */
26064919Sxy150489 s32
e1000_write_phy_reg_bm(struct e1000_hw * hw,u32 offset,u16 data)26074919Sxy150489 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
26084919Sxy150489 {
26094919Sxy150489 	s32 ret_val;
26104919Sxy150489 	u32 page_select = 0;
26114919Sxy150489 	u32 page = offset >> IGP_PAGE_SHIFT;
26124919Sxy150489 	u32 page_shift = 0;
26134919Sxy150489 
26144919Sxy150489 	DEBUGFUNC("e1000_write_phy_reg_bm");
26154919Sxy150489 
261611020SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
261711020SMin.Xu@Sun.COM 	if (ret_val)
261811020SMin.Xu@Sun.COM 		goto out;
261911020SMin.Xu@Sun.COM 
26204919Sxy150489 	/* Page 800 works differently than the rest so it has its own func */
26214919Sxy150489 	if (page == BM_WUC_PAGE) {
26224919Sxy150489 		ret_val = e1000_access_phy_wakeup_reg_bm(hw,
26237607STed.You@Sun.COM 		    offset, &data, false);
26244919Sxy150489 		goto out;
26254919Sxy150489 	}
26264919Sxy150489 
26274919Sxy150489 	hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
26284919Sxy150489 
26294919Sxy150489 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
26304919Sxy150489 		/*
26314919Sxy150489 		 * Page select is register 31 for phy address 1 and 22 for phy
26324919Sxy150489 		 * address 2 and 3. Page select is shifted only for phy
26334919Sxy150489 		 * address 1.
26344919Sxy150489 		 */
26354919Sxy150489 		if (hw->phy.addr == 1) {
26364919Sxy150489 			page_shift = IGP_PAGE_SHIFT;
26374919Sxy150489 			page_select = IGP01E1000_PHY_PAGE_SELECT;
26384919Sxy150489 		} else {
26394919Sxy150489 			page_shift = 0;
26404919Sxy150489 			page_select = BM_PHY_PAGE_SELECT;
26414919Sxy150489 		}
26424919Sxy150489 
26434919Sxy150489 		/* Page is shifted left, PHY expects (page x 32) */
26444919Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw, page_select,
26454919Sxy150489 		    (page << page_shift));
264611020SMin.Xu@Sun.COM 		if (ret_val)
26474919Sxy150489 			goto out;
26484919Sxy150489 	}
26494919Sxy150489 
26504919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw,
26514919Sxy150489 	    MAX_PHY_REG_ADDRESS & offset,
26524919Sxy150489 	    data);
26534919Sxy150489 
265411020SMin.Xu@Sun.COM out:
26556735Scc210113 	hw->phy.ops.release(hw);
26564919Sxy150489 	return (ret_val);
26574919Sxy150489 }
26584919Sxy150489 
26594919Sxy150489 /*
26604919Sxy150489  * e1000_read_phy_reg_bm - Read BM PHY register
26614919Sxy150489  * @hw: pointer to the HW structure
26624919Sxy150489  * @offset: register offset to be read
26634919Sxy150489  * @data: pointer to the read data
26644919Sxy150489  *
26654919Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
26664919Sxy150489  * and storing the retrieved information in data.  Release any acquired
26674919Sxy150489  * semaphores before exiting.
26684919Sxy150489  */
26694919Sxy150489 s32
e1000_read_phy_reg_bm(struct e1000_hw * hw,u32 offset,u16 * data)26704919Sxy150489 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
26714919Sxy150489 {
26724919Sxy150489 	s32 ret_val;
26734919Sxy150489 	u32 page_select = 0;
26744919Sxy150489 	u32 page = offset >> IGP_PAGE_SHIFT;
26754919Sxy150489 	u32 page_shift = 0;
26764919Sxy150489 
26776735Scc210113 	DEBUGFUNC("e1000_read_phy_reg_bm");
26784919Sxy150489 
267911020SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
268011020SMin.Xu@Sun.COM 	if (ret_val)
268111020SMin.Xu@Sun.COM 		goto out;
268211020SMin.Xu@Sun.COM 
26834919Sxy150489 	/* Page 800 works differently than the rest so it has its own func */
26844919Sxy150489 	if (page == BM_WUC_PAGE) {
26854919Sxy150489 		ret_val = e1000_access_phy_wakeup_reg_bm(hw,
26867607STed.You@Sun.COM 		    offset, data, true);
26874919Sxy150489 		goto out;
26884919Sxy150489 	}
26894919Sxy150489 
26904919Sxy150489 	hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
26914919Sxy150489 
26924919Sxy150489 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
26934919Sxy150489 		/*
26944919Sxy150489 		 * Page select is register 31 for phy address 1 and 22 for phy
26954919Sxy150489 		 * address 2 and 3. Page select is shifted only for phy
26964919Sxy150489 		 * address 1.
26974919Sxy150489 		 */
26984919Sxy150489 		if (hw->phy.addr == 1) {
26994919Sxy150489 			page_shift = IGP_PAGE_SHIFT;
27004919Sxy150489 			page_select = IGP01E1000_PHY_PAGE_SELECT;
27014919Sxy150489 		} else {
27024919Sxy150489 			page_shift = 0;
27034919Sxy150489 			page_select = BM_PHY_PAGE_SELECT;
27044919Sxy150489 		}
27054919Sxy150489 
27064919Sxy150489 		/* Page is shifted left, PHY expects (page x 32) */
27074919Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw, page_select,
27084919Sxy150489 		    (page << page_shift));
270911020SMin.Xu@Sun.COM 		if (ret_val)
27104919Sxy150489 			goto out;
27114919Sxy150489 	}
27124919Sxy150489 
27134919Sxy150489 	ret_val = e1000_read_phy_reg_mdic(hw,
27144919Sxy150489 	    MAX_PHY_REG_ADDRESS & offset,
27154919Sxy150489 	    data);
27164919Sxy150489 
27174919Sxy150489 out:
271811020SMin.Xu@Sun.COM 	hw->phy.ops.release(hw);
27194919Sxy150489 	return (ret_val);
27204919Sxy150489 }
27214919Sxy150489 
27224919Sxy150489 /*
27237607STed.You@Sun.COM  * e1000_read_phy_reg_bm2 - Read BM PHY register
27247607STed.You@Sun.COM  * @hw: pointer to the HW structure
27257607STed.You@Sun.COM  * @offset: register offset to be read
27267607STed.You@Sun.COM  * @data: pointer to the read data
27277607STed.You@Sun.COM  *
27287607STed.You@Sun.COM  * Acquires semaphore, if necessary, then reads the PHY register at offset
27297607STed.You@Sun.COM  * and storing the retrieved information in data.  Release any acquired
27307607STed.You@Sun.COM  * semaphores before exiting.
27317607STed.You@Sun.COM  */
27327607STed.You@Sun.COM s32
e1000_read_phy_reg_bm2(struct e1000_hw * hw,u32 offset,u16 * data)27337607STed.You@Sun.COM e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
27347607STed.You@Sun.COM {
27357607STed.You@Sun.COM 	s32 ret_val;
27367607STed.You@Sun.COM 	u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
27377607STed.You@Sun.COM 
27387607STed.You@Sun.COM 	DEBUGFUNC("e1000_write_phy_reg_bm2");
27397607STed.You@Sun.COM 
274011020SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
274111020SMin.Xu@Sun.COM 	if (ret_val)
274211020SMin.Xu@Sun.COM 		goto out;
274311020SMin.Xu@Sun.COM 
27447607STed.You@Sun.COM 	/* Page 800 works differently than the rest so it has its own func */
27457607STed.You@Sun.COM 	if (page == BM_WUC_PAGE) {
27467607STed.You@Sun.COM 		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
27477607STed.You@Sun.COM 		    true);
27487607STed.You@Sun.COM 		goto out;
27497607STed.You@Sun.COM 	}
27507607STed.You@Sun.COM 
27517607STed.You@Sun.COM 	hw->phy.addr = 1;
27527607STed.You@Sun.COM 
27537607STed.You@Sun.COM 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
27547607STed.You@Sun.COM 
27557607STed.You@Sun.COM 		/* Page is shifted left, PHY expects (page x 32) */
27567607STed.You@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
27577607STed.You@Sun.COM 		    page);
27587607STed.You@Sun.COM 
275911020SMin.Xu@Sun.COM 		if (ret_val)
27607607STed.You@Sun.COM 			goto out;
27617607STed.You@Sun.COM 	}
27627607STed.You@Sun.COM 
27637607STed.You@Sun.COM 	ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
27647607STed.You@Sun.COM 	    data);
27657607STed.You@Sun.COM 
27667607STed.You@Sun.COM out:
276711020SMin.Xu@Sun.COM 	hw->phy.ops.release(hw);
27687607STed.You@Sun.COM 	return (ret_val);
27697607STed.You@Sun.COM }
27707607STed.You@Sun.COM 
27717607STed.You@Sun.COM /*
27727607STed.You@Sun.COM  * e1000_write_phy_reg_bm2 - Write BM PHY register
27737607STed.You@Sun.COM  * @hw: pointer to the HW structure
27747607STed.You@Sun.COM  * @offset: register offset to write to
27757607STed.You@Sun.COM  * @data: data to write at register offset
27767607STed.You@Sun.COM  *
27777607STed.You@Sun.COM  * Acquires semaphore, if necessary, then writes the data to PHY register
27787607STed.You@Sun.COM  * at the offset.  Release any acquired semaphores before exiting.
27797607STed.You@Sun.COM  */
27807607STed.You@Sun.COM s32
e1000_write_phy_reg_bm2(struct e1000_hw * hw,u32 offset,u16 data)27817607STed.You@Sun.COM e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
27827607STed.You@Sun.COM {
27837607STed.You@Sun.COM 	s32 ret_val;
27847607STed.You@Sun.COM 	u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
27857607STed.You@Sun.COM 
27867607STed.You@Sun.COM 	DEBUGFUNC("e1000_write_phy_reg_bm2");
27877607STed.You@Sun.COM 
278811020SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.acquire(hw);
278911020SMin.Xu@Sun.COM 	if (ret_val)
279011020SMin.Xu@Sun.COM 		goto out;
279111020SMin.Xu@Sun.COM 
27927607STed.You@Sun.COM 	/* Page 800 works differently than the rest so it has its own func */
27937607STed.You@Sun.COM 	if (page == BM_WUC_PAGE) {
27947607STed.You@Sun.COM 		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
27957607STed.You@Sun.COM 		    false);
27967607STed.You@Sun.COM 		goto out;
27977607STed.You@Sun.COM 	}
27987607STed.You@Sun.COM 
27997607STed.You@Sun.COM 	hw->phy.addr = 1;
28007607STed.You@Sun.COM 
28017607STed.You@Sun.COM 	if (offset > MAX_PHY_MULTI_PAGE_REG) {
28027607STed.You@Sun.COM 		/* Page is shifted left, PHY expects (page x 32) */
28037607STed.You@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
28047607STed.You@Sun.COM 		    page);
28057607STed.You@Sun.COM 
280611020SMin.Xu@Sun.COM 		if (ret_val)
28077607STed.You@Sun.COM 			goto out;
28087607STed.You@Sun.COM 	}
28097607STed.You@Sun.COM 
28107607STed.You@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
28117607STed.You@Sun.COM 	    data);
28127607STed.You@Sun.COM 
281311020SMin.Xu@Sun.COM out:
28147607STed.You@Sun.COM 	hw->phy.ops.release(hw);
28157607STed.You@Sun.COM 	return (ret_val);
28167607STed.You@Sun.COM }
28177607STed.You@Sun.COM 
28187607STed.You@Sun.COM /*
28194919Sxy150489  * e1000_access_phy_wakeup_reg_bm - Read BM PHY wakeup register
28204919Sxy150489  * @hw: pointer to the HW structure
28214919Sxy150489  * @offset: register offset to be read or written
28224919Sxy150489  * @data: pointer to the data to read or write
28234919Sxy150489  * @read: determines if operation is read or write
28244919Sxy150489  *
28254919Sxy150489  * Acquires semaphore, if necessary, then reads the PHY register at offset
28264919Sxy150489  * and storing the retrieved information in data.  Release any acquired
28274919Sxy150489  * semaphores before exiting. Note that procedure to read the wakeup
28284919Sxy150489  * registers are different. It works as such:
28294919Sxy150489  * 1) Set page 769, register 17, bit 2 = 1
28304919Sxy150489  * 2) Set page to 800 for host (801 if we were manageability)
28314919Sxy150489  * 3) Write the address using the address opcode (0x11)
28324919Sxy150489  * 4) Read or write the data using the data opcode (0x12)
28334919Sxy150489  * 5) Restore 769_17.2 to its original value
283411020SMin.Xu@Sun.COM  *
283511020SMin.Xu@Sun.COM  * Assumes semaphore already acquired.
28364919Sxy150489  */
28378479SChenlu.Chen@Sun.COM static s32
e1000_access_phy_wakeup_reg_bm(struct e1000_hw * hw,u32 offset,u16 * data,bool read)28384919Sxy150489 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw,
28396735Scc210113     u32 offset, u16 *data, bool read)
28404919Sxy150489 {
28414919Sxy150489 	s32 ret_val;
284210680SMin.Xu@Sun.COM 	u16 reg = BM_PHY_REG_NUM(offset);
28434919Sxy150489 	u16 phy_reg = 0;
28444919Sxy150489 
284510680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_access_phy_wakeup_reg_bm");
284610680SMin.Xu@Sun.COM 
284710680SMin.Xu@Sun.COM 	/* Gig must be disabled for MDIO accesses to page 800 */
284810680SMin.Xu@Sun.COM 	if ((hw->mac.type == e1000_pchlan) &&
284910680SMin.Xu@Sun.COM 	    (!(E1000_READ_REG(hw, E1000_PHY_CTRL) &
2850*11143SGuoqing.Zhu@Sun.COM 	    E1000_PHY_CTRL_GBE_DISABLE))) {
2851*11143SGuoqing.Zhu@Sun.COM 		/* EMPTY */
285210680SMin.Xu@Sun.COM 		DEBUGOUT("Attempting to access page 800 while gig enabled.\n");
2853*11143SGuoqing.Zhu@Sun.COM 	}
28544919Sxy150489 
28554919Sxy150489 	/* All operations in this function are phy address 1 */
28564919Sxy150489 	hw->phy.addr = 1;
28574919Sxy150489 
28584919Sxy150489 	/* Set page 769 */
28597426SChenliang.Xu@Sun.COM 	(void) e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
28604919Sxy150489 	    (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
28614919Sxy150489 
28624919Sxy150489 	ret_val = e1000_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg);
28634919Sxy150489 	if (ret_val) {
28646735Scc210113 		DEBUGOUT("Could not read PHY page 769\n");
28654919Sxy150489 		goto out;
28664919Sxy150489 	}
28674919Sxy150489 
28684919Sxy150489 	/* First clear bit 4 to avoid a power state change */
28694919Sxy150489 	phy_reg &= ~(BM_WUC_HOST_WU_BIT);
28704919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
28714919Sxy150489 	if (ret_val) {
28726735Scc210113 		DEBUGOUT("Could not clear PHY page 769 bit 4\n");
28734919Sxy150489 		goto out;
28744919Sxy150489 	}
28754919Sxy150489 
28764919Sxy150489 	/* Write bit 2 = 1, and clear bit 4 to 769_17 */
28774919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG,
28784919Sxy150489 	    phy_reg | BM_WUC_ENABLE_BIT);
28794919Sxy150489 	if (ret_val) {
28806735Scc210113 		DEBUGOUT("Could not write PHY page 769 bit 2\n");
28814919Sxy150489 		goto out;
28824919Sxy150489 	}
28834919Sxy150489 
28844919Sxy150489 	/* Select page 800 */
28854919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw,
28864919Sxy150489 	    IGP01E1000_PHY_PAGE_SELECT,
28874919Sxy150489 	    (BM_WUC_PAGE << IGP_PAGE_SHIFT));
28884919Sxy150489 
28894919Sxy150489 	/* Write the page 800 offset value using opcode 0x11 */
28904919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg);
28914919Sxy150489 	if (ret_val) {
28926735Scc210113 		DEBUGOUT("Could not write address opcode to page 800\n");
28934919Sxy150489 		goto out;
28944919Sxy150489 	}
28954919Sxy150489 
28964919Sxy150489 	if (read) {
28974919Sxy150489 		/* Read the page 800 value using opcode 0x12 */
28984919Sxy150489 		ret_val = e1000_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
28994919Sxy150489 		    data);
29004919Sxy150489 	} else {
290110680SMin.Xu@Sun.COM 		/* Write the page 800 value using opcode 0x12 */
29024919Sxy150489 		ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
29034919Sxy150489 		    *data);
29044919Sxy150489 	}
29054919Sxy150489 
29064919Sxy150489 	if (ret_val) {
290710680SMin.Xu@Sun.COM 		DEBUGOUT("Could not access data value from page 800\n");
29084919Sxy150489 		goto out;
29094919Sxy150489 	}
29104919Sxy150489 
29114919Sxy150489 	/*
29124919Sxy150489 	 * Restore 769_17.2 to its original value Set page 769
29134919Sxy150489 	 */
29147426SChenliang.Xu@Sun.COM 	(void) e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
29154919Sxy150489 	    (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
29164919Sxy150489 
29174919Sxy150489 	/* Clear 769_17.2 */
29184919Sxy150489 	ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
29194919Sxy150489 	if (ret_val) {
29206735Scc210113 		DEBUGOUT("Could not clear PHY page 769 bit 2\n");
29214919Sxy150489 		goto out;
29224919Sxy150489 	}
29234919Sxy150489 
29244919Sxy150489 out:
29254919Sxy150489 	return (ret_val);
29264919Sxy150489 }
29276735Scc210113 
29286735Scc210113 /*
29296735Scc210113  * e1000_power_up_phy_copper - Restore copper link in case of PHY power down
29306735Scc210113  * @hw: pointer to the HW structure
29316735Scc210113  *
29326735Scc210113  * In the case of a PHY power down to save power, or to turn off link during a
29336735Scc210113  * driver unload, or wake on lan is not enabled, restore the link to previous
29346735Scc210113  * settings.
29356735Scc210113  */
29366735Scc210113 void
e1000_power_up_phy_copper(struct e1000_hw * hw)29376735Scc210113 e1000_power_up_phy_copper(struct e1000_hw *hw)
29386735Scc210113 {
29396735Scc210113 	u16 mii_reg = 0;
29406735Scc210113 
29416735Scc210113 	/* The PHY will retain its settings across a power down/up cycle */
29426735Scc210113 	hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
29436735Scc210113 	mii_reg &= ~MII_CR_POWER_DOWN;
29446735Scc210113 	hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
29456735Scc210113 }
29466735Scc210113 
29476735Scc210113 /*
29486735Scc210113  * e1000_power_down_phy_copper - Restore copper link in case of PHY power down
29496735Scc210113  * @hw: pointer to the HW structure
29506735Scc210113  *
29516735Scc210113  * In the case of a PHY power down to save power, or to turn off link during a
29526735Scc210113  * driver unload, or wake on lan is not enabled, restore the link to previous
29536735Scc210113  * settings.
29546735Scc210113  */
29556735Scc210113 void
e1000_power_down_phy_copper(struct e1000_hw * hw)29566735Scc210113 e1000_power_down_phy_copper(struct e1000_hw *hw)
29576735Scc210113 {
29586735Scc210113 	u16 mii_reg = 0;
29596735Scc210113 
29606735Scc210113 	/* The PHY will retain its settings across a power down/up cycle */
29616735Scc210113 	hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
29626735Scc210113 	mii_reg |= MII_CR_POWER_DOWN;
29636735Scc210113 	hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
29646735Scc210113 	msec_delay(1);
29656735Scc210113 }
296610680SMin.Xu@Sun.COM 
296711020SMin.Xu@Sun.COM /*
296811020SMin.Xu@Sun.COM  * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
296911020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
297011020SMin.Xu@Sun.COM  * @slow: true for slow mode, false for normal mode
297111020SMin.Xu@Sun.COM  *
297211020SMin.Xu@Sun.COM  * Assumes semaphore already acquired.
297311020SMin.Xu@Sun.COM  */
297410680SMin.Xu@Sun.COM s32
e1000_set_mdio_slow_mode_hv(struct e1000_hw * hw,bool slow)297510680SMin.Xu@Sun.COM e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
297610680SMin.Xu@Sun.COM {
297710680SMin.Xu@Sun.COM 	s32 ret_val = E1000_SUCCESS;
297810680SMin.Xu@Sun.COM 	u16 data = 0;
297910680SMin.Xu@Sun.COM 
298010680SMin.Xu@Sun.COM 	/* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
298110680SMin.Xu@Sun.COM 	hw->phy.addr = 1;
298210680SMin.Xu@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
298310680SMin.Xu@Sun.COM 	    (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
298410680SMin.Xu@Sun.COM 	if (ret_val)
298510680SMin.Xu@Sun.COM 		goto out;
298610680SMin.Xu@Sun.COM 
298710680SMin.Xu@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, BM_CS_CTRL1,
298810680SMin.Xu@Sun.COM 	    (0x2180 | (slow << 10)));
298910680SMin.Xu@Sun.COM 
299010680SMin.Xu@Sun.COM 	if (ret_val)
299110680SMin.Xu@Sun.COM 		goto out;
299210680SMin.Xu@Sun.COM 
299310680SMin.Xu@Sun.COM 	/* dummy read when reverting to fast mode - throw away result */
299410680SMin.Xu@Sun.COM 	if (!slow)
299510680SMin.Xu@Sun.COM 		ret_val = e1000_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
299610680SMin.Xu@Sun.COM 
299710680SMin.Xu@Sun.COM out:
299810680SMin.Xu@Sun.COM 	return (ret_val);
299910680SMin.Xu@Sun.COM }
300010680SMin.Xu@Sun.COM 
300110680SMin.Xu@Sun.COM /*
300211020SMin.Xu@Sun.COM  * __e1000_read_phy_reg_hv -  Read HV PHY register
300310680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
300410680SMin.Xu@Sun.COM  * @offset: register offset to be read
300510680SMin.Xu@Sun.COM  * @data: pointer to the read data
300611020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
300710680SMin.Xu@Sun.COM  *
300810680SMin.Xu@Sun.COM  * Acquires semaphore, if necessary, then reads the PHY register at offset
300911020SMin.Xu@Sun.COM  * and stores the retrieved information in data.  Release any acquired
301010680SMin.Xu@Sun.COM  * semaphore before exiting.
301110680SMin.Xu@Sun.COM  */
301211020SMin.Xu@Sun.COM static s32
__e1000_read_phy_reg_hv(struct e1000_hw * hw,u32 offset,u16 * data,bool locked)301311020SMin.Xu@Sun.COM __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
301411020SMin.Xu@Sun.COM     bool locked)
301510680SMin.Xu@Sun.COM {
301610680SMin.Xu@Sun.COM 	s32 ret_val;
301710680SMin.Xu@Sun.COM 	u16 page = BM_PHY_REG_PAGE(offset);
301810680SMin.Xu@Sun.COM 	u16 reg = BM_PHY_REG_NUM(offset);
301910680SMin.Xu@Sun.COM 	bool in_slow_mode = false;
302010680SMin.Xu@Sun.COM 
302110680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_read_phy_reg_hv");
302210680SMin.Xu@Sun.COM 
302311020SMin.Xu@Sun.COM 	if (!locked) {
302411020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
302511020SMin.Xu@Sun.COM 		if (ret_val)
302611020SMin.Xu@Sun.COM 			return (ret_val);
302711020SMin.Xu@Sun.COM 	}
302811020SMin.Xu@Sun.COM 
302910680SMin.Xu@Sun.COM 	/* Workaround failure in MDIO access while cable is disconnected */
303010680SMin.Xu@Sun.COM 	if ((hw->phy.type == e1000_phy_82577) &&
303110680SMin.Xu@Sun.COM 	    !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
303210680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
303310680SMin.Xu@Sun.COM 		if (ret_val)
303410680SMin.Xu@Sun.COM 			goto out;
303510680SMin.Xu@Sun.COM 
303610680SMin.Xu@Sun.COM 		in_slow_mode = true;
303710680SMin.Xu@Sun.COM 	}
303810680SMin.Xu@Sun.COM 
303910680SMin.Xu@Sun.COM 	/* Page 800 works differently than the rest so it has its own func */
304010680SMin.Xu@Sun.COM 	if (page == BM_WUC_PAGE) {
304110680SMin.Xu@Sun.COM 		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
304210680SMin.Xu@Sun.COM 		    data, true);
304310680SMin.Xu@Sun.COM 		goto out;
304410680SMin.Xu@Sun.COM 	}
304510680SMin.Xu@Sun.COM 
304610680SMin.Xu@Sun.COM 	if (page > 0 && page < HV_INTC_FC_PAGE_START) {
304710680SMin.Xu@Sun.COM 		ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
304810680SMin.Xu@Sun.COM 		    data, true);
304910680SMin.Xu@Sun.COM 		goto out;
305010680SMin.Xu@Sun.COM 	}
305110680SMin.Xu@Sun.COM 
305210680SMin.Xu@Sun.COM 	hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
305310680SMin.Xu@Sun.COM 
305410680SMin.Xu@Sun.COM 	if (page == HV_INTC_FC_PAGE_START)
305510680SMin.Xu@Sun.COM 		page = 0;
305610680SMin.Xu@Sun.COM 
305710680SMin.Xu@Sun.COM 	if (reg > MAX_PHY_MULTI_PAGE_REG) {
305810680SMin.Xu@Sun.COM 		u32 phy_addr = hw->phy.addr;
305911020SMin.Xu@Sun.COM 
306010680SMin.Xu@Sun.COM 		hw->phy.addr = 1;
306110680SMin.Xu@Sun.COM 
306210680SMin.Xu@Sun.COM 		/* Page is shifted left, PHY expects (page x 32) */
306310680SMin.Xu@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw,
306411020SMin.Xu@Sun.COM 		    IGP01E1000_PHY_PAGE_SELECT, (page << IGP_PAGE_SHIFT));
306510680SMin.Xu@Sun.COM 		hw->phy.addr = phy_addr;
306610680SMin.Xu@Sun.COM 	}
306710680SMin.Xu@Sun.COM 
306811020SMin.Xu@Sun.COM 	ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg, data);
306910680SMin.Xu@Sun.COM out:
307010680SMin.Xu@Sun.COM 	/* Revert to MDIO fast mode, if applicable */
307110680SMin.Xu@Sun.COM 	if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
307210680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
307310680SMin.Xu@Sun.COM 
307411020SMin.Xu@Sun.COM 	if (!locked)
307511020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
307611020SMin.Xu@Sun.COM 
307710680SMin.Xu@Sun.COM 	return (ret_val);
307810680SMin.Xu@Sun.COM }
307910680SMin.Xu@Sun.COM 
308010680SMin.Xu@Sun.COM /*
308111020SMin.Xu@Sun.COM  * e1000_read_phy_reg_hv -  Read HV PHY register
308211020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
308311020SMin.Xu@Sun.COM  * @offset: register offset to be read
308411020SMin.Xu@Sun.COM  * @data: pointer to the read data
308511020SMin.Xu@Sun.COM  *
308611020SMin.Xu@Sun.COM  * Acquires semaphore then reads the PHY register at offset and stores
308711020SMin.Xu@Sun.COM  * the retrieved information in data.  Release the acquired semaphore
308811020SMin.Xu@Sun.COM  * before exiting.
308911020SMin.Xu@Sun.COM  */
309011020SMin.Xu@Sun.COM s32
e1000_read_phy_reg_hv(struct e1000_hw * hw,u32 offset,u16 * data)309111020SMin.Xu@Sun.COM e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
309211020SMin.Xu@Sun.COM {
309311020SMin.Xu@Sun.COM 	return (__e1000_read_phy_reg_hv(hw, offset, data, false));
309411020SMin.Xu@Sun.COM }
309511020SMin.Xu@Sun.COM 
309611020SMin.Xu@Sun.COM /*
309711020SMin.Xu@Sun.COM  * e1000_read_phy_reg_hv_locked -  Read HV PHY register
309811020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
309911020SMin.Xu@Sun.COM  * @offset: register offset to be read
310011020SMin.Xu@Sun.COM  * @data: pointer to the read data
310111020SMin.Xu@Sun.COM  *
310211020SMin.Xu@Sun.COM  * Reads the PHY register at offset and stores the retrieved information
310311020SMin.Xu@Sun.COM  * in data.  Assumes semaphore already acquired.
310411020SMin.Xu@Sun.COM  */
310511020SMin.Xu@Sun.COM s32
e1000_read_phy_reg_hv_locked(struct e1000_hw * hw,u32 offset,u16 * data)310611020SMin.Xu@Sun.COM e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data)
310711020SMin.Xu@Sun.COM {
310811020SMin.Xu@Sun.COM 	return (__e1000_read_phy_reg_hv(hw, offset, data, true));
310911020SMin.Xu@Sun.COM }
311011020SMin.Xu@Sun.COM 
311111020SMin.Xu@Sun.COM /*
311211020SMin.Xu@Sun.COM  * __e1000_write_phy_reg_hv - Write HV PHY register
311310680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
311410680SMin.Xu@Sun.COM  * @offset: register offset to write to
311510680SMin.Xu@Sun.COM  * @data: data to write at register offset
311611020SMin.Xu@Sun.COM  * @locked: semaphore has already been acquired or not
311710680SMin.Xu@Sun.COM  *
311810680SMin.Xu@Sun.COM  * Acquires semaphore, if necessary, then writes the data to PHY register
311910680SMin.Xu@Sun.COM  * at the offset.  Release any acquired semaphores before exiting.
312010680SMin.Xu@Sun.COM  */
312111020SMin.Xu@Sun.COM static s32
__e1000_write_phy_reg_hv(struct e1000_hw * hw,u32 offset,u16 data,bool locked)312211020SMin.Xu@Sun.COM __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
312311020SMin.Xu@Sun.COM     bool locked)
312410680SMin.Xu@Sun.COM {
312510680SMin.Xu@Sun.COM 	s32 ret_val;
312610680SMin.Xu@Sun.COM 	u16 page = BM_PHY_REG_PAGE(offset);
312710680SMin.Xu@Sun.COM 	u16 reg = BM_PHY_REG_NUM(offset);
312810680SMin.Xu@Sun.COM 	bool in_slow_mode = false;
312911020SMin.Xu@Sun.COM 	struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
313010680SMin.Xu@Sun.COM 
313110680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_write_phy_reg_hv");
313210680SMin.Xu@Sun.COM 
313311020SMin.Xu@Sun.COM 	if (!locked) {
313411020SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.acquire(hw);
313511020SMin.Xu@Sun.COM 		if (ret_val)
313611020SMin.Xu@Sun.COM 			return (ret_val);
313711020SMin.Xu@Sun.COM 	}
313811020SMin.Xu@Sun.COM 
313910680SMin.Xu@Sun.COM 	/* Workaround failure in MDIO access while cable is disconnected */
314010680SMin.Xu@Sun.COM 	if ((hw->phy.type == e1000_phy_82577) &&
314110680SMin.Xu@Sun.COM 	    !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
314210680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
314310680SMin.Xu@Sun.COM 		if (ret_val)
314410680SMin.Xu@Sun.COM 			goto out;
314510680SMin.Xu@Sun.COM 
314610680SMin.Xu@Sun.COM 		in_slow_mode = true;
314710680SMin.Xu@Sun.COM 	}
314810680SMin.Xu@Sun.COM 
314910680SMin.Xu@Sun.COM 	/* Page 800 works differently than the rest so it has its own func */
315010680SMin.Xu@Sun.COM 	if (page == BM_WUC_PAGE) {
315110680SMin.Xu@Sun.COM 		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
315210680SMin.Xu@Sun.COM 		    &data, false);
315310680SMin.Xu@Sun.COM 		goto out;
315410680SMin.Xu@Sun.COM 	}
315510680SMin.Xu@Sun.COM 
315610680SMin.Xu@Sun.COM 	if (page > 0 && page < HV_INTC_FC_PAGE_START) {
315710680SMin.Xu@Sun.COM 		ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
315810680SMin.Xu@Sun.COM 		    &data, false);
315910680SMin.Xu@Sun.COM 		goto out;
316010680SMin.Xu@Sun.COM 	}
316110680SMin.Xu@Sun.COM 
316211020SMin.Xu@Sun.COM 	/* The LCD Config workaround provides the phy address to use */
316311020SMin.Xu@Sun.COM 	if (dev_spec->nvm_lcd_config_enabled == false)
316411020SMin.Xu@Sun.COM 		hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
316510680SMin.Xu@Sun.COM 
316610680SMin.Xu@Sun.COM 	if (page == HV_INTC_FC_PAGE_START)
316710680SMin.Xu@Sun.COM 		page = 0;
316810680SMin.Xu@Sun.COM 
316910680SMin.Xu@Sun.COM 	/*
317010680SMin.Xu@Sun.COM 	 * Workaround MDIO accesses being disabled after entering IEEE Power
317110680SMin.Xu@Sun.COM 	 * Down (whenever bit 11 of the PHY Control register is set)
317210680SMin.Xu@Sun.COM 	 */
317310680SMin.Xu@Sun.COM 	if ((hw->phy.type == e1000_phy_82578) &&
317410680SMin.Xu@Sun.COM 	    (hw->phy.revision >= 1) &&
317510680SMin.Xu@Sun.COM 	    (hw->phy.addr == 2) &&
317610680SMin.Xu@Sun.COM 	    ((MAX_PHY_REG_ADDRESS & reg) == 0) &&
317710680SMin.Xu@Sun.COM 	    (data & (1 << 11))) {
317810680SMin.Xu@Sun.COM 		u16 data2 = 0x7EFF;
317910680SMin.Xu@Sun.COM 		ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
318010680SMin.Xu@Sun.COM 		    &data2, false);
318110680SMin.Xu@Sun.COM 		if (ret_val)
318210680SMin.Xu@Sun.COM 			goto out;
318310680SMin.Xu@Sun.COM 	}
318410680SMin.Xu@Sun.COM 
318510680SMin.Xu@Sun.COM 	if (reg > MAX_PHY_MULTI_PAGE_REG) {
318610680SMin.Xu@Sun.COM 		u32 phy_addr = hw->phy.addr;
318711020SMin.Xu@Sun.COM 
318810680SMin.Xu@Sun.COM 		hw->phy.addr = 1;
318910680SMin.Xu@Sun.COM 
319010680SMin.Xu@Sun.COM 		/* Page is shifted left, PHY expects (page x 32) */
319110680SMin.Xu@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw,
319211020SMin.Xu@Sun.COM 		    IGP01E1000_PHY_PAGE_SELECT, (page << IGP_PAGE_SHIFT));
319310680SMin.Xu@Sun.COM 		hw->phy.addr = phy_addr;
319410680SMin.Xu@Sun.COM 	}
319510680SMin.Xu@Sun.COM 
319610680SMin.Xu@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
319710680SMin.Xu@Sun.COM 	    data);
319810680SMin.Xu@Sun.COM 
319910680SMin.Xu@Sun.COM out:
320010680SMin.Xu@Sun.COM 	/* Revert to MDIO fast mode, if applicable */
320110680SMin.Xu@Sun.COM 	if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
320210680SMin.Xu@Sun.COM 		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
320310680SMin.Xu@Sun.COM 
320411020SMin.Xu@Sun.COM 	if (!locked)
320511020SMin.Xu@Sun.COM 		hw->phy.ops.release(hw);
320611020SMin.Xu@Sun.COM 
320710680SMin.Xu@Sun.COM 	return (ret_val);
320810680SMin.Xu@Sun.COM }
320910680SMin.Xu@Sun.COM 
321010680SMin.Xu@Sun.COM /*
321111020SMin.Xu@Sun.COM  * e1000_write_phy_reg_hv - Write HV PHY register
321211020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
321311020SMin.Xu@Sun.COM  * @offset: register offset to write to
321411020SMin.Xu@Sun.COM  * @data: data to write at register offset
321511020SMin.Xu@Sun.COM  *
321611020SMin.Xu@Sun.COM  * Acquires semaphore then writes the data to PHY register at the offset.
321711020SMin.Xu@Sun.COM  * Release the acquired semaphores before exiting.
321811020SMin.Xu@Sun.COM  */
321911020SMin.Xu@Sun.COM s32
e1000_write_phy_reg_hv(struct e1000_hw * hw,u32 offset,u16 data)322011020SMin.Xu@Sun.COM e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
322111020SMin.Xu@Sun.COM {
322211020SMin.Xu@Sun.COM 	return (__e1000_write_phy_reg_hv(hw, offset, data, false));
322311020SMin.Xu@Sun.COM }
322411020SMin.Xu@Sun.COM 
322511020SMin.Xu@Sun.COM /*
322611020SMin.Xu@Sun.COM  * e1000_write_phy_reg_hv_locked - Write HV PHY register
322711020SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
322811020SMin.Xu@Sun.COM  * @offset: register offset to write to
322911020SMin.Xu@Sun.COM  * @data: data to write at register offset
323011020SMin.Xu@Sun.COM  *
323111020SMin.Xu@Sun.COM  * Writes the data to PHY register at the offset.  Assumes semaphore
323211020SMin.Xu@Sun.COM  * already acquired.
323311020SMin.Xu@Sun.COM  */
323411020SMin.Xu@Sun.COM s32
e1000_write_phy_reg_hv_locked(struct e1000_hw * hw,u32 offset,u16 data)323511020SMin.Xu@Sun.COM e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data)
323611020SMin.Xu@Sun.COM {
323711020SMin.Xu@Sun.COM 	return (__e1000_write_phy_reg_hv(hw, offset, data, true));
323811020SMin.Xu@Sun.COM }
323911020SMin.Xu@Sun.COM 
324011020SMin.Xu@Sun.COM /*
324110680SMin.Xu@Sun.COM  * e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
324210680SMin.Xu@Sun.COM  * @page: page to be accessed
324310680SMin.Xu@Sun.COM  */
324410680SMin.Xu@Sun.COM static u32
e1000_get_phy_addr_for_hv_page(u32 page)324510680SMin.Xu@Sun.COM e1000_get_phy_addr_for_hv_page(u32 page)
324610680SMin.Xu@Sun.COM {
324710680SMin.Xu@Sun.COM 	u32 phy_addr = 2;
324810680SMin.Xu@Sun.COM 
324910680SMin.Xu@Sun.COM 	if (page >= HV_INTC_FC_PAGE_START)
325010680SMin.Xu@Sun.COM 		phy_addr = 1;
325110680SMin.Xu@Sun.COM 
325210680SMin.Xu@Sun.COM 	return (phy_addr);
325310680SMin.Xu@Sun.COM }
325410680SMin.Xu@Sun.COM 
325510680SMin.Xu@Sun.COM /*
325610680SMin.Xu@Sun.COM  * e1000_access_phy_debug_regs_hv - Read HV PHY vendor specific high registers
325710680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
325810680SMin.Xu@Sun.COM  * @offset: register offset to be read or written
325910680SMin.Xu@Sun.COM  * @data: pointer to the data to be read or written
326010680SMin.Xu@Sun.COM  * @read: determines if operation is read or written
326110680SMin.Xu@Sun.COM  *
326211020SMin.Xu@Sun.COM  * Reads the PHY register at offset and stores the retreived information
326311020SMin.Xu@Sun.COM  * in data.  Assumes semaphore already acquired.  Note that the procedure
326411020SMin.Xu@Sun.COM  * to read these regs uses the address port and data port to read/write.
326510680SMin.Xu@Sun.COM  */
326610680SMin.Xu@Sun.COM static s32
e1000_access_phy_debug_regs_hv(struct e1000_hw * hw,u32 offset,u16 * data,bool read)326710680SMin.Xu@Sun.COM e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
326810680SMin.Xu@Sun.COM     u16 *data, bool read)
326910680SMin.Xu@Sun.COM {
327010680SMin.Xu@Sun.COM 	s32 ret_val;
327110680SMin.Xu@Sun.COM 	u32 addr_reg = 0;
327210680SMin.Xu@Sun.COM 	u32 data_reg = 0;
327310680SMin.Xu@Sun.COM 
327410680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_access_phy_debug_regs_hv");
327510680SMin.Xu@Sun.COM 
327610680SMin.Xu@Sun.COM 	/* This takes care of the difference with desktop vs mobile phy */
327710680SMin.Xu@Sun.COM 	addr_reg = (hw->phy.type == e1000_phy_82578) ?
327810680SMin.Xu@Sun.COM 	    I82578_ADDR_REG : I82577_ADDR_REG;
327910680SMin.Xu@Sun.COM 	data_reg = addr_reg + 1;
328010680SMin.Xu@Sun.COM 
328110680SMin.Xu@Sun.COM 	/* All operations in this function are phy address 2 */
328210680SMin.Xu@Sun.COM 	hw->phy.addr = 2;
328310680SMin.Xu@Sun.COM 
328410680SMin.Xu@Sun.COM 	/* masking with 0x3F to remove the page from offset */
328510680SMin.Xu@Sun.COM 	ret_val = e1000_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F);
328610680SMin.Xu@Sun.COM 	if (ret_val) {
328710680SMin.Xu@Sun.COM 		DEBUGOUT("Could not write PHY the HV address register\n");
328810680SMin.Xu@Sun.COM 		goto out;
328910680SMin.Xu@Sun.COM 	}
329010680SMin.Xu@Sun.COM 
329110680SMin.Xu@Sun.COM 	/* Read or write the data value next */
329210680SMin.Xu@Sun.COM 	if (read)
329310680SMin.Xu@Sun.COM 		ret_val = e1000_read_phy_reg_mdic(hw, data_reg, data);
329410680SMin.Xu@Sun.COM 	else
329510680SMin.Xu@Sun.COM 		ret_val = e1000_write_phy_reg_mdic(hw, data_reg, *data);
329610680SMin.Xu@Sun.COM 
329710680SMin.Xu@Sun.COM 	if (ret_val) {
329810680SMin.Xu@Sun.COM 		DEBUGOUT("Could not read data value from HV data register\n");
329910680SMin.Xu@Sun.COM 		goto out;
330010680SMin.Xu@Sun.COM 	}
330110680SMin.Xu@Sun.COM 
330210680SMin.Xu@Sun.COM out:
330310680SMin.Xu@Sun.COM 	return (ret_val);
330410680SMin.Xu@Sun.COM }
330510680SMin.Xu@Sun.COM 
330610680SMin.Xu@Sun.COM /*
330710680SMin.Xu@Sun.COM  * e1000_link_stall_workaround_hv - Si workaround
330810680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
330910680SMin.Xu@Sun.COM  *
331010680SMin.Xu@Sun.COM  * This function works around a Si bug where the link partner can get
331110680SMin.Xu@Sun.COM  * a link up indication before the PHY does.  If small packets are sent
331210680SMin.Xu@Sun.COM  * by the link partner they can be placed in the packet buffer without
331310680SMin.Xu@Sun.COM  * being properly accounted for by the PHY and will stall preventing
331410680SMin.Xu@Sun.COM  * further packets from being received.  The workaround is to clear the
331510680SMin.Xu@Sun.COM  * packet buffer after the PHY detects link up.
331610680SMin.Xu@Sun.COM  */
331710680SMin.Xu@Sun.COM s32
e1000_link_stall_workaround_hv(struct e1000_hw * hw)331810680SMin.Xu@Sun.COM e1000_link_stall_workaround_hv(struct e1000_hw *hw)
331910680SMin.Xu@Sun.COM {
332010680SMin.Xu@Sun.COM 	s32 ret_val = E1000_SUCCESS;
332110680SMin.Xu@Sun.COM 	u16 data;
332210680SMin.Xu@Sun.COM 
332310680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_link_stall_workaround_hv");
332410680SMin.Xu@Sun.COM 
332510680SMin.Xu@Sun.COM 	if (hw->phy.type != e1000_phy_82578)
332610680SMin.Xu@Sun.COM 		goto out;
332710680SMin.Xu@Sun.COM 
332810680SMin.Xu@Sun.COM 	/* Do not apply workaround if in PHY loopback bit 14 set */
332910680SMin.Xu@Sun.COM 	hw->phy.ops.read_reg(hw, PHY_CONTROL, &data);
333010680SMin.Xu@Sun.COM 	if (data & PHY_CONTROL_LB)
333110680SMin.Xu@Sun.COM 		goto out;
333210680SMin.Xu@Sun.COM 
333310680SMin.Xu@Sun.COM 	/* check if link is up and at 1Gbps */
333410680SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data);
333510680SMin.Xu@Sun.COM 	if (ret_val)
333610680SMin.Xu@Sun.COM 		goto out;
333710680SMin.Xu@Sun.COM 
333810680SMin.Xu@Sun.COM 	data &= BM_CS_STATUS_LINK_UP |
333910680SMin.Xu@Sun.COM 	    BM_CS_STATUS_RESOLVED |
334010680SMin.Xu@Sun.COM 	    BM_CS_STATUS_SPEED_MASK;
334110680SMin.Xu@Sun.COM 
334210680SMin.Xu@Sun.COM 	if (data != (BM_CS_STATUS_LINK_UP |
334310680SMin.Xu@Sun.COM 	    BM_CS_STATUS_RESOLVED |
334410680SMin.Xu@Sun.COM 	    BM_CS_STATUS_SPEED_1000))
334510680SMin.Xu@Sun.COM 		goto out;
334610680SMin.Xu@Sun.COM 
334710680SMin.Xu@Sun.COM 	msec_delay(200);
334810680SMin.Xu@Sun.COM 
334910680SMin.Xu@Sun.COM 	/* flush the packets in the fifo buffer */
335010680SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
335110680SMin.Xu@Sun.COM 	    HV_MUX_DATA_CTRL_GEN_TO_MAC | HV_MUX_DATA_CTRL_FORCE_SPEED);
335210680SMin.Xu@Sun.COM 	if (ret_val)
335310680SMin.Xu@Sun.COM 		goto out;
335410680SMin.Xu@Sun.COM 
335510680SMin.Xu@Sun.COM 	ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
335610680SMin.Xu@Sun.COM 	    HV_MUX_DATA_CTRL_GEN_TO_MAC);
335710680SMin.Xu@Sun.COM 
335810680SMin.Xu@Sun.COM out:
335910680SMin.Xu@Sun.COM 	return (ret_val);
336010680SMin.Xu@Sun.COM }
336110680SMin.Xu@Sun.COM 
336210680SMin.Xu@Sun.COM /*
336310680SMin.Xu@Sun.COM  * e1000_check_polarity_82577 - Checks the polarity.
336410680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
336510680SMin.Xu@Sun.COM  *
336610680SMin.Xu@Sun.COM  * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
336710680SMin.Xu@Sun.COM  *
336810680SMin.Xu@Sun.COM  * Polarity is determined based on the PHY specific status register.
336910680SMin.Xu@Sun.COM  */
337010680SMin.Xu@Sun.COM s32
e1000_check_polarity_82577(struct e1000_hw * hw)337110680SMin.Xu@Sun.COM e1000_check_polarity_82577(struct e1000_hw *hw)
337210680SMin.Xu@Sun.COM {
337310680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
337410680SMin.Xu@Sun.COM 	s32 ret_val;
337510680SMin.Xu@Sun.COM 	u16 data;
337610680SMin.Xu@Sun.COM 
337710680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_check_polarity_82577");
337810680SMin.Xu@Sun.COM 
337910680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
338010680SMin.Xu@Sun.COM 
338110680SMin.Xu@Sun.COM 	if (!ret_val)
338210680SMin.Xu@Sun.COM 		phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY)
338310680SMin.Xu@Sun.COM 		    ? e1000_rev_polarity_reversed
338410680SMin.Xu@Sun.COM 		    : e1000_rev_polarity_normal;
338510680SMin.Xu@Sun.COM 
338610680SMin.Xu@Sun.COM 	return (ret_val);
338710680SMin.Xu@Sun.COM }
338810680SMin.Xu@Sun.COM 
338910680SMin.Xu@Sun.COM /*
339010680SMin.Xu@Sun.COM  * e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY
339110680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
339210680SMin.Xu@Sun.COM  *
339310680SMin.Xu@Sun.COM  * Calls the PHY setup function to force speed and duplex.  Clears the
339410680SMin.Xu@Sun.COM  * auto-crossover to force MDI manually.  Waits for link and returns
339510680SMin.Xu@Sun.COM  * successful if link up is successful, else -E1000_ERR_PHY (-2).
339610680SMin.Xu@Sun.COM  */
339710680SMin.Xu@Sun.COM s32
e1000_phy_force_speed_duplex_82577(struct e1000_hw * hw)339810680SMin.Xu@Sun.COM e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
339910680SMin.Xu@Sun.COM {
340010680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
340110680SMin.Xu@Sun.COM 	s32 ret_val;
340210680SMin.Xu@Sun.COM 	u16 phy_data;
340310680SMin.Xu@Sun.COM 	bool link;
340410680SMin.Xu@Sun.COM 
340510680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_phy_force_speed_duplex_82577");
340610680SMin.Xu@Sun.COM 
340710680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
340810680SMin.Xu@Sun.COM 	if (ret_val)
340910680SMin.Xu@Sun.COM 		goto out;
341010680SMin.Xu@Sun.COM 
341110680SMin.Xu@Sun.COM 	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
341210680SMin.Xu@Sun.COM 
341310680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
341410680SMin.Xu@Sun.COM 	if (ret_val)
341510680SMin.Xu@Sun.COM 		goto out;
341610680SMin.Xu@Sun.COM 
341710680SMin.Xu@Sun.COM 	/*
341810680SMin.Xu@Sun.COM 	 * Clear Auto-Crossover to force MDI manually.  82577 requires MDI
341910680SMin.Xu@Sun.COM 	 * forced whenever speed and duplex are forced.
342010680SMin.Xu@Sun.COM 	 */
342110680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data);
342210680SMin.Xu@Sun.COM 	if (ret_val)
342310680SMin.Xu@Sun.COM 		goto out;
342410680SMin.Xu@Sun.COM 
342510680SMin.Xu@Sun.COM 	phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX;
342610680SMin.Xu@Sun.COM 	phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX;
342710680SMin.Xu@Sun.COM 
342810680SMin.Xu@Sun.COM 	ret_val = phy->ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data);
342910680SMin.Xu@Sun.COM 	if (ret_val)
343010680SMin.Xu@Sun.COM 		goto out;
343110680SMin.Xu@Sun.COM 
343210680SMin.Xu@Sun.COM 	DEBUGOUT1("I82577_PHY_CTRL_2: %X\n", phy_data);
343310680SMin.Xu@Sun.COM 
343410680SMin.Xu@Sun.COM 	usec_delay(1);
343510680SMin.Xu@Sun.COM 
343610680SMin.Xu@Sun.COM 	if (phy->autoneg_wait_to_complete) {
343710680SMin.Xu@Sun.COM 		DEBUGOUT("Waiting for forced speed/duplex link on 82577 phy\n");
343810680SMin.Xu@Sun.COM 
343910680SMin.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
344010680SMin.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
344110680SMin.Xu@Sun.COM 		if (ret_val)
344210680SMin.Xu@Sun.COM 			goto out;
344310680SMin.Xu@Sun.COM 
3444*11143SGuoqing.Zhu@Sun.COM 		if (!link) {
3445*11143SGuoqing.Zhu@Sun.COM 			/* EMPTY */
344610680SMin.Xu@Sun.COM 			DEBUGOUT("Link taking longer than expected.\n");
3447*11143SGuoqing.Zhu@Sun.COM 		}
344810680SMin.Xu@Sun.COM 
344910680SMin.Xu@Sun.COM 		/* Try once more */
345010680SMin.Xu@Sun.COM 		ret_val = e1000_phy_has_link_generic(hw,
345110680SMin.Xu@Sun.COM 		    PHY_FORCE_LIMIT, 100000, &link);
345210680SMin.Xu@Sun.COM 		if (ret_val)
345310680SMin.Xu@Sun.COM 			goto out;
345410680SMin.Xu@Sun.COM 	}
345510680SMin.Xu@Sun.COM 
345610680SMin.Xu@Sun.COM out:
345710680SMin.Xu@Sun.COM 	return (ret_val);
345810680SMin.Xu@Sun.COM }
345910680SMin.Xu@Sun.COM 
346010680SMin.Xu@Sun.COM /*
346110680SMin.Xu@Sun.COM  * e1000_get_phy_info_82577 - Retrieve I82577 PHY information
346210680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
346310680SMin.Xu@Sun.COM  *
346410680SMin.Xu@Sun.COM  * Read PHY status to determine if link is up.  If link is up, then
346510680SMin.Xu@Sun.COM  * set/determine 10base-T extended distance and polarity correction.  Read
346610680SMin.Xu@Sun.COM  * PHY port status to determine MDI/MDIx and speed.  Based on the speed,
346710680SMin.Xu@Sun.COM  * determine on the cable length, local and remote receiver.
346810680SMin.Xu@Sun.COM  */
346910680SMin.Xu@Sun.COM s32
e1000_get_phy_info_82577(struct e1000_hw * hw)347010680SMin.Xu@Sun.COM e1000_get_phy_info_82577(struct e1000_hw *hw)
347110680SMin.Xu@Sun.COM {
347210680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
347310680SMin.Xu@Sun.COM 	s32 ret_val;
347410680SMin.Xu@Sun.COM 	u16 data;
347510680SMin.Xu@Sun.COM 	bool link;
347610680SMin.Xu@Sun.COM 
347710680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_get_phy_info_82577");
347810680SMin.Xu@Sun.COM 
347910680SMin.Xu@Sun.COM 	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
348010680SMin.Xu@Sun.COM 	if (ret_val)
348110680SMin.Xu@Sun.COM 		goto out;
348210680SMin.Xu@Sun.COM 
348310680SMin.Xu@Sun.COM 	if (!link) {
348410680SMin.Xu@Sun.COM 		DEBUGOUT("Phy info is only valid if link is up\n");
348510680SMin.Xu@Sun.COM 		ret_val = -E1000_ERR_CONFIG;
348610680SMin.Xu@Sun.COM 		goto out;
348710680SMin.Xu@Sun.COM 	}
348810680SMin.Xu@Sun.COM 
348910680SMin.Xu@Sun.COM 	phy->polarity_correction = true;
349010680SMin.Xu@Sun.COM 
349110680SMin.Xu@Sun.COM 	ret_val = e1000_check_polarity_82577(hw);
349210680SMin.Xu@Sun.COM 	if (ret_val)
349310680SMin.Xu@Sun.COM 		goto out;
349410680SMin.Xu@Sun.COM 
349510680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
349610680SMin.Xu@Sun.COM 	if (ret_val)
349710680SMin.Xu@Sun.COM 		goto out;
349810680SMin.Xu@Sun.COM 
349910680SMin.Xu@Sun.COM 	phy->is_mdix = (data & I82577_PHY_STATUS2_MDIX) ? true : false;
350010680SMin.Xu@Sun.COM 
350110680SMin.Xu@Sun.COM 	if ((data & I82577_PHY_STATUS2_SPEED_MASK) ==
350210680SMin.Xu@Sun.COM 	    I82577_PHY_STATUS2_SPEED_1000MBPS) {
350310680SMin.Xu@Sun.COM 		ret_val = hw->phy.ops.get_cable_length(hw);
350410680SMin.Xu@Sun.COM 		if (ret_val)
350510680SMin.Xu@Sun.COM 			goto out;
350610680SMin.Xu@Sun.COM 
350710680SMin.Xu@Sun.COM 		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
350810680SMin.Xu@Sun.COM 		if (ret_val)
350910680SMin.Xu@Sun.COM 			goto out;
351010680SMin.Xu@Sun.COM 
351110680SMin.Xu@Sun.COM 		phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
351210680SMin.Xu@Sun.COM 		    ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
351310680SMin.Xu@Sun.COM 
351410680SMin.Xu@Sun.COM 		phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
351510680SMin.Xu@Sun.COM 		    ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
351610680SMin.Xu@Sun.COM 	} else {
351710680SMin.Xu@Sun.COM 		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
351810680SMin.Xu@Sun.COM 		phy->local_rx = e1000_1000t_rx_status_undefined;
351910680SMin.Xu@Sun.COM 		phy->remote_rx = e1000_1000t_rx_status_undefined;
352010680SMin.Xu@Sun.COM 	}
352110680SMin.Xu@Sun.COM 
352210680SMin.Xu@Sun.COM out:
352310680SMin.Xu@Sun.COM 	return (ret_val);
352410680SMin.Xu@Sun.COM }
352510680SMin.Xu@Sun.COM 
352610680SMin.Xu@Sun.COM /*
352710680SMin.Xu@Sun.COM  * e1000_get_cable_length_82577 - Determine cable length for 82577 PHY
352810680SMin.Xu@Sun.COM  * @hw: pointer to the HW structure
352910680SMin.Xu@Sun.COM  *
353010680SMin.Xu@Sun.COM  * Reads the diagnostic status register and verifies result is valid before
353110680SMin.Xu@Sun.COM  * placing it in the phy_cable_length field.
353210680SMin.Xu@Sun.COM  */
353310680SMin.Xu@Sun.COM s32
e1000_get_cable_length_82577(struct e1000_hw * hw)353410680SMin.Xu@Sun.COM e1000_get_cable_length_82577(struct e1000_hw *hw)
353510680SMin.Xu@Sun.COM {
353610680SMin.Xu@Sun.COM 	struct e1000_phy_info *phy = &hw->phy;
353710680SMin.Xu@Sun.COM 	s32 ret_val;
353810680SMin.Xu@Sun.COM 	u16 phy_data, length;
353910680SMin.Xu@Sun.COM 
354010680SMin.Xu@Sun.COM 	DEBUGFUNC("e1000_get_cable_length_82577");
354110680SMin.Xu@Sun.COM 
354210680SMin.Xu@Sun.COM 	ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data);
354310680SMin.Xu@Sun.COM 	if (ret_val)
354410680SMin.Xu@Sun.COM 		goto out;
354510680SMin.Xu@Sun.COM 
354610680SMin.Xu@Sun.COM 	length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >>
354710680SMin.Xu@Sun.COM 	    I82577_DSTATUS_CABLE_LENGTH_SHIFT;
354810680SMin.Xu@Sun.COM 
354910680SMin.Xu@Sun.COM 	if (length == E1000_CABLE_LENGTH_UNDEFINED)
355010680SMin.Xu@Sun.COM 		ret_val = E1000_ERR_PHY;
355110680SMin.Xu@Sun.COM 
355210680SMin.Xu@Sun.COM 	phy->cable_length = length;
355310680SMin.Xu@Sun.COM 
355410680SMin.Xu@Sun.COM out:
355510680SMin.Xu@Sun.COM 	return (ret_val);
355610680SMin.Xu@Sun.COM }
3557