/**************************************************************************//**
 * @file     pwm.c
 * @brief    N9H30 series PWM driver source file
 *
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include "N9H30.h"
#include "sys.h"
#include "pwm.h"
/** @addtogroup N9H30_Device_Driver N9H30 Device Driver
  @{
*/

/** @addtogroup N9H30_PWM_Driver PWM Driver
  @{
*/


/** @addtogroup N9H30_PWM_EXPORTED_FUNCTIONS PWM Exported Functions
  @{
*/

//Internal function definition
/// @cond HIDDEN_SYMBOLS

void pwmISR(PVOID pvParam);


static INT pwmInitGPIO(const INT nTimerIdentity, const INT nValue);
static INT pwmInitTimer(const INT nTimerIdentity);
static INT pwmStartTimer(const INT nTimerIdentity);
static INT pwmStopTimer(const INT nTimerIdentity, const INT nMethod);
// Register operation
static INT pwmSetCP(const INT nTimerIdentity, const INT nValue);
static INT pwmSetDZI(const INT nTimerIdentity, const INT nValue);
static INT pwmSetCSR(const INT nTimerIdentity, const INT nValue);
static INT pwmSetDZGenerator(const INT nTimerIdentity, const INT nStatus);
static INT pwmSetTimerState(const INT nTimerIdentity, const INT nStatus);
static INT pwmSetInverter(const INT nTimerIdentity, const INT nStatus);
static INT pwmSetMode(const INT nTimerIdentity, const INT nStatus);
static INT pwmSetCNR(const INT nTimerIdentity, const INT nValue);
static INT pwmSetCMR(const INT nTimerIdentity, const INT nValue);
static UINT pwmGetPDR(const INT nTimerIdentity);
static INT pwmSetPIER(const INT nTimerIdentity, const INT value);
static INT pwmCleanPIIR(const INT nTimerIdentity);

//Global variable
static BOOL bPWMIRQFlag=FALSE;  //IRQ enable flag, set after PWM IRQ enable
static BOOL bPWMTimerOpenStatus[PWM_TIMER_NUM]; //timer flag which set after open(for disable IRQ decision)
static BOOL bPWMTimerStartStatus[PWM_TIMER_NUM]; //timer flag which set after Start count(to avoid incorrectly stop procedure)
static BOOL bPWMTimerMode[PWM_TIMER_NUM]; //PWM timer toggle/one shot mode
static BOOL volatile bPWMIntFlag[PWM_TIMER_NUM]; //interrupt flag which set by ISR
/// @endcond /* HIDDEN_SYMBOLS */


/**
  * @brief The init function of PWM device driver
  */
INT pwmInit(void)
{
    UINT temp;
    // Enable PWM clock
    temp = inpw(REG_CLK_PCLKEN1);
    temp = temp | 0x8000000;
    outpw(REG_CLK_PCLKEN1, temp);

    sysInstallISR(IRQ_LEVEL_1, PWM_IRQn, (PVOID)pwmISR);
    sysSetLocalInterrupt(ENABLE_IRQ);  // Enable CPSR I bit

    return 0;
}

/**
  * @brief The exit function of PWM device driver
  */
INT pwmExit(void)
{
    return 0;
}

