/** @file

  Copyright (c) 2011 - 2019, Intel Corporaton. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

  The original software modules are licensed as follows:

  Copyright (c) 2012 - 2014, ARM Limited. All rights reserved.
  Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "EmacDxeUtil.h"
#include "PhyDxeUtil.h"

#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>

VOID
EFIAPI
EmacSetMacAddress (
  IN  EFI_MAC_ADDRESS   *MacAddress,
  IN  UINTN             MacBaseAddress
  )
{
  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // Note: This MAC_ADDR0 registers programming sequence cannot be swap:
  // Must program HIGH Offset first before LOW Offset
  // because synchronization is triggered when MAC Address0 Low Register are written.
  MmioWrite32 (MacBaseAddress + DW_EMAC_GMACGRP_MAC_ADDRESS0_HIGH_OFST,
               (UINT32)(MacAddress->Addr[4] & 0xFF) |
                      ((MacAddress->Addr[5] & 0xFF) << 8)
                    );
  // MacAddress->Addr[0,1,2] is the 3 bytes OUI
  MmioWrite32 (MacBaseAddress + DW_EMAC_GMACGRP_MAC_ADDRESS0_LOW_OFST,
                       (MacAddress->Addr[0] & 0xFF) |
                      ((MacAddress->Addr[1] & 0xFF) << 8) |
                      ((MacAddress->Addr[2] & 0xFF) << 16) |
                      ((MacAddress->Addr[3] & 0xFF) << 24)
                    );

  DEBUG ((DEBUG_INFO, "SNP:MAC: gmacgrp_mac_address0_low  = 0x%08X \r\n",
          MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_MAC_ADDRESS0_LOW_OFST)));
  DEBUG ((DEBUG_INFO, "SNP:MAC: gmacgrp_mac_address0_high = 0x%08X \r\n",
          MmioRead32 (MacBaseAddress +DW_EMAC_GMACGRP_MAC_ADDRESS0_HIGH_OFST)));
}


VOID
EFIAPI
EmacReadMacAddress (
  OUT  EFI_MAC_ADDRESS   *MacAddress,
  IN   UINTN             MacBaseAddress
  )
{
  UINT32          MacAddrHighValue;
  UINT32          MacAddrLowValue;

  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // Read the Mac Addr high register
  MacAddrHighValue = (MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_MAC_ADDRESS0_HIGH_OFST) & 0xFFFF);
  // Read the Mac Addr low register
  MacAddrLowValue = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_MAC_ADDRESS0_LOW_OFST);

  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;

  DEBUG ((DEBUG_INFO, "SNP:MAC: MAC Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n",
    MacAddress->Addr[0], MacAddress->Addr[1], MacAddress->Addr[2],
    MacAddress->Addr[3], MacAddress->Addr[4], MacAddress->Addr[5]
  ));
}

EFI_STATUS
EFIAPI
EmacDxeInitialization (
  IN  EMAC_DRIVER   *EmacDriver,
  IN  UINTN         MacBaseAddress
  )
{
  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // Init EMAC DMA
  EmacDmaInit (EmacDriver, MacBaseAddress);

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
EmacDmaInit (
  IN  EMAC_DRIVER   *EmacDriver,
  IN  UINTN         MacBaseAddress
  )
{
  UINT32 DmaConf;
  UINT32 DmaOpmode;
  UINT32 InterruptEnable;

  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // This section provides the instructions for initializing the DMA registers in the proper sequence. This
  // initialization sequence can be done after the EMAC interface initialization has been completed. Perform
  // the following steps to initialize the DMA:
  // 1. Provide a software reset to reset all of the EMAC internal registers and logic. (DMA Register 0 (Bus
  // Mode Register) � bit 0).

  MmioOr32 (MacBaseAddress +
             DW_EMAC_DMAGRP_BUS_MODE_OFST,
             DW_EMAC_DMAGRP_BUS_MODE_SWR_SET_MSK);

  // 2. Wait for the completion of the reset process (poll bit 0 of the DMA Register 0 (Bus Mode Register),
  // which is only cleared after the reset operation is completed).
  while (DW_EMAC_DMAGRP_BUS_MODE_SWR_GET (MmioRead32 (MacBaseAddress + DW_EMAC_DMAGRP_BUS_MODE_OFST)));

  // 3. Poll the bits of Register 11 (AHB or AXI Status) to confirm that all previously initiated (before
  // software reset) or ongoing transactions are complete.
  // Note: If the application cannot poll the register after soft reset (because of performance reasons), then
  // it is recommended that you continue with the next steps and check this register again (as
  // mentioned in 12 on page 1-72) before triggering the DMA operations.�

  // 4. Program the following fields to initialize the Bus Mode Register by setting values in DMA Register 0
  // (Bus Mode Register):
  // � Mixed Burst and AAL
  // � Fixed burst or undefined burst
  // � Burst length values and burst mode values
  // � Descriptor Length (only valid if Ring Mode is used)
  // � TX and RX DMA Arbitration scheme
  // 5. Program the interface options in Register 10 (AXI Bus Mode Register). If fixed burst-length is enabled,
  // then select the maximum burst-length possible on the bus (bits[7:1]).�

  DmaConf = DW_EMAC_DMAGRP_BUS_MODE_FB_SET_MSK | DW_EMAC_DMAGRP_BUS_MODE_PBL_SET_MSK | DW_EMAC_DMAGRP_BUS_MODE_PR_SET_MSK;
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_BUS_MODE_OFST,
            DmaConf);

  // 6. Create a proper descriptor chain for transmit and receive. In addition, ensure that the receive descriptors
  // are owned by DMA (bit 31 of descriptor should be set). When OSF mode is used, at least two
  // descriptors are required.
  // 7. Make sure that your software creates three or more different transmit or receive descriptors in the
  // chain before reusing any of the descriptors.
  // 8. Initialize receive and transmit descriptor list address with the base address of the transmit and receive
  // descriptor (Register 3 (Receive Descriptor List Address Register) and Register 4 (Transmit Descriptor
  // List Address Register) respectively).

  EmacSetupTxdesc (EmacDriver, MacBaseAddress);
  EmacSetupRxdesc (EmacDriver, MacBaseAddress);

  // 9. Program the following fields to initialize the mode of operation in Register 6 (Operation Mode
  // Register):
  // � Receive and Transmit Store And Forward�
  // � Receive and Transmit Threshold Control (RTC and TTC)�
  // � Hardware Flow Control enable�
  // � Flow Control Activation and De-activation thresholds for MTL Receive and Transmit FIFO buffers
  // (RFA and RFD)�
  // � Error frame and undersized good frame forwarding enable�
  // � OSF Mode�

  DmaOpmode = DW_EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK | DW_EMAC_DMAGRP_OPERATION_MODE_TSF_SET_MSK;
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_OPERATION_MODE_OFST,
            DmaOpmode);
  //while (DW_EMAC_DMAGRP_OPERATION_MODE_FTF_GET (MmioRead32 (MacBaseAddress + DW_EMAC_DMAGRP_OPERATION_MODE_OFST)));

  // 10.Clear the interrupt requests, by writing to those bits of the status register (interrupt bits only) that are
  // set. For example, by writing 1 into bit 16, the normal interrupt summary clears this bit (DMA Register 5 (Status Register)).
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_STATUS_OFST,
            0x1FFFF);

  // 11.Enable the interrupts by programming Register 7 (Interrupt Enable Register).
  InterruptEnable = DW_EMAC_DMAGRP_INTERRUPT_ENABLE_TIE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_RIE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_NIE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_AIE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_FBE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_UNE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_TSE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_TUE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_TJE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_OVE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_RUE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_RSE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_RWE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_ETE_SET_MSK |
                    DW_EMAC_DMAGRP_INTERRUPT_ENABLE_ERE_SET_MSK;
   MmioWrite32 (MacBaseAddress +
                DW_EMAC_DMAGRP_INTERRUPT_ENABLE_OFST,
                InterruptEnable);

  // 12.Read Register 11 (AHB or AXI Status) to confirm that all previous transactions are complete.�
  // Note: If any previous transaction is still in progress when you read the Register 11 (AHB or AXI
  // Status), then it is strongly recommended to check the slave components addressed by the
  // master interface.
  if (MmioRead32 (MacBaseAddress + DW_EMAC_DMAGRP_AHB_OR_AXI_STATUS_OFST) != 0) {
    DEBUG ((DEBUG_INFO, "SNP:MAC: Error! Previous AXI transaction is still in progress\r\n"));
    //check the slave components addressed by the master interface
    return EFI_DEVICE_ERROR;
  }

  DmaOpmode = DW_EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK | DW_EMAC_DMAGRP_OPERATION_MODE_SR_SET_MSK;
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_OPERATION_MODE_OFST,
            DmaOpmode);

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
EmacSetupTxdesc (
  IN  EMAC_DRIVER   *EmacDriver,
  IN  UINTN         MacBaseAddress
 )
{
  INTN                       Index;
  DESIGNWARE_HW_DESCRIPTOR   *TxDescriptor;

  for (Index = 0; Index < CONFIG_TX_DESCR_NUM; Index++) {
    TxDescriptor = (VOID *)(UINTN)EmacDriver->TxdescRingMap[Index].AddrMap;
    TxDescriptor->Addr = (UINT32)(UINTN)&EmacDriver->TxBuffer[Index * CONFIG_ETH_BUFSIZE];
    if (Index < 9) {
      TxDescriptor->AddrNext = (UINT32)(UINTN)EmacDriver->TxdescRingMap[Index + 1].AddrMap;
    }
    TxDescriptor->Tdes0 = TDES0_TXCHAIN;
    TxDescriptor->Tdes1 = 0;
  }

  // Correcting the last pointer of the chain
  TxDescriptor->AddrNext = (UINT32)(UINTN)EmacDriver->TxdescRingMap[0].AddrMap;

  // Write the address of tx descriptor list
  MmioWrite32 (MacBaseAddress +
              DW_EMAC_DMAGRP_TRANSMIT_DESCRIPTOR_LIST_ADDRESS_OFST,
              (UINT32)(UINTN)EmacDriver->TxdescRingMap[0].AddrMap);

  // Initialize the descriptor number
  EmacDriver->TxCurrentDescriptorNum = 0;
  EmacDriver->TxNextDescriptorNum = 0;

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
EmacSetupRxdesc (
  IN  EMAC_DRIVER   *EmacDriver,
  IN  UINTN         MacBaseAddress
  )
{
  INTN                        Index;
  DESIGNWARE_HW_DESCRIPTOR    *RxDescriptor;

  for (Index = 0; Index < CONFIG_RX_DESCR_NUM; Index++) {
    RxDescriptor = (VOID *)(UINTN)EmacDriver->RxdescRingMap[Index].AddrMap;
    RxDescriptor->Addr = EmacDriver->RxBufNum[Index].AddrMap;
    if (Index < 9) {
      RxDescriptor->AddrNext = (UINT32)(UINTN)EmacDriver->RxdescRingMap[Index + 1].AddrMap;
    }
    RxDescriptor->Tdes0 = RDES0_OWN;
    RxDescriptor->Tdes1 = RDES1_CHAINED | RX_MAX_PACKET;
  }

  // Correcting the last pointer of the chain
  RxDescriptor->AddrNext = (UINT32)(UINTN)EmacDriver->RxdescRingMap[0].AddrMap;

  // Write the address of tx descriptor list
  MmioWrite32(MacBaseAddress +
              DW_EMAC_DMAGRP_RECEIVE_DESCRIPTOR_LIST_ADDRESS_OFST,
              (UINT32)(UINTN)EmacDriver->RxdescRingMap[0].AddrMap);

  // Initialize the descriptor number
  EmacDriver->RxCurrentDescriptorNum = 0;
  EmacDriver->RxNextDescriptorNum = 0;

  return EFI_SUCCESS;
}


VOID
EFIAPI
EmacStartTransmission (
  IN  UINTN   MacBaseAddress
  )
{
  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));
  MmioOr32 (MacBaseAddress +
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_OFST,
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_RE_SET_MSK |
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_TE_SET_MSK
            );
}


EFI_STATUS
EFIAPI
EmacRxFilters (
  IN  UINT32            ReceiveFilterSetting,
  IN  BOOLEAN           Reset,
  IN  UINTN             NumMfilter          OPTIONAL,
  IN  EFI_MAC_ADDRESS   *Mfilter            OPTIONAL,
  IN  UINTN             MacBaseAddress
  )
{
  UINT32  MacFilter;
  UINT32  Crc;
  UINT32  Count;
  UINT32  HashReg;
  UINT32  HashBit;
  UINT32  Reg;
  UINT32  Val;

  // If reset then clear the filter registers
  if (Reset) {
    for (Count = 0; Count < NumMfilter; Count++)
    {
      MmioWrite32 (MacBaseAddress + HASH_TABLE_REG(Count), 0x00000000);
    }
  }

  // Set MacFilter to the reset value of the  DW_EMAC_GMACGRP_MAC_FRAME_FILTER register.
  MacFilter =  DW_EMAC_GMACGRP_MAC_FRAME_FILTER_RESET;

  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) {
    MacFilter |=  DW_EMAC_GMACGRP_MAC_FRAME_FILTER_HMC_SET_MSK;

    // Set the hash tables
    if ((NumMfilter > 0) && (!Reset)) {
      // Go through each filter address and set appropriate bits on hash table
      for (Count = 0; Count < NumMfilter; Count++) {
        // Generate a 32-bit CRC
        Crc = GenEtherCrc32 (&Mfilter[Count], 6);
        // reserve CRC + take upper 8 bit = take lower 8 bit and reverse it
        Val = BitReverse(Crc & 0xff);
        // The most significant bits determines the register to be used (Hash Table Register X),
        // and the least significant five bits determine the bit within the register.
        // For example, a hash value of 8b'10111111 selects Bit 31 of the Hash Table Register 5.
        HashReg = (Val >> 5);
        HashBit = (Val & 0x1f);

        Reg = MmioRead32 (MacBaseAddress + HASH_TABLE_REG(HashReg));
        // set 1 to HashBit of HashReg
        // for example, set 1 to bit 31 to Reg 5 as in above example
        Reg |= (1 << HashBit);
        MmioWrite32(MacBaseAddress + HASH_TABLE_REG(HashReg), Reg);
      }
    }
  }

  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) == 0) {
    MacFilter |=  DW_EMAC_GMACGRP_MAC_FRAME_FILTER_DBF_SET_MSK;
  }

  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) {
    MacFilter |=  DW_EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK;
  }

  if (ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) {
    MacFilter |= ( DW_EMAC_GMACGRP_MAC_FRAME_FILTER_PM_SET_MSK);
  }

  // Set MacFilter to EMAC register
  MmioWrite32 (MacBaseAddress +  DW_EMAC_GMACGRP_MAC_FRAME_FILTER_OFST, MacFilter);
  return EFI_SUCCESS;
}


UINT32
EFIAPI
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;
      }
    }
  }

  return (~Remainder);
}


STATIC CONST UINT8 NibbleTab[] = {
    /* 0x0 0000 -> 0000 */  0x0,
    /* 0x1 0001 -> 1000 */  0x8,
    /* 0x2 0010 -> 0100 */  0x4,
    /* 0x3 0011 -> 1100 */  0xc,
    /* 0x4 0100 -> 0010 */  0x2,
    /* 0x5 0101 -> 1010 */  0xa,
    /* 0x6 0110 -> 0110 */  0x6,
    /* 0x7 0111 -> 1110 */  0xe,
    /* 0x8 1000 -> 0001 */  0x1,
    /* 0x9 1001 -> 1001 */  0x9,
    /* 0xa 1010 -> 0101 */  0x5,
    /* 0xb 1011 -> 1101 */  0xd,
    /* 0xc 1100 -> 0011 */  0x3,
    /* 0xd 1101 -> 1011 */  0xb,
    /* 0xe 1110 -> 0111 */  0x7,
    /* 0xf 1111 -> 1111 */  0xf
};

