cavium: Add CN81xx SoC and eval board support

This adds Cavium CN81xx SoC and SFF EVB files.

Code is based off of Cavium's Octeon-TX SDK:
https://github.com/Cavium-Open-Source-Distributions/OCTEON-TX-SDK

BDK coreboot differences:
bootblock:
- Get rid of BDK header
- Add Kconfig for link address
- Move CAR setup code into assembly
- Move unaligned memory access enable into assembly
- Implement custom bootblock entry function
- Add CLIB and CSIB blobs

romstage:
- Use minimal DRAM init only

devicetree:
- Convert FTD to static C file containing key value pairs

Tested on CN81xx:
- Boots to payload
- Tested with GNU/Linux 4.16.3
- All hardware is usable (after applying additional commits)

Implemented in future commits:
- Vboot integration
- MMU suuport
- L2 Cache handling
- ATF from external repo
- Devicetree patching
- Extended DRAM testing
- UART init

Not working:
- Booting a payload
- Booting upstream ATF

TODO:
- Configuration straps

Change-Id: I47b4412d29203b45aee49bfa026c1d86ef7ce688
Signed-off-by: David Hendricks <dhendricks@fb.com>
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/23037
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
This commit is contained in:
David Hendricks
2017-12-01 20:49:48 -08:00
committed by Patrick Rudolph
parent 03d3142733
commit 8cbd569f74
55 changed files with 5750 additions and 9 deletions

2
src/soc/cavium/Kconfig Normal file
View File

@@ -0,0 +1,2 @@
# Load all chipsets
source "src/soc/cavium/*/Kconfig"

View File

@@ -0,0 +1,27 @@
config SOC_CAVIUM_CN81XX
bool
default n
select ARCH_BOOTBLOCK_ARMV8_64
select ARCH_RAMSTAGE_ARMV8_64
select ARCH_ROMSTAGE_ARMV8_64
select ARCH_VERSTAGE_ARMV8_64
select BOOTBLOCK_CONSOLE
select DRIVERS_UART_PL011
select GENERIC_UDELAY
select HAVE_MONOTONIC_TIMER
select UART_OVERRIDE_REFCLK
select SOC_CAVIUM_COMMON
if SOC_CAVIUM_CN81XX
config ARCH_ARMV8_EXTENSION
int
default 1
config HEAP_SIZE
default 0x10000
config STACK_SIZE
default 0x2000
endif

View File

@@ -0,0 +1,70 @@
##
## This file is part of the coreboot project.
##
## Copyright 2017-present Facebook, Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; version 2 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
ifeq ($(CONFIG_SOC_CAVIUM_CN81XX),y)
# bootblock
bootblock-$(CONFIG_BOOTBLOCK_CUSTOM) += bootblock_custom.S
bootblock-y += bootblock.c
bootblock-y += twsi.c
bootblock-y += clock.c
bootblock-y += gpio.c
bootblock-y += timer.c
bootblock-y += spi.c
bootblock-y += uart.c
bootblock-y += cpu.c
ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y)
bootblock-$(CONFIG_DRIVERS_UART) += uart.c
endif
################################################################################
# romstage
romstage-y += twsi.c
romstage-y += clock.c
romstage-y += gpio.c
romstage-y += timer.c
romstage-y += spi.c
romstage-y += uart.c
romstage-$(CONFIG_DRIVERS_UART) += uart.c
romstage-< += cpu.c
romstage-y += sdram.c
romstage-y += ../common/cbmem.c
# BDK coreboot interface
romstage-y += ../common/bdk-coreboot.c
################################################################################
# ramstage
ramstage-y += twsi.c
ramstage-y += clock.c
ramstage-y += gpio.c
ramstage-y += timer.c
ramstage-y += spi.c
ramstage-y += uart.c
ramstage-$(CONFIG_DRIVERS_UART) += uart.c
ramstage-y += sdram.c
ramstage-y += soc.c
ramstage-y += cpu.c
# BDK coreboot interface
ramstage-y += ../common/bdk-coreboot.c
CPPFLAGS_common += -Isrc/soc/cavium/cn81xx/include
endif

View File

@@ -0,0 +1,50 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <arch/io.h>
#include <commonlib/helpers.h>
#include <soc/bootblock.h>
#include <soc/sysreg.h>
#include <soc/timer.h>
#include <libbdk-arch/bdk-asm.h>
static void init_sysreg(void)
{
/* The defaults for write buffer timeouts are poor */
u64 cvmmemctl0;
BDK_MRS(s3_0_c11_c0_4, cvmmemctl0);
cvmmemctl0 |= AP_CVMMEMCTL0_EL1_WBFTONSHENA |
AP_CVMMEMCTL0_EL1_WBFTOMRGCLRENA;
BDK_MSR(s3_0_c11_c0_4, cvmmemctl0);
}
void bootblock_soc_early_init(void)
{
}
void bootblock_soc_init(void)
{
/* initialize system registers */
init_sysreg();
/* Set watchdog to 5 seconds timeout */
watchdog_set(0, 5000);
watchdog_poke(0);
/* TODO: additional clock init? */
}

View File

@@ -0,0 +1,257 @@
/*
* Early initialization code for aarch64 (a.k.a. armv8)
*
* Copyright 2016 Cavium, Inc. <support@cavium.com>
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <arch/asm.h>
#include <soc/addressmap.h>
ENTRY(_start)
.org 0
/**
* According to the reference manual the first instruction is fetched from
* offset 0x100, but at offset 0 a branch instruction is always placed.
* Support two entry points for now.
* To save memory put the cavium specific init code between those to entry
* points.
*/
ic ialluis
fmov d30, x0 /* Save X0 in FPR for use later */
fmov d31, x1 /* Save X1 in FPR for use later */
adr x1, _start /* x1 = _start location based on PC */
fmov d29, x1 /* Save PC in FPR for use later */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
/* Change the core to big endian mode for EL3 */
mrs x0, SCTLR_EL3
mov x1, 1<<25 /* Set SCTLR_EL3[ee]=1 */
orr x0, x0, x1
msr SCTLR_EL3, x0
#define ENDIAN_CONVERT64(reg) rev reg, reg
#define ENDIAN_CONVERT32(reg) rev reg, reg
#define ENDIAN_CONVERT16(reg) rev16 reg, reg
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/* Nothing needed, default is little endian */
#define ENDIAN_CONVERT64(reg)
#define ENDIAN_CONVERT32(reg)
#define ENDIAN_CONVERT16(reg)
#else
#error Unknown endianness
#endif
mov x0, (LMC0_PF_BAR0 >> 32)
lsl x0, x0, 32
mov x1, (LMC0_PF_BAR0 & 0xffffffff)
orr x0, x0, x1
/* Test if DRAM PLL is running */
ldr x1, [x0, LMC0_DDR_PLL_CTL0]
tst x1, 0x80
b.ne cache_setup_done
bl _setup_car
cache_setup_done:
/* Check that we're running on the node we're linked for */
mrs x0, MPIDR_EL1
ubfx x0, x0, 16, 8 /* Bits 23:16 are the physical node ID */
mov x1, 0x0
cmp x0, x1
b.ne _wfi
node_check_done:
/* Get code position */
mov x1, 0x020000
mov x0, BOOTROM_OFFSET
add x1, x0, x1
adr x0, _start
/**
* Check if IROM has loaded the code to CONFIG_BOOTROM_OFFSET.
* In case the offset is wrong, try to relocate.
* Ideally the following code is never executed.
* FIXME: Add region overlap check.
*/
cmp x0, x1
b.eq after_relocate
relocate:
/* Get bootblock length */
ldr x2, =_program
ldr x3, =_eprogram
sub x2, x2, x3
b copy_code
.align 7
copy_code:
ldp q0, q1, [x1], 32 /* Load 32 bytes */
subs w2, w2, 32 /* Subtract 32 from length, setting flags */
stp q0, q1, [x0], 32 /* Store 32 bytes */
b.gt copy_code /* Repeat if length is still positive */
dmb sy
/* Load the actual location we're suppose to be at */
adr x0, after_relocate /* Relative address */
adr x1, _start /* Relative address */
sub x0, x0, x1 /* This only works if _start is suppose to be zero */
mov x1, BOOTROM_OFFSET
add x0, x0, x1
br x0 /* Branch to relocated code */
ic ialluis /* Clear the icache now that all code is correct */
after_relocate:
/* Allow unaligned memory access as long as MMU is disabled */
mrs x22, s3_0_c11_c0_4
orr x22, x22, # (1 << 37) /* Set DCVA47 */
msr s3_0_c11_c0_4, x22
bl start
/* Real entry point */
.org 0x100
b _start
ENDPROC(_start)
ENTRY(_setup_car)
mrs x0, MIDR_EL1
ubfx x0, x0, 4, 12 /* Bits 15:4 are the part number */
cmp x0, 0xb0
b.ge _wfi
thunder1_cache_setup:
/**
* Setup L2 cache to allow secure access to all of the address space
* thunder1 compability list:
* - CN81XX
* - CN83XX
* - CN88XX
*/
#define REGIONX_START 0x1000
#define REGIONX_END 0x1008
#define REGIONX_ATTR 0x1010
mov x0, L2C_PF_BAR0 >> 32
lsl x0, x0, 32
mov x1, (L2C_PF_BAR0 & 0xffffffff)
orr x0, x0, x1
str xzr, [x0, REGIONX_START] /* Start of zero */
mov x1, 0x3fffff00000 /* End of max address */
ENDIAN_CONVERT64(x1)
str x1, [x0, REGIONX_END]
mov x1, 2 /* Secure only access */
ENDIAN_CONVERT64(x1)
str x1, [x0, REGIONX_ATTR]
/* Update way partition to allow core 0 to write to L2 */
#define L2C_WPAR_PP0_OFFSET 0x40000
mov x1, L2C_WPAR_PP0_OFFSET
str xzr, [x0, x1]
ldr xzr, [x0, x1] /* Read back to make sure done */
#undef REGIONX_START
#undef REGIONX_END
#undef REGIONX_ATTR
#undef L2C_WPAR_PP0_OFFSET
/**
* At this point the whole CAR is readable and writeable, but if
* we touch to many cache-lines our code might get flushed out.
* We have to lock all cache-lines that are to be used as RAM, which are
* the ones marked as SRAM in memlayout.
*/
mrs x0, CTR_EL0 /* Get cache-line size */
/* [19:16] - Indicates (Log2(number of words in cache line) */
ubfx x0, x0, 16, 4
mov x1, 4 /* Bytes in a word (32-bit) */
lsl x0, x1, x0 /* Number of Bytes in x0 */
sub x1, x0, 1
mvn x1, x1 /* Place mask in x1 */
ldr x3, =_sram
and x3, x3, x1 /* Align addresses with cache-lines */
ldr x4, =_esram
add x4, x4, x0
sub x4, x4, 1
and x4, x4, x1 /* Align addresses with cache-lines */
sub x2, x4, x3 /* Store sram length in x2 */
lock_cache_lines:
sys #0, c11, c1, #4, x3
add x3, x3, x0 /* Increment address by cache-line bytes */
subs w2, w2, w0 /* Subtract cache-line bytes from length */
b.gt lock_cache_lines /* Repeat if length is still positive */
/**
* The locked region isn't considered dirty by L2. Do read/write of
* each cache line to force each to be dirty. This is needed across the
* whole line to make sure the L2 dirty bits are all up to date.
* NOTE: If we'd relocate we could memset the whole memory !
*/
ldr x3, =_sram
and x3, x3, x1 /* Align addresses with cache-lines */
ldr x4, =_esram
add x4, x4, x0
sub x4, x4, 1
and x4, x4, x1 /* Align addresses with cache-lines */
sub x2, x4, x3 /* Store sram length in x2 */
mov x4, x3
b dirty_cache_line
.align 7
dirty_cache_line:
ldp q0, q1, [x3], 32 /* Load 32 bytes */
subs w2, w2, 32 /* Subtract 32 from length, setting flags */
stp q0, q1, [x4], 32 /* Store 32 bytes */
b.gt dirty_cache_line /* Repeat if length is still positive */
dmb sy
clear_interrupts:
/**
* As the memory controller isn't running, but we access the DRAM's
* address space, some interrupt flags had been set.
* Tidy up our mess now on (valid for CN81XX only).
*/
mov x0, (L2C_TAD0_INT_W1C >> 32)
lsl x0, x0, 32
mov x1, (L2C_TAD0_INT_W1C & 0xffffffff)
orr x0, x0, x1
ldr x1, [x0]
orr x1, x1, 0x1c00 /* Clear WRDISLMC, RDDISLMC, RDNXM */
str x1, [x0]
ret
ENDPROC(_setup_car)
ENTRY(_wfi)
wfi
ENDPROC(_wfi)
ENTRY(start)
bl arm64_init_cpu
fmov x0, d30 /* The original X0, info from previous image */
fmov x1, d31 /* The original X1, info from previous image */
fmov x2, d29 /* The original PC we were loaded at */
/* Call C entry */
bl bootblock_main
ENDPROC(start)

