/* ======================================================================
 *   Copyright (C) 2022 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission
 *   of Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been
 *   supplied.
 * ==================================================================== */

/**
 *  \file   mdio.c
 *
 *  \brief  This file contains the device abstraction layer API implementation
 *          corresponding to Management Data Input Output (MDIO).
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */

#include "Std_Types.h"

/* There are static inline functions in hw_types.h file. Map them as well */
#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
#include "hw_types.h"
#define ETH_STOP_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

#include "Eth_Cfg.h"
#include "hw_cpsw_mdio.h"
#include "cpsw_mdio.h"
#include "Dem.h"
#include "Os.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                         Structures and Enums                               */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                 Internal Function Declarations                             */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */

#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

void MDIOInit(uint32 baseAddr,
              uint32 mdioInputFreq,
              uint32 mdioOutputFreq)
{
    uint32 clkDiv = 0U;
    uint32 regVal = 0U;
	uint32 regVal1 = 0U;
	uint32 regVal2 = 0U;
	uint32 regVal3 = 0U;
	uint32 regVal4 = 0U;
	uint32 pollEnableMask = 0xFFFFFFFF;

    if (0U != mdioOutputFreq)
    {
        clkDiv = (mdioInputFreq / mdioOutputFreq) - 1U;
		
    }

      HW_WR_REG32(baseAddr + MDIO_POLL_EN_REG, pollEnableMask | (1U << 31U));
	  
	  HW_SET_FIELD32(regVal, MDIO_LINK_INT_MASK_SET_REG_LINKINTMASKSET, 1U);
	  HW_WR_REG32(baseAddr + MDIO_LINK_INT_MASK_SET_REG, regVal);
	  
	  HW_SET_FIELD32(regVal1, MDIO_POLL_REG_STATECHANGEMODE, 1U);
	  HW_WR_REG32(baseAddr + MDIO_POLL_REG, regVal1);
	  
	  HW_SET_FIELD32(regVal1, MDIO_POLL_REG_IPG, 1U);
	  HW_WR_REG32(baseAddr + MDIO_POLL_REG, regVal1);
	  
	  HW_SET_FIELD32(regVal3, MDIO_CONTROL_REG_CLKDIV, clkDiv); 
	  HW_WR_REG32(baseAddr + MDIO_CONTROL_REG, regVal3);
	  HW_SET_FIELD32(regVal3, MDIO_CONTROL_REG_ENABLE, 1U);
	  HW_WR_REG32(baseAddr + MDIO_CONTROL_REG, regVal3);
	  HW_SET_FIELD32(regVal3, MDIO_CONTROL_REG_PREAMBLE, 1U);
	  HW_WR_REG32(baseAddr + MDIO_CONTROL_REG, regVal3);
    return;
}

void MDIOUsrIntrEnable(uint32 baseAddr)
{
    HW_WR_REG32((baseAddr + MDIO_USER_INT_MASK_SET_REG), 3U);
    return;
}

void MDIOUsrIntrClr(uint32 baseAddr)
{
    HW_WR_REG32((baseAddr + MDIO_USER_INT_MASKED_REG), 3U);
    return;
}

void MDIOPhyRegRead(uint32  baseAddr,
                    uint8   phyAddr,
                    uint32  regNum,
                    uint16 *pData)
{
    uint32     regVal = 0U, offset = 0U;
    StatusType status;
    TickType   startCount = 0U, tempCount, elaspsedCount = 0U;
	if(phyAddr != 0U)
	{
		offset = MDIO_USER_GROUP_USER_OFFSET;
	}
    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
        while (MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX ==
               (uint32) HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG
								+ offset, MDIO_USER_GROUP_USER_ACCESS_REG_GO))
        {
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_GO, 
	               MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_WRITE,
                 	MDIO_USER_GROUP_USER_ACCESS_REG_READ);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_PHYADR, phyAddr);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_REGADR, regNum);
    HW_WR_REG32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG + offset, regVal);

#if (STD_OFF == ETH_USR_MDIO_INTERRUPT)
    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
        while (MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX ==
               HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG + offset,
                             MDIO_USER_GROUP_USER_ACCESS_REG_GO))
        {
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
    /* Store the data if the read is acknowledged */
    if (MDIO_USER_GROUP_USER_ACCESS_REG_ACK_PASS ==
        HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG + offset,
                      MDIO_USER_GROUP_USER_ACCESS_REG_ACK))
    {
        *pData = (uint16) (HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG 
								+ offset, MDIO_USER_GROUP_USER_ACCESS_REG_DATA));
    }
#endif

    return;
}

void MDIOPhyRegWrite(uint32 baseAddr,
                     uint8  phyAddr,
                     uint32 regNum,
                     uint16 wrVal)
{
    uint32     regVal = 0U, offset = 0U;
    StatusType status;
    TickType   startCount = 0U, tempCount, elaspsedCount = 0U;
	if(phyAddr != 0U)
	{
		offset = MDIO_USER_GROUP_USER_OFFSET;
	}
    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
      
        while (MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX ==
               (uint32) HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG 
								+ offset, MDIO_USER_GROUP_USER_ACCESS_REG_GO))
        {
            /* Wait till transaction completion if any */
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_GO, 
	              MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_WRITE, 
	              MDIO_USER_GROUP_USER_ACCESS_REG_WRITE);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_PHYADR, phyAddr);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_REGADR, regNum);
    HW_SET_FIELD32(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_DATA, wrVal);
    HW_WR_REG32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG + offset, regVal);
#if (STD_OFF == ETH_USR_MDIO_INTERRUPT)
    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
        while (MDIO_USER_GROUP_USER_ACCESS_REG_GO_MAX ==
               HW_RD_FIELD32(baseAddr + MDIO_USER_GROUP_USER_ACCESS_REG + offset,
                             MDIO_USER_GROUP_USER_ACCESS_REG_GO))
        {
            /* wait for command completion */
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
#endif
    return;
}

uint32 MDIORegRead(uint32 baseAddr, uint32 regAddr)
{
    return (HW_RD_REG32(baseAddr + regAddr));
}

#define ETH_STOP_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
