/*
 *  Routines to access hardware
 *
 *  Copyright (c) 2013 Realtek Semiconductor Corp.
 *
 */

#include "rtl8195a.h"
#include "rtl8195a_mii.h"
#include "hal_mii.h"
#include "osdep_api.h"
//#include "hal_gdma.h"

#ifdef CONFIG_MII_EN

volatile u8 *pTxDataBuf = NULL;
volatile u8 *pRxDataBuf = NULL;

#if 0 
volatile TX_DESC_FMT TxDesc[MII_TX_DESC_NO];
volatile RX_DESC_FMT RxDesc[MII_RX_DESC_NO];
volatile u32 TxDescIdx = 0;
volatile u32 TxBufIdx = 0;
volatile u32 RxDescIdx = 0;
volatile u32 RxBufIdx = 0;
volatile u8 isTxDone = 0;
volatile u8 isRxDone = 0;
volatile u32 CurrDataLen = 0;
volatile u32 ReadDescIdx = 0;
volatile u32 ReadBufIdx = 0;
volatile u32 CurrSentSize = 0;
volatile u32 CurrReceivedSize = 0;
#endif

// by jimmy 

volatile PTX_DESC_FMT TxDesc;
volatile PRX_DESC_FMT RxDesc;

volatile u8 TxDescWrPtr = 0;
volatile u8 RxDescRdPtr = 0;

volatile u32 CurrDataLen = 0;
#if 0
HAL_GDMA_OBJ gdmaObj;
u8 isGdmaAllocated;
volatile u8 isDmaDone;
#endif
extern HAL_ETHER_ADAPTER HalEtherAdp;



VOID Mii_ISR(
	IN VOID *Data
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER) Data;
    volatile u32 RegValue = HAL_MII_READ32(REG_MII_ISRIMR);

	
	if((RegValue & ISR_RXOK) && ((pEthAdapter->InterruptMask) & IMR_RXOK))
	{	
		/* Disable interrupt and enbale again when the rx descriptor ring is empty */
		pEthAdapter->InterruptMask &= (~IMR_RXOK);

		if(pEthAdapter->CallBack != NULL)
		{
			pEthAdapter->CallBack(ETH_RXDONE, 0);  // receive size is meanless here
		}
	}

	if((RegValue & ISR_RER_OVF) && ((pEthAdapter->InterruptMask) & IMR_RER_OVF))
	{
		DBG_MII_WARN("ISR_RER_OVF 0x%08X \n", RegValue);

		/* Ignore this interrupt, rx thread will clean the OWN bit */
		pEthAdapter->InterruptMask &= (~ISR_RER_OVF);
	}

	if((RegValue & ISR_TXOK) && ((pEthAdapter->InterruptMask) & IMR_TXOK))
	{
		/* Disable TxOK interrupt */
		pEthAdapter->InterruptMask &= (~IMR_TXOK);
		/* User callback function */
		if(pEthAdapter->CallBack != NULL)
		{
			pEthAdapter->CallBack(ETH_TXDONE, 0);
		}
    }

	if((RegValue & ISR_LINKCHG) && ((pEthAdapter->InterruptMask) & IMR_LINKCHG))
	{
		if(pEthAdapter->CallBack != NULL)
		{
			if(HAL_MII_READ32(REG_MII_MS) & MS_LINKB)
			{
				pEthAdapter->CallBack(ETH_LINKDOWN, 0);
			}
			else
			{
				pEthAdapter->CallBack(ETH_LINKUP, 0);
			}
		}
	}

	HAL_MII_WRITE32(REG_MII_ISRIMR, (pEthAdapter->InterruptMask) | ISR_CLR_ALL);
}


u16
HalMiiRwPhyRegRtl8195a(u8 Operation, u8 Address, u16 Data)
{
	u32 tmp;


	tmp = (Operation << 31) | ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((Address << 16) & MIIA_PHY_REG_ADDR_MASK) | Data;
	HAL_MII_WRITE32(REG_MII_MIIA, tmp);
	HalDelayUs(1000);

	if(Operation)
	{
		/* Wait for write operation is completed */
		do {
		}while(HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG);

		return 0;
	}
	else
	{
		/* Wait for read operation is completed */
		do {
		}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);

		return (u16)(HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF);
	}
}


