coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
clock.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <device/mmio.h>
4 #include <console/console.h>
5 #include <soc/clock.h>
6 #include <soc/addressmap.h>
7 #include <stdint.h>
8 
9 // 33.33 Mhz after reset
10 #define FU540_BASE_FQY 33330
11 
12 struct prci_ctlr {
13  u32 hfxosccfg; /* offset 0x00 */
14  u32 corepllcfg0; /* offset 0x04 */
15  u32 reserved08; /* offset 0x08 */
16  u32 ddrpllcfg0; /* offset 0x0c */
17  u32 ddrpllcfg1; /* offset 0x10 */
18  u32 reserved14; /* offset 0x14 */
19  u32 reserved18; /* offset 0x18 */
20  u32 gemgxlpllcfg0; /* offset 0x1c */
21  u32 gemgxlpllcfg1; /* offset 0x20 */
22  u32 coreclksel; /* offset 0x24 */
23  u32 devicesresetreg; /* offset 0x28 */
24 };
25 
26 static struct prci_ctlr *prci = (void *)FU540_PRCI;
27 
28 #define PRCI_CORECLK_MASK 1
29 #define PRCI_CORECLK_CORE_PLL 0
30 #define PRCI_CORECLK_HFCLK 1
31 
32 #define PRCI_PLLCFG_LOCK (1u << 31)
33 #define PRCI_PLLCFG_DIVR_SHIFT 0
34 #define PRCI_PLLCFG_DIVF_SHIFT 6
35 #define PRCI_PLLCFG_DIVQ_SHIFT 15
36 #define PRCI_PLLCFG_RANGE_SHIFT 18
37 #define PRCI_PLLCFG_BYPASS_SHIFT 24
38 #define PRCI_PLLCFG_FSE_SHIFT 25
39 #define PRCI_PLLCFG_DIVR_MASK (0x03f << PRCI_PLLCFG_DIVR_SHIFT)
40 #define PRCI_PLLCFG_DIVF_MASK (0x1ff << PRCI_PLLCFG_DIVF_SHIFT)
41 #define PRCI_PLLCFG_DIVQ_MASK (0x007 << PRCI_PLLCFG_DIVQ_SHIFT)
42 #define PRCI_PLLCFG_RANGE_MASK (0x07 << PRCI_PLLCFG_RANGE_SHIFT)
43 #define PRCI_PLLCFG_BYPASS_MASK (0x1 << PRCI_PLLCFG_BYPASS_SHIFT)
44 #define PRCI_PLLCFG_FSE_MASK (0x1 << PRCI_PLLCFG_FSE_SHIFT)
45 
46 #define PRCI_DDRPLLCFG1_MASK (1u << 31)
47 
48 #define PRCI_GEMGXLPPLCFG1_MASK (1u << 31)
49 
50 #define PRCI_CORECLKSEL_CORECLKSEL 1
51 
52 #define PRCI_DEVICESRESET_DDR_CTRL_RST_N(x) (((x) & 0x1) << 0)
53 #define PRCI_DEVICESRESET_DDR_AXI_RST_N(x) (((x) & 0x1) << 1)
54 #define PRCI_DEVICESRESET_DDR_AHB_RST_N(x) (((x) & 0x1) << 2)
55 #define PRCI_DEVICESRESET_DDR_PHY_RST_N(x) (((x) & 0x1) << 3)
56 #define PRCI_DEVICESRESET_GEMGXL_RST_N(x) (((x) & 0x1) << 5)
57 
58 /* Clock initialization should only be done in romstage. */
59 #if ENV_ROMSTAGE
60 struct pll_settings {
61  unsigned int divr:6;
62  unsigned int divf:9;
63  unsigned int divq:3;
64  unsigned int range:3;
65  unsigned int bypass:1;
66  unsigned int fse:1;
67 };
68 
69 static void configure_pll(u32 *reg, const struct pll_settings *s)
70 {
71  // Write the settings to the register
72  u32 c = read32(reg);
77  (s->divr << PRCI_PLLCFG_DIVR_SHIFT)
78  | (s->divf << PRCI_PLLCFG_DIVF_SHIFT)
79  | (s->divq << PRCI_PLLCFG_DIVQ_SHIFT)
80  | (s->range << PRCI_PLLCFG_RANGE_SHIFT)
81  | (s->bypass << PRCI_PLLCFG_BYPASS_SHIFT)
82  | (s->fse << PRCI_PLLCFG_FSE_SHIFT));
83  write32(reg, c);
84 
85  // Wait for PLL lock
86  while (!(read32(reg) & PRCI_PLLCFG_LOCK))
87  ; /* TODO: implement a timeout */
88 }
89 
90 /*
91  * Set coreclk according to the SiFive FU540-C000 Manual
92  * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
93  *
94  * Section 7.1 recommends a frequency of 1.0 GHz (up to 1.5 GHz is possible)
95  *
96  * Section 7.4.2 provides the necessary values:
97  * For example, to setup COREPLL for 1 GHz operation, program divr = 0 (x1),
98  * divf = 59 (4000 MHz VCO), divq = 2 (/4 Output divider)
99  */
100 static const struct pll_settings corepll_settings = {
101  .divr = 0,
102  .divf = 59,
103  .divq = 2,
104  .range = 4,
105  .bypass = 0,
106  .fse = 1,
107 };
108 
109 /*
110  * Section 7.4.3: DDR and Ethernet Subsystem Clocking and Reset
111  *
112  * Unfortunately the documentation example doesn't match the HiFive
113  * Unleashed board settings.
114  * Configuration values taken from SiFive FSBL:
115  * https://github.com/sifive/freedom-u540-c000-bootloader/blob/master/fsbl/main.c
116  *
117  * DDRPLL is set up for 933 MHz output frequency.
118  * divr = 0, divf = 55 (3730 MHz VCO), divq = 2
119  *
120  * GEMGXLPLL is set up for 125 MHz output frequency.
121  * divr = 0, divf = 59 (4000 MHz VCO), divq = 5
122  */
123 static const struct pll_settings ddrpll_settings = {
124  .divr = 0,
125  .divf = 55,
126  .divq = 2,
127  .range = 4,
128  .bypass = 0,
129  .fse = 1,
130 };
131 
132 static const struct pll_settings gemgxlpll_settings = {
133  .divr = 0,
134  .divf = 59,
135  .divq = 5,
136  .range = 4,
137  .bypass = 0,
138  .fse = 1,
139 };
140 
141 static void init_coreclk(void)
142 {
143  // switch coreclk to input reference frequency before modifying PLL
146 
147  configure_pll(&prci->corepllcfg0, &corepll_settings);
148 
149  // switch coreclk to use corepll
152 }
153 
154 static void init_pll_ddr(void)
155 {
156  // disable ddr clock output before reconfiguring the PLL
157  u32 cfg1 = read32(&prci->ddrpllcfg1);
159  write32(&prci->ddrpllcfg1, cfg1);
160 
161  configure_pll(&prci->ddrpllcfg0, &ddrpll_settings);
162 
163  // enable ddr clock output
165  write32(&prci->ddrpllcfg1, cfg1);
166 }
167 
168 static void init_gemgxlclk(void)
169 {
170  u32 cfg1 = read32(&prci->gemgxlpllcfg1);
172  write32(&prci->gemgxlpllcfg1, cfg1);
173 
174  configure_pll(&prci->gemgxlpllcfg0, &gemgxlpll_settings);
175 
177  write32(&prci->gemgxlpllcfg1, cfg1);
178 }
179 
180 #define FU540_UART_DEVICES 2
181 #define FU540_UART_REG_DIV 0x18
182 #define FU540_UART_DIV_VAL 4
183 
184 #define FU540_SPI_DIV 0x00
185 #define FU540_SPI_DIV_VAL 4
186 
187 static void update_peripheral_clock_dividers(void)
188 {
189  write32((uint32_t *)(FU540_QSPI0 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
190  write32((uint32_t *)(FU540_QSPI1 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
191  write32((uint32_t *)(FU540_QSPI2 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
192 
193  for (size_t i = 0; i < FU540_UART_DEVICES; i++)
194  write32((uint32_t *)(FU540_UART(i) + FU540_UART_REG_DIV), FU540_UART_DIV_VAL);
195 }
196 
197 void clock_init(void)
198 {
199  /*
200  * Update the peripheral clock dividers of UART, SPI and I2C to safe
201  * values as we can't put them in reset before changing frequency.
202  */
203  update_peripheral_clock_dividers();
204 
205  init_coreclk();
206 
207  // put DDR and ethernet in reset
209 
210  init_pll_ddr();
211 
212  // The following code and its comments is mostly derived from the SiFive
213  // u540 bootloader.
214  // https://github.com/sifive/freedom-u540-c000-bootloader
215 
216  // get DDR out of reset
218 
219  // HACK to get the '1 full controller clock cycle'.
220  asm volatile ("fence");
221 
222  // get DDR out of reset
228 
229  // HACK to get the '1 full controller clock cycle'.
230  asm volatile ("fence");
231 
232  // These take like 16 cycles to actually propagate. We can't go sending
233  // stuff before they come out of reset. So wait.
234  // TODO: Add a register to read the current reset states, or DDR Control
235  // device?
236  for (int i = 0; i < 256; i++)
237  asm volatile ("nop");
238 
239  init_gemgxlclk();
240 
247 
248  asm volatile ("fence");
249 }
250 #endif /* ENV_ROMSTAGE */
251 
252 /* Get the core clock's frequency, in KHz */
254 {
256  return FU540_BASE_FQY;
257 
258  u32 cfg = read32(&prci->corepllcfg0);
259  u32 divr = (cfg & PRCI_PLLCFG_DIVR_MASK)
261  u32 divf = (cfg & PRCI_PLLCFG_DIVF_MASK)
263  u32 divq = (cfg & PRCI_PLLCFG_DIVQ_MASK)
265 
266  printk(BIOS_SPEW, "clk: r=%d f=%d q=%d\n", divr, divf, divq);
267  return FU540_BASE_FQY
268  * 2 * (divf + 1)
269  / (divr + 1)
270  / (1ul << divq);
271 }
272 
273 /* Get the TileLink clock's frequency, in KHz */
275 {
276  /*
277  * The TileLink bus and most peripherals use tlclk, which is coreclk/2,
278  * as input.
279  */
280 
281  return clock_get_coreclk_khz() / 2;
282 }
static void write32(void *addr, uint32_t val)
Definition: mmio.h:40
static uint32_t read32(const void *addr)
Definition: mmio.h:22
#define printk(level,...)
Definition: stdlib.h:16
#define setbits32(addr, set)
Definition: mmio.h:21
#define clrsetbits32(addr, clear, set)
Definition: mmio.h:16
#define clrbits32(addr, clear)
Definition: mmio.h:26
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
void clock_init(void)
Definition: clock.c:539
#define PRCI_PLLCFG_RANGE_SHIFT
Definition: clock.c:36
#define PRCI_PLLCFG_BYPASS_SHIFT
Definition: clock.c:37
#define PRCI_PLLCFG_RANGE_MASK
Definition: clock.c:42
#define PRCI_CORECLK_MASK
Definition: clock.c:28
#define PRCI_DEVICESRESET_DDR_PHY_RST_N(x)
Definition: clock.c:55
static struct prci_ctlr * prci
Definition: clock.c:26
#define PRCI_DEVICESRESET_DDR_CTRL_RST_N(x)
Definition: clock.c:52
#define FU540_BASE_FQY
Definition: clock.c:10
#define PRCI_CORECLK_CORE_PLL
Definition: clock.c:29
int clock_get_coreclk_khz(void)
Definition: clock.c:253
#define PRCI_PLLCFG_DIVQ_MASK
Definition: clock.c:41
#define PRCI_DEVICESRESET_DDR_AHB_RST_N(x)
Definition: clock.c:54
#define PRCI_CORECLK_HFCLK
Definition: clock.c:30
#define PRCI_DDRPLLCFG1_MASK
Definition: clock.c:46
#define PRCI_PLLCFG_BYPASS_MASK
Definition: clock.c:43
#define PRCI_PLLCFG_DIVF_MASK
Definition: clock.c:40
#define PRCI_PLLCFG_DIVF_SHIFT
Definition: clock.c:34
#define PRCI_PLLCFG_DIVR_SHIFT
Definition: clock.c:33
#define PRCI_PLLCFG_FSE_MASK
Definition: clock.c:44
#define PRCI_GEMGXLPPLCFG1_MASK
Definition: clock.c:48
#define PRCI_PLLCFG_DIVR_MASK
Definition: clock.c:39
#define PRCI_DEVICESRESET_GEMGXL_RST_N(x)
Definition: clock.c:56
#define PRCI_PLLCFG_LOCK
Definition: clock.c:32
#define PRCI_DEVICESRESET_DDR_AXI_RST_N(x)
Definition: clock.c:53
int clock_get_tlclk_khz(void)
Definition: clock.c:274
#define PRCI_PLLCFG_DIVQ_SHIFT
Definition: clock.c:35
#define PRCI_PLLCFG_FSE_SHIFT
Definition: clock.c:38
#define FU540_QSPI2
Definition: addressmap.h:12
#define FU540_PRCI
Definition: addressmap.h:9
#define FU540_QSPI1
Definition: addressmap.h:11
#define FU540_UART(x)
Definition: addressmap.h:8
#define FU540_QSPI0
Definition: addressmap.h:10
unsigned int uint32_t
Definition: stdint.h:14
uint32_t u32
Definition: stdint.h:51
u32 gemgxlpllcfg1
Definition: clock.c:21
u32 gemgxlpllcfg0
Definition: clock.c:20
u32 reserved14
Definition: clock.c:18
u32 reserved08
Definition: clock.c:15
u32 ddrpllcfg1
Definition: clock.c:17
u32 devicesresetreg
Definition: clock.c:23
u32 ddrpllcfg0
Definition: clock.c:16
u32 corepllcfg0
Definition: clock.c:14
u32 coreclksel
Definition: clock.c:22
u32 reserved18
Definition: clock.c:19
u32 hfxosccfg
Definition: clock.c:13
#define s(param, src_bits, pmcreg, dst_bits)
#define c(value, pmcreg, dst_bits)