summaryrefslogtreecommitdiff
path: root/src/soc/cavium/cn81xx
diff options
context:
space:
mode:
authorDavid Hendricks <dhendricks@fb.com>2017-12-01 20:49:48 -0800
committerPatrick Rudolph <siro@das-labor.org>2018-07-10 07:01:57 +0000
commit8cbd569f74d8929387730e45b0d6e993b1365c02 (patch)
treeca6414a4d81e37280887b0da0f1a6120a50f0a3a /src/soc/cavium/cn81xx
parent03d31427338ba59d3a354ac1beb3b0c153471768 (diff)
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>
Diffstat (limited to 'src/soc/cavium/cn81xx')
-rw-r--r--src/soc/cavium/cn81xx/Kconfig27
-rw-r--r--src/soc/cavium/cn81xx/Makefile.inc70
-rw-r--r--src/soc/cavium/cn81xx/bootblock.c50
-rw-r--r--src/soc/cavium/cn81xx/bootblock_custom.S257
-rw-r--r--src/soc/cavium/cn81xx/chip.h22
-rw-r--r--src/soc/cavium/cn81xx/clock.c79
-rw-r--r--src/soc/cavium/cn81xx/cpu.c26
-rw-r--r--src/soc/cavium/cn81xx/gpio.c193
-rw-r--r--src/soc/cavium/cn81xx/include/soc/addressmap.h123
-rw-r--r--src/soc/cavium/cn81xx/include/soc/clock.h26
-rw-r--r--src/soc/cavium/cn81xx/include/soc/cpu.h22
-rw-r--r--src/soc/cavium/cn81xx/include/soc/gpio.h34
-rw-r--r--src/soc/cavium/cn81xx/include/soc/memlayout.ld41
-rw-r--r--src/soc/cavium/cn81xx/include/soc/sdram.h25
-rw-r--r--src/soc/cavium/cn81xx/include/soc/soc.h43
-rw-r--r--src/soc/cavium/cn81xx/include/soc/spi.h40
-rw-r--r--src/soc/cavium/cn81xx/include/soc/timer.h30
-rw-r--r--src/soc/cavium/cn81xx/include/soc/twsi.h23
-rw-r--r--src/soc/cavium/cn81xx/include/soc/uart.h25
-rw-r--r--src/soc/cavium/cn81xx/sdram.c94
-rw-r--r--src/soc/cavium/cn81xx/soc.c65
-rw-r--r--src/soc/cavium/cn81xx/spi.c401
-rw-r--r--src/soc/cavium/cn81xx/timer.c226
-rw-r--r--src/soc/cavium/cn81xx/twsi.c696
-rw-r--r--src/soc/cavium/cn81xx/uart.c265
25 files changed, 2903 insertions, 0 deletions
diff --git a/src/soc/cavium/cn81xx/Kconfig b/src/soc/cavium/cn81xx/Kconfig
new file mode 100644
index 0000000000..ca8fbd0f46
--- /dev/null
+++ b/src/soc/cavium/cn81xx/Kconfig
@@ -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
diff --git a/src/soc/cavium/cn81xx/Makefile.inc b/src/soc/cavium/cn81xx/Makefile.inc
new file mode 100644
index 0000000000..d265c19bb8
--- /dev/null
+++ b/src/soc/cavium/cn81xx/Makefile.inc
@@ -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
diff --git a/src/soc/cavium/cn81xx/bootblock.c b/src/soc/cavium/cn81xx/bootblock.c
new file mode 100644
index 0000000000..8517467de1
--- /dev/null
+++ b/src/soc/cavium/cn81xx/bootblock.c
@@ -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? */
+}
diff --git a/src/soc/cavium/cn81xx/bootblock_custom.S b/src/soc/cavium/cn81xx/bootblock_custom.S
new file mode 100644
index 0000000000..69985b7834
--- /dev/null
+++ b/src/soc/cavium/cn81xx/bootblock_custom.S
@@ -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)
diff --git a/src/soc/cavium/cn81xx/chip.h b/src/soc/cavium/cn81xx/chip.h
new file mode 100644
index 0000000000..9716a5da6a
--- /dev/null
+++ b/src/soc/cavium/cn81xx/chip.h
@@ -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 */
diff --git a/src/soc/cavium/cn81xx/clock.c b/src/soc/cavium/cn81xx/clock.c
new file mode 100644
index 0000000000..bd6514cd41
--- /dev/null
+++ b/src/soc/cavium/cn81xx/clock.c
@@ -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;
+}
diff --git a/src/soc/cavium/cn81xx/cpu.c b/src/soc/cavium/cn81xx/cpu.c
new file mode 100644
index 0000000000..c054aa8987
--- /dev/null
+++ b/src/soc/cavium/cn81xx/cpu.c
@@ -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);
+}
diff --git a/src/soc/cavium/cn81xx/gpio.c b/src/soc/cavium/cn81xx/gpio.c
new file mode 100644
index 0000000000..340ac1bc73
--- /dev/null
+++ b/src/soc/cavium/cn81xx/gpio.c
@@ -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)
+{
+}
diff --git a/src/soc/cavium/cn81xx/include/soc/addressmap.h b/src/soc/cavium/cn81xx/include/soc/addressmap.h
new file mode 100644
index 0000000000..e23549450b
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/addressmap.h
@@ -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__ */
diff --git a/src/soc/cavium/cn81xx/include/soc/clock.h b/src/soc/cavium/cn81xx/include/soc/clock.h
new file mode 100644
index 0000000000..d436c121cb
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/clock.h
@@ -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_ */
diff --git a/src/soc/cavium/cn81xx/include/soc/cpu.h b/src/soc/cavium/cn81xx/include/soc/cpu.h
new file mode 100644
index 0000000000..df3c06955a
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/cpu.h
@@ -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__ */
diff --git a/src/soc/cavium/cn81xx/include/soc/gpio.h b/src/soc/cavium/cn81xx/include/soc/gpio.h
new file mode 100644
index 0000000000..6986482f79
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/gpio.h
@@ -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
diff --git a/src/soc/cavium/cn81xx/include/soc/memlayout.ld b/src/soc/cavium/cn81xx/include/soc/memlayout.ld
new file mode 100644
index 0000000000..f0ac2c9da5
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/memlayout.ld
@@ -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)
+}
diff --git a/src/soc/cavium/cn81xx/include/soc/sdram.h b/src/soc/cavium/cn81xx/include/soc/sdram.h
new file mode 100644
index 0000000000..5a3e5196b5
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/sdram.h
@@ -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__ */
diff --git a/src/soc/cavium/cn81xx/include/soc/soc.h b/src/soc/cavium/cn81xx/include/soc/soc.h
new file mode 100644
index 0000000000..a751e64846
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/soc.h
@@ -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 */
diff --git a/src/soc/cavium/cn81xx/include/soc/spi.h b/src/soc/cavium/cn81xx/include/soc/spi.h
new file mode 100644
index 0000000000..bb69daac91
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/spi.h
@@ -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 */
diff --git a/src/soc/cavium/cn81xx/include/soc/timer.h b/src/soc/cavium/cn81xx/include/soc/timer.h
new file mode 100644
index 0000000000..a12f68e422
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/timer.h
@@ -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__ */
diff --git a/src/soc/cavium/cn81xx/include/soc/twsi.h b/src/soc/cavium/cn81xx/include/soc/twsi.h
new file mode 100644
index 0000000000..6c5211e63b
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/twsi.h
@@ -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
diff --git a/src/soc/cavium/cn81xx/include/soc/uart.h b/src/soc/cavium/cn81xx/include/soc/uart.h
new file mode 100644
index 0000000000..e4022068bc
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/uart.h
@@ -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 */
diff --git a/src/soc/cavium/cn81xx/sdram.c b/src/soc/cavium/cn81xx/sdram.c
new file mode 100644
index 0000000000..77717ea78d
--- /dev/null
+++ b/src/soc/cavium/cn81xx/sdram.c
@@ -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");
+}
diff --git a/src/soc/cavium/cn81xx/soc.c b/src/soc/cavium/cn81xx/soc.c
new file mode 100644
index 0000000000..03f9122404
--- /dev/null
+++ b/src/soc/cavium/cn81xx/soc.c
@@ -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,
+};
diff --git a/src/soc/cavium/cn81xx/spi.c b/src/soc/cavium/cn81xx/spi.c
new file mode 100644
index 0000000000..5a5865e36e
--- /dev/null
+++ b/src/soc/cavium/cn81xx/spi.c
@@ -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);
diff --git a/src/soc/cavium/cn81xx/timer.c b/src/soc/cavium/cn81xx/timer.c
new file mode 100644
index 0000000000..0321a49ad9
--- /dev/null
+++ b/src/soc/cavium/cn81xx/timer.c
@@ -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));
+}
diff --git a/src/soc/cavium/cn81xx/twsi.c b/src/soc/cavium/cn81xx/twsi.c
new file mode 100644
index 0000000000..e86cac7c8d
--- /dev/null
+++ b/src/soc/cavium/cn81xx/twsi.c
@@ -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;
+}
diff --git a/src/soc/cavium/cn81xx/uart.c b/src/soc/cavium/cn81xx/uart.c
new file mode 100644
index 0000000000..9fb83871ad
--- /dev/null
+++ b/src/soc/cavium/cn81xx/uart.c
@@ -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;
+}