VOID
HalMiiInitIrqRtl8195a(
	IN VOID *Data
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER) Data;
	PIRQ_HANDLE	pMiiIrqHandle = &(pEthAdapter->IrqHandle);


	if(pEthAdapter == NULL)
		DBG_MII_ERR("pEthAdapter is NULL!\n");

	pMiiIrqHandle->Data	 = (u32) pEthAdapter;
	pMiiIrqHandle->IrqNum	 = GMAC_IRQ;
	pMiiIrqHandle->IrqFun	 = (IRQ_FUN) Mii_ISR;
	pMiiIrqHandle->Priority = 6;
	InterruptRegister(pMiiIrqHandle);
	InterruptEn(pMiiIrqHandle);
}


static VOID
HalMiiDeInitIrqRtl8195a(
	IN VOID *Data
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER) Data;
	IRQ_HANDLE	MiiIrqHandle;


	if(pEthAdapter == NULL)
		DBG_8195A("pEthAdapter is NULL!\n");

	/* Clear all interrupt status */
	HAL_MII_WRITE32(REG_MII_ISRIMR, ISR_CLR_ALL);

	MiiIrqHandle.Data	 = (u32) pEthAdapter;
	MiiIrqHandle.IrqNum	 = GMAC_IRQ;
	MiiIrqHandle.IrqFun	 = (IRQ_FUN) Mii_ISR;
	MiiIrqHandle.Priority = 6;
	InterruptDis(&MiiIrqHandle);
	InterruptUnRegister(&MiiIrqHandle);
}

#if 0
void Mii_DmaDone_ISR(u32 id)
{
    isDmaDone = 1;
}
#endif

