/*
 * Copyright 2019 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "usb_phydcd.h"
#include "usb_phydcd_config.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define USB_DCD_DATA_PIN_MAX_DETECTION_COUNT (100U)
/*time settting in ms*/
#define USB_DCD_DATA_PIN_DETECTION_TIME (10U)
#define USB_DCD_PRIMIARY_DETECTION_TIME (100U)
#define USB_DCD_SECONDARY_DETECTION_TIME (80U)
typedef enum _usb_phydcd_dev_status
{
    kUSB_DCDDetectInit = 0x0U,
    kUSB_DCDDetectIdle,
    kUSB_DCDDetectStart,
    kUSB_DCDDataContactDetection,
    kUSB_DCDPrimaryDetection,
    kUSB_DCDSecondaryDetection,
    kUSB_DCDDectionFinished,
} usb_phydcd_dev_status_t;

typedef struct _usb_phydcd_state_struct
{
    volatile uint64_t hwTick;           /*!< Current hw tick(ms)*/
    volatile uint64_t startTime;        /*!< start time for delay*/
    USB_ANALOG_Type *usbAnalogBase;     /*!< The base address of the dcd module */
    usb_phydcd_callback_t dcdCallback;  /*!< DCD callback function*/
    void *dcdCallbackParam;             /*!< DCD callback parameter*/
    void *phyBase;                      /*!< dcd phy base address, if no phy control needed, set to NULL*/
    uint8_t dcdDisable;                 /*!< whether enable dcd function or not*/
    uint8_t detectResult;               /*!< dcd detect result*/
    uint8_t index;                      /*!< analog instance index*/
    volatile uint8_t dataPinCheckTimes; /*!< the check times to make sure data pin is contacted*/
    volatile uint8_t dcdDetectState;    /*!< DCD callback parameter*/
} usb_phydcd_state_struct_t;
/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/* Apply for device dcd state structure */
static usb_phydcd_state_struct_t s_UsbDeviceDcdHSState[FSL_FEATURE_SOC_USBPHY_COUNT];
/*******************************************************************************
 * Code
 ******************************************************************************/

usb_phydcd_status_t USB_PHYDCD_Init(uint8_t index, usb_phydcd_config_struct_t *config, usb_phydcd_handle *dcdHandle)
{
    usb_phydcd_state_struct_t *dcdState;
    usb_phydcd_status_t dcdError = kStatus_phydcd_Success;
    uint32_t analog_base[]       = USB_ANALOG_BASE_ADDRS;
    USB_ANALOG_Type *base;
    uint32_t *temp;
    base               = (USB_ANALOG_Type *)analog_base[0];
    uint32_t phyBase[] = USBPHY_BASE_ADDRS;
    if ((NULL == config) || (NULL == base) || (NULL == config->dcdCallback))
    {
        return kStatus_phydcd_Error;
    }

    dcdState                   = &s_UsbDeviceDcdHSState[index];
    dcdState->index            = index;
    temp                       = (uint32_t *)phyBase[index + 1U];
    dcdState->phyBase          = (void *)temp;
    dcdState->usbAnalogBase    = base;
    dcdState->dcdCallbackParam = config->dcdCallbackParam;
    dcdState->dcdCallback      = config->dcdCallback;
    dcdState->dcdDisable       = 0U;
    dcdState->dcdDetectState   = (uint8_t)kUSB_DCDDetectInit;
    *dcdHandle                 = dcdState;
    return dcdError;
}
usb_phydcd_status_t USB_PHYDCD_Deinit(usb_phydcd_handle dcdHandle)
{
    usb_phydcd_state_struct_t *dcdState;
    dcdState                     = (usb_phydcd_state_struct_t *)dcdHandle;
    usb_phydcd_status_t dcdError = kStatus_phydcd_Success;

    dcdState->index            = 0U;
    dcdState->phyBase          = NULL;
    dcdState->usbAnalogBase    = NULL;
    dcdState->dcdCallbackParam = NULL;
    dcdState->dcdCallback      = NULL;
    dcdState->dcdDisable       = 0U;
    dcdState->dcdDetectState   = (uint8_t)kUSB_DCDDetectInit;

    return dcdError;
}
usb_phydcd_status_t USB_PHYDCD_Control(usb_phydcd_handle handle, usb_phydcd_control_t type, void *param)
{
    usb_phydcd_state_struct_t *dcdState;
    dcdState                     = (usb_phydcd_state_struct_t *)handle;
    usb_phydcd_status_t dcdError = kStatus_phydcd_Success;
    if (NULL == handle)
    {
        return kStatus_phydcd_Error;
    }
    switch (type)
    {
        case kUSB_DevicePHYDcdRun:
            if (0U == dcdState->dcdDisable)
            {
                dcdState->dcdDetectState = (uint8_t)kUSB_DCDDetectStart;
            }
            break;
        case kUSB_DevicePHYDcdStop:
            if (0U == dcdState->dcdDisable)
            {
                dcdState->dcdDetectState = (uint8_t)kUSB_DCDDetectInit;
            }
            break;
        case kUSB_DevicePHYDcdEnable:
            dcdState->dcdDisable = 0U;
            break;
        case kUSB_DevicePHYDcdDisable:
            dcdState->dcdDisable = 1U;
            break;
        default:
            /*no action*/
            break;
    }
    return dcdError;
}

