/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2011, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: 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 | |
* 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \file */ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "chip.h" | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Definition | |
*----------------------------------------------------------------------------*/ | |
#define MAX_PERI_ID ID_MPDDRC | |
/*---------------------------------------------------------------------------- | |
* Local variables | |
*----------------------------------------------------------------------------*/ | |
/** Array of Max peripheral Frequence support for SAMA5 chip*/ | |
static const PeripheralClockMaxFreq periClkMaxFreq[] = { | |
/* peripheral ID, Max frequency */ | |
{ID_FIQ , 133000000 }, | |
{ID_SYS , 133000000 }, | |
{ID_DBGU , 66000000 }, | |
{ID_PIT , 133000000 }, | |
{ID_WDT , 133000000 }, | |
{ID_SMC , 133000000 }, | |
{ID_PIOA , 133000000 }, | |
{ID_PIOB , 133000000 }, | |
{ID_PIOC , 133000000 }, | |
{ID_PIOD , 133000000 }, | |
{ID_PIOE , 133000000 }, | |
{ID_SMD , 24000000 }, | |
{ID_USART0 , 66000000 }, | |
{ID_USART1 , 66000000 }, | |
{ID_USART2 , 66000000 }, | |
{ID_USART3 , 66000000 }, | |
{ID_UART0 , 66000000 }, | |
{ID_UART1 , 66000000 }, | |
{ID_TWI0 , 33000000 }, | |
{ID_TWI1 , 33000000 }, | |
{ID_TWI2 , 33000000 }, | |
{ID_HSMCI0 , 133000000 }, | |
{ID_HSMCI1 , 133000000 }, | |
{ID_HSMCI2 , 133000000 }, | |
{ID_SPI0 , 133000000 }, | |
{ID_SPI1 , 133000000 }, | |
{ID_TC0 , 66000000 }, | |
{ID_TC1 , 66000000 }, | |
{ID_PWM , 133000000 }, | |
{ID_ADC , 66000000 }, | |
{ID_DMAC0 , 133000000 }, | |
{ID_DMAC1 , 133000000 }, | |
{ID_UHPHS , 133000000 }, | |
{ID_UDPHS , 133000000 }, | |
{ID_GMAC , 133000000 }, | |
{ID_EMAC , 133000000 }, | |
{ID_LCDC , 133000000 }, | |
{ID_ISI , 133000000 }, | |
{ID_SSC0 , 66000000 }, | |
{ID_SSC1 , 66000000 }, | |
{ID_CAN0 , 66000000 }, | |
{ID_CAN1 , 66000000 }, | |
{ID_SHA , 133000000 }, | |
{ID_AES , 133000000 }, | |
{ID_TDES , 133000000 }, | |
{ID_TRNG , 133000000 }, | |
{ID_ARM , 133000000 }, | |
{ID_IRQ , 133000000 }, | |
{ID_FUSE , 133000000 }, | |
{ID_MPDDRC , 133000000 } | |
}; | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Get maxinum frequency clock for giving pripheral ID. | |
* | |
* \param id Peripheral ID (ID_xxx). | |
*/ | |
extern uint32_t PMC_GetPeriMaxFreq( uint32_t dwId ) | |
{ | |
uint8_t i; | |
for (i = 0; i < MAX_PERI_ID; i++) { | |
if ( dwId == periClkMaxFreq[i].bPeriphID) return periClkMaxFreq[i].bMaxFrequency; | |
} | |
return 0; | |
} | |
/** | |
* \brief Set maxinum frequency clock for giving pripheral ID. | |
* | |
* \param id Peripheral ID (ID_xxx). | |
* \param mck Master clock. | |
* \return Peripheral clock. | |
*/ | |
extern uint32_t PMC_SetPeriMaxClock( uint32_t dwId, uint32_t mck) | |
{ | |
uint32_t maxClock; | |
uint8_t i; | |
/* Disable peripher clock */ | |
PMC->PMC_PCR = PMC_PCR_PID(dwId) | PMC_PCR_CMD; | |
maxClock = PMC_GetPeriMaxFreq(dwId); | |
for ( i = 0; i < 4; i++) { | |
if ( mck / (1 << i ) <= maxClock) break; | |
} | |
PMC->PMC_PCR = PMC_PCR_PID(dwId) | PMC_PCR_CMD | (i << PMC_PCR_DIV_Pos) | PMC_PCR_EN; | |
return maxClock; | |
} | |
/** | |
* \brief Enables the clock of a peripheral. The peripheral ID is used | |
* to identify which peripheral is targetted. | |
* | |
* \note The ID must NOT be shifted (i.e. 1 << ID_xxx). | |
* | |
* \param id Peripheral ID (ID_xxx). | |
*/ | |
extern void PMC_EnablePeripheral( uint32_t dwId ) | |
{ | |
if (dwId < 32) | |
{ | |
if ( (PMC->PMC_PCSR0 & ((uint32_t)1 << dwId)) == ((uint32_t)1 << dwId) ) | |
{ | |
// TRACE_DEBUG( "PMC_EnablePeripheral: clock of peripheral" " %u is already enabled\n\r", dwId ) ; | |
} | |
else | |
{ | |
PMC->PMC_PCER0 = (1 << dwId) ; | |
} | |
} else { | |
if ( (PMC->PMC_PCSR1 & ((uint32_t)1 << ( dwId - 32))) == ((uint32_t)1 << (dwId - 32)) ) | |
{ | |
// TRACE_DEBUG( "PMC_EnablePeripheral: clock of peripheral" " %u is already enabled\n\r", dwId ) ; | |
} | |
else | |
{ | |
PMC->PMC_PCER1 = 1 << (dwId - 32) ; | |
} | |
} | |
} | |
/** | |
* \brief Disables the clock of a peripheral. The peripheral ID is used | |
* to identify which peripheral is targetted. | |
* | |
* \note The ID must NOT be shifted (i.e. 1 << ID_xxx). | |
* | |
* \param id Peripheral ID (ID_xxx). | |
*/ | |
extern void PMC_DisablePeripheral( uint32_t dwId ) | |
{ | |
if (dwId < 32) | |
{ | |
if ( (PMC->PMC_PCSR0 & ((uint32_t)1 << dwId)) != ((uint32_t)1 << dwId) ) | |
{ | |
TRACE_DEBUG("PMC_DisablePeripheral: clock of peripheral" " %u is not enabled\n\r", (unsigned int)dwId ) ; | |
} | |
else | |
{ | |
PMC->PMC_PCDR0 = 1 << dwId ; | |
} | |
} else { | |
if ( (PMC->PMC_PCSR1 & ((uint32_t)1 << (dwId - 32))) != ((uint32_t)1 << (dwId - 32)) ) | |
{ | |
TRACE_DEBUG("PMC_DisablePeripheral: clock of peripheral" " %u is not enabled\n\r", (unsigned int)dwId ) ; | |
} | |
else | |
{ | |
PMC->PMC_PCDR1 = 1 << (dwId - 32) ; | |
} | |
} | |
} | |
/** | |
* \brief Enable all the periph clock via PMC. | |
*/ | |
extern void PMC_EnableAllPeripherals( void ) | |
{ | |
PMC->PMC_PCER0 = 0xFFFFFFFF ; | |
PMC->PMC_PCER1 = 0xFFFFFFFF ; | |
TRACE_DEBUG( "Enable all periph clocks\n\r" ) ; | |
} | |
/** | |
* \brief Disable all the periph clock via PMC. | |
*/ | |
extern void PMC_DisableAllPeripherals( void ) | |
{ | |
TRACE_DEBUG( "Disable all periph clocks\n\r" ) ; | |
PMC->PMC_PCDR0 = 0xFFFFFFFF ; | |
PMC->PMC_PCDR1 = 0xFFFFFFFF ; | |
} | |
/** | |
* \brief Get Periph Status for the given peripheral ID. | |
* | |
* \param id Peripheral ID (ID_xxx). | |
*/ | |
extern uint32_t PMC_IsPeriphEnabled( uint32_t dwId ) | |
{ | |
if (dwId < 32) { | |
return ( PMC->PMC_PCSR0 & (1 << dwId) ) ; | |
} else { | |
return ( PMC->PMC_PCSR1 & (1 << (dwId - 32)) ) ; | |
} | |
} | |
/** | |
* \brief Select external 32K crystal. | |
*/ | |
extern void PMC_SelectExt32KCrystal(void) | |
{ | |
volatile uint32_t count; | |
/* Switch from internal RC 32kHz to external OSC 32 kHz */ | |
/* before switch slow clock source, switch MCK to Main Clock*/ | |
//PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; | |
//while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); | |
/* enable external OSC 32 kHz */ | |
SCKC->SCKC_CR |= SCKC_CR_OSC32EN; | |
/* Wait 32,768 Hz Startup Time for clock stabilization (software loop) */ | |
for (count = 0; count < 0x1000; count++); | |
/* disable OSC 32 kHz bypass */ | |
SCKC->SCKC_CR &= ~SCKC_CR_OSC32BYP; | |
/* switch slow clock source to external OSC 32 kHz */ | |
SCKC->SCKC_CR = (SCKC->SCKC_CR & ~SCKC_CR_OSCSEL) | SCKC_CR_OSCSEL_XTAL; | |
/* Wait 5 slow clock cycles for internal resynchronization*/ | |
for (count = 0; count < 0x1000; count++); | |
/* wait slow clock status change for external OSC 32 kHz selection */ | |
// while(!(PMC->PMC_SR & PMC_SR_OSCSELS)); | |
/* disable internal RC 32 kHz */ | |
SCKC->SCKC_CR &= ~SCKC_CR_RCEN; | |
} | |
/** | |
* \brief Select internal 32K crystal. | |
*/ | |
extern void PMC_SelectInt32kCrystal(void) | |
{ | |
/* switch from external RC 32kHz to internal OSC 32 kHz */ | |
volatile uint32_t count; | |
/* before switch slow clock source, switch MCK to Main Clock */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; | |
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); | |
/* enable internal RC 32 kHz */ | |
SCKC->SCKC_CR |= SCKC_CR_RCEN; | |
/* Wait 32,768 Hz Startup Time for clock stabilization (software loop)*/ | |
for (count = 0; count < 0x1000; count++); | |
/* switch slow clock source to internal OSC 32 kHz */ | |
SCKC->SCKC_CR = (SCKC->SCKC_CR & ~SCKC_CR_OSCSEL) | SCKC_CR_OSCSEL_RC; | |
/* Wait 5 slow clock cycles for internal resynchronization */ | |
for (count = 0; count < 0x1000; count++); | |
/* wait slow clock status change for internal RC 32 kHz selection */ | |
// while(PMC->PMC_SR & PMC_SR_OSCSELS); | |
/* disable external OSC 32 kHz */ | |
SCKC->SCKC_CR &= ~SCKC_CR_OSC32EN; | |
} | |
/** | |
* \brief Select external 12M OSC. | |
*/ | |
extern void PMC_SelectExt12M_Osc(void) | |
{ | |
/* switch from internal RC 12 MHz to external OSC 12 MHz */ | |
/* wait Main XTAL Oscillator stabilisation*/ | |
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCSEL ) == CKGR_MOR_MOSCSEL) return; | |
/* enable external OSC 12 MHz */ | |
PMC->CKGR_MOR |= CKGR_MOR_MOSCXTEN | CKGR_MOR_KEY(0x37); | |
/* wait Main CLK Ready */ | |
while(!(PMC->CKGR_MCFR & CKGR_MCFR_MAINFRDY)); | |
/* disable external OSC 12 MHz bypass */ | |
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) | CKGR_MOR_KEY(0x37); | |
/* switch MAIN clock to external OSC 12 MHz*/ | |
PMC->CKGR_MOR |= CKGR_MOR_MOSCSEL | CKGR_MOR_KEY(0x37); | |
/* wait MAIN clock status change for external OSC 12 MHz selection*/ | |
while(!(PMC->PMC_SR & PMC_SR_MOSCSELS)); | |
/* in case where MCK is running on MAIN CLK */ | |
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); | |
/* disable internal RC 12 MHz*/ | |
//PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN) | CKGR_MOR_KEY(0x37); | |
} | |
/** | |
* \brief Select internal 12M OSC. | |
*/ | |
extern void PMC_SelectInt12M_Osc(void) | |
{ | |
uint32_t count; | |
/* switch from external OSC 12 MHz to internal RC 12 MHz*/ | |
/* enable internal RC 12 MHz */ | |
PMC->CKGR_MOR |= CKGR_MOR_MOSCRCEN | CKGR_MOR_KEY(0x37); | |
/* Wait internal 12 MHz RC Startup Time for clock stabilization (software loop) */ | |
for (count = 0; count < 0x100000; count++); | |
/* switch MAIN clock to internal RC 12 MHz */ | |
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_KEY(0x37); | |
/* wait MAIN clock status change for internal RC 12 MHz selection*/ | |
while(PMC->PMC_SR & PMC_SR_MOSCSELS); | |
/* in case where MCK is running on MAIN CLK */ | |
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); | |
/* disable external OSC 12 MHz */ | |
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) | CKGR_MOR_KEY(0x37); | |
} | |
/** | |
* \brief Switch PMC from MCK to PLL clock. | |
*/ | |
extern void PMC_SwitchMck2Pll(void) | |
{ | |
/* Select PLL as input clock for PCK and MCK */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_PLLA_CLK ; | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
/** | |
* \brief Switch PMC from MCK to main clock. | |
*/ | |
extern void PMC_SwitchMck2Main(void) | |
{ | |
/* Select Main Oscillator as input clock for PCK and MCK */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_PCK_CSS_MAIN_CLK ; | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
/** | |
* \brief Switch PMC from MCK to slow clock. | |
*/ | |
extern void PMC_SwitchMck2Slck(void) | |
{ | |
/* Select Slow Clock as input clock for PCK and MCK */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_PCK_CSS_SLOW_CLK ; | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
/** | |
* \brief Configure MCK Prescaler. | |
* \param prescaler prescaler value. | |
*/ | |
extern void PMC_SetMckPrescaler(uint32_t prescaler) | |
{ | |
/* Change MCK Prescaler divider in PMC_MCKR register */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | prescaler; | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
/** | |
* \brief Configure MCK PLLA divider. | |
* \param divider PLL divider value. | |
*/ | |
extern void PMC_SetMckPllaDiv(uint32_t divider) | |
{ | |
if ((PMC->PMC_MCKR & PMC_MCKR_PLLADIV2_DIV2) == PMC_MCKR_PLLADIV2_DIV2) | |
{ | |
if(divider == PMC_MCKR_PLLADIV2_NOT_DIV2) { | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PLLADIV2_DIV2); | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
} else { | |
if(divider == PMC_MCKR_PLLADIV2_DIV2) { | |
PMC->PMC_MCKR = (PMC->PMC_MCKR | PMC_MCKR_PLLADIV2_DIV2); | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
} | |
} | |
/** | |
* \brief Configure MCK Divider. | |
* \param divider divider value. | |
*/ | |
extern void PMC_SetMckDivider(uint32_t divider) | |
{ | |
/* change MCK Prescaler divider in PMC_MCKR register */ | |
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | divider; | |
while( !(PMC->PMC_SR & PMC_SR_MCKRDY) ); | |
} | |
/** | |
* \brief Configure PLL Register. | |
* \param pll pll value. | |
* \param cpcr cpcr value. | |
*/ | |
extern void PMC_SetPllA(uint32_t pll, uint32_t cpcr) | |
{ | |
PMC->CKGR_PLLAR = pll; | |
PMC->PMC_PLLICPR = cpcr; | |
while( !(PMC->PMC_SR & PMC_SR_LOCKA) ); | |
} | |
/** | |
* \brief Disable PLLA Register. | |
*/ | |
extern void PMC_DisablePllA(void) | |
{ | |
PMC->CKGR_PLLAR = (PMC->CKGR_PLLAR & ~CKGR_PLLAR_MULA_Msk) | CKGR_PLLAR_MULA(0); | |
} | |