soc/sifive/fu740: Add FU740 SOC

Signed-off-by: Maximilian Brune <maximilian.brune@9elements.com>
Change-Id: I4a8fe02ef0adcb939aa65377a35874715c5ee58a
Reviewed-on: https://review.coreboot.org/c/coreboot/+/76689
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: ron minnich <rminnich@gmail.com>
This commit is contained in:
Maximilian Brune
2024-01-14 21:59:27 +06:00
committed by ron minnich
parent ec7b480760
commit 2ccb8e7891
22 changed files with 3190 additions and 0 deletions

View File

@@ -2,6 +2,7 @@
#ifndef __SOC_SIFIVE_HIFIVE_U_SPI_H__
#define __SOC_SIFIVE_HIFIVE_U_SPI_H__
#include <spi-generic.h>
/* Data Pins: MOSI MISO */

View File

@@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0-only
config SOC_SIFIVE_FU740
bool
select ARCH_RISCV_RV64
select ARCH_RISCV_S
select ARCH_RISCV_U
select ARCH_RISCV_PMP
select ARCH_BOOTBLOCK_RISCV
select ARCH_VERSTAGE_RISCV
select ARCH_ROMSTAGE_RISCV
select ARCH_RAMSTAGE_RISCV
select DRIVERS_UART_SIFIVE
select RISCV_USE_ARCH_TIMER
select UART_OVERRIDE_REFCLK
select RISCV_HAS_OPENSBI
config SEPARATE_ROMSTAGE
default n if SOC_SIFIVE_FU740
if SOC_SIFIVE_FU740
config MEMLAYOUT_LD_FILE
string
default "src/soc/sifive/fu740/memlayout.ld"
config RISCV_ARCH
string
default "rv64imac"
config RISCV_ABI
string
default "lp64"
config RISCV_CODEMODEL
string
default "medany"
# 4x U7 cores (RV64IMAFDC) + 1x S7 core (RV64IMAC)
config MAX_CPUS
int
default 5
config RISCV_WORKING_HARTID
int
default 1 # use U7 core as S7 core does not support supervisor mode
config OPENSBI_PLATFORM
string
default "generic"
config OPENSBI_TEXT_START
hex
default 0x80000000
config OPENSBI_FW_DYNAMIC_BOOT_HART
int
default 1
help
Choose the first U74 core as boot hart since
hart 0 is the S7 which does not support Supervisor mode
endif

View File

@@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0-only
ifeq ($(CONFIG_SOC_SIFIVE_FU740),y)
CPPFLAGS_common += -Isrc/soc/sifive/fu740/include
bootblock-y += uart.c
bootblock-y += clint.c
bootblock-y += spi.c
bootblock-y += sdram.c
bootblock-y += cbmem.c
bootblock-y += otp.c
bootblock-y += clock.c
bootblock-y += ddrregs.c
bootblock-y += chip.c
bootblock-y += gpio.c
ramstage-y += uart.c
ramstage-y += clint.c
ramstage-y += spi.c
ramstage-y += sdram.c
ramstage-y += cbmem.c
ramstage-y += otp.c
ramstage-y += clock.c
ramstage-y += chip.c
$(objcbfs)/bootblock.bin: $(objcbfs)/bootblock.raw.bin
@printf " GPT $(notdir $(@))\n"
@util/riscv/sifive-gpt.py $< $@
endif

51
src/soc/sifive/fu740/TODO Normal file
View File

@@ -0,0 +1,51 @@
1. refactor SPI code. It works but there are quite some TODO left and it also currently only supports 1 MSEL configuration
2. sometimes PCIe is not working with the following error message in Linux:
[ 0.619711] shpchp: Standard Hot Plug PCI Controller Driver version: 0.4
[ 0.621521] fu740-pcie e00000000.pcie: host bridge /soc/pcie@e00000000 ranges:
[ 0.621603] fu740-pcie e00000000.pcie: IO 0x0060080000..0x006008ffff -> 0x0060080000
[ 0.621659] fu740-pcie e00000000.pcie: MEM 0x0060090000..0x007fffffff -> 0x0060090000
[ 0.621685] fu740-pcie e00000000.pcie: MEM 0x2000000000..0x3fffffffff -> 0x2000000000
[ 0.727890] fu740-pcie e00000000.pcie: iATU: unroll T, 8 ob, 8 ib, align 4K, limit 4096G
[ 0.828041] fu740-pcie e00000000.pcie: PCIe Gen.1 x8 link up
[ 1.828944] fu740-pcie e00000000.pcie: Phy link never came up
[ 1.828961] fu740-pcie e00000000.pcie: error: link did not start at new speed
[ 1.829142] ------------[ cut here ]------------
[ 1.829152] WARNING: CPU: 2 PID: 1 at drivers/pci/controller/dwc/pcie-fu740.c:232 fu740_pcie_start_link+0x1b4/0x1ce
[ 1.829191] Modules linked in:
[ 1.829205] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 6.5.0-9-generic #9.1-Ubuntu
[ 1.829215] Hardware name: SiFive HiFive Unmatched A00 (DT)
[ 1.829221] epc : fu740_pcie_start_link+0x1b4/0x1ce
[ 1.829234] ra : fu740_pcie_start_link+0x1ca/0x1ce
[ 1.829247] epc : ffffffff806fa1ac ra : ffffffff806fa1c2 sp : ffffffc800023b30
[ 1.829256] gp : ffffffff822602d8 tp : ffffffd88027b600 t0 : ffffffff82029670
[ 1.829263] t1 : 0000000000000000 t2 : 0000000000000000 s0 : ffffffc800023b70
[ 1.829270] s1 : ffffffd88095d840 a0 : 0000000000000000 a1 : 0000000000000000
[ 1.829276] a2 : 0000000000000000 a3 : 0000000000000000 a4 : 0000000000000000
[ 1.829282] a5 : 0000000000000000 a6 : 0000000000000000 a7 : 0000000000000000
[ 1.829288] s2 : ffffffffffffff92 s3 : 0000000000716c81 s4 : ffffffd8802b2c10
[ 1.829295] s5 : 0000000000000003 s6 : ffffffffffffff92 s7 : ffffffff8108b448
[ 1.829301] s8 : 0000000000000008 s9 : ffffffff80e00106 s10: 0000000000000000
[ 1.829307] s11: 0000000000000000 t3 : 0000000000000000 t4 : 0000000000000000
[ 1.829313] t5 : 0000000000000000 t6 : 0000000000000000
[ 1.829317] status: 0000000200000120 badaddr: ffffffffffffff92 cause: 0000000000000003
[ 1.829326] [<ffffffff806fa1ac>] fu740_pcie_start_link+0x1b4/0x1ce
[ 1.829341] [<ffffffff806f8692>] dw_pcie_host_init+0x21e/0x2cc
[ 1.829355] [<ffffffff806fa2ca>] fu740_pcie_probe+0x104/0x15a
[ 1.829366] [<ffffffff8083e510>] platform_probe+0x5e/0xc6
[ 1.829388] [<ffffffff8083a7b4>] call_driver_probe+0x22/0x142
[ 1.829400] [<ffffffff8083b36e>] really_probe+0x9a/0x2a2
[ 1.829413] [<ffffffff8083b5f4>] __driver_probe_device+0x7e/0x146
[ 1.829425] [<ffffffff8083b6f4>] driver_probe_device+0x38/0xd0
[ 1.829436] [<ffffffff8083b966>] __driver_attach+0xee/0x1e8
[ 1.829448] [<ffffffff80838e78>] bus_for_each_dev+0x6c/0xc4
[ 1.829460] [<ffffffff8083acbe>] driver_attach+0x26/0x34
[ 1.829471] [<ffffffff8083a1fe>] bus_add_driver+0x112/0x21e
[ 1.829483] [<ffffffff8083cb12>] driver_register+0x52/0x106
[ 1.829497] [<ffffffff8083e0b6>] __platform_driver_register+0x28/0x34
[ 1.829511] [<ffffffff80e3322e>] fu740_pcie_driver_init+0x22/0x2c
[ 1.829528] [<ffffffff80002962>] do_one_initcall+0x5c/0x1e2
[ 1.829542] [<ffffffff80e01836>] kernel_init_freeable+0x286/0x300
[ 1.829560] [<ffffffff80ce5b40>] kernel_init+0x2a/0x16e
[ 1.829584] [<ffffffff80003f82>] ret_from_fork+0xe/0x20
[ 1.829597] ---[ end trace 0000000000000000 ]---
[ 1.829635] fu740-pcie: probe of e00000000.pcie failed with error -110