/**
  * @brief The open function of PWM device driver
  * @param[in] nTimerIdentity PWM Timer channel identity
  * @retval Successful PWM successfully opened
  * @retval pwmTimerBusy PWM timer already open
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
INT pwmOpen(const INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == TRUE) {
        return pwmTimerBusy;
    }
    if(bPWMIRQFlag == FALSE) {

        sysEnableInterrupt(PWM_IRQn);

        bPWMIRQFlag=TRUE;
    }
    bPWMTimerOpenStatus[nTimerIdentity]=TRUE;

    // Set PWM timer default value(CSR->PPR->PCR->CMR->CNR)
    pwmInitTimer(nTimerIdentity);

    //Enable PIER
    pwmSetPIER(nTimerIdentity, PWM_ENABLE);

    //Reset PIIR
    pwmCleanPIIR(nTimerIdentity);

    //Reset PWM timer start count flag
    bPWMTimerStartStatus[nTimerIdentity] = FALSE;

    return Successful;

}

/**
  * @brief The close function of PWM device driver
  * @param[in] nTimerIdentity PWM Timer channel identity
  * @retval Successful PWM successfully closed
  * @retval pwmTimerNotOpen PWM timer not open
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
INT pwmClose(const INT nTimerIdentity)
{
    INT nLoop;
    BOOL uAllTimerClose=TRUE;
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX ) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE) {
        return pwmTimerNotOpen;
    }
    bPWMTimerOpenStatus[nTimerIdentity] = FALSE;
    //Check if all timer stop, IRQ can be disable
    for(nLoop=PWM_TIMER_MIN; nLoop<PWM_TIMER_NUM; nLoop++) {
        if(bPWMTimerOpenStatus[nLoop] == TRUE) {
            uAllTimerClose=FALSE;
        }
    }
    //All timer stop, disable IRQs
    if(uAllTimerClose == TRUE) {

        sysDisableInterrupt(PWM_IRQn);
        bPWMIRQFlag=FALSE;
    }

    pwmSetPIER(nTimerIdentity, PWM_DISABLE);
    pwmCleanPIIR(nTimerIdentity);


    return Successful;

}

/**
  * @brief The read function of PWM device driver
  * @param[in] nTimerIdentity PWM Timer channel identity
  * @param[out] pucStatusValue The point of typePWMSTATUS
  * @param[in] uLength The length of typePWMSTATUS
  * @retval Successful Read PWM value successfully
  * @retval pwmTimerNotOpen PWM timer not open
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidStructLength Struct length error(struct type error)
  */
INT pwmRead(const INT nTimerIdentity, PUCHAR pucStatusValue, const UINT uLength)
{
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX ) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE) {
        return pwmTimerNotOpen;
    }
    if(uLength != sizeof(typePWMSTATUS)) {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    if( sizeof(*((typePWMSTATUS *)pucStatusValue)) != sizeof(typePWMSTATUS) ) {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    ((typePWMSTATUS *)pucStatusValue)->PDR=pwmGetPDR(nTimerIdentity);
    if(bPWMIntFlag[nTimerIdentity] == TRUE) {
        bPWMIntFlag[nTimerIdentity]=FALSE;
        ((typePWMSTATUS *)pucStatusValue)->InterruptFlag=TRUE;
    } else {
        ((typePWMSTATUS *)pucStatusValue)->InterruptFlag=FALSE;
    }

    return Successful;

}

/**
  * @brief The write function of PWM device driver
  * @param[in] nTimerIdentity PWM Timer channel identity
  * @param[in] pucCNRCMRValue The value of CNR and CMR
  * @param[in] uLength For future usage
  * @retval Successful Write PWM setting successfully
  * @retval pwmTimerNotOpen PWM timer not open
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
INT pwmWrite(const INT nTimerIdentity, PUCHAR pucCNRCMRValue, const UINT uLength)
{
    typePWMVALUE pwmvalue;
    INT nStatus;
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX ) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE) {
        return pwmTimerNotOpen;
    }
    if(uLength != sizeof(typePWMVALUE)) {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    pwmvalue.value=((typePWMVALUE *)pucCNRCMRValue)->value;
    nStatus=pwmSetCNR(nTimerIdentity, pwmvalue.field.cnr);

    if(nStatus != Successful) {
        return nStatus;
    }
    nStatus=pwmSetCMR(nTimerIdentity, pwmvalue.field.cmr);

    if(nStatus != Successful) {
        return nStatus;
    }
    return Successful;

}

/**
  * @brief The ioctl function of PWM device driver
  * @param[in] nTimerIdentity PWM Timer channel identity
  * @param[in] uCommand Ioctl command which indicates different operation
  * @param[in] uIndication Not use in PWM
  * @param[in] uValue The value which use with uCommand
  * @retval Successful PWM ioctl execute successfully
  * @retval pwmTimerNotOpen PWM timer not open
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidIoctlCommand Ioctl command error
  * @retval Others Error according to different uCommand
  */
INT pwmIoctl(const INT nTimerIdentity, const UINT uCommand, const UINT uIndication, UINT uValue)
{
    INT nStatus;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE) {
        return pwmTimerNotOpen;
    }
    switch(uCommand) {
        case START_PWMTIMER: {
            nStatus=pwmStartTimer(nTimerIdentity);
            break;
        }
        case STOP_PWMTIMER: {
            // default stop method is 2
            nStatus=pwmStopTimer(nTimerIdentity, PWM_STOP_METHOD2);
            break;
        }
        case SET_CSR: {
            nStatus=pwmSetCSR(nTimerIdentity, uValue);
            break;
        }
        case SET_CP: {
            nStatus=pwmSetCP(nTimerIdentity, uValue);
            break;
        }
        case SET_DZI: {
            nStatus=pwmSetDZI(nTimerIdentity, uValue);
            break;
        }
        case SET_INVERTER: {
            nStatus=pwmSetInverter(nTimerIdentity, uValue);
            break;
        }
        case SET_MODE: {
            nStatus=pwmSetMode(nTimerIdentity, uValue);
            break;
        }
        case ENABLE_DZ_GENERATOR: {
            nStatus=pwmSetDZGenerator(nTimerIdentity, PWM_ENABLE);
            break;
        }
        case DISABLE_DZ_GENERATOR: {
            nStatus=pwmSetDZGenerator(nTimerIdentity, PWM_DISABLE);
            break;
        }
        case ENABLE_PWMGPIOOUTPUT: {
            nStatus=pwmInitGPIO(nTimerIdentity, uValue);
            break;
        }
        default: {
            return pwmInvalidIoctlCommand;
        }
    }
    return nStatus;
}


