Philipp Hug has uploaded this change for review. ( https://review.coreboot.org/27439
Change subject: sifive/fu540: PLL + clock initialization ......................................................................
sifive/fu540: PLL + clock initialization
Change-Id: Iba0669e08940e373aaf42cbba3a1ceffd68a4f52 --- A src/soc/sifive/fu540/clock.c A src/soc/sifive/fu540/include/soc/clock.h 2 files changed, 198 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/39/27439/1
diff --git a/src/soc/sifive/fu540/clock.c b/src/soc/sifive/fu540/clock.c new file mode 100644 index 0000000..1fa850c --- /dev/null +++ b/src/soc/sifive/fu540/clock.c @@ -0,0 +1,174 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2018 Philipp Hug philipp@hug.cx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/barrier.h> +#include <arch/io.h> +#include <console/console.h> +#include <soc/clock.h> +#include <soc/addressmap.h> +#include <stdlib.h> +#include <stdint.h> + +struct prci_ctlr { + u32 hfxosccfg; /* offset 0x00 */ + u32 corepllcfg0; /* offset 0x04 */ + u32 reserved08; /* offset 0x08 */ + u32 ddrpllcfg0; /* offset 0x0c */ + u32 ddrpllcfg1; /* offset 0x10 */ + u32 reserved14; /* offset 0x14 */ + u32 reserved18; /* offset 0x18 */ + u32 gemgxlpllcfg0; /* offset 0x1c */ + u32 gemgxlpllcfg1; /* offset 0x20 */ + u32 coreclksel; /* offset 0x24 */ + u32 devicesresetreg; /* offset 0x28 */ +}; + +static struct prci_ctlr *prci = (void *)FU540_PRCI; + +#define PRCI_CORECLK_MASK 1 +#define PRCI_CORECLK_CORE_PLL 0 +#define PRCI_CORECLK_HFCLK 1 + +#define PRCI_COREPLLCFG0_LOCK (1u << 31) +#define PRCI_COREPLLCFG0_DIVR_SHIFT 0 +#define PRCI_COREPLLCFG0_DIVF_SHIFT 6 +#define PRCI_COREPLLCFG0_DIVQ_SHIFT 15 +#define PRCI_COREPLLCFG0_RANGE_SHIFT 18 +#define PRCI_COREPLLCFG0_BYPASS_SHIFT 24 +#define PRCI_COREPLLCFG0_DIVR_MASK (0x03f << PRCI_COREPLLCFG0_DIVR_SHIFT) +#define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT) +#define PRCI_COREPLLCFG0_DIVQ_MASK (0x007 << PRCI_COREPLLCFG0_DIVQ_SHIFT) +#define PRCI_COREPLLCFG0_RANGE_MASK (0x07 << PRCI_COREPLLCFG0_RANGE_SHIFT) +#define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT) + +#define PRCI_DDRPLLCFG0_LOCK (1u << 31) +#define PRCI_DDRPLLCFG0_DIVR_SHIFT 0 +#define PRCI_DDRPLLCFG0_DIVF_SHIFT 6 +#define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15 +#define PRCI_DDRPLLCFG0_RANGE_SHIFT 18 +#define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24 +#define PRCI_DDRPLLCFG0_DIVR_MASK (0x03f << PRCI_DDRPLLCFG0_DIVR_SHIFT) +#define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT) +#define PRCI_DDRPLLCFG0_DIVQ_MASK (0x007 << PRCI_DDRPLLCFG0_DIVQ_SHIFT) +#define PRCI_DDRPLLCFG0_RANGE_MASK (0x07 << PRCI_DDRPLLCFG0_RANGE_SHIFT) +#define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT) +#define PRCI_DDRPLLCFG1_MASK (1u << 31) + +#define PRCI_CORECLK_DIVR 0 +#define PRCI_CORECLK_DIVF 59 +#define PRCI_CORECLK_DIVQ 2 +#define PRCI_CORECLK_RANGE 4 + +#define PRCI_DDRCLK_DIVR 0 +#define PRCI_DDRCLK_DIVF 59 +#define PRCI_DDRCLK_DIVQ 5 +#define PRCI_DDRCLK_RANGE 4 + +#define PRCI_RESET_DDR_CTRL_RST_N (1 << 0) +#define PRCI_RESET_DDR_AXI_RST_N (1 << 1) +#define PRCI_RESET_DDR_AHB_RST_N (1 << 2) +#define PRCI_RESET_DDR_PHY_RST_N (1 << 3) +#define PRCI_RESET_GEMGXL_RST_N (1 << 5) + +// 33.33 Mhz after reset +#define FU540_BASE_FQY 33330 + +static void init_pll_core(void) +{ + clrsetbits_le32(&prci->coreclksel, PRCI_CORECLK_MASK, PRCI_CORECLK_HFCLK); + + u32 c = read32(&prci->corepllcfg0); + clrsetbits_le32(&c, PRCI_COREPLLCFG0_DIVR_MASK + | PRCI_COREPLLCFG0_DIVF_MASK | PRCI_COREPLLCFG0_DIVQ_MASK + | PRCI_COREPLLCFG0_RANGE_MASK | PRCI_COREPLLCFG0_BYPASS_MASK, + (PRCI_CORECLK_DIVR << PRCI_COREPLLCFG0_DIVR_SHIFT) + & (PRCI_CORECLK_DIVF << PRCI_COREPLLCFG0_DIVF_SHIFT) + & (PRCI_CORECLK_DIVQ << PRCI_COREPLLCFG0_DIVQ_SHIFT) + & (PRCI_CORECLK_RANGE << PRCI_COREPLLCFG0_RANGE_SHIFT)); + write32(&prci->corepllcfg0, c); + + // wait for PLL lock + while (!(read32(&prci->corepllcfg0) & PRCI_COREPLLCFG0_LOCK)) + ; /* TODO: implement a timeout */ + + clrsetbits_le32(&prci->coreclksel, PRCI_CORECLK_MASK, PRCI_CORECLK_CORE_PLL); +} + +static void init_pll_ddr(void) +{ + u32 cfg1 = read32(&prci->ddrpllcfg1); + clrbits_le32(&cfg1, PRCI_DDRPLLCFG1_MASK); + write32(&prci->ddrpllcfg1, cfg1); + + u32 c = read32(&prci->ddrpllcfg0); + clrsetbits_le32(&c, PRCI_DDRPLLCFG0_DIVR_MASK + | PRCI_DDRPLLCFG0_DIVF_MASK | PRCI_DDRPLLCFG0_DIVQ_MASK + | PRCI_DDRPLLCFG0_RANGE_MASK | PRCI_DDRPLLCFG0_BYPASS_MASK, + (PRCI_DDRCLK_DIVR << PRCI_DDRPLLCFG0_DIVR_SHIFT) + & (PRCI_DDRCLK_DIVF << PRCI_DDRPLLCFG0_DIVF_SHIFT) + & (PRCI_DDRCLK_DIVQ << PRCI_DDRPLLCFG0_DIVQ_SHIFT) + & (PRCI_DDRCLK_RANGE << PRCI_DDRPLLCFG0_RANGE_SHIFT)); + write32(&prci->ddrpllcfg0, c); + + // wait for PLL lock + while (!(read32(&prci->ddrpllcfg0) & PRCI_DDRPLLCFG0_LOCK)) + ; /* TODO: implement a timeout */ + + setbits_le32(&cfg1, PRCI_DDRPLLCFG1_MASK); + write32(&prci->ddrpllcfg1, cfg1); +} + +int clock_get_coreclk_khz(void) +{ + if (read32(&prci->coreclksel) & PRCI_CORECLK_MASK) { + return FU540_BASE_FQY; + } else { + u32 cfg = read32(&prci->corepllcfg0); + u32 divr = (cfg & PRCI_COREPLLCFG0_DIVR_MASK) + >> PRCI_COREPLLCFG0_DIVR_SHIFT; + u32 divf = (cfg & PRCI_COREPLLCFG0_DIVF_MASK) + >> PRCI_COREPLLCFG0_DIVF_SHIFT; + u32 divq = (cfg & PRCI_COREPLLCFG0_DIVQ_MASK) + >> PRCI_COREPLLCFG0_DIVQ_SHIFT; + + printk(BIOS_SPEW, "clk: r=%d f=%d q=%d\n", divr, divf, divq); + return FU540_BASE_FQY + * 2 * (divf + 1) + / (divr + 1) + / (1 << divq); + } +} + +static void my_sdram_init(void) +{ + setbits_le32(&prci->devicesresetreg, PRCI_RESET_DDR_CTRL_RST_N); + mb(); + setbits_le32(&prci->devicesresetreg, PRCI_RESET_DDR_AXI_RST_N); + setbits_le32(&prci->devicesresetreg, PRCI_RESET_DDR_AHB_RST_N); + setbits_le32(&prci->devicesresetreg, PRCI_RESET_DDR_PHY_RST_N); + for(int i = 0; i<256; i++) + mb(); +} + +void clock_init(void) +{ + init_pll_core(); + + // put all peripherals in reset + write32(&prci->devicesresetreg, 0); + init_pll_ddr(); + + my_sdram_init(); +} diff --git a/src/soc/sifive/fu540/include/soc/clock.h b/src/soc/sifive/fu540/include/soc/clock.h new file mode 100644 index 0000000..cc868e3 --- /dev/null +++ b/src/soc/sifive/fu540/include/soc/clock.h @@ -0,0 +1,24 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2018 Philipp Hug philipp@hug.cx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SOC_SIFIVE_HIFIVE_U_CLOCK_H__ +#define __SOC_SIFIVE_HIFIVE_U_CLOCK_H__ + +#define PRCI_CORECLKSEL_CORECLKSEL 1 + +void clock_init(void); +int clock_get_coreclk_khz(void); + +#endif /* __SOC_SIFIVE_HIFIFE_U_CLOCK_H__ */