blob: 16edb71d2213486b06b00fad430b8c62a6da1c91 [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation.
* Copyright (c) 2013-2015 Wind River Systems, Inc.
*
* Licensed 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.
*/
/**
* @file
* @brief System/hardware module for Atmel SAM3 family processor
*
* This module provides routines to initialize and support board-level hardware
* for the Atmel SAM3 family processor.
*/
#include <nanokernel.h>
#include <device.h>
#include <init.h>
#include <soc.h>
#include <arch/cpu.h>
/**
* @brief Setup various clock on SoC.
*
* Setup the SoC clocks according to section 28.12 in datasheet.
*
* Assumption:
* SLCK = 32.768kHz
*/
static ALWAYS_INLINE void clock_init(void)
{
uint32_t tmp;
/* Note:
* Magic numbers below are obtained by reading the registers
* when the SoC was running the SAM-BA bootloader
* (with reserved bits set to 0).
*/
#ifdef CONFIG_SOC_ATMEL_SAM3_EXT_SLCK
/* This part is to switch the slow clock to using
* the external 32 kHz crystal oscillator.
*/
/* Select external crystal */
__SUPC->cr = SUPC_CR_KEY | SUPC_CR_XTALSEL;
/* Wait for oscillator to be stablized */
while (!(__SUPC->sr & SUPC_SR_OSCSEL))
;
#endif /* CONFIG_SOC_ATMEL_SAM3_EXT_SLCK */
#ifdef CONFIG_SOC_ATMEL_SAM3_EXT_MAINCK
/* Start the external main oscillator */
__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_4MHZ
| PMC_CKGR_MOR_MOSCRCEN | PMC_CKGR_MOR_MOSCXTEN
| PMC_CKGR_MOR_MOSCXTST;
/* Wait for main oscillator to be stablized */
while (!(__PMC->sr & PMC_INT_MOSCXTS))
;
/* Select main oscillator as source since it is more accurate
* according to datasheet.
*/
__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_4MHZ
| PMC_CKGR_MOR_MOSCRCEN | PMC_CKGR_MOR_MOSCXTEN
| PMC_CKGR_MOR_MOSCXTST | PMC_CKGR_MOR_MOSCSEL;
/* Wait for main oscillator to be selected */
while (!(__PMC->sr & PMC_INT_MOSCSELS))
;
#ifdef CONFIG_SOC_ATMEL_SAM3_WAIT_MODE
/*
* Instruct CPU enter Wait mode instead of Sleep mode to
* keep Processor Clock (HCLK) and thus be able to debug
* CPU using JTAG
*/
__PMC->fsmr |= PMC_FSMR_LPM;
#endif
#else
/* Set main fast RC oscillator to 12 MHz */
__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_12MHZ
| PMC_CKGR_MOR_MOSCRCEN;
/* Wait for main fast RC oscillator to be stablized */
while (!(__PMC->sr & PMC_INT_MOSCRCS))
;
#endif /* CONFIG_SOC_ATMEL_SAM3_EXT_MAINCK */
/* Use PLLA as master clock.
* According to datasheet, PMC_MCKR must not be programmed in
* a single write operation. So it seems the safe way is to
* get the system to use main clock (by setting CSS). Then set
* the prescaler (PRES). Finally setting it back to using PLL.
*/
/* Switch to main clock first so we can setup PLL */
tmp = __PMC->mckr & ~PMC_MCKR_CSS_MASK;
__PMC->mckr = tmp | PMC_MCKR_CSS_MAIN;
/* Wait for clock selection complete */
while (!(__PMC->sr & PMC_INT_MCKRDY))
;
/* Setup PLLA */
__PMC->ckgr_pllar = PMC_CKGR_PLLAR_DIVA | PMC_CKGR_PLLAR_ONE
| PMC_CKGR_PLLAR_MULA
| PMC_CKGR_PLLAR_PLLACOUNT;
/* Wait for PLL lock */
while (!(__PMC->sr & PMC_INT_LOCKA))
;
/* Setup prescaler */
tmp = __PMC->mckr & ~PMC_MCKR_PRES_MASK;
__PMC->mckr = tmp | PMC_MCKR_PRES_CLK;
/* Wait for main clock setup complete */
while (!(__PMC->sr & PMC_INT_MCKRDY))
;
/* Finally select PLL as clock source */
tmp = __PMC->mckr & ~PMC_MCKR_CSS_MASK;
__PMC->mckr = tmp | PMC_MCKR_CSS_PLLA;
/* Wait for main clock setup complete */
while (!(__PMC->sr & PMC_INT_MCKRDY))
;
}
/**
* @brief Perform basic hardware initialization at boot.
*
* This needs to be run from the very beginning.
* So the init priority has to be 0 (zero).
*
* @return 0
*/
static int atmel_sam3_init(struct device *arg)
{
uint32_t key;
ARG_UNUSED(arg);
/* Note:
* Magic numbers below are obtained by reading the registers
* when the SoC was running the SAM-BA bootloader
* (with reserved bits set to 0).
*/
key = irq_lock();
/* Setup the vector table offset register (VTOR),
* which is located at the beginning of flash area.
*/
_scs_relocate_vector_table((void *)CONFIG_FLASH_BASE_ADDRESS);
/* Setup the flash controller.
* The bootloader is running @ 48 MHz with
* FWS == 2.
* When running at 84 MHz, FWS == 4 seems
* to be more stable, and allows the board
* to boot.
*/
__EEFC0->fmr = 0x00000400;
__EEFC1->fmr = 0x00000400;
/* Clear all faults */
_ScbMemFaultAllFaultsReset();
_ScbBusFaultAllFaultsReset();
_ScbUsageFaultAllFaultsReset();
_ScbHardFaultAllFaultsReset();
/* Setup master clock */
clock_init();
/* Install default handler that simply resets the CPU
* if configured in the kernel, NOP otherwise
*/
NMI_INIT();
irq_unlock(key);
return 0;
}
SYS_INIT(atmel_sam3_init, PRIMARY, 0);