UINT8
EFIAPI
BitReverse (
  UINT8   Value
  )
{
  return (NibbleTab[Value & 0xf] << 4) | NibbleTab[Value >> 4];
}


VOID
EFIAPI
EmacStopTxRx (
   IN  UINTN   MacBaseAddress
  )
{
  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // Stop DMA TX
  MmioAnd32 (MacBaseAddress +
             DW_EMAC_DMAGRP_OPERATION_MODE_OFST,
             DW_EMAC_DMAGRP_OPERATION_MODE_ST_CLR_MSK);

  // Flush TX
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_OPERATION_MODE_OFST,
            DW_EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK);

  // Stop transmitters
  MmioAnd32 (MacBaseAddress +
             DW_EMAC_GMACGRP_MAC_CONFIGURATION_OFST,
             DW_EMAC_GMACGRP_MAC_CONFIGURATION_RE_CLR_MSK &
             DW_EMAC_GMACGRP_MAC_CONFIGURATION_TE_CLR_MSK);

  // Stop DMA RX
  MmioAnd32 (MacBaseAddress +
             DW_EMAC_DMAGRP_OPERATION_MODE_OFST,
             DW_EMAC_DMAGRP_OPERATION_MODE_SR_CLR_MSK);

}


EFI_STATUS
EFIAPI
EmacDmaStart (
  IN  UINTN   MacBaseAddress
  )
{
  // Start the transmission
  MmioWrite32(MacBaseAddress +
              DW_EMAC_DMAGRP_TRANSMIT_POLL_DEMAND_OFST,
              0x1);
  return EFI_SUCCESS;
}


