text: /*
   * Copyright (c) 2015, Freescale Semiconductor, Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   * o Redistributions of source code must retain the above copyright notice, this list
   *   of conditions and the following disclaimer.
   *
   * o Redistributions in binary form must reproduce the above copyright notice, this
   *   list of conditions and the following disclaimer in the documentation and/or
   *   other materials provided with the distribution.
   *
   * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
   *   contributors may be used to endorse or promote products derived from this
   *   software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "fsl_cmt.h"
  
  /*******************************************************************************
   * Definitions
   ******************************************************************************/
  
  /* The standard intermediate frequency (IF). */
  #define CMT_INTERMEDIATEFREQUENCY_8MHZ (8000000U)
  /* CMT data modulate mask. */
  #define CMT_MODULATE_COUNT_WIDTH (8U)
  /* CMT diver 1. */
  #define CMT_CMTDIV_ONE (1)
  /* CMT diver 2. */
  #define CMT_CMTDIV_TWO (2)
  /* CMT diver 4. */
  #define CMT_CMTDIV_FOUR (4)
  /* CMT diver 8. */
  #define CMT_CMTDIV_EIGHT (8)
  /* CMT mode bit mask. */
  #define CMT_MODE_BIT_MASK (CMT_MSC_MCGEN_MASK | CMT_MSC_FSK_MASK | CMT_MSC_BASE_MASK)
  
  /*******************************************************************************
   * Prototypes
   ******************************************************************************/
  
  /*!
   * @brief Get instance number for CMT module.
   *
   * @param base CMT peripheral base address.
   */
  static uint32_t CMT_GetInstance(CMT_Type *base);
  
  /*******************************************************************************
   * Variables
   ******************************************************************************/
  
  /*! @brief Pointers to cmt clocks for each instance. */
  static const clock_ip_name_t s_cmtClock[FSL_FEATURE_SOC_CMT_COUNT] = CMT_CLOCKS;
  
  /*! @brief Pointers to cmt bases for each instance. */
  static CMT_Type *const s_cmtBases[] = CMT_BASE_PTRS;
  
  /*! @brief Pointers to cmt IRQ number for each instance. */
  static const IRQn_Type s_cmtIrqs[] = CMT_IRQS;
  
  /*******************************************************************************
   * Codes
   ******************************************************************************/
  
  static uint32_t CMT_GetInstance(CMT_Type *base)
  {
      uint32_t instance;
  
      /* Find the instance index from base address mappings. */
      for (instance = 0; instance < FSL_FEATURE_SOC_CMT_COUNT; instance++)
      {
          if (s_cmtBases[instance] == base)
          {
              break;
          }
      }
  
      assert(instance < FSL_FEATURE_SOC_CMT_COUNT);
  
      return instance;
  }
  
  void CMT_GetDefaultConfig(cmt_config_t *config)
  {
      assert(config);
  
      /* Default infrared output is enabled and set with high active, the divider is set to 1. */
      config->isInterruptEnabled = false;
      config->isIroEnabled = true;
      config->iroPolarity = kCMT_IROActiveHigh;
      config->divider = kCMT_SecondClkDiv1;
  }
  
  void CMT_Init(CMT_Type *base, const cmt_config_t *config, uint32_t busClock_Hz)
  {
      assert(config);
      assert(busClock_Hz >= CMT_INTERMEDIATEFREQUENCY_8MHZ);
  
      uint8_t divider;
  
      /* Ungate clock. */
      CLOCK_EnableClock(s_cmtClock[CMT_GetInstance(base)]);
  
      /* Sets clock divider. The divider set in pps should be set
         to make sycClock_Hz/divder = 8MHz */
      base->PPS = CMT_PPS_PPSDIV(busClock_Hz / CMT_INTERMEDIATEFREQUENCY_8MHZ - 1);
      divider = base->MSC;
      divider &= ~CMT_MSC_CMTDIV_MASK;
      divider |= CMT_MSC_CMTDIV(config->divider);
      base->MSC = divider;
  
      /* Set the IRO signal. */
      base->OC = CMT_OC_CMTPOL(config->iroPolarity) | CMT_OC_IROPEN(config->isIroEnabled);
  
      /* Set interrupt. */
      if (config->isInterruptEnabled)
      {
          CMT_EnableInterrupts(base, kCMT_EndOfCycleInterruptEnable);
          EnableIRQ(s_cmtIrqs[CMT_GetInstance(base)]);
      }
  }
  
  void CMT_Deinit(CMT_Type *base)
  {
      /*Disable the CMT modulator. */
      base->MSC = 0;
  
      /* Disable the interrupt. */
      CMT_DisableInterrupts(base, kCMT_EndOfCycleInterruptEnable);
      DisableIRQ(s_cmtIrqs[CMT_GetInstance(base)]);
  
      /* Gate the clock. */
      CLOCK_DisableClock(s_cmtClock[CMT_GetInstance(base)]);
  }
  
  void CMT_SetMode(CMT_Type *base, cmt_mode_t mode, cmt_modulate_config_t *modulateConfig)
  {
      uint8_t mscReg;
  
      /* Set the mode. */
      if (mode != kCMT_DirectIROCtl)
      {
          assert(modulateConfig);
  
          /* Set carrier generator. */
          CMT_SetCarrirGenerateCountOne(base, modulateConfig->highCount1, modulateConfig->lowCount1);
          if (mode == kCMT_FSKMode)
          {
              CMT_SetCarrirGenerateCountTwo(base, modulateConfig->highCount2, modulateConfig->lowCount2);
          }
  
          /* Set carrier modulator. */
          CMT_SetModulateMarkSpace(base, modulateConfig->markCount, modulateConfig->spaceCount);
      }
  
      /* Set the CMT mode. */
      mscReg = base->MSC;
      mscReg &= ~CMT_MODE_BIT_MASK;
      mscReg |= mode;
  
      base->MSC = mscReg;
  }
  
  cmt_mode_t CMT_GetMode(CMT_Type *base)
  {
      uint8_t mode = base->MSC;
  
      if (!(mode & CMT_MSC_MCGEN_MASK))
      { /* Carrier modulator disabled and the IRO signal is in direct software control. */
          return kCMT_DirectIROCtl;
      }
      else
      {
          /* Carrier modulator is enabled. */
          if (mode & CMT_MSC_BASE_MASK)
          {
              /* Base band mode. */
              return kCMT_BasebandMode;
          }
          else if (mode & CMT_MSC_FSK_MASK)
          {
              /* FSK mode. */
              return kCMT_FSKMode;
          }
          else
          {
              /* Time mode. */
              return kCMT_TimeMode;
          }
      }
  }
  
  uint32_t CMT_GetCMTFrequency(CMT_Type *base, uint32_t busClock_Hz)
  {
      uint32_t frequency;
      uint32_t divider;
  
      /* Get intermediate frequency. */
      frequency = busClock_Hz / ((base->PPS & CMT_PPS_PPSDIV_MASK) + 1);
  
      /* Get the second divider. */
      divider = ((base->MSC & CMT_MSC_CMTDIV_MASK) >> CMT_MSC_CMTDIV_SHIFT);
      /* Get CMT frequency. */
      switch ((cmt_second_clkdiv_t)divider)
      {
          case kCMT_SecondClkDiv1:
              frequency = frequency / CMT_CMTDIV_ONE;
              break;
          case kCMT_SecondClkDiv2:
              frequency = frequency / CMT_CMTDIV_TWO;
              break;
          case kCMT_SecondClkDiv4:
              frequency = frequency / CMT_CMTDIV_FOUR;
              break;
          case kCMT_SecondClkDiv8:
              frequency = frequency / CMT_CMTDIV_EIGHT;
              break;
          default:
              frequency = frequency / CMT_CMTDIV_ONE;
              break;
      }
  
      return frequency;
  }
  
  void CMT_SetModulateMarkSpace(CMT_Type *base, uint32_t markCount, uint32_t spaceCount)
  {
      /* Set modulate mark. */
      base->CMD1 = (markCount >> CMT_MODULATE_COUNT_WIDTH) & CMT_CMD1_MB_MASK;
      base->CMD2 = (markCount & CMT_CMD2_MB_MASK);
      /* Set modulate space. */
      base->CMD3 = (spaceCount >> CMT_MODULATE_COUNT_WIDTH) & CMT_CMD3_SB_MASK;
      base->CMD4 = spaceCount & CMT_CMD4_SB_MASK;
  }
  
  void CMT_SetIroState(CMT_Type *base, cmt_infrared_output_state_t state)
  {
      uint8_t ocReg = base->OC;
  
      ocReg &= ~CMT_OC_IROL_MASK;
      ocReg |= CMT_OC_IROL(state);
  
      /* Set the infrared output signal control. */
      base->OC = ocReg;
  }
  