View File

@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <cbmem.h>
#include <commonlib/helpers.h>
#include <soc/addressmap.h>
#include <soc/sdram.h>
#include <symbols.h>
uintptr_t cbmem_top_chipset(void)
{
#define FU740_MAXDRAM 0x800000000ULL // 32 GiB
return MIN((uintptr_t)FU740_DRAM + sdram_size(), FU740_MAXDRAM);
}

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <cbmem.h>
#include <device/device.h>
#include <soc/addressmap.h>
#include <symbols.h>
static void fu740_init(struct device *dev)
{
int index = 0;
ram_from_to(dev, index++, FU740_DRAM, (uintptr_t)cbmem_top());
}
struct chip_operations soc_sifive_fu740_ops = {
.name = "SIFIVE FU740",
.enable_dev = fu740_init,
};

View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <mcall.h>
#include <stdint.h>
#include <device/mmio.h>
#include <soc/addressmap.h>
#define CLINT_MTIME 0xBFF8
#define CLINT_MTIMECMP 0x4000
void mtime_init(void)
{
long hart_id = read_csr(mhartid);
HLS()->time = (uint64_t *)(FU740_CLINT + CLINT_MTIME);
HLS()->timecmp = (uint64_t *)(FU740_CLINT + CLINT_MTIMECMP + 8 * hart_id);
}
void set_msip(int hartid, int val)
{
write32((void *)(FU740_CLINT + 4 * (uintptr_t)hartid), !!val);
}

View File

