blob: b1e170d3355a6563201908b539a0f849651aff9a [file] [log] [blame]
/**
* \file
*
* \brief Chip-specific system clock management functions
*
* Copyright (c) 2012-2013 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*
* \asf_license_stop
*
*/
#include <compiler.h>
#include <stdbool.h>
#include <sysclk.h>
#include <flashcalw.h>
#include <bpm.h>
#include <osc.h>
/**
* \weakgroup sysclk_group
* @{
*/
#if ((CONFIG_SYSCLK_CPU_DIV > CONFIG_SYSCLK_PBA_DIV) || \
(CONFIG_SYSCLK_CPU_DIV > CONFIG_SYSCLK_PBB_DIV) || \
(CONFIG_SYSCLK_CPU_DIV > CONFIG_SYSCLK_PBC_DIV) || \
(CONFIG_SYSCLK_CPU_DIV > CONFIG_SYSCLK_PBD_DIV))
# error CONFIG_SYSCLK_PBx_DIV must be equal to or more than CONFIG_SYSCLK_CPU_DIV.
#endif
/**
* \internal
* \defgroup sysclk_internals_group System Clock internals
*
* System clock management is fairly straightforward apart from one
* thing: Enabling and disabling bus bridges. When all peripherals on a
* given bus are disabled, the bridge to the bus may be disabled. Only
* the PBA and PBB busses support this, and it is not practical to
* disable the PBA bridge as it includes the PM and SCIF modules, so turning
* it off would make it impossible to turn anything back on again.
*
* The system clock implementation keeps track of a reference count for
* PBB. When the reference count is zero, the bus bridge is disabled, otherwise
* it is enabled.
*
* @{
*/
/**
* \internal
* \name Initial module clock masks
*
* These are the mask values written to the xxxMASK registers during
* initialization if the user has overridden the default behavior of all clocks
* left enabled. These values assume that:
* - Debugging should be possible
* - The program may be running from flash
* - The PM should be available to unmask other clocks
* - All on-chip RAM should be available
* - SCIF, BPM, BSCIF and GPIO are made permanently available for now; this
* may change in the future.
*/
//@{
//! \internal
//! \brief Initial value of CPUMASK
#define SYSCLK_INIT_MINIMAL_CPUMASK 0
//! \internal
//! \brief Initial value of HSBMASK
#define SYSCLK_INIT_MINIMAL_HSBMASK \
((1 << SYSCLK_HFLASHC_DATA) \
| (1 << SYSCLK_PBB_BRIDGE) \
| (1 << SYSCLK_PBC_BRIDGE) \
| (1 << SYSCLK_PBD_BRIDGE))
//! \internal
//! \brief Initial value of PBAMASK
#define SYSCLK_INIT_MINIMAL_PBAMASK 0
//! \internal
//! \brief Initial value of PBBMASK
#define SYSCLK_INIT_MINIMAL_PBBMASK (1 << SYSCLK_HFLASHC_REGS)
//! \internal
//! \brief Initial value of PBCMASK
#define SYSCLK_INIT_MINIMAL_PBCMASK \
((1 << SYSCLK_PM) \
| (1 << SYSCLK_GPIO) \
| (1 << SYSCLK_SCIF))
//! \internal
//! \brief Initial value of PBDMASK
#define SYSCLK_INIT_MINIMAL_PBDMASK \
((1 << SYSCLK_BPM) \
| (1 << SYSCLK_BSCIF))
//@}
#if defined(CONFIG_SYSCLK_DEFAULT_RETURNS_SLOW_OSC)
/**
* \brief boolean signalling that the sysclk_init is done.
*/
bool sysclk_initialized = false;
#endif
/**
* \internal
* \brief Enable a maskable module clock.
* \param bus_id Bus index, given by the \c PM_CLK_GRP_xxx definitions.
* \param module_index Index of the module to be enabled. This is the
* bit number in the corresponding xxxMASK register.
*/
void sysclk_priv_enable_module(uint32_t bus_id, uint32_t module_index)
{
irqflags_t flags;
uint32_t mask;
flags = cpu_irq_save();
/* Enable the clock */
mask = *(&PM->PM_CPUMASK + bus_id);
mask |= 1U << module_index;
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu) |
BPM_UNLOCK_ADDR(((uint32_t)&PM->PM_CPUMASK - (uint32_t)PM) + (4 * bus_id));
*(&PM->PM_CPUMASK + bus_id) = mask;
cpu_irq_restore(flags);
}
/**
* \internal
* \brief Disable a maskable module clock.
* \param bus_id Bus index, given by the \c PM_CLK_GRP_xxx definitions.
* \param module_index Index of the module to be disabled. This is the
* bit number in the corresponding xxxMASK register.
*/
void sysclk_priv_disable_module(uint32_t bus_id, uint32_t module_index)
{
irqflags_t flags;
uint32_t mask;
flags = cpu_irq_save();
/* Disable the clock */
mask = *(&PM->PM_CPUMASK + bus_id);
mask &= ~(1U << module_index);
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu) |
BPM_UNLOCK_ADDR(((uint32_t)&PM->PM_CPUMASK - (uint32_t)PM) + (4 * bus_id));
*(&PM->PM_CPUMASK + bus_id) = mask;
cpu_irq_restore(flags);
}
//! @}
/**
* \brief Enable a module clock derived from the PBA clock
* \param index Index of the module clock in the PBAMASK register
*/
void sysclk_enable_pba_module(uint32_t index)
{
irqflags_t flags;
/* Enable the bridge if necessary */
flags = cpu_irq_save();
if (PM->PM_PBAMASK == 0) {
sysclk_enable_hsb_module(SYSCLK_PBA_BRIDGE);
}
cpu_irq_restore(flags);
/* Enable the module */
sysclk_priv_enable_module(PM_CLK_GRP_PBA, index);
}
/**
* \brief Disable a module clock derived from the PBA clock
* \param index Index of the module clock in the PBAMASK register
*/
void sysclk_disable_pba_module(uint32_t index)
{
irqflags_t flags;
/* Disable the module */
sysclk_priv_disable_module(PM_CLK_GRP_PBA, index);
/* Disable the bridge if possible */
flags = cpu_irq_save();
if (PM->PM_PBAMASK == 0) {
sysclk_disable_hsb_module(SYSCLK_PBA_BRIDGE);
}
cpu_irq_restore(flags);
}
/**
* \brief Enable a module clock derived from the PBB clock
* \param index Index of the module clock in the PBBMASK register
*/
void sysclk_enable_pbb_module(uint32_t index)
{
irqflags_t flags;
/* Enable the bridge if necessary */
flags = cpu_irq_save();
if (PM->PM_PBBMASK == 0) {
sysclk_enable_hsb_module(SYSCLK_PBB_BRIDGE);
}
cpu_irq_restore(flags);
/* Enable the module */
sysclk_priv_enable_module(PM_CLK_GRP_PBB, index);
}
/**
* \brief Disable a module clock derived from the PBB clock
* \param index Index of the module clock in the PBBMASK register
*/
void sysclk_disable_pbb_module(uint32_t index)
{
irqflags_t flags;
/* Disable the module */
sysclk_priv_disable_module(PM_CLK_GRP_PBB, index);
/* Disable the bridge if possible */
flags = cpu_irq_save();
if (PM->PM_PBBMASK == 0) {
sysclk_disable_hsb_module(SYSCLK_PBB_BRIDGE);
}
cpu_irq_restore(flags);
}
/**
* \brief Retrieves the current rate in Hz of the Peripheral Bus clock attached
* to the specified peripheral.
*
* \param module Pointer to the module's base address.
*
* \return Frequency of the bus attached to the specified peripheral, in Hz.
*/
uint32_t sysclk_get_peripheral_bus_hz(const volatile void *module)
{
/* Fallthroughs intended for modules sharing the same peripheral bus. */
switch ((uintptr_t)module) {
case IISC_ADDR:
case SPI_ADDR:
case TC0_ADDR:
case TC1_ADDR:
case TWIM0_ADDR:
case TWIS0_ADDR:
case TWIM1_ADDR:
case TWIS1_ADDR:
case USART0_ADDR:
case USART1_ADDR:
case USART2_ADDR:
case USART3_ADDR:
case ADCIFE_ADDR:
case DACC_ADDR:
case ACIFC_ADDR:
case GLOC_ADDR:
case ABDACB_ADDR:
case TRNG_ADDR:
case PARC_ADDR:
case CATB_ADDR:
case TWIM2_ADDR:
case TWIM3_ADDR:
case LCDCA_ADDR:
return sysclk_get_pba_hz();
case HFLASHC_ADDR:
case HCACHE_ADDR:
case HMATRIX_ADDR:
case PDCA_ADDR:
case CRCCU_ADDR:
case USBC_ADDR:
case PEVC_ADDR:
return sysclk_get_pbb_hz();
case PM_ADDR:
case CHIPID_ADDR:
case SCIF_ADDR:
case FREQM_ADDR:
case GPIO_ADDR:
return sysclk_get_pbc_hz();
case BPM_ADDR:
case BSCIF_ADDR:
case AST_ADDR:
case WDT_ADDR:
case EIC_ADDR:
case PICOUART_ADDR:
return sysclk_get_pbd_hz();
default:
Assert(false);
return 0;
}
}
/**
* \brief Enable a peripheral's clock from its base address.
*
* Enables the clock to a peripheral, given its base address. If the peripheral
* has an associated clock on the HSB bus, this will be enabled also.
*
* \param module Pointer to the module's base address.
*/
void sysclk_enable_peripheral_clock(const volatile void *module)
{
switch ((uintptr_t)module) {
case AESA_ADDR:
sysclk_enable_hsb_module(SYSCLK_AESA_HSB);
break;
case IISC_ADDR:
sysclk_enable_pba_module(SYSCLK_IISC);
break;
case SPI_ADDR:
sysclk_enable_pba_module(SYSCLK_SPI);
break;
case TC0_ADDR:
sysclk_enable_pba_module(SYSCLK_TC0);
sysclk_enable_pba_divmask(PBA_DIVMASK_TIMER_CLOCK2
| PBA_DIVMASK_TIMER_CLOCK3
| PBA_DIVMASK_TIMER_CLOCK4
| PBA_DIVMASK_TIMER_CLOCK5
);
break;
case TC1_ADDR:
sysclk_enable_pba_module(SYSCLK_TC1);
sysclk_enable_pba_divmask(PBA_DIVMASK_TIMER_CLOCK2
| PBA_DIVMASK_TIMER_CLOCK3
| PBA_DIVMASK_TIMER_CLOCK4
| PBA_DIVMASK_TIMER_CLOCK5
);
break;
case TWIM0_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIM0);
break;
case TWIS0_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIS0);
break;
case TWIM1_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIM1);
break;
case TWIS1_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIS1);
break;
case USART0_ADDR:
sysclk_enable_pba_module(SYSCLK_USART0);
sysclk_enable_pba_divmask(PBA_DIVMASK_CLK_USART);
break;
case USART1_ADDR:
sysclk_enable_pba_module(SYSCLK_USART1);
sysclk_enable_pba_divmask(PBA_DIVMASK_CLK_USART);
break;
case USART2_ADDR:
sysclk_enable_pba_module(SYSCLK_USART2);
sysclk_enable_pba_divmask(PBA_DIVMASK_CLK_USART);
break;
case USART3_ADDR:
sysclk_enable_pba_module(SYSCLK_USART3);
sysclk_enable_pba_divmask(PBA_DIVMASK_CLK_USART);
break;
case ADCIFE_ADDR:
sysclk_enable_pba_module(SYSCLK_ADCIFE);
break;
case DACC_ADDR:
sysclk_enable_pba_module(SYSCLK_DACC);
break;
case ACIFC_ADDR:
sysclk_enable_pba_module(SYSCLK_ACIFC);
break;
case GLOC_ADDR:
sysclk_enable_pba_module(SYSCLK_GLOC);
break;
case ABDACB_ADDR:
sysclk_enable_pba_module(SYSCLK_ABDACB);
break;
case TRNG_ADDR:
sysclk_enable_pba_module(SYSCLK_TRNG);
break;
case PARC_ADDR:
sysclk_enable_pba_module(SYSCLK_PARC);
break;
case CATB_ADDR:
sysclk_enable_pba_module(SYSCLK_CATB);
break;
case TWIM2_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIM2);
break;
case TWIM3_ADDR:
sysclk_enable_pba_module(SYSCLK_TWIM3);
break;
case LCDCA_ADDR:
sysclk_enable_pba_module(SYSCLK_LCDCA);
break;
case HFLASHC_ADDR:
sysclk_enable_hsb_module(SYSCLK_HFLASHC_DATA);
sysclk_enable_pbb_module(SYSCLK_HFLASHC_REGS);
break;
case HCACHE_ADDR:
sysclk_enable_hsb_module(SYSCLK_HRAMC1_DATA);
sysclk_enable_pbb_module(SYSCLK_HRAMC1_REGS);
break;
case HMATRIX_ADDR:
sysclk_enable_pbb_module(SYSCLK_HMATRIX);
break;
case PDCA_ADDR:
sysclk_enable_hsb_module(SYSCLK_PDCA_HSB);
sysclk_enable_pbb_module(SYSCLK_PDCA_PB);
break;
case CRCCU_ADDR:
sysclk_enable_hsb_module(SYSCLK_CRCCU_DATA);
sysclk_enable_pbb_module(SYSCLK_CRCCU_REGS);
break;
case USBC_ADDR:
sysclk_enable_hsb_module(SYSCLK_USBC_DATA);
sysclk_enable_pbb_module(SYSCLK_USBC_REGS);
break;
case PEVC_ADDR:
sysclk_enable_pbb_module(SYSCLK_PEVC);
break;
case PM_ADDR:
sysclk_enable_pbc_module(SYSCLK_PM);
break;
case CHIPID_ADDR:
sysclk_enable_pbc_module(SYSCLK_CHIPID);
break;
case SCIF_ADDR:
sysclk_enable_pbc_module(SYSCLK_SCIF);
break;
case FREQM_ADDR:
sysclk_enable_pbc_module(SYSCLK_FREQM);
break;
case GPIO_ADDR:
sysclk_enable_pbc_module(SYSCLK_GPIO);
break;
case BPM_ADDR:
sysclk_enable_pbd_module(SYSCLK_BPM);
break;
case BSCIF_ADDR:
sysclk_enable_pbd_module(SYSCLK_BSCIF);
break;
case AST_ADDR:
sysclk_enable_pbd_module(SYSCLK_AST);
break;
case WDT_ADDR:
sysclk_enable_pbd_module(SYSCLK_WDT);
break;
case EIC_ADDR:
sysclk_enable_pbd_module(SYSCLK_EIC);
break;
case PICOUART_ADDR:
sysclk_enable_pbd_module(SYSCLK_PICOUART);
break;
default:
Assert(false);
return;
}
}
/**
* \brief Disable a peripheral's clock from its base address.
*
* Disables the clock to a peripheral, given its base address. If the peripheral
* has an associated clock on the HSB bus, this will be disabled also.
*
* \param module Pointer to the module's base address.
*/
void sysclk_disable_peripheral_clock(const volatile void *module)
{
switch ((uintptr_t)module) {
case AESA_ADDR:
sysclk_disable_hsb_module(SYSCLK_AESA_HSB);
break;
case IISC_ADDR:
sysclk_disable_pba_module(SYSCLK_IISC);
break;
case SPI_ADDR:
sysclk_disable_pba_module(SYSCLK_SPI);
break;
case TC0_ADDR:
sysclk_disable_pba_module(SYSCLK_TC0);
break;
case TC1_ADDR:
sysclk_disable_pba_module(SYSCLK_TC1);
break;
case TWIM0_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIM0);
break;
case TWIS0_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIS0);
break;
case TWIM1_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIM1);
break;
case TWIS1_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIS1);
break;
case USART0_ADDR:
sysclk_disable_pba_module(SYSCLK_USART0);
break;
case USART1_ADDR:
sysclk_disable_pba_module(SYSCLK_USART1);
break;
case USART2_ADDR:
sysclk_disable_pba_module(SYSCLK_USART2);
break;
case USART3_ADDR:
sysclk_disable_pba_module(SYSCLK_USART3);
break;
case ADCIFE_ADDR:
sysclk_disable_pba_module(SYSCLK_ADCIFE);
break;
case DACC_ADDR:
sysclk_disable_pba_module(SYSCLK_DACC);
break;
case ACIFC_ADDR:
sysclk_disable_pba_module(SYSCLK_ACIFC);
break;
case GLOC_ADDR:
sysclk_disable_pba_module(SYSCLK_GLOC);
break;
case ABDACB_ADDR:
sysclk_disable_pba_module(SYSCLK_ABDACB);
break;
case TRNG_ADDR:
sysclk_disable_pba_module(SYSCLK_TRNG);
break;
case PARC_ADDR:
sysclk_disable_pba_module(SYSCLK_PARC);
break;
case CATB_ADDR:
sysclk_disable_pba_module(SYSCLK_CATB);
break;
case TWIM2_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIM2);
break;
case TWIM3_ADDR:
sysclk_disable_pba_module(SYSCLK_TWIM3);
break;
case LCDCA_ADDR:
sysclk_disable_pba_module(SYSCLK_LCDCA);
break;
case HFLASHC_ADDR:
sysclk_disable_pbb_module(SYSCLK_HFLASHC_REGS);
break;
case HCACHE_ADDR:
sysclk_disable_hsb_module(SYSCLK_HRAMC1_DATA);
sysclk_disable_pbb_module(SYSCLK_HRAMC1_REGS);
break;
case HMATRIX_ADDR:
sysclk_disable_pbb_module(SYSCLK_HMATRIX);
break;
case PDCA_ADDR:
sysclk_disable_hsb_module(SYSCLK_PDCA_HSB);
sysclk_disable_pbb_module(SYSCLK_PDCA_PB);
break;
case CRCCU_ADDR:
sysclk_disable_hsb_module(SYSCLK_CRCCU_DATA);
sysclk_disable_pbb_module(SYSCLK_CRCCU_REGS);
break;
case USBC_ADDR:
sysclk_disable_hsb_module(SYSCLK_USBC_DATA);
sysclk_disable_pbb_module(SYSCLK_USBC_REGS);
break;
case PEVC_ADDR:
sysclk_disable_pbb_module(SYSCLK_PEVC);
break;
case PM_ADDR:
sysclk_disable_pbc_module(SYSCLK_PM);
break;
case CHIPID_ADDR:
sysclk_disable_pbc_module(SYSCLK_CHIPID);
break;
case SCIF_ADDR:
sysclk_disable_pbc_module(SYSCLK_SCIF);
break;
case FREQM_ADDR:
sysclk_disable_pbc_module(SYSCLK_FREQM);
break;
case GPIO_ADDR:
sysclk_disable_pbc_module(SYSCLK_GPIO);
break;
case BPM_ADDR:
sysclk_disable_pbd_module(SYSCLK_BPM);
break;
case BSCIF_ADDR:
sysclk_disable_pbd_module(SYSCLK_BSCIF);
break;
case AST_ADDR:
sysclk_disable_pbd_module(SYSCLK_AST);
break;
case WDT_ADDR:
sysclk_disable_pbd_module(SYSCLK_WDT);
break;
case EIC_ADDR:
sysclk_disable_pbd_module(SYSCLK_EIC);
break;
case PICOUART_ADDR:
sysclk_disable_pbd_module(SYSCLK_PICOUART);
break;
default:
Assert(false);
return;
}
// Disable PBA divided clock if possible.
#define PBADIV_CLKSRC_MASK (SYSCLK_TC0 | SYSCLK_TC1 \
| SYSCLK_USART0 | SYSCLK_USART1 | SYSCLK_USART2 | SYSCLK_USART3)
if ((PM->PM_PBAMASK & PBADIV_CLKSRC_MASK) == 0) {
sysclk_disable_pba_divmask(PBA_DIVMASK_Msk);
}
}
/**
* \brief Set system clock prescaler configuration
*
* This function will change the system clock prescaler configuration to
* match the parameters.
*
* \note The parameters to this function are device-specific.
*
* \param cpu_shift The CPU clock will be divided by \f$2^{cpu\_shift}\f$
* \param pba_shift The PBA clock will be divided by \f$2^{pba\_shift}\f$
* \param pbb_shift The PBB clock will be divided by \f$2^{pbb\_shift}\f$
* \param pbc_shift The PBC clock will be divided by \f$2^{pbc\_shift}\f$
* \param pbd_shift The PBD clock will be divided by \f$2^{pbd\_shift}\f$
*/
void sysclk_set_prescalers(uint32_t cpu_shift,
uint32_t pba_shift, uint32_t pbb_shift,
uint32_t pbc_shift, uint32_t pbd_shift)
{
irqflags_t flags;
uint32_t cpu_cksel = 0;
uint32_t pba_cksel = 0;
uint32_t pbb_cksel = 0;
uint32_t pbc_cksel = 0;
uint32_t pbd_cksel = 0;
Assert(cpu_shift <= pba_shift);
Assert(cpu_shift <= pbb_shift);
Assert(cpu_shift <= pbc_shift);
Assert(cpu_shift <= pbd_shift);
if (cpu_shift > 0) {
cpu_cksel = (PM_CPUSEL_CPUSEL(cpu_shift - 1))
| PM_CPUSEL_CPUDIV;
}
if (pba_shift > 0) {
pba_cksel = (PM_PBASEL_PBSEL(pba_shift - 1))
| PM_PBASEL_PBDIV;
}
if (pbb_shift > 0) {
pbb_cksel = (PM_PBBSEL_PBSEL(pbb_shift - 1))
| PM_PBBSEL_PBDIV;
}
if (pbc_shift > 0) {
pbc_cksel = (PM_PBCSEL_PBSEL(pbc_shift - 1))
| PM_PBCSEL_PBDIV;
}
if (pbd_shift > 0) {
pbd_cksel = (PM_PBDSEL_PBSEL(pbd_shift - 1))
| PM_PBDSEL_PBDIV;
}
flags = cpu_irq_save();
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_CPUSEL - (uint32_t)PM);
PM->PM_CPUSEL = cpu_cksel;
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBASEL - (uint32_t)PM);
PM->PM_PBASEL = pba_cksel;
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBBSEL - (uint32_t)PM);
PM->PM_PBBSEL = pbb_cksel;
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBCSEL - (uint32_t)PM);
PM->PM_PBCSEL = pbc_cksel;
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBDSEL - (uint32_t)PM);
PM->PM_PBDSEL = pbd_cksel;
cpu_irq_restore(flags);
}
/**
* \brief Change the source of the main system clock.
*
* \param src The new system clock source. Must be one of the constants
* from the <em>System Clock Sources</em> section.
*/
void sysclk_set_source(uint32_t src)
{
irqflags_t flags;
Assert(src <= SYSCLK_SRC_RC1M);
flags = cpu_irq_save();
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_MCCTRL - (uint32_t)PM);
PM->PM_MCCTRL = src;
cpu_irq_restore(flags);
}
#if defined(CONFIG_USBCLK_SOURCE) || defined(__DOXYGEN__)
/**
* \brief Enable the USB generic clock
*
* \pre The USB generic clock must be configured to 48MHz.
* CONFIG_USBCLK_SOURCE and CONFIG_USBCLK_DIV must be defined with proper
* configuration. The selected clock source must also be configured.
*/
void sysclk_enable_usb(void)
{
// Note: the SYSCLK_PBB_BRIDGE clock is enabled by
// sysclk_enable_pbb_module().
sysclk_enable_pbb_module(SYSCLK_USBC_REGS);
sysclk_enable_hsb_module(SYSCLK_USBC_DATA);
genclk_enable_config(7, CONFIG_USBCLK_SOURCE, CONFIG_USBCLK_DIV);
}
/**
* \brief Disable the USB generic clock
*/
void sysclk_disable_usb(void)
{
genclk_disable(7);
}
#endif // CONFIG_USBCLK_SOURCE
void sysclk_init(void)
{
uint32_t ps_value = 0;
bool is_fwu_enabled = false;
#if CONFIG_HCACHE_ENABLE == 1
/* Enable HCACHE */
sysclk_enable_peripheral_clock(HCACHE);
HCACHE->HCACHE_CTRL = HCACHE_CTRL_CEN_YES;
while (!(HCACHE->HCACHE_SR & HCACHE_SR_CSTS_EN));
#endif
/* Set up system clock dividers if different from defaults */
if ((CONFIG_SYSCLK_CPU_DIV > 0) || (CONFIG_SYSCLK_PBA_DIV > 0) ||
(CONFIG_SYSCLK_PBB_DIV > 0) || (CONFIG_SYSCLK_PBC_DIV > 0) ||
(CONFIG_SYSCLK_PBD_DIV > 0)) {
sysclk_set_prescalers(CONFIG_SYSCLK_CPU_DIV,
CONFIG_SYSCLK_PBA_DIV,
CONFIG_SYSCLK_PBB_DIV,
CONFIG_SYSCLK_PBC_DIV,
CONFIG_SYSCLK_PBD_DIV
);
}
/* Automatically select best power scaling mode */
#ifdef CONFIG_FLASH_READ_MODE_HIGH_SPEED_ENABLE
ps_value = BPM_PS_2;
is_fwu_enabled = false;
#else
if (sysclk_get_cpu_hz() <= FLASH_FREQ_PS1_FWS_1_MAX_FREQ) {
ps_value = BPM_PS_1;
if (sysclk_get_cpu_hz() > FLASH_FREQ_PS1_FWS_0_MAX_FREQ) {
bpm_enable_fast_wakeup(BPM);
is_fwu_enabled = true;
}
} else {
ps_value = BPM_PS_0;
}
#endif
/* Switch to system clock selected by user */
if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RCSYS) {
/* Already running from RCSYS */
}
#ifdef BOARD_OSC0_HZ
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_OSC0) {
osc_enable(OSC_ID_OSC0);
osc_wait_ready(OSC_ID_OSC0);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_OSC0);
}
#endif
#ifdef CONFIG_DFLL0_SOURCE
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_DFLL) {
dfll_enable_config_defaults(0);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_DFLL);
}
#endif
#ifdef CONFIG_PLL0_SOURCE
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_PLL0) {
pll_enable_config_defaults(0);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_PLL0);
}
#endif
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RC80M) {
osc_enable(OSC_ID_RC80M);
osc_wait_ready(OSC_ID_RC80M);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_RC80M);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RCFAST) {
osc_enable(OSC_ID_RCFAST);
osc_wait_ready(OSC_ID_RCFAST);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_RCFAST);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RC1M) {
osc_enable(OSC_ID_RC1M);
osc_wait_ready(OSC_ID_RC1M);
// Set a flash wait state depending on the new cpu frequency.
flash_set_bus_freq(sysclk_get_cpu_hz(), ps_value, is_fwu_enabled);
sysclk_set_source(SYSCLK_SRC_RC1M);
}
else {
Assert(false);
}
/* Automatically switch to low power mode */
bpm_configure_power_scaling(BPM, ps_value, BPM_PSCM_CPU_NOT_HALT);
while ((bpm_get_status(BPM) & BPM_SR_PSOK) == 0);
/* If the user has specified clock masks, enable only requested clocks */
irqflags_t const flags = cpu_irq_save();
#if defined(CONFIG_SYSCLK_INIT_CPUMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_CPUMASK - (uint32_t)PM);
PM->PM_CPUMASK = SYSCLK_INIT_MINIMAL_CPUMASK | CONFIG_SYSCLK_INIT_CPUMASK;
#endif
#if defined(CONFIG_SYSCLK_INIT_HSBMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_HSBMASK - (uint32_t)PM);
PM->PM_HSBMASK = SYSCLK_INIT_MINIMAL_HSBMASK | CONFIG_SYSCLK_INIT_HSBMASK;
#endif
#if defined(CONFIG_SYSCLK_INIT_PBAMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBAMASK - (uint32_t)PM);
PM->PM_PBAMASK = SYSCLK_INIT_MINIMAL_PBAMASK | CONFIG_SYSCLK_INIT_PBAMASK;
#endif
#if defined(CONFIG_SYSCLK_INIT_PBBMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBBMASK - (uint32_t)PM);
PM->PM_PBBMASK = SYSCLK_INIT_MINIMAL_PBBMASK | CONFIG_SYSCLK_INIT_PBBMASK;
#endif
#if defined(CONFIG_SYSCLK_INIT_PBCMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBCMASK - (uint32_t)PM);
PM->PM_PBCMASK = SYSCLK_INIT_MINIMAL_PBCMASK | CONFIG_SYSCLK_INIT_PBCMASK;
#endif
#if defined(CONFIG_SYSCLK_INIT_PBDMASK)
PM->PM_UNLOCK = PM_UNLOCK_KEY(0xAAu)
| PM_UNLOCK_ADDR((uint32_t)&PM->PM_PBDMASK - (uint32_t)PM);
PM->PM_PBDMASK = SYSCLK_INIT_MINIMAL_PBDMASK | CONFIG_SYSCLK_INIT_PBDMASK;
#endif
cpu_irq_restore(flags);
#if (defined CONFIG_SYSCLK_DEFAULT_RETURNS_SLOW_OSC)
/* Signal that the internal frequencies are setup */
sysclk_initialized = true;
#endif
}
//! @}