/*************************************************************************
 *
 *    Used with ICCARM and AARM.
 *
 *    (c) Copyright IAR Systems 2006
 *
 *    File name   : LPC23xx_enet.c
 *    Description : MAC/DMA Controller with DMA (ENET) driver
 *
 *    History :
 *    1. Date        : December 14, 2006
 *       Author      : Stanimir Bonev
 *       Description : Create
 *
 *    $Revision: 1.1.2.3 $
 **************************************************************************/

#define __no_init                           /* Dummy for __no_init          */

#include "LPC23xx.h"
#include "types.h"
#include "LPC23xx_enet.h"

//#pragma segment="EMAC_DMA_RAM"

//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init EnetDmaRxDesc_t EnetDmaRx[ENET_DMA_DESC_NUMB]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=8
__no_init EnetDmaRxStatus_t EnetDmaRxSta[ENET_DMA_DESC_NUMB]__attribute__ ((section (".ethram"))) __attribute__((aligned (8)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init Int8U RxBuff0[EMAC_MAX_PACKET_SIZE]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init Int8U RxBuff1[EMAC_MAX_PACKET_SIZE]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));


//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init EnetDmaTxDesc_t EnetDmaTx[ENET_DMA_DESC_NUMB]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init EnetDmaTxStatus_t EnetDmaTxSta[ENET_DMA_DESC_NUMB]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init Int8U TxBuff0[EMAC_MAX_PACKET_SIZE]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));
//#pragma location="EMAC_DMA_RAM"
//#pragma data_alignment=4
__no_init Int8U TxBuff1[EMAC_MAX_PACKET_SIZE]__attribute__ ((section (".ethram"))) __attribute__((aligned (4)));

static Int8U PhyAddr;

/*************************************************************************
 * Function Name:
 * Parameters: None
 *
 * Return: Boolean
 *
 * Description: Init  MAC/DMA Controller
 *
 *************************************************************************/