/// @cond HIDDEN_SYMBOLS

/**
  * @brief The interrupt service routines of PWM
  * @param[in] pvParam IRQ Parameter(not use in PWM)
  */
VOID pwmISR(PVOID pvParam)
{
    INT i;

    UINT32 uRegisterValue = 0;
    uRegisterValue = inpw(REG_PWM_PIIR);// Get PIIR value
    for(i = 0; i < PWM_TIMER_NUM ; i++) {
        if(uRegisterValue & (1 << i)) {
            bPWMIntFlag[i] = 1;
            outpw(REG_PWM_PIIR, (1 << i));
        }
    }
}

/**
  * @brief This function set corresponding GPIO as PWM function according to the
  *        parameter nTimerIdentity
  * @param[in] nTimerIdentity Timer channel number
  * @retval Successful PWM init GPIO successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidPin PWM output pin setting error
  */
static INT pwmInitGPIO(const INT nTimerIdentity, const INT nValue)
{
    UINT temp=0;

    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }

    if(nTimerIdentity == PWM_TIMER0) {
        if(nValue == PWM0_GPA12) {
            temp = inpw(REG_SYS_GPA_MFPH);
            temp = (temp & ~0x000F0000) | 0xD0000;
            outpw(REG_SYS_GPA_MFPH, temp);
        } else if(nValue == PWM0_GPB2) {
            temp = inpw(REG_SYS_GPB_MFPL);
            temp = (temp & ~0xF00) | 0xD00;
            outpw(REG_SYS_GPB_MFPL, temp);
        } else
            return pwmInvalidPin;
    } else if(nTimerIdentity == PWM_TIMER1) {
        if(nValue == PWM1_GPA13) {
            temp = inpw(REG_SYS_GPA_MFPH);
            temp = (temp & ~0x00F00000) | 0xD00000;
            outpw(REG_SYS_GPA_MFPH, temp);
        } else if(nValue == PWM1_GPB3) {
            temp = inpw(REG_SYS_GPB_MFPL);
            temp = (temp & ~0xF000) | 0xD000;
            outpw(REG_SYS_GPB_MFPL, temp);
        } else
            return pwmInvalidPin;
    } else if(nTimerIdentity == PWM_TIMER2) {
        if(nValue == PWM2_GPA14) {
            temp = inpw(REG_SYS_GPA_MFPH);
            temp = (temp & ~0x0F000000) | 0xD000000;
            outpw(REG_SYS_GPA_MFPH, temp);
        } else if(nValue == PWM2_GPH2) {
            temp = inpw(REG_SYS_GPH_MFPL);
            temp = (temp & ~0xF00) | 0xD00;
            outpw(REG_SYS_GPH_MFPL, temp);
        } else
            return pwmInvalidPin;
    } else {
        if(nValue == PWM3_GPA15) {
            temp = inpw(REG_SYS_GPA_MFPH);
            temp = (temp & ~0xF0000000) | 0xD0000000;
            outpw(REG_SYS_GPA_MFPH, temp);
        } else if(nValue == PWM3_GPH3) {
            temp = inpw(REG_SYS_GPH_MFPL);
            temp = (temp & ~0xF000) | 0xD000;
            outpw(REG_SYS_GPH_MFPL, temp);
        } else
            return pwmInvalidPin;
    }

    return Successful;
}


