/*
 *  Routines to access hardware
 *
 *  Copyright (c) 2013 Realtek Semiconductor Corp.
 *
 *  This module is a confidential and proprietary property of RealTek and
 *  possession or use of this module requires written permission of RealTek.
 */


#include "rtl8195a.h"
#include "hal_peri_on.h"

#ifdef CONFIG_PWM_EN
#include "rtl8195a_pwm.h"
#include "hal_pwm.h"

extern HAL_PWM_ADAPTER PWMPin[];

extern HAL_TIMER_OP HalTimerOp;

/**
  * @brief  Configure a G-Timer to generate a tick with certain time.
  *
  * @param  pwm_id: the PWM pin index
  * @param  tick_time: the time (micro-second) of a tick
  *
  * @retval None
  */
void 
Pwm_SetTimerTick_8195a(
    HAL_PWM_ADAPTER *pPwmAdapt,
    u32 tick_time
)
{
    TIMER_ADAPTER TimerAdapter;


    if (tick_time <= MIN_GTIMER_TIMEOUT) {
        tick_time = MIN_GTIMER_TIMEOUT;
    }
    else {
        tick_time = (((tick_time-1)/TIMER_TICK_US)+1) * TIMER_TICK_US;
    }
    
    // Initial a G-Timer for the PWM pin
    if (pPwmAdapt->tick_time != tick_time) {
        TimerAdapter.IrqDis = 1;    // Disable Irq
        TimerAdapter.IrqHandle.IrqFun = (IRQ_FUN) NULL;
        TimerAdapter.IrqHandle.IrqNum = TIMER2_7_IRQ;
        TimerAdapter.IrqHandle.Priority = 0;
        TimerAdapter.IrqHandle.Data = (u32)NULL;
        TimerAdapter.TimerId = pPwmAdapt->gtimer_id;
        TimerAdapter.TimerIrqPriority = 0;
        TimerAdapter.TimerLoadValueUs = tick_time-1;
        TimerAdapter.TimerMode = 1; // auto-reload with user defined value

        HalTimerOp.HalTimerInit((VOID*) &TimerAdapter);
        pPwmAdapt->tick_time = tick_time;
        DBG_PWM_INFO("%s: Timer_Id=%d Count=%d\n", __FUNCTION__, pPwmAdapt->gtimer_id, tick_time);
     }

}


/**
  * @brief  Set the duty ratio of the PWM pin.
  *
  * @param  pwm_id: the PWM pin index
  * @param  period: the period time, in micro-second.
  * @param  pulse_width: the pulse width time, in micro-second.
  *
  * @retval None
  */
void
HAL_Pwm_SetDuty_8195a(
    HAL_PWM_ADAPTER *pPwmAdapt,
    u32 period,
    u32 pulse_width
)
{
    u32 RegAddr;
    u32 RegValue;
    u32 period_tick;
    u32 pulsewidth_tick;
    u32 tick_time;
    u8 timer_id;
    u8 pwm_id;

    pwm_id = pPwmAdapt->pwm_id;
    // Adjust the tick time to a proper value
    if (period < (MIN_GTIMER_TIMEOUT*2)) {
        DBG_PWM_ERR ("HAL_Pwm_SetDuty_8195a: Invalid PWM period(%d), too short!!\n", period);
        tick_time = MIN_GTIMER_TIMEOUT;
        period = MIN_GTIMER_TIMEOUT*2;
    }
    else {
        tick_time = period / 0x3fc;
        if (tick_time < MIN_GTIMER_TIMEOUT) {
            tick_time = MIN_GTIMER_TIMEOUT;
        }
    }

    Pwm_SetTimerTick_8195a(pPwmAdapt, tick_time);
    tick_time = pPwmAdapt->tick_time;
#if 0    
    // Check if current tick time needs adjustment
    if ((pPwmAdapt->tick_time << 12) <= period) {
        // need a longger tick time
    }
    else if ((pPwmAdapt->tick_time >> 2) >= period) {
        // need a shorter tick time
    }
#endif
    period_tick = period/tick_time;
    if (period_tick == 0) {
        period_tick = 1;
    }

    if (pulse_width >= period) {
//        pulse_width = period-1;
        pulse_width = period;
    }
    pulsewidth_tick = pulse_width/tick_time;
    if (pulsewidth_tick == 0) {
//        pulsewidth_tick = 1;
    }
    
    timer_id = pPwmAdapt->gtimer_id;

    pPwmAdapt->period = period_tick & 0x3ff;
    pPwmAdapt->pulsewidth = pulsewidth_tick & 0x3ff;
    
    RegAddr = REG_PERI_PWM0_CTRL + (pwm_id*4);
    RegValue = BIT31 | (timer_id<<24) | (pulsewidth_tick<<12) | period_tick;
    
    HAL_WRITE32(PERI_ON_BASE, RegAddr, RegValue);
}

/**
  * @brief  Initializes and enable a PWM control pin.
  *
  * @param  pwm_id: the PWM pin index
  * @param  sel: pin mux selection
  * @param  timer_id: the G-timer index assigned to this PWM
  *
  * @retval HAL_Status
  */
HAL_Status 
HAL_Pwm_Init_8195a(
    HAL_PWM_ADAPTER *pPwmAdapt
)
{
    u32 pwm_id;
    u32 pin_sel;

    pwm_id = pPwmAdapt->pwm_id;
    pin_sel =  pPwmAdapt->sel;
    // Initial a G-Timer for the PWM pin
    Pwm_SetTimerTick_8195a(pPwmAdapt, MIN_GTIMER_TIMEOUT);

    // Set default duty ration
    HAL_Pwm_SetDuty_8195a(pPwmAdapt, 20000, 10000);

    // Configure the Pin Mux
    PinCtrl((PWM0+pwm_id), pin_sel, 1);

    return HAL_OK;
}


/**
  * @brief  Enable a PWM control pin.
  *
  * @param  pwm_id: the PWM pin index
  *
  * @retval None
  */
void 
HAL_Pwm_Enable_8195a(
    HAL_PWM_ADAPTER *pPwmAdapt
)
{
    u32 pwm_id;
    
    pwm_id = pPwmAdapt->pwm_id;
    // Configure the Pin Mux
    if (!pPwmAdapt->enable) {
        PinCtrl((PWM0+pwm_id), pPwmAdapt->sel, 1);
        HalTimerOp.HalTimerEn(pPwmAdapt->gtimer_id);        
        pPwmAdapt->enable = 1;
    }
}


/**
  * @brief  Disable a PWM control pin.
  *
  * @param  pwm_id: the PWM pin index
  *
  * @retval None
  */
void 
HAL_Pwm_Disable_8195a(
    HAL_PWM_ADAPTER *pPwmAdapt
)
{
    u32 pwm_id;
    
    pwm_id = pPwmAdapt->pwm_id;
    // Configure the Pin Mux
    if (pPwmAdapt->enable) {
        PinCtrl((PWM0+pwm_id), pPwmAdapt->sel, 0);
        HalTimerOp.HalTimerDis(pPwmAdapt->gtimer_id);        
        pPwmAdapt->enable = 0;
    }
}

#endif  //CONFIG_PWM_EN