usb_phydcd_status_t USB_PHYDCD_TimerIsrFunction(usb_phydcd_handle handle)
{
    usb_phydcd_status_t dcdError = kStatus_phydcd_Success;
    usb_phydcd_state_struct_t *dcdState;
    dcdState = (usb_phydcd_state_struct_t *)handle;
    USBPHY_Type *usbPhyBase;
    usb_phydcd_dev_status_t dcdStatus;
    if (NULL == handle)
    {
        return kStatus_phydcd_Error;
    }
    dcdState->hwTick++;

    dcdStatus = (usb_phydcd_dev_status_t)dcdState->dcdDetectState;
    switch (dcdStatus)
    {
        case kUSB_DCDDetectInit:
            break;
        case kUSB_DCDDetectIdle:
            break;
        case kUSB_DCDDetectStart:
            /*Enable the charger detector.*/
            dcdState->dcdDetectState    = (uint8_t)kUSB_DCDDataContactDetection;
            dcdState->detectResult      = (uint8_t)kUSB_DcdUnknownType;
            dcdState->dataPinCheckTimes = 0U;
            dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_CLR |= USB_ANALOG_CHRG_DETECT_CLR_EN_B_MASK;
            dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_SET |=
                USB_ANALOG_CHRG_DETECT_SET_CHK_CONTACT_MASK | USB_ANALOG_CHRG_DETECT_SET_CHK_CHRG_B_MASK;
            dcdState->startTime = dcdState->hwTick;
            break;
        case kUSB_DCDDataContactDetection:
            if (0U == ((dcdState->hwTick - dcdState->startTime) % USB_DCD_DATA_PIN_DETECTION_TIME))
            {
                if (0U != (dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_STAT &
                           USB_ANALOG_CHRG_DETECT_STAT_PLUG_CONTACT_MASK))
                {
                    dcdState->dataPinCheckTimes++;
                    if (dcdState->dataPinCheckTimes >= 5U)
                    {
                        dcdState->dcdDetectState = (uint8_t)kUSB_DCDPrimaryDetection;
                        dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_CLR |=
                            USB_ANALOG_CHRG_DETECT_CLR_CHK_CONTACT_MASK | USB_ANALOG_CHRG_DETECT_CLR_CHK_CHRG_B_MASK;
                        dcdState->startTime = dcdState->hwTick;
                    }
                }
                else
                {
                    dcdState->dataPinCheckTimes = 0U;
                }
            }

            if ((dcdState->hwTick - dcdState->startTime) >=
                USB_DCD_DATA_PIN_DETECTION_TIME * USB_DCD_DATA_PIN_MAX_DETECTION_COUNT)
            {
                if (((uint8_t)kUSB_DCDDataContactDetection) == dcdState->dcdDetectState)
                {
                    dcdState->dcdDetectState = (uint8_t)kUSB_DCDDetectInit;
                    dcdState->startTime      = 0U;
                    dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_SET |=
                        USB_ANALOG_CHRG_DETECT_SET_EN_B_MASK | USB_ANALOG_CHRG_DETECT_SET_CHK_CHRG_B_MASK;
                    dcdState->detectResult   = (uint8_t)kUSB_DcdError;
                    dcdState->dcdDetectState = (uint8_t)kUSB_DCDDectionFinished;
                }
            }
            break;
        case kUSB_DCDPrimaryDetection:
            if (dcdState->hwTick - dcdState->startTime >= USB_DCD_PRIMIARY_DETECTION_TIME)
            {
                if (0U == (dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_STAT &
                           USB_ANALOG_CHRG_DETECT_STAT_CHRG_DETECTED_MASK))
                {
                    dcdState->detectResult   = (uint8_t)kUSB_DcdSDP;
                    dcdState->dcdDetectState = (uint8_t)kUSB_DCDDectionFinished;
                }
                else
                {
                    dcdState->dcdDetectState = (uint8_t)kUSB_DCDSecondaryDetection;
                }
                dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_SET |=
                    USB_ANALOG_CHRG_DETECT_SET_EN_B_MASK | USB_ANALOG_CHRG_DETECT_SET_CHK_CHRG_B_MASK;
                if (((uint8_t)kUSB_DCDSecondaryDetection) == dcdState->dcdDetectState)
                {
                    usbPhyBase = (USBPHY_Type *)dcdState->phyBase;
                    usbPhyBase->DEBUG_CLR |= USBPHY_DEBUG_CLR_CLKGATE_MASK;
                    dcdState->usbAnalogBase->INSTANCE[dcdState->index].LOOPBACK_SET |=
                        USB_ANALOG_LOOPBACK_UTMI_TESTSTART_MASK;
                }
                dcdState->startTime = dcdState->hwTick;
            }
            break;
        case kUSB_DCDSecondaryDetection:
            if (dcdState->hwTick - dcdState->startTime >= USB_DCD_SECONDARY_DETECTION_TIME)
            {
                if (0U != (dcdState->usbAnalogBase->INSTANCE[dcdState->index].CHRG_DETECT_STAT &
                           USB_ANALOG_CHRG_DETECT_STAT_DM_STATE_MASK))
                {
                    dcdState->detectResult = (uint8_t)kUSB_DcdDCP;
                }
                else
                {
                    dcdState->detectResult = (uint8_t)kUSB_DcdCDP;
                }
                dcdState->usbAnalogBase->INSTANCE[dcdState->index].LOOPBACK_CLR |=
                    USB_ANALOG_LOOPBACK_UTMI_TESTSTART_MASK;
                usbPhyBase = (USBPHY_Type *)dcdState->phyBase;
                usbPhyBase->DEBUG_SET |= USBPHY_DEBUG_CLR_CLKGATE_MASK;
                dcdState->dcdDetectState = (uint8_t)kUSB_DCDDectionFinished;
            }
            break;
        case kUSB_DCDDectionFinished:
            dcdState->dcdDetectState = (uint8_t)kUSB_DCDDetectIdle;
            (void)dcdState->dcdCallback(dcdState->dcdCallbackParam, dcdState->detectResult,
                                        (void *)&dcdState->detectResult);
            break;
        default:
            /* no action*/
            break;
    }
    return dcdError;
}
