📄 e100_phy.c
字号:
/******************************************************************************* Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Linux NICS <linux.nics@intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497*******************************************************************************/#include "e100_phy.h"void e100_handle_zlock(struct e100_private *bdp);/* * Procedure: e100_mdi_write * * Description: This routine will write a value to the specified MII register * of an external MDI compliant device (e.g. PHY 100). The * command will execute in polled mode. * * Arguments: * bdp - Ptr to this card's e100_bdconfig structure * reg_addr - The MII register that we are writing to * phy_addr - The MDI address of the Phy component. * data - The value that we are writing to the MII register. * * Returns: * NOTHING */inte100_mdi_write(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 data){ int e100_retry; u32 temp_val; unsigned int mdi_cntrl; spin_lock_bh(&bdp->mdi_access_lock); temp_val = (((u32) data) | (reg_addr << 16) | (phy_addr << 21) | (MDI_WRITE << 26)); writel(temp_val, &bdp->scb->scb_mdi_cntrl); readw(&bdp->scb->scb_status); /* wait 20usec before checking status */ udelay(20); /* poll for the mdi write to complete */ e100_retry = E100_CMD_WAIT; while ((!((mdi_cntrl = readl(&bdp->scb->scb_mdi_cntrl)) & MDI_PHY_READY)) && (e100_retry)) { udelay(20); e100_retry--; } spin_unlock_bh(&bdp->mdi_access_lock); if (mdi_cntrl & MDI_PHY_READY) return 0; else { printk(KERN_ERR "e100: MDI write timeout\n"); return 1; }}/* * Procedure: e100_mdi_read * * Description: This routine will read a value from the specified MII register * of an external MDI compliant device (e.g. PHY 100), and return * it to the calling routine. The command will execute in polled * mode. * * Arguments: * bdp - Ptr to this card's e100_bdconfig structure * reg_addr - The MII register that we are reading from * phy_addr - The MDI address of the Phy component. * * Results: * data - The value that we read from the MII register. * * Returns: * NOTHING */inte100_mdi_read(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 *data){ int e100_retry; u32 temp_val; unsigned int mdi_cntrl; spin_lock_bh(&bdp->mdi_access_lock); /* Issue the read command to the MDI control register. */ temp_val = ((reg_addr << 16) | (phy_addr << 21) | (MDI_READ << 26)); writel(temp_val, &bdp->scb->scb_mdi_cntrl); readw(&bdp->scb->scb_status); /* wait 20usec before checking status */ udelay(20); /* poll for the mdi read to complete */ e100_retry = E100_CMD_WAIT; while ((!((mdi_cntrl = readl(&bdp->scb->scb_mdi_cntrl)) & MDI_PHY_READY)) && (e100_retry)) { udelay(20); e100_retry--; } spin_unlock_bh(&bdp->mdi_access_lock); if (mdi_cntrl & MDI_PHY_READY) { /* return the lower word */ *data = (u16) mdi_cntrl; return 0; } else { printk(KERN_ERR "e100: MDI read timeout\n"); return 1; }}static unsigned char __devinite100_phy_valid(struct e100_private *bdp, unsigned int phy_address){ u16 ctrl_reg, stat_reg; /* Read the MDI control register */ e100_mdi_read(bdp, MII_BMCR, phy_address, &ctrl_reg); /* Read the status register twice, bacause of sticky bits */ e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); if ((ctrl_reg == 0xffff) || ((stat_reg == 0) && (ctrl_reg == 0))) return false; return true;}static void __devinite100_phy_address_detect(struct e100_private *bdp){ unsigned int addr; unsigned char valid_phy_found = false; if (IS_NC3133(bdp)) { bdp->phy_addr = 0; return; } if (e100_phy_valid(bdp, PHY_DEFAULT_ADDRESS)) { bdp->phy_addr = PHY_DEFAULT_ADDRESS; valid_phy_found = true; } else { for (addr = MIN_PHY_ADDR; addr <= MAX_PHY_ADDR; addr++) { if (e100_phy_valid(bdp, addr)) { bdp->phy_addr = addr; valid_phy_found = true; break; } } } if (!valid_phy_found) { bdp->phy_addr = PHY_ADDRESS_503; }}static void __devinite100_phy_id_detect(struct e100_private *bdp){ u16 low_id_reg, high_id_reg; if (bdp->phy_addr == PHY_ADDRESS_503) { bdp->PhyId = PHY_503; return; } if (!(bdp->flags & IS_ICH)) { if (bdp->rev_id >= D102_REV_ID) { bdp->PhyId = PHY_82562ET; return; } } /* Read phy id from the MII register */ e100_mdi_read(bdp, MII_PHYSID1, bdp->phy_addr, &low_id_reg); e100_mdi_read(bdp, MII_PHYSID2, bdp->phy_addr, &high_id_reg); bdp->PhyId = ((unsigned int) low_id_reg | ((unsigned int) high_id_reg << 16));}static void __devinite100_phy_isolate(struct e100_private *bdp){ unsigned int phy_address; u16 ctrl_reg; /* Go over all phy addresses. Deisolate the selected one, and isolate * all the rest */ for (phy_address = 0; phy_address <= MAX_PHY_ADDR; phy_address++) { if (phy_address != bdp->phy_addr) { e100_mdi_write(bdp, MII_BMCR, phy_address, BMCR_ISOLATE); } else { e100_mdi_read(bdp, MII_BMCR, bdp->phy_addr, &ctrl_reg); ctrl_reg &= ~BMCR_ISOLATE; e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, ctrl_reg); } udelay(100); }}static unsigned char __devinite100_phy_specific_setup(struct e100_private *bdp){ u16 misc_reg; if (bdp->phy_addr == PHY_ADDRESS_503) { switch (bdp->params.e100_speed_duplex) { case E100_AUTONEG: /* The adapter can't autoneg. so set to 10/HALF */ printk(KERN_INFO "e100: 503 serial component detected which " "cannot autonegotiate\n"); printk(KERN_INFO "e100: speed/duplex forced to " "10Mbps / Half duplex\n"); bdp->params.e100_speed_duplex = E100_SPEED_10_HALF; break; case E100_SPEED_100_HALF: case E100_SPEED_100_FULL: printk(KERN_ERR "e100: 503 serial component detected " "which does not support 100Mbps\n"); printk(KERN_ERR "e100: Change the forced speed/duplex " "to a supported setting\n"); return false; } return true; } if (IS_NC3133(bdp)) { u16 int_reg; /* enable 100BASE fiber interface */ e100_mdi_write(bdp, MDI_NC3133_CONFIG_REG, bdp->phy_addr, MDI_NC3133_100FX_ENABLE); if ((bdp->params.e100_speed_duplex != E100_AUTONEG) && (bdp->params.e100_speed_duplex != E100_SPEED_100_FULL)) { /* just inform user about 100 full */ printk(KERN_ERR "e100: NC3133 NIC can only run " "at 100Mbps full duplex\n"); } bdp->params.e100_speed_duplex = E100_SPEED_100_FULL; /* enable interrupts */ e100_mdi_read(bdp, MDI_NC3133_INT_ENABLE_REG, bdp->phy_addr, &int_reg); int_reg |= MDI_NC3133_INT_ENABLE; e100_mdi_write(bdp, MDI_NC3133_INT_ENABLE_REG, bdp->phy_addr, int_reg); } /* Handle the National TX */ if ((bdp->PhyId & PHY_MODEL_REV_ID_MASK) == PHY_NSC_TX) { e100_mdi_read(bdp, NSC_CONG_CONTROL_REG, bdp->phy_addr, &misc_reg); misc_reg |= NSC_TX_CONG_TXREADY; /* disable the congestion control bit in the National Phy */ misc_reg &= ~NSC_TX_CONG_ENABLE; e100_mdi_write(bdp, NSC_CONG_CONTROL_REG, bdp->phy_addr, misc_reg); } return true;}/* * Procedure: e100_phy_fix_squelch * * Description: * Help find link on certain rare scenarios. * NOTE: This routine must be called once per watchdog, * and *after* setting the current link state. * * Arguments: * bdp - Ptr to this card's e100_bdconfig structure * * Returns: * NOTHING */static voide100_phy_fix_squelch(struct e100_private *bdp){ if ((bdp->PhyId != PHY_82555_TX) || (bdp->flags & DF_SPEED_FORCED)) return; if (netif_carrier_ok(bdp->device)) { switch (bdp->PhyState) { case 0: break; case 1: e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, 0x0000); break; case 2: e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, bdp->phy_addr, 0x3000); break; } bdp->PhyState = 0; bdp->PhyDelay = 0; } else if (!bdp->PhyDelay--) { switch (bdp->PhyState) { case 0: e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, EXTENDED_SQUELCH_BIT); bdp->PhyState = 1; break; case 1: e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, 0x0000); e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, bdp->phy_addr, 0x2010); bdp->PhyState = 2; break; case 2: e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, bdp->phy_addr, 0x3000); bdp->PhyState = 0; break; } e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, BMCR_ANENABLE | BMCR_ANRESTART); bdp->PhyDelay = 3; }}/* * Procedure: e100_fix_polarity * * Description: * Fix for 82555 auto-polarity toggle problem. With a short cable * connecting an 82555 with an 840A link partner, if the medium is noisy, * the 82555 sometime thinks that the polarity might be wrong and so * toggles polarity. This happens repeatedly and results in a high bit * error rate. * NOTE: This happens only at 10 Mbps * * Arguments: * bdp - Ptr to this card's e100_bdconfig structure * * Returns: * NOTHING */static void __devinite100_fix_polarity(struct e100_private *bdp){ u16 status; u16 errors; u16 misc_reg; int speed; if ((bdp->PhyId != PHY_82555_TX) && (bdp->PhyId != PHY_82562ET) && (bdp->PhyId != PHY_82562EM)) return; /* If the user wants auto-polarity disabled, do only that and nothing * * else. * e100_autopolarity == 0 means disable --- we do just the * disabling * e100_autopolarity == 1 means enable --- we do nothing at * all * e100_autopolarity >= 2 means we do the workaround code. */ /* Change for 82558 enhancement */ switch (E100_AUTOPOLARITY) { case 0: e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, &misc_reg); e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, (u16) (misc_reg | DISABLE_AUTO_POLARITY)); break; case 1: e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, &misc_reg); e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, (u16) (misc_reg & ~DISABLE_AUTO_POLARITY)); break; case 2: /* we do this only if link is up */ if (!netif_carrier_ok(bdp->device)) { break; } e100_mdi_read(bdp, PHY_82555_CSR, bdp->phy_addr, &status); speed = (status & PHY_82555_SPEED_BIT) ? 100 : 10; /* we need to do this only if speed is 10 */ if (speed != 10) { break; } /* see if we have any end of frame errors */ e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, bdp->phy_addr, &errors); /* if non-zero, wait for 100 ms before reading again */ if (errors) { udelay(200); e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, bdp->phy_addr, &errors); /* if non-zero again, we disable polarity */ if (errors) { e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, &misc_reg); e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, (u16) (misc_reg | DISABLE_AUTO_POLARITY)); } } if (!errors) { /* it is safe to read the polarity now */ e100_mdi_read(bdp, PHY_82555_CSR, bdp->phy_addr, &status); /* if polarity is normal, disable polarity */ if (!(status & PHY_82555_POLARITY_BIT)) { e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, &misc_reg); e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, (u16) (misc_reg | DISABLE_AUTO_POLARITY)); } } break; default: break; }}/* * Procedure: e100_find_speed_duplex * * Description: This routine will figure out what line speed and duplex mode * the PHY is currently using. * * Arguments: * bdp - Ptr to this card's e100_bdconfig structure * * Returns: * NOTHING */static voide100_find_speed_duplex(struct e100_private *bdp){ unsigned int PhyId; u16 stat_reg, misc_reg; u16 ad_reg, lp_ad_reg; PhyId = bdp->PhyId & PHY_MODEL_REV_ID_MASK; /* First we should check to see if we have link */ /* If we don't have a link no reason to print a speed and duplex */ if (!e100_update_link_state(bdp)) { bdp->cur_line_speed = 0; bdp->cur_dplx_mode = 0; return; } /* On the 82559 and later controllers, speed/duplex is part of the * * SCB. So, we save an mdi_read and get these from the SCB. * */ if (bdp->rev_id >= D101MA_REV_ID) { /* Read speed */ if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_1) bdp->cur_line_speed = 100; else bdp->cur_line_speed = 10; /* Read duplex */ if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_2) bdp->cur_dplx_mode = FULL_DUPLEX; else bdp->cur_dplx_mode = HALF_DUPLEX; return; } /* If this is a Phy 100, then read bits 1 and 0 of extended register 0, * to get the current speed and duplex settings. */ if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_82555_TX)) { /* Read Phy 100 extended register 0 */ e100_mdi_read(bdp, EXTENDED_REG_0, bdp->phy_addr, &misc_reg); /* Get current speed setting */ if (misc_reg & PHY_100_ER0_SPEED_INDIC) bdp->cur_line_speed = 100; else bdp->cur_line_speed = 10; /* Get current duplex setting -- FDX enabled if bit is set */ if (misc_reg & PHY_100_ER0_FDX_INDIC) bdp->cur_dplx_mode = FULL_DUPLEX; else bdp->cur_dplx_mode = HALF_DUPLEX; return; } /* See if link partner is capable of Auto-Negotiation (bit 0, reg 6) */ e100_mdi_read(bdp, MII_EXPANSION, bdp->phy_addr, &misc_reg); /* See if Auto-Negotiation was complete (bit 5, reg 1) */ e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); /* If a True NWAY connection was made, then we can detect speed/dplx * by ANDing our adapter's advertised abilities with our link partner's * advertised ablilities, and then assuming that the highest common * denominator was chosed by NWAY. */ if ((misc_reg & EXPANSION_NWAY) && (stat_reg & BMSR_ANEGCOMPLETE)) { /* Read our advertisement register */ e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &ad_reg); /* Read our link partner's advertisement register */ e100_mdi_read(bdp, MII_LPA, bdp->phy_addr, &lp_ad_reg); /* AND the two advertisement registers together, and get rid * of any extraneous bits. */ ad_reg &= (lp_ad_reg & NWAY_LP_ABILITY); /* Get speed setting */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -