/* * Copyright (C) 2017, 2019 Ken Rabold, JP Bonn * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup cpu_fe310 * @{ * * @file cpu.c * @brief Implementation of the clock initialization for SiFive FE310 * * @author Ken Rabold * @} */ #include "clk.h" #include "cpu.h" #include "periph_conf.h" #include "vendor/prci_driver.h" #if IS_ACTIVE(CONFIG_USE_CLOCK_HFROSC) || IS_ACTIVE(CONFIG_USE_CLOCK_HFROSC_PLL) uint32_t cpu_coreclk = 0; #endif void fe310_clock_init(void) { /* Ensure that we aren't running off the PLL before we mess with it. */ if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) { /* Make sure the HFROSC is running at its default setting */ /* It is OK to change this even if we are running off of it.*/ PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(4) | ROSC_TRIM(16) | ROSC_EN(1)); /* Wait for HFROSC to be ready */ while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0) {} /* Don't use PLL clock source */ PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(PLL_SEL_PLL); } if (IS_ACTIVE(CONFIG_USE_CLOCK_HFXOSC) || IS_ACTIVE(CONFIG_USE_CLOCK_HFXOSC_PLL)) { /* Ensure HFXOSC is enabled */ PRCI_REG(PRCI_HFXOSCCFG) = XOSC_EN(1); /* Wait for HFXOSC to become ready */ while ((PRCI_REG(PRCI_HFXOSCCFG) & XOSC_RDY(1)) == 0) {} /* Select HFXOSC as reference frequency and bypass PLL */ PRCI_REG(PRCI_PLLCFG) = PLL_REFSEL(PLL_REFSEL_HFXOSC) | PLL_BYPASS(1); if (IS_ACTIVE(CONFIG_USE_CLOCK_HFXOSC_PLL)) { /* Divide final output frequency by 1 */ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); /* Configure PLL */ PRCI_REG(PRCI_PLLCFG) |= PLL_R(CONFIG_CLOCK_PLL_R) | PLL_F( CONFIG_CLOCK_PLL_F) | PLL_Q(CONFIG_CLOCK_PLL_Q); /* Disable PLL Bypass */ PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); /* Now it is safe to check for PLL Lock */ while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0) {} } /* Switch over to PLL Clock source */ PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(PLL_SEL_PLL); /* Turn off the HFROSC */ PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1); } else if (IS_ACTIVE(CONFIG_USE_CLOCK_HFROSC_PLL)) { PRCI_set_hfrosctrim_for_f_cpu(CONFIG_CLOCK_DESIRED_FREQUENCY, PRCI_FREQ_UNDERSHOOT); } else { /* Clock HFROSC */ /* Disable Bypass */ PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); /* Configure trim and divider values of HFROSC */ PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(CONFIG_CLOCK_HFROSC_DIV) | ROSC_TRIM(CONFIG_CLOCK_HFROSC_TRIM) | ROSC_EN(1)); /* Wait for HFROSC to be ready */ while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0) {} /* Don't use PLL clock source */ PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(PLL_SEL_PLL); } #if IS_ACTIVE(CONFIG_USE_CLOCK_HFROSC) || IS_ACTIVE(CONFIG_USE_CLOCK_HFROSC_PLL) /* Ignore the first run (for icache reasons) */ cpu_coreclk = PRCI_measure_mcycle_freq(3000, RTC_FREQ); cpu_coreclk = PRCI_measure_mcycle_freq(3000, RTC_FREQ); #endif }