s32
HalMiiInitRtl8195a(
	IN VOID
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER)&HalEtherAdp;
	u32 regVal = 0;
	u8 macAddr[6];
	u32 i = 0;
	u16 ret;
	

	if((!(pEthAdapter->tx_desc_num)) || (!(pEthAdapter->rx_desc_num)))
	{
		DBG_MII_ERR("Invalid Tx/Rx descriptor number!\n");
		return -1;
	}
	DBG_MII_INFO("Tx/Rx: %d/%d\n", pEthAdapter->tx_desc_num, pEthAdapter->rx_desc_num);

	regVal = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PINMUX_CTRL);
	regVal &= ~(BIT_EEPROM_PIN_EN);
	HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PINMUX_CTRL, regVal);

	regVal = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PINMUX_CTRL);
	regVal &= ~(BIT_SIC_PIN_EN);
	HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PINMUX_CTRL, regVal);

	SPI_FLASH_PIN_FCTRL(OFF);

	ACTCK_GPIO_CCTRL(ON);
	SLPCK_GPIO_CCTRL(ON);
	HAL_PERI_ON_WRITE32(REG_GPIO_SHTDN_CTRL, 0x7FF);
	GPIO_FCTRL(ON);

	/* 1. enable MII Pinmux & disable SDIO Host/Device mode Pinmux */
	regVal = HAL_READ32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL);
	regVal |= BIT24;
	regVal &= ~(BIT0 | BIT1); // Important!
	HAL_WRITE32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL, regVal);

	/* 2. enable MII IP block (214, 12) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN);
	regVal |= BIT12;
	HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, regVal);

	/* 3. Lexra2AHB Function Enable (304, 11) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL);
	regVal |= BIT11;
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, regVal);

	/* 4. enable MII bus clock (240, 24|25) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0);
	regVal |= (BIT24 | BIT25);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0, regVal);

	/* 5. */
	regVal = HAL_READ32(SYSTEM_CTRL_BASE, 0x74) & 0xFFFFC7FF;
	HAL_WRITE32(SYSTEM_CTRL_BASE, 0x74, (regVal | 0x00003000));

	/* 6. AHB mux: select MII (214, 13) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN);
	regVal |= BIT13;
	HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, regVal);

	/* 7. Vendor Register Clock Enable (230, 6|7) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_PESOC_CLK_CTRL);
	regVal |= (BIT6 | BIT7);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_CLK_CTRL, regVal);

	/* 8. Enable GMAC Lexra Timeout (090, 16|17|18) */
	regVal = HAL_READ32(VENDOR_REG_BASE, 0x0090);
	regVal |= (BIT16 | BIT17 | BIT18);
	HAL_WRITE32(VENDOR_REG_BASE, 0x0090, regVal);

	/* 9. Endian Swap Control (304, 12|13) */
	regVal = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL);
	regVal |= (BIT12 | BIT13);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, regVal);

	HAL_MII_WRITE32(REG_MII_COM, (HAL_MII_READ32(REG_MII_COM) | COM_RST));

	/* Tx settings */
	HAL_MII_WRITE32(REG_MII_TC, ((TC_IFG_TIME << 10) & TC_IFG_MASK) | ((TC_NORMAL_MODE << 8) & TC_LBK_MASK));  // Normal mode

	/* Rx settings */
	HalMiiGetMacAddressRtl8195a(macAddr);
	HAL_MII_WRITE32(REG_MII_IDR0, (macAddr[0] << 24) | (macAddr[1] << 16) | (macAddr[2] << 8) | macAddr[3]);
	HAL_MII_WRITE32(REG_MII_IDR4, (macAddr[4] << 24) | (macAddr[5] << 16));

	HAL_MII_WRITE32(REG_MII_RC, RC_AER | RC_AR | RC_AB | RC_AM | RC_APM | RC_AAP);  // Accept error/runt/broadcast/multicast, etc.
	HAL_MII_WRITE32(REG_MII_COM, (HAL_MII_READ32(REG_MII_COM) | COM_RXJUMBO));  // Support jumbo packet receiving
	HAL_MII_WRITE32(REG_MII_ETNRXCPU1, 0x01010100);

	TxDesc = (PTX_DESC_FMT)(pEthAdapter->TxDescAddr);
	RxDesc = (PRX_DESC_FMT)(pEthAdapter->RxDescAddr);
	if((TxDesc == NULL) || (RxDesc == NULL))
	{
		DBG_MII_ERR("Invalid Tx/Rx descriptor address!\n");
		return -1;
	}

	HAL_MII_WRITE32(REG_MII_TXFDP1, (u32)TxDesc);
	HAL_MII_WRITE32(REG_MII_RXFDP1, (u32)RxDesc);
	HAL_MII_WRITE32(REG_MII_IOCMD1, ((IOCMD1_DSC_FMT_EXTRA << 28) & IOCMD1_DSCFMTEXTRA_MASK) |
					IOCMD1_EN_1GB);  // Extra desc. format = 011, support 1GB addressing
	HAL_MII_WRITE32(REG_MII_IOCMD, IOCMD_SHORT_DES_FMT |
					((IOCMD_TXFTH_256 << 19) & IOCMD_TXFTH_MASK) |
					((IOCMD_RXFTH_256 << 11) & IOCMD_RXFTH_MASK) |
					IOCMD_TE);	// short desc. format = 1, Tx & Rx FIFO threshold = 256 bytes, enable MII Tx

	/* Reset PHY */
	ret = HalMiiRwPhyRegRtl8195a(1, 0, PHY_SW_RESET);
	while(1)
	{
		/* Check the reset status */
		ret = HalMiiRwPhyRegRtl8195a(0, 0, 0);
		if(!(ret & PHY_SW_RESET))
			break;
	}
	/* Auto-Negotiation */
	HalMiiForceLinkRtl8195a(-1, -1);

	HalMiiInitIrqRtl8195a((VOID*)(&HalEtherAdp));
	HAL_WRITE32(0xE000E100, 0x0, 0x00040000);  // enable external interrupt[18] (GMAC)
	/* ISR & IMR */
	HalEtherAdp.InterruptMask = IMR_LINKCHG | IMR_TXOK | IMR_RER_OVF | IMR_RXOK | ISR_CLR_ALL;
	HAL_MII_WRITE32(REG_MII_ISRIMR, HalEtherAdp.InterruptMask);
	#if 0
	/* Init GDMA */
	gdmaObj.GdmaIrqHandle.IrqFun = (IRQ_FUN)Mii_DmaDone_ISR;
	gdmaObj.GdmaIrqHandle.Data = (u32)(&gdmaObj);
	isGdmaAllocated = HalGdmaMemCpyInit(&gdmaObj);
	#endif

	pTxDataBuf = pEthAdapter->pTxPktBuf;
	pRxDataBuf = pEthAdapter->pRxPktBuf;
	if((pTxDataBuf == NULL) || (pRxDataBuf == NULL))
	{
		DBG_MII_ERR("Invalid Tx/Rx packet buffer address!\n");
		return -1;
	}

	/* Fill Tx descriptors */
	for(i = 0; i < (pEthAdapter->tx_desc_num); i++)
	{
		if(i == ((pEthAdapter->tx_desc_num) - 1))
			TxDesc[i].dw1 = TX_DESC_EOR;
		TxDesc[i].addr = (u32)(pTxDataBuf + (i * MII_BUF_SIZE));
		TxDesc[i].dw2 = ((TX_DESC_VLAN_REMOVE << 25) & TX_DESC_VLAN_ACT_MASK) | (C_VLAN_HDR & TX_DESC_VLAN_TAG_MASK);
		TxDesc[i].dw3 = 0;
		TxDesc[i].dw4 = 0;
	}
	/* Fill Rx descriptors */
	for(i = 0; i < (pEthAdapter->rx_desc_num); i++)
	{	
		if(i == ((pEthAdapter->rx_desc_num) - 1))
			RxDesc[i].dw1 = RX_DESC_OWN | RX_DESC_EOR | MII_BUF_SIZE;
		else
			RxDesc[i].dw1 = RX_DESC_OWN | MII_BUF_SIZE;
		RxDesc[i].addr = (u32)(pRxDataBuf + (i * MII_BUF_SIZE));
		RxDesc[i].dw2 = 0;
		RxDesc[i].dw3 = 0;
	}

	#if 0
	// Reset flag
	TxDescIdx = 0;
	TxBufIdx = 0;
	isTxDone = 0;
	CurrDataLen = 0;
	CurrSentSize = 0;

	RxDescIdx = 0;
	RxBufIdx = 0;
	isRxDone = 0;

	CurrReceivedSize = 0;
	ReadBufIdx = 0;
	#endif
	HAL_MII_WRITE32(REG_MII_IOCMD, (HAL_MII_READ32(REG_MII_IOCMD) | IOCMD_RE));  // enable MII Rx
	HalDelayUs(1000);

	DBG_8195A("Initialization is finished...\n");

	return 0;
}