@@ -0,0 +1,448 @@
/* SPDX-License-Identifier: GPL-2.0-only */
// This file is used for setting up clocks and get devices out of reset
// For more Information see FU740-C000 Manual Chapter 7 Clocking and Reset
#include <console/console.h>
#include <delay.h>
#include <device/mmio.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
#include <soc/gpio.h>
#include <gpio.h>
#include <stdint.h>
// Clock frequencies for the cores, ddr and the peripherals are all derived from the hfclk (high frequency clock) and it is always 26 MHz
#define FU740_HFCLK_FREQ (26 * MHz)
struct prci_ctlr {
u32 hfxosccfg; // offset 0x00
u32 core_pllcfg; // offset 0x04
u32 core_plloutdiv; // offset 0x08
u32 ddr_pllcfg; // offset 0x0c
u32 ddr_plloutdiv; // offset 0x10
u32 pcieaux_plloutdiv; // offset 0x14 (undocumented)
u32 reserved18; // offset 0x18
u32 gemgxl_pllcfg; // offset 0x1c
u32 gemgxl_plloutdiv; // offset 0x20
u32 core_clk_sel_reg; // offset 0x24
u32 devices_reset_n; // offset 0x28
u32 clk_mux_status; // offset 0x2C
u32 cltx_pllcfg; // offset 0x30 chiplink (undocumented)
u32 cltx_plloutdiv; // offset 0x34 chiplink (undocumented)
u32 dvfs_core_pllcfg; // offset 0x38
u32 dvfs_core_plloutdiv; // offset 0x3C
u32 corepllsel; // offset 0x40 (undocumented, but probably same as last gen)
u8 reserved44[12]; // offset 0x44
u32 hfpclk_pllcfg; // offset 0x50
u32 hfpclk_plloutdiv; // offset 0x54
u32 hfpclkpllsel; // offset 0x58 (undocumented, but probably same as last gen)
u32 hfpclk_div_reg; // offset 0x5C
u8 reserved60[128]; // offset 0x60
u32 prci_plls; // offset 0xE0
u8 reservedE4[12]; // offset 0xE4
u32 procmoncfg_core_clock; // offset 0xF0 (undocumented)
} __packed;
static struct prci_ctlr *prci = (void *)FU740_PRCI;
// =================================
// clock selections
// =================================
#define PRCI_COREPLLSEL_MASK 1
#define PRCI_COREPLLSEL_COREPLL 0
#define PRCI_COREPLLSEL_DVFSCOREPLL 1
#define PRCI_CORECLKSEL_MASK 1
#define PRCI_CORECLKSEL_CORECLKPLL 0
#define PRCI_CORECLKSEL_HFCLK 1
#define PRCI_HFPCLKSEL_MASK 1
#define PRCI_HFPCLKSEL_PLL 0
#define PRCI_HFPCLKSEL_HFCLK 1
// ===================================
// pllcfg register format is used by all PLLs
// ===================================
#define PRCI_PLLCFG_DIVR_SHIFT 0
#define PRCI_PLLCFG_DIVF_SHIFT 6
#define PRCI_PLLCFG_DIVQ_SHIFT 15
#define PRCI_PLLCFG_RANGE_SHIFT 18
#define PRCI_PLLCFG_BYPASS_SHIFT 24
#define PRCI_PLLCFG_FSEBYPASS_SHIFT 25
#define PRCI_PLLCFG_LOCK_SHIFT 31
#define PRCI_PLLCFG_DIVR_MASK (0x03f << PRCI_PLLCFG_DIVR_SHIFT)
#define PRCI_PLLCFG_DIVF_MASK (0x1ff << PRCI_PLLCFG_DIVF_SHIFT)
#define PRCI_PLLCFG_DIVQ_MASK (0x007 << PRCI_PLLCFG_DIVQ_SHIFT)
#define PRCI_PLLCFG_RANGE_MASK (0x007 << PRCI_PLLCFG_RANGE_SHIFT)
#define PRCI_PLLCFG_BYPASS_MASK (0x001 << PRCI_PLLCFG_BYPASS_SHIFT)
#define PRCI_PLLCFG_FSEBYPASS_MASK (0x001 << PRCI_PLLCFG_FSEBYPASS_SHIFT)
#define PRCI_PLLCFG_LOCK_MASK (0x001 << PRCI_PLLCFG_LOCK_SHIFT)
// ===================================
// plloutdiv register formats
// ===================================
// registered are used to enable/disable PLLs
#define PRCI_DVFSCORE_PLLOUTDIV_MASK (1 << 24) // Note: u-boot and fu740 manual differ here ...
#define PRCI_HFPCLK_PLLOUTDIV_MASK (1 << 31) // Note: according to u-boot it is (1 << 24) but if I use that it gets stuck
#define PRCI_DDR_PLLOUTDIV_MASK (1 << 31)
#define PRCI_GEMGXL_PLLOUTDIV_MASK (1 << 31)
#define PRCI_CLTX_PLLOUTDIV_MASK (1 << 24) // undocumented (chiplink tx)
#define PRCI_PCIEAUX_PLLOUTDIV_MASK (1 << 0) // undocumented
#define PRCI_CORE_PLLOUTDIV_MASK (1 << 31) // undocumented
// ===================================
// devicereset register formats
// ===================================
// used to get devices in or out of reset
#define PRCI_DEVICES_RESET_DDR_CTRL_RST (1 << 0) // DDR Controller
#define PRCI_DEVICES_RESET_DDR_AXI_RST (1 << 1) // DDR Controller AXI Interface
#define PRCI_DEVICES_RESET_DDR_AHB_RST (1 << 2) // DDR Controller AHB Interface
#define PRCI_DEVICES_RESET_DDR_PHY_RST (1 << 3) // DDR PHY
#define PRCI_DEVICES_RESET_PCIEAUX_RST (1 << 4)
#define PRCI_DEVICES_RESET_GEMGXL_RST (1 << 5) // Gigabit Ethernet Subsystem
#define PRCI_DEVICES_RESET_CLTX_RST (1 << 6) // chiplink reset (undocumented)
// ===================================
// prci_plls register format
// ===================================
// used to check if certain PLLs are present in the SOC
#define PRCI_PLLS_CLTXPLL (1 << 0)
#define PRCI_PLLS_GEMGXLPLL (1 << 1)
#define PRCI_PLLS_DDRPLL (1 << 2)
#define PRCI_PLLS_HFPCLKPLL (1 << 3)
#define PRCI_PLLS_DVFSCOREPLL (1 << 4)
#define PRCI_PLLS_COREPLL (1 << 5)
// ===================================
// clk_mux_status register format
// ===================================
// read only register which is used to set some clock multiplex settings
// the value of this register depends on the state of pins connected to the FU740 SOC
// on the hifive-unmatched board the state of the pins is set by a hardware switch
#define PRCI_CLK_MUX_STATUS_CORECLKPLLSEL (1 << 0)
// 0 - HFCLK or CORECLK
// 1 - only HFCLK
#define PRCI_CLK_MUX_STATUS_TLCLKSEL (1 << 1)
// 0 - CORECLK/2
// 1 - CORECLK
#define PRCI_CLK_MUX_STATUS_RTCXSEL (1 << 2)
// 0 - use HFXCLK for RTC
// 1 - use RTCXALTCLKIN for RTC
#define PRCI_CLK_MUX_STATUS_DDRCTRLCLKSEL (1 << 3)
#define PRCI_CLK_MUX_STATUS_DDRPHYCLKSEL (1 << 4)
#define PRCI_CLK_MUX_STATUS_RESERVED (1 << 5)
#define PRCI_CLK_MUX_STATUS_GEMGXLCLKSEL (1 << 6)
#define PRCI_CLK_MUX_STATUS_MAINMEMCLKSEL (1 << 7)
// ===================================
// hfxosccfg register format
// ===================================
#define PRCI_HFXOSCCFG_HFXOSEN (1 << 30) // Crystal oscillator enable
// Note: I guess (it is not documented)
// 0 - XTAL PADS
// 1 - OSC PADS
#define PRCI_HFXOSCCFG_HFXOSCRDY (1 << 31) // Crystal oscillator ready
struct pll_settings {
unsigned int divr:6; // divider before PLL loop (reference), equal to divr + 1
unsigned int divf:9; // VCO feedback divider value, equal to 2 * (divf + 1)
unsigned int divq:3; // divider after PLL loop, equal to 2^divq
// PLL filter range (TODO documentation is not really clear on how to set it)
unsigned int range:3;
unsigned int bypass:1; // probably used to bypass the PLL
// internal or external input path (internal = 1, external = 0)
//WARN this is only a guess since it is undocumented
unsigned int fsebypass:1;
};
static void configure_pll(u32 *reg, const struct pll_settings *s)
{
// Write the settings to the register
u32 c = read32(reg);
clrsetbits32(&c, PRCI_PLLCFG_DIVR_MASK
| PRCI_PLLCFG_DIVF_MASK
| PRCI_PLLCFG_DIVQ_MASK
| PRCI_PLLCFG_RANGE_MASK
| PRCI_PLLCFG_BYPASS_MASK
| PRCI_PLLCFG_FSEBYPASS_MASK,
(s->divr << PRCI_PLLCFG_DIVR_SHIFT)
| (s->divf << PRCI_PLLCFG_DIVF_SHIFT)
| (s->divq << PRCI_PLLCFG_DIVQ_SHIFT)
| (s->range << PRCI_PLLCFG_RANGE_SHIFT)
| (s->bypass << PRCI_PLLCFG_BYPASS_SHIFT)
| (s->fsebypass << PRCI_PLLCFG_FSEBYPASS_SHIFT));
write32(reg, c);
// Wait for PLL lock
while (!(read32(reg) & PRCI_PLLCFG_LOCK_MASK))
;
}
/*
* Section 7.1 recommends a frequency of 1.0 GHz (up to 1.5 GHz is possible)
* Section 7.4.2 provides the necessary values
*
* COREPLL is set up for ~1 GHz output frequency.
* divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 2 (/4 Output divider)
*/
static const struct pll_settings corepll_settings = {
.divr = 0,
.divf = 76,
.divq = 2,
.range = 4,
.bypass = 0,
.fsebypass = 1, // external feedback mode is not supported
};
/*
* Section 7.4.3: DDR and Ethernet Subsystem Clocking and Reset
*
* DDRPLL is set up for 933 MHz output frequency.
* divr = 0 (x1), divf = 71 (x144) => (3744 MHz VCO), divq = 2 (/4 output divider)
*/
static const struct pll_settings ddrpll_settings = {
.divr = 0,
.divf = 71,
.divq = 2,
.range = 4,
.bypass = 0,
.fsebypass = 1, // external feedback mode is not supported
};
/*
* GEMGXLPLL is set up for 125 MHz output frequency.
* divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 5 (/32 output divider)
*/
static const struct pll_settings gemgxlpll_settings = {
.divr = 0,
.divf = 76,
.divq = 5,
.range = 4,
.bypass = 0,
.fsebypass = 1, // external feedback mode is not supported
};
/*
* HFPCLKPLL is set up for 520 MHz output frequency.
* TODO a lower value should also suffice as well as safe some power
* divr = 1 (/2), divf = 39 (x80) => (2080 MHz VCO), divq = 2 (/4 output divider)
*/
static const struct pll_settings hfpclkpll_settings = {
.divr = 1,
//.divf = 122,
.divf = 39,
.divq = 2,
.range = 4,
.bypass = 0,
.fsebypass = 1, // external feedback mode is not supported
};
/*
* CLTXCLKPLL is set up for 520 MHz output frequency.
* divr = 1 (/2), divf = 122 (x154) => (4004 MHz VCO), divq = 2 (/4 output divider)
*/
static const struct pll_settings cltxpll_settings = {
.divr = 1,
.divf = 39,
.divq = 2,
.range = 4,
.bypass = 0,
.fsebypass = 1, // external feedback mode is not supported
};
static void init_coreclk(void)
{
// we can't modify the coreclk PLL while we are running on it, so let coreclk devise
// its clock from hfclk before modifying PLL
clrsetbits32(&prci->core_clk_sel_reg, PRCI_CORECLKSEL_MASK, PRCI_CORECLKSEL_HFCLK);
// only configure pll if it is present
if (!(read32(&prci->prci_plls) & PRCI_PLLS_COREPLL)) {
return;
}
configure_pll(&prci->core_pllcfg, &corepll_settings);
// switch coreclk multiplexer to use corepll as clock source again
clrsetbits32(&prci->core_clk_sel_reg, PRCI_CORECLKSEL_MASK, PRCI_CORECLKSEL_CORECLKPLL);
}
static void init_ddrclk(void)
{
// only configure pll if it is present
if (!(read32(&prci->prci_plls) & PRCI_PLLS_DDRPLL)) {
return;
}
// disable ddr clock output before reconfiguring the PLL
u32 cfg1 = read32(&prci->ddr_plloutdiv);
clrbits32(&cfg1, PRCI_DDR_PLLOUTDIV_MASK);
write32(&prci->ddr_plloutdiv, cfg1);
configure_pll(&prci->ddr_pllcfg, &ddrpll_settings);
// PLL is ready/locked so enable it (its gated)
setbits32(&cfg1, PRCI_DDR_PLLOUTDIV_MASK);
write32(&prci->ddr_plloutdiv, cfg1);
}
static void init_gemgxlclk(void)
{
// only configure pll if it is present
if (!(read32(&prci->prci_plls) & PRCI_PLLS_GEMGXLPLL)) {
return;
}
// disable gemgxl clock output before reconfiguring the PLL
u32 cfg1 = read32(&prci->gemgxl_plloutdiv);
clrbits32(&cfg1, PRCI_GEMGXL_PLLOUTDIV_MASK);
write32(&prci->gemgxl_plloutdiv, cfg1);
configure_pll(&prci->gemgxl_pllcfg, &gemgxlpll_settings);
// PLL is ready/locked so enable it (its gated)
setbits32(&cfg1, PRCI_GEMGXL_PLLOUTDIV_MASK);
write32(&prci->gemgxl_plloutdiv, cfg1);
}
/*
* Configure High Frequency peripheral clock which is used by
* UART, SPI, GPIO, I2C and PWM subsystem
*/
static void init_hfpclk(void)
{
// we can't modify the hfpclk PLL while we are running on it, so let pclk devise
// its clock from hfclk before modifying PLL
u32 hfpclksel = read32(&prci->hfpclkpllsel);
hfpclksel |= PRCI_HFPCLKSEL_HFCLK;
write32(&prci->hfpclkpllsel, hfpclksel);
configure_pll(&prci->hfpclk_pllcfg, &hfpclkpll_settings);
// PLL is ready/locked so enable it (its gated)
u32 hfpclk_plloutdiv = read32(&prci->hfpclk_plloutdiv);
hfpclk_plloutdiv |= PRCI_HFPCLK_PLLOUTDIV_MASK;
write32(&prci->hfpclk_plloutdiv, hfpclk_plloutdiv);
mdelay(1);
// switch to using PLL for hfpclk
clrbits32(&prci->hfpclkpllsel, PRCI_HFPCLKSEL_MASK);
udelay(70);
}
static void reset_deassert(u8 reset_index)
{
u32 device_reset = read32(&prci->devices_reset_n);
device_reset |= reset_index;
write32(&prci->devices_reset_n, device_reset);
}
static void init_cltx(void)
{
// disable hfpclkpll before configuring it
u32 cfg1 = read32(&prci->cltx_plloutdiv);
clrbits32(&cfg1, PRCI_CLTX_PLLOUTDIV_MASK);
write32(&prci->cltx_plloutdiv, cfg1);
configure_pll(&prci->cltx_pllcfg, &cltxpll_settings);
// PLL is ready/locked so enable it (its gated)
setbits32(&cfg1, PRCI_CLTX_PLLOUTDIV_MASK);
write32(&prci->cltx_plloutdiv, cfg1);
// get chiplink out of reset
reset_deassert(PRCI_DEVICES_RESET_CLTX_RST);
udelay(70);
}
void clock_init(void)
{
// first configure the coreclk (used by HARTs) to get maximum speed early on
init_coreclk();
// put all devices in reset (e.g. DDR, ethernet, pcie) before configuring their clocks
write32(&prci->devices_reset_n, 0);
// initialize clock used by DDR subsystem
init_ddrclk();
// get DDR controller out of reset
reset_deassert(PRCI_DEVICES_RESET_DDR_CTRL_RST);
// wait at least one full DDR controller clock cycle
asm volatile ("fence");
// get DDR controller (register interface) out of reset
// get DDR subsystem PHY out of reset
reset_deassert(PRCI_DEVICES_RESET_DDR_AXI_RST |
PRCI_DEVICES_RESET_DDR_AHB_RST |
PRCI_DEVICES_RESET_DDR_PHY_RST);
// we need to wait 256 full ddrctrl clock cycles until we can interact with the DDR subsystem
for (int i = 0; i < 256; i++)
asm volatile ("nop");
if (read32(&prci->prci_plls) & PRCI_PLLS_HFPCLKPLL) {
// set hfclk as reference for peripheral clock since we don't have the PLL
//clrsetbits32(&prci->hfpclkpllsel, PRCI_HFPCLKSEL_MASK, PRCI_HFPCLKSEL_HFCLK);
init_hfpclk();
} else if (read32(&prci->prci_plls) & PRCI_PLLS_CLTXPLL) {
// Note: this path has never been tested since the platforms tested with
// always have HFPCLKPLL
init_cltx();
// get chiplink out of reset
reset_deassert(PRCI_DEVICES_RESET_CLTX_RST);
}
// GEMGXL init VSC8541 PHY reset sequence;
gpio_set_direction(GEMGXL_RST, GPIO_OUTPUT);
gpio_set(GEMGXL_RST, 1);
udelay(1);
/* Reset PHY again to enter unmanaged mode */
gpio_set(GEMGXL_RST, 0);
udelay(1);
gpio_set(GEMGXL_RST, 1);
mdelay(15);
init_gemgxlclk();
// get ethernet out of reset
reset_deassert(PRCI_DEVICES_RESET_GEMGXL_RST);
}
// get the peripheral clock frequency used by UART (probably also SPI, GPIO, I2C and PWM)
int clock_get_pclk(void)
{
u64 pclk = FU740_HFCLK_FREQ;
// check if hfpclkpll is present and
// check if hfpclkpll is selected in the multiplexer TODO
// check if hpfclkpll is enabled
if ((read32(&prci->prci_plls) & PRCI_PLLS_HFPCLKPLL) &&
(read32(&prci->hfpclk_plloutdiv) & PRCI_HFPCLK_PLLOUTDIV_MASK)) {
int hfpclk_pllcfg = read32(&prci->hfpclk_pllcfg);
int divr = (hfpclk_pllcfg & PRCI_PLLCFG_DIVR_MASK) >> PRCI_PLLCFG_DIVR_SHIFT;
int divf = (hfpclk_pllcfg & PRCI_PLLCFG_DIVF_MASK) >> PRCI_PLLCFG_DIVF_SHIFT;
int divq = (hfpclk_pllcfg & PRCI_PLLCFG_DIVQ_MASK) >> PRCI_PLLCFG_DIVQ_SHIFT;
pclk /= (divr + 1); // reference divider
pclk *= (2 * (divf + 1)); // feedback divider
pclk /= (1 << divq); // output divider
}
// divider value before pclk seems to be (hfpclkdiv + 2). Not mentioned in fu740 manual though.
return pclk / (read32(&prci->hfpclk_div_reg) + 2);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <soc/addressmap.h>
#include <soc/gpio.h>
#include <stdint.h>
#include <arch/mmio.h>
#include <delay.h>
#include <assert.h>
#include <device/mmio.h>
#include <gpio.h>
#define SIFIVE_GPIO_INPUT_VAL (FU740_GPIO + 0x00)
#define SIFIVE_GPIO_INPUT_EN (FU740_GPIO + 0x04)
#define SIFIVE_GPIO_OUTPUT_EN (FU740_GPIO + 0x08)
#define SIFIVE_GPIO_OUTPUT_VAL (FU740_GPIO + 0x0C)
#define SIFIVE_GPIO_PUE (FU740_GPIO + 0x10)
#define SIFIVE_GPIO_DS (FU740_GPIO + 0x14)
#define SIFIVE_GPIO_RISE_IE (FU740_GPIO + 0x18)
#define SIFIVE_GPIO_RISE_IP (FU740_GPIO + 0x1C)
#define SIFIVE_GPIO_FALL_IE (FU740_GPIO + 0x20)
#define SIFIVE_GPIO_FALL_IP (FU740_GPIO + 0x24)
#define SIFIVE_GPIO_HIGH_IE (FU740_GPIO + 0x28)
#define SIFIVE_GPIO_HIGH_IP (FU740_GPIO + 0x2C)
#define SIFIVE_GPIO_LOW_IE (FU740_GPIO + 0x30)
#define SIFIVE_GPIO_LOW_IP (FU740_GPIO + 0x34)
#define SIFIVE_GPIO_IOF_EN (FU740_GPIO + 0x38)
#define SIFIVE_GPIO_IOF_SEL (FU740_GPIO + 0x3C)
#define SIFIVE_GPIO_OUT_XOR (FU740_GPIO + 0x40)
void gpio_set(gpio_t gpio, int value)
{
uint32_t output_val = read32((void *)SIFIVE_GPIO_OUTPUT_VAL);
if (value)
output_val |= (1 << gpio);
else
output_val &= ~(1 << gpio);
write32((void *)SIFIVE_GPIO_OUTPUT_VAL, output_val);
}
int gpio_get(gpio_t gpio)
{
uint32_t input_val = read32((void *)SIFIVE_GPIO_INPUT_VAL);
return !!(input_val & (1 << gpio));
}
void gpio_set_direction(gpio_t gpio, enum gpio_direction gpio_dir)
{
uint32_t input_en = read32((void *)SIFIVE_GPIO_INPUT_EN);
uint32_t output_en = read32((void *)SIFIVE_GPIO_OUTPUT_EN);
if (gpio_dir == GPIO_OUTPUT) {
input_en &= ~(1 << gpio);
output_en |= (1 << gpio);
} else if (gpio_dir == GPIO_INPUT) {
input_en |= (1 << gpio);
output_en &= ~(1 << gpio);
}
write32((void *)SIFIVE_GPIO_INPUT_EN, input_en);
write32((void *)SIFIVE_GPIO_OUTPUT_EN, output_en);
}

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
// reference: fu740-c000 manual chapter 5: Memory Map
// Table 15: (TODO: subject for common code: none of these changed compared to fu540)
#define FU740_ROM 0x00001000
#define FU740_DTIM 0x01000000
#define FU740_CLINT 0x02000000
#define FU740_L2LIM 0x08000000
#define FU740_PRCI 0x10000000
#define FU740_UART0 0x10010000
#define FU740_UART1 0x10011000
#define FU740_QSPI0 0x10040000
#define FU740_QSPI1 0x10041000
#define FU740_QSPI2 0x10050000 // in unmatched board schematics it's called SPI0
#define FU740_GPIO 0x10060000
#define FU740_OTP 0x10070000
#define FU740_PINCTRL 0x10080000
#define FU740_QSPI0FLASH 0x20000000
#define FU740_QSPI1FLASH 0x30000000
#define FU740_DRAM 0x80000000
#define PCIE_MGMT 0x100D0000
#define PCIE_CONFIG 0x000DF0000000
#define PCIE_DBI 0x000E00000000
#define FU740_I2C_0 0x10030000
#define FU740_I2C_1 0x10031000
// Reset Vector - 4
#define FU740_ROM1 0x00001000
// After reset vector it will jump directly to this address if ZSBL is used (ZSBL code)
#define FU740_ROM2 0x00010000
#define FU740_MSEL FU740_ROM // mode select is always at start of ROM
// naming changed a bit between FU540 and FU740 manuals
// Ethernet MAC -> Ethernet
// Ethernet Management -> GEMGXL MGMT
#define SIFIVE_ETHERNET_MAC 0x10090000
#define SIFIVE_ETHERNET_MGMT 0x100A0000
#define FU740_UART(i) (FU740_UART0 + 0x1000 * i)

View File

@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_SIFIVE_FU740_CLOCK_H__
#define __SOC_SIFIVE_FU740_CLOCK_H__
void clock_init(void);
int clock_get_pclk(void);
#endif /* __SOC_SIFIVE_FU740_CLOCK_H__ */

View File

@@ -0,0 +1,36 @@
#ifndef _SOC_SIFIVE_FU740_GPIO_H_
#define _SOC_SIFIVE_FU740_GPIO_H_
#include <stdint.h>
//TODO these are mainboard specific
enum gpio {
J29_1,
PMICINTB,
PMICSHDN,
J8_1,
J8_3,
PCIE_PWREN, // connected to power rails of PCIe connectors
THERM,
UBRDG_RSTN,
PCIE_PERSTN, // connected to PERST pin of PCIe connectors
ULPI_RSTN,
J8_2,
UHUB_RSTN,
GEMGXL_RST,
J8_4,
EN_VDD_SD,
SD_CD,
};
// this is to satisfy src/include/gpio.h
typedef enum gpio gpio_t;
enum gpio_direction {
GPIO_INPUT,
GPIO_OUTPUT,
};
void gpio_set_direction(gpio_t gpio, enum gpio_direction gpio_dir);
#endif // _SOC_SIFIVE_FU740_GPIO_H_

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_SIFIVE_FU740_OTP_H__
#define __SOC_SIFIVE_FU740_OTP_H__
#include <stdint.h>
u32 otp_read_word(u16 idx);
u32 otp_read_serial(void);
#endif /* __SOC_SIFIVE_FU740_OTP_H__ */

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef SIFIVE_COMMON_SDRAM_H
#define SIFIVE_COMMON_SDRAM_H
#include <stdint.h>
#include <types.h>
void sdram_init(size_t dram_size);
size_t sdram_size(void);
#endif

View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_SIFIVE_FU740_SPI_H__
#define __SOC_SIFIVE_FU740_SPI_H__
#include <spi-generic.h>
// Frame Format Register (fmt)
#define FU740_SPI_PROTO_S 0 // Data Pins: MOSI MISO
#define FU740_SPI_PROTO_D 1 // Data Pins: DQ0 DQ1
#define FU740_SPI_PROTO_Q 2 // Data Pins: DQ0 DQ1 DQ2 DQ3
#define FU740_SPI_ENDIAN_BIG 0 // send MSB first
#define FU740_SPI_ENDIAN_LITTLE 1 // send LSB first
// Serial Clock Mode Register (sckmode)
#define FU740_SPI_PHA_LOW 0 // inactive state of SCK is logical 0
#define FU740_SPI_PHA_HIGH 1 // inactive state of SCK is logical 1
#define FU740_SPI_POL_LEADING 0 // data is sampled on leading edge of sck
#define FU740_SPI_POL_TRAILING 1 // data is sampled on trailing edge of sck
// ffmt register (SPI Flash Instruction Format Register)
struct fu740_spi_ffmt_config {
unsigned int cmd_en; // enable sending of command
unsigned int addr_len; // number of address bytes (0-4)
unsigned int pad_cnt; // number of dummy cycles
unsigned int cmd_proto; // protocol for transmitting command
unsigned int addr_proto; // protocol for transmitting address and padding
unsigned int data_proto; // protocol for receiving data bytes
unsigned int cmd_code; // value of command byte
unsigned int pad_code; // first 8 bits to transmit during dummy cycles
};
struct fu740_spi_fmt_config {
unsigned int protocol; // FU740_SPI_PROTO_S, FU740_SPI_PROTO_D, FU740_SPI_PROTO_Q
unsigned int endianness; // 0 = MSB, 1 = LSB
unsigned int io_dir; // Rx, Tx
unsigned int bits_per_frame; // up to 8bits
};
struct fu740_spi_config {
unsigned int freq; // speed of spi interface
unsigned int pha; // serial clock phase
unsigned int pol; // serial clock polarity
struct fu740_spi_fmt_config fmt_config; // frame format config
struct fu740_spi_ffmt_config ffmt_config; // flash instruction format
};
extern struct fu740_spi_config fu740_spi_configs[];
int fu740_spi_setup(const struct spi_slave *slave);
#endif /* __SOC_SIFIVE_FU740_SPI_H__ */

View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <memlayout.h>
#include <soc/addressmap.h>
#include <arch/header.ld>
SECTIONS
{
SRAM_START(FU740_L2LIM)
/* The 64K size is not allocated. It's just for basic size checking. */
BOOTBLOCK(FU740_L2LIM, 64K)
CAR_STACK(FU740_L2LIM + 64K, 12K) // considering each harts stack is a pagesize (4K), we can potentially have up to 16 harts regarding stack space //TODO reduce it, since fu740 doesn't even have/support 16 harts
PRERAM_CBMEM_CONSOLE(FU740_L2LIM + 76K, 8K)
CBFS_MCACHE(FU740_L2LIM + 84K, 8K)
FMAP_CACHE(FU740_L2LIM + 92K, 2K)
PRERAM_CBFS_CACHE(FU740_L2LIM + 94K, 128K)
SRAM_END(FU740_L2LIM + 2M)
DRAM_START(FU740_DRAM)
OPENSBI(FU740_DRAM, 256K)
/* The 256K size is not allocated. It's just for basic size checking. */
RAMSTAGE(FU740_DRAM + 256K, 256K)
MEM_STACK(FU740_DRAM + 512K, 20K)
POSTRAM_CBFS_CACHE(FU740_DRAM + 532K, 32M - 532K)
}

View File

@@ -0,0 +1,90 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <delay.h>
#include <device/mmio.h>
#include <console/console.h>
#include <console/uart.h>
#include <soc/addressmap.h>
#include <soc/otp.h>
/*
* This is a driver for the eMemory EG004K32TQ028XW01 NeoFuse
* One-Time-Programmable (OTP) memory used within the SiFive FU740.
* It is documented in the FU740 manual here:
* https://www.sifive.com/documentation/chips/freedom-u740-c000-manual/
*/
struct sifive_otp_registers {
u32 pa; /* Address input */
u32 paio; /* Program address input */
u32 pas; /* Program redundancy cell selection input */
u32 pce; /* OTP Macro enable input */
u32 pclk; /* Clock input */
u32 pdin; /* Write data input */
u32 pdout; /* Read data output */
u32 pdstb; /* Deep standby mode enable input (active low) */
u32 pprog; /* Program mode enable input */
u32 ptc; /* Test column enable input */
u32 ptm; /* Test mode enable input */
u32 ptm_rep;/* Repair function test mode enable input */
u32 ptr; /* Test row enable input */
u32 ptrim; /* Repair function enable input */
u32 pwe; /* Write enable input (defines program cycle) */
} __packed;
/*
* Read a 32 bit value addressed by its index from the OTP.
* The FU740 stores 4096x32 bit (16KiB) values.
* Index 0x00-0xff are reserved for SiFive internal use. (first 1KiB)
*/
u32 otp_read_word(u16 idx)
{
u32 w;
if (idx >= 0x1000)
die("otp: idx out of bounds");
struct sifive_otp_registers *regs = (void *)(FU740_OTP);
// wake up from stand-by
write32(&regs->pdstb, 0x01);
// enable repair function
write32(&regs->ptrim, 0x01);
// enable input
write32(&regs->pce, 0x01);
// address to read
write32(&regs->pa, idx);
// cycle clock to read
write32(&regs->pclk, 0x01);
mdelay(1);
write32(&regs->pclk, 0x00);
mdelay(1);
w = read32(&regs->pdout);
// shut down
write32(&regs->pce, 0x00);
write32(&regs->ptrim, 0x00);
write32(&regs->pdstb, 0x00);
return w;
}
u32 otp_read_serial(void)
{
u32 serial = 0;
u32 serial_n = 0;
for (int i = 0xfe; i > 0; i -= 2) {
serial = otp_read_word(i);
serial_n = otp_read_word(i+1);
if (serial == ~serial_n)
break;
}
return serial;
}

View File

@@ -0,0 +1,246 @@
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* See the file LICENSE for further information */
#ifndef _SIFIVE_SDRAM_H
#define _SIFIVE_SDRAM_H
#include <arch/mmio.h>
#include <console/console.h>
#include <device/mmio.h>
#include <soc/addressmap.h>
#include <soc/sdram.h>
#include <stdint.h>
#include <stddef.h>
#define DRAM_CLASS_OFFSET 8
#define DRAM_CLASS_DDR4 0xA
#define OPTIMAL_RMODW_EN_OFFSET 0
#define DISABLE_RD_INTERLEAVE_OFFSET 16
#define OUT_OF_RANGE_FLAG (1 << 1)
#define MULTIPLE_OUT_OF_RANGE_FLAG (1 << 2)
#define PORT_COMMAND_CHANNEL_ERROR_FLAG (1 << 7)
#define MC_INIT_COMPLETE_FLAG (1 << 8) // Memory Controller init complete
#define LEVELING_OPERATION_COMPLETED_FLAG (1 << 22)
#define DFI_PHY_WRLELV_MODE_OFFSET 24
#define DFI_PHY_RDLVL_MODE_OFFSET 24
#define DFI_PHY_RDLVL_GATE_MODE_OFFSET 0
#define VREF_EN_OFFSET 24
#define PORT_ADDR_PROTECTION_EN_OFFSET 0
#define AXI0_ADDRESS_RANGE_ENABLE_OFFSET 8
#define AXI0_RANGE_PROT_BITS_0_OFFSET 24
#define RDLVL_EN_OFFSET 16
#define RDLVL_GATE_EN_OFFSET 24
#define WRLVL_EN_OFFSET 0
#define PHY_RX_CAL_DQ0_0_OFFSET 0
#define PHY_RX_CAL_DQ1_0_OFFSET 16
// reference: fu740-c000 manual chapter 32: DDR Subsystem
// Cahpter 32.2: Memory Map
#define FU740_DDRCTRL 0x100b0000
#define FU740_DDRPHY 0x100b2000
#define FU740_PHYSICAL_FILTER 0x100b8000 // formerly called DDRBUSBLOCKER (FU540)
#define FU740_DDRMGMT 0x100c0000
static void phy_reset(u32 *ddrphyreg, const u32 *physettings)
{
for (int i = 1152; i <= 1214; i++)
write32(&ddrphyreg[i], physettings[i]);
for (int i = 0; i <= 1151; i++)
write32(&ddrphyreg[i], physettings[i]);
}
static void ux00ddr_writeregmap(u32 *ahbregaddr, const u32 *ctlsettings, const u32 *physettings)
{
u32 *ddrctlreg = (u32 *) ahbregaddr;
u32 *ddrphyreg = ((u32 *) ahbregaddr) + (0x2000 / sizeof(u32)); //TODO use FU740_DDRPHY instead
for (int i = 0; i <= 264; i++)
write32((void *)&ddrctlreg[i], ctlsettings[i]);
phy_reset(ddrphyreg, physettings);
}
static void ux00ddr_start(u32 *ahbregaddr, u64 *filteraddr, uint64_t ddrend)
{
// start calibration and training operation
setbits32(ahbregaddr, 0x1);
// wait for memory initialization complete
// bit 8 of INT_STATUS (DENALI_CTL_132) 0x210
while (!(read32(&ahbregaddr[132]) & MC_INIT_COMPLETE_FLAG))
;
// Disable the BusBlocker in front of the controller AXI slave ports
write64(filteraddr, 0x0f00000000000000UL | (ddrend >> 2));
// ^ RWX + TOR
}
static void ux00ddr_mask_mc_init_complete_interrupt(u32 *ahbregaddr)
{
// Mask off Bit 8 of Interrupt Status
// Bit [8] The MC initialization has been completed
setbits32(&ahbregaddr[136], MC_INIT_COMPLETE_FLAG);
}
static void ux00ddr_mask_outofrange_interrupts(u32 *ahbregaddr)
{
// Mask off Bit 8, Bit 2 and Bit 1 of Interrupt Status
// Bit [2] Multiple accesses outside the defined PHYSICAL memory space have occurred
// Bit [1] A memory access outside the defined PHYSICAL memory space has occurred
setbits32(&ahbregaddr[136], OUT_OF_RANGE_FLAG | MULTIPLE_OUT_OF_RANGE_FLAG);
}
static void ux00ddr_mask_port_command_error_interrupt(u32 *ahbregaddr)
{
// Mask off Bit 7 of Interrupt Status
// Bit [7] An error occurred on the port command channel
setbits32(&ahbregaddr[136], PORT_COMMAND_CHANNEL_ERROR_FLAG);
}
static void ux00ddr_mask_leveling_completed_interrupt(u32 *ahbregaddr)
{
// Mask off Bit 22 of Interrupt Status
// Bit [22] The leveling operation has completed
setbits32(&ahbregaddr[136], LEVELING_OPERATION_COMPLETED_FLAG);
}
static void ux00ddr_setuprangeprotection(u32 *ahbregaddr, size_t size)
{
write32(&ahbregaddr[209], 0x0);
u32 size_16Kblocks = ((size >> 14) & 0x7FFFFF) - 1;
write32(&ahbregaddr[210], size_16Kblocks);
write32(&ahbregaddr[212], 0x0);
write32(&ahbregaddr[214], 0x0);
write32(&ahbregaddr[216], 0x0);
setbits32(&ahbregaddr[224], (0x3 << AXI0_RANGE_PROT_BITS_0_OFFSET));
write32(&ahbregaddr[225], 0xFFFFFFFF);
setbits32(&ahbregaddr[208], (1 << AXI0_ADDRESS_RANGE_ENABLE_OFFSET));
setbits32(&ahbregaddr[208], (1 << PORT_ADDR_PROTECTION_EN_OFFSET));
}
static void ux00ddr_disableaxireadinterleave(u32 *ahbregaddr)
{
setbits32(&ahbregaddr[120], (1 << DISABLE_RD_INTERLEAVE_OFFSET));
}
static void ux00ddr_disableoptimalrmodw(u32 *ahbregaddr)
{
clrbits32(&ahbregaddr[21], (1 << OPTIMAL_RMODW_EN_OFFSET));
}
static void ux00ddr_enablewriteleveling(u32 *ahbregaddr)
{
setbits32(&ahbregaddr[170], (1 << WRLVL_EN_OFFSET) | (1 << DFI_PHY_WRLELV_MODE_OFFSET));
}
static void ux00ddr_enablereadleveling(u32 *ahbregaddr)
{
setbits32(&ahbregaddr[181], (1 << DFI_PHY_RDLVL_MODE_OFFSET));
setbits32(&ahbregaddr[260], (1 << RDLVL_EN_OFFSET));
}
static void ux00ddr_enablereadlevelinggate(u32 *ahbregaddr)
{
setbits32(&ahbregaddr[260], (1 << RDLVL_GATE_EN_OFFSET));
setbits32(&ahbregaddr[182], (1 << DFI_PHY_RDLVL_GATE_MODE_OFFSET));
}
static void ux00ddr_enablevreftraining(u32 *ahbregaddr)
{
setbits32(&ahbregaddr[184], (1 << VREF_EN_OFFSET));
}
static u32 ux00ddr_getdramclass(u32 *ahbregaddr)
{
return ((read32(ahbregaddr) >> DRAM_CLASS_OFFSET) & 0xF);
}
static void ux00ddr_phy_fixup(void *ahbregaddr)
{
void *ddrphyreg = ahbregaddr + 0x2000;
// bitmask of failed lanes
uint64_t fails = 0;
u32 slicebase = 0;
u32 dq = 0;
// check errata condition
for (u32 slice = 0; slice < 8; slice++) {
u32 regbase = slicebase + 34;
for (u32 reg = 0 ; reg < 4; reg++) {
u32 updownreg = read32(ddrphyreg + ((regbase+reg) << 2));
for (u32 bit = 0; bit < 2; bit++) {
u32 phy_rx_cal_dqn_0_offset;
if (bit == 0)
phy_rx_cal_dqn_0_offset = PHY_RX_CAL_DQ0_0_OFFSET;
else
phy_rx_cal_dqn_0_offset = PHY_RX_CAL_DQ1_0_OFFSET;
u32 down = (updownreg >> phy_rx_cal_dqn_0_offset) & 0x3F;
u32 up = (updownreg >> (phy_rx_cal_dqn_0_offset + 6)) & 0x3F;
uint8_t failc0 = ((down == 0) && (up == 0x3F));
uint8_t failc1 = ((up == 0) && (down == 0x3F));
// print error message on failure
if (failc0 || failc1) {
fails |= (1 << dq);
char slicelsc = '0';
char slicemsc = '0';
slicelsc += (dq % 10);
slicemsc += (dq / 10);
printk(BIOS_ERR, "S %c%c%c\n", slicelsc, slicemsc, failc0 ? 'U' : 'D');
}
dq++;
}
}
slicebase += 128;
}
if (fails)
printk(BIOS_ERR, "DDR error in fixing up: %llx\n", fails);
}
extern const u32 denali_ddr_phy_data[1215];
extern const u32 denali_ddr_ctl_data[265];
void sdram_init(size_t dram_size)
{
u32 *ddrctrl = (u32 *)FU740_DDRCTRL;
u64 *ddr_physical_filter = (u64 *)FU740_PHYSICAL_FILTER;
ux00ddr_writeregmap(ddrctrl, denali_ddr_ctl_data, denali_ddr_phy_data);
ux00ddr_disableaxireadinterleave(ddrctrl);
ux00ddr_disableoptimalrmodw(ddrctrl);
ux00ddr_enablewriteleveling(ddrctrl);
ux00ddr_enablereadleveling(ddrctrl);
ux00ddr_enablereadlevelinggate(ddrctrl);
if (ux00ddr_getdramclass(ddrctrl) == DRAM_CLASS_DDR4)
ux00ddr_enablevreftraining(ddrctrl);
// mask off interrupts for leveling completion
ux00ddr_mask_leveling_completed_interrupt(ddrctrl);
ux00ddr_mask_mc_init_complete_interrupt(ddrctrl);
ux00ddr_mask_outofrange_interrupts(ddrctrl);
ux00ddr_setuprangeprotection(ddrctrl, dram_size);
ux00ddr_mask_port_command_error_interrupt(ddrctrl);
ux00ddr_start(ddrctrl, ddr_physical_filter, FU740_DRAM + dram_size);
ux00ddr_phy_fixup(ddrctrl);
}
// sdram_init MUST be called before sdram_size
size_t sdram_size(void)
{
u64 devicepmp0 = read64((u64 *)FU740_PHYSICAL_FILTER);
return ((devicepmp0 & 0xFFFFFFFFFFFFFF) << 2) - FU740_DRAM;
}
#endif

209
src/soc/sifive/fu740/spi.c Normal file
View File

@@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0-only */
//TODO needs work
#include <device/mmio.h>
#include <soc/spi.h>
#include <soc/clock.h>
#include <soc/addressmap.h>
#include <delay.h>
#include <lib.h>
#include "spi_internal.h"
static struct fu740_spi_ctrl *fu740_spi_ctrls[] = {
(struct fu740_spi_ctrl *)FU740_QSPI0,
(struct fu740_spi_ctrl *)FU740_QSPI1,
(struct fu740_spi_ctrl *)FU740_QSPI2
};
// Wait until SPI is ready for transmission and transmit byte.
static void fu740_spi_tx(volatile struct fu740_spi_ctrl *spictrl_reg, uint8_t in)
{
#if __riscv_atomic
int32_t r;
do {
asm volatile (
"amoor.w %0, %2, %1\n"
: "=r" (r), "+A" (spictrl_reg->txdata.raw_bits)
: "r" (in)
);
} while (r < 0);
#else
while ((int32_t) spictrl_reg->txdata.raw_bits < 0)
;
spictrl_reg->txdata.data = in;
#endif
}
// Wait until SPI receive queue has data and read byte.
static uint8_t fu740_spi_rx(volatile struct fu740_spi_ctrl *spictrl_reg)
{
int32_t out;
while ((out = (int32_t) spictrl_reg->rxdata.raw_bits) < 0)
;
return (uint8_t) out;
}
static int fu740_spi_xfer(const struct spi_slave *slave,
const void *dout, size_t bytesout,
void *din, size_t bytesin)
{
printk(BIOS_DEBUG, "%s 0, bytesin: %zu, bytesout: %zu, din: %p\n", __func__, bytesin, bytesout, din);
hexdump(dout, bytesout);
struct fu740_spi_ctrl *spictrl_reg = fu740_spi_ctrls[slave->bus];
union fu740_spi_reg_fmt fmt;
fmt.raw_bits = read32(&spictrl_reg->fmt.raw_bits);
if (fmt.proto == FU740_SPI_PROTO_S) {
// working in full-duplex mode
// receiving data needs to be triggered by sending data
while (bytesout || bytesin) {
uint8_t in, out = 0;
if (bytesout) {
out = *(uint8_t *)dout++;
bytesout--;
}
fu740_spi_tx(spictrl_reg, out);
in = fu740_spi_rx(spictrl_reg);
if (bytesin) {
*(uint8_t *)din++ = in;
bytesin--;
}
}
} else {
// Working in half duplex
// send and receive can be done separately
if (dout && din)
return -1;
if (dout) {
while (bytesout) {
fu740_spi_tx(spictrl_reg, *(uint8_t *)dout++);
bytesout--;
}
}
if (din) {
while (bytesin) {
*(uint8_t *)din++ = fu740_spi_rx(spictrl_reg);
bytesin--;
}
}
}
return 0;
}
static int fu740_spi_claim_bus(const struct spi_slave *slave)
{
struct fu740_spi_ctrl *spictrl = fu740_spi_ctrls[slave->bus];
union fu740_spi_reg_csmode csmode;
csmode.raw_bits = 0;
csmode.mode = FU740_SPI_CSMODE_HOLD;
write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
return 0;
}
static void fu740_spi_release_bus(const struct spi_slave *slave)
{
struct fu740_spi_ctrl *spictrl = fu740_spi_ctrls[slave->bus];
union fu740_spi_reg_csmode csmode;
csmode.raw_bits = 0;
csmode.mode = FU740_SPI_CSMODE_OFF;
write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
}
// reset spi flash chip
static void fu740_spi_reset(volatile struct fu740_spi_ctrl *spictrl_reg)
{
fu740_spi_tx(spictrl_reg, 0x66);
fu740_spi_tx(spictrl_reg, 0x99);
}
// setup the ffmt (SPI flash instruction format) register
__maybe_unused static int fu740_spi_setup_ffmt(volatile struct fu740_spi_ctrl *spictrl_reg,
const struct fu740_spi_config *config)
{
//union fu740_spi_reg_fctrl fctrl;
union fu740_spi_reg_ffmt ffmt;
printk(BIOS_DEBUG, "config->data_proto: %d, config->cmd_code: %d\n",
config->ffmt_config.data_proto,
config->ffmt_config.cmd_code);
//TODO test without this here
fu740_spi_reset(spictrl_reg);
ffmt.raw_bits = 0;
ffmt.cmd_en = 1; // enable sending of command
ffmt.addr_len = 3; // number of address bytes (0-4)
ffmt.pad_cnt = 0; // number of dummy cycles TODO maybe not working
ffmt.cmd_proto = FU740_SPI_PROTO_S; // protocol for transmitting command
ffmt.addr_proto = FU740_SPI_PROTO_S; // protocol for transmitting address and padding
ffmt.data_proto = config->ffmt_config.data_proto; // protocol for receiving data bytes
ffmt.cmd_code = config->ffmt_config.cmd_code; // value of command byte
ffmt.pad_code = 0; // First 8 bits to transmit during dummy cycles
write32(&spictrl_reg->ffmt.raw_bits, ffmt.raw_bits);
return 0;
}
int fu740_spi_setup(const struct spi_slave *slave)
{
union fu740_spi_reg_csmode csmode;
union fu740_spi_reg_fctrl fctrl;
struct fu740_spi_ctrl *spictrl_reg = fu740_spi_ctrls[slave->bus];
struct fu740_spi_config *config = &fu740_spi_configs[slave->bus];
if ((config->pha > 1)
|| (config->pol > 1)
|| (config->fmt_config.protocol > 2)
|| (config->fmt_config.endianness > 1)
|| (config->fmt_config.bits_per_frame > 8))
return -1;
write32(&spictrl_reg->sckdiv, fu740_spi_min_clk_divisor(clock_get_pclk(), config->freq));
/* disable direct memory-mapped spi flash mode */
//TODO test if we need to disable it before changing the settings
fctrl.raw_bits = 0;
fctrl.en = 0;
write32(&spictrl_reg->fctrl.raw_bits, fctrl.raw_bits);
csmode.raw_bits = 0;
csmode.mode = FU740_SPI_CSMODE_HOLD;
write32(&spictrl_reg->csmode.raw_bits, csmode.raw_bits);
char din[10];
char dout[10] = { 0x66 };
slave->ctrlr->xfer(slave, dout, 1, din, 0);
dout[0] = 0x99;
slave->ctrlr->xfer(slave, dout, 1, din, 0);
int addr = 0x200;
dout[0] = 0x03;
dout[1] = (addr >> 16) & 0xFF;
dout[2] = (addr >> 8) & 0xFF;
dout[3] = addr & 0xFF;
slave->ctrlr->xfer(slave, dout, 4, din, 10);
csmode.mode = FU740_SPI_CSMODE_AUTO;
write32(&spictrl_reg->csmode.raw_bits, csmode.raw_bits);
din[9] = 0;
return 0;
}
struct spi_ctrlr fu740_spi_ctrlr = {
.xfer = fu740_spi_xfer,
.setup = fu740_spi_setup,
.claim_bus = fu740_spi_claim_bus,
.release_bus = fu740_spi_release_bus,
.max_xfer_size = SPI_CTRLR_DEFAULT_MAX_XFER_SIZE,
};
const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
{
.bus_start = 0,
.bus_end = 2,
.ctrlr = &fu740_spi_ctrlr,
}
};
const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);

