blob: 7be7d4258814dc43ccdbf08aff9a2d127b9eefc5 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/max326xx/max32660/max32660_clockconfig.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <assert.h>
#include <debug.h>
#include <nuttx/irq.h>
#include "arm_internal.h"
#include "hardware/max326_gcr.h"
#include "hardware/max326_pwrseq.h"
#include "hardware/max326_flc.h"
#include "max326_periphclks.h"
#include "max326_clockconfig.h"
#include <arch/board/board.h>
/****************************************************************************
* Public Data
****************************************************************************/
/* This describes the initial clock configuration. It is needed by
* max326_start.c
*/
const struct clock_setup_s g_initial_clock_setup =
{
.ovr = 2, /* Output voltage range for internal regulator */
.clksrc = CLKSRC_HFIO, /* See enum clock_source_e. Determines Fsysosc */
.psc = 0, /* System Oscillator Prescaler. Derives Fsysclk */
.hfio = true, /* True: Enable the High frequency internal oscillator. */
#ifdef BOARD_HAVE_X32K
.x32k = true, /* True: Enable the 32.768KHz ext crystal oscillator. */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: max326_sysosc_frequency
*
* Description:
* Return the SYSOSC frequency. This is simply the frequency of the
* selected clock source.
*
****************************************************************************/
static uint32_t max326_sysosc_frequency(void)
{
uint32_t regval;
/* The clock source is in the GCR_CLKCTRL:CLKCTRL field */
regval = getreg32(MAX326_GCR_CLKCTRL);
switch ((regval & GCR_CLKCTRL_CLKSEL_MASK) >> GCR_CLKCTRL_CLKSEL_SHIFT)
{
default:
DEBUGPANIC();
case CLKSRC_HFIO:
return max326_hfio_frequency();
case CLKSRC_8KHZ:
return 8000000; /* Nominally 8MHz */
case CLKSRC_32KHZ:
return 32768;
}
}
/****************************************************************************
* Name: max326_sysclk_frequency
*
* Description:
* Return the SYSCLK frequency. The SYSCLOCK is simply divided down from
* the SYSOSC.
*
****************************************************************************/
static uint32_t max326_sysclk_frequency(void)
{
uint32_t regval;
uint32_t psc;
/* The divider is in the GCR_CLKCTRL:PSC setting:
*
* Fsysclk = Fsysclk / (2^psc)
*/
regval = getreg32(MAX326_GCR_CLKCTRL);
psc = (regval & GCR_CLKCTRL_PSC_MASK) >> GCR_CLKCTRL_PSC_SHIFT;
return max326_sysosc_frequency() >> psc;
}
/****************************************************************************
* Name: max326_enable_hfio
*
* Description:
* Enable High-Frequency Internal Oscillator (HFIO) if it is needed.
*
****************************************************************************/
static void max326_enable_hfio(const struct clock_setup_s *clksetup)
{
uint32_t regval;
/* Check if the HFIO is needed. */
if (clksetup->hfio)
{
/* Yes.. Enable the HFIO. */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval |= GCR_CLKCTRL_HIRCEN;
putreg32(regval, MAX326_GCR_CLKCTRL);
/* Wait for the oscillator to become ready */
while ((getreg32(MAX326_GCR_CLKCTRL) & GCR_CLKCTRL_HIRCRDY) == 0)
{
}
}
}
/****************************************************************************
* Name: max326_disable_hfio
*
* Description:
* Disable the High-Frequency Internal Oscillator (HFIO) if it is not used.
*
****************************************************************************/
static void max326_disable_hfio(const struct clock_setup_s *clksetup)
{
uint32_t regval;
/* Check if the HFIO is used. */
if (!clksetup->hfio)
{
/* No.. Disable the HFIO */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_HIRCEN;
putreg32(regval, MAX326_GCR_CLKCTRL);
}
}
/****************************************************************************
* Name: max326_select_hfio
*
* Description:
* Select the High-Frequency Internal Oscillator (HFIO) as the SYSOSC
* clock source.
*
****************************************************************************/
static void max326_select_hfio(void)
{
uint32_t regval;
/* Select the HIFO as the SYSOSC clock source. */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_CLKSEL_MASK;
regval |= GCR_CLKCTRL_CLKSEL_HIRC;
putreg32(regval, MAX326_GCR_CLKCTRL);
}
/****************************************************************************
* Name: max326_select_lirc8k
*
* Description:
* Select the 8kHz Internal Ultra-Low Power Nano-Ring Oscillator as the
* SYSOSC clock source.
*
****************************************************************************/
static void max326_select_lirc8k(void)
{
uint32_t regval;
/* The 8kHz nano-ring oscillator is "always-on": "This oscillator is
* enabled at device powerup by hardware and cannot be disabled by
* application firmware.
*/
/* Make sure that the oscillator is ready */
while ((getreg32(MAX326_GCR_CLKCTRL) & GCR_CLKCTRL_LIRC8KRDY) == 0)
{
}
/* Select the 8kHz nano-ring oscillator as the SYSOSC clock source. */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_CLKSEL_MASK;
regval |= GCR_CLKCTRL_CLKSEL_LIRC8K;
putreg32(regval, MAX326_GCR_CLKCTRL);
}
/****************************************************************************
* Name: max326_enable_x32k
*
* Description:
* Enable the 32.768kHz External Crystal Oscillator if is it needed.
*
****************************************************************************/
#ifdef BOARD_HAVE_X32K
static void max326_enable_x32k(const struct clock_setup_s *clksetup)
{
uint32_t regval;
/* Check if the 2.768kHz External Crystal Oscillator is needed. */
regval = getreg32(MAX326_GCR_CLKCTRL);
if (clksetup->x32k)
{
/* Yes.. Enable the 32.768kHz external oscillator. */
regval |= GCR_CLKCTRL_X32KEN;
putreg32(regval, MAX326_GCR_CLKCTRL);
/* Wait for the oscillator to become ready */
while ((getreg32(MAX326_GCR_CLKCTRL) & GCR_CLKCTRL_X32KRDY) == 0)
{
}
}
}
#endif
/****************************************************************************
* Name: max326_disable_x32k
*
* Description:
* Disable the 32.768 KHz crystal oscillator if it is not used
*
****************************************************************************/
#ifdef BOARD_HAVE_X32K
static void max326_disable_x32k(const struct clock_setup_s *clksetup)
{
uint32_t regval;
/* Check if the 32.768 KHz crystal oscillator is used */
if (!clksetup->x32k)
{
/* No.. Disable the 32.768kHz external oscillator. */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_X32KEN;
putreg32(regval, MAX326_GCR_CLKCTRL);
}
}
#endif
/****************************************************************************
* Name: max326_select_x32k
*
* Description:
* Select the 32.768kHz External Crystal Oscillator as the SYSOSC clock
* source.
*
****************************************************************************/
#ifdef BOARD_HAVE_X32K
static void max326_select_x32k(void)
{
uint32_t regval;
/* Select the 32.768kHz external oscillator as the SYSOSC clock
* source.
*/
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_CLKSEL_MASK;
regval |= GCR_CLKCTRL_CLKSEL_X32K;
putreg32(regval, MAX326_GCR_CLKCTRL);
}
#endif
/****************************************************************************
* Name: max326_set_ovr
*
* Description:
* Set the operating voltage range selection. If the OVR setting is
* different from the previous setting, then upon return, we will be
* running on either the 32.768kHz external oscillator or the 8kHz nano-
* ring oscillator with SYCLK prescaler == 0.
*
****************************************************************************/
static void max326_set_ovr(const struct clock_setup_s *clksetup)
{
uint32_t ovr;
uint32_t regval;
#ifndef BOARD_HAVE_X32K
DEBUGASSERT(clksetup->ovr != CLKSRC_32KHZ);
#endif
/* First check of the OVR setting is being changed */
regval = getreg32(MAX326_PWRSEQ_LPCTRL);
ovr = (regval & PWRSEQ_LPCTRL_OVR_MASK) >> PWRSEQ_LPCTRL_OVR_SHIFT;
if (ovr != clksetup->ovr)
{
/* Switch to the internal LDO for VCORE
*
* NOTE: "If using an external supply for VCORE, ensure the external
* supply is set to the same voltage as the current OVR setting. The
* external supply must be equal to or greater than the set OVR
* voltage."
*/
regval &= ~PWRSEQ_LPCTRL_LDODIS;
putreg32(regval, MAX326_PWRSEQ_LPCTRL);
/* Disable any SYSCLK divider */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_PSC_MASK;
putreg32(regval, MAX326_GCR_CLKCTRL);
#ifdef BOARD_HAVE_X32K
/* Select the 32.768kHz External Crystal Oscillator as the SYSOSC
* clock source (if it was enabled)
*/
if (clksetup->x32k)
{
max326_select_x32k();
}
else
#endif
{
/* Select the 8kHz Internal Ultra-Low Power Nano-Ring Oscillator
* as the SYSOSC clock source.
*/
max326_select_lirc8k();
}
/* Wait for SYSOSC to become ready */
while ((getreg32(MAX326_GCR_CLKCTRL) & GCR_CLKCTRL_CLKRDY) == 0)
{
}
/* Change the OVR setting to the desired range */
regval = getreg32(MAX326_PWRSEQ_LPCTRL);
regval &= ~PWRSEQ_LPCTRL_OVR_MASK;
regval |= PWRSEQ_LPCTRL_OVR(clksetup->ovr);
putreg32(regval, MAX326_PWRSEQ_LPCTRL);
}
/* Set the Flash Low Voltage Enable according to the OVR setting */
regval = getreg32(MAX326_FLC_CTRL);
if (clksetup->ovr < 2)
{
regval |= FLC_CTRL_LVE;
}
else
{
regval &= ~FLC_CTRL_LVE;
}
putreg32(regval, MAX326_FLC_CTRL);
}
/****************************************************************************
* Name: max326_set_clksrc
*
* Description:
* Select the requested clock source.
*
****************************************************************************/
static void max326_set_clksrc(const struct clock_setup_s *clksetup)
{
uint32_t regval;
uint32_t clksrc;
/* Set the system clock source if it is different from the currently
* selected clock source.
*/
regval = getreg32(MAX326_GCR_CLKCTRL);
clksrc = (regval & GCR_CLKCTRL_CLKSEL_MASK) >> GCR_CLKCTRL_CLKSEL_SHIFT;
if (clksrc != clksetup->clksrc)
{
/* Set the selected clock source */
switch (clksetup->clksrc)
{
case CLKSRC_HFIO: /* High frequency internal oscillator */
/* Select the High-Frequency Internal Oscillator (HFIO) as the
* SYSOSC clock source.
*/
DEBUGASSERT(clksetup->hfio);
max326_select_hfio();
break;
case CLKSRC_8KHZ: /* 8kHz Internal Ultra-Low Power Nano-Ring Oscillator */
/* Select the 8kHz Internal Ultra-Low Power Nano-Ring Oscillator
* as the SYSOSC clock source.
*/
max326_select_lirc8k();
break;
case CLKSRC_32KHZ: /* 32.768kHz External Crystal Oscillator */
#ifdef BOARD_HAVE_X32K
/* Select the 32.768kHz External Crystal Oscillator as the
* SYSOSC clock source.
*/
DEBUGASSERT(clksetup->x32k);
max326_select_x32k();
break;
#endif
default:
DEBUGPANIC();
return;
}
/* Wait for SYSOSC to become ready */
while ((getreg32(MAX326_GCR_CLKCTRL) & GCR_CLKCTRL_CLKRDY) == 0)
{
}
}
/* Set the SYSCLK prescaler */
regval = getreg32(MAX326_GCR_CLKCTRL);
regval &= ~GCR_CLKCTRL_PSC_MASK;
regval |= GCR_CLKCTRL_PSC(clksetup->psc);
putreg32(regval, MAX326_GCR_CLKCTRL);
}
/****************************************************************************
* Name: max326_set_fwsdefault
*
* Description:
* Set the the FLASH wait states to the default value (5)
*
****************************************************************************/
static void max326_set_fwsdefault(void)
{
uint32_t regval;
/* Set the number of Flash Wait States to the POR default value of 5. */
regval = getreg32(MAX326_GCR_MEMCTRL);
regval &= ~GCR_MEMCTRL_FWS_MASK;
regval |= GCR_MEMCTRL_FWS(5);
putreg32(regval, MAX326_GCR_MEMCTRL);
}
/****************************************************************************
* Name: max326_set_fws
*
* Description:
* Set an optimal value for the FLASH wait states.
*
****************************************************************************/
static void max326_set_fws(void)
{
uint32_t regval;
uint32_t sysclk;
uint32_t fws;
/* Get the SYSCLK frequency */
sysclk = max326_sysclk_frequency();
/* Set the FLASH states according to the SYSCLK frequency.
*
* This does not quite match the settings in Table 4.2 of the User Guide.
* that table has 1 or 2 wait states at 24MHz and 2 or 3 wait states at
* 48MH. Perhaps there is more to the wait state calculations than raw
* SYSCLK frequency?
*/
if (sysclk < 24000000)
{
fws = 1;
}
else if (sysclk < 48000000)
{
fws = 2;
}
else if (sysclk < 72000000)
{
fws = 3;
}
else if (sysclk <= 96000000)
{
fws = 4;
}
else
{
fws = 5;
}
regval = getreg32(MAX326_GCR_MEMCTRL);
regval &= ~GCR_MEMCTRL_FWS_MASK;
regval |= GCR_MEMCTRL_FWS(fws);
putreg32(regval, MAX326_GCR_MEMCTRL);
}
/****************************************************************************
* Name: max326_periph_reset
*
* Description:
* Set an optimal value for the FLASH wait states.
*
****************************************************************************/
static void max326_periph_reset(void)
{
uint32_t regval;
/* Initiate the peripheral reset */
regval = getreg32(MAX326_GCR_RST0);
regval |= GCR_RST0_PERIPH;
putreg32(regval, MAX326_GCR_RST0);
/* Wait for the peripheral reset to complete */
while ((getreg32(MAX326_GCR_RST0) & GCR_RST0_PERIPH) != 0)
{
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: max326_clockconfig
*
* Description:
* Called to initialize the MAX3266xx. This does whatever setup is needed
* to put the MCU in a usable state. This includes the initialization of
* clocking using the settings in board.h. This function also performs
* other low-level chip as necessary.
*
****************************************************************************/
void max326_clockconfig(const struct clock_setup_s *clksetup)
{
/* Set the the FLASH wait states to the default value (5) */
max326_set_fwsdefault();
#ifdef BOARD_HAVE_X32K
/* Enable the 32.768 KHz crystal oscillator if it is needed */
max326_enable_x32k(clksetup);
#endif
/* Enable the High frequency internal oscillator if it is needed */
max326_enable_hfio(clksetup);
/* Set the operating voltage range selection. If the OVR setting is
* different from the previous setting, then upon return, we will be
* running on either the 32.768kHz external oscillator or the 8kHz nano-
* ring oscillator with SYCLK prescaler == 0.
*/
max326_set_ovr(clksetup);
/* Select the requested clock source. */
max326_set_clksrc(clksetup);
#ifdef BOARD_HAVE_X32K
/* Disable the 32.768 KHz crystal oscillator if it is not used */
max326_disable_x32k(clksetup);
#endif
/* Disable the High frequency internal oscillator if it is not used. */
max326_disable_hfio(clksetup);
/* Set an optimal value for the FLASH wait states */
max326_set_fws();
/* Perform a peripheral reset */
max326_periph_reset();
/* Disable most clocks to peripherals by default to reduce power */
#ifndef CONFIG_MAX326XX_DMA
max326_dma_disableclk();
#endif
#if !defined(CONFIG_MAX326XX_SPIM0) && !defined(CONFIG_MAX326XX_SPIS0)
max326_spi0_disableclk();
#endif
#if !defined(CONFIG_MAX326XX_SPIM1) && !defined(CONFIG_MAX326XX_SPIS1)
max326_spi1_disableclk();
#endif
#ifndef CONFIG_MAX326XX_UART0
max326_uart0_disableclk();
#endif
#ifndef CONFIG_MAX326XX_UART1
max326_uart1_disableclk();
#endif
#if !defined(CONFIG_MAX326XX_I2CM0) && !defined(CONFIG_MAX326XX_I2CS0)
max326_i2c0_disableclk();
#endif
#if !defined(CONFIG_MAX326XX_I2CM1) && !defined(CONFIG_MAX326XX_I2CS1)
max326_i2c1_disableclk();
#endif
#ifndef CONFIG_MAX326XX_TMR32_0
max326_tmr0_disableclk();
#endif
#ifndef CONFIG_MAX326XX_TMR32_1
max326_tmr1_disableclk();
#endif
#ifndef CONFIG_MAX326XX_TMR32_0
max326_tmr2_disableclk();
#endif
}
/****************************************************************************
* Name: max326_hfio_frequency
*
* Description:
* Return the High-Frequency Internal Oscillator (HFIO) frequency.
*
****************************************************************************/
uint32_t max326_hfio_frequency(void)
{
uint32_t regval;
/* The HFIO frequency depends of the PWRSEQ_LP_CTRL:OVR setting */
regval = getreg32(MAX326_PWRSEQ_LPCTRL);
switch ((regval & PWRSEQ_LPCTRL_OVR_MASK) >> PWRSEQ_LPCTRL_OVR_SHIFT)
{
case 0:
return 24000000; /* Nominally 24MHz */
case 1:
return 48000000; /* Nominally 48MHz */
default:
DEBUGPANIC();
case 2:
return 96000000; /* Nominally 96MHz */
}
}
/****************************************************************************
* Name: max326_cpu_frequency
*
* Description:
* Return the current CPU frequency.
*
****************************************************************************/
uint32_t max326_cpu_frequency(void)
{
return max326_sysclk_frequency();
}
/****************************************************************************
* Name: max326_pclk_frequency
*
* Description:
* Return the current peripheral clock frequency.
*
****************************************************************************/
uint32_t max326_pclk_frequency(void)
{
/* Fpclk = Fsysclk / 2 */
return max326_sysclk_frequency() >> 1;
}