VOID
HalMiiDeInitRtl8195a(
	IN VOID
)
{
	#if 0
	if (isGdmaAllocated)
		HalGdmaMemCpyDeInit(&gdmaObj);
	#endif
	HalMiiDeInitIrqRtl8195a((VOID*)(&HalEtherAdp));
	HAL_WRITE32(0xE000E100, 0x0, (HAL_READ32(0xE000E100, 0x0) & (~BIT18)));  // disable external interrupt[18] (GMAC)

	ACTCK_MII_MPHY_CCTRL(OFF);
	SLPCK_MII_MPHY_CCTRL(OFF);
	PinCtrl(MII, 0, OFF);
	MII_FCTRL(OFF);
}


s32
HalMiiWriteDataRtl8195a(
	IN const char *Data,
	IN u32 Size
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER)&HalEtherAdp ;


	if((Data == NULL) || (Size == 0) || (Size > MAX_FRAME_SIZE))
	{
		DBG_MII_ERR("Invalid parameter!\n");
		return -1;
	}

	/* Check if current Tx descriptor is available */
	u8 tx_serach_idx = TxDescWrPtr;
	if((((volatile u32)TxDesc[tx_serach_idx].dw1) & TX_DESC_OWN) == 0)
	{
		#if 1
		_memcpy((void*)(TxDesc[tx_serach_idx].addr), Data, Size);
		#else
		isDmaDone = 0;
		HalGdmaMemCpy(&gdmaObj, (void*)(TxDesc[tx_serach_idx].addr), (void*)Data, Size);		
		while (isDmaDone == 0);
		#endif

		TxDesc[tx_serach_idx].dw1 &= TX_DESC_EOR;  // keep the EOR bit
		TxDesc[tx_serach_idx].dw1 |= TX_DESC_OWN | TX_DESC_FS | TX_DESC_LS | TX_DESC_CRC | Size;
		TxDesc[tx_serach_idx].dw2 = ((TX_DESC_VLAN_REMOVE << 25) & TX_DESC_VLAN_ACT_MASK) | (C_VLAN_HDR & TX_DESC_VLAN_TAG_MASK);
		CurrDataLen += Size;
	}
	else
	{
		/* Enable TxOK interrupt */
		HalEtherAdp.InterruptMask |= IMR_TXOK;
		HAL_MII_WRITE32(REG_MII_ISRIMR, HalEtherAdp.InterruptMask);
		/* Enable Tx ring1 */
		HAL_MII_WRITE32(REG_MII_IOCMD, (HAL_MII_READ32(REG_MII_IOCMD) | IOCMD_TXFN1ST));
		DBG_MII_WARN("Tx ring full\n");

		/* Upper layer will re-transmit this packet */
		return 0;
	}

	/* Check if reach to the end of the ring */
	if(tx_serach_idx == ((pEthAdapter->tx_desc_num) - 1))
		TxDescWrPtr = 0;
	else
		TxDescWrPtr++;


	return Size;
}


u32
HalMiiSendPacketRtl8195a(
	IN VOID
)
{
	u32 size = CurrDataLen;


	/* Enable TxOK interrupt */
	HalEtherAdp.InterruptMask |= IMR_TXOK;
	HAL_MII_WRITE32(REG_MII_ISRIMR, HalEtherAdp.InterruptMask);
	/* Enable Tx ring1 */
	HAL_MII_WRITE32(REG_MII_IOCMD, (HAL_MII_READ32(REG_MII_IOCMD) | IOCMD_TXFN1ST));
	CurrDataLen = 0;
	
	
	return size;
}