View File

@@ -0,0 +1,227 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
#define __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
#include <stdint.h>
#define _ASSERT_SIZEOF(type, size) _Static_assert( \
sizeof(type) == (size), \
#type " must be " #size " bytes wide")
// Chip Select Mode Register (csmode)
#define FU740_SPI_CSMODE_AUTO 0
#define FU740_SPI_CSMODE_HOLD 2
#define FU740_SPI_CSMODE_OFF 3
union fu740_spi_reg_sckmode {
struct {
uint32_t pha : 1;
uint32_t pol : 1;
uint32_t reserved : 30;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_sckmode, 4);
union fu740_spi_reg_csmode {
struct {
uint32_t mode : 2;
uint32_t reserved : 30;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_csmode, 4);
union fu740_spi_reg_delay0 {
struct {
uint32_t cssck : 8;
uint32_t reserved0 : 8;
uint32_t sckcs : 8;
uint32_t reserved1 : 8;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_delay0, 4);
union fu740_spi_reg_delay1 {
struct {
uint32_t intercs : 8;
uint32_t reserved0 : 8;
uint32_t interxfr : 8;
uint32_t reserved1 : 8;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_delay1, 4);
union fu740_spi_reg_fmt {
struct {
uint32_t proto : 2;
uint32_t endian : 1;
uint32_t dir : 1;
uint32_t reserved0 : 12;
uint32_t len : 4;
uint32_t reserved1 : 12;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_fmt, 4);
union fu740_spi_reg_txdata {
struct {
uint32_t data : 8;
uint32_t reserved : 23;
uint32_t full : 1;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_txdata, 4);
union fu740_spi_reg_rxdata {
struct {
uint32_t data : 8;
uint32_t reserved : 23;
uint32_t empty : 1;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_rxdata, 4);
union fu740_spi_reg_txmark {
struct {
uint32_t txmark : 3;
uint32_t reserved : 29;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_txmark, 4);
union fu740_spi_reg_rxmark {
struct {
uint32_t rxmark : 3;
uint32_t reserved : 29;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_rxmark, 4);
union fu740_spi_reg_fctrl {
struct {
uint32_t en : 1;
uint32_t reserved : 31;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_fctrl, 4);
union fu740_spi_reg_ffmt {
struct {
uint32_t cmd_en : 1;
uint32_t addr_len : 3;
uint32_t pad_cnt : 4;
uint32_t cmd_proto : 2;
uint32_t addr_proto : 2;
uint32_t data_proto : 2;
uint32_t reserved : 2;
uint32_t cmd_code : 8;
uint32_t pad_code : 8;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_ffmt, 4);
union fu740_spi_reg_ie {
struct {
uint32_t txwm : 1;
uint32_t rxwm : 1;
uint32_t reserved : 30;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_ie, 4);
union fu740_spi_reg_ip {
struct {
uint32_t txwm : 1;
uint32_t rxwm : 1;
uint32_t reserved : 30;
};
uint32_t raw_bits;
};
_ASSERT_SIZEOF(union fu740_spi_reg_ip, 4);
#undef _ASSERT_SIZEOF
/**
* SPI control register memory map.
*
* All functions take a pointer to a SPI device's control registers.
*/
struct fu740_spi_ctrl {
uint32_t sckdiv;
union fu740_spi_reg_sckmode sckmode;
uint32_t reserved08;
uint32_t reserved0c;
uint32_t csid;
uint32_t csdef;
union fu740_spi_reg_csmode csmode;
uint32_t reserved1c;
uint32_t reserved20;
uint32_t reserved24;
union fu740_spi_reg_delay0 delay0;
union fu740_spi_reg_delay1 delay1;
uint32_t reserved30;
uint32_t reserved34;
uint32_t reserved38;
uint32_t reserved3c;
union fu740_spi_reg_fmt fmt;
uint32_t reserved44;
union fu740_spi_reg_txdata txdata;
union fu740_spi_reg_rxdata rxdata;
union fu740_spi_reg_txmark txmark;
union fu740_spi_reg_rxmark rxmark;
uint32_t reserved58;
uint32_t reserved5c;
union fu740_spi_reg_fctrl fctrl;
union fu740_spi_reg_ffmt ffmt;
uint32_t reserved68;
uint32_t reserved6c;
union fu740_spi_reg_ie ie;
union fu740_spi_reg_ip ip;
};
/**
* Get smallest clock divisor that divides input_khz to a quotient less than or
* equal to max_target_khz;
*/
static inline unsigned int
fu740_spi_min_clk_divisor(unsigned int input_khz, unsigned int max_target_khz)
{
// f_sck = f_in / (2 * (div + 1)) => div = (f_in / (2*f_sck)) - 1
//
// The nearest integer solution for div requires rounding up as to not
// exceed max_target_khz.
//
// div = ceil(f_in / (2*f_sck)) - 1
// = floor((f_in - 1 + 2*f_sck) / (2*f_sck)) - 1
//
// This should not overflow as long as (f_in - 1 + 2*f_sck) does not
// exceed 2^32 - 1, which is unlikely since we represent frequencies
// in kHz.
unsigned int quotient =
(input_khz + 2 * max_target_khz - 1) / (2 * max_target_khz);
// Avoid underflow
if (quotient == 0)
return 0;
return quotient - 1;
}
#endif /* __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__ */

View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <console/uart.h>
#include <commonlib/bsd/helpers.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
uintptr_t uart_platform_base(unsigned int idx)
{
if (idx < 2)
return FU740_UART(idx);
else
return 0;
}
unsigned int uart_platform_refclk(void)
{
// peripheral clock is attached to UART subsystem
return clock_get_pclk();
}