diff options
Diffstat (limited to 'drivers/staging/et131x/et1310_phy.c')
-rw-r--r-- | drivers/staging/et131x/et1310_phy.c | 1281 |
1 files changed, 1281 insertions, 0 deletions
diff --git a/drivers/staging/et131x/et1310_phy.c b/drivers/staging/et131x/et1310_phy.c new file mode 100644 index 00000000000..6c4fa54419e --- /dev/null +++ b/drivers/staging/et131x/et1310_phy.c @@ -0,0 +1,1281 @@ +/* + * 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 <linux/pci.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> +#include <linux/random.h> +#include <linux/delay.h> + +#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_jagcore.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; + } +} |