/**
  * @brief This function initiates PWM timer n and set the default setting to CSR,
  *        PPR, PCR, CNR, CMR
  * @param[in] nTimerIdentity Timer channel number
  * @retval Successful PWM init timer successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmInitTimer(const INT nTimerIdentity)
{
    typePPR PWMPPR;
    INT nStatus;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }

    //Set CSR
    nStatus=pwmSetCSR(nTimerIdentity, DEFAULT_CSR);

    if(nStatus != Successful) {
        return nStatus;
    }

    //Set PPR
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            if(PWMPPR.field.cp0 == 0) {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER1: {
            if(PWMPPR.field.cp0 == 0) {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER2: {
            if(PWMPPR.field.cp1 == 0) {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER3: {
            if(PWMPPR.field.cp1 == 0) {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
    }

    //Set PCR
    nStatus=pwmSetMode(nTimerIdentity, DEFAULT_MODE);

    if(nStatus != Successful) {
        return nStatus;
    }
    bPWMTimerMode[nTimerIdentity] = DEFAULT_MODE;

    //Set CMR
    nStatus=pwmSetCMR(nTimerIdentity, DEFAULT_CMR);

    if(nStatus != Successful) {
        return nStatus;
    }

    //Set CNR
    nStatus=pwmSetCNR(nTimerIdentity, DEFAULT_CNR);

    if(nStatus != Successful) {
        return nStatus;
    }

    return Successful;

}


/**
  * @brief This function starts PWM timer according to the parameter
  * @param[in] nTimerIdentity Timer channel number
  * @retval Successful PWM start timer successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmStartTimer(const INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    pwmSetTimerState(nTimerIdentity, PWM_ENABLE);
    if(bPWMTimerMode[nTimerIdentity] == PWM_TOGGLE) {
        bPWMTimerStartStatus[nTimerIdentity] = TRUE;
    }

    return Successful;
}

/**
  * @brief This function stops PWM timer n using method 1, 2, or 3 according to the
  *        parameter nTimerIdentity and nStatus
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nMethod Stop PWM timer method
  * @retval Successful PWM stop timer successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidStopMethod Stop method error
  */
static INT pwmStopTimer(const INT nTimerIdentity, INT nMethod)
{
    typeCNR PWMCNR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        // Timer_num value error
        return pwmInvalidTimerChannel;
    }
    //Can't stop before open PWM timer
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE) {
        return Successful;
    }
    // one shot mode didn't need stop procedure
    if(bPWMTimerMode[nTimerIdentity] == PWM_ONESHOT) {
        return Successful;
    }
    // Timer stop already, no need to stop again
    if(bPWMTimerStartStatus[nTimerIdentity] == FALSE) {
        return Successful;
    }

    // Set CNR as 0
    PWMCNR.field.cnr=0;
    outpw(REG_PWM_CNR0+(PWM_OFFSET*nTimerIdentity), PWMCNR.value);

    switch(nMethod) {
        case PWM_STOP_METHOD1: {
            while(1) {
                if(pwmGetPDR(nTimerIdentity) == 0) { // Wait PDR reach to 0
                    pwmSetTimerState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
                    bPWMIntFlag[nTimerIdentity]=FALSE;
                    bPWMTimerStartStatus[nTimerIdentity] = FALSE;
                    break;
                }
            }
            break;
        }
        case PWM_STOP_METHOD2: {
            while(1) {
                if(bPWMIntFlag[nTimerIdentity] == TRUE) { // Wait interrupt happen
                    pwmSetTimerState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
                    bPWMIntFlag[nTimerIdentity] = FALSE;
                    bPWMTimerStartStatus[nTimerIdentity] = FALSE;
                    break;
                }
            }
            break;
        }
        /*case PWM_STOP_METHOD3:
        {
            pwmSetPCRState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
            bPWMIntFlag[nTimerIdentity] = FALSE;
            bPWMTimerStartStatus[nTimerIdentity] = FALSE;
            break;
        }*/
        default: {
            return pwmInvalidStopMethod;// Stop method value error
        }
    }

    return Successful;
}

/**
  * @brief This function set CPn value according to the parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue The value which want to set in CSRn
  * @retval Successful Set CPn successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidCPValue PWM_PPR CPn value out of range
  */
