
//------------------------------------------------------------------------------
// Copyright (c) 2012 by Silicon Laboratories. 
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User 
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------
// library
#include <si32McuComponent.h>
// hal
#include <si32_device.h>
#include <SI32_PLL_A_Type.h>
// application
#include "myApplication.h"
#include "myPLL0.h"

//------------------------------------------------------------------------------
void myPLL0_initialize_memory()
{
  // nothing to do.
}

//------------------------------------------------------------------------------
void PLL0_IRQHandler(void)
{
  // nothing to do.
}

//------------------------------------------------------------------------------
void myPLL0_enter_default_mode()
{
  // lock to 2.5Mhz boot-oscillator * 19.2 = 48Mhz
  SI32_PLL_A_initialize(SI32_PLL_0,
                        0x00000000,
                        0x00000000,
                        0x00000000,
                        0x000FFF0);
  SI32_PLL_A_set_numerator(SI32_PLL_0,
                           1920);
  SI32_PLL_A_set_denominator(SI32_PLL_0,
                             100);
  SI32_PLL_A_select_reference_clock_source_lp0osc(SI32_PLL_0);
  myPLL0_lock_freq_to_hz(48000000);
}

//------------------------------------------------------------------------------
void myPLL0_enter_active_mode()
{
  // nothing to do; default mode is sufficient.
}

// If a need arises for an off or stop mode, consider
// SI32_PLL_A_select_disable_dco_output(SI32_PLL_0);

//------------------------------------------------------------------------------
void myPLL0_lock_freq_to_hz(uint32 freq)
{
  uint8_t mode = 4;
  if (freq < 35000000)
  {
    mode = 0;
  }
  else if (freq < 45000000)
  {
    mode = 1;
  }
  else if (freq < 55000000)
  {
    mode = 2;
  }
  else if (freq < 70000000)
  {
    mode = 3;
  }

  // set range
  SI32_PLL_A_select_disable_dco_output(SI32_PLL_0);
  SI32_PLL_A_set_frequency_adjuster_value(SI32_PLL_0,
                                          0xFFF);
  SI32_PLL_A_set_output_frequency_range(SI32_PLL_0, mode);

  // lock and block waiting for result
  SI32_PLL_A_select_dco_frequency_lock_mode(SI32_PLL_0);
  while (!(SI32_PLL_A_is_locked(SI32_PLL_0)
        || SI32_PLL_A_is_saturation_low_interrupt_pending(SI32_PLL_0)
        || SI32_PLL_A_is_saturation_high_interrupt_pending(SI32_PLL_0)));
  if (!SI32_PLL_A_is_locked(SI32_PLL_0))
  {
    SI32_PLL_A_select_disable_dco_output(SI32_PLL_0);

    // adjust range
    if (SI32_PLL_A_is_saturation_low_interrupt_pending(SI32_PLL_0)
        && (mode < 4))
    {
      ++mode;
    }
    else if (mode > 0)
    {
      --mode;
    }
    SI32_PLL_A_set_output_frequency_range(SI32_PLL_0,
                                          mode);

    // lock and block waiting for result
    SI32_PLL_A_select_dco_frequency_lock_mode(SI32_PLL_0);
    while (!(SI32_PLL_A_is_locked(SI32_PLL_0)
          || SI32_PLL_A_is_saturation_low_interrupt_pending(SI32_PLL_0)
          || SI32_PLL_A_is_saturation_high_interrupt_pending(SI32_PLL_0)));
  }
  if (!SI32_PLL_A_is_locked(SI32_PLL_0))
  {
    si32SetError("pll failed to lock");
    si32Assert(NO);
  }
}

//------------------------------------------------------------------------------
void myPLL0_set_frequency(uint32 hz)
{
  // this needs to be incorporated into the larger context...
  si32Assert(NO);
#if 0
  // TBD only if AHB is PLL ... application should have already switched clocks
  // prior to setting frequency; only do this if app failed to do it.
  // switch to boot oscillator and release pll lock.
  SI32_CLKCTRL_A_select_ahb_source_low_power_oscillator(SI32_CLKCTRL_0);
  SI32_PLL_A_select_disable_dco_output(SI32_PLL_0);

  // TBD should be computed based on SystemCoreClock.
  // compute divider based on boot oscillator.
  uint32_t m = 4096 / ((float)hz / 2500000);
  uint32_t n = ((float)hz / 2500000) * m;
  SI32_PLL_A_set_numerator(SI32_PLL_0, n);
  SI32_PLL_A_set_denominator(SI32_PLL_0, m);

  // lock the pll.
  myPLL0_lock_freq_to_hz(hz);
#endif
}

//---eof------------------------------------------------------------------------