View File

@@ -0,0 +1,22 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_CHIP_H
#define __SOC_CAVIUM_CN81XX_CHIP_H
struct soc_cavium_cn81xx_config {
};
#endif /* __SOC_CAVIUM_CN81XX_CHIP_H */

View File

@@ -0,0 +1,79 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <soc/clock.h>
#include <arch/io.h>
#include <soc/addressmap.h>
#define PLL_REF_CLK 50000000 /* 50 MHz */
union cavm_rst_boot {
u64 u;
struct {
u64 rboot_pin:1;
u64 rboot:1;
u64 lboot:10;
u64 lboot_ext23:6;
u64 lboot_ext45:6;
u64 reserved_24_29:6;
u64 lboot_oci:3;
u64 pnr_mul:6;
u64 reserved_39_39:1;
u64 c_mul:7;
u64 reserved_47_54:8;
u64 dis_scan:1;
u64 dis_huk:1;
u64 vrm_err:1;
u64 jt_tstmode:1;
u64 ckill_ppdis:1;
u64 trusted_mode:1;
u64 ejtagdis:1;
u64 jtcsrdis:1;
u64 chipkill:1;
} s;
};
/**
* Returns the reference clock speed in Hz
*/
u64 thunderx_get_ref_clock(void)
{
return PLL_REF_CLK;
}
/**
* Returns the I/O clock speed in Hz
*/
u64 thunderx_get_io_clock(void)
{
union cavm_rst_boot rst_boot;
rst_boot.u = read64((void *)RST_PF_BAR0);
return rst_boot.s.pnr_mul * PLL_REF_CLK;
}
/**
* Returns the core clock speed in Hz
*/
u64 thunderx_get_core_clock(void)
{
union cavm_rst_boot rst_boot;
rst_boot.u = read64((void *)RST_PF_BAR0);
return rst_boot.s.c_mul * PLL_REF_CLK;
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <types.h>
#include <arch/io.h>
#include <soc/cpu.h>
#include <bdk-coreboot.h>
/* Return the number of cores available in the chip */
size_t cpu_get_num_cores(void)
{
uint64_t available = read64((void *)0x87e006001738ll);
return bdk_dpop(available);
}

View File

@@ -0,0 +1,193 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <console/console.h>
#include <soc/gpio.h>
#include <arch/io.h>
#include <endian.h>
#include <soc/addressmap.h>
union gpio_const {
u64 u;
struct {
u64 gpios:8; /** Number of GPIOs implemented */
u64 pp:8; /** Number of PP vectors */
u64:48; /* Reserved */
} s;
};
union bit_cfg {
u64 u;
struct {
u64 tx_oe : 1; /* Output Enable */
u64 xor : 1; /* Invert */
u64 int_en : 1; /* Interrupt Enable */
u64 int_type : 1; /* Type of Interrupt */
u64 filt_cnt : 4; /* Glitch filter counter */
u64 filt_sel : 4; /* Glitch filter select */
u64 tx_od : 1; /* Set Output to Open Drain */
u64 : 3;
u64 pin_sel : 10; /* Select type of pin */
u64 : 38;
} s;
};
struct cavium_gpio {
u64 rx_dat;
u64 tx_set;
u64 tx_clr;
u64 multicast;
u64 ocla_exten_trg;
u64 strap;
u64 reserved[12];
union gpio_const gpio_const; /* Offset 90 */
u64 reserved2[109];
union bit_cfg bit_cfg[48]; /* Offset 400 */
};
/* Base address of GPIO BAR */
static const void *gpio_get_baseaddr(void)
{
return (const void *)GPIO_PF_BAR0;
}
/* Number of GPIO pins. Usually 48. */
gpio_t gpio_pin_count(void)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union gpio_const gpio_const;
gpio_const.u = read64(&regs->gpio_const.u);
if (gpio_const.s.gpios > 64)
return 64; // FIXME: Add support for more than 64 GPIOs
return gpio_const.s.gpios;
}
/* Set GPIO to software control and direction INPUT */
void gpio_input(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
printk(BIOS_SPEW, "GPIO(%u): direction input\n", gpio);
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.pin_sel = 0;
bit_cfg.s.tx_oe = 0;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
}
/* Set GPIO of direction OUTPUT to level */
void gpio_set(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return;
printk(BIOS_SPEW, "GPIO(%u): level: %u\n", gpio, !!value);
if (value)
write64(&regs->tx_set, 1 << gpio);
else
write64(&regs->tx_clr, 1 << gpio);
}
/* Set GPIO direction to OUTPUT with level */
void gpio_output(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
gpio_set(gpio, value);
printk(BIOS_SPEW, "GPIO(%u): direction output with level: %u\n", gpio,
!!value);
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.pin_sel = 0;
bit_cfg.s.tx_oe = 1;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
}
/* Set GPIO invert flag, that affects INPUT and OUTPUT */
void gpio_invert(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.xor = !!value;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
printk(BIOS_SPEW, "GPIO(%u): invert: %s\n", gpio, value ? "ON" : "OFF");
}
/* Read GPIO level with direction set to INPUT */
int gpio_get(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return 0;
const u64 reg = read64(&regs->rx_dat);
printk(BIOS_SPEW, "GPIO(%u): input: %u\n", gpio, !!(reg & (1 << gpio)));
return !!(reg & (1 << gpio));
}
/* Read GPIO STRAP level sampled at cold boot */
int gpio_strap_value(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return 0;
const u64 reg = read64(&regs->strap);
printk(BIOS_SPEW, "GPIO(%u): strap: %u\n", gpio, !!(reg & (1 << gpio)));
return !!(reg & (1 << gpio));
}
/* FIXME: Parse devicetree ? */
void gpio_init(void)
{
const size_t pin_count = gpio_pin_count();
printk(BIOS_DEBUG, "GPIO: base address: %p, pin count: %zd\n",
gpio_get_baseaddr(), pin_count);
if (!pin_count)
return;
}
void gpio_input_pulldown(gpio_t gpio)
{
}
void gpio_input_pullup(gpio_t gpio)
{
}

View File

@@ -0,0 +1,123 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_ADDRESSMAP_H__
#define __SOC_CAVIUM_CN81XX_ADDRESSMAP_H__
#define MAX_DRAM_ADDRESS 0x2000000000ULL /* 128GB */
/* Physical addressed with bit 47 set indicate I/O memory space. */
/* ARM code entry vector */
#define BOOTROM_OFFSET 0x100000
/* L2C */
#define L2C_PF_BAR0 0x87E080800000ULL
#define L2C_TAD0_PF_BAR0 (0x87E050000000ULL + 0x10000)
#define L2C_TAD0_INT_W1C (0x87E050000000ULL + 0x40000)
#define L2C_CBC0_PF_BAR0 0x87E058000000ULL
#define L2C_MCI0_PF_BAR0 0x87E05C000000ULL
/* LMC */
#define LMC0_PF_BAR0 0x87E088000000ULL
#define LMC0_DDR_PLL_CTL0 0x258
/* OCLA */
/* IOB */
#define IOBN0_PF_BAR0 0x87E0F0000000ULL
#define MRML_PF_BAR0 0x87E0FC000000ULL
/* SMMU */
#define SMMU_PF_BAR0 0x830000000000ULL
/* GTI */
#define GTI_PF_BAR0 0x844000000000ULL
/* PCC */
#define ECAM_PF_BAR2 0x848000000000ULL
/* CPT */
/* SLI */
/* RST */
#define RST_PF_BAR0 (0x87E006000000ULL + 0x1600)
#define FUSF_PF_BAR0 0x87E004000000ULL
#define MIO_FUS_PF_BAR0 0x87E003000000ULL
#define MIO_BOOT_PF_BAR0 0x87E000000000ULL
/* PTP */
#define MIO_PTP_PF_BAR0 0x807000000000ULL
/* GIC */
/* NIC */
/* LBK */
#define GTI_PF_BAR0 0x844000000000ULL
/* DAP */
/* BCH */
/* KEY */
/* RNG */
#define GSER0_PF_BAR0 (0x87E090000000ULL + (0 << 24))
#define GSER1_PF_BAR0 (0x87E090000000ULL + (1 << 24))
#define GSER2_PF_BAR0 (0x87E090000000ULL + (2 << 24))
#define GSER3_PF_BAR0 (0x87E090000000ULL + (3 << 24))
#define GSERx_PF_BAR0(x) \
((((x) == 0) || ((x) == 1) || ((x) == 2) || ((x) == 3)) ? \
(0x87E090000000ULL + ((x) << 24)) : 0)
/* PEM */
/* SATA */
/* USB */
/* UAA */
#define UAA0_PF_BAR0 (0x87E028000000ULL + (0 << 24))
#define UAA1_PF_BAR0 (0x87E028000000ULL + (1 << 24))
#define UAA2_PF_BAR0 (0x87E028000000ULL + (2 << 24))
#define UAA3_PF_BAR0 (0x87E028000000ULL + (3 << 24))
#define UAAx_PF_BAR0(x) \
((((x) == 0) || ((x) == 1) || ((x) == 2) || ((x) == 3)) ? \
(0x87E028000000ULL + ((x) << 24)) : 0)
/* TWSI */
#define MIO_TWS0_PF_BAR0 (0x87E0D0000000ULL + (0 << 24))
#define MIO_TWS1_PF_BAR0 (0x87E0D0000000ULL + (1 << 24))
#define MIO_TWSx_PF_BAR0(x) \
((((x) == 0) || ((x) == 1)) ? (0x87E0D0000000ULL + ((x) << 24)) : 0)
/* GPIO */
#define GPIO_PF_BAR0 0x803000000000ULL
/* SGPIO */
#define SGP_PF_BAR0 0x803000000000ULL
/* SMI */
/* SPI */
#define MPI_PF_BAR0 (0x804000000000ULL + 0x1000)
/* PCM */
/* PBUS */
/* NDF */
/* EMM */
/* VRM */
/* VRM BARs are spaced apart by 0x1000000 */
#define VRM0_PF_BAR0 0x87E021000000ULL
#endif /* __SOC_CAVIUM_CN81XX_ADDRESSMAP_H__ */

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef SRC_SOC_CAVIUM_CN81XX_INCLUDE_CLOCK_H_
#define SRC_SOC_CAVIUM_CN81XX_INCLUDE_CLOCK_H_
#include <types.h>
u64 thunderx_get_ref_clock(void);
u64 thunderx_get_io_clock(void);
u64 thunderx_get_core_clock(void);
#endif /* SRC_SOC_CAVIUM_CN81XX_INCLUDE_CLOCK_H_ */

