/***************************************************************************** GPL LICENSE SUMMARY Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called LICENSE.GPL. Contact Information: Intel Corporation version: Embedded.Release.Patch.L.1.0.7-5 Contact Information: Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 *****************************************************************************/ /************************************************************************** * @ingroup OEM_PHY_GENERAL * * @file oem_phy.c * * @description * This module contains oem functions. * **************************************************************************/ #include "e1000_oem_phy.h" #include "e1000.h" #include "e1000_hw.h" #include "gcu_if.h" /* * List of functions leveraged from the base e1000 driver. * * Ideally, it would have been nice to keep e1000_oem_phy.c * minimally dependent on the e1000. Any function taking * a struct e1000_hw as a parameter can be implemented in * this file. It was chosen to reuse as much code as possible * to save time (isn't that always the case ;) */ extern int e1000_up(struct e1000_adapter *adapter); extern void e1000_down(struct e1000_adapter *adapter); extern void e1000_reset(struct e1000_adapter *adapter); extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx); extern int32_t e1000_copper_link_autoneg(struct e1000_hw *hw); extern int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw); extern int32_t e1000_copper_link_postconfig(struct e1000_hw *hw); extern int32_t e1000_oem_set_trans_gasket(struct e1000_hw *hw); /* forward declarations for static support functions */ static int32_t e1000_oem_link_m88_setup(struct e1000_hw *hw); static int32_t e1000_oem_link_v8211_setup(struct e1000_hw *hw); static int32_t e1000_oem_link_v8601_setup(struct e1000_hw *hw); static int32_t e1000_oem_set_phy_mode(struct e1000_hw *hw); static int32_t e1000_oem_detect_phy(struct e1000_hw *hw); #define NON_PHY_PORT 0xFFFFFFFF #define SILVERTIP_BC5860 #define DEBUGFUNC1(F, B...) DEBUGOUT1(F, B) /** * e1000_oem_setup_link * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * Performs OEM Transceiver specific link setup as part of the * global e1000_setup_link() function. **/ int32_t e1000_oem_setup_link(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO /* * see e1000_setup_copper_link() as the primary example. Look at both * the M88 and IGP functions that are called for ideas, possibly for * power management. */ int32_t ret_val; uint32_t ctrl; uint16_t i; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* AFU: add test to exit out if improper phy type */ /* relevent parts of e1000_copper_link_preconfig */ ctrl = E1000_READ_REG(hw, CTRL); ctrl |= E1000_CTRL_SLU; ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); E1000_WRITE_REG(hw, CTRL, ctrl); /* this is required for *hw init */ ret_val = e1000_oem_detect_phy(hw); if (ret_val) return ret_val; ret_val = e1000_oem_set_phy_mode(hw); if (ret_val) return ret_val; switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_link_m88_setup(hw); if (ret_val) return ret_val; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_link_v8211_setup(hw); if (ret_val) return ret_val; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_link_v8601_setup(hw); if (ret_val) return ret_val; break; case NON_PHY_PORT: hw->icp_xxxx_is_link_up = true; /* Reture for skipping the latter blocks about autoneg and link status checking */ return E1000_SUCCESS; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } if (hw->autoneg) { ret_val = e1000_copper_link_autoneg(hw); if (ret_val) return ret_val; } else { DEBUGOUT("Forcing speed and duplex\n"); ret_val = e1000_phy_force_speed_duplex(hw); } /* * Check link status. Wait up to 100 microseconds for link to become * valid. */ for (i = 0; i < 0xa; i++) { ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register PHY_STATUS\n"); return ret_val; } ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register PHY_STATUS\n"); return ret_val; } hw->icp_xxxx_is_link_up = (phy_data & MII_SR_LINK_STATUS) != 0; if (phy_data & MII_SR_LINK_STATUS) { /* Config the MAC and PHY after link is up */ ret_val = e1000_copper_link_postconfig(hw); if (ret_val) return ret_val; DEBUGOUT("Valid link established!!!\n"); return E1000_SUCCESS; } udelay(0xa); } DEBUGOUT("Unable to establish link!!!\n"); return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ DEBUGOUT("Invalid value for hw->media_type"); return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_link_m88_setup * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * lifted from e1000_copper_link_mgp_setup, pretty much * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset **/ static int32_t e1000_oem_link_m88_setup(struct e1000_hw *hw) { int32_t ret_val; uint16_t phy_data = 0; DEBUGFUNC1("%s", __func__); if (!hw) return -1; ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); phy_data |= 0x00000008; ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); /* phy_reset_disable is set in e1000_oem_set_phy_mode */ if (hw->phy_reset_disable) return E1000_SUCCESS; /* Enable CRS on TX. This must be set for half-duplex operation. */ ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL register\n"); return ret_val; } phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX; /* * Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode * 2 - MDI-X mode * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) */ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; switch (hw->mdix) { case 0x1: phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; break; case 0x2: phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; break; case 0x3: phy_data |= M88E1000_PSCR_AUTO_X_1000T; break; case 0: default: phy_data |= M88E1000_PSCR_AUTO_X_MODE; break; } /* * Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled * 1 - Enabled */ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; if (hw->disable_polarity_correction == 1) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL register\n"); return ret_val; } /* * Force TX_CLK in the Extended PHY Specific Control Register * to 25MHz clock. */ ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read M88E1000_EXT_PHY_SPEC_CTRL register\n"); return ret_val; } /* * For Truxton, it is necessary to add RGMII tx and rx * timing delay though the EXT_PHY_SPEC_CTRL register */ phy_data |= M88E1000_EPSCR_TX_TIME_CTRL; phy_data |= M88E1000_EPSCR_RX_TIME_CTRL; if (hw->phy_revision < M88E1011_I_REV_4) { phy_data |= M88E1000_EPSCR_TX_CLK_25; /* Configure Master and Slave downshift values */ phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); } ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) { DEBUGOUT ("Unable to read M88E1000_EXT_PHY_SPEC_CTRL register\n"); return ret_val; } /* SW Reset the PHY so all changes take effect */ ret_val = e1000_phy_hw_reset(hw); if (ret_val) { DEBUGOUT("Error Resetting the PHY\n"); return ret_val; } return E1000_SUCCESS; } /** * e1000_oem_link_v8211_setup * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * lifted from e1000_copper_link_mgp_setup, pretty much * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset **/ static int32_t e1000_oem_link_v8211_setup(struct e1000_hw *hw) { int32_t ret_val; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* phy_reset_disable is set in e1000_oem_set_phy_mode */ if (hw->phy_reset_disable) return E1000_SUCCESS; /* * Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled * 1 - Enabled */ ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read VSC8211_BYPASS_CTRL register\n"); return ret_val; } if (hw->disable_polarity_correction) phy_data |= VSC8211_BYPASS_POLAR_INVERS_DISABLE; else phy_data &= ~VSC8211_BYPASS_POLAR_INVERS_DISABLE; e1000_oem_write_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write VSC8211_BYPASS_CTRL register\n"); return ret_val; } /* Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode * 2 - MDI-X mode * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) */ switch (hw->mdix) { default: break; } /* SW Reset the PHY so all changes take effect */ ret_val = e1000_phy_hw_reset(hw); if (ret_val) { DEBUGOUT("Error Resetting the PHY\n"); return ret_val; } return E1000_SUCCESS; } /** * e1000_oem_link_v8601_setup * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * lifted from e1000_copper_link_mgp_setup, pretty much * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset **/ static int32_t e1000_oem_link_v8601_setup(struct e1000_hw *hw) { int32_t ret_val; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* phy_reset_disable is set in e1000_oem_set_phy_mode */ if (hw->phy_reset_disable) return E1000_SUCCESS; /* * Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled * 1 - Enabled */ ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read VSC8601_BYPASS_CTRL register\n"); return ret_val; } if (hw->disable_polarity_correction) phy_data |= VSC8601_BYPASS_POLAR_INVERS_DISABLE; else phy_data &= ~VSC8601_BYPASS_POLAR_INVERS_DISABLE; e1000_oem_write_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write VSC8601_BYPASS_CTRL register\n"); return ret_val; } /* Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode * 2 - MDI-X mode * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) */ switch (hw->mdix) { default: break; } /* SW Reset the PHY so all changes take effect */ ret_val = e1000_phy_hw_reset(hw); if (ret_val) { DEBUGOUT("Error Resetting the PHY\n"); return ret_val; } // SDG - this is done to setup the LED mode correctly but should really // be fixed in the hardware phy_data = 0x0001; e1000_oem_write_phy_reg_ex(hw, 31, phy_data); // switch to extended page registers // phy_data = 0x0C17; // just link phy_data = 0x0410; // blink mode e1000_oem_write_phy_reg_ex(hw, 17, phy_data); // enhanced led mode, // no activity (done by turning off combination feature) phy_data = 0x0021; e1000_oem_write_phy_reg_ex(hw, 16, phy_data); // link100 and link1000 phy_data = 0x0000; e1000_oem_write_phy_reg_ex(hw, 31, phy_data); // switch back to standard page registers return E1000_SUCCESS; } /** * e1000_oem_force_mdi * @hw: e1000_hw struct containing device specific information * @resetPhy: returns true if after calling this function the * PHY requires a reset * * Returns E1000_SUCCESS, negative E1000 error code on failure * * This is called from e1000_phy_force_speed_duplex, which is * called from e1000_oem_setup_link. **/ int32_t e1000_oem_force_mdi(struct e1000_hw *hw, int *resetPhy) { #ifdef EXTERNAL_MDIO uint16_t phy_data; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !resetPhy) return -1; /* * a boolean to indicate if the phy needs to be reset * * Make note that the M88 phy is what'll be used on Truxton * see e1000_phy_force_speed_duplex, which does the following for M88 */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL " "register\n"); return ret_val; } /* * Clear Auto-Crossover to force MDI manually. M88E1000 requires * MDI forced whenever speed are duplex are forced. */ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL " "register\n"); return ret_val; } *resetPhy = true; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read VSC8211_BYPASS_CTRL register\n"); return ret_val; } /* disable automatic MDI and MDI-X */ phy_data |= VSC8211_BYPASS_AUTO_MDI_DISABLE; e1000_oem_write_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, phy_data); if (ret_val) { DEBUGOUT ("Unable to write VSC8211_BYPASS_CTRL register\n"); return ret_val; } *resetPhy = true; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read VSC8601_BYPASS_CTRL register\n"); return ret_val; } /* disable automatic MDI and MDI-X */ phy_data |= VSC8601_BYPASS_AUTO_MDI_DISABLE; e1000_oem_write_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, phy_data); if (ret_val) { DEBUGOUT ("Unable to write VSC8601_BYPASS_CTRL register\n"); return ret_val; } *resetPhy = true; break; case NON_PHY_PORT: *resetPhy = false; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !resetPhy) return -1; *resetPhy = false; return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_reset_dsp * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * This is called from e1000_phy_force_speed_duplex, which is * called from e1000_oem_setup_link. **/ int32_t e1000_oem_phy_reset_dsp(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* * Make note that the M88 phy is what'll be used on Truxton. * * See e1000_phy_force_speed_duplex, which calls e1000_phy_reset_dsp * for the M88 PHY. The code as written references registers 29 and 30, * which are reserved for the M88 used on Truxton, so this will be a * no-op. */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: case NON_PHY_PORT: DEBUGOUT("No DSP to reset on OEM PHY\n"); break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_cleanup_after_phy_reset * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * This is called from e1000_phy_force_speed_duplex, which is * called from e1000_oem_setup_link. **/ int32_t e1000_oem_cleanup_after_phy_reset(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO uint16_t phy_data; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* * Make note that the M88 phy is what'll be used on Truxton. * see e1000_phy_force_speed_duplex, which does the following for M88 */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: /* * Because we reset the PHY above, we need to re-force * TX_CLK in the Extended PHY Specific Control Register to * 25MHz clock. This value defaults back to a 2.5MHz clock * when the PHY is reset. */ ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read M88E1000_EXT_SPEC_CTRL " "register\n"); return ret_val; } phy_data |= M88E1000_EPSCR_TX_CLK_25; ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write M88E1000_EXT_PHY_SPEC_CTRL " "register\n"); return ret_val; } /* * In addition, because of the s/w reset above, we need to * enable CRX on TX. This must be set for both full and half * duplex operation. */ ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL " "register\n"); return ret_val; } phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX; ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL " "register\n"); return ret_val; } break; case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: case NON_PHY_PORT: /* do nothing */ break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_set_phy_mode * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * This is called from e1000_oem_setup_link which is * called from e1000_setup_link. **/ static int32_t e1000_oem_set_phy_mode(struct e1000_hw *hw) { /* * it is unclear if it is necessary to set the phy mode. Right now only * one MAC 82545 Rev 3 does it, but the other MACs like tola do not. * Leave the functionality off for now until it is determined that * Tolapai needs it as well. */ #ifdef skip_set_mode #undef skip_set_mode #endif #ifdef skip_set_mode int32_t ret_val; uint16_t eeprom_data; #endif DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* * e1000_set_phy_mode specifically works for 82545 Rev 3 only, * since it is a 'loner' compared to the 82545, 82546, and * 82546 Rev 3, assume for now it is anomaly and don't repeat * for Truxton/Haxton. * Note that this is the approach taken in both the Windows and * FreeBSD drivers */ switch (hw->phy_id) { case VSC8211_E_PHY_ID: { int32_t ret_val; int16_t phy_data; /* Set CMODE to the RGMII-CAT5 combination */ phy_data = VSC8211_PHY_CTRL1_INTF_MODE1_RGMII | VSC8211_PHY_CTRL1_TXC_SKEW_2NS | VSC8211_PHY_CTRL1_RXC_SKEW_2NS | VSC8211_PHY_CTRL1_RX_IDLE_CLK_ENABLE | VSC8211_PHY_CTRL1_INTF_MODE2_CAT5; ret_val = e1000_oem_write_phy_reg_ex(hw, VSC8211_PHY_CTRL_1, phy_data); if (ret_val) { DEBUGOUT("Unable to write VSC8211_PHY_CTRL_1 " "register\n"); return ret_val; } break; } } #ifndef skip_set_mode DEBUGOUT("No need to call oem_set_phy_mode on Truxton\n"); #else /* * Make note that the M88 phy is what'll be used on Truxton. * * use e1000_set_phy_mode as example */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); if (ret_val) return ret_val; if ((eeprom_data != EEPROM_RESERVED_WORD) && (eeprom_data & EEPROM_PHY_CLASS_A)) { ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_PAGE_SELECT, 0x000B); if (ret_val) { DEBUGOUT("Unable to write to " "M88E1000_PHY_PAGE_SELECT register " "on PHY\n"); return ret_val; } ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_GEN_CONTROL, 0x8104); if (ret_val) { DEBUGOUT("Unable to write to " "M88E1000_PHY_GEN_CONTROL register" "on PHY\n"); return ret_val; } hw->phy_reset_disable = false; } break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } #endif return E1000_SUCCESS; } /** * e1000_oem_detect_phy * @hw: e1000_hw struct containing device specific information * * Fills hw->phy_type, hw->phy_id and hw->phy_revision fields as well * as verifies that the PHY identified is one that is comprehended * by the driver. * * This borrows heavily from e1000_detect_gig_phy **/ static int32_t e1000_oem_detect_phy(struct e1000_hw *hw) { int32_t ret_val; uint16_t phy_id_high, phy_id_low; DEBUGFUNC1("%s", __func__); if (!hw) return -1; hw->phy_type = e1000_phy_oem; { struct e1000_adapter *adapter; uint32_t dev_num; adapter = (struct e1000_adapter *)hw->back; dev_num = PCI_SLOT(adapter->pdev->devfn); switch (dev_num) { #ifndef CONFIG_E1000_EP80579_PHY0 case ICP_XXXX_MAC_0: hw->phy_id = NON_PHY_PORT; return E1000_SUCCESS; #endif #ifndef CONFIG_E1000_EP80579_PHY1 case ICP_XXXX_MAC_1: hw->phy_id = NON_PHY_PORT; return E1000_SUCCESS; #endif #ifndef CONFIG_E1000_EP80579_PHY2 case ICP_XXXX_MAC_2: hw->phy_id = NON_PHY_PORT; return E1000_SUCCESS; #endif } } ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_ID1, &phy_id_high); if (ret_val) { DEBUGOUT("Unable to read PHY register PHY_ID1\n"); return ret_val; } udelay(0x14); ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_ID2, &phy_id_low); if (ret_val) { DEBUGOUT("Unable to read PHY register PHY_ID2\n"); return ret_val; } hw->phy_id = (uint32_t) ((phy_id_high << 0x10) + (phy_id_low & PHY_REVISION_MASK)); hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; return E1000_SUCCESS; } /** * e1000_oem_get_tipg * @hw: e1000_hw struct containing device specific information * * Returns the value of the Inter Packet Gap (IPG) Transmit Time (IPGT) in the * Transmit IPG register appropriate for the given PHY. This field is only 10 * bits wide. * * In the original e1000 code, only the IPGT field varied between media types. * If the OEM phy requires setting IPG Receive Time 1 & 2 Registers, it would * be required to modify the e1000_config_tx() function to accomdate the change * **/ uint32_t e1000_oem_get_tipg(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO uint32_t phy_num; DEBUGFUNC1("%s", __func__); if (!hw) return DEFAULT_ICP_XXXX_TIPG_IPGT; switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: case NON_PHY_PORT: phy_num = DEFAULT_ICP_XXXX_TIPG_IPGT; break; default: DEBUGOUT("Invalid PHY ID\n"); return DEFAULT_ICP_XXXX_TIPG_IPGT; } return phy_num; #else /* ifdef EXTERNAL_MDIO */ /* return the default value required by ICP_xxxx style MACS */ DEBUGOUT("Invalid value for transceiver type, return default" " TIPG.IPGT value\n"); return DEFAULT_ICP_XXXX_TIPG_IPGT; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_is_copper * @hw: e1000_hw struct containing device specific information * * Test for media type within the e1000 driver is common, so this is a simple * test for copper PHYs. The ICP_XXXX family of controllers initially only * supported copper interconnects (no TBI (ten bit interface) for Fiber * existed). If future revs support either Fiber or an internal SERDES, it * may become necessary to evaluate where this function is used to go beyond * determining whether or not media type is just copper. * **/ int e1000_oem_phy_is_copper(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO int isCopper = true; DEBUGFUNC1("%s", __func__); if (!hw) return isCopper; switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: isCopper = true; break; case NON_PHY_PORT: isCopper = false; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return isCopper; #else /* ifdef EXTERNAL_MDIO */ /* * caught between returning true or false. True allows it to * be entered into && statements w/o ill effect, but false * would make more sense */ DEBUGOUT("Invalid value for transceiver type, return false\n"); return false; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_get_phy_dev_number * @hw: e1000_hw struct containing device specific information * * For ICP_XXXX family of devices, there are 3 MACs, each of which may * have a different PHY (and indeed a different media interface). This * function is used to indicate which of the MAC/PHY pairs we are interested * in. * **/ uint32_t e1000_oem_get_phy_dev_number(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO /* * for ICP_XXXX family of devices, the three network interfaces are * differentiated by their PCI device number, where the three share * the same PCI bus */ struct e1000_adapter *adapter; uint32_t device_number; DEBUGFUNC1("%s", __func__); if (!hw) return 0; adapter = (struct e1000_adapter *)hw->back; device_number = PCI_SLOT(adapter->pdev->devfn); switch (device_number) { case ICP_XXXX_MAC_0: hw->phy_addr = 0x00; break; case ICP_XXXX_MAC_1: hw->phy_addr = 0x01; break; case ICP_XXXX_MAC_2: hw->phy_addr = 0x02; break; default: hw->phy_addr = 0x00; } return hw->phy_addr; #else /* ifdef EXTERNAL_MDIO */ DEBUGOUT("Invalid value for transceiver type, return 0\n"); return 0; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_mii_ioctl * @adapter: e1000_hw struct containing device specific information * @flags: The saved adapter->stats_lock flags from the initiating spinlock * @ifr: interface request structure for socket ioctls * @cmd: the original IOCTL command that instigated the call chain. * * This function abstracts out the code necessary to service the * SIOCSMIIREG case within the e1000_mii_ioctl() for oem PHYs. * e1000_mii_ioctl() was implemented for copper phy's only and this * function will only be called if e1000_oem_phy_is_copper() returns true for * a given MAC. Note that e1000_mii_ioctl() has a compile flag * and exists only if SIOCGMIIPHY is defined. * * NOTE: a spinlock is in effect for the duration of this call. It is * imperative that a negative value be returned on any error, so * the spinlock can be released properly. * **/ int e1000_oem_mii_ioctl(struct e1000_adapter *adapter, unsigned long flags, struct ifreq *ifr, int cmd) { #ifdef EXTERNAL_MDIO struct mii_ioctl_data *data = if_mii(ifr); uint16_t mii_reg = data->val_in; uint16_t spddplx; int retval; DEBUGFUNC1("%s", __func__); if (!adapter || !ifr) return -1; switch (data->reg_num) { case PHY_CTRL: if (mii_reg & MII_CR_POWER_DOWN) break; if (mii_reg & MII_CR_AUTO_NEG_EN) { adapter->hw.autoneg = 1; adapter->hw.autoneg_advertised = ICP_XXXX_AUTONEG_ADV_DEFAULT; } else { if (mii_reg & 0x40) spddplx = SPEED_1000; else if (mii_reg & 0x2000) spddplx = SPEED_100; else spddplx = SPEED_10; spddplx += (mii_reg & 0x100) ? FULL_DUPLEX : HALF_DUPLEX; retval = e1000_set_spd_dplx(adapter, spddplx); if (retval) return retval; } if (netif_running(adapter->netdev)) { e1000_down(adapter); e1000_up(adapter); } else { e1000_reset(adapter); } break; case M88E1000_PHY_SPEC_CTRL: case M88E1000_EXT_PHY_SPEC_CTRL: retval = e1000_phy_reset(&adapter->hw); if (retval) { DEBUGOUT("Error resetting the PHY\n"); return -EIO; } break; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -EOPNOTSUPP; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_get_phy_regs * @adapter e1000_adapter struct containing device specific information * @data unsigned integer array of size data_len * @data_len number of elements in data * * This is called by e1000_get_regs() in response to an ethtool request * to return the data of the controller. Most of the data returned is from * the MAC, but some data comes from the PHY, thus from this f(). * * Note: The call to e1000_get_regs() assumed an array of 24 elements * where the last 11 are passed to this function. If the array * that is passed to the calling function has its size or element * defintions changed, this function becomes broken. * **/ void e1000_oem_get_phy_regs(struct e1000_adapter *adapter, uint32_t * data, uint32_t data_len) { #define EXPECTED_ARRAY_LEN 11 uint32_t corrected_len; DEBUGFUNC1("%s", __func__); if (!adapter || !data) return; /* This f(n) expects to have EXPECTED_ARRAY_LEN elements to initialize. * Use the corrected_length variable to make sure we don't exceed that * length */ corrected_len = data_len > EXPECTED_ARRAY_LEN ? EXPECTED_ARRAY_LEN : data_len; memset(data, 0, corrected_len * sizeof(uint32_t)); #ifdef EXTERNAL_MDIO /* * Fill data[] with... * * [0] = cable length * [1] = cable length * [2] = cable length * [3] = cable length * [4] = extended 10bt distance * [5] = cable polarity * [6] = cable polarity * [7] = polarity correction enabled * [8] = undefined * [9] = phy receive errors * [10] = mdix mode */ switch (adapter->hw.phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: if (corrected_len > 0) e1000_oem_read_phy_reg_ex(&adapter->hw, M88E1000_PHY_SPEC_STATUS, (uint16_t *) &data[0]); if (corrected_len > 0x1) data[0x1] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ if (corrected_len > 0x2) data[0x2] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ if (corrected_len > 0x3) data[0x3] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ if (corrected_len > 0x4) e1000_oem_read_phy_reg_ex(&adapter->hw, M88E1000_PHY_SPEC_CTRL, (uint16_t *) &data[0x4]); if (corrected_len > 0x5) data[0x5] = data[0x0]; if (corrected_len > 0x6) data[0x6] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ if (corrected_len > 0x7) data[0x7] = data[0x4]; /* phy receive errors */ if (corrected_len > 0x9) data[0x9] = adapter->phy_stats.receive_errors; if (corrected_len > 0xa) data[0xa] = data[0x0]; break; default: DEBUGOUT("Invalid PHY ID\n"); return; } #endif /* ifdef EXTERNAL_MDIO */ #undef EXPECTED_ARRAY_LEN return; } /** * e1000_oem_phy_loopback * @adapter e1000_adapter struct containing device specific information * * This is called from e1000_set_phy_loopback in response from call from * ethtool to place the PHY into loopback mode. **/ int e1000_oem_phy_loopback(struct e1000_adapter *adapter) { #ifdef EXTERNAL_MDIO int ret_val; uint32_t ctrl_reg = 0; DEBUGFUNC1("%s", __func__); if (!adapter) return -1; /* * This borrows liberally from e1000_integrated_phy_loopback(). * e1000_nonintegrated_phy_loopback() was also a point of reference * since it was similar. The biggest difference between the two * was that nonintegrated called e1000_phy_reset_clk_and_crs(), * hopefully this won't matter as CRS required for half-duplex * operation and this is set to full duplex. * * Make note that the M88 phy is what'll be used on Truxton * Loopback configuration is the same for each of the supported PHYs. */ switch (adapter->hw.phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: adapter->hw.autoneg = false; /* turn off Auto-MDI/MDIX */ /*ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, M88E1000_PHY_SPEC_CTRL, 0x0808); if(ret_val) { DEBUGOUT("Unable to write to register M88E1000_PHY_SPEC_CTRL\n"); return ret_val; } */ /* reset to update Auto-MDI/MDIX */ /* ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x9140); if(ret_val) { DEBUGOUT("Unable to write to register PHY__CTRL\n"); return ret_val; } */ /* autoneg off */ /*ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x8140); */ ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0xa100); if (ret_val) { DEBUGOUT("Unable to write to register PHY_CTRL\n"); return ret_val; } /* force 1000, set loopback */ /*ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x4140); */ ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x6100); if (ret_val) { DEBUGOUT("Unable to write to register PHY_CTRL\n"); return ret_val; } ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ ctrl_reg |= (E1000_CTRL_FRCSPD /* Set the Force Speed Bit */ | E1000_CTRL_FRCDPX /* Set the Force Duplex Bit */ | E1000_CTRL_SPD_100 /* Force Speed to 1000 */ | E1000_CTRL_FD); /* Force Duplex to FULL */ /* | E1000_CTRL_ILOS); *//* Invert Loss of Signal */ E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg); /* * Write out to PHY registers 29 and 30 to disable the Receiver. * This directly lifted from e1000_phy_disable_receiver(). * * The code is currently commented out as for the M88 used in * Truxton, registers 29 and 30 are unutilized. Leave in, just * in case we are on the receiving end of an 'undocumented' * feature */ /* * e1000_oem_write_phy_reg_ex(&adapter->hw, 29, 0x001F); * e1000_oem_write_phy_reg_ex(&adapter->hw, 30, 0x8FFC); * e1000_oem_write_phy_reg_ex(&adapter->hw, 29, 0x001A); * e1000_oem_write_phy_reg_ex(&adapter->hw, 30, 0x8FF0); */ break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return 0; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_loopback_cleanup * @adapter e1000_adapter struct containing device specific information * * This is called from e1000_loopback_cleanup in response from call from * ethtool to place the PHY out of loopback mode. This handles the OEM * specific part of loopback cleanup. **/ void e1000_oem_loopback_cleanup(struct e1000_adapter *adapter) { #ifdef EXTERNAL_MDIO /* * This borrows liberally from e1000_loopback_cleanup(). * making note that the M88 phy is what'll be used on Truxton * * Loopback cleanup is the same for all supported PHYs. */ int32_t ret_val; uint16_t phy_reg; DEBUGFUNC1("%s", __func__); if (!adapter) return; switch (adapter->hw.phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: default: adapter->hw.autoneg = true; ret_val = e1000_oem_read_phy_reg_ex(&adapter->hw, PHY_CTRL, &phy_reg); if (ret_val) { DEBUGOUT("Unable to read to register PHY_CTRL\n"); return; } if (phy_reg & MII_CR_LOOPBACK) { phy_reg &= ~MII_CR_LOOPBACK; ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, phy_reg); if (ret_val) { DEBUGOUT ("Unable to write to register PHY_CTRL\n"); return; } e1000_phy_reset(&adapter->hw); } } #endif /* ifdef EXTERNAL_MDIO */ return; } /** * e1000_oem_phy_speed_downgraded * @hw e1000_hw struct containing device specific information * @isDowngraded returns with value > 0 if the link belonging to hw * has been downshifted * * Called by e1000_check_downshift(), checks the PHY to see if it running * at as speed slower than its maximum. **/ uint32_t e1000_oem_phy_speed_downgraded(struct e1000_hw *hw, uint16_t * isDowngraded) { #ifdef EXTERNAL_MDIO uint32_t ret_val; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw || !isDowngraded) return 1; /* * borrow liberally from E1000_check_downshift e1000_phy_m88 case. * Make note that the M88 phy is what'll be used on Truxton */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } *isDowngraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> M88E1000_PSSR_DOWNSHIFT_SHIFT; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8211_AUX_CTRL_STS\n"); return ret_val; } *isDowngraded = (phy_data & VSC8211_AUX_SPEED_MASK) != VSC8211_AUX_SPEED_IS_1000; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8601_AUX_CTRL_STS\n"); return ret_val; } *isDowngraded = (phy_data & VSC8601_AUX_SPEED_MASK) != VSC8601_AUX_SPEED_IS_1000; break; default: DEBUGOUT("Invalid PHY ID\n"); return 1; } return 0; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !isDowngraded) return 1; *isDowngraded = 0; return 0; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_check_polarity * @hw e1000_hw struct containing device specific information * @isDowngraded returns with value > 0 if the link belonging to hw * has its polarity shifted. * * Called by e1000_check_downshift(), checks the PHY to see if it running * at as speed slower than its maximum. **/ int32_t e1000_oem_check_polarity(struct e1000_hw *hw, uint16_t *polarity) { #ifdef EXTERNAL_MDIO int32_t ret_val; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw || !polarity) return -1; /* * borrow liberally from e1000_check_polarity. * Make note that the M88 phy is what'll be used on Truxton */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: /* return the Polarity bit in the Status register. */ ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >> M88E1000_PSSR_REV_POLARITY_SHIFT; break; case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: DEBUGOUT("check polarity is not supported by VSC8XXX\n"); return -E1000_ERR_PHY_TYPE; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return 0; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !polarity) return -1; *polarity = 0; return -1; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_is_full_duplex * @hw e1000_hw struct containing device specific information * @isFD a boolean returning true if phy is full duplex * * This is called as part of e1000_config_mac_to_phy() to align * the MAC with the PHY. It turns out on ICP_XXXX, this is not * done automagically. **/ int32_t e1000_oem_phy_is_full_duplex(struct e1000_hw *hw, int *isFD) { #ifdef EXTERNAL_MDIO uint16_t phy_data; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !isFD) return -1; /* * Make note that the M88 phy is what'll be used on Truxton * see e1000_config_mac_to_phy */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } *isFD = (phy_data & M88E1000_PSSR_DPLX) != 0; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8211_AUX_CTRL_STS\n"); return ret_val; } *isFD = (phy_data & VSC8211_AUX_FDX_MASK) == VSC8211_AUX_FDX_IS_FULL; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8601_AUX_CTRL_STS\n"); return ret_val; } *isFD = (phy_data & VSC8601_AUX_FDX_MASK) == VSC8601_AUX_FDX_IS_FULL; break; case NON_PHY_PORT: *isFD = true; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !isFD) return -1; *isFD = false; return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_is_speed_1000 * @hw e1000_hw struct containing device specific information * @is1000 a boolean returning true if phy is running at 1000 * * This is called as part of e1000_config_mac_to_phy() to align * the MAC with the PHY. It turns out on ICP_XXXX, this is not * done automagically. **/ int32_t e1000_oem_phy_is_speed_1000(struct e1000_hw *hw, int *is1000) { #ifdef EXTERNAL_MDIO uint16_t phy_data; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !is1000) return -1; /* * Make note that the M88 phy is what'll be used on Truxton. * see e1000_config_mac_to_phy */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } *is1000 = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8211_AUX_CTRL_STS\n"); return ret_val; } *is1000 = (phy_data & VSC8211_AUX_SPEED_MASK) == VSC8211_AUX_SPEED_IS_1000; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8601_AUX_CTRL_STS\n"); return ret_val; } *is1000 = (phy_data & VSC8601_AUX_SPEED_MASK) == VSC8601_AUX_SPEED_IS_1000; break; case NON_PHY_PORT: *is1000 = true; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !is1000) return -1; *is1000 = false; return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_is_speed_100 * @hw e1000_hw struct containing device specific information * @is100 a boolean returning true if phy is running at 100 * * This is called as part of e1000_config_mac_to_phy() to align * the MAC with the PHY. It turns out on ICP_XXXX, this is not * done automagically. **/ int32_t e1000_oem_phy_is_speed_100(struct e1000_hw *hw, int *is100) { #ifdef EXTERNAL_MDIO uint16_t phy_data; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !is100) return -1; /* * Make note that the M88 phy is what'll be used on Truxton * see e1000_config_mac_to_phy */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } *is100 = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS; break; case VSC8211_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8211_AUX_CTRL_STS\n"); return ret_val; } *is100 = (phy_data & VSC8211_AUX_SPEED_MASK) == VSC8211_AUX_SPEED_IS_100; break; case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); if (ret_val) { DEBUGOUT ("Unable to read register VSC8601_AUX_CTRL_STS\n"); return ret_val; } *is100 = (phy_data & VSC8601_AUX_SPEED_MASK) == VSC8601_AUX_SPEED_IS_100; break; case NON_PHY_PORT: *is100 = false; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !is100) return -1; *is100 = false; return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_get_info * @hw struct e1000_hw containing hardware specific data * @phy_info struct e1000_phy_info that returned * * This is called by e1000_phy_get_info to gather PHY specific * data. This is called for copper media based phys. **/ int32_t e1000_oem_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) { #ifdef EXTERNAL_MDIO int32_t ret_val; uint16_t phy_data, polarity; DEBUGFUNC1("%s", __func__); if (!hw || !phy_info) return -1; /* * Make note that the M88 phy is what'll be used on Truxton * see e1000_phy_m88_get_info */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: /* The downshift status is checked only once, after link is * established and it stored in the hw->speed_downgraded * parameter.*/ phy_info->downshift = (e1000_downshift) hw->speed_downgraded; ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_CTRL\n"); return ret_val; } phy_info->extended_10bt_distance = (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT; phy_info->polarity_correction = (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >> M88E1000_PSCR_POLARITY_REVERSAL_SHIFT; /* Check polarity status */ ret_val = e1000_oem_check_polarity(hw, &polarity); if (ret_val) return ret_val; phy_info->cable_polarity = polarity; ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "M88E1000_PHY_SPEC_STATUS\n"); return ret_val; } phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >> M88E1000_PSSR_MDIX_SHIFT; if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { /* Cable Length Estimation and Local/Remote Receiver * Information are only valid at 1000 Mbps. */ phy_info->cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> M88E1000_PSSR_CABLE_LENGTH_SHIFT; ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_1000T_STATUS, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register " "PHY_1000T_STATUS\n"); return ret_val; } phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >> SR_1000T_LOCAL_RX_STATUS_SHIFT; phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >> SR_1000T_REMOTE_RX_STATUS_SHIFT; } break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_hw_reset * @hw struct e1000_hw containing hardware specific data * * This function will perform a software initiated reset of * the PHY **/ int32_t e1000_oem_phy_hw_reset(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO int32_t ret_val; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* * This code pretty much copies the default case from * e1000_phy_reset() as that is what is appropriate for * the M88 used in truxton. */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_CTRL, &phy_data); if (ret_val) { DEBUGOUT("Unable to read register PHY_CTRL\n"); return ret_val; } phy_data |= MII_CR_RESET; ret_val = e1000_oem_write_phy_reg_ex(hw, PHY_CTRL, phy_data); if (ret_val) { DEBUGOUT("Unable to write register PHY_CTRL\n"); return ret_val; } udelay(1); break; case NON_PHY_PORT: /* do nothing */ break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_init_script * @hw struct e1000_hw containing hardware specific data * * This gets called in three places, after egbe_oem_phy_hw_reset() * to perform and post reset initialiation. Not all PHYs require * this, which is why it was split off as a seperate function. **/ void e1000_oem_phy_init_script(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO DEBUGFUNC1("%s", __func__); if (!hw) return; /* call the GCU func that can do any phy specific init * functions after a reset * * Make note that the M88 phy is what'll be used on Truxton * * The closest thing is in e1000_phy_init_script, however this is * for the IGP style of phy. This is probably a no-op for truxton * but may be needed by OEM's later on * */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: case NON_PHY_PORT: DEBUGOUT("Nothing to do for OEM PHY Init"); break; default: DEBUGOUT("Invalid PHY ID\n"); return; } #endif /* ifdef EXTERNAL_MDIO */ return; } /** * e1000_oem_read_phy_reg_ex * @hw struct e1000_hw containing hardware specific data * @reg_addr address location within the PHY register set * @phy_data returns the data read from reg_addr * * This encapsulates the interface call to the GCU for access * to the MDIO for the PHY. **/ int32_t e1000_oem_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data) { #ifdef EXTERNAL_MDIO int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !phy_data) return -1; /* call the GCU func that will read the phy * * Make note that the M88 phy is what'll be used on Truxton. * * The closest thing is in e1000_read_phy_reg_ex. * * NOTE: this is 1 (of 2) functions that is truly dependant on the * gcu module */ ret_val = gcu_read_eth_phy(e1000_oem_get_phy_dev_number(hw), reg_addr, phy_data); if (ret_val) { DEBUGOUT("Error reading GCU"); return ret_val; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_set_trans_gasket * @hw: e1000_hw struct containing device specific information * * Returns E1000_SUCCESS, negative E1000 error code on failure * * This is called from e1000_config_mac_to_phy. Various supported * Phys may require the RGMII/RMII Translation gasket be set to RMII. **/ int32_t e1000_oem_set_trans_gasket(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO DEBUGFUNC1("%s", __func__); if (!hw) return -1; switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: /* Gasket set correctly for Marvell Phys, so nothing to do */ break; #if 0 /* Add your PHY_ID here if your device requires an RMII * interface */ case YOUR_PHY_ID: uint32_t ctrl_aux_reg = 0; ctrl_aux_reg = E1000_READ_REG(hw, CTRL_AUX); /* Set the RGMII_RMII bit */ ctrl_aux_reg |= E1000_CTRL_AUX_ICP_xxxx_MII_TGS; E1000_WRITE_REG(hw, CTRL_AUX, ctrl_aux_reg); break; #endif case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_write_phy_reg_ex * @hw struct e1000_hw containing hardware specific data * @reg_addr address location within the PHY register set * @phy_data data to be written to reg_addr * * This encapsulates the interface call to the GCU for access * to the MDIO for the PHY. **/ int32_t e1000_oem_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data) { #ifdef EXTERNAL_MDIO int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* call the GCU func that will write to the phy * * Make note that the M88 phy is what'll be used on Truxton. * * The closest thing is in e1000_write_phy_reg_ex * * NOTE: this is 2 (of 2) functions that is truly dependant on the * gcu module */ ret_val = gcu_write_eth_phy(e1000_oem_get_phy_dev_number(hw), reg_addr, phy_data); if (ret_val) { DEBUGOUT("Error writing to GCU"); return ret_val; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_needs_reset_with_mac * @hw struct e1000_hw hardware specific data * * e1000_reset_hw is called to reset the MAC. If, for * some reason the PHY needs to be reset as well, this * should return true and then e1000_oem_phy_hw_reset() * will be called. **/ int e1000_oem_phy_needs_reset_with_mac(struct e1000_hw *hw) { #ifdef EXTERNAL_MDIO int ret_val; DEBUGFUNC1("%s", __func__); if (!hw) return false; /* * From the original e1000 driver, the M88 * PHYs did not seem to need this reset, * so returning false. */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: case NON_PHY_PORT: ret_val = false; break; default: DEBUGOUT("Invalid PHY ID\n"); return false; } return ret_val; #else /* ifdef EXTERNAL_MDIO */ return false; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_config_dsp_after_link_change * @hw struct e1000_hw containing hardware specific data * @link_up allows different configurations based on whether * not the link was up. * * This is called from e1000_check_for_link, and allows for * tweaking of the PHY, for PHYs that support a DSP. * **/ int32_t e1000_oem_config_dsp_after_link_change(struct e1000_hw *hw, int link_up) { #ifdef EXTERNAL_MDIO DEBUGFUNC1("%s", __func__); if (!hw) return -1; /* * Make note that the M88 phy is what'll be used on Truxton, * but in the e1000 driver, it had no such func. This is a no-op * for M88, but may be useful for other phys * * use e1000_config_dsp_after_link_change as example */ switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: DEBUGOUT("No DSP to configure on OEM PHY"); break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_get_cable_length * @hw struct e1000_hw containing hardware specific data * @min_length pointer to return the approx minimum length * @max_length pointer to return the approx maximum length * * **/ int32_t e1000_oem_get_cable_length(struct e1000_hw *hw, uint16_t *min_length, uint16_t *max_length) { #ifdef EXTERNAL_MDIO int32_t ret_val; uint16_t cable_length; uint16_t phy_data; DEBUGFUNC1("%s", __func__); if (!hw || !min_length || !max_length) return -1; switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) return ret_val; cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> M88E1000_PSSR_CABLE_LENGTH_SHIFT; /* Convert the enum value to ranged values */ switch (cable_length) { case e1000_cable_length_50: *min_length = 0; *max_length = e1000_igp_cable_length_50; break; case e1000_cable_length_50_80: *min_length = e1000_igp_cable_length_50; *max_length = e1000_igp_cable_length_80; break; case e1000_cable_length_80_110: *min_length = e1000_igp_cable_length_80; *max_length = e1000_igp_cable_length_110; break; case e1000_cable_length_110_140: *min_length = e1000_igp_cable_length_110; *max_length = e1000_igp_cable_length_140; break; case e1000_cable_length_140: *min_length = e1000_igp_cable_length_140; *max_length = e1000_igp_cable_length_170; break; default: return -E1000_ERR_PHY; break; } break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ } /** * e1000_oem_phy_is_link_up * @hw e1000_hw struct containing device specific information * @isUp a boolean returning true if link is up * * This is called as part of e1000_config_mac_to_phy() to align * the MAC with the PHY. It turns out on ICP_XXXX, this is not * done automagically. **/ int32_t e1000_oem_phy_is_link_up(struct e1000_hw *hw, int *isUp) { #ifdef EXTERNAL_MDIO uint16_t phy_data; uint16_t statusMask; int32_t ret_val; DEBUGFUNC1("%s", __func__); if (!hw || !isUp) return -1; /* * Make note that the M88 phy is what'll be used on Truxton * see e1000_config_mac_to_phy */ if (hw->phy_id == NON_PHY_PORT) { *isUp = 1; } else { switch (hw->phy_id) { case M88E1000_I_PHY_ID: case M88E1141_E_PHY_ID: e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); statusMask = M88E1000_PSSR_LINK; break; case VSC8211_E_PHY_ID: case VSC8601_E_PHY_ID: ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); statusMask = MII_SR_LINK_STATUS; break; default: DEBUGOUT("Invalid PHY ID\n"); return -E1000_ERR_PHY_TYPE; } if (ret_val) { DEBUGOUT("Unable to read PHY register\n"); return ret_val; } *isUp = (phy_data & statusMask) != 0; } return E1000_SUCCESS; #else /* ifdef EXTERNAL_MDIO */ if (!hw || !isFD) return -1; *isUp = false; return -E1000_ERR_PHY_TYPE; #endif /* ifdef EXTERNAL_MDIO */ }