VOID
EFIAPI
EmacGetDmaStatus (
  OUT  UINT32   *IrqStat  OPTIONAL,
  IN   UINTN    MacBaseAddress
  )
{
  UINT32  DmaStatus;
  UINT32  ErrorBit;
  UINT32  Mask = 0;

  DmaStatus = MmioRead32 (MacBaseAddress +
                           DW_EMAC_DMAGRP_STATUS_OFST);
  if (DmaStatus & DW_EMAC_DMAGRP_STATUS_NIS_SET_MSK) {
    Mask |= DW_EMAC_DMAGRP_STATUS_NIS_SET_MSK;
    // Rx interrupt
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_RI_SET_MSK) {
      *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
      Mask |= DW_EMAC_DMAGRP_STATUS_RI_SET_MSK;
    } else {
      *IrqStat &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
    }
    // Tx interrupt
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_TI_SET_MSK) {
      *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
      Mask |= DW_EMAC_DMAGRP_STATUS_TI_SET_MSK;
    } else {
      *IrqStat &= ~EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
    }
    // Tx Buffer
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_TU_SET_MSK){
      Mask |= DW_EMAC_DMAGRP_STATUS_TU_SET_MSK;
      }
    // Early receive interrupt
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_ERI_SET_MSK) {
      Mask |= DW_EMAC_DMAGRP_STATUS_ERI_SET_MSK;
    }
  }
  if (DmaStatus & DW_EMAC_DMAGRP_STATUS_AIS_SET_MSK) {
    Mask |= DW_EMAC_DMAGRP_STATUS_AIS_SET_MSK;
    // Transmit process stop
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_TPS_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Transmit process stop\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_TPS_SET_MSK;
    }
    // Transmit jabber timeout
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_TJT_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Transmit jabber timeout\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_TJT_SET_MSK;
    }
    // Receive FIFO overflow
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_OVF_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Receive FIFO overflow\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_OVF_SET_MSK;
    }
    // Transmit FIFO underflow
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_UNF_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Receive FIFO underflow\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_UNF_SET_MSK;
    }
    // Receive buffer unavailable
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_RU_SET_MSK) {
      Mask |= DW_EMAC_DMAGRP_STATUS_RU_SET_MSK;
    }

    // Receive process stop
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_RPS_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Receive process stop\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_RPS_SET_MSK;
    }
    // Receive watchdog timeout
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_RWT_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Receive watchdog timeout\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_RWT_SET_MSK;
    }
    // Early transmit interrupt
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_ETI_SET_MSK) {
      Mask |= DW_EMAC_DMAGRP_STATUS_ETI_SET_MSK;
    }
    // Fatal bus error
    if (DmaStatus & DW_EMAC_DMAGRP_STATUS_FBI_SET_MSK) {
      DEBUG ((DEBUG_INFO, "SNP:MAC: Fatal bus error:\n"));
      Mask |= DW_EMAC_DMAGRP_STATUS_FBI_SET_MSK;

      ErrorBit = DW_EMAC_DMAGRP_STATUS_EB_GET (DmaStatus);
      switch (ErrorBit) {
        case RX_DMA_WRITE_DATA_TRANSFER_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Rx Dma write data transfer error\n"));
          break;
        case TX_DMA_READ_DATA_TRANSFER_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Tx Dma read data transfer error\n"));
          break;
        case RX_DMA_DESCRIPTOR_WRITE_ACCESS_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Rx Dma descriptor write access error\n"));
          break;
        case RX_DMA_DESCRIPTOR_READ_ACCESS_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Rx Dma descriptor read access error\n"));
          break;
        case TX_DMA_DESCRIPTOR_WRITE_ACCESS_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Tx Dma descriptor write access error\n"));
          break;
        case TX_DMA_DESCRIPTOR_READ_ACCESS_ERROR:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Tx Dma descriptor read access error\n"));
          break;
        default:
          DEBUG ((DEBUG_INFO, "SNP:MAC: Undefined error\n"));
          break;
      }
    }
  }
  MmioOr32 (MacBaseAddress +
            DW_EMAC_DMAGRP_STATUS_OFST,
            Mask);
}