View File

@@ -0,0 +1,22 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_CPU_H__
#define __SOC_CAVIUM_CN81XX_CPU_H__
size_t cpu_get_num_cores(void);
#endif /* __SOC_CAVIUM_CN81XX_CPU_H__ */

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2016 Rockchip Inc.
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __COREBOOT_SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_GPIO_H
#define __COREBOOT_SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_GPIO_H
#include <types.h>
typedef u32 gpio_t;
#include <gpio.h>
/* The following functions must be implemented by SoC/board code. */
gpio_t gpio_pin_count(void);
void gpio_invert(gpio_t gpio, int value);
int gpio_strap_value(gpio_t gpio);
void gpio_init(void);
#endif

View File

@@ -0,0 +1,41 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2016 Rockchip Inc.
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <memlayout.h>
#include <soc/addressmap.h>
#include <arch/header.ld>
SECTIONS
{
DRAM_START(0x00000000)
/* FIXME: Place BL31 in first 1MiB */
/* bootblock-custom.S does setup CAR from SRAM_START to SRAM_END */
SRAM_START(BOOTROM_OFFSET)
STACK(BOOTROM_OFFSET, 16K)
TIMESTAMP(BOOTROM_OFFSET + 0x4000, 4K)
PRERAM_CBFS_CACHE(BOOTROM_OFFSET + 0x6000, 8K)
PRERAM_CBMEM_CONSOLE(BOOTROM_OFFSET + 0x8000, 8K)
BOOTBLOCK(BOOTROM_OFFSET + 0x20000, 64K)
ROMSTAGE(BOOTROM_OFFSET + 0x40000, 256K)
SRAM_END(BOOTROM_OFFSET + 0x80000)
TTB(BOOTROM_OFFSET + 0x80000, 128K)
RAMSTAGE(BOOTROM_OFFSET + 0xa0000, 512K)
/* Leave some space for the payload */
POSTRAM_CBFS_CACHE(0x2000000, 16M)
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_SDRAM_H__
#define __SOC_CAVIUM_CN81XX_SDRAM_H__
#include <types.h>
size_t sdram_size_mb(void);
void sdram_init(void);
#endif /* !__SOC_CAVIUM_CN81XX_SDRAM_H__ */

View File

@@ -0,0 +1,43 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_INCLUDE_SOC_SOC_H
#define __SOC_CAVIUM_CN81XX_INCLUDE_SOC_SOC_H
#include <inttypes.h>
#include <types.h>
/* MIO BOOT Registers */
struct cn81xx_mio_boot {
u8 rsvd0[0xb0];
u64 thr;
u8 rsvd1[0x8];
u64 pin_defs;
u8 rsvd2[0x8];
u64 ap_jump;
u64 rom_limit;
u8 rsvd3[0x18];
u64 bist_stat;
};
check_member(cn81xx_mio_boot, bist_stat, 0xf8);
/*
* 0 = Board supplies 100MHz to DLM_REF_CLK
* 1 = bOard supplies 50MHz to PLL_REFCLK
* */
#define MIO_BOOT_PIN_DEFS_UART0_RTS (1 << 16)
#define MIO_BOOT_PIN_DEFS_UART1_RTS (1 << 17)
#endif /* ! __SOC_CAVIUM_CN81XX_INCLUDE_SOC_SOC_H */

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H
#define __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H
/* This driver serves as a CBFS media source. */
#include <spi-generic.h>
#include <stddef.h>
void spi_enable(const size_t bus);
void spi_disable(const size_t bus);
void spi_set_cs(const size_t bus,
const size_t chip_select,
const size_t assert_is_low);
void spi_set_clock(const size_t bus,
const size_t speed_hz,
const size_t idle_low,
const size_t idle_cycles);
void spi_set_lsbmsb(const size_t bus, const size_t lsb_first);
void spi_init_custom(const size_t bus,
const size_t speed_hz,
const size_t idle_low,
const size_t idle_cycles,
const size_t lsb_first,
const size_t chip_select,
const size_t assert_is_low);
#endif /* ! __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H */

View File

@@ -0,0 +1,30 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_CN81XX_TIMER_H__
#define __SOC_CAVIUM_CN81XX_TIMER_H__
#include <inttypes.h>
#include <types.h>
#include <timer.h>
#include <delay.h>
/* Watchdog functions */
void watchdog_set(const size_t index, unsigned int timeout_ms);
void watchdog_poke(const size_t index);
void watchdog_disable(const size_t index);
int watchdog_is_running(const size_t index);
#endif /* __SOC_CAVIUM_CN81XX_TIMER_H__ */

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <types.h>
#include <device/i2c.h>
#ifndef __SOC_CAVIUM_CN81XX_INCLUDE_SOC_TWSI_H
#define __SOC_CAVIUM_CN81XX_INCLUDE_SOC_TWSI_H
int twsi_init(unsigned int bus, enum i2c_speed hz);
#endif

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H
#define __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H
#include <inttypes.h>
#include <types.h>
int uart_is_enabled(const size_t bus);
int uart_setup(const size_t bus, int baudrate);
#endif /* __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H */

View File

@@ -0,0 +1,94 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <console/console.h>
#include <soc/sdram.h>
#include <libbdk-arch/bdk-warn.h>
#include <libbdk-arch/bdk-csrs-rst.h>
#include <libbdk-boot/bdk-watchdog.h>
#include <libbdk-dram/bdk-dram-config.h>
#include <libbdk-dram/bdk-dram-test.h>
#include <libbdk-hal/bdk-config.h>
#include <libbdk-hal/bdk-utils.h>
#include <libbdk-hal/bdk-l2c.h>
#include <libdram/libdram-config.h>
size_t sdram_size_mb(void)
{
return bdk_dram_get_size_mbytes(0);
}
/* based on bdk_boot_dram() */
void sdram_init(void)
{
printk(BIOS_DEBUG, "Initializing DRAM\n");
/**
* FIXME: second arg is actually a desired frequency if set (the
* function usually obtains frequency via the config). That might
* be useful if FDT or u-boot env is too cumbersome.
*/
int mbytes = bdk_dram_config(0, 0);
if (mbytes < 0) {
bdk_error("N0: Failed DRAM init\n");
die("DRAM INIT FAILED !\n");
}
/* Poke the watchdog */
bdk_watchdog_poke();
/* Report DRAM status */
printf("N0: DRAM:%s\n", bdk_dram_get_info_string(0));
/* See if we should test this node's DRAM during boot */
int test_dram = bdk_config_get_int(BDK_CONFIG_DRAM_BOOT_TEST, 0);
if (test_dram) {
/* Run the address test to make sure DRAM works */
if (bdk_dram_test(13, 0, 0x10000000000ull, BDK_DRAM_TEST_NO_STATS | (1<<0))) {
/**
* FIXME(dhendrix): This should be handled by mainboard code since we
* don't necessarily have a BMC to report to. Also, we need to figure out
* if we need to keep going as to avoid getting into a boot loop.
*/
// bdk_boot_status(BDK_BOOT_STATUS_REQUEST_POWER_CYCLE);
printk(BIOS_ERR, "%s: Failed DRAM test.\n", __func__);
}
bdk_watchdog_poke();
/* Put other node core back in reset */
if (0 != bdk_numa_master())
BDK_CSR_WRITE(0, BDK_RST_PP_RESET, -1);
/* Clear DRAM */
uint64_t skip = 0;
if (0 == bdk_numa_master())
skip = bdk_dram_get_top_of_bdk();
void *base = bdk_phys_to_ptr(bdk_numa_get_address(0, skip));
bdk_zero_memory(base, ((uint64_t)mbytes << 20) - skip);
bdk_watchdog_poke();
}
/* Unlock L2 now that DRAM works */
if (0 == bdk_numa_master()) {
uint64_t l2_size = bdk_l2c_get_cache_size_bytes(0);
BDK_TRACE(INIT, "Unlocking L2\n");
bdk_l2c_unlock_mem_region(0, 0, l2_size);
bdk_watchdog_poke();
}
printk(BIOS_INFO, "SDRAM initialization finished.\n");
}

View File

@@ -0,0 +1,65 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <bootmode.h>
#include <console/console.h>
#include <cpu/cpu.h>
#include <device/device.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
#include <soc/sdram.h>
#include <soc/timer.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <symbols.h>
#include <libbdk-boot/bdk-boot.h>
static void soc_read_resources(device_t dev)
{
ram_resource(dev, 0, (uintptr_t)_dram / KiB, sdram_size_mb() * KiB);
}
static void soc_init(device_t dev)
{
/* Init ECAM, MDIO, PEM, PHY, QLM ... */
bdk_boot();
/* TODO: additional trustzone init */
}
static void soc_final(device_t dev)
{
watchdog_disable(0);
}
static struct device_operations soc_ops = {
.read_resources = soc_read_resources,
.init = soc_init,
.final = soc_final,
};
static void enable_soc_dev(device_t dev)
{
dev->ops = &soc_ops;
}
struct chip_operations soc_cavium_cn81xx_ops = {
CHIP_NAME("SOC Cavium CN81XX")
.enable_dev = enable_soc_dev,
};

401
src/soc/cavium/cn81xx/spi.c Normal file
View File

@@ -0,0 +1,401 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <arch/io.h>
#include <assert.h>
#include <console/console.h>
#include <delay.h>
#include <endian.h>
#include <soc/addressmap.h>
#include <soc/spi.h>
#include <soc/clock.h>
#include <spi-generic.h>
#include <spi_flash.h>
#include <stdlib.h>
#include <timer.h>
union cavium_spi_cfg {
u64 u;
struct {
u64 enable : 1;
u64 idlelow : 1;
u64 clk_cont : 1;
u64 wireor : 1;
u64 lsbfirst : 1;
u64 : 2;
u64 cshi : 1;
u64 idleclks : 2;
u64 tristate : 1;
u64 cslate : 1;
u64 csena : 4; /* Must be one */
u64 clkdiv : 13;
u64 : 35;
} s;
};
union cavium_spi_sts {
u64 u;
struct {
u64 busy : 1;
u64 mpi_intr : 1;
u64 : 6;
u64 rxnum : 5;
u64 : 51;
} s;
};
union cavium_spi_tx {
u64 u;
struct {
u64 totnum : 5;
u64 : 3;
u64 txnum : 5;
u64 : 3;
u64 leavecs : 1;
u64 : 3;
u64 csid : 2;
u64 : 42;
} s;
};
struct cavium_spi {
union cavium_spi_cfg cfg;
union cavium_spi_sts sts;
union cavium_spi_tx tx;
u64 rsvd1;
u64 sts_w1s;
u64 rsvd2;
u64 int_ena_w1c;
u64 int_ena_w1s;
u64 wide_dat;
u8 rsvd4[0x38];
u64 dat[8];
};
check_member(cavium_spi, cfg, 0);
check_member(cavium_spi, sts, 0x8);
check_member(cavium_spi, tx, 0x10);
check_member(cavium_spi, dat[7], 0xb8);
struct cavium_spi_slave {
struct cavium_spi *regs;
int cs;
};
#define SPI_TIMEOUT_US 5000
static struct cavium_spi_slave cavium_spi_slaves[] = {
{
.regs = (struct cavium_spi *)MPI_PF_BAR0,
.cs = 0,
},
};
static struct cavium_spi_slave *to_cavium_spi(const struct spi_slave *slave)
{
assert(slave->bus < ARRAY_SIZE(cavium_spi_slaves));
return &cavium_spi_slaves[slave->bus];
}
/**
* Enable the SPI controller. Pins are driven.
*
* @param bus The SPI bus to operate on
*/
void spi_enable(const size_t bus)
{
union cavium_spi_cfg cfg;
assert(bus < ARRAY_SIZE(cavium_spi_slaves));
if (bus >= ARRAY_SIZE(cavium_spi_slaves))
return;
struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
cfg.u = read64(&regs->cfg);
cfg.s.csena = 0xf;
cfg.s.enable = 1;
write64(&regs->cfg, cfg.u);
}
/**
* Disable the SPI controller. Pins are tristated.
*
* @param bus The SPI bus to operate on
*/
void spi_disable(const size_t bus)
{
union cavium_spi_cfg cfg;
assert(bus < ARRAY_SIZE(cavium_spi_slaves));
if (bus >= ARRAY_SIZE(cavium_spi_slaves))
return;
struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
cfg.u = read64(&regs->cfg);
cfg.s.csena = 0xf;
cfg.s.enable = 0;
write64(&regs->cfg, cfg.u);
}
/**
* Set SPI Chip select line and level if asserted.
*
* @param bus The SPI bus to operate on
* @param chip_select The chip select pin to use (0 - 3)
* @param assert_is_low CS pin state is low when asserted
*/
void spi_set_cs(const size_t bus,
const size_t chip_select,
const size_t assert_is_low)
{
union cavium_spi_cfg cfg;
assert(bus < ARRAY_SIZE(cavium_spi_slaves));
if (bus >= ARRAY_SIZE(cavium_spi_slaves))
return;
cavium_spi_slaves[bus].cs = chip_select & 0x3;
struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
cfg.u = read64(&regs->cfg);
cfg.s.csena = 0xf;
cfg.s.cshi = !assert_is_low;
write64(&regs->cfg, cfg.u);
//FIXME: CS2/3: Change pin mux here
}
/**
* Set SPI clock frequency.
*
* @param bus The SPI bus to operate on
* @param speed_hz The SPI frequency in Hz
* @param idle_low The SPI clock idles low
* @param idle_cycles Number of CLK cycles between two commands (0 - 3)
*/
void spi_set_clock(const size_t bus,
const size_t speed_hz,
const size_t idle_low,
const size_t idle_cycles)
{
union cavium_spi_cfg cfg;
assert(bus < ARRAY_SIZE(cavium_spi_slaves));
if (bus >= ARRAY_SIZE(cavium_spi_slaves))
return;
struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
const uint64_t sclk = thunderx_get_io_clock();
cfg.u = read64(&regs->cfg);
cfg.s.csena = 0xf;
cfg.s.clk_cont = 0;
cfg.s.idlelow = !!idle_low;
cfg.s.idleclks = idle_cycles & 0x3;
cfg.s.clkdiv = MIN(sclk / (2ULL * speed_hz), 0x1fff);
write64(&regs->cfg, cfg.u);
printk(BIOS_DEBUG, "SPI: set clock to %lld kHz\n",
(sclk / (2ULL * cfg.s.clkdiv)) >> 10);
}
/**
* Set SPI LSB/MSB first.
*
* @param bus The SPI bus to operate on
* @param lsb_first The SPI operates LSB first
*
*/
void spi_set_lsbmsb(const size_t bus, const size_t lsb_first)
{
union cavium_spi_cfg cfg;
assert(bus < ARRAY_SIZE(cavium_spi_slaves));
if (bus >= ARRAY_SIZE(cavium_spi_slaves))
return;
struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
cfg.u = read64(&regs->cfg);
cfg.s.csena = 0xf;
cfg.s.lsbfirst = !!lsb_first;
write64(&regs->cfg, cfg.u);
}
/**
* Init SPI with custom parameters and enable SPI controller.
*
* @param bus The SPI bus to operate on
* @param speed_hz The SPI frequency in Hz
* @param idle_low The SPI clock idles low
* @param idle_cycles Number of CLK cycles between two commands (0 - 3)
* @param lsb_first The SPI operates LSB first
* @param chip_select The chip select pin to use (0 - 3)
* @param assert_is_low CS pin state is low when asserted
*/
void spi_init_custom(const size_t bus,
const size_t speed_hz,
const size_t idle_low,
const size_t idle_cycles,
const size_t lsb_first,
const size_t chip_select,
const size_t assert_is_low)
{
spi_disable(bus);
spi_set_clock(bus, speed_hz, idle_low, idle_cycles);
spi_set_lsbmsb(bus, lsb_first);
spi_set_cs(bus, chip_select, assert_is_low);
spi_enable(bus);
}
/**
* Init all SPI controllers with default values and enable all SPI controller.
*
*/
void spi_init(void)
{
for (size_t i = 0; i < ARRAY_SIZE(cavium_spi_slaves); i++) {
spi_disable(i);
spi_set_clock(i, 12500000, 0, 0);
spi_set_lsbmsb(i, 0);
spi_set_cs(i, 0, 1);
spi_enable(i);
}
}
static int cavium_spi_wait(struct cavium_spi *regs)
{
struct stopwatch sw;
union cavium_spi_sts sts;
stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_US);
do {
sts.u = read64(&regs->sts);
if (!sts.s.busy)
return 0;
} while (!stopwatch_expired(&sw));
printk(BIOS_DEBUG, "SPI: Timed out after %uus\n", SPI_TIMEOUT_US);
return -1;
}
static int do_xfer(const struct spi_slave *slave, struct spi_op *vector,
int leavecs)
{
struct cavium_spi *regs = to_cavium_spi(slave)->regs;
uint8_t *out_buf = (uint8_t *)vector->dout;
size_t bytesout = vector->bytesout;
uint8_t *in_buf = (uint8_t *)vector->din;
size_t bytesin = vector->bytesin;
union cavium_spi_sts sts;
union cavium_spi_tx tx;
/**
* The CN81xx SPI controller is half-duplex and has 8 data registers.
* If >8 bytes remain in the transfer then we must set LEAVECS = 1 so
* that the /CS remains asserted. Once <=8 bytes remain we must set
* LEAVECS = 0 so that /CS is de-asserted, thus completing the transfer.
*/
while (bytesout) {
size_t out_now = MIN(bytesout, 8);
unsigned int i;
for (i = 0; i < out_now; i++)
write64(&regs->dat[i], out_buf[i] & 0xff);
tx.u = 0;
tx.s.csid = to_cavium_spi(slave)->cs;
if (leavecs || ((bytesout > 8) || bytesin))
tx.s.leavecs = 1;
/* number of bytes to transmit goes in both TXNUM and TOTNUM */
tx.s.totnum = out_now;
tx.s.txnum = out_now;
write64(&regs->tx, tx.u);
/* check status */
if (cavium_spi_wait(regs) < 0)
return -1;
bytesout -= out_now;
out_buf += out_now;
}
while (bytesin) {
size_t in_now = MIN(bytesin, 8);
unsigned int i;
tx.u = 0;
tx.s.csid = to_cavium_spi(slave)->cs;
if (leavecs || (bytesin > 8))
tx.s.leavecs = 1;
tx.s.totnum = in_now;
write64(&regs->tx, tx.u);
/* check status */
if (cavium_spi_wait(regs) < 0)
return -1;
sts.u = read64(&regs->sts);
if (sts.s.rxnum != in_now) {
printk(BIOS_ERR,
"SPI: Incorrect number of bytes received: %u.\n",
sts.s.rxnum);
return -1;
}
for (i = 0; i < in_now; i++) {
*in_buf = (uint8_t)((read64(&regs->dat[i]) & 0xff));
in_buf++;
}
bytesin -= in_now;
}
return 0;
}
static int spi_ctrlr_xfer_vector(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
int i;
for (i = 0; i < count; i++) {
if (do_xfer(slave, &vectors[i], count - 1 == i ? 0 : 1)) {
printk(BIOS_ERR,
"SPI: Failed to transfer %zu vectors.\n", count);
return -1;
}
}
return 0;
}
static const struct spi_ctrlr spi_ctrlr = {
.xfer_vector = spi_ctrlr_xfer_vector,
.max_xfer_size = SPI_CTRLR_DEFAULT_MAX_XFER_SIZE,
};
const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
{
.ctrlr = &spi_ctrlr,
.bus_start = 0,
.bus_end = ARRAY_SIZE(cavium_spi_slaves) - 1,
},
};
const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);