static INT pwmSetCP(const INT nTimerIdentity, const INT nValue)
{
    typePPR PWMPPR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<CP_MIN || nValue>CP_MAX) {
        return pwmInvalidCPValue;// CP value error
    }
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPPR.field.cp0=nValue;
            break;
        }
        case PWM_TIMER1: {
            PWMPPR.field.cp0=nValue;
            break;
        }
        case PWM_TIMER2: {
            PWMPPR.field.cp1=nValue;
            break;
        }
        case PWM_TIMER3: {
            PWMPPR.field.cp1=nValue;
            break;
        }
    }
    outpw(REG_PWM_PPR, PWMPPR.value);

    return Successful;
}

/**
  * @brief This function set DZIn value according to the parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue The value which want to set in DZIn
  * @retval Successful Set DZIn successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidDZIValue PWM_PPR DZIn value out of range
  */
static INT pwmSetDZI(const INT nTimerIdentity, const INT nValue)
{
    typePPR PWMPPR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<DZI_MIN || nValue>DZI_MAX) {
        return pwmInvalidDZIValue;// CSR value error
    }
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPPR.field.dzi0=nValue;
            break;
        }
        case PWM_TIMER1: {
            PWMPPR.field.dzi0=nValue;
            break;
        }
        case PWM_TIMER2: {
            PWMPPR.field.dzi1=nValue;
            break;
        }
        case PWM_TIMER3: {
            PWMPPR.field.dzi1=nValue;
            break;
        }
    }
    outpw(REG_PWM_PPR, PWMPPR.value);

    return Successful;
}

/**
  * @brief This function set CSRn value according to the parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue The value which want to set in CSRn
  * @retval Successful Set CSRn successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmSetCSR(const INT nTimerIdentity, const INT nValue)
{
    typeCSR PWMCSR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<CSR_MIN || nValue>CSR_MAX) {
        return pwmInvalidCSRValue;// CSR value error
    }
    PWMCSR.value=(UINT)inpw(REG_PWM_CSR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMCSR.field.csr0=nValue;
            break;
        }
        case PWM_TIMER1: {
            PWMCSR.field.csr1=nValue;
            break;
        }
        case PWM_TIMER2: {
            PWMCSR.field.csr2=nValue;
            break;
        }
        case PWM_TIMER3: {
            PWMCSR.field.csr3=nValue;
            break;
        }
    }
    outpw(REG_PWM_CSR, PWMCSR.value);

    return Successful;
}

/**
  * @brief This function enable/disable PWM channel n dead zone function according to the
  *        parameter nTimerIdentity and nStatus
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nStatus PWMDZG_ENABLE/PWMDZG_DISABLE
  * @retval Successful Set dead zone successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidDZGStatus PWM Dead-Zone Generator enable/disable status error
  */
static INT pwmSetDZGenerator(const INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWMDZG_ENABLE && nStatus != PWMDZG_DISABLE) {
        return pwmInvalidDZGStatus;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPCR.field.grpup0_dzen=nStatus;
            break;
        }
        case PWM_TIMER1: {
            PWMPCR.field.grpup0_dzen=nStatus;
            break;
        }
        case PWM_TIMER2: {
            PWMPCR.field.grpup1_dzen=nStatus;
            break;
        }
        case PWM_TIMER3: {
            PWMPCR.field.grpup1_dzen=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);

    return Successful;
}

/**
  * @brief This function set PWM channel n enable/disable according to the
  *        parameter nTimerIdentity and nStatus
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nStatus PWM_ENABLE/PWMDISABLE
  * @retval Successful Set channel enable/disable successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmSetTimerState(const INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_ENABLE && nStatus != PWM_DISABLE) {
        return pwmInvalidTimerStatus;
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPCR.field.ch0_en=nStatus;
            break;
        }
        case PWM_TIMER1: {
            PWMPCR.field.ch1_en=nStatus;
            break;
        }
        case PWM_TIMER2: {
            PWMPCR.field.ch2_en=nStatus;
            break;
        }
        case PWM_TIMER3: {
            PWMPCR.field.ch3_en=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);

    return Successful;
}


/**
  * @brief This function set PWM channel n inverter on/off according to the
  *        parameter nTimerIdentity and nStatus
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nStatus PWM_ENABLE/PWM_DISABLE
  * @retval Successful Set inverter successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidInverterValue Inverter value error
  */
static INT pwmSetInverter(const INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_INVON && nStatus != PWM_INVOFF) {
        return pwmInvalidInverterValue;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPCR.field.ch0_inverter=nStatus;
            break;
        }
        case PWM_TIMER1: {
            PWMPCR.field.ch1_inverter=nStatus;
            break;
        }
        case PWM_TIMER2: {
            PWMPCR.field.ch2_inverter=nStatus;
            break;
        }
        case PWM_TIMER3: {
            PWMPCR.field.ch3_inverter=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);

    return Successful;
}

