| /** @file | |
| * | |
| * Copyright (c) 2012-2014, ARM Limited. All rights reserved. | |
| * | |
| * This program and the accompanying materials | |
| * are licensed and made available under the terms and conditions of the BSD License | |
| * which accompanies this distribution. The full text of the license may be found at | |
| * http://opensource.org/licenses/bsd-license.php | |
| * | |
| * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| * | |
| **/ | |
| #include "Lan9118Dxe.h" | |
| STATIC EFI_MAC_ADDRESS mZeroMac = { { 0 } }; | |
| /** | |
| This internal function reverses bits for 32bit data. | |
| @param Value The data to be reversed. | |
| @return Data reversed. | |
| **/ | |
| UINT32 | |
| ReverseBits ( | |
| UINT32 Value | |
| ) | |
| { | |
| UINTN Index; | |
| UINT32 NewValue; | |
| NewValue = 0; | |
| for (Index = 0; Index < 32; Index++) { | |
| if ((Value & (1 << Index)) != 0) { | |
| NewValue = NewValue | (1 << (31 - Index)); | |
| } | |
| } | |
| return NewValue; | |
| } | |
| /* | |
| ** Create Ethernet CRC | |
| ** | |
| ** INFO USED: | |
| ** 1: http://en.wikipedia.org/wiki/Cyclic_redundancy_check | |
| ** | |
| ** 2: http://www.erg.abdn.ac.uk/~gorry/eg3567/dl-pages/crc.html | |
| ** | |
| ** 3: http://en.wikipedia.org/wiki/Computation_of_CRC | |
| */ | |
| UINT32 | |
| GenEtherCrc32 ( | |
| IN EFI_MAC_ADDRESS *Mac, | |
| IN UINT32 AddrLen | |
| ) | |
| { | |
| INT32 Iter; | |
| UINT32 Remainder; | |
| UINT8 *Ptr; | |
| Iter = 0; | |
| Remainder = 0xFFFFFFFF; // 0xFFFFFFFF is standard seed for Ethernet | |
| // Convert Mac Address to array of bytes | |
| Ptr = (UINT8*)Mac; | |
| // Generate the Crc bit-by-bit (LSB first) | |
| while (AddrLen--) { | |
| Remainder ^= *Ptr++; | |
| for (Iter = 0;Iter < 8;Iter++) { | |
| // Check if exponent is set | |
| if (Remainder & 1) { | |
| Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL; | |
| } else { | |
| Remainder = (Remainder >> 1) ^ 0; | |
| } | |
| } | |
| } | |
| // Reverse the bits before returning (to Big Endian) | |
| //TODO: Need to be reviewed. Do we want to do a bit reverse or a byte reverse (in this case use SwapBytes32()) | |
| return ReverseBits (Remainder); | |
| } | |
| // Function to read from MAC indirect registers | |
| UINT32 | |
| IndirectMACRead32 ( | |
| UINT32 Index | |
| ) | |
| { | |
| UINT32 MacCSR; | |
| // Check index is in the range | |
| ASSERT(Index <= 12); | |
| // Wait until CSR busy bit is cleared | |
| while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY); | |
| // Set CSR busy bit to ensure read will occur | |
| // Set the R/W bit to indicate we are reading | |
| // Set the index of CSR Address to access desired register | |
| MacCSR = MAC_CSR_BUSY | MAC_CSR_READ | MAC_CSR_ADDR(Index); | |
| // Write to the register | |
| MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR); | |
| // Wait until CSR busy bit is cleared | |
| while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY); | |
| // Now read from data register to get read value | |
| return MmioRead32 (LAN9118_MAC_CSR_DATA); | |
| } | |
| // Function to write to MAC indirect registers | |
| UINT32 | |
| IndirectMACWrite32 ( | |
| UINT32 Index, | |
| UINT32 Value | |
| ) | |
| { | |
| UINT32 ValueWritten; | |
| UINT32 MacCSR; | |
| // Check index is in the range | |
| ASSERT(Index <= 12); | |
| // Wait until CSR busy bit is cleared | |
| while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY); | |
| // Set CSR busy bit to ensure read will occur | |
| // Set the R/W bit to indicate we are writing | |
| // Set the index of CSR Address to access desired register | |
| MacCSR = MAC_CSR_BUSY | MAC_CSR_WRITE | MAC_CSR_ADDR(Index); | |
| // Now write the value to the register before issuing the write command | |
| ValueWritten = MmioWrite32 (LAN9118_MAC_CSR_DATA, Value); | |
| // Write the config to the register | |
| MmioWrite32 (LAN9118_MAC_CSR_CMD, MacCSR); | |
| // Wait until CSR busy bit is cleared | |
| while ((MmioRead32 (LAN9118_MAC_CSR_CMD) & MAC_CSR_BUSY) == MAC_CSR_BUSY); | |
| return ValueWritten; | |
| } | |
| // Function to read from MII register (PHY Access) | |
| UINT32 | |
| IndirectPHYRead32 ( | |
| UINT32 Index | |
| ) | |
| { | |
| UINT32 ValueRead; | |
| UINT32 MiiAcc; | |
| // Check it is a valid index | |
| ASSERT(Index < 31); | |
| // Wait for busy bit to clear | |
| while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY); | |
| // Clear the R/W bit to indicate we are reading | |
| // Set the index of the MII register | |
| // Set the PHY Address | |
| // Set the MII busy bit to allow read | |
| MiiAcc = MII_ACC_MII_READ | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY; | |
| // Now write this config to register | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF); | |
| // Wait for busy bit to clear | |
| while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY); | |
| // Now read the value of the register | |
| ValueRead = (IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_DATA) & 0xFFFF); // only lower 16 bits are valid for any PHY register | |
| return ValueRead; | |
| } | |
| // Function to write to the MII register (PHY Access) | |
| UINT32 | |
| IndirectPHYWrite32 ( | |
| UINT32 Index, | |
| UINT32 Value | |
| ) | |
| { | |
| UINT32 MiiAcc; | |
| UINT32 ValueWritten; | |
| // Check it is a valid index | |
| ASSERT(Index < 31); | |
| // Wait for busy bit to clear | |
| while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY); | |
| // Clear the R/W bit to indicate we are reading | |
| // Set the index of the MII register | |
| // Set the PHY Address | |
| // Set the MII busy bit to allow read | |
| MiiAcc = MII_ACC_MII_WRITE | MII_ACC_MII_REG_INDEX(Index) | MII_ACC_PHY_VALUE | MII_ACC_MII_BUSY; | |
| // Write the desired value to the register first | |
| ValueWritten = IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_DATA, (Value & 0xFFFF)); | |
| // Now write the config to register | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_MII_ACC, MiiAcc & 0xFFFF); | |
| // Wait for operation to terminate | |
| while ((IndirectMACRead32 (INDIRECT_MAC_INDEX_MII_ACC) & MII_ACC_MII_BUSY) == MII_ACC_MII_BUSY); | |
| return ValueWritten; | |
| } | |
| /* ---------------- EEPROM Operations ------------------ */ | |
| // Function to read from EEPROM memory | |
| UINT32 | |
| IndirectEEPROMRead32 ( | |
| UINT32 Index | |
| ) | |
| { | |
| UINT32 EepromCmd; | |
| // Set the busy bit to ensure read will occur | |
| EepromCmd = E2P_EPC_BUSY | E2P_EPC_CMD_READ; | |
| // Set the index to access desired EEPROM memory location | |
| EepromCmd |= E2P_EPC_ADDRESS(Index); | |
| // Write to Eeprom command register | |
| MmioWrite32 (LAN9118_E2P_CMD, EepromCmd); | |
| gBS->Stall (LAN9118_STALL); | |
| // Wait until operation has completed | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| // Check that operation didn't time out | |
| if (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) { | |
| DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Read command on index %x\n",Index)); | |
| return 0; | |
| } | |
| // Wait until operation has completed | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| // Finally read the value | |
| return MmioRead32 (LAN9118_E2P_DATA); | |
| } | |
| // Function to write to EEPROM memory | |
| UINT32 | |
| IndirectEEPROMWrite32 ( | |
| UINT32 Index, | |
| UINT32 Value | |
| ) | |
| { | |
| UINT32 ValueWritten; | |
| UINT32 EepromCmd; | |
| ValueWritten = 0; | |
| // Read the EEPROM Command register | |
| EepromCmd = MmioRead32 (LAN9118_E2P_CMD); | |
| // Set the busy bit to ensure read will occur | |
| EepromCmd |= ((UINT32)1 << 31); | |
| // Set the EEPROM command to write(0b011) | |
| EepromCmd &= ~(7 << 28); // Clear the command first | |
| EepromCmd |= (3 << 28); // Write 011 | |
| // Set the index to access desired EEPROM memory location | |
| EepromCmd |= (Index & 0xF); | |
| // Write the value to the data register first | |
| ValueWritten = MmioWrite32 (LAN9118_E2P_DATA, Value); | |
| // Write to Eeprom command register | |
| MmioWrite32 (LAN9118_E2P_CMD, EepromCmd); | |
| gBS->Stall (LAN9118_STALL); | |
| // Wait until operation has completed | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| // Check that operation didn't time out | |
| if (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_TIMEOUT) { | |
| DEBUG ((EFI_D_ERROR, "EEPROM Operation Timed out: Write command at memloc 0x%x, with value 0x%x\n",Index, Value)); | |
| return 0; | |
| } | |
| // Wait until operation has completed | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| return ValueWritten; | |
| } | |
| /* ---------------- General Operations ----------------- */ | |
| VOID | |
| Lan9118SetMacAddress ( | |
| EFI_MAC_ADDRESS *Mac, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRL, | |
| (Mac->Addr[0] & 0xFF) | | |
| ((Mac->Addr[1] & 0xFF) << 8) | | |
| ((Mac->Addr[2] & 0xFF) << 16) | | |
| ((Mac->Addr[3] & 0xFF) << 24) | |
| ); | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_ADDRH, | |
| (UINT32)(Mac->Addr[4] & 0xFF) | | |
| ((Mac->Addr[5] & 0xFF) << 8) | |
| ); | |
| } | |
| VOID | |
| Lan9118ReadMacAddress ( | |
| OUT EFI_MAC_ADDRESS *MacAddress | |
| ) | |
| { | |
| UINT32 MacAddrHighValue; | |
| UINT32 MacAddrLowValue; | |
| // Read the Mac Addr high register | |
| MacAddrHighValue = (IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRH) & 0xFFFF); | |
| // Read the Mac Addr low register | |
| MacAddrLowValue = IndirectMACRead32 (INDIRECT_MAC_INDEX_ADDRL); | |
| SetMem (MacAddress, sizeof(*MacAddress), 0); | |
| MacAddress->Addr[0] = (MacAddrLowValue & 0xFF); | |
| MacAddress->Addr[1] = (MacAddrLowValue & 0xFF00) >> 8; | |
| MacAddress->Addr[2] = (MacAddrLowValue & 0xFF0000) >> 16; | |
| MacAddress->Addr[3] = (MacAddrLowValue & 0xFF000000) >> 24; | |
| MacAddress->Addr[4] = (MacAddrHighValue & 0xFF); | |
| MacAddress->Addr[5] = (MacAddrHighValue & 0xFF00) >> 8; | |
| } | |
| /* | |
| * Power up the 9118 and find its MAC address. | |
| * | |
| * This operation can be carried out when the LAN9118 is in any power state | |
| * | |
| */ | |
| EFI_STATUS | |
| Lan9118Initialize ( | |
| IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINTN Timeout; | |
| UINT64 DefaultMacAddress; | |
| // Attempt to wake-up the device if it is in a lower power state | |
| if (((MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PM_MODE_MASK) >> 12) != 0) { | |
| DEBUG ((DEBUG_NET, "Waking from reduced power state.\n")); | |
| MmioWrite32 (LAN9118_BYTE_TEST, 0xFFFFFFFF); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| // Check that device is active | |
| Timeout = 20; | |
| while ((MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_READY) == 0 && --Timeout) { | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| if (!Timeout) { | |
| return EFI_TIMEOUT; | |
| } | |
| // Check that EEPROM isn't active | |
| Timeout = 20; | |
| while ((MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY) && --Timeout){ | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| if (!Timeout) { | |
| return EFI_TIMEOUT; | |
| } | |
| // Check if a MAC address was loaded from EEPROM, and if it was, set it as the | |
| // current address. | |
| if ((MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_MAC_ADDRESS_LOADED) == 0) { | |
| DEBUG ((EFI_D_ERROR, "Warning: There was an error detecting EEPROM or loading the MAC Address.\n")); | |
| // If we had an address before (set by StationAddess), continue to use it | |
| if (CompareMem (&Snp->Mode->CurrentAddress, &mZeroMac, NET_ETHER_ADDR_LEN)) { | |
| Lan9118SetMacAddress (&Snp->Mode->CurrentAddress, Snp); | |
| } else { | |
| // If there are no cached addresses, then fall back to a default | |
| DEBUG ((EFI_D_WARN, "Warning: using driver-default MAC address\n")); | |
| DefaultMacAddress = FixedPcdGet64 (PcdLan9118DefaultMacAddress); | |
| Lan9118SetMacAddress((EFI_MAC_ADDRESS *) &DefaultMacAddress, Snp); | |
| CopyMem (&Snp->Mode->CurrentAddress, &DefaultMacAddress, NET_ETHER_ADDR_LEN); | |
| } | |
| } else { | |
| // Store the MAC address that was loaded from EEPROM | |
| Lan9118ReadMacAddress (&Snp->Mode->CurrentAddress); | |
| CopyMem (&Snp->Mode->PermanentAddress, &Snp->Mode->CurrentAddress, NET_ETHER_ADDR_LEN); | |
| } | |
| // Clear and acknowledge interrupts | |
| MmioWrite32 (LAN9118_INT_EN, 0); | |
| MmioWrite32 (LAN9118_IRQ_CFG, 0); | |
| MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); | |
| // Do self tests here? | |
| return EFI_SUCCESS; | |
| } | |
| // Perform software reset on the LAN9118 | |
| // Return 0 on success, -1 on error | |
| EFI_STATUS | |
| SoftReset ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 HwConf; | |
| UINT32 ResetTime; | |
| // Initialize variable | |
| ResetTime = 0; | |
| // Stop Rx and Tx | |
| StopTx (STOP_TX_MAC | STOP_TX_CFG | STOP_TX_CLEAR, Snp); | |
| StopRx (STOP_RX_CLEAR, Snp); // Clear receiver FIFO | |
| // Issue the reset | |
| HwConf = MmioRead32 (LAN9118_HW_CFG); | |
| HwConf |= 1; | |
| // Set the Must Be One (MBO) bit | |
| if (((HwConf & HWCFG_MBO) >> 20) == 0) { | |
| HwConf |= HWCFG_MBO; | |
| } | |
| // Check that EEPROM isn't active | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| // Write the configuration | |
| MmioWrite32 (LAN9118_HW_CFG, HwConf); | |
| gBS->Stall (LAN9118_STALL); | |
| // Wait for reset to complete | |
| while (MmioRead32 (LAN9118_HW_CFG) & HWCFG_SRST) { | |
| gBS->Stall (LAN9118_STALL); | |
| ResetTime += 1; | |
| // If time taken exceeds 100us, then there was an error condition | |
| if (ResetTime > 1000) { | |
| Snp->Mode->State = EfiSimpleNetworkStopped; | |
| return EFI_TIMEOUT; | |
| } | |
| } | |
| // Check that EEPROM isn't active | |
| while (MmioRead32 (LAN9118_E2P_CMD) & E2P_EPC_BUSY); | |
| // TODO we probably need to re-set the mac address here. | |
| // Clear and acknowledge all interrupts | |
| if (Flags & SOFT_RESET_CLEAR_INT) { | |
| MmioWrite32 (LAN9118_INT_EN, 0); | |
| MmioWrite32 (LAN9118_IRQ_CFG, 0); | |
| MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); | |
| } | |
| // Do self tests here? | |
| if (Flags & SOFT_RESET_SELF_TEST) { | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Perform PHY software reset | |
| EFI_STATUS | |
| PhySoftReset ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 PmtCtrl = 0; | |
| // PMT PHY reset takes precedence over BCR | |
| if (Flags & PHY_RESET_PMT) { | |
| PmtCtrl = MmioRead32 (LAN9118_PMT_CTRL); | |
| PmtCtrl |= MPTCTRL_PHY_RST; | |
| MmioWrite32 (LAN9118_PMT_CTRL,PmtCtrl); | |
| // Wait for completion | |
| while (MmioRead32 (LAN9118_PMT_CTRL) & MPTCTRL_PHY_RST) { | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| // PHY Basic Control Register reset | |
| } else if (Flags & PHY_RESET_BCR) { | |
| IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PHYCR_RESET); | |
| // Wait for completion | |
| while (IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) { | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| } | |
| // Clear and acknowledge all interrupts | |
| if (Flags & PHY_SOFT_RESET_CLEAR_INT) { | |
| MmioWrite32 (LAN9118_INT_EN, 0); | |
| MmioWrite32 (LAN9118_IRQ_CFG, 0); | |
| MmioWrite32 (LAN9118_INT_STS, 0xFFFFFFFF); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Configure hardware for LAN9118 | |
| EFI_STATUS | |
| ConfigureHardware ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 GpioConf; | |
| // Check if we want to use LEDs on GPIO | |
| if (Flags & HW_CONF_USE_LEDS) { | |
| GpioConf = MmioRead32 (LAN9118_GPIO_CFG); | |
| // Enable GPIO as LEDs and Config as Push-Pull driver | |
| GpioConf |= GPIO_GPIO0_PUSH_PULL | GPIO_GPIO1_PUSH_PULL | GPIO_GPIO2_PUSH_PULL | | |
| GPIO_LED1_ENABLE | GPIO_LED2_ENABLE | GPIO_LED3_ENABLE; | |
| // Write the configuration | |
| MmioWrite32 (LAN9118_GPIO_CFG, GpioConf); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Configure flow control | |
| EFI_STATUS | |
| ConfigureFlow ( | |
| UINT32 Flags, | |
| UINT32 HighTrig, | |
| UINT32 LowTrig, | |
| UINT32 BPDuration, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| // Do auto-negotiation | |
| EFI_STATUS | |
| AutoNegotiate ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 PhyControl; | |
| UINT32 PhyStatus; | |
| UINT32 Features; | |
| UINT32 TimeOut; | |
| // First check that auto-negotiation is supported | |
| PhyStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS); | |
| if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) { | |
| DEBUG ((EFI_D_ERROR, "Auto-negotiation not supported.\n")); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| // Check that link is up first | |
| if ((PhyStatus & PHYSTS_LINK_STS) == 0) { | |
| // Wait until it is up or until Time Out | |
| TimeOut = 2000; | |
| while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_LINK_STS) == 0) { | |
| gBS->Stall (LAN9118_STALL); | |
| TimeOut--; | |
| if (!TimeOut) { | |
| DEBUG ((EFI_D_ERROR, "Link timeout in auto-negotiation.\n")); | |
| return EFI_TIMEOUT; | |
| } | |
| } | |
| } | |
| // Configure features to advertise | |
| Features = IndirectPHYRead32 (PHY_INDEX_AUTO_NEG_ADVERT); | |
| if ((Flags & AUTO_NEGOTIATE_ADVERTISE_ALL) > 0) { | |
| // Link speed capabilities | |
| Features |= (PHYANA_10BASET | PHYANA_10BASETFD | PHYANA_100BASETX | PHYANA_100BASETXFD); | |
| // Pause frame capabilities | |
| Features &= ~(PHYANA_PAUSE_OP_MASK); | |
| Features |= 3 << 10; | |
| } | |
| // Write the features | |
| IndirectPHYWrite32 (PHY_INDEX_AUTO_NEG_ADVERT, Features); | |
| // Read control register | |
| PhyControl = IndirectPHYRead32 (PHY_INDEX_BASIC_CTRL); | |
| // Enable Auto-Negotiation | |
| if ((PhyControl & PHYCR_AUTO_EN) == 0) { | |
| PhyControl |= PHYCR_AUTO_EN; | |
| } | |
| // Restart auto-negotiation | |
| PhyControl |= PHYCR_RST_AUTO; | |
| // Enable collision test if required to do so | |
| if (Flags & AUTO_NEGOTIATE_COLLISION_TEST) { | |
| PhyControl |= PHYCR_COLL_TEST; | |
| } else { | |
| PhyControl &= ~ PHYCR_COLL_TEST; | |
| } | |
| // Write this configuration | |
| IndirectPHYWrite32 (PHY_INDEX_BASIC_CTRL, PhyControl); | |
| // Wait until process has completed | |
| while ((IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0); | |
| return EFI_SUCCESS; | |
| } | |
| // Check the Link Status and take appropriate action | |
| EFI_STATUS | |
| CheckLinkStatus ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| // Get the PHY Status | |
| UINT32 PhyBStatus = IndirectPHYRead32 (PHY_INDEX_BASIC_STATUS); | |
| if (PhyBStatus & PHYSTS_LINK_STS) { | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| // Stop the transmitter | |
| EFI_STATUS | |
| StopTx ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 MacCsr; | |
| UINT32 TxCfg; | |
| MacCsr = 0; | |
| TxCfg = 0; | |
| // Check if we want to clear tx | |
| if (Flags & STOP_TX_CLEAR) { | |
| TxCfg = MmioRead32 (LAN9118_TX_CFG); | |
| TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP; | |
| MmioWrite32 (LAN9118_TX_CFG, TxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| // Check if already stopped | |
| if (Flags & STOP_TX_MAC) { | |
| MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR); | |
| if (MacCsr & MACCR_TX_EN) { | |
| MacCsr &= ~MACCR_TX_EN; | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr); | |
| } | |
| } | |
| if (Flags & STOP_TX_CFG) { | |
| TxCfg = MmioRead32 (LAN9118_TX_CFG); | |
| if (TxCfg & TXCFG_TX_ON) { | |
| TxCfg |= TXCFG_STOP_TX; | |
| MmioWrite32 (LAN9118_TX_CFG, TxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| // Wait for Tx to finish transmitting | |
| while (MmioRead32 (LAN9118_TX_CFG) & TXCFG_STOP_TX); | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Stop the receiver | |
| EFI_STATUS | |
| StopRx ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 MacCsr; | |
| UINT32 RxCfg; | |
| RxCfg = 0; | |
| // Check if already stopped | |
| MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR); | |
| if (MacCsr & MACCR_RX_EN) { | |
| MacCsr &= ~ MACCR_RX_EN; | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr); | |
| } | |
| // Check if we want to clear receiver FIFOs | |
| if (Flags & STOP_RX_CLEAR) { | |
| RxCfg = MmioRead32 (LAN9118_RX_CFG); | |
| RxCfg |= RXCFG_RX_DUMP; | |
| MmioWrite32 (LAN9118_RX_CFG, RxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| while (MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Start the transmitter | |
| EFI_STATUS | |
| StartTx ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 MacCsr; | |
| UINT32 TxCfg; | |
| MacCsr = 0; | |
| TxCfg = 0; | |
| // Check if we want to clear tx | |
| if (Flags & START_TX_CLEAR) { | |
| TxCfg = MmioRead32 (LAN9118_TX_CFG); | |
| TxCfg |= TXCFG_TXS_DUMP | TXCFG_TXD_DUMP; | |
| MmioWrite32 (LAN9118_TX_CFG, TxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| // Check if tx was started from MAC and enable if not | |
| if (Flags & START_TX_MAC) { | |
| MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR); | |
| gBS->Stall (LAN9118_STALL); | |
| if ((MacCsr & MACCR_TX_EN) == 0) { | |
| MacCsr |= MACCR_TX_EN; | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| } | |
| // Check if tx was started from TX_CFG and enable if not | |
| if (Flags & START_TX_CFG) { | |
| TxCfg = MmioRead32 (LAN9118_TX_CFG); | |
| gBS->Stall (LAN9118_STALL); | |
| if ((TxCfg & TXCFG_TX_ON) == 0) { | |
| TxCfg |= TXCFG_TX_ON; | |
| MmioWrite32 (LAN9118_TX_CFG, TxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| } | |
| // Set the tx data trigger level | |
| return EFI_SUCCESS; | |
| } | |
| // Start the receiver | |
| EFI_STATUS | |
| StartRx ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 MacCsr; | |
| UINT32 RxCfg; | |
| RxCfg = 0; | |
| // Check if already started | |
| MacCsr = IndirectMACRead32 (INDIRECT_MAC_INDEX_CR); | |
| if ((MacCsr & MACCR_RX_EN) == 0) { | |
| // Check if we want to clear receiver FIFOs before starting | |
| if (Flags & START_RX_CLEAR) { | |
| RxCfg = MmioRead32 (LAN9118_RX_CFG); | |
| RxCfg |= RXCFG_RX_DUMP; | |
| MmioWrite32 (LAN9118_RX_CFG, RxCfg); | |
| gBS->Stall (LAN9118_STALL); | |
| while (MmioRead32 (LAN9118_RX_CFG) & RXCFG_RX_DUMP); | |
| } | |
| MacCsr |= MACCR_RX_EN; | |
| IndirectMACWrite32 (INDIRECT_MAC_INDEX_CR, MacCsr); | |
| gBS->Stall (LAN9118_STALL); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // Check Tx Data available space | |
| UINT32 | |
| TxDataFreeSpace ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 TxInf; | |
| UINT32 FreeSpace; | |
| // Get the amount of free space from information register | |
| TxInf = MmioRead32 (LAN9118_TX_FIFO_INF); | |
| FreeSpace = (TxInf & TXFIFOINF_TDFREE_MASK); | |
| return FreeSpace; // Value in bytes | |
| } | |
| // Check Tx Status used space | |
| UINT32 | |
| TxStatusUsedSpace ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 TxInf; | |
| UINT32 UsedSpace; | |
| // Get the amount of used space from information register | |
| TxInf = MmioRead32 (LAN9118_TX_FIFO_INF); | |
| UsedSpace = (TxInf & TXFIFOINF_TXSUSED_MASK) >> 16; | |
| return UsedSpace << 2; // Value in bytes | |
| } | |
| // Check Rx Data used space | |
| UINT32 | |
| RxDataUsedSpace ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 RxInf; | |
| UINT32 UsedSpace; | |
| // Get the amount of used space from information register | |
| RxInf = MmioRead32 (LAN9118_RX_FIFO_INF); | |
| UsedSpace = (RxInf & RXFIFOINF_RXDUSED_MASK); | |
| return UsedSpace; // Value in bytes (rounded up to nearest DWORD) | |
| } | |
| // Check Rx Status used space | |
| UINT32 | |
| RxStatusUsedSpace ( | |
| UINT32 Flags, | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 RxInf; | |
| UINT32 UsedSpace; | |
| // Get the amount of used space from information register | |
| RxInf = MmioRead32 (LAN9118_RX_FIFO_INF); | |
| UsedSpace = (RxInf & RXFIFOINF_RXSUSED_MASK) >> 16; | |
| return UsedSpace << 2; // Value in bytes | |
| } | |
| // Change the allocation of FIFOs | |
| EFI_STATUS | |
| ChangeFifoAllocation ( | |
| IN UINT32 Flags, | |
| IN OUT UINTN *TxDataSize OPTIONAL, | |
| IN OUT UINTN *RxDataSize OPTIONAL, | |
| IN OUT UINT32 *TxStatusSize OPTIONAL, | |
| IN OUT UINT32 *RxStatusSize OPTIONAL, | |
| IN OUT EFI_SIMPLE_NETWORK_PROTOCOL *Snp | |
| ) | |
| { | |
| UINT32 HwConf; | |
| UINT32 TxFifoOption; | |
| // Check that desired sizes don't exceed limits | |
| if (*TxDataSize > TX_FIFO_MAX_SIZE) | |
| return EFI_INVALID_PARAMETER; | |
| #if defined(RX_FIFO_MIN_SIZE) && defined(RX_FIFO_MAX_SIZE) | |
| if (*RxDataSize > RX_FIFO_MAX_SIZE) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| #endif | |
| if (Flags & ALLOC_USE_DEFAULT) { | |
| return EFI_SUCCESS; | |
| } | |
| // If we use the FIFOs (always use this first) | |
| if (Flags & ALLOC_USE_FIFOS) { | |
| // Read the current value of allocation | |
| HwConf = MmioRead32 (LAN9118_HW_CFG); | |
| TxFifoOption = (HwConf >> 16) & 0xF; | |
| // Choose the correct size (always use larger than requested if possible) | |
| if (*TxDataSize < TX_FIFO_MIN_SIZE) { | |
| *TxDataSize = TX_FIFO_MIN_SIZE; | |
| *RxDataSize = 13440; | |
| *RxStatusSize = 896; | |
| TxFifoOption = 2; | |
| } else if ((*TxDataSize > TX_FIFO_MIN_SIZE) && (*TxDataSize <= 2560)) { | |
| *TxDataSize = 2560; | |
| *RxDataSize = 12480; | |
| *RxStatusSize = 832; | |
| TxFifoOption = 3; | |
| } else if ((*TxDataSize > 2560) && (*TxDataSize <= 3584)) { | |
| *TxDataSize = 3584; | |
| *RxDataSize = 11520; | |
| *RxStatusSize = 768; | |
| TxFifoOption = 4; | |
| } else if ((*TxDataSize > 3584) && (*TxDataSize <= 4608)) { // default option | |
| *TxDataSize = 4608; | |
| *RxDataSize = 10560; | |
| *RxStatusSize = 704; | |
| TxFifoOption = 5; | |
| } else if ((*TxDataSize > 4608) && (*TxDataSize <= 5632)) { | |
| *TxDataSize = 5632; | |
| *RxDataSize = 9600; | |
| *RxStatusSize = 640; | |
| TxFifoOption = 6; | |
| } else if ((*TxDataSize > 5632) && (*TxDataSize <= 6656)) { | |
| *TxDataSize = 6656; | |
| *RxDataSize = 8640; | |
| *RxStatusSize = 576; | |
| TxFifoOption = 7; | |
| } else if ((*TxDataSize > 6656) && (*TxDataSize <= 7680)) { | |
| *TxDataSize = 7680; | |
| *RxDataSize = 7680; | |
| *RxStatusSize = 512; | |
| TxFifoOption = 8; | |
| } else if ((*TxDataSize > 7680) && (*TxDataSize <= 8704)) { | |
| *TxDataSize = 8704; | |
| *RxDataSize = 6720; | |
| *RxStatusSize = 448; | |
| TxFifoOption = 9; | |
| } else if ((*TxDataSize > 8704) && (*TxDataSize <= 9728)) { | |
| *TxDataSize = 9728; | |
| *RxDataSize = 5760; | |
| *RxStatusSize = 384; | |
| TxFifoOption = 10; | |
| } else if ((*TxDataSize > 9728) && (*TxDataSize <= 10752)) { | |
| *TxDataSize = 10752; | |
| *RxDataSize = 4800; | |
| *RxStatusSize = 320; | |
| TxFifoOption = 11; | |
| } else if ((*TxDataSize > 10752) && (*TxDataSize <= 11776)) { | |
| *TxDataSize = 11776; | |
| *RxDataSize = 3840; | |
| *RxStatusSize = 256; | |
| TxFifoOption = 12; | |
| } else if ((*TxDataSize > 11776) && (*TxDataSize <= 12800)) { | |
| *TxDataSize = 12800; | |
| *RxDataSize = 2880; | |
| *RxStatusSize = 192; | |
| TxFifoOption = 13; | |
| } else if ((*TxDataSize > 12800) && (*TxDataSize <= 13824)) { | |
| *TxDataSize = 13824; | |
| *RxDataSize = 1920; | |
| *RxStatusSize = 128; | |
| TxFifoOption = 14; | |
| } | |
| } else { | |
| ASSERT(0); // Untested code path | |
| HwConf = 0; | |
| TxFifoOption = 0; | |
| } | |
| // Do we need DMA? | |
| if (Flags & ALLOC_USE_DMA) { | |
| return EFI_UNSUPPORTED; // Unsupported as of now | |
| } | |
| // Clear and assign the new size option | |
| HwConf &= ~(0xF0000); | |
| HwConf |= ((TxFifoOption & 0xF) << 16); | |
| MmioWrite32 (LAN9118_HW_CFG, HwConf); | |
| gBS->Stall (LAN9118_STALL); | |
| return EFI_SUCCESS; | |
| } |