View File

@@ -0,0 +1,226 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <arch/io.h>
#include <console/console.h>
#include <inttypes.h>
#include <soc/clock.h>
#include <soc/timer.h>
#include <stdint.h>
#include <timer.h>
#include <soc/addressmap.h>
#include <assert.h>
/* Global System Timers Unit (GTI) registers */
struct cn81xx_timer {
u32 cc_cntcr;
u32 cc_cntsr;
u64 cc_cntcv;
u8 rsvd[0x10];
u32 cc_cntfid0;
u32 cc_cntfid1;
u8 rsvd2[0x98];
u32 cc_cntrate;
u32 cc_cntracc;
u64 cc_cntadd;
u64 cc_cntmb;
u64 cc_cntmbts;
u64 cc_cntmb_int;
u64 cc_cntmb_int_set;
u64 cc_cntmb_int_ena_clr;
u64 cc_cntmb_int_ena_set;
u64 cc_imp_ctl;
u8 skip[0x1fef8];
u32 ctl_cntfrq;
u32 ctl_cntnsar;
u32 ctl_cnttidr;
u8 rsvd3[0x34];
u32 ctl_cntacr0;
u8 skip2[0x1ffb8];
u64 cwd_wdog[48]; /* Offset 0x40000 */
u8 skip3[0xfe80];
u64 cwd_poke[48]; /* Offset 0x50000 */
};
check_member(cn81xx_timer, cc_imp_ctl, 0x100);
check_member(cn81xx_timer, ctl_cntacr0, 0x20040);
check_member(cn81xx_timer, cwd_wdog[0], 0x40000);
check_member(cn81xx_timer, cwd_poke[0], 0x50000);
#define GTI_CC_CNTCR_EN (1 << 0)
#define GTI_CC_CNTCR_HDBG (1 << 1)
#define GTI_CC_CNTCR_FCREQ (1 << 8)
#define GTI_CC_CNTSR_DBGH (1 << 1)
#define GTI_CC_CNTSR_FCACK (1 << 8)
#define GTI_CWD_WDOG_MODE_SHIFT 0
#define GTI_CWD_WDOG_MODE_MASK 0x3
#define GTI_CWD_WDOG_STATE_SHIFT 2
#define GTI_CWD_WDOG_STATE_MASK 0x3
#define GTI_CWD_WDOG_LEN_SHIFT 4
#define GTI_CWD_WDOG_LEN_MASK 0xffff
#define GTI_CWD_WDOG_CNT_SHIFT 20
#define GTI_CWD_WDOG_CNT_MASK 0xffffff
#define GTI_CWD_WDOC_DSTOP (1 << 44)
#define GTI_CWD_WDOC_GSTOP (1 << 45)
static uint64_t timer_raw_value(void)
{
struct cn81xx_timer *timer = (void *)GTI_PF_BAR0;
return read64(&timer->cc_cntcv);
}
/**
* Get GTI counter value.
* @param mt Structure to fill
*/
void timer_monotonic_get(struct mono_time *mt)
{
mono_time_set_usecs(mt, timer_raw_value());
}
/**
* Init Global System Timers Unit (GTI).
* Configure timer to run at 1MHz tick-rate.
*/
void init_timer(void)
{
struct cn81xx_timer *gti = (struct cn81xx_timer *)GTI_PF_BAR0;
/* Check if the counter was already setup */
if (gti->cc_cntcr & GTI_CC_CNTCR_EN)
return;
u64 sclk = thunderx_get_io_clock();
/* Use coprocessor clock source */
write32(&gti->cc_imp_ctl, 0);
/* Setup counter to operate at 1MHz */
const size_t tickrate = 1000000;
write32(&gti->cc_cntfid0, tickrate);
write32(&gti->ctl_cntfrq, tickrate);
write32(&gti->cc_cntrate, ((1ULL << 32) * tickrate) / sclk);
/* Enable the counter */
setbits_le32(&gti->cc_cntcr, GTI_CC_CNTCR_EN);
//u32 u = (CNTPS_CTL_EL1_IMASK | CNTPS_CTL_EL1_EN);
//BDK_MSR(CNTPS_CTL_EL1, u);
}
/**
* Setup the watchdog to expire in timeout_ms milliseconds. When the watchdog
* expires, the chip three things happen:
* 1) Expire 1: interrupt that is ignored by the BDK
* 2) Expire 2: DEL3T interrupt, which is disabled and ignored
* 3) Expire 3: Soft reset of the chip
*
* Since we want a soft reset, we actually program the watchdog to expire at
* the timeout / 3.
*
* @param index Index of watchdog to configure
* @param timeout_ms Timeout in milliseconds.
*/
void watchdog_set(const size_t index, unsigned int timeout_ms)
{
uint64_t sclk = thunderx_get_io_clock();
uint64_t timeout_sclk = sclk * timeout_ms / 1000;
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return;
/*
* Per comment above, we want the watchdog to expire at 3x the rate
* specified
*/
timeout_sclk /= 3;
/* Watchdog counts in 1024 cycle steps */
uint64_t timeout_wdog = timeout_sclk >> 10;
/* We can only specify the upper 16 bits of a 24 bit value. Round up */
timeout_wdog = (timeout_wdog + 0xff) >> 8;
/* If the timeout overflows the hardware limit, set max */
if (timeout_wdog >= 0x10000)
timeout_wdog = 0xffff;
printk(BIOS_DEBUG, "Watchdog: Set to expire %llu SCLK cycles\n",
timeout_wdog << 18);
clrsetbits_le64(&timer->cwd_wdog[index],
(GTI_CWD_WDOG_LEN_MASK << GTI_CWD_WDOG_LEN_SHIFT) |
(GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT),
(timeout_wdog << GTI_CWD_WDOG_LEN_SHIFT) |
(3 << GTI_CWD_WDOG_MODE_SHIFT));
}
/**
* Signal the watchdog that we are still running.
*
* @param index Index of watchdog to configure.
*/
void watchdog_poke(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_poke));
if (index >= ARRAY_SIZE(timer->cwd_poke))
return;
write64(&timer->cwd_poke[0], 0);
}
/**
* Disable the hardware watchdog
*
* @param index Index of watchdog to configure.
*/
void watchdog_disable(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return;
write64(&timer->cwd_wdog[index], 0);
printk(BIOS_DEBUG, "Watchdog: Disabled\n");
}
/**
* Return true if the watchdog is configured and running
*
* @param index Index of watchdog to configure.
*
* @return Non-zero if watchdog is running.
*/
int watchdog_is_running(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return 0;
uint64_t val = read64(&timer->cwd_wdog[index]);
return !!(val & (GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT));
}

