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:
committed by
ron minnich
parent
ec7b480760
commit
2ccb8e7891
@@ -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 */
|
||||
|
63
src/soc/sifive/fu740/Kconfig
Normal file
63
src/soc/sifive/fu740/Kconfig
Normal 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
|
32
src/soc/sifive/fu740/Makefile.inc
Normal file
32
src/soc/sifive/fu740/Makefile.inc
Normal 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
51
src/soc/sifive/fu740/TODO
Normal 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
|
13
src/soc/sifive/fu740/cbmem.c
Normal file
13
src/soc/sifive/fu740/cbmem.c
Normal 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);
|
||||
}
|
17
src/soc/sifive/fu740/chip.c
Normal file
17
src/soc/sifive/fu740/chip.c
Normal 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,
|
||||
};
|
21
src/soc/sifive/fu740/clint.c
Normal file
21
src/soc/sifive/fu740/clint.c
Normal 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);
|
||||
}
|
448
src/soc/sifive/fu740/clock.c
Normal file
448
src/soc/sifive/fu740/clock.c
Normal 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);
|
||||
}
|
1496
src/soc/sifive/fu740/ddrregs.c
Normal file
1496
src/soc/sifive/fu740/ddrregs.c
Normal file
File diff suppressed because it is too large
Load Diff
64
src/soc/sifive/fu740/gpio.c
Normal file
64
src/soc/sifive/fu740/gpio.c
Normal 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);
|
||||
}
|
41
src/soc/sifive/fu740/include/soc/addressmap.h
Normal file
41
src/soc/sifive/fu740/include/soc/addressmap.h
Normal 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)
|
9
src/soc/sifive/fu740/include/soc/clock.h
Normal file
9
src/soc/sifive/fu740/include/soc/clock.h
Normal 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__ */
|
36
src/soc/sifive/fu740/include/soc/gpio.h
Normal file
36
src/soc/sifive/fu740/include/soc/gpio.h
Normal 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_
|
11
src/soc/sifive/fu740/include/soc/otp.h
Normal file
11
src/soc/sifive/fu740/include/soc/otp.h
Normal 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__ */
|
12
src/soc/sifive/fu740/include/soc/sdram.h
Normal file
12
src/soc/sifive/fu740/include/soc/sdram.h
Normal 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
|
56
src/soc/sifive/fu740/include/soc/spi.h
Normal file
56
src/soc/sifive/fu740/include/soc/spi.h
Normal 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__ */
|
26
src/soc/sifive/fu740/memlayout.ld
Normal file
26
src/soc/sifive/fu740/memlayout.ld
Normal 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)
|
||||
}
|
90
src/soc/sifive/fu740/otp.c
Normal file
90
src/soc/sifive/fu740/otp.c
Normal 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(®s->pdstb, 0x01);
|
||||
|
||||
// enable repair function
|
||||
write32(®s->ptrim, 0x01);
|
||||
|
||||
// enable input
|
||||
write32(®s->pce, 0x01);
|
||||
|
||||
// address to read
|
||||
write32(®s->pa, idx);
|
||||
|
||||
// cycle clock to read
|
||||
write32(®s->pclk, 0x01);
|
||||
mdelay(1);
|
||||
|
||||
write32(®s->pclk, 0x00);
|
||||
mdelay(1);
|
||||
|
||||
w = read32(®s->pdout);
|
||||
|
||||
// shut down
|
||||
write32(®s->pce, 0x00);
|
||||
write32(®s->ptrim, 0x00);
|
||||
write32(®s->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;
|
||||
}
|
246
src/soc/sifive/fu740/sdram.c
Normal file
246
src/soc/sifive/fu740/sdram.c
Normal 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
209
src/soc/sifive/fu740/spi.c
Normal 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);
|
227
src/soc/sifive/fu740/spi_internal.h
Normal file
227
src/soc/sifive/fu740/spi_internal.h
Normal 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__ */
|
21
src/soc/sifive/fu740/uart.c
Normal file
21
src/soc/sifive/fu740/uart.c
Normal 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();
|
||||
}
|
Reference in New Issue
Block a user