/**
  * @brief This function set PWM channel n toggle/one shot mode according to the
  *        parameter nTimerIdentity and nStatus
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nStatus PWM_TOGGLE/PWM_ONESHOT
  * @retval Successful Set operation mode successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidModeStatus Operating mode error
  */
static INT pwmSetMode(const INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_TOGGLE && nStatus != PWM_ONESHOT) {
        return pwmInvalidModeStatus;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity) {
        case PWM_TIMER0: {
            PWMPCR.field.ch0_mode=nStatus;
            break;
        }
        case PWM_TIMER1: {
            PWMPCR.field.ch1_mode=nStatus;
            break;
        }
        case PWM_TIMER2: {
            PWMPCR.field.ch2_mode=nStatus;
            break;
        }
        case PWM_TIMER3: {
            PWMPCR.field.ch3_mode=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);
    bPWMTimerMode[nTimerIdentity] = nStatus;

    return Successful;
}


/**
  * @brief This function set PWM_CNRn value according to the parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue CNR value
  * @retval Successful Set CNR successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidCNRValue Invalid CNR value
  */
static INT pwmSetCNR(const INT nTimerIdentity, INT nValue)
{
    typeCNR PWMCNR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue < CNR_MIN || nValue > CNR_MAX) {
        return pwmInvalidCNRValue;// PCR inverter value error
    }
    PWMCNR.field.cnr=nValue;
    outpw(REG_PWM_CNR0+(PWM_OFFSET*nTimerIdentity), PWMCNR.value);

    return Successful;
}

/**
  * @brief This function set PWM_CMRn value according to the parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue CMR value
  * @retval Successful Set CMR successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval pwmInvalidCMRValue Invalid CMR value
  */
static INT pwmSetCMR(const INT nTimerIdentity, INT nValue)
{
    typeCMR PWMCMR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue < CMR_MIN || nValue > CMR_MAX) {
        return pwmInvalidCMRValue;// CMR value error
    }
    PWMCMR.field.cmr=nValue;
    outpw(REG_PWM_CMR0+(PWM_OFFSET*nTimerIdentity), PWMCMR.value);

    return Successful;
}

/**
  * @brief This function return the PDR value of PWM timer n
  * @param[in] nTimerIdentity Timer channel number
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  * @retval Others Current PDR value
  */
static UINT pwmGetPDR(const INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    } else {
        return (UINT)inpw(REG_PWM_PDR0+(PWM_OFFSET*nTimerIdentity));// Return PDR value
    }
}

/**
  * @brief This function set the PIERn bit of PWM timer n as 1 or 0 according to the
  *        parameter nTimerIdentity and nValue
  * @param[in] nTimerIdentity Timer channel number
  * @param[in] nValue PWM_ENABLE/PWM_DISABLE
  * @retval Successful Set PIER successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmSetPIER(const INT nTimerIdentity, INT nValue)
{
    UINT uRegisterValue=0;;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// Timer_num value error
    } else {
        uRegisterValue=(UINT)inpw(REG_PWM_PIER);
        if(nValue==PWM_ENABLE) {
            uRegisterValue=uRegisterValue|(1<<nTimerIdentity);// Set PIER
        } else {
            uRegisterValue=uRegisterValue&(0<<nTimerIdentity);// Clear PIER
        }
        outpw(REG_PWM_PIER, uRegisterValue);// Write value to PIER

        return Successful;
    }
}


/**
  * @brief This function clear PIIRn bit according to the parameter nTimerIdentity
  * @param[in] nTimerIdentity Timer channel number
  * @retval Successful Clear PIIR successfully
  * @retval pwmInvalidTimerChannel PWM Timer channel number error
  */
static INT pwmCleanPIIR(const INT nTimerIdentity)
{
    UINT uRegisterValue=0;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX) {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    uRegisterValue=(UINT)inpw(REG_PWM_PIIR);
    uRegisterValue=uRegisterValue&~(1<<nTimerIdentity);
    outpw(REG_PWM_PIIR,uRegisterValue);

    return Successful;
}

/// @endcond /* HIDDEN_SYMBOLS */

/*@}*/ /* end of group N9H30_PWM_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group N9H30_PWM_Driver */

/*@}*/ /* end of group N9H30_Device_Driver */

/*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/