View File

@@ -0,0 +1,696 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <console/console.h>
#include <soc/twsi.h>
#include <soc/clock.h>
#include <device/i2c.h>
#include <device/i2c_simple.h>
#include <assert.h>
#include <delay.h>
#include <arch/io.h>
#include <soc/addressmap.h>
#define TWSI_THP 24
#define TWSI_SW_TWSI 0x1000
#define TWSI_TWSI_SW 0x1008
#define TWSI_INT 0x1010
#define TWSI_SW_TWSI_EXT 0x1018
union twsx_sw_twsi {
u64 u;
struct {
u64 data:32;
u64 eop_ia:3;
u64 ia:5;
u64 addr:10;
u64 scr:2;
u64 size:3;
u64 sovr:1;
u64 r:1;
u64 op:4;
u64 eia:1;
u64 slonly:1;
u64 v:1;
} s;
};
union twsx_sw_twsi_ext {
u64 u;
struct {
u64 data:32;
u64 ia:8;
u64 :24;
} s;
};
union twsx_int {
u64 u;
struct {
u64 st_int:1; /** TWSX_SW_TWSI register update int */
u64 ts_int:1; /** TWSX_TWSI_SW register update int */
u64 core_int:1; /** TWSI core interrupt, ignored for HLC */
u64 :5; /** Reserved */
u64 sda_ovr:1; /** SDA testing override */
u64 scl_ovr:1; /** SCL testing override */
u64 sda:1; /** SDA signal */
u64 scl:1; /** SCL signal */
u64 :52; /** Reserved */
} s;
};
enum {
TWSI_OP_WRITE = 0,
TWSI_OP_READ = 1,
};
enum {
TWSI_EOP_SLAVE_ADDR = 0,
TWSI_EOP_CLK_CTL = 3,
TWSI_SW_EOP_IA = 6,
};
enum {
TWSI_SLAVEADD = 0,
TWSI_DATA = 1,
TWSI_CTL = 2,
TWSI_CLKCTL = 3,
TWSI_STAT = 3,
TWSI_SLAVEADD_EXT = 4,
TWSI_RST = 7,
};
enum {
TWSI_CTL_AAK = (1 << 2),
TWSI_CTL_IFLG = (1 << 3),
TWSI_CTL_STP = (1 << 4),
TWSI_CTL_STA = (1 << 5),
TWSI_CTL_ENAB = (1 << 6),
TWSI_CTL_CE = (1 << 7),
};
enum {
/** Bus error */
TWSI_STAT_BUS_ERROR = 0x00,
/** Start condition transmitted */
TWSI_STAT_START = 0x08,
/** Repeat start condition transmitted */
TWSI_STAT_RSTART = 0x10,
/** Address + write bit transmitted, ACK received */
TWSI_STAT_TXADDR_ACK = 0x18,
/** Address + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR_NAK = 0x20,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_ACK = 0x28,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_NAK = 0x30,
/** Arbitration lost in address or data byte */
TWSI_STAT_TX_ARB_LOST = 0x38,
/** Address + read bit transmitted, ACK received */
TWSI_STAT_RXADDR_ACK = 0x40,
/** Address + read bit transmitted, /ACK received */
TWSI_STAT_RXADDR_NAK = 0x48,
/** Data byte received in master mode, ACK transmitted */
TWSI_STAT_RXDATA_ACK_SENT = 0x50,
/** Data byte received, NACK transmitted */
TWSI_STAT_RXDATA_NAK_SENT = 0x58,
/** Slave address received, sent ACK */
TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
/**
* Arbitration lost in address as master, slave address + write bit
* received, ACK transmitted
*/
TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
/** General call address received, ACK transmitted */
TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
/**
* Arbitration lost in address as master, general call address
* received, ACK transmitted
*/
TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
/** Data byte received after slave address received, ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
/** Data byte received after slave address received, /ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
/**
* Data byte received after general call address received, ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_ACK = 0x90,
/**
* Data byte received after general call address received, /ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_NAK = 0x98,
/** STOP or repeated START condition received in slave mode */
TWSI_STAT_STOP_MULTI_START = 0xA0,
/** Slave address + read bit received, ACK transmitted */
TWSI_STAT_SLAVE_RXADDR2_ACK = 0xA8,
/**
* Arbitration lost in address as master, slave address + read bit
* received, ACK transmitted
*/
TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xB0,
/** Data byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_ACK = 0xB8,
/** Data byte transmitted in slave mode, /ACK received */
TWSI_STAT_SLAVE_TXDATA_NAK = 0xC0,
/** Last byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xC8,
/** Second address byte + write bit transmitted, ACK received */
TWSI_STAT_TXADDR2DATA_ACK = 0xD0,
/** Second address byte + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR2DATA_NAK = 0xD8,
/** No relevant status information */
TWSI_STAT_IDLE = 0xF8
};
/**
* Returns true if we lost arbitration
*
* @param code status code
* @param final_read true if this is the final read operation
*
* @return true if arbitration has been lost, false if it hasn't been lost.
*/
static int twsi_i2c_lost_arb(u8 code, int final_read)
{
switch (code) {
/* Arbitration lost */
case TWSI_STAT_TX_ARB_LOST:
case TWSI_STAT_TX_ACK_ARB_LOST:
case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
case TWSI_STAT_RXDATA_ACK_ARB_LOST:
return -1;
/* Being addressed as slave, should back off and listen */
case TWSI_STAT_SLAVE_RXADDR_ACK:
case TWSI_STAT_RX_GEN_ADDR_ACK:
case TWSI_STAT_GEN_RXADDR_ACK:
case TWSI_STAT_GEN_RXADDR_NAK:
return -1;
/* Core busy as slave */
case TWSI_STAT_SLAVE_RXDATA_ACK:
case TWSI_STAT_SLAVE_RXDATA_NAK:
case TWSI_STAT_STOP_MULTI_START:
case TWSI_STAT_SLAVE_RXADDR2_ACK:
case TWSI_STAT_SLAVE_TXDATA_ACK:
case TWSI_STAT_SLAVE_TXDATA_NAK:
case TWSI_STAT_SLAVE_TXDATA_END_ACK:
return -1;
/* Ack allowed on pre-terminal bytes only */
case TWSI_STAT_RXDATA_ACK_SENT:
if (!final_read)
return 0;
return -1;
/* NAK allowed on terminal byte only */
case TWSI_STAT_RXDATA_NAK_SENT:
if (!final_read)
return 0;
return -1;
case TWSI_STAT_TXDATA_NAK:
case TWSI_STAT_TXADDR_NAK:
case TWSI_STAT_RXADDR_NAK:
case TWSI_STAT_TXADDR2DATA_NAK:
return -1;
}
return 0;
}
#define RST_BOOT_PNR_MUL(Val) ((Val >> 33) & 0x1F)
/**
* Writes to the MIO_TWS(0..5)_SW_TWSI register
*
* @param baseaddr Base address of i2c registers
* @param sw_twsi value to write
*
* @return 0 for success, otherwise error
*/
static u64 twsi_write_sw(void *baseaddr, union twsx_sw_twsi sw_twsi)
{
unsigned long timeout = 500000;
sw_twsi.s.r = 0;
sw_twsi.s.v = 1;
printk(BIOS_SPEW, "%s(%p, 0x%llx)\n", __func__, baseaddr, sw_twsi.u);
write64(baseaddr + TWSI_SW_TWSI, sw_twsi.u);
do {
sw_twsi.u = read64(baseaddr + TWSI_SW_TWSI);
timeout--;
} while (sw_twsi.s.v != 0 && timeout > 0);
if (sw_twsi.s.v)
printk(BIOS_ERR, "%s: timed out\n", __func__);
return sw_twsi.u;
}
/**
* Reads the MIO_TWS(0..5)_SW_TWSI register
*
* @param baseaddr Base address of i2c registers
* @param sw_twsi value for eia and op, etc. to read
*
* @return value of the register
*/
static u64 twsi_read_sw(void *baseaddr, union twsx_sw_twsi sw_twsi)
{
unsigned long timeout = 500000;
sw_twsi.s.r = 1;
sw_twsi.s.v = 1;
printk(BIOS_SPEW, "%s(%p, 0x%llx)\n", __func__, baseaddr, sw_twsi.u);
write64(baseaddr + TWSI_SW_TWSI, sw_twsi.u);
do {
sw_twsi.u = read64(baseaddr + TWSI_SW_TWSI);
timeout--;
} while (sw_twsi.s.v != 0 && timeout > 0);
if (sw_twsi.s.v)
printk(BIOS_ERR, "%s: Error writing 0x%llx\n", __func__,
sw_twsi.u);
printk(BIOS_SPEW, "%s: Returning 0x%llx\n", __func__, sw_twsi.u);
return sw_twsi.u;
}
/**
* Write control register
*
* @param baseaddr Base address for i2c registers
* @param data data to write
*/
static void twsi_write_ctl(void *baseaddr, const u8 data)
{
union twsx_sw_twsi twsi_sw;
printk(BIOS_SPEW, "%s(%p, 0x%x)\n", __func__, baseaddr, data);
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_CTL;
twsi_sw.s.data = data;
twsi_write_sw(baseaddr, twsi_sw);
}
/**
* Reads the TWSI Control Register
*
* @param[in] baseaddr Base address for i2c
*
* @return 8-bit TWSI control register
*/
static u32 twsi_read_ctl(void *baseaddr)
{
union twsx_sw_twsi sw_twsi;
sw_twsi.u = 0;
sw_twsi.s.op = TWSI_SW_EOP_IA;
sw_twsi.s.eop_ia = TWSI_CTL;
sw_twsi.u = twsi_read_sw(baseaddr, sw_twsi);
printk(BIOS_SPEW, "%s(%p): 0x%x\n", __func__, baseaddr, sw_twsi.s.data);
return sw_twsi.s.data;
}
/**
* Read i2c status register
*
* @param baseaddr Base address of i2c registers
*
* @return value of status register
*/
static u8 twsi_read_status(void *baseaddr)
{
union twsx_sw_twsi twsi_sw;
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_STAT;
return twsi_read_sw(baseaddr, twsi_sw);
}
/**
* Waits for an i2c operation to complete
*
* @param baseaddr Base address of registers
*
* @return 0 for success, 1 if timeout
*/
static int twsi_wait(void *baseaddr)
{
unsigned long timeout = 500000;
u8 twsi_ctl;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
do {
twsi_ctl = twsi_read_ctl(baseaddr);
twsi_ctl &= TWSI_CTL_IFLG;
timeout--;
} while (!twsi_ctl && timeout > 0);
printk(BIOS_SPEW, " return: %u\n", !twsi_ctl);
return !twsi_ctl;
}
/**
* Sends an i2c stop condition
*
* @param baseaddr register base address
*
* @return 0 for success, -1 if error
*/
static int twsi_stop(void *baseaddr)
{
u8 stat;
twsi_write_ctl(baseaddr, TWSI_CTL_STP | TWSI_CTL_ENAB);
stat = twsi_read_status(baseaddr);
if (stat != TWSI_STAT_IDLE) {
printk(BIOS_ERR, "%s: Bad status on bus@%p\n", __func__,
baseaddr);
return -1;
}
return 0;
}
/**
* Manually clear the I2C bus and send a stop
*/
static void twsi_unblock(void *baseaddr)
{
int i;
union twsx_int int_reg;
int_reg.u = 0;
for (i = 0; i < 9; i++) {
int_reg.s.scl_ovr = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.s.scl_ovr = 1;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
}
int_reg.s.sda_ovr = 1;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.s.scl_ovr = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.u = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
}
/**
* Unsticks the i2c bus
*
* @param baseaddr base address of registers
*/
static int twsi_start_unstick(void *baseaddr)
{
twsi_stop(baseaddr);
twsi_unblock(baseaddr);
return 0;
}
/**
* Sends an i2c start condition
*
* @param baseaddr base address of registers
*
* @return 0 for success, otherwise error
*/
static int twsi_start(void *baseaddr)
{
int result;
u8 stat;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
twsi_write_ctl(baseaddr, TWSI_CTL_STA | TWSI_CTL_ENAB);
result = twsi_wait(baseaddr);
if (result) {
stat = twsi_read_status(baseaddr);
printk(BIOS_SPEW, "%s: result: 0x%x, status: 0x%x\n", __func__,
result, stat);
switch (stat) {
case TWSI_STAT_START:
case TWSI_STAT_RSTART:
return 0;
case TWSI_STAT_RXADDR_ACK:
default:
return twsi_start_unstick(baseaddr);
}
}
printk(BIOS_SPEW, "%s: success\n", __func__);
return 0;
}
/**
* Writes data to the i2c bus
*
* @param baseraddr register base address
* @param slave_addr address of slave to write to
* @param buffer Pointer to buffer to write
* @param length Number of bytes in buffer to write
*
* @return 0 for success, otherwise error
*/
static int twsi_write_data(void *baseaddr, const u8 slave_addr,
const u8 *buffer, const unsigned int length)
{
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, 0x%x)\n", __func__, baseaddr,
slave_addr, buffer, length);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: Could not start BUS transaction\n",
__func__);
return -1;
}
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
}
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = (u32) (slave_addr << 1) | TWSI_OP_WRITE;
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
printk(BIOS_SPEW, "%s: Waiting\n", __func__);
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing slave address 0x%x\n",
__func__, slave_addr);
return result;
}
result = twsi_read_status(baseaddr);
if ((result = twsi_read_status(baseaddr)) != TWSI_STAT_TXADDR_ACK) {
twsi_stop(baseaddr);
return twsi_i2c_lost_arb(result, 0);
}
while (curr < length) {
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = buffer[curr++];
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing data to 0x%x\n",
__func__, slave_addr);
return result;
}
}
printk(BIOS_SPEW, "%s: Stopping\n", __func__);
return twsi_stop(baseaddr);
}
/**
* Performs a read transaction on the i2c bus
*
* @param baseaddr Base address of twsi registers
* @param slave_addr i2c bus address to read from
* @param buffer buffer to read into
* @param length number of bytes to read
*
* @return 0 for success, otherwise error
*/
static int twsi_read_data(void *baseaddr, const u8 slave_addr,
u8 *buffer, const unsigned int length)
{
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, %u)\n", __func__, baseaddr,
slave_addr, buffer, length);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: start failed\n", __func__);
return result;
}
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
}
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = (u32) (slave_addr << 1) | TWSI_OP_READ;
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: waiting for sending addr failed\n", __func__);
return result;
}
result = twsi_read_status(baseaddr);
if (result != TWSI_STAT_RXADDR_ACK) {
twsi_stop(baseaddr);
return twsi_i2c_lost_arb(result, 0);
}
while (curr < length) {
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB |
((curr < length - 1) ? TWSI_CTL_AAK : 0));
result = twsi_wait(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: waiting for data failed\n",
__func__);
return result;
}
twsi_sw.u = twsi_read_sw(baseaddr, twsi_sw);
buffer[curr++] = twsi_sw.s.data;
}
twsi_stop(baseaddr);
return 0;
}
static int twsi_set_speed(void *baseaddr, const unsigned int speed)
{
u64 io_clock_hz;
int n_div;
int m_div;
union twsx_sw_twsi sw_twsi;
io_clock_hz = thunderx_get_io_clock();
/* Set the TWSI clock to a conservative TWSI_BUS_FREQ. Compute the
* clocks M divider based on the SCLK.
* TWSI freq = (core freq) / (20 x (M+1) x (thp+1) x 2^N)
* M = ((core freq) / (20 x (TWSI freq) x (thp+1) x 2^N)) - 1
*/
for (n_div = 0; n_div < 8; n_div++) {
m_div = io_clock_hz / (20 * speed * (TWSI_THP + 1));
m_div /= 1 << n_div;
m_div -= 1;
if (m_div < 16)
break;
}
if (m_div >= 16)
return -1;
sw_twsi.u = 0;
sw_twsi.s.v = 1;
sw_twsi.s.op = 0x6; /* See EOP field */
sw_twsi.s.r = 0; /* Select CLKCTL when R = 0 */
sw_twsi.s.eop_ia = 3; /* R=0 selects CLKCTL, R=1 selects STAT */
sw_twsi.s.data = ((m_div & 0xf) << 3) | ((n_div & 0x7) << 0);
twsi_write_sw(baseaddr, sw_twsi);
return 0;
}
int twsi_init(unsigned int bus, enum i2c_speed hz)
{
void *baseaddr = (void *)MIO_TWSx_PF_BAR0(bus);
if (!baseaddr)
return -1;
if (twsi_set_speed(baseaddr, hz) < 0)
return -1;
/* Enable TWSI, HLC disable, STOP, NAK */
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
return 0;
}
int platform_i2c_transfer(unsigned bus, struct i2c_msg *segments,
int seg_count)
{
int result;
void *baseaddr = (void *)MIO_TWSx_PF_BAR0(bus);
if (!baseaddr)
return -1;
printk(BIOS_SPEW, "%s: %d messages\n", __func__, seg_count);
for (; seg_count > 0; seg_count--, segments++) {
if (segments->flags & I2C_M_RD) {
result = twsi_read_data(baseaddr, segments->slave,
segments->buf, segments->len);
} else {
result = twsi_write_data(baseaddr, segments->slave,
segments->buf, segments->len);
}
if (result) {
printk(BIOS_ERR, "%s: error transmitting data\n",
__func__);
return -1;
}
}
return 0;
}

