/* * Agere Systems Inc. * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs * * Copyright © 2005 Agere Systems Inc. * All rights reserved. * http://www.agere.com * *------------------------------------------------------------------------------ * * et1310_phy.c - Routines for configuring and accessing the PHY * *------------------------------------------------------------------------------ * * SOFTWARE LICENSE * * This software is provided subject to the following terms and conditions, * which you should read carefully before using the software. Using this * software indicates your acceptance of these terms and conditions. If you do * not agree with these terms and conditions, do not use the software. * * Copyright © 2005 Agere Systems Inc. * All rights reserved. * * Redistribution and use in source or binary forms, with or without * modifications, are permitted provided that the following conditions are met: * * . Redistributions of source code must retain the above copyright notice, this * list of conditions and the following Disclaimer as comments in the code as * well as in the documentation and/or other materials provided with the * distribution. * * . Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following Disclaimer in the documentation * and/or other materials provided with the distribution. * * . Neither the name of Agere Systems Inc. nor the names of the contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * Disclaimer * * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ANY * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR 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, INCLUDING, BUT NOT LIMITED TO, 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. * */ #include "et131x_version.h" #include "et131x_debug.h" #include "et131x_defs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "et1310_phy.h" #include "et1310_pm.h" #include "et1310_jagcore.h" #include "et131x_adapter.h" #include "et131x_netdev.h" #include "et131x_initpci.h" #include "et1310_address_map.h" #include "et1310_tx.h" #include "et1310_rx.h" #include "et1310_mac.h" /* Data for debugging facilities */ #ifdef CONFIG_ET131X_DEBUG extern dbg_info_t *et131x_dbginfo; #endif /* CONFIG_ET131X_DEBUG */ /* Prototypes for functions with local scope */ static int et131x_xcvr_init(struct et131x_adapter *adapter); /** * PhyMiRead - Read from the PHY through the MII Interface on the MAC * @adapter: pointer to our private adapter structure * @xcvrAddr: the address of the transciever * @xcvrReg: the register to read * @value: pointer to a 16-bit value in which the value will be stored * * Returns 0 on success, errno on failure (as defined in errno.h) */ int PhyMiRead(struct et131x_adapter *adapter, uint8_t xcvrAddr, uint8_t xcvrReg, uint16_t *value) { struct _MAC_t __iomem *mac = &adapter->CSRAddress->mac; int status = 0; uint32_t delay; MII_MGMT_ADDR_t miiAddr; MII_MGMT_CMD_t miiCmd; MII_MGMT_INDICATOR_t miiIndicator; /* Save a local copy of the registers we are dealing with so we can * set them back */ miiAddr.value = readl(&mac->mii_mgmt_addr.value); miiCmd.value = readl(&mac->mii_mgmt_cmd.value); /* Stop the current operation */ writel(0, &mac->mii_mgmt_cmd.value); /* Set up the register we need to read from on the correct PHY */ { MII_MGMT_ADDR_t mii_mgmt_addr = { 0 }; mii_mgmt_addr.bits.phy_addr = xcvrAddr; mii_mgmt_addr.bits.reg_addr = xcvrReg; writel(mii_mgmt_addr.value, &mac->mii_mgmt_addr.value); } /* Kick the read cycle off */ delay = 0; writel(0x1, &mac->mii_mgmt_cmd.value); do { udelay(50); delay++; miiIndicator.value = readl(&mac->mii_mgmt_indicator.value); } while ((miiIndicator.bits.not_valid || miiIndicator.bits.busy) && delay < 50); /* If we hit the max delay, we could not read the register */ if (delay >= 50) { DBG_WARNING(et131x_dbginfo, "xcvrReg 0x%08x could not be read\n", xcvrReg); DBG_WARNING(et131x_dbginfo, "status is 0x%08x\n", miiIndicator.value); status = -EIO; } /* If we hit here we were able to read the register and we need to * return the value to the caller */ /* TODO: make this stuff a simple readw()?! */ { MII_MGMT_STAT_t mii_mgmt_stat; mii_mgmt_stat.value = readl(&mac->mii_mgmt_stat.value); *value = (uint16_t) mii_mgmt_stat.bits.phy_stat; } /* Stop the read operation */ writel(0, &mac->mii_mgmt_cmd.value); DBG_VERBOSE(et131x_dbginfo, " xcvr_addr = 0x%02x, " "xcvr_reg = 0x%02x, " "value = 0x%04x.\n", xcvrAddr, xcvrReg, *value); /* set the registers we touched back to the state at which we entered * this function */ writel(miiAddr.value, &mac->mii_mgmt_addr.value); writel(miiCmd.value, &mac->mii_mgmt_cmd.value); return status; } /** * MiWrite - Write to a PHY register through the MII interface of the MAC * @adapter: pointer to our private adapter structure * @xcvrReg: the register to read * @value: 16-bit value to write * * Return 0 on success, errno on failure (as defined in errno.h) */ int MiWrite(struct et131x_adapter *adapter, uint8_t xcvrReg, uint16_t value) { struct _MAC_t __iomem *mac = &adapter->CSRAddress->mac; int status = 0; uint8_t xcvrAddr = adapter->Stats.xcvr_addr; uint32_t delay; MII_MGMT_ADDR_t miiAddr; MII_MGMT_CMD_t miiCmd; MII_MGMT_INDICATOR_t miiIndicator; /* Save a local copy of the registers we are dealing with so we can * set them back */ miiAddr.value = readl(&mac->mii_mgmt_addr.value); miiCmd.value = readl(&mac->mii_mgmt_cmd.value); /* Stop the current operation */ writel(0, &mac->mii_mgmt_cmd.value); /* Set up the register we need to write to on the correct PHY */ { MII_MGMT_ADDR_t mii_mgmt_addr; mii_mgmt_addr.bits.phy_addr = xcvrAddr; mii_mgmt_addr.bits.reg_addr = xcvrReg; writel(mii_mgmt_addr.value, &mac->mii_mgmt_addr.value); } /* Add the value to write to the registers to the mac */ writel(value, &mac->mii_mgmt_ctrl.value); delay = 0; do { udelay(50); delay++; miiIndicator.value = readl(&mac->mii_mgmt_indicator.value); } while (miiIndicator.bits.busy && delay < 100); /* If we hit the max delay, we could not write the register */ if (delay == 100) { uint16_t TempValue; DBG_WARNING(et131x_dbginfo, "xcvrReg 0x%08x could not be written", xcvrReg); DBG_WARNING(et131x_dbginfo, "status is 0x%08x\n", miiIndicator.value); DBG_WARNING(et131x_dbginfo, "command is 0x%08x\n", readl(&mac->mii_mgmt_cmd.value)); MiRead(adapter, xcvrReg, &TempValue); status = -EIO; } /* Stop the write operation */ writel(0, &mac->mii_mgmt_cmd.value); /* set the registers we touched back to the state at which we entered * this function */ writel(miiAddr.value, &mac->mii_mgmt_addr.value); writel(miiCmd.value, &mac->mii_mgmt_cmd.value); DBG_VERBOSE(et131x_dbginfo, " xcvr_addr = 0x%02x, " "xcvr_reg = 0x%02x, " "value = 0x%04x.\n", xcvrAddr, xcvrReg, value); return status; } /** * et131x_xcvr_find - Find the PHY ID * @adapter: pointer to our private adapter structure * * Returns 0 on success, errno on failure (as defined in errno.h) */ int et131x_xcvr_find(struct et131x_adapter *adapter) { int status = -ENODEV; uint8_t xcvr_addr; MI_IDR1_t idr1; MI_IDR2_t idr2; uint32_t xcvr_id; DBG_ENTER(et131x_dbginfo); /* We need to get xcvr id and address we just get the first one */ for (xcvr_addr = 0; xcvr_addr < 32; xcvr_addr++) { /* Read the ID from the PHY */ PhyMiRead(adapter, xcvr_addr, (uint8_t) offsetof(MI_REGS_t, idr1), &idr1.value); PhyMiRead(adapter, xcvr_addr, (uint8_t) offsetof(MI_REGS_t, idr2), &idr2.value); xcvr_id = (uint32_t) ((idr1.value << 16) | idr2.value); if ((idr1.value != 0) && (idr1.value != 0xffff)) { DBG_TRACE(et131x_dbginfo, "Xcvr addr: 0x%02x\tXcvr_id: 0x%08x\n", xcvr_addr, xcvr_id); adapter->Stats.xcvr_id = xcvr_id; adapter->Stats.xcvr_addr = xcvr_addr; status = 0; break; } } DBG_LEAVE(et131x_dbginfo); return status; } /** * et131x_setphy_normal - Set PHY for normal operation. * @adapter: pointer to our private adapter structure * * Used by Power Management to force the PHY into 10 Base T half-duplex mode, * when going to D3 in WOL mode. Also used during initialization to set the * PHY for normal operation. */ int et131x_setphy_normal(struct et131x_adapter *adapter) { int status; DBG_ENTER(et131x_dbginfo); /* Make sure the PHY is powered up */ ET1310_PhyPowerDown(adapter, 0); status = et131x_xcvr_init(adapter); DBG_LEAVE(et131x_dbginfo); return status; } /** * et131x_xcvr_init - Init the phy if we are setting it into force mode * @adapter: pointer to our private adapter structure * * Returns 0 on success, errno on failure (as defined in errno.h) */ static int et131x_xcvr_init(struct et131x_adapter *adapter) { int status = 0; MI_IMR_t imr; MI_ISR_t isr; MI_LCR2_t lcr2; DBG_ENTER(et131x_dbginfo); /* Zero out the adapter structure variable representing BMSR */ adapter->Bmsr.value = 0; MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, isr), &isr.value); MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, imr), &imr.value); /* Set the link status interrupt only. Bad behavior when link status * and auto neg are set, we run into a nested interrupt problem */ imr.bits.int_en = 0x1; imr.bits.link_status = 0x1; imr.bits.autoneg_status = 0x1; MiWrite(adapter, (uint8_t) offsetof(MI_REGS_t, imr), imr.value); /* Set the LED behavior such that LED 1 indicates speed (off = * 10Mbits, blink = 100Mbits, on = 1000Mbits) and LED 2 indicates * link and activity (on for link, blink off for activity). * * NOTE: Some customizations have been added here for specific * vendors; The LED behavior is now determined by vendor data in the * EEPROM. However, the above description is the default. */ if ((adapter->eepromData[1] & 0x4) == 0) { MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, lcr2), &lcr2.value); if ((adapter->eepromData[1] & 0x8) == 0) lcr2.bits.led_tx_rx = 0x3; else lcr2.bits.led_tx_rx = 0x4; lcr2.bits.led_link = 0xa; MiWrite(adapter, (uint8_t) offsetof(MI_REGS_t, lcr2), lcr2.value); } /* Determine if we need to go into a force mode and set it */ if (adapter->AiForceSpeed == 0 && adapter->AiForceDpx == 0) { if ((adapter->RegistryFlowControl == TxOnly) || (adapter->RegistryFlowControl == Both)) { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 4, 11, NULL); } else { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 11, NULL); } if (adapter->RegistryFlowControl == Both) { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 4, 10, NULL); } else { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 10, NULL); } /* Set the phy to autonegotiation */ ET1310_PhyAutoNeg(adapter, true); /* NOTE - Do we need this? */ ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 0, 9, NULL); DBG_LEAVE(et131x_dbginfo); return status; } else { ET1310_PhyAutoNeg(adapter, false); /* Set to the correct force mode. */ if (adapter->AiForceDpx != 1) { if ((adapter->RegistryFlowControl == TxOnly) || (adapter->RegistryFlowControl == Both)) { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 4, 11, NULL); } else { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 11, NULL); } if (adapter->RegistryFlowControl == Both) { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 4, 10, NULL); } else { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 10, NULL); } } else { ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 10, NULL); ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_CLEAR, 4, 11, NULL); } switch (adapter->AiForceSpeed) { case 10: if (adapter->AiForceDpx == 1) { TPAL_SetPhy10HalfDuplex(adapter); } else if (adapter->AiForceDpx == 2) { TPAL_SetPhy10FullDuplex(adapter); } else { TPAL_SetPhy10Force(adapter); } break; case 100: if (adapter->AiForceDpx == 1) { TPAL_SetPhy100HalfDuplex(adapter); } else if (adapter->AiForceDpx == 2) { TPAL_SetPhy100FullDuplex(adapter); } else { TPAL_SetPhy100Force(adapter); } break; case 1000: TPAL_SetPhy1000FullDuplex(adapter); break; } DBG_LEAVE(et131x_dbginfo); return status; } } void et131x_Mii_check(struct et131x_adapter *pAdapter, MI_BMSR_t bmsr, MI_BMSR_t bmsr_ints) { uint8_t ucLinkStatus; uint32_t uiAutoNegStatus; uint32_t uiSpeed; uint32_t uiDuplex; uint32_t uiMdiMdix; uint32_t uiMasterSlave; uint32_t uiPolarity; unsigned long lockflags; DBG_ENTER(et131x_dbginfo); if (bmsr_ints.bits.link_status) { if (bmsr.bits.link_status) { pAdapter->PoMgmt.TransPhyComaModeOnBoot = 20; /* Update our state variables and indicate the * connected state */ spin_lock_irqsave(&pAdapter->Lock, lockflags); pAdapter->MediaState = NETIF_STATUS_MEDIA_CONNECT; MP_CLEAR_FLAG(pAdapter, fMP_ADAPTER_LINK_DETECTION); spin_unlock_irqrestore(&pAdapter->Lock, lockflags); /* Don't indicate state if we're in loopback mode */ if (pAdapter->RegistryPhyLoopbk == false) { netif_carrier_on(pAdapter->netdev); } } else { DBG_WARNING(et131x_dbginfo, "Link down cable problem\n"); if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_10MBPS) { // NOTE - Is there a way to query this without TruePHY? // && TRU_QueryCoreType(pAdapter->hTruePhy, 0) == EMI_TRUEPHY_A13O) { uint16_t Register18; MiRead(pAdapter, 0x12, &Register18); MiWrite(pAdapter, 0x12, Register18 | 0x4); MiWrite(pAdapter, 0x10, Register18 | 0x8402); MiWrite(pAdapter, 0x11, Register18 | 511); MiWrite(pAdapter, 0x12, Register18); } /* For the first N seconds of life, we are in "link * detection" When we are in this state, we should * only report "connected". When the LinkDetection * Timer expires, we can report disconnected (handled * in the LinkDetectionDPC). */ if ((MP_IS_FLAG_CLEAR (pAdapter, fMP_ADAPTER_LINK_DETECTION)) || (pAdapter->MediaState == NETIF_STATUS_MEDIA_DISCONNECT)) { spin_lock_irqsave(&pAdapter->Lock, lockflags); pAdapter->MediaState = NETIF_STATUS_MEDIA_DISCONNECT; spin_unlock_irqrestore(&pAdapter->Lock, lockflags); /* Only indicate state if we're in loopback * mode */ if (pAdapter->RegistryPhyLoopbk == false) { netif_carrier_off(pAdapter->netdev); } } pAdapter->uiLinkSpeed = 0; pAdapter->uiDuplexMode = 0; /* Free the packets being actively sent & stopped */ et131x_free_busy_send_packets(pAdapter); /* Re-initialize the send structures */ et131x_init_send(pAdapter); /* Reset the RFD list and re-start RU */ et131x_reset_recv(pAdapter); /* * Bring the device back to the state it was during * init prior to autonegotiation being complete. This * way, when we get the auto-neg complete interrupt, * we can complete init by calling ConfigMacREGS2. */ et131x_soft_reset(pAdapter); /* Setup ET1310 as per the documentation */ et131x_adapter_setup(pAdapter); /* Setup the PHY into coma mode until the cable is * plugged back in */ if (pAdapter->RegistryPhyComa == 1) { EnablePhyComa(pAdapter); } } } if (bmsr_ints.bits.auto_neg_complete || ((pAdapter->AiForceDpx == 3) && (bmsr_ints.bits.link_status))) { if (bmsr.bits.auto_neg_complete || (pAdapter->AiForceDpx == 3)) { ET1310_PhyLinkStatus(pAdapter, &ucLinkStatus, &uiAutoNegStatus, &uiSpeed, &uiDuplex, &uiMdiMdix, &uiMasterSlave, &uiPolarity); pAdapter->uiLinkSpeed = uiSpeed; pAdapter->uiDuplexMode = uiDuplex; DBG_TRACE(et131x_dbginfo, "pAdapter->uiLinkSpeed 0x%04x, pAdapter->uiDuplex 0x%08x\n", pAdapter->uiLinkSpeed, pAdapter->uiDuplexMode); pAdapter->PoMgmt.TransPhyComaModeOnBoot = 20; if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_10MBPS) { // NOTE - Is there a way to query this without TruePHY? // && TRU_QueryCoreType(pAdapter->hTruePhy, 0) == EMI_TRUEPHY_A13O) { uint16_t Register18; MiRead(pAdapter, 0x12, &Register18); MiWrite(pAdapter, 0x12, Register18 | 0x4); MiWrite(pAdapter, 0x10, Register18 | 0x8402); MiWrite(pAdapter, 0x11, Register18 | 511); MiWrite(pAdapter, 0x12, Register18); } ConfigFlowControl(pAdapter); if ((pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) && (pAdapter->RegistryJumboPacket > 2048)) { ET1310_PhyAndOrReg(pAdapter, 0x16, 0xcfff, 0x2000); } SetRxDmaTimer(pAdapter); ConfigMACRegs2(pAdapter); } } DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy10HalfDuplex - Force the phy into 10 Base T Half Duplex mode. * @pAdapter: pointer to the adapter structure * * Also sets the MAC so it is syncd up properly */ void TPAL_SetPhy10HalfDuplex(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* First we need to turn off all other advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Set our advertise values accordingly */ ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_HALF); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy10FullDuplex - Force the phy into 10 Base T Full Duplex mode. * @pAdapter: pointer to the adapter structure * * Also sets the MAC so it is syncd up properly */ void TPAL_SetPhy10FullDuplex(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* First we need to turn off all other advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Set our advertise values accordingly */ ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy10Force - Force Base-T FD mode WITHOUT using autonegotiation * @pAdapter: pointer to the adapter structure */ void TPAL_SetPhy10Force(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* Disable autoneg */ ET1310_PhyAutoNeg(pAdapter, false); /* Disable all advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Force 10 Mbps */ ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_10MBPS); /* Force Full duplex */ ET1310_PhyDuplexMode(pAdapter, TRUEPHY_DUPLEX_FULL); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy100HalfDuplex - Force 100 Base T Half Duplex mode. * @pAdapter: pointer to the adapter structure * * Also sets the MAC so it is syncd up properly. */ void TPAL_SetPhy100HalfDuplex(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* first we need to turn off all other advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Set our advertise values accordingly */ ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_HALF); /* Set speed */ ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_100MBPS); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy100FullDuplex - Force 100 Base T Full Duplex mode. * @pAdapter: pointer to the adapter structure * * Also sets the MAC so it is syncd up properly */ void TPAL_SetPhy100FullDuplex(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* First we need to turn off all other advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Set our advertise values accordingly */ ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy100Force - Force 100 BaseT FD mode WITHOUT using autonegotiation * @pAdapter: pointer to the adapter structure */ void TPAL_SetPhy100Force(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* Disable autoneg */ ET1310_PhyAutoNeg(pAdapter, false); /* Disable all advertisement */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* Force 100 Mbps */ ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_100MBPS); /* Force Full duplex */ ET1310_PhyDuplexMode(pAdapter, TRUEPHY_DUPLEX_FULL); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhy1000FullDuplex - Force 1000 Base T Full Duplex mode * @pAdapter: pointer to the adapter structure * * Also sets the MAC so it is syncd up properly. */ void TPAL_SetPhy1000FullDuplex(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* first we need to turn off all other advertisement */ ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); /* set our advertise values accordingly */ ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL); /* power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /** * TPAL_SetPhyAutoNeg - Set phy to autonegotiation mode. * @pAdapter: pointer to the adapter structure */ void TPAL_SetPhyAutoNeg(struct et131x_adapter *pAdapter) { DBG_ENTER(et131x_dbginfo); /* Power down PHY */ ET1310_PhyPowerDown(pAdapter, 1); /* Turn on advertisement of all capabilities */ ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_BOTH); ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_BOTH); if (pAdapter->DeviceID != ET131X_PCI_DEVICE_ID_FAST) { ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL); } else { ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE); } /* Make sure auto-neg is ON (it is disabled in FORCE modes) */ ET1310_PhyAutoNeg(pAdapter, true); /* Power up PHY */ ET1310_PhyPowerDown(pAdapter, 0); DBG_LEAVE(et131x_dbginfo); } /* * The routines which follow provide low-level access to the PHY, and are used * primarily by the routines above (although there are a few places elsewhere * in the driver where this level of access is required). */ static const uint16_t ConfigPhy[25][2] = { /* Reg Value Register */ /* Addr */ {0x880B, 0x0926}, /* AfeIfCreg4B1000Msbs */ {0x880C, 0x0926}, /* AfeIfCreg4B100Msbs */ {0x880D, 0x0926}, /* AfeIfCreg4B10Msbs */ {0x880E, 0xB4D3}, /* AfeIfCreg4B1000Lsbs */ {0x880F, 0xB4D3}, /* AfeIfCreg4B100Lsbs */ {0x8810, 0xB4D3}, /* AfeIfCreg4B10Lsbs */ {0x8805, 0xB03E}, /* AfeIfCreg3B1000Msbs */ {0x8806, 0xB03E}, /* AfeIfCreg3B100Msbs */ {0x8807, 0xFF00}, /* AfeIfCreg3B10Msbs */ {0x8808, 0xE090}, /* AfeIfCreg3B1000Lsbs */ {0x8809, 0xE110}, /* AfeIfCreg3B100Lsbs */ {0x880A, 0x0000}, /* AfeIfCreg3B10Lsbs */ {0x300D, 1}, /* DisableNorm */ {0x280C, 0x0180}, /* LinkHoldEnd */ {0x1C21, 0x0002}, /* AlphaM */ {0x3821, 6}, /* FfeLkgTx0 */ {0x381D, 1}, /* FfeLkg1g4 */ {0x381E, 1}, /* FfeLkg1g5 */ {0x381F, 1}, /* FfeLkg1g6 */ {0x3820, 1}, /* FfeLkg1g7 */ {0x8402, 0x01F0}, /* Btinact */ {0x800E, 20}, /* LftrainTime */ {0x800F, 24}, /* DvguardTime */ {0x8010, 46}, /* IdlguardTime */ {0, 0} }; /* condensed version of the phy initialization routine */ void ET1310_PhyInit(struct et131x_adapter *pAdapter) { uint16_t usData, usIndex; if (pAdapter == NULL) { return; } // get the identity (again ?) MiRead(pAdapter, PHY_ID_1, &usData); MiRead(pAdapter, PHY_ID_2, &usData); // what does this do/achieve ? MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData); // should read 0002 MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0006); // read modem register 0402, should I do something with the return data ? MiWrite(pAdapter, PHY_INDEX_REG, 0x0402); MiRead(pAdapter, PHY_DATA_REG, &usData); // what does this do/achieve ? MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002); // get the identity (again ?) MiRead(pAdapter, PHY_ID_1, &usData); MiRead(pAdapter, PHY_ID_2, &usData); // what does this achieve ? MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData); // should read 0002 MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0006); // read modem register 0402, should I do something with the return data? MiWrite(pAdapter, PHY_INDEX_REG, 0x0402); MiRead(pAdapter, PHY_DATA_REG, &usData); MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002); // what does this achieve (should return 0x1040) MiRead(pAdapter, PHY_CONTROL, &usData); MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData); // should read 0002 MiWrite(pAdapter, PHY_CONTROL, 0x1840); MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0007); // here the writing of the array starts.... usIndex = 0; while (ConfigPhy[usIndex][0] != 0x0000) { // write value MiWrite(pAdapter, PHY_INDEX_REG, ConfigPhy[usIndex][0]); MiWrite(pAdapter, PHY_DATA_REG, ConfigPhy[usIndex][1]); // read it back MiWrite(pAdapter, PHY_INDEX_REG, ConfigPhy[usIndex][0]); MiRead(pAdapter, PHY_DATA_REG, &usData); // do a check on the value read back ? usIndex++; } // here the writing of the array ends... MiRead(pAdapter, PHY_CONTROL, &usData); // 0x1840 MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData); // should read 0007 MiWrite(pAdapter, PHY_CONTROL, 0x1040); MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002); } void ET1310_PhyReset(struct et131x_adapter *pAdapter) { MiWrite(pAdapter, PHY_CONTROL, 0x8000); } void ET1310_PhyPowerDown(struct et131x_adapter *pAdapter, bool down) { uint16_t usData; MiRead(pAdapter, PHY_CONTROL, &usData); if (down == false) { // Power UP usData &= ~0x0800; MiWrite(pAdapter, PHY_CONTROL, usData); } else { // Power DOWN usData |= 0x0800; MiWrite(pAdapter, PHY_CONTROL, usData); } } void ET1310_PhyAutoNeg(struct et131x_adapter *pAdapter, bool enable) { uint16_t usData; MiRead(pAdapter, PHY_CONTROL, &usData); if (enable == true) { // Autonegotiation ON usData |= 0x1000; MiWrite(pAdapter, PHY_CONTROL, usData); } else { // Autonegotiation OFF usData &= ~0x1000; MiWrite(pAdapter, PHY_CONTROL, usData); } } void ET1310_PhyDuplexMode(struct et131x_adapter *pAdapter, uint16_t duplex) { uint16_t usData; MiRead(pAdapter, PHY_CONTROL, &usData); if (duplex == TRUEPHY_DUPLEX_FULL) { // Set Full Duplex usData |= 0x100; MiWrite(pAdapter, PHY_CONTROL, usData); } else { // Set Half Duplex usData &= ~0x100; MiWrite(pAdapter, PHY_CONTROL, usData); } } void ET1310_PhySpeedSelect(struct et131x_adapter *pAdapter, uint16_t speed) { uint16_t usData; // Read the PHY control register MiRead(pAdapter, PHY_CONTROL, &usData); // Clear all Speed settings (Bits 6, 13) usData &= ~0x2040; // Reset the speed bits based on user selection switch (speed) { case TRUEPHY_SPEED_10MBPS: // Bits already cleared above, do nothing break; case TRUEPHY_SPEED_100MBPS: // 100M == Set bit 13 usData |= 0x2000; break; case TRUEPHY_SPEED_1000MBPS: default: usData |= 0x0040; break; } // Write back the new speed MiWrite(pAdapter, PHY_CONTROL, usData); } void ET1310_PhyAdvertise1000BaseT(struct et131x_adapter *pAdapter, uint16_t duplex) { uint16_t usData; // Read the PHY 1000 Base-T Control Register MiRead(pAdapter, PHY_1000_CONTROL, &usData); // Clear Bits 8,9 usData &= ~0x0300; switch (duplex) { case TRUEPHY_ADV_DUPLEX_NONE: // Duplex already cleared, do nothing break; case TRUEPHY_ADV_DUPLEX_FULL: // Set Bit 9 usData |= 0x0200; break; case TRUEPHY_ADV_DUPLEX_HALF: // Set Bit 8 usData |= 0x0100; break; case TRUEPHY_ADV_DUPLEX_BOTH: default: usData |= 0x0300; break; } // Write back advertisement MiWrite(pAdapter, PHY_1000_CONTROL, usData); } void ET1310_PhyAdvertise100BaseT(struct et131x_adapter *pAdapter, uint16_t duplex) { uint16_t usData; // Read the Autonegotiation Register (10/100) MiRead(pAdapter, PHY_AUTO_ADVERTISEMENT, &usData); // Clear bits 7,8 usData &= ~0x0180; switch (duplex) { case TRUEPHY_ADV_DUPLEX_NONE: // Duplex already cleared, do nothing break; case TRUEPHY_ADV_DUPLEX_FULL: // Set Bit 8 usData |= 0x0100; break; case TRUEPHY_ADV_DUPLEX_HALF: // Set Bit 7 usData |= 0x0080; break; case TRUEPHY_ADV_DUPLEX_BOTH: default: // Set Bits 7,8 usData |= 0x0180; break; } // Write back advertisement MiWrite(pAdapter, PHY_AUTO_ADVERTISEMENT, usData); } void ET1310_PhyAdvertise10BaseT(struct et131x_adapter *pAdapter, uint16_t duplex) { uint16_t usData; // Read the Autonegotiation Register (10/100) MiRead(pAdapter, PHY_AUTO_ADVERTISEMENT, &usData); // Clear bits 5,6 usData &= ~0x0060; switch (duplex) { case TRUEPHY_ADV_DUPLEX_NONE: // Duplex already cleared, do nothing break; case TRUEPHY_ADV_DUPLEX_FULL: // Set Bit 6 usData |= 0x0040; break; case TRUEPHY_ADV_DUPLEX_HALF: // Set Bit 5 usData |= 0x0020; break; case TRUEPHY_ADV_DUPLEX_BOTH: default: // Set Bits 5,6 usData |= 0x0060; break; } // Write back advertisement MiWrite(pAdapter, PHY_AUTO_ADVERTISEMENT, usData); } void ET1310_PhyLinkStatus(struct et131x_adapter *pAdapter, uint8_t *ucLinkStatus, uint32_t *uiAutoNeg, uint32_t *uiLinkSpeed, uint32_t *uiDuplexMode, uint32_t *uiMdiMdix, uint32_t *uiMasterSlave, uint32_t *uiPolarity) { uint16_t usMiStatus = 0; uint16_t us1000BaseT = 0; uint16_t usVmiPhyStatus = 0; uint16_t usControl = 0; MiRead(pAdapter, PHY_STATUS, &usMiStatus); MiRead(pAdapter, PHY_1000_STATUS, &us1000BaseT); MiRead(pAdapter, PHY_PHY_STATUS, &usVmiPhyStatus); MiRead(pAdapter, PHY_CONTROL, &usControl); if (ucLinkStatus) { *ucLinkStatus = (unsigned char)((usVmiPhyStatus & 0x0040) ? 1 : 0); } if (uiAutoNeg) { *uiAutoNeg = (usControl & 0x1000) ? ((usVmiPhyStatus & 0x0020) ? TRUEPHY_ANEG_COMPLETE : TRUEPHY_ANEG_NOT_COMPLETE) : TRUEPHY_ANEG_DISABLED; } if (uiLinkSpeed) { *uiLinkSpeed = (usVmiPhyStatus & 0x0300) >> 8; } if (uiDuplexMode) { *uiDuplexMode = (usVmiPhyStatus & 0x0080) >> 7; } if (uiMdiMdix) { /* NOTE: Need to complete this */ *uiMdiMdix = 0; } if (uiMasterSlave) { *uiMasterSlave = (us1000BaseT & 0x4000) ? TRUEPHY_CFG_MASTER : TRUEPHY_CFG_SLAVE; } if (uiPolarity) { *uiPolarity = (usVmiPhyStatus & 0x0400) ? TRUEPHY_POLARITY_INVERTED : TRUEPHY_POLARITY_NORMAL; } } void ET1310_PhyAndOrReg(struct et131x_adapter *pAdapter, uint16_t regnum, uint16_t andMask, uint16_t orMask) { uint16_t reg; // Read the requested register MiRead(pAdapter, regnum, ®); // Apply the AND mask reg &= andMask; // Apply the OR mask reg |= orMask; // Write the value back to the register MiWrite(pAdapter, regnum, reg); } void ET1310_PhyAccessMiBit(struct et131x_adapter *pAdapter, uint16_t action, uint16_t regnum, uint16_t bitnum, uint8_t *value) { uint16_t reg; uint16_t mask = 0; // Create a mask to isolate the requested bit mask = 0x0001 << bitnum; // Read the requested register MiRead(pAdapter, regnum, ®); switch (action) { case TRUEPHY_BIT_READ: if (value != NULL) { *value = (reg & mask) >> bitnum; } break; case TRUEPHY_BIT_SET: reg |= mask; MiWrite(pAdapter, regnum, reg); break; case TRUEPHY_BIT_CLEAR: reg &= ~mask; MiWrite(pAdapter, regnum, reg); break; default: break; } }