VOID
EFIAPI
EmacGetStatistic (
  OUT  EFI_NETWORK_STATISTICS   *Statistic,
  IN   UINTN                     MacBaseAddress
  )
{
  EFI_NETWORK_STATISTICS   *Stats;

  DEBUG ((DEBUG_INFO, "SNP:MAC: %a ()\r\n", __FUNCTION__));

  // Allocate Resources
  Stats = AllocateZeroPool (sizeof (EFI_NETWORK_STATISTICS));
  if (Stats == NULL) {
    return;
  }

  Stats->RxTotalFrames     = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXFRAMECOUNT_GB_OFST);
  Stats->RxUndersizeFrames = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXUNDERSIZE_G_OFST);
  Stats->RxOversizeFrames  = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXOVERSIZE_G_OFST);
  Stats->RxUnicastFrames   = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXUNICASTFRAMES_G_OFST);
  Stats->RxBroadcastFrames = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXBROADCASTFRAMES_G_OFST);
  Stats->RxMulticastFrames = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXMULTICASTFRAMES_G_OFST);
  Stats->RxCrcErrorFrames  = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXCRCERROR_OFST);
  Stats->RxTotalBytes      = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_RXOCTETCOUNT_GB_OFST);
  Stats->RxGoodFrames      = Stats->RxUnicastFrames + Stats->RxBroadcastFrames + Stats->RxMulticastFrames;

  Stats->TxTotalFrames     = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXFRAMECOUNT_GB_OFST);
  Stats->TxGoodFrames      = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXFRAMECOUNT_G_OFST);
  Stats->TxOversizeFrames  = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXOVERSIZE_G_OFST);
  Stats->TxUnicastFrames   = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXUNICASTFRAMES_GB_OFST);
  Stats->TxBroadcastFrames = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXBROADCASTFRAMES_G_OFST);
  Stats->TxMulticastFrames = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXMULTICASTFRAMES_G_OFST);
  Stats->TxTotalBytes      = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXOCTETCOUNT_GB_OFST);
  Stats->Collisions        = MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXLATECOL_OFST) +
                             MmioRead32 (MacBaseAddress + DW_EMAC_GMACGRP_TXEXESSCOL_OFST);

  // Fill in the statistics
  CopyMem (Statistic, Stats, sizeof (EFI_NETWORK_STATISTICS));
}


VOID
EFIAPI
EmacConfigAdjust (
  IN  UINT32   Speed,
  IN  UINT32   Duplex,
  IN  UINTN    MacBaseAddress
  )
{
  UINT32   Config;

  Config = 0;
  if (Speed != SPEED_1000) {
   Config |= DW_EMAC_GMACGRP_MAC_CONFIGURATION_PS_SET_MSK;
  }
  if (Speed == SPEED_100) {
    Config |= DW_EMAC_GMACGRP_MAC_CONFIGURATION_FES_SET_MSK;
  }
  if (Duplex == DUPLEX_FULL) {
    Config |= DW_EMAC_GMACGRP_MAC_CONFIGURATION_DM_SET_MSK;
  }
  MmioOr32 (MacBaseAddress +
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_OFST,
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_BE_SET_MSK |
            DW_EMAC_GMACGRP_MAC_CONFIGURATION_DO_SET_MSK |
            Config);

}