u32
HalMiiReceivePacketRtl8195a(
	IN VOID
)
{
	u8 rx_serach_idx = RxDescRdPtr;
	u32 rx_len = 0;


	/* Check if Rx descriptor is available */
	if((((volatile u32)RxDesc[rx_serach_idx].dw1) & RX_DESC_OWN) == 0)
	{
		rx_len = (RxDesc[rx_serach_idx].dw1) & RX_DESC_DATA_LEN_MASK;
	}
	else
	{
		/* Enable interrupt again */
		DBG_MII_INFO("RX ring empty\n\r");
		HalEtherAdp.InterruptMask |= (IMR_RXOK | ISR_RER_OVF);
		HAL_MII_WRITE32(REG_MII_ISRIMR, HalEtherAdp.InterruptMask);
		return 0;
	}

	return rx_len;
}


u32
HalMiiReadDataRtl8195a(
	IN u8 *Data,
	IN u32 Size
)
{
	PHAL_ETHER_ADAPTER pEthAdapter = (PHAL_ETHER_ADAPTER)&HalEtherAdp;
	u32 read_idx = RxDescRdPtr;


	if((Data == NULL) || (Size == 0) || (Size > 1518))
	{
		DBG_MII_ERR("Wrong params\n");
		return 0;
	}

	#if 1
	_memcpy((void*)Data, (void*)(RxDesc[read_idx].addr + 2), Size);
	#else
	isDmaDone = 0;
	HalGdmaMemCpy(&gdmaObj, (void*)Data, (void*)(RxDesc[read_idx].addr + 2), Size);	
	while (isDmaDone == 0);
	#endif
	
	RxDesc[read_idx].dw1 &= RX_DESC_EOR;  // keep the EOR bit
	RxDesc[read_idx].dw1 |= (RX_DESC_OWN | MII_BUF_SIZE);
	//RxDesc[read_idx].addr = (u32)(pRxDataBuf + (read_idx * MII_BUF_SIZE));
	RxDesc[read_idx].dw2 = 0;
	RxDesc[read_idx].dw3 = 0;

	/* Update the read pointer */
	if(read_idx == ((pEthAdapter->rx_desc_num) - 1))
		RxDescRdPtr = 0;
	else
		RxDescRdPtr++;

	
	return Size;
}


VOID
HalMiiGetMacAddressRtl8195a(
	IN u8 *Addr
)
{
	u8 *ptr;

// To do: Get info. from Flash or Efuse
	ptr = Addr;
	*(ptr)   = 0x12;
	*(ptr+1) = 0x34;
	*(ptr+2) = 0x56;
	*(ptr+3) = 0x78;
	*(ptr+4) = 0x9A;
	*(ptr+5) = 0xBC;
}


u32
HalMiiGetLinkStatusRtl8195a(
	IN VOID
)
{
	u32 ret;


	if(HAL_MII_READ32(REG_MII_MS) & MS_LINKB)
		ret = 0;  // Link down
	else
		ret = 1;  // Link up
	
	return ret;
}


