diff --git a/boards/riscv/hifive1_revb/hifive1_revb.dts b/boards/riscv/hifive1_revb/hifive1_revb.dts index b7830fed2c6..f329286bb08 100644 --- a/boards/riscv/hifive1_revb/hifive1_revb.dts +++ b/boards/riscv/hifive1_revb/hifive1_revb.dts @@ -71,6 +71,11 @@ }; }; +&coreclk { + clock-frequency = ; + status = "okay"; +}; + &gpio0 { status = "okay"; }; diff --git a/soc/riscv/riscv-privilege/sifive-freedom/fe310_clock.c b/soc/riscv/riscv-privilege/sifive-freedom/fe310_clock.c index 837643f5c3c..5a42ca66fed 100644 --- a/soc/riscv/riscv-privilege/sifive-freedom/fe310_clock.c +++ b/soc/riscv/riscv-privilege/sifive-freedom/fe310_clock.c @@ -8,19 +8,50 @@ #include #include "fe310_prci.h" -BUILD_ASSERT(MHZ(16) == DT_PROP(DT_NODELABEL(coreclk), clock_frequency), - "Unsupported CORECLK frequency"); +#define CORECLK_HZ (DT_PROP(DT_NODELABEL(coreclk), clock_frequency)) BUILD_ASSERT(DT_PROP(DT_NODELABEL(tlclk), clock_div) == 1, "Unsupported TLCLK divider"); -/* Selects the 16MHz oscillator on the HiFive1 board, which provides a clock - * that's accurate enough to actually drive serial ports off of. - */ static int fe310_clock_init(const struct device *dev) { ARG_UNUSED(dev); - PRCI_REG(PRCI_PLLCFG) = PLL_REFSEL(1) | PLL_BYPASS(1); + /* + * HFXOSC (16 MHz) is used to produce coreclk (and therefore tlclk / + * peripheral clock). This code supports the following frequencies: + * - 16 MHz (bypass HFPLL). + * - 48 MHz - 320 MHz, in 8 MHz steps (use HFPLL). + */ + BUILD_ASSERT(MHZ(16) == CORECLK_HZ || + (MHZ(48) <= CORECLK_HZ && MHZ(320) >= CORECLK_HZ && + (CORECLK_HZ % MHZ(8)) == 0), + "Unsupported CORECLK frequency"); + + uint32_t prci; + + if (MHZ(16) == CORECLK_HZ) { + /* Bypass HFPLL. */ + prci = PLL_REFSEL(1) | PLL_BYPASS(1); + } else { + /* refr = 8 MHz. */ + const int pll_r = 0x1; + int pll_q; + + /* Select Q divisor to produce vco on [384 MHz, 768 MHz]. */ + if (MHZ(768) / 8 >= CORECLK_HZ) { + pll_q = 0x3; + } else if (MHZ(768) / 4 >= CORECLK_HZ) { + pll_q = 0x2; + } else { + pll_q = 0x1; + } + /* Select F multiplier to produce vco target. */ + const int pll_f = ((CORECLK_HZ / MHZ(1)) >> (4 - pll_q)) - 1; + + prci = PLL_REFSEL(1) | PLL_R(pll_r) | PLL_F(pll_f) | PLL_Q(pll_q); + } + + PRCI_REG(PRCI_PLLCFG) = prci; PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1); PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1);