/** @file
  Rockchip PWM Driver.

  Copyright (c) 2022 Rockchip Electronics Co. Ltd.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/TimerLib.h>
#include <Library/PWMLib.h>

#define PWM_INT_EN(ch)                      (1 << (ch))
#define PWM_PWR_INT_EN(ch)                  (1 << ((ch) + 4 ))

#define PWM_DISABLE                         (0 << PWM_PWM0_CTRL_PWM_EN_SHIFT)
#define PWM_ENABLE                          (1 << PWM_PWM0_CTRL_PWM_EN_SHIFT)

#define PWM_MODE_SHIFT                      (1)
#define PWM_MODE_MASK                       (0x3U << PWM_MODE_SHIFT)

#define PWM_DUTY_POSTIVE                    (1 << PWM_PWM0_CTRL_DUTY_POL_SHIFT)
#define PWM_DUTY_NEGATIVE                   (0 << PWM_PWM0_CTRL_DUTY_POL_SHIFT)
#define PWM_DUTY_MASK                       (1 << 3)

#define PWM_INACTIVE_POSTIVE                (1 << PWM_PWM0_CTRL_INACTIVE_POL_SHIFT)
#define PWM_INACTIVE_NEGATIVE               (0 << PWM_PWM0_CTRL_INACTIVE_POL_SHIFT)
#define PWM_INACTIVE_MASK                   (1 << 4)

#define PWM_OUTPUT_LEFT                     (0 << PWM_PWM0_CTRL_OUTPUT_MODE_SHIFT)
#define PWM_OUTPUT_CENTER                   (1 << PWM_PWM0_CTRL_OUTPUT_MODE_SHIFT)

#define PWM_UNLOCK                          (0 << PWM_PWM0_CTRL_CONLOCK_SHIFT)
#define PWM_LOCK                            (1 << PWM_PWM0_CTRL_CONLOCK_SHIFT)

#define PWM_LP_DISABLE                      (0 << PWM_PWM0_CTRL_FORCE_CLK_EN_SHIFT)
#define PWM_LP_ENABLE                       (1 << PWM_PWM0_CTRL_FORCE_CLK_EN_SHIFT)

#define PWM_SEL_SRC_CLK                     (0 << PWM_PWM0_CTRL_CLK_SEL_SHIFT)
#define PWM_SEL_SCALE_CLK                   (1 << PWM_PWM0_CTRL_CLK_SEL_SHIFT)

#define PWM_CTRL_SCALE_SHIFT                (PWM_PWM0_CTRL_SCALE_SHIFT)
#define PWM_CTRL_SCALE_MASK                 (PWM_PWM0_CTRL_SCALE_MASK)

#define PWM_PWRMATCH_MAX_SHIFT              (PWM_PWRMATCH_LPRE_CNT_MIN_SHIFT)

#define PWM_CTRL_OFFSET                     (PWM_PWM0_CTRL_OFFSET)
#define PWM_PERIOD_HPR_OFFSET               (PWM_PWM0_PERIOD_HPR_OFFSET)
#define PWM_DUTY_LPR_OFFSET                 (PWM_PWM0_DUTY_LPR_OFFSET)

#define PWM_ONE_SHOT_MODE                   (0)
#define PWM_CONTINUOUS_MODE                 (1)
#define PWM_CAPTURE_MODE                    (2)

#define FREQUENCY_24M                       (24000000)

#define PWM_MAX_CONTROLLER_NUM              (4)

UINTN mPwmControllerBase[PWM_MAX_CONTROLLER_NUM] = {
  RK3588_PWM0_BASE,
  RK3588_PWM1_BASE,
  RK3588_PWM2_BASE,
  RK3588_PWM3_BASE
};

RETURN_STATUS
EFIAPI
RkPwmSetConfig (
  IN PWM_DATA                   *Data
  )
{
  UINTN Base = mPwmControllerBase[Data->ControllerID];
  UINT32 Ctrl;
  UINT32 PeriodCycle, DutyCycle;
  UINT64 ChannelOffset = Data->ChannelID * 0x10;

  PeriodCycle = ((UINT64)FREQUENCY_24M * Data->PeriodNs / 1000000000);
  DutyCycle = ((UINT64)FREQUENCY_24M * Data->DutyNs / 1000000000);

  Ctrl = MmioRead32(Base + ChannelOffset + PWM_CTRL_OFFSET);
  Ctrl |= PWM_LOCK;
  MmioWrite32(Base + ChannelOffset + PWM_CTRL_OFFSET, Ctrl);
  MicroSecondDelay (1);

  MmioWrite32(Base + ChannelOffset + PWM_PERIOD_HPR_OFFSET, PeriodCycle);
  MmioWrite32(Base + ChannelOffset + PWM_DUTY_LPR_OFFSET, DutyCycle);

  Ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);

  if (Data->Polarity) {
    Ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
  } else {
    Ctrl |= PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
  }

  Ctrl &= ~PWM_LOCK;
  MmioWrite32(Base + ChannelOffset + PWM_CTRL_OFFSET, Ctrl);

  return RETURN_SUCCESS;
}

RETURN_STATUS
EFIAPI
RkPwmEnable (
  IN PWM_DATA                   *Data
  )
{
  UINTN Base = mPwmControllerBase[Data->ControllerID];
  UINT32 Ctrl;
  UINT64 ChannelOffset = Data->ChannelID * 0x10;

  Ctrl = MmioRead32(Base + ChannelOffset + PWM_CTRL_OFFSET);
  Ctrl &= ~PWM_MODE_MASK;
	Ctrl |= (PWM_CONTINUOUS_MODE << PWM_MODE_SHIFT) | PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE;

  MmioWrite32(Base + ChannelOffset + PWM_CTRL_OFFSET, Ctrl);

  return RETURN_SUCCESS;
}

RETURN_STATUS
EFIAPI
RkPwmDisable (
  IN PWM_DATA                   *Data
  )
{
  UINTN Base = mPwmControllerBase[Data->ControllerID];
  UINT32 Ctrl;
  UINT64 ChannelOffset = Data->ChannelID * 0x10;

  Ctrl = MmioRead32(Base + ChannelOffset + PWM_CTRL_OFFSET);
  Ctrl &= ~PWM_ENABLE;

  MmioWrite32(Base + ChannelOffset + PWM_CTRL_OFFSET, Ctrl);

  return RETURN_SUCCESS;
}