#if 0
VOID
HalMiiForceLinkRtl8195a(
	IN s32 Speed,
	IN s32 Duplex
)
{
	u32 regVal = 0;
	u16 ret;

	#if 0
	/* Reset PHY */
	HAL_MII_WRITE32(REG_MII_MIIA, MIIA_FLAG |
								((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) |
								((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK) |
								PHY_SW_RESET);
	HalDelayUs(1000);
	do {
	}while(HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG);

	while(1)
	{
		/* Check reset status */
		HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));
		HalDelayUs(1000);
		do {
		}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);

		regVal = HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF;
		if(!(regVal & PHY_SW_RESET))
			break;
	}
	DBG_MII_INFO("PHY reset is completed...\n");
	#endif
	/* Switch to Page 0 */
	ret = HalMiiRwPhyRegRtl8195a(1, 31, 0);

	if(((Speed == 1) && (Duplex == 1)) || ((Speed == 1) && (Duplex == 0)) || ((Speed == 0) && (Duplex == 1)) || ((Speed == 0) && (Duplex == 0)))
	{
		/* Disable Auto-negotiation */
		HAL_MII_WRITE32(REG_MII_MIIA, MIIA_FLAG |
									((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) |
									((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK) |
									0x0000);
		HalDelayUs(1000);
		do {
		}while(HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG);

		/* Manual settings */
		HAL_MII_WRITE32(REG_MII_MIIA, MIIA_FLAG |
									((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) |
									((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK) |
									(((Speed << 13) & PHY_SPEED_LSB) | ((Duplex << 8) & PHY_DUPLEX_MODE)));
		HalDelayUs(1000);
		do {
		}while(HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG);
	}
	else
	{		
		#if 1
		/* Reset PHY */
		HAL_MII_WRITE32(REG_MII_MIIA, MIIA_FLAG |
									((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) |
									((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK) |
									PHY_SW_RESET);
		HalDelayUs(1000);
		do {
		}while(HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG);

		while(1)
		{
			/* Check reset status */
			HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));
			HalDelayUs(1000);
			do {
			}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);

			regVal = HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF;
			if(!(regVal & PHY_SW_RESET))
				break;
		}
		DBG_MII_INFO("PHY reset is completed...\n");
		#endif
		while(1)
		{
			/* Check Auto-negotiation process status */
			HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG1_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));
			HalDelayUs(1000);
			do {
			}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);
		
			regVal = HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF;
			if(regVal & PHY_NWAY_COMPLETE)
				break;
		}
		DBG_MII_INFO("Auto-negotiation is completed...\n");
	}

	/* Wait until link is up */
	while(1)
	{
		HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG1_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));
		HalDelayUs(1000);
		do {
		}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);

		HalDelayUs(1000);

		HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG1_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));
		HalDelayUs(1000);
		do {
		}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);

		regVal = HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF;
		if(regVal & PHY_LINK_STATUS)
			break;
	}

	/* Get link info. */
	HAL_MII_WRITE32(REG_MII_MIIA, ((PHY_ADDRESS << 26) & MIIA_PHY_ADDR_MASK) | ((PHY_REG0_ADDR << 16) & MIIA_PHY_REG_ADDR_MASK));	
	HalDelayUs(1000);
	do {
	}while((HAL_MII_READ32(REG_MII_MIIA) & MIIA_FLAG) == 0);
	
	regVal = HAL_MII_READ32(REG_MII_MIIA) & 0xFFFF;
	
	if((!(regVal & PHY_SPEED_MSB)) && (regVal & PHY_SPEED_LSB))
	{
		if(regVal & PHY_DUPLEX_MODE)
			DBG_8195A("Link status: 100 Mb/s, Full duplex\n");
		else
			DBG_8195A("Link status: 100 Mb/s, Half duplex\n");
	}
	else if((!(regVal & PHY_SPEED_MSB)) && (!(regVal & PHY_SPEED_LSB)))
	{
		if(regVal & PHY_DUPLEX_MODE)
			DBG_8195A("Link status: 10 Mb/s, Full duplex\n");
		else
			DBG_8195A("Link status: 10 Mb/s, Half duplex\n");
	}

	HalDelayUs(4000000);
}
#else
VOID
HalMiiForceLinkRtl8195a(
	IN s32 Speed,
	IN s32 Duplex
)
{
	u16 ret;


	/* Switch to Page 0 */
	ret = HalMiiRwPhyRegRtl8195a(1, 31, 0);

	if(((Speed == 1) && (Duplex == 1)) || ((Speed == 1) && (Duplex == 0)) || ((Speed == 0) && (Duplex == 1)) || ((Speed == 0) && (Duplex == 0)))
	{
		/* Disable Auto-negotiation */
		ret = HalMiiRwPhyRegRtl8195a(1, 0, 0x0);
		/* Manual settings */
		ret = HalMiiRwPhyRegRtl8195a(1, 0, (((Speed << 13) & PHY_SPEED_LSB) | ((Duplex << 8) & PHY_DUPLEX_MODE)));
	}
	else
	{		
		/* Restart Auto-negotiation */
		ret = HalMiiRwPhyRegRtl8195a(1, 0, PHY_NWAY_EN | PHY_RESTART_NWAY);
		while(1)
		{
			/* Check the reset status */
			ret = HalMiiRwPhyRegRtl8195a(0, 0, 0);
			if(!(ret & PHY_RESTART_NWAY))
				break;
		}

		/* Check Auto-negotiation process status */
		while(1)
		{
			ret = HalMiiRwPhyRegRtl8195a(0, 1, 0);
			if(ret & PHY_NWAY_COMPLETE)
				break;
		}
		DBG_MII_INFO("Auto-negotiation is completed...\n");
	}

	/* Wait until link is up */
	while(1)
	{
		ret = HalMiiRwPhyRegRtl8195a(0, 1, 0);
		HalDelayUs(1000);
		ret = HalMiiRwPhyRegRtl8195a(0, 1, 0);
		if(ret & PHY_LINK_STATUS)
			break;
	}

	/* Get link info. */
	ret = HalMiiRwPhyRegRtl8195a(0, 0, 0);
	if((!(ret & PHY_SPEED_MSB)) && (ret & PHY_SPEED_LSB))
	{
		if(ret & PHY_DUPLEX_MODE)
			DBG_8195A("Link status: 100 Mb/s, Full duplex\n");
		else
			DBG_8195A("Link status: 100 Mb/s, Half duplex\n");
	}
	else if((!(ret & PHY_SPEED_MSB)) && (!(ret & PHY_SPEED_LSB)))
	{
		if(ret & PHY_DUPLEX_MODE)
			DBG_8195A("Link status: 10 Mb/s, Full duplex\n");
		else
			DBG_8195A("Link status: 10 Mb/s, Half duplex\n");
	}

	HalDelayUs(4000000);
}
#endif