View File

@@ -0,0 +1,265 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <arch/io.h>
#include <console/uart.h>
#include <delay.h>
#include <endian.h>
#include <stdint.h>
#include <soc/clock.h>
#include <soc/uart.h>
#include <assert.h>
#include <soc/addressmap.h>
#include <drivers/uart/pl011.h>
union cn81xx_uart_ctl {
u64 u;
struct {
u64 uctl_rst : 1;
u64 uaa_rst : 1;
u64 : 2;
u64 csclk_en : 1;
u64 : 19;
u64 h_clkdiv_sel : 3;
u64 : 1;
u64 h_clkdiv_rst : 1;
u64 h_clk_byp_sel : 1;
u64 h_clk_en : 1;
u64 : 33;
} s;
};
struct cn81xx_uart {
struct pl011_uart pl011;
union cn81xx_uart_ctl uctl_ctl;
u8 rsvd4[0x8];
u64 uctl_spare0;
u8 rsvd5[0xe0];
u64 uctl_spare1;
};
#define UART_IBRD_BAUD_DIVINT_SHIFT 0
#define UART_IBRD_BAUD_DIVINT_MASK 0xffff
#define UART_FBRD_BAUD_DIVFRAC_SHIFT 0
#define UART_FBRD_BAUD_DIVFRAC_MASK 0x3f
check_member(cn81xx_uart, uctl_ctl, 0x1000);
check_member(cn81xx_uart, uctl_spare1, 0x10f8);
#define UART_SCLK_DIV 3
/**
* Returns the current UART HCLK divider
*
* @param reg The H_CLKDIV_SEL value
* @return The HCLK divider
*/
static size_t uart_sclk_divisor(const size_t reg)
{
static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32};
assert(reg < ARRAY_SIZE(div));
return div[reg];
}
/**
* Returns the current UART HCLK
*
* @param uart The UART to operate on
* @return The HCLK in Hz
*/
static size_t uart_hclk(struct cn81xx_uart *uart)
{
union cn81xx_uart_ctl ctl;
const uint64_t sclk = thunderx_get_io_clock();
ctl.u = read64(&uart->uctl_ctl);
return sclk / uart_sclk_divisor(ctl.s.h_clkdiv_sel);
}
unsigned int uart_platform_refclk(void)
{
struct cn81xx_uart *uart =
(struct cn81xx_uart *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
if (!uart)
return 0;
return uart_hclk(uart);
}
uintptr_t uart_platform_base(int idx)
{
return CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
}
/**
* Waits given count if HCLK cycles
*
* @param uart The UART to operate on
* @param hclks The number of HCLK cycles to wait
*/
static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks)
{
const size_t hclk = uart_hclk(uart);
const size_t delay = (hclks * 1000000ULL) / hclk;
udelay(MAX(delay, 1));
}
/**
* Returns the UART state.
*
* @param bus The UART to operate on
* @return Boolean: True if UART is enabled
*/
int uart_is_enabled(const size_t bus)
{
struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
union cn81xx_uart_ctl ctl;
assert(uart);
if (!uart)
return 0;
ctl.u = read64(&uart->uctl_ctl);
return !!ctl.s.csclk_en;
}
/**
* Setup UART with desired BAUD rate in 8N1, no parity mode.
*
* @param bus The UART to operate on
* @param baudrate baudrate to set up
*
* @return Boolean: True on error
*/
int uart_setup(const size_t bus, int baudrate)
{
union cn81xx_uart_ctl ctl;
struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
assert(uart);
if (!uart)
return 1;
/* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */
/* 1. Wait for IOI reset (srst_n) to deassert. */
/**
* 2. Assert all resets:
* a. UAA reset: UCTL_CTL[UAA_RST] = 1
* b. UCTL reset: UCTL_CTL[UCTL_RST] = 1
*/
ctl.u = read64(&uart->uctl_ctl);
ctl.s.uctl_rst = 1;
ctl.s.uaa_rst = 1;
write64(&uart->uctl_ctl, ctl.u);
/**
* 3. Configure the HCLK:
* a. Reset the clock dividers: UCTL_CTL[H_CLKDIV_RST] = 1.
* b. Select the HCLK frequency
* i. UCTL_CTL[H_CLKDIV] = desired value,
* ii. UCTL_CTL[H_CLKDIV_EN] = 1 to enable the HCLK.
* iii. Readback UCTL_CTL to ensure the values take effect.
* c. Deassert the HCLK clock divider reset: UCTL_CTL[H_CLKDIV_RST] = 0.
*/
ctl.u = read64(&uart->uctl_ctl);
ctl.s.h_clkdiv_sel = UART_SCLK_DIV;
write64(&uart->uctl_ctl, ctl.u);
ctl.u = read64(&uart->uctl_ctl);
ctl.s.h_clk_byp_sel = 0;
write64(&uart->uctl_ctl, ctl.u);
ctl.u = read64(&uart->uctl_ctl);
ctl.s.h_clk_en = 1;
write64(&uart->uctl_ctl, ctl.u);
ctl.u = read64(&uart->uctl_ctl);
ctl.s.h_clkdiv_rst = 0;
write64(&uart->uctl_ctl, ctl.u);
/**
* 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo
* to properly reset.
*/
uart_wait_hclk(uart, 20 + 1);
/**
* 5. Deassert UCTL and UAHC resets:
* a. UCTL_CTL[UCTL_RST] = 0
* b. Wait 10 HCLK cycles.
* c. UCTL_CTL[UAHC_RST] = 0
* d. You will have to wait 10 HCLK cycles before accessing any
* HCLK-only registers.
*/
ctl.u = read64(&uart->uctl_ctl);
ctl.s.uctl_rst = 0;
write64(&uart->uctl_ctl, ctl.u);
uart_wait_hclk(uart, 10 + 1);
ctl.u = read64(&uart->uctl_ctl);
ctl.s.uaa_rst = 0;
write64(&uart->uctl_ctl, ctl.u);
uart_wait_hclk(uart, 10 + 1);
/**
* 6. Enable conditional SCLK of UCTL by writing
* UCTL_CTL[CSCLK_EN] = 1.
*/
ctl.u = read64(&uart->uctl_ctl);
ctl.s.csclk_en = 1;
write64(&uart->uctl_ctl, ctl.u);
/**
* 7. Initialize the integer and fractional baud rate divider registers
* UARTIBRD and UARTFBRD as follows:
* a. Baud Rate Divisor = UARTCLK/(16xBaud Rate) = BRDI + BRDF
* b. The fractional register BRDF, m is calculated as
* integer(BRDF x 64 + 0.5)
* Example calculation:
* If the required baud rate is 230400 and hclk = 4MHz then:
* Baud Rate Divisor = (4x10^6)/(16x230400) = 1.085
* This means BRDI = 1 and BRDF = 0.085.
* Therefore, fractional part, BRDF = integer((0.085x64)+0.5) = 5
* Generated baud rate divider = 1+5/64 = 1.078
*/
u64 divisor = thunderx_get_io_clock() /
(baudrate * 16 * uart_sclk_divisor(UART_SCLK_DIV) / 64);
write32(&uart->pl011.ibrd, divisor >> 6);
write32(&uart->pl011.fbrd, divisor & UART_FBRD_BAUD_DIVFRAC_MASK);
/**
* 8. Program the line control register UAA(0..1)_LCR_H and the control
* register UAA(0..1)_CR
*/
/* 8-bits, FIFO enable */
write32(&uart->pl011.lcr_h, PL011_UARTLCR_H_WLEN_8 |
PL011_UARTLCR_H_FEN);
/* RX/TX enable, UART enable */
write32(&uart->pl011.cr, PL011_UARTCR_RXE | PL011_UARTCR_TXE |
PL011_UARTCR_UARTEN);
return 0;
}

