freebsd-skq/sys/dev/em/if_em_phy.c
Prafulla Deuskar e6770f4c46 This is the first commit of the Intel gigabit driver for
PRO/1000 cards.

Submitted by:Prafulla Deuskar
Reviewed by: Paul Saab
MFC after:1 week
2001-12-02 07:37:17 +00:00

1224 lines
40 KiB
C

/*************************************************************************
**************************************************************************
Copyright (c) 2001 Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms of the Software, with or
without modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code of the Software may retain the above
copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form of the Software may reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
3. Neither the name of the Intel Corporation nor the names of its
contributors shall be used to endorse or promote products derived from
this Software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
$FreeBSD$
***************************************************************************
**************************************************************************/
/*
* Workfile: phy.c
* Date: 9/25/01 2:40p
* Revision: 37
*/
#include <dev/em/if_em_fxhw.h>
#include <dev/em/if_em_phy.h>
static void em_mii_shift_out_phy_data(struct adapter *Adapter,
u32 Data, u16 Count);
static void em_raise_mdc_clock(struct adapter *Adapter,
u32 * CtrlRegValue);
static void em_lower_mdc_clock(struct adapter *Adapter,
u32 * CtrlRegValue);
static u16 em_mii_shift_in_phy_data(struct adapter *Adapter);
static u8 em_phy_setup_auto_neg_advertisement(struct adapter *Adapter);
static void em_phy_force_speed_and_duplex(struct adapter *Adapter);
#define GOOD_MII_IF 0
u16 em_read_phy_register(struct adapter *Adapter,
u32 RegAddress, u32 PhyAddress)
{
u32 i;
u32 Data = 0;
u32 Command = 0;
ASSERT(RegAddress <= MAX_PHY_REG_ADDRESS);
if (Adapter->MacType > MAC_LIVENGOOD) {
Command = ((RegAddress << MDI_REGADD_SHIFT) |
(PhyAddress << MDI_PHYADD_SHIFT) |
(E1000_MDI_READ));
E1000_WRITE_REG(Mdic, Command);
for (i = 0; i < 32; i++) {
DelayInMicroseconds(10);
Data = E1000_READ_REG(Mdic);
if (Data & E1000_MDI_READY)
break;
}
} else {
em_mii_shift_out_phy_data(Adapter, PHY_PREAMBLE,
PHY_PREAMBLE_SIZE);
Command = ((RegAddress) |
(PhyAddress << 5) |
(PHY_OP_READ << 10) | (PHY_SOF << 12));
em_mii_shift_out_phy_data(Adapter, Command, 14);
Data = (u32) em_mii_shift_in_phy_data(Adapter);
}
ASSERT(!(Data & E1000_MDI_ERR));
return ((u16) Data);
}
void em_write_phy_register(struct adapter *Adapter,
u32 RegAddress, u32 PhyAddress, u16 Data)
{
u32 i;
u32 Command = 0;
u32 MdicRegValue;
ASSERT(RegAddress <= MAX_PHY_REG_ADDRESS);
if (Adapter->MacType > MAC_LIVENGOOD) {
Command = (((u32) Data) |
(RegAddress << MDI_REGADD_SHIFT) |
(PhyAddress << MDI_PHYADD_SHIFT) |
(E1000_MDI_WRITE));
E1000_WRITE_REG(Mdic, Command);
for (i = 0; i < 10; i++) {
DelayInMicroseconds(10);
MdicRegValue = E1000_READ_REG(Mdic);
if (MdicRegValue & E1000_MDI_READY)
break;
}
} else {
em_mii_shift_out_phy_data(Adapter, PHY_PREAMBLE,
PHY_PREAMBLE_SIZE);
Command = ((PHY_TURNAROUND) |
(RegAddress << 2) |
(PhyAddress << 7) |
(PHY_OP_WRITE << 12) | (PHY_SOF << 14));
Command <<= 16;
Command |= ((u32) Data);
em_mii_shift_out_phy_data(Adapter, Command, 32);
}
return;
}
static u16 em_mii_shift_in_phy_data(struct adapter *Adapter)
{
u32 CtrlRegValue;
u16 Data = 0;
u8 i;
CtrlRegValue = E1000_READ_REG(Ctrl);
CtrlRegValue &= ~E1000_CTRL_MDIO_DIR;
CtrlRegValue &= ~E1000_CTRL_MDIO;
E1000_WRITE_REG(Ctrl, CtrlRegValue);
em_raise_mdc_clock(Adapter, &CtrlRegValue);
em_lower_mdc_clock(Adapter, &CtrlRegValue);
for (Data = 0, i = 0; i < 16; i++) {
Data = Data << 1;
em_raise_mdc_clock(Adapter, &CtrlRegValue);
CtrlRegValue = E1000_READ_REG(Ctrl);
if (CtrlRegValue & E1000_CTRL_MDIO)
Data |= 1;
em_lower_mdc_clock(Adapter, &CtrlRegValue);
}
em_raise_mdc_clock(Adapter, &CtrlRegValue);
em_lower_mdc_clock(Adapter, &CtrlRegValue);
CtrlRegValue &= ~E1000_CTRL_MDIO;
return (Data);
}
static void em_mii_shift_out_phy_data(struct adapter *Adapter,
u32 Data, u16 Count)
{
u32 CtrlRegValue;
u32 Mask;
if (Count > 32)
ASSERT(0);
Mask = 0x01;
Mask <<= (Count - 1);
CtrlRegValue = E1000_READ_REG(Ctrl);
CtrlRegValue |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR);
while (Mask) {
if (Data & Mask)
CtrlRegValue |= E1000_CTRL_MDIO;
else
CtrlRegValue &= ~E1000_CTRL_MDIO;
E1000_WRITE_REG(Ctrl, CtrlRegValue);
DelayInMicroseconds(2);
em_raise_mdc_clock(Adapter, &CtrlRegValue);
em_lower_mdc_clock(Adapter, &CtrlRegValue);
Mask = Mask >> 1;
}
CtrlRegValue &= ~E1000_CTRL_MDIO;
}
static void em_raise_mdc_clock(struct adapter *Adapter, u32 * CtrlRegValue)
{
E1000_WRITE_REG(Ctrl, (*CtrlRegValue | E1000_CTRL_MDC));
DelayInMicroseconds(2);
}
static void em_lower_mdc_clock(struct adapter *Adapter, u32 * CtrlRegValue)
{
E1000_WRITE_REG(Ctrl, (*CtrlRegValue & ~E1000_CTRL_MDC));
DelayInMicroseconds(2);
}
void em_phy_hardware_reset(struct adapter *Adapter)
{
u32 ExtCtrlRegValue, CtrlRegValue;
DEBUGFUNC("em_phy_hardware_reset")
DEBUGOUT("Resetting Phy...\n");
if (Adapter->MacType > MAC_LIVENGOOD) {
CtrlRegValue = E1000_READ_REG(Ctrl);
CtrlRegValue |= E1000_CTRL_PHY_RST;
E1000_WRITE_REG(Ctrl, CtrlRegValue);
DelayInMilliseconds(20);
CtrlRegValue &= ~E1000_CTRL_PHY_RST;
E1000_WRITE_REG(Ctrl, CtrlRegValue);
DelayInMilliseconds(20);
} else {
ExtCtrlRegValue = E1000_READ_REG(Exct);
ExtCtrlRegValue |= E1000_CTRL_PHY_RESET_DIR4;
E1000_WRITE_REG(Exct, ExtCtrlRegValue);
DelayInMilliseconds(20);
ExtCtrlRegValue = E1000_READ_REG(Exct);
ExtCtrlRegValue &= ~E1000_CTRL_PHY_RESET4;
E1000_WRITE_REG(Exct, ExtCtrlRegValue);
DelayInMilliseconds(20);
ExtCtrlRegValue = E1000_READ_REG(Exct);
ExtCtrlRegValue |= E1000_CTRL_PHY_RESET4;
E1000_WRITE_REG(Exct, ExtCtrlRegValue);
DelayInMilliseconds(20);
}
return;
}
u8 em_phy_reset(struct adapter * Adapter)
{
u16 RegData;
u16 i;
DEBUGFUNC("em_phy_reset")
RegData = em_read_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress);
RegData |= MII_CR_RESET;
em_write_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress, RegData);
i = 0;
while ((RegData & MII_CR_RESET) && i++ < 500) {
RegData = em_read_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress);
DelayInMicroseconds(1);
}
if (i >= 500) {
DEBUGOUT("Timeout waiting for PHY to reset.\n");
return 0;
}
return 1;
}
u8 em_phy_setup(struct adapter * Adapter, u32 DeviceControlReg)
{
u16 MiiCtrlReg, MiiStatusReg;
u16 PhySpecCtrlReg;
u16 MiiAutoNegAdvertiseReg, Mii1000TCtrlReg;
u16 i, Data;
u16 AutoNegHwSetting;
u16 AutoNegFCSetting;
u8 RestartAutoNeg = 0;
u8 ForceAutoNegRestart = 0;
DEBUGFUNC("em_phy_setup")
ASSERT(Adapter->MacType >= MAC_LIVENGOOD);
if (Adapter->MacType > MAC_WAINWRIGHT) {
DeviceControlReg |= (E1000_CTRL_ASDE | E1000_CTRL_SLU);
E1000_WRITE_REG(Ctrl, DeviceControlReg);
} else {
DeviceControlReg |= (E1000_CTRL_FRCSPD |
E1000_CTRL_FRCDPX | E1000_CTRL_SLU);
E1000_WRITE_REG(Ctrl, DeviceControlReg);
if (Adapter->MacType == MAC_LIVENGOOD)
em_phy_hardware_reset(Adapter);
}
Adapter->PhyAddress = em_auto_detect_gigabit_phy(Adapter);
if (Adapter->PhyAddress > MAX_PHY_REG_ADDRESS) {
DEBUGOUT
("em_phy_setup failure, did not detect valid phy.\n");
return (0);
}
DEBUGOUT1("Phy ID = %x \n", Adapter->PhyId);
MiiCtrlReg = em_read_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress);
DEBUGOUT1("MII Ctrl Reg contents = %x\n", MiiCtrlReg);
if (!(MiiCtrlReg & MII_CR_AUTO_NEG_EN))
ForceAutoNegRestart = 1;
MiiCtrlReg &= ~(MII_CR_ISOLATE);
em_write_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress, MiiCtrlReg);
Data = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
Data |= PXN_PSCR_ASSERT_CRS_ON_TX;
DEBUGOUT1("Paxson PSCR: %x \n", Data);
em_write_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, Data);
Data = em_read_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
Data |= PXN_EPSCR_TX_CLK_25;
em_write_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, Data);
MiiAutoNegAdvertiseReg = em_read_phy_register(Adapter,
PHY_AUTONEG_ADVERTISEMENT,
Adapter->PhyAddress);
AutoNegHwSetting = (MiiAutoNegAdvertiseReg >> 5) & 0xF;
Mii1000TCtrlReg = em_read_phy_register(Adapter,
PHY_1000T_CTRL_REG,
Adapter->PhyAddress);
AutoNegHwSetting |= ((Mii1000TCtrlReg & 0x0300) >> 4);
AutoNegFCSetting = ((MiiAutoNegAdvertiseReg & 0x0C00) >> 10);
Adapter->AutoNegAdvertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
if (Adapter->AutoNegAdvertised == 0)
Adapter->AutoNegAdvertised =
AUTONEG_ADVERTISE_SPEED_DEFAULT;
if (!ForceAutoNegRestart && Adapter->AutoNeg &&
(Adapter->AutoNegAdvertised == AutoNegHwSetting) &&
(Adapter->FlowControl == AutoNegFCSetting)) {
DEBUGOUT("No overrides - Reading MII Status Reg..\n");
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
DEBUGOUT1("MII Status Reg contents = %x\n", MiiStatusReg);
if (MiiStatusReg & MII_SR_LINK_STATUS) {
Data = em_read_phy_register(Adapter,
PXN_PHY_SPEC_STAT_REG,
Adapter->PhyAddress);
DEBUGOUT1
("Paxson Phy Specific Status Reg contents = %x\n",
Data);
if (Adapter->MacType > MAC_WAINWRIGHT)
em_configure_collision_distance(Adapter);
else
em_configure_mac_to_phy_settings(Adapter,
Data);
em_config_flow_control_after_link_up(Adapter);
return (1);
}
}
PhySpecCtrlReg = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhySpecCtrlReg &= ~PXN_PSCR_AUTO_X_MODE;
switch (Adapter->MdiX) {
case 1:
PhySpecCtrlReg |= PXN_PSCR_MDI_MANUAL_MODE;
break;
case 2:
PhySpecCtrlReg |= PXN_PSCR_MDIX_MANUAL_MODE;
break;
case 3:
PhySpecCtrlReg |= PXN_PSCR_AUTO_X_1000T;
break;
case 0:
default:
PhySpecCtrlReg |= PXN_PSCR_AUTO_X_MODE;
break;
}
em_write_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, PhySpecCtrlReg);
PhySpecCtrlReg = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhySpecCtrlReg &= ~PXN_PSCR_POLARITY_REVERSAL;
if (Adapter->DisablePolarityCorrection == 1)
PhySpecCtrlReg |= PXN_PSCR_POLARITY_REVERSAL;
em_write_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, PhySpecCtrlReg);
if (Adapter->AutoNeg) {
DEBUGOUT
("Livengood - Reconfiguring auto-neg advertisement params\n");
RestartAutoNeg =
em_phy_setup_auto_neg_advertisement(Adapter);
} else {
DEBUGOUT("Livengood - Forcing speed and duplex\n");
em_phy_force_speed_and_duplex(Adapter);
}
if (RestartAutoNeg) {
DEBUGOUT("Restarting Auto-Neg\n");
MiiCtrlReg = em_read_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress);
MiiCtrlReg |=
(MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
em_write_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress, MiiCtrlReg);
if (Adapter->WaitAutoNegComplete)
em_wait_for_auto_neg(Adapter);
}
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
DEBUGOUT1
("Checking for link status - MII Status Reg contents = %x\n",
MiiStatusReg);
for (i = 0; i < 10; i++) {
if (MiiStatusReg & MII_SR_LINK_STATUS) {
break;
}
DelayInMicroseconds(10);
DEBUGOUT(". ");
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
}
if (MiiStatusReg & MII_SR_LINK_STATUS) {
Data = em_read_phy_register(Adapter,
PXN_PHY_SPEC_STAT_REG,
Adapter->PhyAddress);
DEBUGOUT1("Paxson Phy Specific Status Reg contents = %x\n",
Data);
if (Adapter->MacType > MAC_WAINWRIGHT)
em_configure_collision_distance(Adapter);
else
em_configure_mac_to_phy_settings(Adapter, Data);
em_config_flow_control_after_link_up(Adapter);
DEBUGOUT("Valid link established!!!\n");
} else {
DEBUGOUT("Unable to establish link!!!\n");
}
return (1);
}
u8 em_phy_setup_auto_neg_advertisement(struct adapter * Adapter)
{
u16 MiiAutoNegAdvertiseReg, Mii1000TCtrlReg;
DEBUGFUNC("em_phy_setup_auto_neg_advertisement")
MiiAutoNegAdvertiseReg = em_read_phy_register(Adapter,
PHY_AUTONEG_ADVERTISEMENT,
Adapter->
PhyAddress);
Mii1000TCtrlReg = em_read_phy_register(Adapter,
PHY_1000T_CTRL_REG,
Adapter->PhyAddress);
MiiAutoNegAdvertiseReg &= ~REG4_SPEED_MASK;
Mii1000TCtrlReg &= ~REG9_SPEED_MASK;
DEBUGOUT1("AutoNegAdvertised %x\n", Adapter->AutoNegAdvertised);
if (Adapter->AutoNegAdvertised & ADVERTISE_10_HALF) {
DEBUGOUT("Advertise 10mb Half duplex\n");
MiiAutoNegAdvertiseReg |= NWAY_AR_10T_HD_CAPS;
}
if (Adapter->AutoNegAdvertised & ADVERTISE_10_FULL) {
DEBUGOUT("Advertise 10mb Full duplex\n");
MiiAutoNegAdvertiseReg |= NWAY_AR_10T_FD_CAPS;
}
if (Adapter->AutoNegAdvertised & ADVERTISE_100_HALF) {
DEBUGOUT("Advertise 100mb Half duplex\n");
MiiAutoNegAdvertiseReg |= NWAY_AR_100TX_HD_CAPS;
}
if (Adapter->AutoNegAdvertised & ADVERTISE_100_FULL) {
DEBUGOUT("Advertise 100mb Full duplex\n");
MiiAutoNegAdvertiseReg |= NWAY_AR_100TX_FD_CAPS;
}
if (Adapter->AutoNegAdvertised & ADVERTISE_1000_HALF) {
DEBUGOUT
("Advertise 1000mb Half duplex requested, request denied!\n");
}
if (Adapter->AutoNegAdvertised & ADVERTISE_1000_FULL) {
DEBUGOUT("Advertise 1000mb Full duplex\n");
Mii1000TCtrlReg |= CR_1000T_FD_CAPS;
}
switch (Adapter->FlowControl) {
case FLOW_CONTROL_NONE:
MiiAutoNegAdvertiseReg &=
~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
case FLOW_CONTROL_RECEIVE_PAUSE:
MiiAutoNegAdvertiseReg |=
(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
case FLOW_CONTROL_TRANSMIT_PAUSE:
MiiAutoNegAdvertiseReg |= NWAY_AR_ASM_DIR;
MiiAutoNegAdvertiseReg &= ~NWAY_AR_PAUSE;
break;
case FLOW_CONTROL_FULL:
MiiAutoNegAdvertiseReg |=
(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
default:
DEBUGOUT("Flow control param set incorrectly\n");
ASSERT(0);
break;
}
em_write_phy_register(Adapter,
PHY_AUTONEG_ADVERTISEMENT,
Adapter->PhyAddress, MiiAutoNegAdvertiseReg);
DEBUGOUT1("Auto-Neg Advertising %x\n", MiiAutoNegAdvertiseReg);
em_write_phy_register(Adapter,
PHY_1000T_CTRL_REG,
Adapter->PhyAddress, Mii1000TCtrlReg);
return (1);
}
static void em_phy_force_speed_and_duplex(struct adapter *Adapter)
{
u16 MiiCtrlReg;
u16 MiiStatusReg;
u16 PhyData;
u16 i;
u32 TctlReg;
u32 DeviceCtrlReg;
u32 Shift32;
DEBUGFUNC("em_phy_force_speed_and_duplex")
Adapter->FlowControl = FLOW_CONTROL_NONE;
DEBUGOUT1("Adapter->FlowControl = %d\n", Adapter->FlowControl);
DeviceCtrlReg = E1000_READ_REG(Ctrl);
DeviceCtrlReg |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
DeviceCtrlReg &= ~(DEVICE_SPEED_MASK);
DeviceCtrlReg &= ~E1000_CTRL_ASDE;
MiiCtrlReg = em_read_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress);
MiiCtrlReg &= ~MII_CR_AUTO_NEG_EN;
if (Adapter->ForcedSpeedDuplex == FULL_100 ||
Adapter->ForcedSpeedDuplex == FULL_10) {
DeviceCtrlReg |= E1000_CTRL_FD;
MiiCtrlReg |= MII_CR_FULL_DUPLEX;
DEBUGOUT("Full Duplex\n");
} else {
DeviceCtrlReg &= ~E1000_CTRL_FD;
MiiCtrlReg &= ~MII_CR_FULL_DUPLEX;
DEBUGOUT("Half Duplex\n");
}
if (Adapter->ForcedSpeedDuplex == FULL_100 ||
Adapter->ForcedSpeedDuplex == HALF_100) {
DeviceCtrlReg |= E1000_CTRL_SPD_100;
MiiCtrlReg |= MII_CR_SPEED_100;
MiiCtrlReg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10);
DEBUGOUT("Forcing 100mb ");
} else {
DeviceCtrlReg &=
~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
MiiCtrlReg |= MII_CR_SPEED_10;
MiiCtrlReg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
DEBUGOUT("Forcing 10mb ");
}
TctlReg = E1000_READ_REG(Tctl);
DEBUGOUT1("TctlReg = %x\n", TctlReg);
if (!(MiiCtrlReg & MII_CR_FULL_DUPLEX)) {
TctlReg &= ~E1000_TCTL_COLD;
Shift32 = E1000_HDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
} else {
TctlReg &= ~E1000_TCTL_COLD;
Shift32 = E1000_FDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
}
E1000_WRITE_REG(Tctl, TctlReg);
E1000_WRITE_REG(Ctrl, DeviceCtrlReg);
PhyData = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhyData &= ~PXN_PSCR_AUTO_X_MODE;
em_write_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, PhyData);
DEBUGOUT1("Paxson PSCR: %x \n", PhyData);
MiiCtrlReg |= MII_CR_RESET;
em_write_phy_register(Adapter,
PHY_MII_CTRL_REG,
Adapter->PhyAddress, MiiCtrlReg);
if (Adapter->WaitAutoNegComplete) {
DEBUGOUT("Waiting for forced speed/duplex link.\n");
MiiStatusReg = 0;
#define PHY_WAIT_FOR_FORCED_TIME 20
for (i = 20; i > 0; i--) {
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->
PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->
PhyAddress);
if (MiiStatusReg & MII_SR_LINK_STATUS) {
break;
}
DelayInMilliseconds(100);
}
if (i == 0) {
em_pxn_phy_reset_dsp(Adapter);
}
for (i = 20; i > 0; i--) {
if (MiiStatusReg & MII_SR_LINK_STATUS) {
break;
}
DelayInMilliseconds(100);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->
PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->
PhyAddress);
}
}
PhyData = em_read_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhyData |= PXN_EPSCR_TX_CLK_25;
em_write_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, PhyData);
PhyData = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhyData |= PXN_PSCR_ASSERT_CRS_ON_TX;
em_write_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress, PhyData);
DEBUGOUT1("After force, Paxson Phy Specific Ctrl Reg = %4x\r\n",
PhyData);
return;
}
void em_configure_mac_to_phy_settings(struct adapter *Adapter,
u16 MiiRegisterData)
{
u32 DeviceCtrlReg, TctlReg;
u32 Shift32;
DEBUGFUNC("em_configure_mac_to_phy_settings")
TctlReg = E1000_READ_REG(Tctl);
DEBUGOUT1("TctlReg = %x\n", TctlReg);
DeviceCtrlReg = E1000_READ_REG(Ctrl);
DeviceCtrlReg |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
DeviceCtrlReg &= ~(DEVICE_SPEED_MASK);
DEBUGOUT1("MII Register Data = %x\r\n", MiiRegisterData);
DeviceCtrlReg &= ~E1000_CTRL_ILOS;
if (MiiRegisterData & PXN_PSSR_DPLX) {
DeviceCtrlReg |= E1000_CTRL_FD;
TctlReg &= ~E1000_TCTL_COLD;
Shift32 = E1000_FDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
} else {
DeviceCtrlReg &= ~E1000_CTRL_FD;
if ((MiiRegisterData & PXN_PSSR_SPEED) == PXN_PSSR_1000MBS) {
TctlReg &= ~E1000_TCTL_COLD;
Shift32 = E1000_GB_HDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
TctlReg |= E1000_TCTL_PBE;
} else {
TctlReg &= ~E1000_TCTL_COLD;
Shift32 = E1000_HDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
}
}
if ((MiiRegisterData & PXN_PSSR_SPEED) == PXN_PSSR_1000MBS)
DeviceCtrlReg |= E1000_CTRL_SPD_1000;
else if ((MiiRegisterData & PXN_PSSR_SPEED) == PXN_PSSR_100MBS)
DeviceCtrlReg |= E1000_CTRL_SPD_100;
else
DeviceCtrlReg &=
~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
E1000_WRITE_REG(Tctl, TctlReg);
E1000_WRITE_REG(Ctrl, DeviceCtrlReg);
return;
}
void em_configure_collision_distance(struct adapter *Adapter)
{
u32 TctlReg;
u16 Speed;
u16 Duplex;
u32 Shift32;
DEBUGFUNC("em_configure_collision_distance")
em_get_speed_and_duplex(Adapter, &Speed, &Duplex);
TctlReg = E1000_READ_REG(Tctl);
DEBUGOUT1("TctlReg = %x\n", TctlReg);
TctlReg &= ~E1000_TCTL_COLD;
if (Duplex == FULL_DUPLEX) {
Shift32 = E1000_FDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
} else {
if (Speed == SPEED_1000) {
Shift32 = E1000_GB_HDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
TctlReg |= E1000_TCTL_PBE;
} else {
Shift32 = E1000_HDX_COLLISION_DISTANCE;
Shift32 <<= E1000_COLD_SHIFT;
TctlReg |= Shift32;
}
}
E1000_WRITE_REG(Tctl, TctlReg);
return;
}
void em_display_mii_contents(struct adapter *Adapter, u8 PhyAddress)
{
u16 Data, PhyIDHi, PhyIDLo;
u32 PhyID;
DEBUGFUNC("em_display_mii_contents")
DEBUGOUT1("Adapter Base Address = %x\n",
Adapter->HardwareVirtualAddress);
Data = em_read_phy_register(Adapter, PHY_MII_CTRL_REG, PhyAddress);
DEBUGOUT1("MII Ctrl Reg contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG, PhyAddress);
Data = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG, PhyAddress);
DEBUGOUT1("MII Status Reg contents = %x\n", Data);
PhyIDHi = em_read_phy_register(Adapter,
PHY_PHY_ID_REG1, PhyAddress);
DelayInMicroseconds(2);
PhyIDLo = em_read_phy_register(Adapter,
PHY_PHY_ID_REG2, PhyAddress);
PhyID = (PhyIDLo | (PhyIDHi << 16)) & PHY_REVISION_MASK;
DEBUGOUT1("Phy ID = %x \n", PhyID);
Data = em_read_phy_register(Adapter,
PHY_AUTONEG_ADVERTISEMENT, PhyAddress);
DEBUGOUT1("Reg 4 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_AUTONEG_LP_BPA, PhyAddress);
DEBUGOUT1("Reg 5 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_AUTONEG_EXPANSION_REG, PhyAddress);
DEBUGOUT1("Reg 6 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_AUTONEG_NEXT_PAGE_TX, PhyAddress);
DEBUGOUT1("Reg 7 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_AUTONEG_LP_RX_NEXT_PAGE,
PhyAddress);
DEBUGOUT1("Reg 8 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_1000T_CTRL_REG, PhyAddress);
DEBUGOUT1("Reg 9 contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_1000T_STATUS_REG, PhyAddress);
DEBUGOUT1("Reg A contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PHY_IEEE_EXT_STATUS_REG, PhyAddress);
DEBUGOUT1("Reg F contents = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG, PhyAddress);
DEBUGOUT1("Paxson Specific Control Reg (0x10) = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_PHY_SPEC_STAT_REG, PhyAddress);
DEBUGOUT1("Paxson Specific Status Reg (0x11) = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_INT_ENABLE_REG, PhyAddress);
DEBUGOUT1("Paxson Interrupt Enable Reg (0x12) = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_INT_STATUS_REG, PhyAddress);
DEBUGOUT1("Paxson Interrupt Status Reg (0x13) = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG, PhyAddress);
DEBUGOUT1("Paxson Ext. Phy Specific Control (0x14) = %x\n", Data);
Data = em_read_phy_register(Adapter,
PXN_RX_ERROR_COUNTER, PhyAddress);
DEBUGOUT1("Paxson Receive Error Counter (0x15) = %x\n", Data);
Data = em_read_phy_register(Adapter, PXN_LED_CTRL_REG, PhyAddress);
DEBUGOUT1("Paxson LED control reg (0x18) = %x\n", Data);
}
u32 em_auto_detect_gigabit_phy(struct adapter *Adapter)
{
u32 PhyAddress = 1;
u32 PhyIDHi;
u16 PhyIDLo;
u8 GotOne = 0;
DEBUGFUNC("em_auto_detect_gigabit_phy")
while ((!GotOne) && (PhyAddress <= MAX_PHY_REG_ADDRESS)) {
PhyIDHi = em_read_phy_register(Adapter,
PHY_PHY_ID_REG1,
PhyAddress);
DelayInMicroseconds(2);
PhyIDLo = em_read_phy_register(Adapter,
PHY_PHY_ID_REG2,
PhyAddress);
Adapter->PhyId =
(PhyIDLo | (PhyIDHi << 16)) & PHY_REVISION_MASK;
if (Adapter->PhyId == PAXSON_PHY_88E1000 ||
Adapter->PhyId == PAXSON_PHY_88E1000S ||
Adapter->PhyId == PAXSON_PHY_INTEGRATED) {
DEBUGOUT2("PhyId 0x%x detected at address 0x%x\n",
Adapter->PhyId, PhyAddress);
GotOne = 1;
} else {
PhyAddress++;
}
}
if (PhyAddress > MAX_PHY_REG_ADDRESS) {
DEBUGOUT("Could not auto-detect Phy!\n");
}
return (PhyAddress);
}
void em_pxn_phy_reset_dsp(struct adapter *Adapter)
{
em_write_phy_register(Adapter, 29, Adapter->PhyAddress, 0x1d);
em_write_phy_register(Adapter, 30, Adapter->PhyAddress, 0xc1);
em_write_phy_register(Adapter, 30, Adapter->PhyAddress, 0x00);
}
u8 em_wait_for_auto_neg(struct adapter *Adapter)
{
u8 AutoNegComplete = 0;
u16 i;
u16 MiiStatusReg;
DEBUGFUNC("em_wait_for_auto_neg");
DEBUGOUT("Waiting for Auto-Neg to complete.\n");
MiiStatusReg = 0;
for (i = PHY_AUTO_NEG_TIME; i > 0; i--) {
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
MiiStatusReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
if (MiiStatusReg & MII_SR_AUTONEG_COMPLETE) {
AutoNegComplete = 1;
break;
}
DelayInMilliseconds(100);
}
return (AutoNegComplete);
}
u8 em_phy_get_status_info(struct adapter * Adapter,
phy_status_info_struct * PhyStatusInfo)
{
u16 PhyMIIStatReg;
u16 PhySpecCtrlReg;
u16 PhySpecStatReg;
u16 PhyExtSpecCtrlReg;
u16 Phy1000BTStatReg;
PhyStatusInfo->CableLength = PXN_PSSR_CABLE_LENGTH_UNDEFINED;
PhyStatusInfo->Extended10BTDistance =
PXN_PSCR_10BT_EXT_DIST_ENABLE_UNDEFINED;
PhyStatusInfo->CablePolarity = PXN_PSSR_REV_POLARITY_UNDEFINED;
PhyStatusInfo->PolarityCorrection =
PXN_PSCR_POLARITY_REVERSAL_UNDEFINED;
PhyStatusInfo->LinkReset = PXN_EPSCR_DOWN_NO_IDLE_UNDEFINED;
PhyStatusInfo->MDIXMode = PXN_PSCR_AUTO_X_MODE_UNDEFINED;
PhyStatusInfo->LocalRx = SR_1000T_RX_STATUS_UNDEFINED;
PhyStatusInfo->RemoteRx = SR_1000T_RX_STATUS_UNDEFINED;
if (Adapter == NULL || Adapter->MediaType != MEDIA_TYPE_COPPER)
return 0;
PhyMIIStatReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
PhyMIIStatReg = em_read_phy_register(Adapter,
PHY_MII_STATUS_REG,
Adapter->PhyAddress);
if ((PhyMIIStatReg & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS)
return 0;
PhySpecCtrlReg = em_read_phy_register(Adapter,
PXN_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
PhySpecStatReg = em_read_phy_register(Adapter,
PXN_PHY_SPEC_STAT_REG,
Adapter->PhyAddress);
PhyExtSpecCtrlReg = em_read_phy_register(Adapter,
PXN_EXT_PHY_SPEC_CTRL_REG,
Adapter->PhyAddress);
Phy1000BTStatReg = em_read_phy_register(Adapter,
PHY_1000T_STATUS_REG,
Adapter->PhyAddress);
PhyStatusInfo->CableLength = (PXN_PSSR_CABLE_LENGTH_ENUM)
((PhySpecStatReg & PXN_PSSR_CABLE_LENGTH) >>
PXN_PSSR_CABLE_LENGTH_SHIFT);
PhyStatusInfo->Extended10BTDistance =
(PXN_PSCR_10BT_EXT_DIST_ENABLE_ENUM) (PhySpecCtrlReg &
PXN_PSCR_10BT_EXT_DIST_ENABLE)
>> PXN_PSCR_10BT_EXT_DIST_ENABLE_SHIFT;
PhyStatusInfo->CablePolarity = (PXN_PSSR_REV_POLARITY_ENUM)
(PhySpecStatReg & PXN_PSSR_REV_POLARITY) >>
PXN_PSSR_REV_POLARITY_SHIFT;
PhyStatusInfo->PolarityCorrection =
(PXN_PSCR_POLARITY_REVERSAL_ENUM) (PhySpecCtrlReg &
PXN_PSCR_POLARITY_REVERSAL)
>> PXN_PSCR_POLARITY_REVERSAL_SHIFT;
PhyStatusInfo->LinkReset = (PXN_EPSCR_DOWN_NO_IDLE_ENUM)
(PhyExtSpecCtrlReg & PXN_EPSCR_DOWN_NO_IDLE) >>
PXN_EPSCR_DOWN_NO_IDLE_SHIFT;
PhyStatusInfo->MDIXMode = (PXN_PSCR_AUTO_X_MODE_ENUM)
(PhySpecCtrlReg & PXN_PSCR_AUTO_X_MODE) >>
PXN_PSCR_AUTO_X_MODE_SHIFT;
PhyStatusInfo->LocalRx = (SR_1000T_RX_STATUS_ENUM)
(Phy1000BTStatReg & SR_1000T_LOCAL_RX_STATUS) >>
SR_1000T_LOCAL_RX_STATUS_SHIFT;
PhyStatusInfo->RemoteRx = (SR_1000T_RX_STATUS_ENUM)
(Phy1000BTStatReg & SR_1000T_REMOTE_RX_STATUS) >>
SR_1000T_REMOTE_RX_STATUS_SHIFT;
return 1;
}