#ifdef CONFIG_MII_VERIFY
VOID MiiIrqHandle (IN VOID *Data);

VOID MiiIrqHandle (IN VOID *Data) {
    u32 RegValue = HalMiiGmacGetInterruptStatusRtl8195a();
	extern volatile u8 isRxOK;
	extern volatile u8 isTxOK;
	extern volatile u8 RxIntCnt;
	

//	DBG_8195A("ISR = 0x%08X\n", RegValue);
	if(RegValue & GMAC_ISR_ROK) {
		HalMiiGmacClearInterruptStatusRtl8195a(0x00410001);
		isRxOK = 1;
		RxIntCnt++;
	}

	if(RegValue & GMAC_ISR_TOK_TI) {
		HalMiiGmacClearInterruptStatusRtl8195a(0x00410040);
		isTxOK = 1;
    }
}


/**
 * MII Initialize.
 *
 * MII Initialize.
 *
 * Initialization Steps:
 *   I. Rtl8195A Board Configurations:
 *     1. MII Function Enable & AHB mux
 *
 * @return runtime status value.
 */
BOOL
HalMiiGmacInitRtl8195a(
        IN VOID *Data
        )
{
	u32 RegValue;


	/* 1. enable MII Pinmux & disable SDIO Host/Device mode Pinmux */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL);
	RegValue |= BIT24;
	RegValue &= ~(BIT0 | BIT1); // Important!
	HAL_WRITE32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL, RegValue);

	/* 2. enable MII IP block (214, 12) */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN);
	RegValue |= BIT12;
	HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, RegValue);

	/* 3. Lexra2AHB Function Enable (304, 11) */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL);
	RegValue |= BIT11;
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue);

	/* 4. enable MII bus clock (240, 24|25) */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0);
	RegValue |= (BIT24 | BIT25);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0, RegValue);

	/* 5. */
	RegValue = HAL_READ32(SYSTEM_CTRL_BASE, 0x74) & 0xFFFFC7FF;
	HAL_WRITE32(SYSTEM_CTRL_BASE, 0x74, (RegValue | 0x00003000));

	/* 6. AHB mux: select MII (214, 13) */
    	RegValue = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN);
	RegValue |= BIT13;
	HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, RegValue);

	/* 7. Vendor Register Clock Enable (230, 6|7) */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_CLK_CTRL);
	RegValue |= (BIT6 | BIT7);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_CLK_CTRL, RegValue);

	/* 8. Enable GMAC Lexra Timeout (090, 16|17|18) */
	RegValue = HAL_READ32(VENDOR_REG_BASE, 0x0090);
	RegValue |= (BIT16 | BIT17 | BIT18);
	HAL_WRITE32(VENDOR_REG_BASE, 0x0090, RegValue);

	/* 9. Endian Swap Control (304, 12|13) */
	RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL);
	RegValue |= (BIT12 | BIT13);
	HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue);

	return _TRUE;
}


BOOL
HalMiiGmacResetRtl8195a(
        IN VOID *Data
        )
{
	HAL_MII_WRITE32(REG_RTL_MII_CR, (HAL_MII_READ32(REG_RTL_MII_CR) | BIT0));

    return _TRUE;
}


BOOL
HalMiiGmacEnablePhyModeRtl8195a(
        IN VOID *Data
        )
{
    return _TRUE;
}


u32
HalMiiGmacXmitRtl8195a(
        IN VOID *Data
        )
{
    return 0;
}


VOID
HalMiiGmacCleanTxRingRtl8195a(
        IN VOID *Data
        )
{
}


