platform: generic: Tenstorrent Atlantis support

Add the Tenstorrent Atlantis as a generic-platform. This initial support
enables the single_fw_region option, and verifies and prints HART PMA
CSR configuration.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260424062520.238403-1-npiggin@gmail.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Nicholas Piggin
2026-04-24 16:25:19 +10:00
committed by Anup Patel
parent 1932ee3f0a
commit 6767861c48
11 changed files with 331 additions and 0 deletions
+6
View File
@@ -89,6 +89,11 @@ config PLATFORM_STARFIVE_JH7110
bool "StarFive JH7110 support"
default n
config PLATFORM_TENSTORRENT_ATLANTIS
bool "Tenstorrent Atlantis support"
select CPU_TENSTORRENT_ASCALON
default n
config PLATFORM_THEAD
bool "THEAD C9xx support"
select THEAD_C9XX_ERRATA
@@ -114,6 +119,7 @@ config PLATFORM_MIPS_P8700_BOSTON
source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
source "$(OPENSBI_SRC_DIR)/platform/generic/eswin/Kconfig"
source "$(OPENSBI_SRC_DIR)/platform/generic/tenstorrent/Kconfig"
source "$(OPENSBI_SRC_DIR)/platform/generic/thead/Kconfig"
endif
+1
View File
@@ -10,6 +10,7 @@ CONFIG_PLATFORM_SIFIVE_FU540=y
CONFIG_PLATFORM_SIFIVE_FU740=y
CONFIG_PLATFORM_SOPHGO_SG2042=y
CONFIG_PLATFORM_STARFIVE_JH7110=y
CONFIG_PLATFORM_TENSTORRENT_ATLANTIS=y
CONFIG_PLATFORM_THEAD=y
CONFIG_PLATFORM_MIPS_P8700_EYEQ7H=y
CONFIG_PLATFORM_MIPS_P8700_BOSTON=y
@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
* SPDX-License-Identifier: BSD-2-Clause
*/
#ifndef __TENSTORRENT_ASCALON_H__
#define __TENSTORRENT_ASCALON_H__
void tt_ascalon_discover_pmas_from_boot_hart(void);
void tt_ascalon_verify_pmas_nonboot_hart(void);
#endif
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
* SPDX-License-Identifier: BSD-2-Clause
*/
#ifndef __TENSTORRENT_PMA_H__
#define __TENSTORRENT_PMA_H__
/* Max number of PMAs for devices (CPU, IOMMU) for Tenstorrent platforms. */
#define TT_MAX_PMAS 32
u64 tt_pma_get(unsigned int n);
void tt_pma_set(unsigned int n, u64 pma);
bool tt_pma_validate(unsigned int i, u64 pma);
void tt_pma_print(unsigned int i, u64 pma);
#endif
+5
View File
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
# SPDX-License-Identifier: BSD-2-Clause
config CPU_TENSTORRENT_ASCALON
bool
+53
View File
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <sbi/riscv_asm.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_csr_detect.h>
#include <tenstorrent/ascalon.h>
#include <tenstorrent/pma.h>
#define CSR_PMACFG0 0x7e0
void tt_ascalon_discover_pmas_from_boot_hart(void)
{
struct sbi_trap_info trap = {0};
/* Whisper virtual platform does not implement PMA */
csr_read_allowed(CSR_PMACFG0, &trap);
if (trap.cause)
return;
for (unsigned int i = 0; i < TT_MAX_PMAS; i++) {
u64 pma = csr_read_num(CSR_PMACFG0 + i);
if (!tt_pma_validate(i, pma)) {
sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx\n",
current_hartid(), i, pma);
}
tt_pma_set(i, pma);
if (pma)
tt_pma_print(i, pma);
}
}
void tt_ascalon_verify_pmas_nonboot_hart(void)
{
struct sbi_trap_info trap = {0};
/* Whisper virtual platform does not implement PMA */
csr_read_allowed(CSR_PMACFG0, &trap);
if (trap.cause)
return;
for (unsigned int i = 0; i < TT_MAX_PMAS; i++) {
u64 pma = csr_read_num(CSR_PMACFG0 + i);
if (pma != tt_pma_get(i)) {
sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx does not match boot HART\n",
current_hartid(), i, pma);
}
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <platform_override.h>
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_console.h>
#include <tenstorrent/ascalon.h>
#include <tenstorrent/pma.h>
static int tt_atlantis_final_init(bool cold_boot)
{
if (cold_boot) {
/* Boot firmware sets HART PMAs. Read and verify them. */
tt_ascalon_discover_pmas_from_boot_hart();
} else {
/* Verify nonboot HARTs have PMAs matching boot HART */
tt_ascalon_verify_pmas_nonboot_hart();
}
return generic_final_init(cold_boot);
}
static bool tt_atlantis_single_fw_region(void)
{
return true;
}
static int tt_atlantis_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
generic_platform_ops.final_init = tt_atlantis_final_init;
generic_platform_ops.single_fw_region = tt_atlantis_single_fw_region;
return 0;
}
static const struct fdt_match tt_atlantis_match[] = {
{ .compatible = "tenstorrent,atlantis" },
{ },
};
const struct fdt_driver tenstorrent_atlantis = {
.match_table = tt_atlantis_match,
.init = tt_atlantis_platform_init,
};
+12
View File
@@ -0,0 +1,12 @@
#
# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
# SPDX-License-Identifier: BSD-2-Clause
#
ifeq ($(PLATFORM_RISCV_XLEN), 64)
platform-objs-y += tenstorrent/pma.o
platform-objs-$(CONFIG_CPU_TENSTORRENT_ASCALON) += tenstorrent/ascalon.o
carray-platform_override_modules-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent_atlantis
platform-objs-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent/atlantis.o
endif
+138
View File
@@ -0,0 +1,138 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <sbi/sbi_error.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_math.h>
#include <libfdt.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <tenstorrent/pma.h>
/*
* All PMAs in the system should be the same (after boot). The init code
* must have set PMAs for all HARTs.
*/
/*
* Ascalon CPU and IOMMU PMA layout:
* Field
* [2:0] Permission [0] Read, [1] Write, [2] Execute
* [4:3] Memory type 00: Main memory, 01: IO memory relaxed,
* 10: IO memory channel 0, 11: IO memory channel 1
* [6:5] AMO type 00: AMONone, 01: AMOSwap,
* 10: AMOLogical, 11: AMOArithmetic
* [7] Cacheability (main memory type)
* 1: Cacheable, 0: Non-cacheable
* Combining Capability (IO memory type)
* 1: Combining allowed, 0: Combining disallowed
* [8] Routing (coherency)
* 1: Coherent network, 0: Non-coherent network
* [11:9] Reserved
* [51:12] Physical address [51:12] base
* [63:58] Size log 2 (number of address LSB to ignore when matching)
* 0 = invalid entry (no match)
*/
#define PMA_PERMISSION_R 0x1
#define PMA_PERMISSION_W 0x2
#define PMA_PERMISSION_X 0x4
#define PMA_PERMISSION_MASK 0x7
#define PMA_TYPE_MAIN_MEMORY 0x0
#define PMA_TYPE_IO_RELAXED 0x8
#define PMA_TYPE_IO_ORDERED_0 0x10
#define PMA_TYPE_IO_ORDERED_1 0x18
#define PMA_TYPE_MASK 0x18
#define PMA_AMO_NONE 0x0
#define PMA_AMO_SWAP 0x20
#define PMA_AMO_LOGICAL 0x40
#define PMA_AMO_ARITHMETIC 0x60
#define PMA_AMO_MASK 0x60
#define PMA_MEMORY_CACHEABLE 0x80
#define PMA_IO_COMBINING 0x80
#define PMA_ROUTING_COHERENT 0x100
#define PMA_FLAGS_MASK 0x00000000000001ffULL
#define PMA_ADDRESS_MASK 0x000ffffffffff000ULL
#define PMA_SIZE_MASK 0xfc00000000000000ULL
#define PMA_RESERVED_MASK 0x0300000000000e00ULL
#define PMA_SIZE_SHIFT 58
static u64 tt_pma_size(u64 pma)
{
if ((pma & PMA_SIZE_MASK) == 0)
return 0;
return 1ULL << ((pma & PMA_SIZE_MASK) >> PMA_SIZE_SHIFT);
}
static u64 tt_pma_address(u64 pma)
{
return (pma & PMA_ADDRESS_MASK) & ~((tt_pma_size(pma) - 1));
}
bool tt_pma_validate(unsigned int i, u64 pma)
{
if (!pma)
return true;
if (pma & PMA_RESERVED_MASK) {
sbi_printf("PMA%02u 0x%016lx contains reserved bits\n", i, pma);
return false;
}
if (tt_pma_size(pma) < 4096) {
sbi_printf("PMA%02u 0x%016lx size < 4KB\n", i, pma);
return false;
}
if (tt_pma_address(pma) != (pma & PMA_ADDRESS_MASK)) {
sbi_printf("PMA%02u 0x%016lx address is not aligned to size\n", i, pma);
return false;
}
return true;
}
void tt_pma_print(unsigned int i, u64 pma)
{
sbi_printf("PMA%02d : 0x%016lx-0x%016lx perm:%s%s%s type:%s %s %s amo:%s\n", i,
tt_pma_address(pma), tt_pma_address(pma) + tt_pma_size(pma) - 1,
pma & PMA_PERMISSION_R ? "R" : " ",
pma & PMA_PERMISSION_W ? "W" : " ",
pma & PMA_PERMISSION_X ? "X" : " ",
(pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ? "main-memory" :
((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_RELAXED ? "io-relaxed" :
((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_ORDERED_0 ? "io-ordered-0" : "io-ordered-1")),
(pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ?
(pma & PMA_MEMORY_CACHEABLE ? "cacheable" : "non-cacheable") :
(pma & PMA_IO_COMBINING ? "combining" : "non-combining"),
pma & PMA_ROUTING_COHERENT ? "coherent" : "non-coherent",
(pma & PMA_AMO_MASK) == PMA_AMO_NONE ? "none" :
((pma & PMA_AMO_MASK) == PMA_AMO_SWAP ? "swap" :
((pma & PMA_AMO_MASK) == PMA_AMO_LOGICAL ? "logical" : "arithmetic")));
}
static u64 pmas[TT_MAX_PMAS];
void tt_pma_set(unsigned int n, u64 pma)
{
if (n >= TT_MAX_PMAS)
sbi_panic("PMA exceeded TT_MAX_PMAS");
pmas[n] = pma;
}
u64 tt_pma_get(unsigned int n)
{
if (n >= TT_MAX_PMAS)
sbi_panic("PMA exceeded TT_MAX_PMAS");
return pmas[n];
}