Boolean tapdev_init(void)
{
Int32U Reg,to,i;

  // Pins assignment
  PINMODE2 = 0xA02A220A;  // P1[0,1,4,6,8,9,10,14,15] disable pu/pd
  PINMODE3 = 0x0000000A;  // P1[17:16] disable pu/pd
  PINSEL2  = 0x50151105;  // selects P1[0,1,4,6,8,9,10,14,15]
  PINSEL3  = 0x00000005;  // selects P1[17:16]
  // clk enable
//  PCONP_bit.PCENET = 1;
  PCONP |= (1 << 30);                       /* Set Bit 30 (eth) in PCONP    */
//  POWERDOWN_bit.POWERDOWN = 0;
  MAC_POWERDOWN &=~(1 << 31);               /* Clear Bit 31(powerdown)      */
  // Reset entire MAC
  MAC_MAC1 = 0x0000CF00;                    /* Reset entire MAC             */
  MAC_COMMAND = 0x0038;                     /* Reset all control registers  */
  MAC_MAC1 = 0;                             /* Clear entire MAC             */
//  COMMAND_bit.RMII = 1;
  MAC_COMMAND |= (1 << 9);                  /* Set Bit 9 (RMII) in CMD      */
  MAC_SUPP = 0;                             /* Clear PHY support register   */
  MAC_TEST = 0;                             /* Clear test register          */

  // write the station address registers
  MAC_SA0 = (UIP_ETHADDR1<<8) | UIP_ETHADDR0;
  MAC_SA1 = (UIP_ETHADDR3<<8) | UIP_ETHADDR2;
  MAC_SA2 = (UIP_ETHADDR5<<8) | UIP_ETHADDR4;

  MAC_MAXF = 0x600;                         /* Set maximum Frame            */
  MAC_MCFG = 0x8018;                        /* clk/20                       */
  MAC_MCMD = 0;                             /* Clear MCommand register      */

  // MIIM init
//  MCFG_bit.RSTMIIMGMT = 0;
  MAC_MCFG &=~(1 << 15);                    /* Clear RSTMIIMGMT Bit         */

  // descriptors init
//  MAC1_bit.RSTMCSTX = 1;
  MAC_MAC1 |= (1 << 9);                     /* Set Bit 9 (RSTMCSTX) in MAC1 */
//  COMMAND_bit.TXENABLE = 0;
  MAC_COMMAND &=~(1 << 1);                  /* Clear Bit 1 (TXENABLE)       */
//  MAC1_bit.RSTTX = 1;
  MAC_MAC1 |= (1 << 8);                     /* Set Bit 8 (RSTTX) in MAC1    */
//  COMMAND_bit.TXRESET = 1;
  MAC_COMMAND |= (1 << 4);                  /* Set Bit 4 (TXRESET) in MCMD  */
//  MAC1_bit.RSTMCSTX = 0;
  MAC_MAC1 &=~(1 << 9);                     /* Clear Bit 9 (RSTMCSTX)       */
//  MAC1_bit.RSTTX = 0;
  MAC_MAC1 &=~(1 << 8);                     /* Clear Bit 8 (RSTTX) in MAC1  */

//  MAC1_bit.RE = 0;
  MAC_MAC1 &=~(1 << 0);                     /* Clear Bit 0 (RE) in MAC1     */
//  MAC1_bit.RSTMCSRX = 1;
  MAC_MAC1 |= (1 << 11);                    /* Set Bit 11 (RSTMCSRX) in MAC1*/
//  COMMAND_bit.RXENABLE = 0;
  MAC_COMMAND &=~(1 << 0);                  /* Clear Bit 0 (RXENABLE)       */
//  MAC1_bit.RSTRX = 1;
  MAC_MAC1 |= (1 << 10);                    /* Set Bit 10 (RSTRX) in MAC1   */
//  COMMAND_bit.RXRESET = 1;
  MAC_COMMAND |= (1 << 5);                  /* Set Bit 5 (RXRESET) in MCMD  */
//  MAC1_bit.RSTMCSRX = 0;
  MAC_MAC1 &=~(1 << 11);                    /* Clear Bit 11 (RSTMCSRX)      */
//  MAC1_bit.RSTRX = 0;
  MAC_MAC1 &=~(1 << 10);                    /* Clear Bit 10 (RSTRX)         */

  MAC_RXDESCRIPTORNUM = ENET_DMA_DESC_NUMB-1;
  MAC_TXDESCRIPTORNUM = ENET_DMA_DESC_NUMB-1;
//  COMMAND_bit.PASSRUNTFRAME = 1;
  MAC_COMMAND |= (1 << 6);                  /* Set Bit 6 (PASSRUNTFRAME)    */
//  COMMAND_bit.PASSRXFILTER = 1;
  MAC_COMMAND |= (1 << 7);                  /* Set Bit 7 (PASSRXFILTER)     */
//  MAC1_bit.PARF = 1;
  MAC_MAC1 |= (1 << 1);                     /* Set bit 1 (PARF) in MAC1     */
  MAC_MAC2 = 0x30;                          /* Set CRCEN & PADCRCEN in MAC2 */

//  CLRT_bit.RM    = 0xF;
//  CLRT_bit.CW    = 0x37;
  MAC_CLRT = 0x0000370F;                    /* Set RM (0xF) and CW (0x3700) */

//  IPGR_bit.IPGR2 = 0x12;
//  IPGR_bit.IPGR1 = 0x0c;
  MAC_IPGR = 0x00000C12;                    /*Set IPGR2(0x12) & IPGR1(0xC00)*/
  
  MAC_IPGT = 0x12;                          /* Set IPGT register            */
//  MAC2_bit.FD = COMMAND_bit.FULLDUPLEX = 0;
  MAC_MAC2 &=~(1 << 0);                     /* Clear Bit 0 (FD) in MAC2     */
  MAC_COMMAND &=~(1 << 10);                 /* Clear Bit 10 (FULLDUPLEX)    */
//  SUPP_bit.SPEED = 0;
  MAC_SUPP &=~(1 << 8);                     /* Clear Bit 8 (SPEED) in SUPP  */


  /* find PHY address */
  for(PhyAddr = 1; PhyAddr < 32; ++PhyAddr)
  {
    /* See Micrel PHY KS8721 Users Manual for more details */
    if((ENET_MIIReadRegister(PhyAddr, PHY_PHYIDR1) & 0xFFFF) != 0x0022)
    {
      continue;
    }

    if ((ENET_MIIReadRegister(PhyAddr, PHY_PHYIDR2) & 0xFFFF) == 0x1619)
    {
      break;
    }
  }

  if(PhyAddr == 32)
  {
    return(FALSE);
  }

  printf("PHY Address - %d\r\n",PhyAddr);

  ENET_MIIWriteRegister(PhyAddr,PHY_BMCR,BMCR_RESET);

  to = PHY_TO;

  while(to)
  {
    Reg = ENET_MIIReadRegister(PhyAddr,PHY_BMCR);

    if(!(Reg & BMCR_RESET))
    {
      break;
    }
  }

  if(!to)
  {
    return(FALSE);
  }

#ifdef TRACE_PHY
  Reg = ENET_MIIReadRegister(PhyAddr,0);
  printf("PHY_BMCR(0) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,1);
  printf("PHY_BMSR(1) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x4);
  printf("PHY_ANAR(4) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x5);
  printf("PHY_ANLPAR(5) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x6);
  printf("PHY_ANER(6) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x1f);
  printf("PHY_100PHY(1F) - 0x%04X\n\r",Reg);
#endif // TRACE_PHY

  if(ENET_MIIReadRegister(PhyAddr,PHY_BMSR ) & BMSR_NOPREAM)
  {
    // PHY support preamble suppression
    MAC_MCFG |= 1 << 1;
  }

#if AUTO_NEGOTIATION_ENA != 0
  // PHY interrupt init, clear and enable
  // Set Auto-Negotiation Advertisement
  ENET_MIIWriteRegister(PhyAddr,PHY_ANAR, 1 | ANAR_10BT | ANAR_10BT_FULL | ANAR_100BT | ANAR_100BT_FULL);
  // Enable Auto-Negotiation
  Reg = BMCR_AN | BMCR_RE_AN;
#else

  Reg = 0;

  if(FIX_DUPLEX == FULL_DUPLEX)
  {
    Reg |= BMCR_DUPLEX;
  }

  if(FIX_SPEED == SPEED_100)
  {
    Reg |= BMCR_SPEED_100;
  }

#endif

  ENET_MIIWriteRegister(PhyAddr,PHY_BMCR,Reg);

  to = PHY_TO;

  while(to)
  {
    --to;
    if(ENET_MIIReadRegister(PhyAddr,PHY_BMSR) & BMSR_LINK_ESTABLISHED)
    {
      break;
    }
  }

  if(!to)
  {
    return(FALSE);
  }

#ifdef TRACE_PHY
  Reg = ENET_MIIReadRegister(PhyAddr,0);
  printf("PHY_BMCR(0) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,1);
  printf("PHY_BMSR(1) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x4);
  printf("PHY_ANAR(4) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x5);
  printf("PHY_ANLPAR(5) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x6);
  printf("PHY_ANER(6) - 0x%04X\n\r",Reg);
  Reg = ENET_MIIReadRegister(PhyAddr,0x1f);
  printf("PHY_100PHY(1F) - 0x%04X\n\r",Reg);
#endif // TRACE_PHY

  Reg = ENET_MIIReadRegister(PhyAddr,PHY_100PHY) & PHYCR_MODE;

  switch(Reg)
  {
  case 0x04:  // 10 BASE T Half-duplex
    printf("10 BASE T Half-duplex\r\n");
    Reg = 0;
    break;
  case 0x08:  // 100 BASE TX Half-duplex
    printf("100 BASE TX Half-duplex\r\n");
    Reg = BMCR_SPEED_100;
//    SUPP_bit.SPEED = 1;
    MAC_SUPP |= (1 << 8);                   /* Set Bit 8 (SPEED) in SUPP    */
    break;
  case 0x14: // 10 BASE T Full-duplex
    printf("10 BASE T Full-duplex\r\n");
    Reg = BMCR_DUPLEX;
    MAC_IPGT = 0x15;
//    MAC2_bit.FD = COMMAND_bit.FULLDUPLEX = 1;
    MAC_MAC2 |= (1 << 0);                   /* Set Bit 0 (FD) in MAC2       */
    MAC_COMMAND |= (1 << 10);               /* Set Bit 10 (FULLDUPLEX)      */
    break;
  case 0x18: // 100 BASE TX Full-duplex
    printf("100 BASE TX Full-duplex\r\n");
    Reg = BMCR_SPEED_100 | BMCR_DUPLEX;
    MAC_IPGT = 0x15;
//    MAC2_bit.FD = COMMAND_bit.FULLDUPLEX = 1;
    MAC_MAC2 |= (1 << 0);                   /* Set Bit 0 (FD) in MAC2       */
    MAC_COMMAND |= (1 << 10);               /* Set Bit 10 (FULLDUPLEX)      */
//    SUPP_bit.SPEED = 1;
    MAC_SUPP |= (1 << 8);                   /* Set Bit 8 (SPEED) in SUPP    */
    break;
  default:
    return(FALSE);
  }

  // Disable Auto-Negotiation and update speed and duplex settings
  ENET_MIIWriteRegister(PhyAddr,PHY_BMCR,Reg);

  to = PHY_TO;

  while(to)
  {
    --to;
    if(ENET_MIIReadRegister(PhyAddr,PHY_BMSR) & BMSR_LINK_ESTABLISHED)
    {
      break;
    }
  }

  if(!to)
  {
    return(FALSE);
  }

  for( i = 0; i < ENET_DMA_DESC_NUMB; ++i)
  {
    EnetDmaRx[i].EnetRxCtrl.Size = EMAC_MAX_PACKET_SIZE-1;
    EnetDmaRx[i].EnetRxCtrl.Intr = 1;
    EnetDmaRxSta[i].Data[0] = EnetDmaRxSta[i].Data[1] = 0;

    EnetDmaTx[i].EnetTxCtrl.Data = (1<<31) | (1<<30) | (1<<29) | (1<<28) | (1<<26) | (EMAC_MAX_PACKET_SIZE-1);
    EnetDmaTxSta[i].Data = 0;

  }

  EnetDmaRx[0].pBuffer = (pInt32U)RxBuff0;
  EnetDmaRx[1].pBuffer = (pInt32U)RxBuff1;
  EnetDmaTx[0].pBuffer = (pInt32U)TxBuff0;
  EnetDmaTx[1].pBuffer = (pInt32U)TxBuff1;
  MAC_RXCONSUMEINDEX = 0;
  MAC_TXPRODUCEINDEX = 0;
  MAC_TXDESCRIPTOR = (Int32U)EnetDmaTx;
  MAC_TXSTATUS = (Int32U)EnetDmaTxSta;
  MAC_RXDESCRIPTOR = (Int32U)EnetDmaRx;
  MAC_RXSTATUS = (Int32U)EnetDmaRxSta;

//  COMMAND_bit.RXENABLE = 1;
  MAC_COMMAND |= (1 << 0);                  /* Set Bit 0 (RXENABLE) in CMD  */
//  MAC1_bit.RE = 1;
  MAC_MAC1 |= (1 << 0);                     /* Set Bit 0 (RE) in MAC1       */
//  COMMAND_bit.TXENABLE = 1;
  MAC_COMMAND |= (1 << 1);                  /* Set Bit 1 (TXENABLE) in CMD  */

  return(TRUE);
}

/*************************************************************************
 * Function Name: tapdev_read
 * Parameters:
 * Return:
 *
 * Description: Read data for MAC/DMA Controller
 *
 *************************************************************************/
Int32U tapdev_read(void * pPacket)
{
Int32U Indx = MAC_RXCONSUMEINDEX;
Int32U Size = EMAC_MAX_PACKET_SIZE;
  if(Indx == MAC_RXPRODUCEINDEX)
  {
    return(0);
  }
  Size = MIN(Size,(EnetDmaRxSta[Indx].RxSize+1));
  memcpy(pPacket,EnetDmaRx[Indx].pBuffer,Size);
  if(++Indx > MAC_RXDESCRIPTORNUM)
  {
    Indx = 0;
  }
  MAC_RXCONSUMEINDEX = Indx;
  return(Size);
}

/*************************************************************************
 * Function Name: tapdev_send
 * Parameters:
 * Return: Boolean
 *
 * Description: Send data to MAC/DMA Controller
 *
 *************************************************************************/
Boolean tapdev_send(void *pPacket, Int32U size)
{
Int32U Indx, IndxHold = MAC_TXPRODUCEINDEX + 1;
  if(size == 0)
  {
    return(TRUE);
  }
  if(IndxHold > MAC_TXDESCRIPTORNUM)
  {
    IndxHold = 0;
  }
  if(IndxHold == MAC_TXCONSUMEINDEX)
  {
    return(FALSE);
  }
  Indx = MAC_TXPRODUCEINDEX;
  size = MIN(size,EMAC_MAX_PACKET_SIZE);
  memcpy(EnetDmaTx[Indx].pBuffer,pPacket,size);
  EnetDmaTx[Indx].EnetTxCtrl.Size = size - 1;
  MAC_TXPRODUCEINDEX = IndxHold;

  return(TRUE);
}

/*************************************************************************
 * Function Name: ENET_MIIWriteRegister
 * Parameters: Int8U DevId, Int8U RegAddr, Int32U Value
 * Return: none
 *
 * Description: Writes a value on the PHY registers
 *
 *************************************************************************/
static void ENET_MIIWriteRegister (Int8U DevId, Int8U RegAddr, Int32U Value)
{
    MAC_MCMD = 0;                             /* set read operation           */
//    MADR_bit.PHY_ADDR = DevId;
  
    MAC_MADR = MAC_MADR & 0xFFFFE0E0;         /* Reset PHY_ADDR and REG_ADDR  */
    MAC_MADR |= (DevId << 8) | (RegAddr << 0); /* Set PHY_ADDR and REG_ADDR   */ // \TODO is this working??

    MAC_MWTD = Value;
//    while(MIND_bit.BUSY);
    while (MAC_MIND & (1 << 0));                                                // \TODO is this working??
}

/*************************************************************************
 * Function Name: ENET_MIIReadRegister
 * Parameters: Int8U DevId, Int8U RegAddr, Int32U Value
 * Return: Int32U
 *
 * Description: Read a value from the PHY registers
 *
 *************************************************************************/
static Int32U ENET_MIIReadRegister (Int8U DevId, Int8U RegAddr)
{
    MAC_MCMD = 0;
//    MADR_bit.PHY_ADDR = DevId;  // set the MII Physical address
//    MADR_bit.REGADDR = RegAddr; // set the MII register address
    MAC_MADR = MAC_MADR & 0xFFFFE0E0;         /* Reset PHY_ADDR and REG_ADDR  */
    MAC_MADR |= (DevId << 8) | (RegAddr << 0); /* Set PHY_ADDR and REG_ADDR   */ // \TODO is this working??
  
  MAC_MCMD = 1;                   // set read operation
//  while(MIND_bit.BUSY | MIND_bit.NOT_VALID);
  while (MAC_MIND & (5 << 0));                                                  // \TODO is this working??
  return(MAC_MRDD);
}