View File

@@ -0,0 +1,12 @@
config SOC_CAVIUM_COMMON
bool
default n
select BOOTBLOCK_CUSTOM
select CAVIUM_BDK
select FLATTENED_DEVICE_TREE
# FIXME: No Cavium support in ATF
# select ARM64_USE_ARM_TRUSTED_FIRMWARE
if SOC_CAVIUM_COMMON
endif

View File

@@ -0,0 +1,58 @@
##
## This file is part of the coreboot project.
##
## Copyright 2017-present Facebook, Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; version 2 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
ifeq ($(CONFIG_SOC_CAVIUM_COMMON),y)
CFLAGS_arm64 += -Wstack-usage=8192
bootblock-$(CONFIG_BOOTBLOCK_CUSTOM) += bootblock.c
################################################################################
# romstage
romstage-y += cbmem.c
romstage-y += bdk-coreboot.c
################################################################################
# ramstage
ramstage-y += cbmem.c
ramstage-$(CONFIG_ARM64_USE_ARM_TRUSTED_FIRMWARE) += bl31_plat_params.c
CPPFLAGS_common += -Isrc/soc/cavium/common/include
ROM_HEADER_BIN := $(objgenerated)/rom_header.bin
ROM_HEADER_SOURCES += rom_clib_s_nbl1fw
ROM_HEADER_SOURCES += rom_csib_s_nbl1fw
ROM_HEADER_DEPS := $(foreach f, $(ROM_HEADER_SOURCES), src/soc/cavium/common/$(f).bin.hex)
# Include ROM header
$(ROM_HEADER_BIN): $(ROM_HEADER_DEPS)
for f in $+; \
do for c in $$(cat $$f | grep -v ^#); \
do printf $$(printf '\%o' 0x$$c); \
done; \
done > $@
$(objcbfs)/bootblock.bin: $(objcbfs)/bootblock.raw.bin $(ROM_HEADER_BIN)
@printf " GEN $(subst $(obj)/,,$(@))\n"
dd if=/dev/zero ibs=1 count=$$(($(shell stat --printf="%s" $(objcbfs)/bootblock.raw.bin) + 0x10000)) of=$@ status=none
# Insert CLIB at 0x0 and CSIB at 0x100
dd if=$(ROM_HEADER_BIN) of=$@ bs=1 seek=0 conv=notrunc status=none
# Insert bootblock at 0x10000
dd if=$(objcbfs)/bootblock.raw.bin of=$@ bs=1 seek=$$((0x10000)) conv=notrunc status=none
endif

View File

@@ -0,0 +1,130 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2017-present Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This file consists of data imported from bdk-config.c
*/
// coreboot
#include <string.h>
#include <assert.h>
#include <device/i2c.h>
#include <device/i2c_simple.h>
#include <endian.h>
#include <arch/io.h>
#include <delay.h>
#include <reset.h>
#include <soc/timer.h>
// BDK
#include <libbdk-arch/bdk-numa.h>
#include <libbdk-hal/bdk-config.h>
#include <libbdk-hal/bdk-twsi.h>
#include <libbdk-boot/bdk-watchdog.h>
/**
* Do a twsi read from a 7 bit device address using an (optional)
* internal address. Up to 4 bytes can be read at a time.
*
* @param twsi_id which TWSI bus to use
* @param dev_addr Device address (7 bit)
* @param internal_addr
* Internal address. Can be 0, 1 or 2 bytes in width
* @param num_bytes Number of data bytes to read (1-4)
* @param ia_width_bytes
* Internal address size in bytes (0, 1, or 2)
*
* @return Read data, or -1 on failure
*/
int64_t bdk_twsix_read_ia(bdk_node_t node, int twsi_id, uint8_t dev_addr,
uint16_t internal_addr, int num_bytes,
int ia_width_bytes)
{
struct i2c_msg seg[2];
u32 buf;
assert (num_bytes < 5);
assert (ia_width_bytes < 3);
seg[0].flags = 0;
seg[0].slave = dev_addr;
seg[0].buf = (u8 *)&internal_addr;
seg[0].len = ia_width_bytes;
seg[1].flags = I2C_M_RD;
seg[1].slave = dev_addr;
seg[1].buf = (u8 *)&buf;
seg[1].len = num_bytes;
if (i2c_transfer(twsi_id, seg, ARRAY_SIZE(seg)) < 0)
return -1;
return cpu_to_be32(buf);
}
/**
* Write 1-8 bytes to a TWSI device using an internal address.
*
* @param twsi_id which TWSI interface to use
* @param dev_addr TWSI device address (7 bit only)
* @param internal_addr
* TWSI internal address (0, 8, or 16 bits)
* @param num_bytes Number of bytes to write (1-8)
* @param ia_width_bytes
* internal address width, in bytes (0, 1, 2)
* @param data Data to write. Data is written MSB first on the twsi bus, and
* only the lower num_bytes bytes of the argument are valid. (If
* a 2 byte write is done, only the low 2 bytes of the argument is
* used.
*
* @return Zero on success, -1 on error
*/
int bdk_twsix_write_ia(bdk_node_t node, int twsi_id, uint8_t dev_addr,
uint16_t internal_addr, int num_bytes,
int ia_width_bytes, uint64_t data)
{
struct i2c_msg seg;
u8 buf[10];
assert (num_bytes <= 8);
assert (ia_width_bytes < 3);
memcpy(buf, &internal_addr, ia_width_bytes);
memcpy(&buf[ia_width_bytes], &data, num_bytes);
seg.flags = 0;
seg.slave = dev_addr;
seg.buf = buf;
seg.len = num_bytes + ia_width_bytes;
return platform_i2c_transfer(twsi_id, &seg, 1);
}
void bdk_watchdog_set(unsigned int timeout_ms)
{
watchdog_set(0, timeout_ms);
}
void bdk_watchdog_poke(void)
{
watchdog_poke(0);
}
void bdk_watchdog_disable(void)
{
watchdog_disable(0);
}
int bdk_watchdog_is_running(void)
{
return watchdog_is_running(0);
}

View File

@@ -0,0 +1,32 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2016 Rockchip Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <arm_tf.h>
#include <assert.h>
#include <soc/bl31_plat_params.h>
static struct bl31_plat_param *plat_params;
void register_bl31_param(struct bl31_plat_param *param)
{
param->next = plat_params;
plat_params = param;
}
void *soc_get_bl31_plat_params(bl31_params_t *bl31_params)
{
return plat_params;
}

View File

@@ -0,0 +1,68 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <arch/exception.h>
#include <console/console.h>
#include <delay.h>
#include <program_loading.h>
#include <symbols.h>
#include <timestamp.h>
#include <soc/bootblock.h>
DECLARE_OPTIONAL_REGION(timestamp);
__attribute__((weak)) void bootblock_mainboard_early_init(void) { /* no-op */ }
__attribute__((weak)) void bootblock_soc_early_init(void) { /* do nothing */ }
__attribute__((weak)) void bootblock_soc_init(void) { /* do nothing */ }
__attribute__((weak)) void bootblock_mainboard_init(void) { /* do nothing */ }
/* C code entry point for the boot block */
void bootblock_main(const uint64_t reg_x0,
const uint64_t reg_x1,
const uint64_t reg_pc)
{
uint64_t base_timestamp = 0;
init_timer();
if (IS_ENABLED(CONFIG_COLLECT_TIMESTAMPS))
base_timestamp = timestamp_get();
/* Initialize timestamps if we have TIMESTAMP region in memlayout.ld. */
if (IS_ENABLED(CONFIG_COLLECT_TIMESTAMPS) && _timestamp_size > 0)
timestamp_init(base_timestamp);
bootblock_soc_early_init();
bootblock_mainboard_early_init();
if (IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE)) {
console_init();
exception_init();
if (reg_x0)
printk(BIOS_ERR,
"BOOTBLOCK: RST Boot Failure Code %lld\n",
reg_x0);
printk(BIOS_DEBUG, "BOOTBLOCK: FDT 0x%llX\n", reg_x1);
}
bootblock_soc_init();
bootblock_mainboard_init();
run_romstage();
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2014 Rockchip Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <cbmem.h>
#include <soc/addressmap.h>
#include <soc/sdram.h>
#include <stdlib.h>
#include <symbols.h>
void *cbmem_top(void)
{
return (void *)min((uintptr_t)_dram + sdram_size_mb() * MiB,
MAX_DRAM_ADDRESS);
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2016 Rockchip Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __BL31_PLAT_PARAMS_H__
#define __BL31_PLAT_PARAMS_H__
// FIXME: use correct path one ATF is upstream
#include <arm-trusted-firmware/plat/rockchip/common/include/plat_params.h>
void register_bl31_param(struct bl31_plat_param *param);
#endif/* __BL31_PLAT_PARAMS_H__ */

View File

@@ -0,0 +1,29 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_BOOTBLOCK_H_
#define SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_BOOTBLOCK_H_
void bootblock_mainboard_early_init(void);
void bootblock_soc_early_init(void);
void bootblock_soc_init(void);
void bootblock_mainboard_init(void);
void bootblock_main(const uint64_t reg_x0,
const uint64_t reg_x1,
const uint64_t reg_pc);
#endif /* SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_BOOTBLOCK_H_ */

View File

@@ -0,0 +1,65 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
* Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_COMMON_INCLUDE_SOC_SYSREG_H
#define __SOC_CAVIUM_COMMON_INCLUDE_SOC_SYSREG_H
/* TODO: add CVMCTL_EL1 */
#define AP_CVMMEMCTL0_EL1_NODE_SHIFT 61
#define AP_CVMMEMCTL0_EL1_NODE_MASK 0x3
#define AP_CVMMEMCTL0_EL1_STEXFAILCNT_SHIFT 58
#define AP_CVMMEMCTL0_EL1_STEXFAILCNT_MASK 0x7
#define AP_CVMMEMCTL0_EL1_WFELDEX1DIS (1 << 57)
#define AP_CVMMEMCTL0_EL1_STLSTALLFORCE (1 << 56)
#define AP_CVMMEMCTL0_EL1_DMBSTALLFORCE (1 << 55)
#define AP_CVMMEMCTL0_EL1_TLBINOPDIS (1 << 54)
#define AP_CVMMEMCTL0_EL1_TLBIICFLUSH (1 << 53)
#define AP_CVMMEMCTL0_EL1_GSYNCTO_SHIFT 48
#define AP_CVMMEMCTL0_EL1_GSYNCTO_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_UTLBFILLBYPDIS (1 << 47)
#define AP_CVMMEMCTL0_EL1_TLBIALL (1 << 46)
#define AP_CVMMEMCTL0_EL1_WBFDSBFLUSHALL (1 << 45)
#define AP_CVMMEMCTL0_EL1_WBFDMBFLUSHNEXT (1 << 44)
#define AP_CVMMEMCTL0_EL1_STEXL2CFORCE (1 << 43)
#define AP_CVMMEMCTL0_EL1_WCUMISSFORCE (1 << 41)
#define AP_CVMMEMCTL0_EL1_REPLAYPREFDIS (1 << 40)
#define AP_CVMMEMCTL0_EL1_ZVAL2CDIS (1 << 39)
#define AP_CVMMEMCTL0_EL1_LDIL2CDIS (1 << 38)
#define AP_CVMMEMCTL0_EL1_DVCA47 (1 << 37)
#define AP_CVMMEMCTL0_EL1_STPREFDIS (1 << 36)
#define AP_CVMMEMCTL0_EL1_LDPREFDIS (1 << 35)
#define AP_CVMMEMCTL0_EL1_WFILDEXDIS (1 << 34)
#define AP_CVMMEMCTL0_EL1_WFITO_SHIFT 31
#define AP_CVMMEMCTL0_EL1_WFITO_MASK 0x7
#define AP_CVMMEMCTL0_EL1_RBFSHORTTO_SHIFT 26
#define AP_CVMMEMCTL0_EL1_RBFSHORTTO_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_RBFTO_SHIFT 21
#define AP_CVMMEMCTL0_EL1_RBFTO_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_WBFALLBARRIER (1 << 20)
#define AP_CVMMEMCTL0_EL1_WBFNOMERGE (1 << 19)
#define AP_CVMMEMCTL0_EL1_WBFTONSHENA (1 << 18)
#define AP_CVMMEMCTL0_EL1_WBFTOMRGCLRENA (1 << 17)
#define AP_CVMMEMCTL0_EL1_WBFTO_SHIFT 12
#define AP_CVMMEMCTL0_EL1_WBFTO_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_WBFTHRESH_SHIFT 7
#define AP_CVMMEMCTL0_EL1_WBFTHRESH_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_UTLBENTRIESM1_SHIFT 2
#define AP_CVMMEMCTL0_EL1_UTLBENTRIESM1_MASK 0x1f
#define AP_CVMMEMCTL0_EL1_CCLKFORCE (1 << 1)
#define AP_CVMMEMCTL0_EL1_MCLKFORCE (1 << 0)
#endif /* __SOC_CAVIUM_COMMON_INCLUDE_SOC_SYSREG_H */

View File

@@ -0,0 +1,16 @@
43 56 4d 5f 43 4c 49 42 00 00 00 00 00 00 00 00
00 00 02 00 00 00 00 00 00 00 03 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

View File

@@ -0,0 +1,16 @@
43 56 4d 5f 43 53 49 42 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00