ChibiOS-Contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c

492 lines
14 KiB
C
Raw Normal View History

/*
2020-12-01 16:44:33 -08:00
Copyright (C) 2020 Alex Lewontin
Copyright (C) 2019 /u/KeepItUnder
2020-12-01 16:44:33 -08:00
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
2020-12-01 16:44:33 -08:00
http://www.apache.org/licenses/LICENSE-2.0
2020-12-01 16:44:33 -08:00
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.
*/
/**
2020-12-01 16:44:33 -08:00
* @file hal_lld.c
2020-12-12 18:12:58 -08:00
* @brief NUC123 HAL subsystem low level driver source.
*
* @addtogroup HAL
* @{
*/
#include "hal.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
2020-12-01 16:44:33 -08:00
#define FREQ_25MHZ 25000000
#define FREQ_50MHZ 50000000
#define FREQ_72MHZ 72000000
#define FREQ_100MHZ 100000000
#define FREQ_200MHZ 200000000
2020-12-01 16:44:33 -08:00
#define CLK_CLKDIV_HCLK(x) (((x)-1) << CLK_CLKDIV_HCLK_N_Pos)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
2020-12-01 16:44:33 -08:00
_Bool clock_initialized = FALSE;
uint32_t SystemCoreClock = __HSI; /* System Clock Frequency (Core Clock)*/
uint32_t CyclesPerUs = (__HSI / 1000000); /* Cycles per micro second */
uint32_t PllClock = __HSI; /*!< PLL Clock Frequency */
volatile const uint32_t config0 __attribute__((used, unused, section(".nuc123_config0"))) = NUC123_CONFIG0;
volatile const uint32_t config1 __attribute__((used, unused, section(".nuc123_config1"))) = NUC123_CONFIG1;
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
2020-12-01 16:44:33 -08:00
void SystemCoreClockUpdate(void) /* Get Core Clock Frequency */
{
/* ToDo: add code to calculate the system frequency based upon the current
register settings.
This function can be used to retrieve the system core clock frequeny
after user changed register sittings. */
2020-12-11 09:39:59 -08:00
/* SystemCoreClock = SYSTEM_CLOCK; */
2020-12-01 16:44:33 -08:00
uint32_t clkFreq;
uint32_t PllReg;
uint32_t pllFIN, pllNF, pllNR, pllNO;
/* Update PLL Clock */
2020-12-11 09:39:59 -08:00
/* PllClock = clks_lld_get_pll_clock_freq(); */
2020-12-01 16:44:33 -08:00
PllReg = CLK->PLLCON;
if (PllReg & (CLK_PLLCON_PD_Msk | CLK_PLLCON_OE_Msk)) {
PllClock = 0; /* PLL is off. */
} else {
if (PllReg & 0x00080000ul) {
pllFIN = __HIRC; /* Use HXT for PLL clock */
} else {
pllFIN = __HXT; /* Use HXT for PLL clock */
}
if (PllReg & CLK_PLLCON_BP_Msk) {
PllClock = pllFIN;
} else {
switch (((PllReg & CLK_PLLCON_OUT_DV_Msk) >> CLK_PLLCON_OUT_DV_Pos)) {
case 0: /* OUT_DIV == 00 : NO = 1 */
pllNO = 1;
break;
case 3: /* OUT_DIV == 11 : NO = 4 */
pllNO = 4;
break;
default: /* OUT_DIV == 01 or 10 : NO = 2 */
pllNO = 2;
break;
}
pllNF = ((PllReg & CLK_PLLCON_FB_DV_Msk) >> CLK_PLLCON_FB_DV_Pos) + 2;
pllNR = ((PllReg & CLK_PLLCON_IN_DV_Msk) >> CLK_PLLCON_IN_DV_Pos) + 2;
/* Shift right to avoid overflow condition */
PllClock = (((pllFIN >> 2) * pllNF) / (pllNR * pllNO) << 2);
}
}
/* Pick Clock Source */
switch (CLK->CLKSEL0 & CLK_CLKSEL0_HCLK_S_Msk) {
2020-12-11 09:39:59 -08:00
case 0: /* External HF Xtal */
2020-12-01 16:44:33 -08:00
clkFreq = __HXT;
break;
2020-12-11 09:39:59 -08:00
case 1: /* PLL clock / 2 */
2020-12-01 16:44:33 -08:00
clkFreq = PllClock >> 1;
break;
2020-12-11 09:39:59 -08:00
case 3: /* Internal 10kHz */
2020-12-01 16:44:33 -08:00
clkFreq = __LIRC;
break;
2020-12-11 09:39:59 -08:00
case 2: /* PLL clock */
2020-12-01 16:44:33 -08:00
clkFreq = PllClock;
break;
2020-12-11 09:39:59 -08:00
case 7: /* Internal 22.184MHz */
2020-12-01 16:44:33 -08:00
clkFreq = __HIRC;
break;
default:
clkFreq = 0;
break;
}
SystemCoreClock = clkFreq / ((CLK->CLKDIV & CLK_CLKDIV_HCLK_N_Msk) + 1);
CyclesPerUs = SystemCoreClock / 1000000;
}
/**
2020-12-01 16:44:33 -08:00
* @brief Get PLL clock frequency
* @param None
* @return PLL frequency
* @details This function get PLL frequency. The frequency unit is Hz.
*/
static inline uint32_t get_pll_clock_freq(void)
{
uint32_t PllReg;
uint32_t pllFIN, pllNF, pllNR, pllNO;
PllReg = CLK->PLLCON;
if (PllReg & (CLK_PLLCON_PD_Msk | CLK_PLLCON_OE_Msk)) {
PllClock = 0; /* PLL is in power down mode or fix low */
} else {
if (PllReg & NUC123_PLLSRC_HSI) {
pllFIN = __HIRC; /* Use HXT for PLL clock */
} else {
pllFIN = __HXT; /* Use HXT for PLL clock */
}
if (PllReg & CLK_PLLCON_BP_Msk) {
PllClock = pllFIN;
} else {
switch (((PllReg & CLK_PLLCON_OUT_DV_Msk) >> CLK_PLLCON_OUT_DV_Pos)) {
2020-12-11 09:39:59 -08:00
case 0: /* OUT_DIV == 00 : NO = 1 */
2020-12-01 16:44:33 -08:00
pllNO = 1;
break;
2020-12-11 09:39:59 -08:00
case 3: /* OUT_DIV == 11 : NO = 4 */
2020-12-01 16:44:33 -08:00
pllNO = 4;
break;
2020-12-11 09:39:59 -08:00
default: /* OUT_DIV == 01 or 10 : NO = 2 */
2020-12-01 16:44:33 -08:00
pllNO = 2;
break;
}
pllNF = ((PllReg & CLK_PLLCON_FB_DV_Msk) >> CLK_PLLCON_FB_DV_Pos) + 2;
pllNR = ((PllReg & CLK_PLLCON_IN_DV_Msk) >> CLK_PLLCON_IN_DV_Pos) + 2;
2020-12-11 09:39:59 -08:00
/* Shift to avoid overflow condition */
2020-12-01 16:44:33 -08:00
PllClock = (((pllFIN >> 2) * pllNF) / (pllNR * pllNO) << 2);
}
}
return PllClock;
}
/**
2020-12-01 16:44:33 -08:00
* @brief Wait for stable clock
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* @description Always wait around 300ms for clock to be stable
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
*/
static uint32_t wait_for_clock_ready(uint32_t clkMask)
{
int32_t timeout = 2180000;
while (timeout-- > 0) {
if ((CLK->CLKSTATUS & clkMask) == clkMask) {
return 1;
}
}
return 0;
}
/** @brief Set system HCLK
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* @description Setup HCLK source and divider
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* Always switch to a known stable clock source before changing a
* system clock, to avoid issues related to the original clock's
* speed/settings.
2020-12-11 09:39:59 -08:00
*
*/
2020-12-01 16:44:33 -08:00
static void set_HCLK(uint32_t clkSource, uint32_t clkDivider)
{
uint32_t stableHIRC;
2020-12-01 16:44:33 -08:00
/* Read HIRC clock source stable flag */
stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
2020-12-01 16:44:33 -08:00
/* Setup __HIRC */
CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
2020-12-01 16:44:33 -08:00
wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
2020-12-01 16:44:33 -08:00
/* Use __HIRC as HCLK, temporarily */
CLK->CLKSEL0 =
(CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLK_S_Msk)) | NUC123_HCLKSRC_HSI;
2020-12-01 16:44:33 -08:00
/* Set new clock divider */
CLK->CLKDIV = (CLK->CLKDIV & (~CLK_CLKDIV_HCLK_N_Msk)) | clkDivider;
2020-12-01 16:44:33 -08:00
/* Switch HCLK to new HCLK source */
CLK->CLKSEL0 = (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLK_S_Msk)) | clkSource;
2020-12-01 16:44:33 -08:00
/* Update System Core Clock */
SystemCoreClockUpdate();
2020-12-01 16:44:33 -08:00
/* Disable HIRC if HIRC was disabled before we started */
if (stableHIRC == 0) {
CLK->PWRCON &= ~CLK_PWRCON_OSC22M_EN_Msk;
}
}
2020-12-01 16:44:33 -08:00
static uint32_t enable_pll(uint32_t pllSrc, uint32_t pllFreq)
{
/* Disable PLL first to avoid unstable when setting PLL. */
CLK->PLLCON = CLK_PLLCON_PD_Msk;
2020-12-01 16:44:33 -08:00
/* Check and setup correct clock source */
switch (pllSrc) {
case NUC123_PLLSRC_HSE:
/* Use HXT clock */
CLK->PWRCON |= CLK_PWRCON_XTL12M_EN_Msk;
2020-12-01 16:44:33 -08:00
/* Wait for stable HXT */
wait_for_clock_ready(CLK_CLKSTATUS_XTL12M_STB_Msk);
2020-12-01 16:44:33 -08:00
break;
case NUC123_PLLSRC_HSI:
/* Use HIRC clock */
CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
2020-12-01 16:44:33 -08:00
/* Wait for stable HIRC */
wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
break;
}
/**
* Calculate best PLL variables from requested frequency
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* See NUC123 Technical Reference Manual 5.4.8 PLL Control Register Description, page 124
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* NF 1
* FOUT = FIN x -- x --
* NR NO
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
*/
uint32_t NO = 0;
uint32_t NR = 0;
uint32_t clkCalc = 0;
/* Set "NO" for requested frequency */
/* We're using "NO" first to set the PLLCON - so make it "NO" - 1; */
if (pllFreq >= FREQ_25MHZ && pllFreq <= FREQ_50MHZ) {
/* Low frequency - use full variable headroom */
pllFreq <<= 2;
NO = 3;
} else if (pllFreq > FREQ_50MHZ && pllFreq <= FREQ_100MHZ) {
/* Medium frequency - use full variable headroom */
pllFreq <<= 1;
NO = 1;
} else if (pllFreq > FREQ_100MHZ && pllFreq <= FREQ_200MHZ) {
/* High frequency - full variable headroom already used */
NO = 0;
} else {
/* Frequency out of range - use default PLL settings
*
* See NUC123 Technical Reference Manual PLL COntrol Register Description, page 124
* The default value: 0xC22E
* FIN = 12 MHz
* NR = (1+2) = 3
* NF = (46+2) = 48
* NO = 4
* FOUT = 12/4 x 48 x 1/3 = 48 MHz
*/
if (pllSrc == NUC123_PLLSRC_HSE) {
CLK->PLLCON = 0xC22E;
} else {
CLK->PLLCON = 0xD66F;
}
/* Wait for stable PLL clock */
wait_for_clock_ready(CLK_CLKSTATUS_PLL_STB_Msk);
return get_pll_clock_freq();
}
/* Setup "NR" and clkCalc */
switch (pllSrc) {
case NUC123_PLLSRC_HSE:
NR = 2;
clkCalc = __HXT;
break;
case NUC123_PLLSRC_HSI:
NR = 4;
clkCalc = __HIRC;
break;
}
/**
* Loop to calculate best/lowest NR (between 0 or 2 and 31) and best/lowest NF (between 0 and 511)
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* Best results are off-by-2 until final equation calculation (to allow use in PLLCON)
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
*/
uint32_t bestNR = 0;
uint32_t bestNF = 0;
uint32_t minLimit = -1;
while (NR <= 33) {
uint32_t tmpCalc1 = clkCalc / NR;
if (tmpCalc1 > 1600000 && tmpCalc1 < 16000000) {
uint32_t NF = 2;
while (NF <= 513) {
uint32_t tmpCalc2 = tmpCalc1 * NF;
if (tmpCalc2 >= 100000000 && tmpCalc2 <= 200000000) {
uint32_t tmpCalc3;
if (tmpCalc2 > pllFreq) {
tmpCalc3 = tmpCalc2 - pllFreq;
} else {
tmpCalc3 = pllFreq - tmpCalc2;
}
if (tmpCalc3 < minLimit) {
minLimit = tmpCalc3;
bestNF = NF;
bestNR = NR;
/* Stop NF calc loop when minLimit tends back to 0 */
if (minLimit == 0)
break;
}
}
NF++;
}
}
NR++;
}
/* Enable and apply new PLL setting. */
CLK->PLLCON = pllSrc | (NO << 14) | ((bestNR - 2) << 9) | (bestNF - 2);
/* Wait for stable PLL clock */
wait_for_clock_ready(CLK_CLKSTATUS_PLL_STB_Msk);
/* Return equation result */
return (clkCalc / ((NO + 1) * bestNR) * bestNF);
}
2020-12-01 16:44:33 -08:00
/** @brief Set Core Clock
2020-12-11 09:39:59 -08:00
*
2020-12-01 16:44:33 -08:00
* @description Set the core system clock some reference speed (Hz).
2020-12-11 09:39:59 -08:00
* This should be between 25MHz and 72MHz for the NUC123SD4AN0.
2020-12-11 09:17:24 -08:00
*
2020-12-01 16:44:33 -08:00
* Use either the HXT (exact) or HIRC (nearest using 22.1184MHz)
* as the clock source.
2020-12-11 09:39:59 -08:00
*
*/
2020-12-01 16:44:33 -08:00
static uint32_t set_core_clock(uint32_t clkCore)
{
uint32_t stableHIRC;
2020-12-01 16:44:33 -08:00
/* Read HIRC clock source stable flag */
stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
2020-12-01 16:44:33 -08:00
/* Setup __HIRC */
CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
2020-12-01 16:44:33 -08:00
wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
2020-12-01 16:44:33 -08:00
/* Use __HIRC as HCLK temporarily */
CLK->CLKSEL0 |= CLK_CLKSEL0_HCLK_S_Msk;
CLK->CLKDIV &= (~CLK_CLKDIV_HCLK_N_Msk);
2020-12-01 16:44:33 -08:00
/* Is HXT stable ? */
if (CLK->CLKSTATUS & CLK_CLKSTATUS_XTL12M_STB_Msk) {
/* Use __HXT as PLL source */
2020-12-12 18:12:58 -08:00
clkCore = enable_pll(NUC123_PLLSRC_HSE, (2 * clkCore));
2020-12-01 16:44:33 -08:00
} else {
/* Use __HIRC as PLL source */
2020-12-12 18:12:58 -08:00
clkCore = enable_pll(NUC123_PLLSRC_HSI, (2 * clkCore));
2020-12-01 16:44:33 -08:00
/* Read HIRC clock source stable flag again (since we're using it now) */
stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
}
2020-12-01 16:44:33 -08:00
/* Set HCLK clock source to PLL */
2020-12-12 18:12:58 -08:00
set_HCLK(NUC123_HCLKSRC_PLL_2, CLK_CLKDIV_HCLK(1));
2020-12-01 16:44:33 -08:00
/* Disable HIRC if HIRC was disabled before we started */
if (stableHIRC == 0) {
CLK->PWRCON &= ~CLK_PWRCON_OSC22M_EN_Msk;
}
2020-12-01 16:44:33 -08:00
/* Return actual HCLK frequency is PLL frequency divide 2 */
return (clkCore >> 1);
}
2020-12-01 16:44:33 -08:00
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level HAL driver initialization.
*
* @notapi
*/
2020-12-01 16:44:33 -08:00
void hal_lld_init(void)
{
if (!clock_initialized) {
NUC123_clock_init();
}
}
2020-12-01 16:44:33 -08:00
void NUC123_clock_init(void)
{
clock_initialized = TRUE;
UNLOCKREG();
2020-12-01 16:44:33 -08:00
/* Always initialize HSI and go from there, things can change later */
/* TODO: Technically this could also be the crystal, figure out how to allow
* config in linker? */
/* Enable HSI */
CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
2020-12-01 16:44:33 -08:00
set_HCLK(NUC123_HCLKSRC_HSI, CLK_CLKDIV_HCLK(1));
2020-12-01 16:44:33 -08:00
#if NUC123_HSE_ENABLED
2020-12-01 16:44:33 -08:00
SYS->GPF_MFP |= (SYS_GPF_MFP_GPF_MFP0_Msk | SYS_GPF_MFP_GPF_MFP1_Msk);
2020-12-01 16:44:33 -08:00
CLK->PWRCON |= CLK_PWRCON_XTL12M_EN_Msk;
wait_for_clock_ready(CLK_CLKSTATUS_XTL12M_STB_Msk);
2020-12-01 16:44:33 -08:00
#endif /* NUC123_HSE_ENABLED */
2020-12-01 16:44:33 -08:00
#if NUC123_LSI_ENABLED
CLK->PWRCON |= CLK_PWRCON_IRC10K_EN_Msk;
wait_for_clock_ready(CLK_CLKSTATUS_IRC10K_STB_Msk);
#endif /* NUC123_LSI_ENABLED */
2020-12-01 16:44:33 -08:00
set_core_clock(NUC123_HCLK);
SystemCoreClock = NUC123_HCLK;
2020-12-01 16:44:33 -08:00
LOCKREG();
}
/** @} */