blob: e6bbee93689f7bf93e649e3e778e94b3b03b4600 [file] [log] [blame]
/*
* Copyright (c) 2021 Katsuhiro Suzuki
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/init.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/util.h>
#include "fu740_prci.h"
BUILD_ASSERT(MHZ(1000) == DT_PROP(DT_NODELABEL(coreclk), clock_frequency),
"Unsupported CORECLK frequency");
BUILD_ASSERT(KHZ(125125) == DT_PROP(DT_NODELABEL(pclk), clock_frequency),
"Unsupported PCLK frequency");
static inline void wait_controller_cycle(void)
{
/* HACK to get the '1 full controller clock cycle'. */
__asm__ volatile ("fence");
}
/*
* Switch the clock source
* - core: to 1GHz PLL (CORE_PLL) from 26MHz oscillator (HFCLK)
* - peri: to 250MHz PLL (HFPCLKPLL) from HFCLK
* - ddr: to 923MHz PLL (DDRPLL) from HFCLK (half of the data rate)
* on the HiFive Unmatched board.
*
* Note: Valid PLL VCO range is 2400MHz to 4800MHz
*/
static int fu740_clock_init(void)
{
PRCI_REG(PRCI_COREPLLCFG) =
PLL_R(0) | /* input divider: Fin / (0 + 1) = 26MHz */
PLL_F(76) | /* VCO: 2 x (76 + 1) = 154 = 4004MHz */
PLL_Q(2) | /* output divider: VCO / 2^2 = 1001MHz */
PLL_RANGE(PLL_RANGE_18MHZ) | /* 18MHz <= post divr(= 26MHz) < 30MHz */
PLL_BYPASS(PLL_BYPASS_DISABLE) |
PLL_FSE(PLL_FSE_INTERNAL);
while ((PRCI_REG(PRCI_COREPLLCFG) & PLL_LOCK(1)) == 0)
;
/* Switch CORE_CLK to CORE_PLL from HFCLK */
PRCI_REG(PRCI_COREPLLSEL) = COREPLLSEL_SEL(COREPLLSEL_COREPLL);
PRCI_REG(PRCI_CORECLKSEL) = CLKSEL_SEL(CLKSEL_PLL);
PRCI_REG(PRCI_HFPCLKPLLCFG) =
PLL_R(0) | /* input divider: Fin / (0 + 1) = 26MHz */
PLL_F(76) | /* VCO: 2 x (76 + 1) = 154 = 4004MHz */
PLL_Q(4) | /* output divider: VCO / 2^4 = 250.25MHz */
PLL_RANGE(PLL_RANGE_18MHZ) | /* 18MHz <= post divr(= 26MHz) < 30MHz */
PLL_BYPASS(PLL_BYPASS_DISABLE) |
PLL_FSE(PLL_FSE_INTERNAL);
while ((PRCI_REG(PRCI_HFPCLKPLLCFG) & PLL_LOCK(1)) == 0)
;
/* Switch PCLK to HFPCLKPLL/2 from HFCLK/2 */
PRCI_REG(PRCI_HFPCLKPLLOUTDIV) = OUTDIV_PLLCKE(OUTDIV_PLLCKE_ENA);
PRCI_REG(PRCI_HFPCLKPLLSEL) = CLKSEL_SEL(CLKSEL_PLL);
PRCI_REG(PRCI_DDRPLLCFG) =
PLL_R(0) | /* input divider: Fin / (0 + 1) = 26MHz */
PLL_F(70) | /* VCO: 2 x (70 + 1) = 154 = 1872MHz */
PLL_Q(2) | /* output divider: VCO / 2^2 = 936MHz */
PLL_RANGE(PLL_RANGE_18MHZ) |
PLL_BYPASS(PLL_BYPASS_DISABLE) |
PLL_FSE(PLL_FSE_INTERNAL);
while ((PRCI_REG(PRCI_DDRPLLCFG) & PLL_LOCK(1)) == 0)
;
PRCI_REG(PRCI_DDRPLLOUTDIV) |= OUTDIV_PLLCKE(OUTDIV_PLLCKE_ENA);
PRCI_REG(PRCI_DEVICESRESETN) |= DEVICERESETN_DDRCTRL;
wait_controller_cycle();
/* Release DDR reset */
PRCI_REG(PRCI_DEVICESRESETN) |= DEVICERESETN_DDRAXI |
DEVICERESETN_DDRAHB |
DEVICERESETN_DDRPHY;
wait_controller_cycle();
/*
* These take like 16 cycles to actually propagate. We can't go sending stuff before they
* come out of reset. So wait.
*/
for (int i = 0; i < 256; i++) {
__asm__ volatile ("nop");
}
return 0;
}
SYS_INIT(fu740_clock_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);