text: /****************************************************************************
   * arch/avr/src/at32uc3/at32uc3_clkinit.c
   *
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.  The
   * ASF licenses this file to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance with the
   * License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
   * License for the specific language governing permissions and limitations
   * under the License.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  
  #include <arch/board/board.h>
  
  #include "up_arch.h"
  
  #include "at32uc3_config.h"
  #include "up_internal.h"
  #include "at32uc3.h"
  #include "at32uc3_pm.h"
  #include "at32uc3_flashc.h"
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  #if defined(AVR32_CLOCK_OSC0) || \
      (defined (AVR32_CLOCK_PLL0) && defined(AVR32_CLOCK_PLL0_OSC0)) || \
      (defined (AVR32_CLOCK_PLL1) && defined(AVR32_CLOCK_PLL1_OSC0))
  #  define NEED_OSC0
  #endif
  
  #if defined(AVR32_CLOCK_OSC1) || \
      (defined (AVR32_CLOCK_PLL0) && defined(AVR32_CLOCK_PLL0_OSC1)) || \
      (defined (AVR32_CLOCK_PLL1) && defined(AVR32_CLOCK_PLL1_OSC1))
  #  define NEED_OSC1
  #endif
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  
  /****************************************************************************
   * Public Data
   ****************************************************************************/
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: up_enableosc32
   *
   * Description:
   *   Initialize the 32KHz oscillaor.  This oscillaor is used by the RTC
   *   logic to provide the system timer.
   *
   ****************************************************************************/
  
  #ifdef AVR32_CLOCK_OSC32
  static inline void up_enableosc32(void)
  {
    uint32_t regval;
  
    /* Select the 32KHz oscillator crystal */
  
    regval = getreg32(AVR32_PM_OSCCTRL32);
    regval &= ~PM_OSCCTRL32_MODE_MASK;
    regval |= PM_OSCCTRL32_MODE_XTAL;
    putreg32(regval, AVR32_PM_OSCCTRL32);
  
    /* Enable the 32-kHz clock */
  
    regval = getreg32(AVR32_PM_OSCCTRL32);
    regval &= ~PM_OSCCTRL32_STARTUP_MASK;
    regval |= PM_OSCCTRL32_EN |
              (AVR32_OSC32STARTUP << PM_OSCCTRL32_STARTUP_SHIFT);
    putreg32(regval, AVR32_PM_OSCCTRL32);
  }
  #endif
  
  /****************************************************************************
   * Name: up_enableosc0
   *
   * Description:
   *   Initialize OSC0 settings per the definitions in the board.h file.
   *
   ****************************************************************************/
  
  #ifdef NEED_OSC0
  static inline void up_enableosc0(void)
  {
    uint32_t regval;
  
    /* Enable OSC0 in the correct crystal mode by setting the mode value in
     * OSCCTRL0
     */
  
    regval  = getreg32(AVR32_PM_OSCCTRL0);
    regval &= ~PM_OSCCTRL_MODE_MASK;
  #if AVR32_FOSC0 < 900000
    regval |= PM_OSCCTRL_MODE_XTALP9;  /* Crystal XIN 0.4-0.9MHz */
  #elif AVR32_FOSC0 < 3000000
    regval |= PM_OSCCTRL_MODE_XTAL3;   /* Crystal XIN 0.9-3.0MHz */
  #elif AVR32_FOSC0 < 8000000
    regval |= PM_OSCCTRL_MODE_XTAL8;   /* Crystal XIN 3.0-8.0MHz */
  #else
    regval |= PM_OSCCTRL_MODE_XTALHI;  /* Crystal XIN above 8.0MHz */
  #endif
    putreg32(regval, AVR32_PM_OSCCTRL0);
  
    /* Enable OSC0 using the startup time provided in board.h.  This startup
     * time is critical and depends on the characteristics of the crystal.
     */
  
    regval  = getreg32(AVR32_PM_OSCCTRL0);
    regval &= ~PM_OSCCTRL_STARTUP_MASK;
    regval |= (AVR32_OSC0STARTUP << PM_OSCCTRL_STARTUP_SHIFT);
    putreg32(regval, AVR32_PM_OSCCTRL0);
  
    /* Enable OSC0 */
  
    regval = getreg32(AVR32_PM_MCCTRL);
    regval |= PM_MCCTRL_OSC0EN;
    putreg32(regval, AVR32_PM_MCCTRL);
  
    /* Wait for OSC0 to be ready */
  
    while ((getreg32(AVR32_PM_POSCSR) & PM_POSCSR_OSC0RDY) == 0);
  }
  #endif
  
  /****************************************************************************
   * Name: up_enableosc1
   *
   * Description:
   *   Initialize OSC0 settings per the definitions in the board.h file.
   *
   ****************************************************************************/
  
  #ifdef NEED_OSC1
  static inline void up_enableosc1(void)
  {
    uint32_t regval;
  
    /* Enable OSC1 in the correct crystal mode by setting the mode value in
     * OSCCTRL1
     */
  
    regval  = getreg32(AVR32_PM_OSCCTRL1);
    regval &= ~PM_OSCCTRL_MODE_MASK;
  #if AVR32_FOSC1 < 900000
    regval |= PM_OSCCTRL_MODE_XTALP9;  /* Crystal XIN 0.4-0.9MHz */
  #elif AVR32_FOSC1 < 3000000
    regval |= PM_OSCCTRL_MODE_XTAL3;   /* Crystal XIN 0.9-3.0MHz */
  #elif AVR32_FOSC1 < 8000000
    regval |= PM_OSCCTRL_MODE_XTAL8;   /* Crystal XIN 3.0-8.0MHz */
  #else
    regval |= PM_OSCCTRL_MODE_XTALHI;  /* Crystal XIN above 8.0MHz */
  #endif
    putreg32(regval, AVR32_PM_OSCCTRL1);
  
    /* Enable OSC1 using the startup time provided in board.h.  This startup
     * time is critical and depends on the characteristics of the crystal.
     */
  
    regval  = getreg32(AVR32_PM_OSCCTRL1);
    regval &= ~PM_OSCCTRL_STARTUP_MASK;
    regval |= (AVR32_OSC1STARTUP << PM_OSCCTRL_STARTUP_SHIFT);
    putreg32(regval, AVR32_PM_OSCCTRL1);
  
    /* Enable OSC1 */
  
    regval = getreg32(AVR32_PM_MCCTRL);
    regval |= PM_MCCTRL_OSC1EN;
    putreg32(regval, AVR32_PM_MCCTRL);
  
    /* Wait for OSC1 to be ready */
  
    while ((getreg32(AVR32_PM_POSCSR) & PM_POSCSR_OSC1RDY) == 0);
  }
  #endif
  
  /****************************************************************************
   * Name: up_enablepll0
   *
   * Description:
   *   Initialize PLL0 settings per the definitions in the board.h file.
   *
   ****************************************************************************/
  
  #ifdef AVR32_CLOCK_PLL0
  static inline void up_enablepll0(void)
  {
    /* Setup PLL0 */
  
    regval = (AVR32_PLL0_DIV << PM_PLL_PLLDIV_SHIFT) |
             (AVR32_PLL0_MUL << PM_PLL_PLLMUL_SHIFT) |
             (16 << PM_PLL_PLLCOUNT_SHIFT)
  
    /* Select PLL0/1 oscillator */
  
  #ifdef AVR32_CLOCK_PLL0_OSC1
    regval |= PM_PLL_PLLOSC;
  #endif
  
    putreg32(regval, AVR32_PM_PLL0);
  
    /* Set PLL0 options */
  
    regval = getreg32(AVR32_PM_PLL0);
    regval &= ~PM_PLL_PLLOPT_MASK
  #if AVR32_PLL0_FREQ < 160000000
    regval |= PM_PLL_PLLOPT_VCO;
  #endif
  #if AVR32_PLL0_DIV2 != 0
    regval |= PM_PLL_PLLOPT_XTRADIV;
  #endif
  #if AVR32_PLL0_WBWM != 0
    regval |= PM_PLL_PLLOPT_WBWDIS;
  #endif
    putreg32(regval, AVR32_PM_PLL0)
  
    /* Enable PLL0 */
  
    regval = getreg32(AVR32_PM_PLL0);
    regval |= PM_PLL_PLLEN;
    putreg32(regval, AVR32_PM_PLL0)
  
    /* Wait for PLL0 locked. */
  
    while ((getreg32(AVR32_PM_POSCSR) & PM_POSCSR_LOCK0) == 0);
  }
  #endif
  
  /****************************************************************************
   * Name: up_enablepll1
   *
   * Description:
   *   Initialize PLL1 settings per the definitions in the board.h file.
   *
   ****************************************************************************/
  
  #ifdef AVR32_CLOCK_PLL1
  static inline void up_enablepll1(void)
  {
    /* Setup PLL1 */
  
    regval = (AVR32_PLL1_DIV << PM_PLL_PLLDIV_SHIFT) |
             (AVR32_PLL1_MUL << PM_PLL_PLLMUL_SHIFT) |
             (16 << PM_PLL_PLLCOUNT_SHIFT)
  
    /* Select PLL0/1 oscillator */
  
  #ifdef AVR32_CLOCK_PLL1_OSC1
    regval |= PM_PLL_PLLOSC;
  #endif
  
    putreg32(regval, AVR32_PM_PLL1);
  
    /* Set PLL1 options */
  
    regval = getreg32(AVR32_PM_PLL1);
    regval &= ~PM_PLL_PLLOPT_MASK
  #if AVR32_PLL1_FREQ < 160000000
    regval |= PM_PLL_PLLOPT_VCO;
  #endif
  #if AVR32_PLL1_DIV2 != 0
    regval |= PM_PLL_PLLOPT_XTRADIV;
  #endif
  #if AVR32_PLL1_WBWM != 0
    regval |= PM_PLL_PLLOPT_WBWDIS;
  #endif
    putreg32(regval, AVR32_PM_PLL1)
  
    /* Enable PLL1 */
  
    regval = getreg32(AVR32_PM_PLL1);
    regval |= PM_PLL_PLLEN;
    putreg32(regval, AVR32_PM_PLL1)
  
    /* Wait for PLL1 locked. */
  
    while ((getreg32(AVR32_PM_POSCSR) & PM_POSCSR_LOCK1) == 0);
  }
  #endif
  
  /****************************************************************************
   * Name: up_clksel
   *
   * Description:
   *   Configure derived clocks.
   *
   ****************************************************************************/
  
  static inline void up_clksel(void)
  {
    uint32_t regval = 0;
  
  #if AVR32_CKSEL_CPUDIV != 0
    regval |= PM_CKSEL_CPUDIV;
    regval |= (AVR32_CKSEL_CPUDIV << PM_CKSEL_CPUSEL_SHIFT)
  #endif
  
  #if AVR32_CKSEL_HSBDIV != 0
    regval |= PM_CKSEL_HSBDIV;
    regval |= (AVR32_CKSEL_HSBDIV << PM_CKSEL_HSBSEL_SHIFT)
  #endif
  
  #if AVR32_CKSEL_PBADIV != 0
    regval |= PM_CKSEL_PBADIV;
    regval |= (AVR32_CKSEL_PBADIV << PM_CKSEL_PBASEL_SHIFT)
  #endif
  
  #if AVR32_CKSEL_PBBDIV != 0
    regval |= PM_CKSEL_PBBDIV;
    regval |= (AVR32_CKSEL_PBBDIV << PM_CKSEL_PBBSEL_SHIFT)
  #endif
  
    putreg32(regval, AVR32_PM_CKSEL);
  
    /* Wait for CLKRDY */
  
    while ((getreg32(AVR32_PM_POSCSR) & PM_POSCSR_CKRDY) == 0);
  }
  
  /****************************************************************************
   * Name: up_fws
   *
   * Description:
   *   Setup FLASH wait states.
   *
   ****************************************************************************/
  
  static void up_fws(uint32_t cpuclock)
  {
    uint32_t regval;
  
    regval = getreg32(AVR32_FLASHC_FCR);
    if (cpuclock > AVR32_FLASHC_FWS0_MAXFREQ)
      {
        regval |= FLASHC_FCR_FWS;
      }
    else
      {
        regval &= ~FLASHC_FCR_FWS;
      }
  
    putreg32(regval, AVR32_FLASHC_FCR);
  }
  
  /****************************************************************************
   * Name: up_mainclk
   *
   * Description:
   *   Select the main clock.
   *
   ****************************************************************************/
  
  static inline void up_mainclk(uint32_t mcsel)
  {
    uint32_t regval;
  
    regval = getreg32(AVR32_PM_MCCTRL);
    regval &= ~PM_MCCTRL_MCSEL_MASK;
    regval |= mcsel;
    putreg32(regval, AVR32_PM_MCCTRL);
  }
  
  /****************************************************************************
   * Name: up_usbclock
   *
   * Description:
   *   Setup the USBB GCLK.
   *
   ****************************************************************************/
  
  #ifdef CONFIG_USBDEV
  static inline void up_usbclock(void)
  {
    uint32_t regval = 0;
  
  #if defined(AVR32_CLOCK_USB_PLL0) || defined(AVR32_CLOCK_USB_PLL1)
    regval |= PM_GCCTRL_PLLSEL;
  #endif
  #if defined(AVR32_CLOCK_USB_OSC1) || defined(AVR32_CLOCK_USB_PLL1)
    regval |= PM_GCCTRL_OSCSEL;
  #endif
  #if AVR32_CLOCK_USB_DIV > 0
  
    u_avr32_pm_gcctrl.GCCTRL.diven  = diven;
    u_avr32_pm_gcctrl.GCCTRL.div    = div;
  #endif
    putreg32(regval, AVR32_PM_GCCTRL(AVR32_PM_GCLK_USBB))
  
    /* Enable USB GCLK */
  
    regval = getreg32(AVR32_PM_GCCTRL(AVR32_PM_GCLK_USBB))
    regval |= PM_GCCTRL_CEN;
    putreg32(regval, AVR32_PM_GCCTRL(AVR32_PM_GCLK_USBB))
  }
  #endif
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: up_clkinit
   *
   * Description:
   *   Initialize clock/PLL settings per the definitions in the board.h
   *   file.
   *
   ****************************************************************************/
  
  void up_clkinitialize(void)
  {
  #ifdef AVR32_CLOCK_OSC32
    /* Enable the 32KHz oscillator (need by the RTC module) */
  
    up_enableosc32();
  #endif
  
  #ifdef NEED_OSC0
    /* Enable OSC0 using the settings in board.h */
  
    up_enableosc0();
  
    /* Set up FLASH wait states */
  
    up_fws(AVR32_FOSC0);
  
    /* Then switch the main clock to OSC0 */
  
    up_mainclk(PM_MCCTRL_MCSEL_OSC0);
  #endif
  
  #ifdef NEED_OSC1
    /* Enable OSC1 using the settings in board.h */
  
    up_enableosc1();
  #endif
  
  #ifdef AVR32_CLOCK_PLL0
    /* Enable PLL0 using the settings in board.h */
  
    up_enablepll0();
  
    /* Set up FLASH wait states */
  
    up_fws(AVR32_CPU_CLOCK);
  
    /* Then switch the main clock to PLL0 */
  
    up_mainclk(PM_MCCTRL_MCSEL_PLL0);
  #endif
  
  #ifdef AVR32_CLOCK_PLL1
    /* Enable PLL1 using the settings in board.h */
  
    up_enablepll1();
  #endif
  
    /* Configure derived clocks */
  
    up_clksel();
  
    /* Set up the USBB GCLK */
  
  #ifdef CONFIG_USBDEV
    void up_usbclock();
  #endif
  }
  