/**
* @file
*
* @ingroup RTEMSBSPsARMLPC24XX
*
* @brief Startup code.
*/
/*
* Copyright (c) 2008-2012 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Obere Lagerstr. 30
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <bsp.h>
#include <bsp/io.h>
#include <bsp/start.h>
#include <bsp/lpc24xx.h>
#include <bsp/lpc-emc.h>
#include <bsp/start-config.h>
static BSP_START_TEXT_SECTION void lpc24xx_cpu_delay(unsigned ticks)
{
unsigned i = 0;
/* One loop execution needs four instructions */
ticks /= 4;
for (i = 0; i <= ticks; ++i) {
__asm__ volatile ("nop");
}
}
static BSP_START_TEXT_SECTION void lpc24xx_udelay(unsigned us)
{
lpc24xx_cpu_delay(us * (LPC24XX_CCLK / 1000000));
}
static BSP_START_TEXT_SECTION void lpc24xx_init_pinsel(void)
{
lpc24xx_pin_config(
&lpc24xx_start_config_pinsel [0],
LPC24XX_PIN_SET_FUNCTION
);
}
static BSP_START_TEXT_SECTION void lpc24xx_init_emc_static(void)
{
size_t i = 0;
size_t chip_count = lpc24xx_start_config_emc_static_chip_count;
for (i = 0; i < chip_count; ++i) {
const lpc24xx_emc_static_chip_config *chip_config =
&lpc24xx_start_config_emc_static_chip [i];
lpc24xx_emc_static_chip_config chip_config_on_stack;
size_t config_size = sizeof(chip_config_on_stack.config);
bsp_start_memcpy(
(int *) &chip_config_on_stack.config,
(const int *) &chip_config->config,
config_size
);
bsp_start_memcpy(
(int *) chip_config->chip_select,
(const int *) &chip_config_on_stack.config,
config_size
);
}
}
static BSP_START_TEXT_SECTION void lpc24xx_init_emc_dynamic(void)
{
size_t chip_count = lpc24xx_start_config_emc_dynamic_chip_count;
if (chip_count > 0) {
bool do_initialization = true;
size_t i = 0;
for (i = 0; do_initialization && i < chip_count; ++i) {
const lpc24xx_emc_dynamic_chip_config *chip_cfg =
&lpc24xx_start_config_emc_dynamic_chip [i];
volatile lpc_emc_dynamic *chip_select = chip_cfg->chip_select;
do_initialization = (chip_select->config & EMC_DYN_CFG_B) == 0;
}
if (do_initialization) {
volatile lpc_emc *emc = (volatile lpc_emc *) EMC_BASE_ADDR;
const lpc24xx_emc_dynamic_config *cfg =
&lpc24xx_start_config_emc_dynamic [0];
uint32_t dynamiccontrol = EMC_DYN_CTRL_CE | EMC_DYN_CTRL_CS;
#ifdef ARM_MULTILIB_ARCH_V7M
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
/* Delay control */
scb->emcdlyctl = cfg->emcdlyctl;
#endif
emc->dynamicreadconfig = cfg->readconfig;
/* Timings */
emc->dynamictrp = cfg->trp;
emc->dynamictras = cfg->tras;
emc->dynamictsrex = cfg->tsrex;
emc->dynamictapr = cfg->tapr;
emc->dynamictdal = cfg->tdal;
emc->dynamictwr = cfg->twr;
emc->dynamictrc = cfg->trc;
emc->dynamictrfc = cfg->trfc;
emc->dynamictxsr = cfg->txsr;
emc->dynamictrrd = cfg->trrd;
emc->dynamictmrd = cfg->tmrd;
/* NOP period */
emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_NOP;
lpc24xx_udelay(200);
/* Precharge */
emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_PALL;
emc->dynamicrefresh = 1;
/*
* Perform several refresh cycles with a memory refresh every 16 AHB
* clock cycles. Wait until eight SDRAM refresh cycles have occurred
* (128 AHB clock cycles).
*/
lpc24xx_cpu_delay(128);
/* Refresh timing */
emc->dynamicrefresh = cfg->refresh;
lpc24xx_cpu_delay(128);
for (i = 0; i < chip_count; ++i) {
const lpc24xx_emc_dynamic_chip_config *chip_cfg =
&lpc24xx_start_config_emc_dynamic_chip [i];
volatile lpc_emc_dynamic *chip_select = chip_cfg->chip_select;
uint32_t config = chip_cfg->config;
/* Chip select */
chip_select->config = config;
chip_select->rascas = chip_cfg->rascas;
/* Set modes */
emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_MODE;
*(volatile uint32_t *)(chip_cfg->address + chip_cfg->mode);
/* Enable buffer */
chip_select->config = config | EMC_DYN_CFG_B;
}
emc->dynamiccontrol = 0;
}
}
}
static BSP_START_TEXT_SECTION void lpc24xx_init_main_oscillator(void)
{
#ifdef ARM_MULTILIB_ARCH_V4
if ((SCS & 0x40) == 0) {
SCS |= 0x20;
while ((SCS & 0x40) == 0) {
/* Wait */
}
}
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
if ((scb->scs & LPC17XX_SCB_SCS_OSC_STATUS) == 0) {
scb->scs |= LPC17XX_SCB_SCS_OSC_ENABLE;
while ((scb->scs & LPC17XX_SCB_SCS_OSC_STATUS) == 0) {
/* Wait */
}
}
#endif
}
#ifdef ARM_MULTILIB_ARCH_V4
static BSP_START_TEXT_SECTION void lpc24xx_pll_config(
uint32_t val
)
{
PLLCON = val;
PLLFEED = 0xaa;
PLLFEED = 0x55;
}
/**
* @brief Sets the Phase Locked Loop (PLL).
*
* All parameter values are the actual register field values.
*
* @param clksrc Selects the clock source for the PLL.
*
* @param nsel Selects PLL pre-divider value (sometimes named psel).
*
* @param msel Selects PLL multiplier value.
*
* @param cclksel Selects the divide value for creating the CPU clock (CCLK)
* from the PLL output.
*/
static BSP_START_TEXT_SECTION void lpc24xx_set_pll(
unsigned clksrc,
unsigned nsel,
unsigned msel,
unsigned cclksel
)
{
uint32_t pllstat = PLLSTAT;
uint32_t pllcfg = SET_PLLCFG_NSEL(0, nsel) | SET_PLLCFG_MSEL(0, msel);
uint32_t clksrcsel = SET_CLKSRCSEL_CLKSRC(0, clksrc);
uint32_t cclkcfg = SET_CCLKCFG_CCLKSEL(0, cclksel | 1);
bool pll_enabled = (pllstat & PLLSTAT_PLLE) != 0;
/* Disconnect PLL if necessary */
if ((pllstat & PLLSTAT_PLLC) != 0) {
if (pll_enabled) {
/* Check if we run already with the desired settings */
if (PLLCFG == pllcfg && CLKSRCSEL == clksrcsel && CCLKCFG == cclkcfg) {
/* Nothing to do */
return;
}
lpc24xx_pll_config(PLLCON_PLLE);
} else {
lpc24xx_pll_config(0);
}
}
/* Set CPU clock divider to a reasonable save value */
CCLKCFG = 0;
/* Disable PLL if necessary */
if (pll_enabled) {
lpc24xx_pll_config(0);
}
/* Select clock source */
CLKSRCSEL = clksrcsel;
/* Set PLL Configuration Register */
PLLCFG = pllcfg;
/* Enable PLL */
lpc24xx_pll_config(PLLCON_PLLE);
/* Wait for lock */
while ((PLLSTAT & PLLSTAT_PLOCK) == 0) {
/* Wait */
}
/* Set CPU clock divider and ensure that we have an odd value */
CCLKCFG = cclkcfg;
/* Connect PLL */
lpc24xx_pll_config(PLLCON_PLLE | PLLCON_PLLC);
}
#else /* ARM_MULTILIB_ARCH_V4 */
static BSP_START_TEXT_SECTION void lpc17xx_pll_config(
volatile lpc17xx_pll *pll,
uint32_t val
)
{
pll->con = val;
pll->feed = 0xaa;
pll->feed = 0x55;
}
static BSP_START_TEXT_SECTION void lpc17xx_set_pll(
unsigned msel,
unsigned psel,
unsigned cclkdiv
)
{
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
volatile lpc17xx_pll *pll = &scb->pll_0;
uint32_t pllcfg = LPC17XX_PLL_SEL_MSEL(msel)
| LPC17XX_PLL_SEL_PSEL(psel);
uint32_t pllstat = LPC17XX_PLL_STAT_PLLE
| LPC17XX_PLL_STAT_PLOCK | pllcfg;
uint32_t cclksel_cclkdiv = LPC17XX_SCB_CCLKSEL_CCLKDIV(cclkdiv);
uint32_t cclksel = LPC17XX_SCB_CCLKSEL_CCLKSEL | cclksel_cclkdiv;
if (
pll->stat != pllstat
|| scb->cclksel != cclksel
|| scb->clksrcsel != LPC17XX_SCB_CLKSRCSEL_CLKSRC
) {
/* Use SYSCLK for CCLK */
scb->cclksel = LPC17XX_SCB_CCLKSEL_CCLKDIV(1);
/* Turn off USB */
scb->usbclksel = 0;
/* Disable PLL */
lpc17xx_pll_config(pll, 0);
/* Select main oscillator as clock source */
scb->clksrcsel = LPC17XX_SCB_CLKSRCSEL_CLKSRC;
/* Set PLL configuration */
pll->cfg = pllcfg;
/* Set the CCLK, PCLK and EMCCLK divider */
scb->cclksel = cclksel_cclkdiv;
scb->pclksel = LPC17XX_SCB_PCLKSEL_PCLKDIV(cclkdiv * LPC24XX_PCLKDIV);
scb->emcclksel = LPC24XX_EMCCLKDIV == 1 ? 0 : LPC17XX_SCB_EMCCLKSEL_EMCDIV;
/* Enable PLL */
lpc17xx_pll_config(pll, LPC17XX_PLL_CON_PLLE);
/* Wait for lock */
while ((pll->stat & LPC17XX_PLL_STAT_PLOCK) == 0) {
/* Wait */
}
/* Use the PLL clock */
scb->cclksel = cclksel;
}
}
#endif /* ARM_MULTILIB_ARCH_V4 */
static BSP_START_TEXT_SECTION void lpc24xx_init_pll(void)
{
#ifdef ARM_MULTILIB_ARCH_V4
#if LPC24XX_OSCILLATOR_MAIN == 12000000U
#if LPC24XX_CCLK == 72000000U
lpc24xx_set_pll(1, 0, 11, 3);
#elif LPC24XX_CCLK == 51612800U
lpc24xx_set_pll(1, 30, 399, 5);
#else
#error "unexpected CCLK"
#endif
#elif LPC24XX_OSCILLATOR_MAIN == 3686400U
#if LPC24XX_CCLK == 58982400U
lpc24xx_set_pll(1, 0, 47, 5);
#else
#error "unexpected CCLK"
#endif
#else
#error "unexpected main oscillator frequency"
#endif
#else
#if LPC24XX_OSCILLATOR_MAIN == 12000000U
#if LPC24XX_CCLK == 120000000U
lpc17xx_set_pll(9, 0, 1);
#elif LPC24XX_CCLK == 96000000U
lpc17xx_set_pll(7, 0, 1);
#elif LPC24XX_CCLK == 72000000U
lpc17xx_set_pll(5, 1, 1);
#elif LPC24XX_CCLK == 48000000U
lpc17xx_set_pll(3, 1, 1);
#else
#error "unexpected CCLK"
#endif
#else
#error "unexpected main oscillator frequency"
#endif
#endif
}
static BSP_START_TEXT_SECTION void lpc24xx_init_memory_map(void)
{
#ifdef ARM_MULTILIB_ARCH_V4
/* Re-map interrupt vectors to internal RAM */
MEMMAP = SET_MEMMAP_MAP(MEMMAP, 2);
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
scb->memmap = LPC17XX_SCB_MEMMAP_MAP;
#endif
/* Use normal memory map */
EMC_CTRL &= ~0x2U;
}
static BSP_START_TEXT_SECTION void lpc24xx_init_memory_accelerator(void)
{
#ifdef ARM_MULTILIB_ARCH_V4
/* Fully enable memory accelerator module functions (MAM) */
MAMCR = 0;
#if LPC24XX_CCLK <= 20000000U
MAMTIM = 0x1;
#elif LPC24XX_CCLK <= 40000000U
MAMTIM = 0x2;
#elif LPC24XX_CCLK <= 60000000U
MAMTIM = 0x3;
#else
MAMTIM = 0x4;
#endif
MAMCR = 0x2;
/* Enable fast IO for ports 0 and 1 */
SCS |= 0x1;
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
#if LPC24XX_CCLK <= 20000000U
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x0);
#elif LPC24XX_CCLK <= 40000000U
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x1);
#elif LPC24XX_CCLK <= 60000000U
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x2);
#elif LPC24XX_CCLK <= 80000000U
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x3);
#elif LPC24XX_CCLK <= 100000000U
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x4);
#else
scb->flashcfg = LPC17XX_SCB_FLASHCFG_FLASHTIM(0x5);
#endif
#endif
}
static BSP_START_TEXT_SECTION void lpc24xx_stop_gpdma(void)
{
#ifdef LPC24XX_STOP_GPDMA
#ifdef ARM_MULTILIB_ARCH_V4
bool has_power = (PCONP & PCONP_GPDMA) != 0;
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
bool has_power = (scb->pconp & LPC17XX_SCB_PCONP_GPDMA) != 0;
#endif
if (has_power) {
GPDMA_CONFIG = 0;
#ifdef ARM_MULTILIB_ARCH_V4
PCONP &= ~PCONP_GPDMA;
#else
scb->pconp &= ~LPC17XX_SCB_PCONP_GPDMA;
#endif
}
#endif
}
static BSP_START_TEXT_SECTION void lpc24xx_stop_ethernet(void)
{
#ifdef LPC24XX_STOP_ETHERNET
#ifdef ARM_MULTILIB_ARCH_V4
bool has_power = (PCONP & PCONP_ETHERNET) != 0;
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
bool has_power = (scb->pconp & LPC17XX_SCB_PCONP_ENET) != 0;
#endif
if (has_power) {
MAC_COMMAND = 0x38;
MAC_MAC1 = 0xcf00;
MAC_MAC1 = 0;
#ifdef ARM_MULTILIB_ARCH_V4
PCONP &= ~PCONP_ETHERNET;
#else
scb->pconp &= ~LPC17XX_SCB_PCONP_ENET;
#endif
}
#endif
}
static BSP_START_TEXT_SECTION void lpc24xx_stop_usb(void)
{
#ifdef LPC24XX_STOP_USB
#ifdef ARM_MULTILIB_ARCH_V4
bool has_power = (PCONP & PCONP_USB) != 0;
#else
volatile lpc17xx_scb *scb = &LPC17XX_SCB;
bool has_power = (scb->pconp & LPC17XX_SCB_PCONP_USB) != 0;
#endif
if (has_power) {
OTG_CLK_CTRL = 0;
#ifdef ARM_MULTILIB_ARCH_V4
PCONP &= ~PCONP_USB;
#else
scb->pconp &= ~LPC17XX_SCB_PCONP_USB;
scb->usbclksel = 0;
#endif
}
#endif
}
static BSP_START_TEXT_SECTION void lpc24xx_init_mpu(void)
{
#ifdef ARM_MULTILIB_ARCH_V7M
volatile ARMV7M_MPU *mpu = _ARMV7M_MPU;
size_t region_count = lpc24xx_start_config_mpu_region_count;
size_t i = 0;
for (i = 0; i < region_count; ++i) {
mpu->rbar = lpc24xx_start_config_mpu_region [i].rbar;
mpu->rasr = lpc24xx_start_config_mpu_region [i].rasr;
}
if (region_count > 0) {
mpu->ctrl = ARMV7M_MPU_CTRL_ENABLE;
}
#endif
}
BSP_START_TEXT_SECTION void bsp_start_hook_0(void)
{
lpc24xx_init_main_oscillator();
lpc24xx_init_pll();
lpc24xx_init_pinsel();
lpc24xx_init_emc_static();
}
BSP_START_TEXT_SECTION void bsp_start_hook_1(void)
{
lpc24xx_init_memory_map();
lpc24xx_init_memory_accelerator();
lpc24xx_init_emc_dynamic();
lpc24xx_init_mpu();
lpc24xx_stop_gpdma();
lpc24xx_stop_ethernet();
lpc24xx_stop_usb();
bsp_start_copy_sections();
bsp_start_clear_bss();
/* At this point we can use objects outside the .start section */
}