VOID
HalMiiGmacFillTxInfoRtl8195a(
        IN VOID *Data
        )
{
    PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data;
    PTX_INFO pTx_Info = pMiiAdapter->pTx_Info;
    VOID* TxBuffer  = pMiiAdapter->TxBuffer;


    pTx_Info->opts1.dw = 0xBC8001FE;
    /* pTx_Info->opts1.dw = 0xBC800080; // size: 128 */

    pTx_Info->addr     = (u32)TxBuffer;
    pTx_Info->opts2.dw = 0x0400279F;
    pTx_Info->opts3.dw = 0x00000000;
    /* pTx_Info->opts4.dw = 0x57800000; */
    pTx_Info->opts4.dw = 0x1FE00000;

    HAL_MII_WRITE32(REG_RTL_MII_TXFDP1, pTx_Info);
}


VOID
HalMiiGmacFillRxInfoRtl8195a(
        IN VOID *Data
        )
{
    PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data;
    PRX_INFO pRx_Info = pMiiAdapter->pRx_Info;
    VOID* RxBuffer    = pMiiAdapter->RxBuffer;


    /* pRx_Info->opts1.dw = 0x80000200; //Data Length: 4095(FFF), 512(200) */
    pRx_Info->opts1.dw = 0x800001FC; //Data Length: 4095(FFF), 512(200)
    /* pRx_Info->opts1.dw = 0x8000007F; */

    pRx_Info->addr     = (u32)RxBuffer;
    pRx_Info->opts2.dw = 0x00000000;
    pRx_Info->opts3.dw = 0x00000000;

    HAL_MII_WRITE32(REG_RTL_MII_RXFDP1, pRx_Info);
}


VOID
HalMiiGmacTxRtl8195a(
        IN VOID *Data
        )
{
    u32 RegValue;


    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD);
    RegValue |= BIT_IOCMD_TXENABLE(1);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue);

    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD);
    RegValue |= BIT_IOCMD_FIRST_DMATX_ENABLE(1);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue);
}


VOID
HalMiiGmacRxRtl8195a(
        IN VOID *Data
        )
{
    u32 RegValue;


    RegValue = HAL_MII_READ32(REG_RTL_MII_TCR);

    HAL_MII_WRITE32(REG_RTL_MII_TCR, 0x00000D00);  // loopback R2T mode

    RegValue = HAL_MII_READ32(REG_RTL_MII_RCR);
    HAL_MII_WRITE32(REG_RTL_MII_RCR, 0x0000007F);

    RegValue = HAL_MII_READ32(REG_RTL_MII_ETNRXCPU1);
    HAL_MII_WRITE32(REG_RTL_MII_ETNRXCPU1, 0x1F0A0F00);

    RegValue = HAL_MII_READ32(REG_RTL_MII_RX_PSE1);
    HAL_MII_WRITE32(REG_RTL_MII_RX_PSE1, 0x00000022);

    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1);
    RegValue |= BIT_IOCMD1_FIRST_DMARX_ENABLE(1);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, RegValue);

    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD);
    RegValue |= BIT_IOCMD_RXENABLE(1);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue);
}


VOID
HalMiiGmacSetDefaultEthIoCmdRtl8195a(
        IN VOID *Data
        )
{
    u32 RegValue;


    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD, CMD_CONFIG);

    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1);
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, CMD1_CONFIG);

    //2014-04-29 yclin (disable 0x40051438[27] r_en_precise_dma) {
    RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1);
    RegValue = RegValue & 0xF7FFFFFF;
    HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, RegValue);
    // }
}


VOID
HalMiiGmacInitIrqRtl8195a(
		IN VOID *Data
		)
{
	IRQ_HANDLE	MiiIrqHandle_Master;
	PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data;


	MiiIrqHandle_Master.Data	 = (u32) (pMiiAdapter);
	MiiIrqHandle_Master.IrqNum	 = GMAC_IRQ;
	MiiIrqHandle_Master.IrqFun	 = (IRQ_FUN) MiiIrqHandle;
	MiiIrqHandle_Master.Priority = 10;
	InterruptRegister(&MiiIrqHandle_Master);
	InterruptEn(&MiiIrqHandle_Master);
}


u32
HalMiiGmacGetInterruptStatusRtl8195a(
        VOID
        )
{
    u32 RegValue;


    RegValue = HAL_MII_READ32(REG_RTL_MII_IMRISR);

    return RegValue;
}


VOID
HalMiiGmacClearInterruptStatusRtl8195a(
        u32 IsrStatus
        )
{
    HAL_MII_WRITE32(REG_RTL_MII_IMRISR, IsrStatus);
}
#endif  // #ifdef CONFIG_MII_VERIFY


#endif  // #ifdef CONFIG_MII_EN
