Compare commits
13 Commits
v1.1
...
sg200x-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| f879119605 | |||
| aedef73d12 | |||
| 92ee61dae4 | |||
|
|
6766e9cc96 | ||
|
|
3b045ae374 | ||
|
|
fffb4754ee | ||
|
|
ab9b8f8d10 | ||
|
|
3fd99912a6 | ||
|
|
216793f112 | ||
|
|
ba54b0038e | ||
|
|
ba85a8ec52 | ||
|
|
885df4577a | ||
|
|
f67338cc74 |
@@ -1,16 +0,0 @@
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignTrailingComments: true
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
BreakBeforeBraces: Custom
|
||||
BreakStringLiterals: false
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
IndentWidth: 8
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SpacesInContainerLiterals: false
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
175
Makefile
175
Makefile
@@ -76,54 +76,26 @@ OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sb
|
||||
OPENSBI_VERSION_GIT=$(shell if [ -d $(src_dir)/.git ]; then git describe 2> /dev/null; fi)
|
||||
|
||||
# Setup compilation commands
|
||||
ifneq ($(LLVM),)
|
||||
CC = clang
|
||||
AR = llvm-ar
|
||||
LD = ld.lld
|
||||
OBJCOPY = llvm-objcopy
|
||||
else
|
||||
ifdef CROSS_COMPILE
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CPP = $(CROSS_COMPILE)cpp
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
LD = $(CROSS_COMPILE)ld
|
||||
OBJCOPY = $(CROSS_COMPILE)objcopy
|
||||
else
|
||||
CC ?= gcc
|
||||
CPP ?= cpp
|
||||
AR ?= ar
|
||||
LD ?= ld
|
||||
OBJCOPY ?= objcopy
|
||||
endif
|
||||
endif
|
||||
CPP = $(CC) -E
|
||||
AS = $(CC)
|
||||
DTC = dtc
|
||||
|
||||
ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),)
|
||||
CC_IS_CLANG = y
|
||||
else
|
||||
CC_IS_CLANG = n
|
||||
endif
|
||||
|
||||
ifneq ($(shell $(LD) --version 2>&1 | head -n 1 | grep LLD),)
|
||||
LD_IS_LLD = y
|
||||
else
|
||||
LD_IS_LLD = n
|
||||
endif
|
||||
|
||||
ifeq ($(CC_IS_CLANG),y)
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
CLANG_TARGET = --target=$(notdir $(CROSS_COMPILE:%-=%))
|
||||
endif
|
||||
endif
|
||||
|
||||
# Guess the compiler's XLEN
|
||||
OPENSBI_CC_XLEN := $(shell TMP=`$(CC) $(CLANG_TARGET) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP})
|
||||
|
||||
# Guess the compiler's ABI and ISA
|
||||
ifneq ($(CC_IS_CLANG),y)
|
||||
# Guess the compillers xlen
|
||||
OPENSBI_CC_XLEN := $(shell TMP=`$(CC) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP})
|
||||
OPENSBI_CC_ABI := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-abi=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP})
|
||||
OPENSBI_CC_ISA := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-arch=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP})
|
||||
endif
|
||||
|
||||
# Setup platform XLEN
|
||||
ifndef PLATFORM_RISCV_XLEN
|
||||
@@ -134,47 +106,6 @@ ifndef PLATFORM_RISCV_XLEN
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CC_IS_CLANG),y)
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
CLANG_TARGET = --target=riscv$(PLATFORM_RISCV_XLEN)-unknown-elf
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(LD_IS_LLD),y)
|
||||
RELAX_FLAG = -mno-relax
|
||||
USE_LD_FLAG = -fuse-ld=lld
|
||||
else
|
||||
USE_LD_FLAG = -fuse-ld=bfd
|
||||
endif
|
||||
|
||||
# Check whether the linker supports creating PIEs
|
||||
OPENSBI_LD_PIE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) $(USE_LD_FLAG) -fPIE -nostdlib -Wl,-pie -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo y || echo n)
|
||||
|
||||
# Check whether the compiler supports -m(no-)save-restore
|
||||
CC_SUPPORT_SAVE_RESTORE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -mno-save-restore -x c /dev/null -o /dev/null 2>&1 | grep "\-save\-restore" >/dev/null && echo n || echo y)
|
||||
|
||||
# Check whether the assembler and the compiler support the Zicsr and Zifencei extensions
|
||||
CC_SUPPORT_ZICSR_ZIFENCEI := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -march=rv$(OPENSBI_CC_XLEN)imafd_zicsr_zifencei -x c /dev/null -o /dev/null 2>&1 | grep "zicsr\|zifencei" > /dev/null && echo n || echo y)
|
||||
|
||||
# Build Info:
|
||||
# OPENSBI_BUILD_TIME_STAMP -- the compilation time stamp
|
||||
# OPENSBI_BUILD_COMPILER_VERSION -- the compiler version info
|
||||
BUILD_INFO ?= n
|
||||
ifeq ($(BUILD_INFO),y)
|
||||
OPENSBI_BUILD_DATE_FMT = +%Y-%m-%d %H:%M:%S %z
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
OPENSBI_BUILD_TIME_STAMP ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" \
|
||||
"$(OPENSBI_BUILD_DATE_FMT)" 2>/dev/null || \
|
||||
date -u -r "$(SOURCE_DATE_EPOCH)" \
|
||||
"$(OPENSBI_BUILD_DATE_FMT)" 2>/dev/null || \
|
||||
date -u "$(OPENSBI_BUILD_DATE_FMT)")
|
||||
else
|
||||
OPENSBI_BUILD_TIME_STAMP ?= $(shell date "$(OPENSBI_BUILD_DATE_FMT)")
|
||||
endif
|
||||
OPENSBI_BUILD_COMPILER_VERSION=$(shell $(CC) -v 2>&1 | grep ' version ' | \
|
||||
sed 's/[[:space:]]*$$//')
|
||||
endif
|
||||
|
||||
# Setup list of objects.mk files
|
||||
ifdef PLATFORM
|
||||
platform-object-mks=$(shell if [ -d $(platform_src_dir)/ ]; then find $(platform_src_dir) -iname "objects.mk" | sort -r; fi)
|
||||
@@ -212,6 +143,20 @@ deps-y+=$(libsbi-objs-path-y:.o=.dep)
|
||||
deps-y+=$(libsbiutils-objs-path-y:.o=.dep)
|
||||
deps-y+=$(firmware-objs-path-y:.o=.dep)
|
||||
|
||||
ifeq (,$(wildcard ${PM_SRAM_BIN_PATH}))
|
||||
ifeq (${CHIP_ARCH},CV180X)
|
||||
PM_SRAM_BIN_PATH=${OPENSBI_PATH}/pm_default_cv180x.bin
|
||||
endif
|
||||
ifeq (${CHIP_ARCH},CV181X)
|
||||
PM_SRAM_BIN_PATH=${OPENSBI_PATH}/pm_default_cv181x.bin
|
||||
endif
|
||||
else
|
||||
ifeq ($(filter clean %clean clean%,$(MAKECMDGOALS)),)
|
||||
$(shell touch -c platform\generic\cvitek_riscv.c > /dev/null)
|
||||
endif
|
||||
endif
|
||||
$(info PM_SRAM_BIN_PATH is '${PM_SRAM_BIN_PATH}')
|
||||
|
||||
# Setup platform ABI, ISA and Code Model
|
||||
ifndef PLATFORM_RISCV_ABI
|
||||
ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1)
|
||||
@@ -226,11 +171,7 @@ ifndef PLATFORM_RISCV_ABI
|
||||
endif
|
||||
ifndef PLATFORM_RISCV_ISA
|
||||
ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1)
|
||||
ifeq ($(CC_SUPPORT_ZICSR_ZIFENCEI), y)
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc_zicsr_zifencei
|
||||
else
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc
|
||||
endif
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc
|
||||
else
|
||||
PLATFORM_RISCV_ISA = $(OPENSBI_CC_ISA)
|
||||
endif
|
||||
@@ -267,72 +208,61 @@ else
|
||||
endif
|
||||
|
||||
# Setup compilation commands flags
|
||||
ifeq ($(CC_IS_CLANG),y)
|
||||
GENFLAGS += $(CLANG_TARGET)
|
||||
GENFLAGS += -Wno-unused-command-line-argument
|
||||
endif
|
||||
GENFLAGS += -I$(platform_src_dir)/include
|
||||
GENFLAGS = -I$(platform_src_dir)/include
|
||||
GENFLAGS += -I$(include_dir)
|
||||
ifneq ($(OPENSBI_VERSION_GIT),)
|
||||
GENFLAGS += -DOPENSBI_VERSION_GIT="\"$(OPENSBI_VERSION_GIT)\""
|
||||
endif
|
||||
ifeq ($(BUILD_INFO),y)
|
||||
GENFLAGS += -DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
|
||||
GENFLAGS += -DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
|
||||
endif
|
||||
GENFLAGS += $(libsbiutils-genflags-y)
|
||||
GENFLAGS += $(platform-genflags-y)
|
||||
GENFLAGS += $(firmware-genflags-y)
|
||||
|
||||
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing -O2
|
||||
CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
|
||||
# enable -m(no-)save-restore option by CC_SUPPORT_SAVE_RESTORE
|
||||
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
|
||||
CFLAGS += -mno-save-restore
|
||||
define add_define
|
||||
GENFLAGS += -D$(1)$(if $(value $(1)),=$(value $(1)),)
|
||||
endef
|
||||
|
||||
ifeq (${CONFIG_OPENSBI_FASTBOOT},y)
|
||||
$(eval $(call add_define,CONFIG_OPENSBI_FASTBOOT))
|
||||
endif
|
||||
|
||||
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-strict-aliasing -O2
|
||||
CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
CFLAGS += -mno-save-restore -mstrict-align
|
||||
CFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
|
||||
CFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
|
||||
CFLAGS += $(RELAX_FLAG)
|
||||
CFLAGS += $(GENFLAGS)
|
||||
CFLAGS += $(platform-cflags-y)
|
||||
CFLAGS += -fno-pie -no-pie
|
||||
CFLAGS += $(firmware-cflags-y)
|
||||
CFLAGS += -DPM_SRAM_BIN_PATH=$(PM_SRAM_BIN_PATH)
|
||||
ifeq ($(CHIP_ARCH),CV180X)
|
||||
CFLAGS += -DCONFIG_CV180X
|
||||
endif
|
||||
ifeq (${CHIP_ARCH},CV181X)
|
||||
CFLAGS += -DCONFIG_CV181X
|
||||
endif
|
||||
|
||||
CPPFLAGS += $(GENFLAGS)
|
||||
CPPFLAGS += $(platform-cppflags-y)
|
||||
CPPFLAGS += $(firmware-cppflags-y)
|
||||
|
||||
ASFLAGS = -g -Wall -nostdlib
|
||||
ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
|
||||
# enable -m(no-)save-restore option by CC_SUPPORT_SAVE_RESTORE
|
||||
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
|
||||
ASFLAGS += -mno-save-restore
|
||||
endif
|
||||
ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
ASFLAGS += -mno-save-restore -mstrict-align
|
||||
ASFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
|
||||
ASFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
|
||||
ASFLAGS += $(RELAX_FLAG)
|
||||
ifneq ($(CC_IS_CLANG),y)
|
||||
ifneq ($(RELAX_FLAG),)
|
||||
ASFLAGS += -Wa,$(RELAX_FLAG)
|
||||
endif
|
||||
endif
|
||||
ASFLAGS += $(GENFLAGS)
|
||||
ASFLAGS += $(platform-asflags-y)
|
||||
ASFLAGS += $(firmware-asflags-y)
|
||||
|
||||
ARFLAGS = rcs
|
||||
|
||||
ELFFLAGS += $(USE_LD_FLAG)
|
||||
ELFFLAGS += -Wl,--build-id=none -Wl,-N
|
||||
ELFFLAGS += -Wl,--build-id=none -N -static-libgcc -lgcc
|
||||
ELFFLAGS += $(platform-ldflags-y)
|
||||
ELFFLAGS += $(firmware-ldflags-y)
|
||||
|
||||
MERGEFLAGS += -r
|
||||
ifeq ($(LD_IS_LLD),y)
|
||||
MERGEFLAGS += -b elf
|
||||
else
|
||||
MERGEFLAGS += -b elf$(PLATFORM_RISCV_XLEN)-littleriscv
|
||||
endif
|
||||
MERGEFLAGS += -m elf$(PLATFORM_RISCV_XLEN)lriscv
|
||||
|
||||
DTSCPPFLAGS = $(CPPFLAGS) -nostdinc -nostdlib -fno-builtin -D__DTS__ -x assembler-with-cpp
|
||||
@@ -404,10 +334,6 @@ compile_d2c = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
$(if $($(2)-varprefix-$(3)),$(eval D2C_NAME_PREFIX := $($(2)-varprefix-$(3))),$(eval D2C_NAME_PREFIX := $(5))) \
|
||||
$(if $($(2)-padding-$(3)),$(eval D2C_PADDING_BYTES := $($(2)-padding-$(3))),$(eval D2C_PADDING_BYTES := 0)) \
|
||||
$(src_dir)/scripts/d2c.sh -i $(6) -a $(D2C_ALIGN_BYTES) -p $(D2C_NAME_PREFIX) -t $(D2C_PADDING_BYTES) > $(1)
|
||||
compile_carray = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " CARRAY $(subst $(build_dir)/,,$(1))"; \
|
||||
$(eval CARRAY_VAR_LIST := $(carray-$(subst .c,,$(shell basename $(1)))-y)) \
|
||||
$(src_dir)/scripts/carray.sh -i $(2) -l "$(CARRAY_VAR_LIST)" > $(1)
|
||||
compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " GEN-DEP $(subst $(build_dir)/,,$(1))"; \
|
||||
echo "$(1:.dep=$(2)): $(3)" >> $(1)
|
||||
@@ -441,27 +367,12 @@ $(build_dir)/%.dep: $(src_dir)/%.c
|
||||
$(build_dir)/%.o: $(src_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(build_dir)/%.o: $(build_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
ifeq ($(BUILD_INFO),y)
|
||||
$(build_dir)/lib/sbi/sbi_init.o: $(libsbi_dir)/sbi_init.c FORCE
|
||||
$(call compile_cc,$@,$<)
|
||||
endif
|
||||
|
||||
$(build_dir)/%.dep: $(src_dir)/%.S
|
||||
$(call compile_as_dep,$@,$<)
|
||||
|
||||
$(build_dir)/%.o: $(src_dir)/%.S
|
||||
$(call compile_as,$@,$<)
|
||||
|
||||
$(build_dir)/%.dep: $(src_dir)/%.carray
|
||||
$(call compile_gen_dep,$@,.c,$<)
|
||||
$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
|
||||
|
||||
$(build_dir)/%.c: $(src_dir)/%.carray
|
||||
$(call compile_carray,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
|
||||
$(call compile_objcopy,$@,$<)
|
||||
|
||||
@@ -477,6 +388,9 @@ $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
|
||||
$(platform_build_dir)/%.o: $(platform_src_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.o: $(platform_build_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.dep: $(platform_src_dir)/%.S
|
||||
$(call compile_as_dep,$@,$<)
|
||||
|
||||
@@ -606,6 +520,3 @@ ifeq ($(install_root_dir),$(install_root_dir_default)/usr)
|
||||
$(if $(V), @echo " RM $(install_root_dir_default)")
|
||||
$(CMD_PREFIX)rm -rf $(install_root_dir_default)
|
||||
endif
|
||||
|
||||
.PHONY: FORCE
|
||||
FORCE:
|
||||
|
||||
88
README.md
88
README.md
@@ -96,21 +96,8 @@ Required Toolchain
|
||||
------------------
|
||||
|
||||
OpenSBI can be compiled natively or cross-compiled on a x86 host. For
|
||||
cross-compilation, you can build your own toolchain, download a prebuilt one
|
||||
from the [Bootlin toolchain repository] or install a distribution-provided
|
||||
toolchain; if you opt to use LLVM/Clang, most distribution toolchains will
|
||||
support cross-compiling for RISC-V using the same toolchain as your native
|
||||
LLVM/Clang toolchain due to LLVM's ability to support multiple backends in the
|
||||
same binary, so is often an easy way to obtain a working cross-compilation
|
||||
toolchain.
|
||||
|
||||
Basically, we prefer toolchains with Position Independent Executable (PIE)
|
||||
support like *riscv64-linux-gnu-gcc*, *riscv64-unknown-freebsd-gcc*, or
|
||||
*Clang/LLVM* as they generate PIE firmware images that can run at arbitrary
|
||||
address with appropriate alignment. If a bare-metal GNU toolchain (e.g.
|
||||
*riscv64-unknown-elf-gcc*) is used, static linked firmware images are
|
||||
generated instead. *Clang/LLVM* can still generate PIE images if a bare-metal
|
||||
triple is used (e.g. *-target riscv64-unknown-elf*).
|
||||
cross-compilation, you can build your own toolchain or just download
|
||||
a prebuilt one from the [Bootlin toolchain repository].
|
||||
|
||||
Please note that only a 64-bit version of the toolchain is available in
|
||||
the Bootlin toolchain repository for now.
|
||||
@@ -124,7 +111,7 @@ architecture than RISC-V.
|
||||
|
||||
For cross-compiling, the environment variable *CROSS_COMPILE* must be defined
|
||||
to specify the name prefix of the RISC-V compiler toolchain executables, e.g.
|
||||
*riscv64-linux-gnu-* if the gcc executable used is *riscv64-linux-gnu-gcc*.
|
||||
*riscv64-unknown-elf-* if the gcc executable used is *riscv64-unknown-elf-gcc*.
|
||||
|
||||
To build *libsbi.a* simply execute:
|
||||
```
|
||||
@@ -200,83 +187,21 @@ Building 32-bit / 64-bit OpenSBI Images
|
||||
---------------------------------------
|
||||
By default, building OpenSBI generates 32-bit or 64-bit images based on the
|
||||
supplied RISC-V cross-compile toolchain. For example if *CROSS_COMPILE* is set
|
||||
to *riscv64-linux-gnu-*, 64-bit OpenSBI images will be generated. If building
|
||||
to *riscv64-unknown-elf-*, 64-bit OpenSBI images will be generated. If building
|
||||
32-bit OpenSBI images, *CROSS_COMPILE* should be set to a toolchain that is
|
||||
pre-configured to generate 32-bit RISC-V codes, like *riscv32-linux-gnu-*.
|
||||
pre-configured to generate 32-bit RISC-V codes, like *riscv32-unknown-elf-*.
|
||||
|
||||
However it's possible to explicitly specify the image bits we want to build with
|
||||
a given RISC-V toolchain. This can be done by setting the environment variable
|
||||
*PLATFORM_RISCV_XLEN* to the desired width, for example:
|
||||
|
||||
```
|
||||
export CROSS_COMPILE=riscv64-linux-gnu-
|
||||
export CROSS_COMPILE=riscv64-unknown-elf-
|
||||
export PLATFORM_RISCV_XLEN=32
|
||||
```
|
||||
|
||||
will generate 32-bit OpenSBI images. And vice vesa.
|
||||
|
||||
Building with Clang/LLVM
|
||||
------------------------
|
||||
|
||||
OpenSBI can also be built with Clang/LLVM. To build with just Clang but keep
|
||||
the default binutils (which will still use the *CROSS_COMPILE* prefix if
|
||||
defined), override the *CC* make variable with:
|
||||
```
|
||||
make CC=clang
|
||||
```
|
||||
|
||||
To build with a full LLVM-based toolchain, not just Clang, enable the *LLVM*
|
||||
option with:
|
||||
```
|
||||
make LLVM=1
|
||||
```
|
||||
|
||||
When using Clang, *CROSS_COMPILE* often does not need to be defined unless
|
||||
using GNU binutils with prefixed binary names. *PLATFORM_RISCV_XLEN* will be
|
||||
used to infer a default triple to pass to Clang, so if *PLATFORM_RISCV_XLEN*
|
||||
itself defaults to an undesired value then prefer setting that rather than the
|
||||
full triple via *CROSS_COMPILE*. If *CROSS_COMPILE* is nonetheless defined,
|
||||
rather than being used as a prefix for the executable name, it will instead be
|
||||
passed via the `--target` option with the trailing `-` removed, so must be a
|
||||
valid triple.
|
||||
|
||||
These can also be mixed; for example using a GCC cross-compiler but LLVM
|
||||
binutils would be:
|
||||
```
|
||||
make CC=riscv64-linux-gnu-gcc LLVM=1
|
||||
```
|
||||
|
||||
These variables must be passed for all the make invocations described in this
|
||||
document.
|
||||
|
||||
NOTE: Using Clang with a `riscv*-linux-gnu` GNU binutils linker has been seen
|
||||
to produce broken binaries with missing relocations; it is therefore currently
|
||||
recommended that this combination be avoided or *FW_PIC=n* be used to disable
|
||||
building OpenSBI as a position-independent binary.
|
||||
|
||||
Building with timestamp and compiler info
|
||||
-----------------------------------------
|
||||
|
||||
When doing development, we may want to know the build time and compiler info
|
||||
for debug purpose. OpenSBI can also be built with timestamp and compiler info.
|
||||
To build with those info and print it out at boot time, we can just simply add
|
||||
`BUILD_INFO=y`, like:
|
||||
```
|
||||
make BUILD_INFO=y
|
||||
```
|
||||
|
||||
But if you have used `BUILD_INFO=y`, and want to switch back to `BUILD_INFO=n`,
|
||||
you must do
|
||||
```
|
||||
make clean
|
||||
```
|
||||
before the next build.
|
||||
|
||||
NOTE: Using `BUILD_INFO=y` without specifying SOURCE_DATE_EPOCH will violate
|
||||
[reproducible builds]. This definition is ONLY for development and debug
|
||||
purpose, and should NOT be used in a product which follows "reproducible
|
||||
builds".
|
||||
|
||||
Contributing to OpenSBI
|
||||
-----------------------
|
||||
|
||||
@@ -359,4 +284,3 @@ make I=<install_directory> install_docs
|
||||
[Doxygen manual]: http://www.doxygen.nl/manual/index.html
|
||||
[Kendryte standalone SDK]: https://github.com/kendryte/kendryte-standalone-sdk
|
||||
[third party notices]: ThirdPartyNotices.md
|
||||
[reproducible builds]: https://reproducible-builds.org
|
||||
|
||||
@@ -52,6 +52,7 @@ has following details:
|
||||
* **next_mode** - Privilege mode of the next booting stage for this
|
||||
domain. This can be either S-mode or U-mode.
|
||||
* **system_reset_allowed** - Is domain allowed to reset the system?
|
||||
* **system_suspend_allowed** - Is domain allowed to suspend the system?
|
||||
|
||||
The memory regions represented by **regions** in **struct sbi_domain** have
|
||||
following additional constraints to align with RISC-V PMP requirements:
|
||||
@@ -91,6 +92,7 @@ following manner:
|
||||
* **next_mode** - Next booting stage mode in coldboot HART scratch space
|
||||
is the next mode for the ROOT domain
|
||||
* **system_reset_allowed** - The ROOT domain is allowed to reset the system
|
||||
* **system_suspend_allowed** - The ROOT domain is allowed to suspend the system
|
||||
|
||||
Domain Effects
|
||||
--------------
|
||||
@@ -187,6 +189,8 @@ The DT properties of a domain instance DT node are as follows:
|
||||
stage mode of coldboot HART** is used as default value.
|
||||
* **system-reset-allowed** (Optional) - A boolean flag representing
|
||||
whether the domain instance is allowed to do system reset.
|
||||
* **system-suspend-allowed** (Optional) - A boolean flag representing
|
||||
whether the domain instance is allowed to do system suspend.
|
||||
|
||||
### Assigning HART To Domain Instance
|
||||
|
||||
@@ -252,6 +256,7 @@ be done:
|
||||
next-addr = <0x0 0x80100000>;
|
||||
next-mode = <0x0>;
|
||||
system-reset-allowed;
|
||||
system-suspend-allowed;
|
||||
};
|
||||
|
||||
udomain: untrusted-domain {
|
||||
|
||||
@@ -9,13 +9,6 @@ OpenSBI generic library code. The supported firmwares type will differ in how
|
||||
the arguments passed by the platform early boot stage are handled, as well as
|
||||
how the boot stage following the firmware will be handled and executed.
|
||||
|
||||
The previous booting stage will pass information via the following registers
|
||||
of RISC-V CPU:
|
||||
|
||||
* hartid via *a0* register
|
||||
* device tree blob address in memory via *a1* register. The address must
|
||||
be aligned to 8 bytes.
|
||||
|
||||
OpenSBI currently supports three different types of firmwares.
|
||||
|
||||
Firmware with Dynamic Information (*FW_DYNAMIC*)
|
||||
@@ -69,12 +62,6 @@ parameters:
|
||||
argument by the prior booting stage.
|
||||
* **FW_FDT_PADDING** - Optional zero bytes padding to the embedded flattened
|
||||
device tree binary file specified by **FW_FDT_PATH** option.
|
||||
* **FW_PIC** - "FW_PIC=y" generates position independent executable firmware
|
||||
images. OpenSBI can run at arbitrary address with appropriate alignment.
|
||||
Therefore, the original relocation mechanism ("FW_PIC=n") will be skipped.
|
||||
In other words, OpenSBI will directly run at the load address without any
|
||||
code movement. This option requires a toolchain with PIE support, and it
|
||||
is on by default.
|
||||
|
||||
Additionally, each firmware type as a set of type specific configuration
|
||||
parameters. Detailed information for each firmware type can be found in the
|
||||
|
||||
@@ -6,9 +6,8 @@ information about next booting stage (e.g. a bootloader or an OS) and runtime
|
||||
OpenSBI library options from previous booting stage.
|
||||
|
||||
The previous booting stage will pass information to *FW_DYNAMIC* by creating
|
||||
*struct fw_dynamic_info* in memory and passing its address to *FW_DYNAMIC*
|
||||
via *a2* register of RISC-V CPU. The address must be aligned to 8 bytes on
|
||||
RV64 and 4 bytes on RV32.
|
||||
*struct fw_dynamic_info* in memory and passing it's address to *FW_DYNAMIC*
|
||||
via *a2* register of RISC-V CPU.
|
||||
|
||||
A *FW_DYNAMIC* firmware is particularly useful when the booting stage executed
|
||||
prior to OpenSBI firmware is capable of loading both the OpenSBI firmware and
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Andes AE350 SoC Platform
|
||||
========================
|
||||
The AE350 AXI/AHB-based platform N25(F)/NX25(F)/D25F/A25/AX25 CPU with level-one
|
||||
memories, interrupt controller, debug module, AXI and AHB Bus Matrix Controller,
|
||||
AXI-to-AHB Bridge and a collection of fundamental AHB/APB bus IP components
|
||||
pre-integrated together as a system design. The high-quality and configurable
|
||||
memories,interrupt controller, debug module, AXI and AHB Bus Matrix Controller,
|
||||
AXI-to-AHB Bridge and a collection of fundamentalAHB/APB bus IP components
|
||||
pre-integrated together as a system design.The high-quality and configurable
|
||||
AHB/APB IPs suites a majority embedded systems, and the verified platform serves
|
||||
as a starting point to jump start SoC designs.
|
||||
|
||||
|
||||
@@ -46,13 +46,11 @@ RISC-V Platforms Using Generic Platform
|
||||
---------------------------------------
|
||||
|
||||
* **QEMU RISC-V Virt Machine** (*[qemu_virt.md]*)
|
||||
* **Shakti C-class SoC Platform** (*[shakti_cclass.md]*)
|
||||
* **SiFive HiFive Unleashed** (*[sifive_fu540.md]*)
|
||||
* **Spike** (*[spike.md]*)
|
||||
* **Shakti C-class SoC Platform** (*[shakti_cclass.md]*)
|
||||
* **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*)
|
||||
|
||||
[qemu_virt.md]: qemu_virt.md
|
||||
[shakti_cclass.md]: shakti_cclass.md
|
||||
[sifive_fu540.md]: sifive_fu540.md
|
||||
[spike.md]: spike.md
|
||||
[shakti_cclass.md]: shakti_cclass.md
|
||||
[thead-c9xx.md]: thead-c9xx.md
|
||||
|
||||
@@ -147,27 +147,3 @@ qemu-system-riscv32 -M virt -m 256M -nographic \
|
||||
-device virtio-blk-device,drive=hd0 \
|
||||
-append "root=/dev/vda rw console=ttyS0"
|
||||
```
|
||||
|
||||
Debugging with GDB
|
||||
------------------
|
||||
|
||||
In a first console start OpenSBI with QEMU:
|
||||
|
||||
```
|
||||
qemu-system-riscv64 -M virt -m 256M -nographic \
|
||||
-bios build/platform/generic/firmware/fw_payload.bin \
|
||||
-gdb tcp::1234 \
|
||||
-S
|
||||
|
||||
```
|
||||
|
||||
Parameter *-gdb tcp::1234* specifies 1234 as the debug port.
|
||||
Parameter *-S* lets QEMU wait at the first instruction.
|
||||
|
||||
In a second console start GDB:
|
||||
|
||||
```
|
||||
gdb build/platform/generic/firmware/fw_payload.elf \
|
||||
-ex 'target remote localhost:1234'
|
||||
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@ With QEMU v4.2 or above release, the 'sifive_u' machine can be used to test
|
||||
OpenSBI image built for the real hardware as well.
|
||||
|
||||
To build platform specific library and firmwares, provide the
|
||||
*PLATFORM=generic* parameter to the top level `make` command.
|
||||
*PLATFORM=sifive/fu540* parameter to the top level `make` command.
|
||||
|
||||
Platform Options
|
||||
----------------
|
||||
@@ -32,10 +32,10 @@ To use Linux v5.2 (or higher), the pre-built DTB (DT binary) from Linux v5.2
|
||||
the compile time option *FW_FDT_PATH*.
|
||||
|
||||
```
|
||||
make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
|
||||
or
|
||||
(For Linux v5.2 or higher)
|
||||
make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
|
||||
```
|
||||
|
||||
**U-Boot Payload**
|
||||
@@ -45,7 +45,7 @@ sifive_fu540_defconfig configuration and with U-Boot v2020.01, and up to
|
||||
v2021.04. sifive_unleashed_defconfig shall be used with v2021.07 or above.
|
||||
|
||||
```
|
||||
make PLATFORM=generic FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin
|
||||
```
|
||||
For U-Boot v2020.07-rc4 or later releases, SPL support was added in U-Boot.
|
||||
Please refer to the detailed U-Boot booting guide available at [U-Boot].
|
||||
@@ -64,7 +64,7 @@ That's why the generated firmware binary in above steps should be copied to
|
||||
the partition of the sdcard with above GUID.
|
||||
|
||||
```
|
||||
dd if=build/platform/generic/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024
|
||||
dd if=build/platform/sifive/fu540/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024
|
||||
```
|
||||
|
||||
In my case, it is the first partition is **disk2s1** that has been formatted
|
||||
@@ -161,7 +161,7 @@ U-Boot uses the DTB generated by QEMU, and u-boot.bin should be used as the
|
||||
payload image, like:
|
||||
|
||||
```
|
||||
make PLATFORM=generic FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
|
||||
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
|
||||
```
|
||||
|
||||
U-Boot v2020.07 release added SPL support to SiFive HiFive Unleashed board,
|
||||
@@ -179,12 +179,12 @@ The above errors can be safely ignored as we don't run U-Boot SPL under QEMU.
|
||||
Run:
|
||||
```
|
||||
qemu-system-riscv64 -M sifive_u -m 256M -nographic \
|
||||
-bios build/platform/generic/firmware/fw_payload.bin
|
||||
-bios build/platform/sifive/fu540/firmware/fw_payload.bin
|
||||
```
|
||||
or
|
||||
```
|
||||
qemu-system-riscv64 -M sifive_u -m 256M -nographic \
|
||||
-bios build/platform/generic/firmware/fw_jump.bin \
|
||||
-bios build/platform/sifive/fu540/firmware/fw_jump.bin \
|
||||
-kernel <uboot_build_dir>/u-boot.bin
|
||||
```
|
||||
|
||||
|
||||
@@ -43,18 +43,7 @@ make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Im
|
||||
|
||||
Run:
|
||||
```
|
||||
spike -m256 \
|
||||
--initrd <path_to_cpio_ramdisk> \
|
||||
--bootargs 'root=/dev/ram rw console=hvc0 earlycon=sbi' \
|
||||
build/platform/generic/firmware/fw_payload.elf
|
||||
```
|
||||
or
|
||||
```
|
||||
spike -m256 \
|
||||
--kernel <linux_build_directory>/arch/riscv/boot/Image \
|
||||
--initrd <path_to_cpio_ramdisk> \
|
||||
--bootargs 'root=/dev/ram rw console=hvc0 earlycon=sbi' \
|
||||
build/platform/generic/firmware/fw_jump.elf
|
||||
spike --initrd <path_to_cpio_ramdisk> build/platform/generic/firmware/fw_payload.elf
|
||||
```
|
||||
|
||||
Execution on QEMU RISC-V 64-bit
|
||||
|
||||
@@ -16,7 +16,7 @@ The *T-HEAD C9xx* does not have any platform-specific compile options
|
||||
because it use generic platform.
|
||||
|
||||
```
|
||||
CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic /usr/bin/make
|
||||
CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic FW_PIC=y /usr/bin/make
|
||||
```
|
||||
|
||||
The *T-HEAD C9xx* DTB provided to OpenSBI generic firmwares will usually have
|
||||
@@ -51,18 +51,23 @@ DTS Example1: (Single core, eg: Allwinner D1 - c906)
|
||||
compatible = "simple-bus";
|
||||
ranges;
|
||||
|
||||
reset: reset-sample {
|
||||
compatible = "thead,reset-sample";
|
||||
plic-delegate = <0x0 0x101ffffc>;
|
||||
};
|
||||
|
||||
clint0: clint@14000000 {
|
||||
compatible = "allwinner,sun20i-d1-clint";
|
||||
compatible = "riscv,clint0";
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 3 &cpu0_intc 7
|
||||
>;
|
||||
reg = <0x0 0x14000000 0x0 0x04000000>;
|
||||
clint,has-no-64bit-mmio;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@10000000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "allwinner,sun20i-d1-plic",
|
||||
"thead,c900-plic";
|
||||
compatible = "riscv,plic0";
|
||||
interrupt-controller;
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 0xffffffff &cpu0_intc 9
|
||||
@@ -145,6 +150,7 @@ DTS Example2: (Multi cores with soc reset-regs)
|
||||
|
||||
reset: reset-sample {
|
||||
compatible = "thead,reset-sample";
|
||||
plic-delegate = <0xff 0xd81ffffc>;
|
||||
entry-reg = <0xff 0xff019050>;
|
||||
entry-cnt = <4>;
|
||||
control-reg = <0xff 0xff015004>;
|
||||
@@ -162,11 +168,12 @@ DTS Example2: (Multi cores with soc reset-regs)
|
||||
&cpu4_intc 3 &cpu4_intc 7
|
||||
>;
|
||||
reg = <0xff 0xdc000000 0x0 0x04000000>;
|
||||
clint,has-no-64bit-mmio;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@ffd8000000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "thead,c900-plic";
|
||||
compatible = "riscv,plic0";
|
||||
interrupt-controller;
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 0xffffffff &cpu0_intc 9
|
||||
@@ -187,6 +194,7 @@ DTS Example2: (Multi cores with old reset csrs)
|
||||
```
|
||||
reset: reset-sample {
|
||||
compatible = "thead,reset-sample";
|
||||
plic-delegate = <0xff 0xd81ffffc>;
|
||||
using-csr-reset;
|
||||
csr-copy = <0x7c0 0x7c1 0x7c2 0x7c3 0x7c5 0x7cc
|
||||
0x3b0 0x3b1 0x3b2 0x3b3
|
||||
|
||||
@@ -17,7 +17,7 @@ the supported platforms. These firmwares are linked against *libplatsbi.a*.
|
||||
Firmware binaries are installed in
|
||||
*<install_directory>/platform/<platform_subdir>/bin*. These firmwares can be
|
||||
used as executable runtime firmwares on the supported platforms as a replacement
|
||||
for the legacy *riscv-pk* boot loader (BBL).
|
||||
for the legacy *riskv-pk* boot loader (BBL).
|
||||
|
||||
A complete doxygen-style documentation of *struct sbi_platform* and related
|
||||
APIs is available in the file *include/sbi/sbi_platform.h*.
|
||||
@@ -25,18 +25,18 @@ APIs is available in the file *include/sbi/sbi_platform.h*.
|
||||
Adding support for a new platform
|
||||
---------------------------------
|
||||
|
||||
Support for a new platform named *<xyz>* can be added as follows:
|
||||
Support for a new platform named *<xyz>* can be added as follows:
|
||||
|
||||
1. Create a directory named *<xyz>* under the *platform/* directory.
|
||||
1. Create a directory named *<xyz>* under the *platform/* directory.
|
||||
2. Create a platform configuration file named *config.mk* under the
|
||||
*platform/<xyz>/* directory. This configuration file will provide
|
||||
*platform/<xyz>/* directory. This configuration file will provide
|
||||
compiler flags, and select firmware options.
|
||||
3. Create a *platform/<xyz>/objects.mk* file for listing the
|
||||
3. Create a *platform/<xyz>/objects.mk* file for listing the
|
||||
platform-specific object files to be compiled.
|
||||
4. Create a *platform/<xyz>/platform.c* file providing a
|
||||
*struct sbi_platform* instance.
|
||||
4. Create a *platform/<xyz>/platform.c* file providing a *struct sbi_platform*
|
||||
instance.
|
||||
|
||||
A platform support code template is available under the *platform/template*
|
||||
directory. Copying this directory and its content as a new directory named
|
||||
*<xyz>* under the *platform/* directory will create all the files
|
||||
mentioned above.
|
||||
*<xyz>* under the *platform/* directory will create all the files mentioned
|
||||
above.
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
OpenSBI SBI PMU extension support
|
||||
==================================
|
||||
SBI PMU extension supports allow supervisor software to configure/start/stop
|
||||
any performance counter at anytime. Thus, an user can leverage full
|
||||
capability of performance analysis tools such as perf if SBI PMU extension is
|
||||
enabled. The OpenSBI implementation makes the following assumptions about the
|
||||
hardware platform.
|
||||
|
||||
* MCOUNTINHIBIT CSR must be implemented in the hardware. Otherwise, SBI PMU
|
||||
extension will not be enabled.
|
||||
|
||||
* The platform must provide information about PMU event to counter mapping
|
||||
via device tree or platform specific hooks. Otherwise, SBI PMU extension will
|
||||
not be enabled.
|
||||
|
||||
* The platforms should provide information about the PMU event selector values
|
||||
that should be encoded in the expected value of MHPMEVENTx while configuring
|
||||
MHPMCOUNTERx for that specific event. This can be done via a device tree or
|
||||
platform specific hooks. The exact value to be written to he MHPMEVENTx is
|
||||
completely depends on platform. Generic platform writes the zero-extended event_idx
|
||||
as the expected value for hardware cache/generic events as suggested by the SBI
|
||||
specification.
|
||||
|
||||
SBI PMU Device Tree Bindings
|
||||
----------------------------
|
||||
|
||||
Platforms may choose to describe PMU event selector and event to counter mapping
|
||||
values via device tree. The following sections describes the PMU DT node
|
||||
bindings in details.
|
||||
|
||||
* **compatible** (Mandatory) - The compatible string of SBI PMU device tree node.
|
||||
This DT property must have the value **riscv,pmu**.
|
||||
|
||||
* **riscv,event-to-mhpmevent**(Optional) - It represents an ONE-to-ONE mapping
|
||||
between a PMU event and the event selector value that platform expects to be
|
||||
written to the MHPMEVENTx CSR for that event. The mapping is encoded in a
|
||||
table format where each row represents an event. The first column represent the
|
||||
event idx where the 2nd & 3rd column represent the event selector value that
|
||||
should be encoded in the expected value to be written in MHPMEVENTx.
|
||||
This property shouldn't encode any raw hardware event.
|
||||
|
||||
* **riscv,event-to-mhpmcounters**(Optional) - It represents a MANY-to-MANY
|
||||
mapping between a range of events and all the MHPMCOUNTERx in a bitmap format
|
||||
that can be used to monitor these range of events. The information is encoded in
|
||||
a table format where each row represent a certain range of events and
|
||||
corresponding counters. The first column represents starting of the pmu event id
|
||||
and 2nd column represents the end of the pmu event id. The third column
|
||||
represent a bitmap of all the MHPMCOUNTERx. This property is mandatory if
|
||||
event-to-mhpmevent is present. Otherwise, it can be omitted. This property
|
||||
shouldn't encode any raw event.
|
||||
|
||||
* **riscv,raw-event-to-mhpmcounters**(Optional) - It represents an ONE-to-MANY
|
||||
or MANY-to-MANY mapping between the raw event(s) and all the MHPMCOUNTERx in
|
||||
a bitmap format that can be used to monitor that raw event. The encoding of the
|
||||
raw events are platform specific. The information is encoded in a table format
|
||||
where each row represent the specific raw event(s). The first column is a 64bit
|
||||
match value where the invariant bits of range of events are set. The second
|
||||
column is a 64 bit mask that will have all the variant bits of the range of
|
||||
events cleared. Every other bits should be set in the mask.
|
||||
The third column is a 32bit value to represent bitmap of all MHPMCOUNTERx that
|
||||
can monitor these set of event(s).
|
||||
If a platform directly encodes each raw PMU event as a unique ID, the value of
|
||||
select_mask must be 0xffffffff_ffffffff.
|
||||
|
||||
*Note:* A platform may choose to provide the mapping between event & counters
|
||||
via platform hooks rather than the device tree.
|
||||
|
||||
### Example 1
|
||||
|
||||
```
|
||||
pmu {
|
||||
compatible = "riscv,pmu";
|
||||
riscv,event-to-mhpmevent = <0x0000B 0x0000 0x0001>,
|
||||
riscv,event-to-mhpmcounters = <0x00001 0x00001 0x00000001>,
|
||||
<0x00002 0x00002 0x00000004>,
|
||||
<0x00003 0x0000A 0x00000ff8>,
|
||||
<0x10000 0x10033 0x000ff000>,
|
||||
/* For event ID 0x0002 */
|
||||
riscv,raw-event-to-mhpmcounters = <0x0000 0x0002 0xffffffff 0xffffffff 0x00000f8>,
|
||||
/* For event ID 0-4 */
|
||||
<0x0 0x0 0xffffffff 0xfffffff0 0x00000ff0>,
|
||||
/* For event ID 0xffffffff0000000f - 0xffffffff000000ff */
|
||||
<0xffffffff 0x0 0xffffffff 0xffffff0f 0x00000ff0>,
|
||||
};
|
||||
```
|
||||
|
||||
### Example 2
|
||||
|
||||
```
|
||||
/*
|
||||
* For HiFive Unmatched board. The encodings can be found here
|
||||
* https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
|
||||
*/
|
||||
pmu {
|
||||
compatible = "riscv,pmu";
|
||||
riscv,raw-event-to-mhpmcounters = <0x0 0x0 0xffffffff 0xfc0000ff 0xc>,
|
||||
<0x0 0x1 0xffffffff 0xfff800ff 0xc>,
|
||||
<0x0 0x2 0xffffffff 0xffffe0ff 0xc>;
|
||||
};
|
||||
```
|
||||
@@ -65,14 +65,14 @@ _try_lottery:
|
||||
|
||||
/* Save load address */
|
||||
lla t0, _load_start
|
||||
lla t1, _fw_start
|
||||
lla t1, _start
|
||||
REG_S t1, 0(t0)
|
||||
|
||||
#ifdef FW_PIC
|
||||
/* relocate the global table content */
|
||||
lla t0, _link_start
|
||||
REG_L t0, 0(t0)
|
||||
/* t1 shall has the address of _fw_start */
|
||||
/* t1 shall has the address of _start */
|
||||
sub t2, t1, t0
|
||||
lla t3, _runtime_offset
|
||||
REG_S t2, (t3)
|
||||
@@ -172,7 +172,7 @@ _relocate_copy_to_upper_loop:
|
||||
blt t0, t1, _relocate_copy_to_upper_loop
|
||||
jr t4
|
||||
_wait_relocate_copy_done:
|
||||
lla t0, _fw_start
|
||||
lla t0, _start
|
||||
lla t1, _link_start
|
||||
REG_L t1, 0(t1)
|
||||
beq t0, t1, _wait_for_boot_hart
|
||||
@@ -259,7 +259,7 @@ _bss_zero:
|
||||
* s8 -> HART Stack Size
|
||||
*/
|
||||
lla a4, platform
|
||||
#if __riscv_xlen > 32
|
||||
#if __riscv_xlen == 64
|
||||
lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4)
|
||||
lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4)
|
||||
#else
|
||||
@@ -278,14 +278,6 @@ _bss_zero:
|
||||
/* hartid 0 is mandated by ISA */
|
||||
li t1, 0
|
||||
_scratch_init:
|
||||
/*
|
||||
* The following registers hold values that are computed before
|
||||
* entering this block, and should remain unchanged.
|
||||
*
|
||||
* t3 -> the firmware end address
|
||||
* s7 -> HART count
|
||||
* s8 -> HART stack size
|
||||
*/
|
||||
add tp, t3, zero
|
||||
mul a5, s8, t1
|
||||
sub tp, tp, a5
|
||||
@@ -295,7 +287,10 @@ _scratch_init:
|
||||
/* Initialize scratch space */
|
||||
/* Store fw_start and fw_size in scratch space */
|
||||
lla a4, _fw_start
|
||||
sub a5, t3, a4
|
||||
lla a5, _fw_end
|
||||
mul t0, s7, s8
|
||||
add a5, a5, t0
|
||||
sub a5, a5, a4
|
||||
REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp)
|
||||
REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp)
|
||||
/* Store next arg1 in scratch space */
|
||||
@@ -349,7 +344,8 @@ _scratch_init:
|
||||
* previous booting stage.
|
||||
*/
|
||||
beqz a1, _fdt_reloc_done
|
||||
/* Mask values in a4 */
|
||||
/* Mask values in a3 and a4 */
|
||||
li a3, ~(__SIZEOF_POINTER__ - 1)
|
||||
li a4, 0xff
|
||||
/* t1 = destination FDT start address */
|
||||
MOV_3R s0, a0, s1, a1, s2, a2
|
||||
@@ -358,8 +354,10 @@ _scratch_init:
|
||||
MOV_3R a0, s0, a1, s1, a2, s2
|
||||
beqz t1, _fdt_reloc_done
|
||||
beq t1, a1, _fdt_reloc_done
|
||||
and t1, t1, a3
|
||||
/* t0 = source FDT start address */
|
||||
add t0, a1, zero
|
||||
and t0, t0, a3
|
||||
/* t2 = source FDT size in big-endian */
|
||||
#if __riscv_xlen == 64
|
||||
lwu t2, 4(t0)
|
||||
@@ -503,7 +501,6 @@ _skip_trap_exit_rv32_hyp:
|
||||
/* We don't expect to reach here hence just hang */
|
||||
j _start_hang
|
||||
|
||||
.data
|
||||
.align 3
|
||||
#ifdef FW_PIC
|
||||
_runtime_offset:
|
||||
@@ -556,39 +553,12 @@ _start_hang:
|
||||
|
||||
.section .entry, "ax", %progbits
|
||||
.align 3
|
||||
.globl fw_platform_init
|
||||
.weak fw_platform_init
|
||||
fw_platform_init:
|
||||
add a0, a1, zero
|
||||
ret
|
||||
|
||||
/* Map implicit memcpy() added by compiler to sbi_memcpy() */
|
||||
.section .text
|
||||
.align 3
|
||||
.globl memcpy
|
||||
memcpy:
|
||||
tail sbi_memcpy
|
||||
|
||||
/* Map implicit memset() added by compiler to sbi_memset() */
|
||||
.section .text
|
||||
.align 3
|
||||
.globl memset
|
||||
memset:
|
||||
tail sbi_memset
|
||||
|
||||
/* Map implicit memmove() added by compiler to sbi_memmove() */
|
||||
.section .text
|
||||
.align 3
|
||||
.globl memmove
|
||||
memmove:
|
||||
tail sbi_memmove
|
||||
|
||||
/* Map implicit memcmp() added by compiler to sbi_memcmp() */
|
||||
.section .text
|
||||
.align 3
|
||||
.globl memcmp
|
||||
memcmp:
|
||||
tail sbi_memcmp
|
||||
|
||||
.macro TRAP_SAVE_AND_SETUP_SP_T0
|
||||
/* Swap TP and MSCRATCH */
|
||||
csrrw tp, CSR_MSCRATCH, tp
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
. = FW_TEXT_START;
|
||||
/* Don't add any section between FW_TEXT_START and _fw_start */
|
||||
|
||||
PROVIDE(_fw_start = .);
|
||||
|
||||
. = ALIGN(0x1000); /* Need this to create proper sections */
|
||||
|
||||
@@ -36,7 +36,7 @@ fw_boot_hart:
|
||||
bgt a0, a1, _bad_dynamic_info
|
||||
|
||||
/* Read boot HART id */
|
||||
li a1, FW_DYNAMIC_INFO_VERSION_2
|
||||
li a1, 0x2
|
||||
blt a0, a1, 2f
|
||||
REG_L a0, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2)
|
||||
ret
|
||||
@@ -57,6 +57,14 @@ fw_save_info:
|
||||
lla a4, _dynamic_next_arg1
|
||||
REG_S a1, (a4)
|
||||
|
||||
/* Sanity checks */
|
||||
li a4, FW_DYNAMIC_INFO_MAGIC_VALUE
|
||||
REG_L a3, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2)
|
||||
bne a3, a4, _bad_dynamic_info
|
||||
li a4, FW_DYNAMIC_INFO_VERSION_MAX
|
||||
REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
|
||||
bgt a3, a4, _bad_dynamic_info
|
||||
|
||||
/* Save version == 0x1 fields */
|
||||
lla a4, _dynamic_next_addr
|
||||
REG_L a3, FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET(a2)
|
||||
@@ -69,7 +77,7 @@ fw_save_info:
|
||||
REG_S a3, (a4)
|
||||
|
||||
/* Save version == 0x2 fields */
|
||||
li a4, FW_DYNAMIC_INFO_VERSION_2
|
||||
li a4, 0x2
|
||||
REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
|
||||
blt a3, a4, 2f
|
||||
lla a4, _dynamic_boot_hart
|
||||
|
||||
@@ -14,14 +14,14 @@ firmware-asflags-y +=
|
||||
firmware-ldflags-y +=
|
||||
|
||||
ifndef FW_PIC
|
||||
FW_PIC := $(OPENSBI_LD_PIE)
|
||||
FW_PIC := y
|
||||
endif
|
||||
|
||||
ifeq ($(FW_PIC),y)
|
||||
firmware-genflags-y += -DFW_PIC
|
||||
firmware-asflags-y += -fpic
|
||||
firmware-cflags-y += -fPIE -pie
|
||||
firmware-ldflags-y += -Wl,--no-dynamic-linker -Wl,-pie
|
||||
firmware-ldflags-y += -Wl,--no-dynamic-linker
|
||||
endif
|
||||
|
||||
ifdef FW_TEXT_START
|
||||
|
||||
@@ -9,25 +9,24 @@
|
||||
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
|
||||
#define SBI_ECALL(__eid, __fid, __a0, __a1, __a2) \
|
||||
#define SBI_ECALL(__num, __a0, __a1, __a2) \
|
||||
({ \
|
||||
register unsigned long a0 asm("a0") = (unsigned long)(__a0); \
|
||||
register unsigned long a1 asm("a1") = (unsigned long)(__a1); \
|
||||
register unsigned long a2 asm("a2") = (unsigned long)(__a2); \
|
||||
register unsigned long a6 asm("a6") = (unsigned long)(__fid); \
|
||||
register unsigned long a7 asm("a7") = (unsigned long)(__eid); \
|
||||
register unsigned long a7 asm("a7") = (unsigned long)(__num); \
|
||||
asm volatile("ecall" \
|
||||
: "+r"(a0) \
|
||||
: "r"(a1), "r"(a2), "r"(a6), "r"(a7) \
|
||||
: "r"(a1), "r"(a2), "r"(a7) \
|
||||
: "memory"); \
|
||||
a0; \
|
||||
})
|
||||
|
||||
#define SBI_ECALL_0(__eid, __fid) SBI_ECALL(__eid, __fid, 0, 0, 0)
|
||||
#define SBI_ECALL_1(__eid, __fid, __a0) SBI_ECALL(__eid, __fid, __a0, 0, 0)
|
||||
#define SBI_ECALL_2(__eid, __fid, __a0, __a1) SBI_ECALL(__eid, __fid, __a0, __a1, 0)
|
||||
#define SBI_ECALL_0(__num) SBI_ECALL(__num, 0, 0, 0)
|
||||
#define SBI_ECALL_1(__num, __a0) SBI_ECALL(__num, __a0, 0, 0)
|
||||
#define SBI_ECALL_2(__num, __a0, __a1) SBI_ECALL(__num, __a0, __a1, 0)
|
||||
|
||||
#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, (c))
|
||||
#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, (c))
|
||||
|
||||
static inline void sbi_ecall_console_puts(const char *str)
|
||||
{
|
||||
|
||||
@@ -15,24 +15,23 @@
|
||||
/* clang-format off */
|
||||
|
||||
/** Offset of magic member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_POINTER__)
|
||||
/** Offset of version member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_addr member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_mode member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_POINTER__)
|
||||
/** Offset of options member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_POINTER__)
|
||||
/** Offset of boot_hart member in fw_dynamic_info (version >= 2) */
|
||||
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_LONG__)
|
||||
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_POINTER__)
|
||||
|
||||
/** Expected value of info magic ('OSBI' ascii string in hex) */
|
||||
#define FW_DYNAMIC_INFO_MAGIC_VALUE 0x4942534f
|
||||
|
||||
/** Maximum supported info version */
|
||||
#define FW_DYNAMIC_INFO_VERSION_2 0x2
|
||||
#define FW_DYNAMIC_INFO_VERSION_MAX FW_DYNAMIC_INFO_VERSION_2
|
||||
#define FW_DYNAMIC_INFO_VERSION_MAX 0x2
|
||||
|
||||
/** Possible next mode values */
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_U 0x0
|
||||
@@ -75,41 +74,6 @@ struct fw_dynamic_info {
|
||||
unsigned long boot_hart;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Prevent modification of struct fw_dynamic_info from affecting
|
||||
* FW_DYNAMIC_INFO_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, magic)
|
||||
== FW_DYNAMIC_INFO_MAGIC_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_MAGIC_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, version)
|
||||
== FW_DYNAMIC_INFO_VERSION_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, next_addr)
|
||||
== FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, next_mode)
|
||||
== FW_DYNAMIC_INFO_NEXT_MODE_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_NEXT_MODE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, options)
|
||||
== FW_DYNAMIC_INFO_OPTIONS_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_OPTIONS_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, boot_hart)
|
||||
== FW_DYNAMIC_INFO_BOOT_HART_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_BOOT_HART_OFFSET");
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define MSTATUS_MPP (_UL(3) << MSTATUS_MPP_SHIFT)
|
||||
#define MSTATUS_FS _UL(0x00006000)
|
||||
#define MSTATUS_XS _UL(0x00018000)
|
||||
#define MSTATUS_VS _UL(0x00000600)
|
||||
#define MSTATUS_VS _UL(0x01800000)
|
||||
#define MSTATUS_MPRV _UL(0x00020000)
|
||||
#define MSTATUS_SUM _UL(0x00040000)
|
||||
#define MSTATUS_MXR _UL(0x00080000)
|
||||
@@ -86,7 +86,6 @@
|
||||
#define IRQ_VS_EXT 10
|
||||
#define IRQ_M_EXT 11
|
||||
#define IRQ_S_GEXT 12
|
||||
#define IRQ_PMU_OVF 13
|
||||
|
||||
#define MIP_SSIP (_UL(1) << IRQ_S_SOFT)
|
||||
#define MIP_VSSIP (_UL(1) << IRQ_VS_SOFT)
|
||||
@@ -98,7 +97,6 @@
|
||||
#define MIP_VSEIP (_UL(1) << IRQ_VS_EXT)
|
||||
#define MIP_MEIP (_UL(1) << IRQ_M_EXT)
|
||||
#define MIP_SGEIP (_UL(1) << IRQ_S_GEXT)
|
||||
#define MIP_LCOFIP (_UL(1) << IRQ_PMU_OVF)
|
||||
|
||||
#define SIP_SSIP MIP_SSIP
|
||||
#define SIP_STIP MIP_STIP
|
||||
@@ -173,52 +171,6 @@
|
||||
#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT
|
||||
#endif
|
||||
|
||||
#define TOPI_IID_SHIFT 16
|
||||
#define TOPI_IID_MASK 0xfff
|
||||
#define TOPI_IPRIO_MASK 0xff
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
#define MHPMEVENT_OF (_UL(1) << 63)
|
||||
#define MHPMEVENT_MINH (_UL(1) << 62)
|
||||
#define MHPMEVENT_SINH (_UL(1) << 61)
|
||||
#define MHPMEVENT_UINH (_UL(1) << 60)
|
||||
#define MHPMEVENT_VSINH (_UL(1) << 59)
|
||||
#define MHPMEVENT_VUINH (_UL(1) << 58)
|
||||
#else
|
||||
#define MHPMEVENTH_OF (_ULL(1) << 31)
|
||||
#define MHPMEVENTH_MINH (_ULL(1) << 30)
|
||||
#define MHPMEVENTH_SINH (_ULL(1) << 29)
|
||||
#define MHPMEVENTH_UINH (_ULL(1) << 28)
|
||||
#define MHPMEVENTH_VSINH (_ULL(1) << 27)
|
||||
#define MHPMEVENTH_VUINH (_ULL(1) << 26)
|
||||
|
||||
#define MHPMEVENT_OF (MHPMEVENTH_OF << 32)
|
||||
#define MHPMEVENT_MINH (MHPMEVENTH_MINH << 32)
|
||||
#define MHPMEVENT_SINH (MHPMEVENTH_SINH << 32)
|
||||
#define MHPMEVENT_UINH (MHPMEVENTH_UINH << 32)
|
||||
#define MHPMEVENT_VSINH (MHPMEVENTH_VSINH << 32)
|
||||
#define MHPMEVENT_VUINH (MHPMEVENTH_VUINH << 32)
|
||||
|
||||
#endif
|
||||
|
||||
#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000)
|
||||
|
||||
#if __riscv_xlen > 32
|
||||
#define ENVCFG_STCE (_ULL(1) << 63)
|
||||
#define ENVCFG_PBMTE (_ULL(1) << 62)
|
||||
#else
|
||||
#define ENVCFGH_STCE (_UL(1) << 31)
|
||||
#define ENVCFGH_PBMTE (_UL(1) << 30)
|
||||
#endif
|
||||
#define ENVCFG_CBZE (_UL(1) << 7)
|
||||
#define ENVCFG_CBCFE (_UL(1) << 6)
|
||||
#define ENVCFG_CBIE_SHIFT 4
|
||||
#define ENVCFG_CBIE (_UL(0x3) << ENVCFG_CBIE_SHIFT)
|
||||
#define ENVCFG_CBIE_ILL _UL(0x0)
|
||||
#define ENVCFG_CBIE_FLUSH _UL(0x1)
|
||||
#define ENVCFG_CBIE_INV _UL(0x3)
|
||||
#define ENVCFG_FIOM _UL(0x1)
|
||||
|
||||
/* ===== User-level CSRs ===== */
|
||||
|
||||
/* User Trap Setup (N-extension) */
|
||||
@@ -314,9 +266,6 @@
|
||||
#define CSR_STVEC 0x105
|
||||
#define CSR_SCOUNTEREN 0x106
|
||||
|
||||
/* Supervisor Configuration */
|
||||
#define CSR_SENVCFG 0x10a
|
||||
|
||||
/* Supervisor Trap Handling */
|
||||
#define CSR_SSCRATCH 0x140
|
||||
#define CSR_SEPC 0x141
|
||||
@@ -324,31 +273,9 @@
|
||||
#define CSR_STVAL 0x143
|
||||
#define CSR_SIP 0x144
|
||||
|
||||
/* Sstc extension */
|
||||
#define CSR_STIMECMP 0x14D
|
||||
#define CSR_STIMECMPH 0x15D
|
||||
|
||||
/* Supervisor Protection and Translation */
|
||||
#define CSR_SATP 0x180
|
||||
|
||||
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
#define CSR_SISELECT 0x150
|
||||
#define CSR_SIREG 0x151
|
||||
|
||||
/* Supervisor-Level Interrupts (AIA) */
|
||||
#define CSR_STOPEI 0x15c
|
||||
#define CSR_STOPI 0xdb0
|
||||
|
||||
/* Supervisor-Level High-Half CSRs (AIA) */
|
||||
#define CSR_SIEH 0x114
|
||||
#define CSR_SIPH 0x154
|
||||
|
||||
/* Supervisor stateen CSRs */
|
||||
#define CSR_SSTATEEN0 0x10C
|
||||
#define CSR_SSTATEEN1 0x10D
|
||||
#define CSR_SSTATEEN2 0x10E
|
||||
#define CSR_SSTATEEN3 0x10F
|
||||
|
||||
/* ===== Hypervisor-level CSRs ===== */
|
||||
|
||||
/* Hypervisor Trap Setup (H-extension) */
|
||||
@@ -359,10 +286,6 @@
|
||||
#define CSR_HCOUNTEREN 0x606
|
||||
#define CSR_HGEIE 0x607
|
||||
|
||||
/* Hypervisor Configuration */
|
||||
#define CSR_HENVCFG 0x60a
|
||||
#define CSR_HENVCFGH 0x61a
|
||||
|
||||
/* Hypervisor Trap Handling (H-extension) */
|
||||
#define CSR_HTVAL 0x643
|
||||
#define CSR_HIP 0x644
|
||||
@@ -388,39 +311,6 @@
|
||||
#define CSR_VSIP 0x244
|
||||
#define CSR_VSATP 0x280
|
||||
|
||||
/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
|
||||
#define CSR_HVIEN 0x608
|
||||
#define CSR_HVICTL 0x609
|
||||
#define CSR_HVIPRIO1 0x646
|
||||
#define CSR_HVIPRIO2 0x647
|
||||
|
||||
/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
|
||||
#define CSR_VSISELECT 0x250
|
||||
#define CSR_VSIREG 0x251
|
||||
|
||||
/* VS-Level Interrupts (H-extension with AIA) */
|
||||
#define CSR_VSTOPEI 0x25c
|
||||
#define CSR_VSTOPI 0xeb0
|
||||
|
||||
/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */
|
||||
#define CSR_HIDELEGH 0x613
|
||||
#define CSR_HVIENH 0x618
|
||||
#define CSR_HVIPH 0x655
|
||||
#define CSR_HVIPRIO1H 0x656
|
||||
#define CSR_HVIPRIO2H 0x657
|
||||
#define CSR_VSIEH 0x214
|
||||
#define CSR_VSIPH 0x254
|
||||
|
||||
/* Hypervisor stateen CSRs */
|
||||
#define CSR_HSTATEEN0 0x60C
|
||||
#define CSR_HSTATEEN0H 0x61C
|
||||
#define CSR_HSTATEEN1 0x60D
|
||||
#define CSR_HSTATEEN1H 0x61D
|
||||
#define CSR_HSTATEEN2 0x60E
|
||||
#define CSR_HSTATEEN2H 0x61E
|
||||
#define CSR_HSTATEEN3 0x60F
|
||||
#define CSR_HSTATEEN3H 0x61F
|
||||
|
||||
/* ===== Machine-level CSRs ===== */
|
||||
|
||||
/* Machine Information Registers */
|
||||
@@ -439,10 +329,6 @@
|
||||
#define CSR_MCOUNTEREN 0x306
|
||||
#define CSR_MSTATUSH 0x310
|
||||
|
||||
/* Machine Configuration */
|
||||
#define CSR_MENVCFG 0x30a
|
||||
#define CSR_MENVCFGH 0x31a
|
||||
|
||||
/* Machine Trap Handling */
|
||||
#define CSR_MSCRATCH 0x340
|
||||
#define CSR_MEPC 0x341
|
||||
@@ -630,40 +516,6 @@
|
||||
#define CSR_MHPMEVENT30 0x33e
|
||||
#define CSR_MHPMEVENT31 0x33f
|
||||
|
||||
/* For RV32 */
|
||||
#define CSR_MHPMEVENT3H 0x723
|
||||
#define CSR_MHPMEVENT4H 0x724
|
||||
#define CSR_MHPMEVENT5H 0x725
|
||||
#define CSR_MHPMEVENT6H 0x726
|
||||
#define CSR_MHPMEVENT7H 0x727
|
||||
#define CSR_MHPMEVENT8H 0x728
|
||||
#define CSR_MHPMEVENT9H 0x729
|
||||
#define CSR_MHPMEVENT10H 0x72a
|
||||
#define CSR_MHPMEVENT11H 0x72b
|
||||
#define CSR_MHPMEVENT12H 0x72c
|
||||
#define CSR_MHPMEVENT13H 0x72d
|
||||
#define CSR_MHPMEVENT14H 0x72e
|
||||
#define CSR_MHPMEVENT15H 0x72f
|
||||
#define CSR_MHPMEVENT16H 0x730
|
||||
#define CSR_MHPMEVENT17H 0x731
|
||||
#define CSR_MHPMEVENT18H 0x732
|
||||
#define CSR_MHPMEVENT19H 0x733
|
||||
#define CSR_MHPMEVENT20H 0x734
|
||||
#define CSR_MHPMEVENT21H 0x735
|
||||
#define CSR_MHPMEVENT22H 0x736
|
||||
#define CSR_MHPMEVENT23H 0x737
|
||||
#define CSR_MHPMEVENT24H 0x738
|
||||
#define CSR_MHPMEVENT25H 0x739
|
||||
#define CSR_MHPMEVENT26H 0x73a
|
||||
#define CSR_MHPMEVENT27H 0x73b
|
||||
#define CSR_MHPMEVENT28H 0x73c
|
||||
#define CSR_MHPMEVENT29H 0x73d
|
||||
#define CSR_MHPMEVENT30H 0x73e
|
||||
#define CSR_MHPMEVENT31H 0x73f
|
||||
|
||||
/* Counter Overflow CSR */
|
||||
#define CSR_SCOUNTOVF 0xda0
|
||||
|
||||
/* Debug/Trace Registers */
|
||||
#define CSR_TSELECT 0x7a0
|
||||
#define CSR_TDATA1 0x7a1
|
||||
@@ -676,36 +528,6 @@
|
||||
#define CSR_DSCRATCH0 0x7b2
|
||||
#define CSR_DSCRATCH1 0x7b3
|
||||
|
||||
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
#define CSR_MISELECT 0x350
|
||||
#define CSR_MIREG 0x351
|
||||
|
||||
/* Machine-Level Interrupts (AIA) */
|
||||
#define CSR_MTOPEI 0x35c
|
||||
#define CSR_MTOPI 0xfb0
|
||||
|
||||
/* Virtual Interrupts for Supervisor Level (AIA) */
|
||||
#define CSR_MVIEN 0x308
|
||||
#define CSR_MVIP 0x309
|
||||
|
||||
/* Smstateen extension registers */
|
||||
/* Machine stateen CSRs */
|
||||
#define CSR_MSTATEEN0 0x30C
|
||||
#define CSR_MSTATEEN0H 0x31C
|
||||
#define CSR_MSTATEEN1 0x30D
|
||||
#define CSR_MSTATEEN1H 0x31D
|
||||
#define CSR_MSTATEEN2 0x30E
|
||||
#define CSR_MSTATEEN2H 0x31E
|
||||
#define CSR_MSTATEEN3 0x30F
|
||||
#define CSR_MSTATEEN3H 0x31F
|
||||
|
||||
/* Machine-Level High-Half CSRs (AIA) */
|
||||
#define CSR_MIDELEGH 0x313
|
||||
#define CSR_MIEH 0x314
|
||||
#define CSR_MVIENH 0x318
|
||||
#define CSR_MVIPH 0x319
|
||||
#define CSR_MIPH 0x354
|
||||
|
||||
/* ===== Trap/Exception Causes ===== */
|
||||
|
||||
#define CAUSE_MISALIGNED_FETCH 0x0
|
||||
@@ -728,23 +550,6 @@
|
||||
#define CAUSE_VIRTUAL_INST_FAULT 0x16
|
||||
#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17
|
||||
|
||||
/* Common defines for all smstateen */
|
||||
#define SMSTATEEN_MAX_COUNT 4
|
||||
#define SMSTATEEN0_CS_SHIFT 0
|
||||
#define SMSTATEEN0_CS (_ULL(1) << SMSTATEEN0_CS_SHIFT)
|
||||
#define SMSTATEEN0_FCSR_SHIFT 1
|
||||
#define SMSTATEEN0_FCSR (_ULL(1) << SMSTATEEN0_FCSR_SHIFT)
|
||||
#define SMSTATEEN0_IMSIC_SHIFT 58
|
||||
#define SMSTATEEN0_IMSIC (_ULL(1) << SMSTATEEN0_IMSIC_SHIFT)
|
||||
#define SMSTATEEN0_AIA_SHIFT 59
|
||||
#define SMSTATEEN0_AIA (_ULL(1) << SMSTATEEN0_AIA_SHIFT)
|
||||
#define SMSTATEEN0_SVSLCT_SHIFT 60
|
||||
#define SMSTATEEN0_SVSLCT (_ULL(1) << SMSTATEEN0_SVSLCT_SHIFT)
|
||||
#define SMSTATEEN0_HSENVCFG_SHIFT 62
|
||||
#define SMSTATEEN0_HSENVCFG (_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT)
|
||||
#define SMSTATEEN_STATEN_SHIFT 63
|
||||
#define SMSTATEEN_STATEN (_ULL(1) << SMSTATEEN_STATEN_SHIFT)
|
||||
|
||||
/* ===== Instruction Encodings ===== */
|
||||
|
||||
#define INSN_MATCH_LB 0x3
|
||||
@@ -820,29 +625,6 @@
|
||||
#define INSN_MASK_WFI 0xffffff00
|
||||
#define INSN_MATCH_WFI 0x10500000
|
||||
|
||||
#define INSN_MASK_FENCE_TSO 0xffffffff
|
||||
#define INSN_MATCH_FENCE_TSO 0x8330000f
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
|
||||
/* 64-bit read for VS-stage address translation (RV64) */
|
||||
#define INSN_PSEUDO_VS_LOAD 0x00003000
|
||||
|
||||
/* 64-bit write for VS-stage address translation (RV64) */
|
||||
#define INSN_PSEUDO_VS_STORE 0x00003020
|
||||
|
||||
#elif __riscv_xlen == 32
|
||||
|
||||
/* 32-bit read for VS-stage address translation (RV32) */
|
||||
#define INSN_PSEUDO_VS_LOAD 0x00002000
|
||||
|
||||
/* 32-bit write for VS-stage address translation (RV32) */
|
||||
#define INSN_PSEUDO_VS_STORE 0x00002020
|
||||
|
||||
#else
|
||||
#error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
|
||||
#define INSN_16BIT_MASK 0x3
|
||||
#define INSN_32BIT_MASK 0x1c
|
||||
|
||||
|
||||
@@ -42,28 +42,15 @@
|
||||
: "t0"); \
|
||||
})
|
||||
#define init_fp_reg(i) SET_F32_REG((i) << 3, 3, 0, 0)
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
#define GET_F64_REG(insn, pos, regs) \
|
||||
({ \
|
||||
register ulong value asm("a0") = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
|
||||
register ulong value asm("a0") = \
|
||||
SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
|
||||
ulong tmp; \
|
||||
asm("1: auipc %0, %%pcrel_hi(get_f64_reg); add %0, %0, %1; jalr t0, %0, %%pcrel_lo(1b)" \
|
||||
: "=&r"(tmp), "+&r"(value)::"t0"); \
|
||||
value; \
|
||||
sizeof(ulong) == 4 ? *(int64_t *)value : (int64_t)value; \
|
||||
})
|
||||
#else
|
||||
#define GET_F64_REG(insn, pos, regs) \
|
||||
({ \
|
||||
u64 value; \
|
||||
ulong offset = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
|
||||
register ulong ptr asm("a0") = (ulong)&value; \
|
||||
asm ("1: auipc t1, %%pcrel_hi(get_f64_reg); add t1, t1, %2; jalr t0, t1, %%pcrel_lo(1b)" \
|
||||
: "=m"(value) : "r"(ptr), "r"(offset) : "t0", "t1"); \
|
||||
value; \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define SET_F64_REG(insn, pos, regs, val) \
|
||||
({ \
|
||||
uint64_t __val = (val); \
|
||||
|
||||
@@ -34,9 +34,9 @@ typedef struct {
|
||||
#define DEFINE_SPIN_LOCK(x) \
|
||||
spinlock_t SPIN_LOCK_INIT(x)
|
||||
|
||||
bool spin_lock_check(spinlock_t *lock);
|
||||
int spin_lock_check(spinlock_t *lock);
|
||||
|
||||
bool spin_trylock(spinlock_t *lock);
|
||||
int spin_trylock(spinlock_t *lock);
|
||||
|
||||
void spin_lock(spinlock_t *lock);
|
||||
|
||||
|
||||
@@ -37,12 +37,47 @@
|
||||
(((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
|
||||
|
||||
/**
|
||||
* sbi_ffs - find first (less-significant) set bit in a long word.
|
||||
* ffs - Find first bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This is defined the same way as
|
||||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
static inline int ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff)) {
|
||||
x >>= 16;
|
||||
r += 16;
|
||||
}
|
||||
if (!(x & 0xff)) {
|
||||
x >>= 8;
|
||||
r += 8;
|
||||
}
|
||||
if (!(x & 0xf)) {
|
||||
x >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (!(x & 3)) {
|
||||
x >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (!(x & 1))
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ffs - find first bit in word.
|
||||
* @word: The word to search
|
||||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline int sbi_ffs(unsigned long word)
|
||||
static inline int __ffs(unsigned long word)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
@@ -74,20 +109,55 @@ static inline int sbi_ffs(unsigned long word)
|
||||
}
|
||||
|
||||
/*
|
||||
* sbi_ffz - find first zero in word.
|
||||
* ffz - find first zero in word.
|
||||
* @word: The word to search
|
||||
*
|
||||
* Undefined if no zero exists, so code should check against ~0UL first.
|
||||
*/
|
||||
#define sbi_ffz(x) sbi_ffs(~(x))
|
||||
#define ffz(x) __ffs(~(x))
|
||||
|
||||
/**
|
||||
* sbi_fls - find last (most-significant) set bit in a long word
|
||||
* fls - find last (most-significant) bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
|
||||
static inline int fls(int x)
|
||||
{
|
||||
int r = 32;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff0000u)) {
|
||||
x <<= 16;
|
||||
r -= 16;
|
||||
}
|
||||
if (!(x & 0xff000000u)) {
|
||||
x <<= 8;
|
||||
r -= 8;
|
||||
}
|
||||
if (!(x & 0xf0000000u)) {
|
||||
x <<= 4;
|
||||
r -= 4;
|
||||
}
|
||||
if (!(x & 0xc0000000u)) {
|
||||
x <<= 2;
|
||||
r -= 2;
|
||||
}
|
||||
if (!(x & 0x80000000u))
|
||||
r -= 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* __fls - find last (most-significant) set bit in a long word
|
||||
* @word: the word to search
|
||||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long sbi_fls(unsigned long word)
|
||||
static inline unsigned long __fls(unsigned long word)
|
||||
{
|
||||
int num = BITS_PER_LONG - 1;
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ int __printf(1, 2) sbi_printf(const char *format, ...);
|
||||
|
||||
int __printf(1, 2) sbi_dprintf(const char *format, ...);
|
||||
|
||||
void __printf(1, 2) __attribute__((noreturn)) sbi_panic(const char *format, ...);
|
||||
|
||||
const struct sbi_console_device *sbi_console_get_device(void);
|
||||
|
||||
void sbi_console_set_device(const struct sbi_console_device *dev);
|
||||
@@ -53,9 +51,4 @@ struct sbi_scratch;
|
||||
|
||||
int sbi_console_init(struct sbi_scratch *scratch);
|
||||
|
||||
#define SBI_ASSERT(cond, args) do { \
|
||||
if (unlikely(!(cond))) \
|
||||
sbi_panic args; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -78,6 +78,8 @@ struct sbi_domain {
|
||||
unsigned long next_mode;
|
||||
/** Is domain allowed to reset the system */
|
||||
bool system_reset_allowed;
|
||||
/** Is domain allowed to suspend the system */
|
||||
bool system_suspend_allowed;
|
||||
};
|
||||
|
||||
/** The root domain instance */
|
||||
@@ -174,9 +176,7 @@ int sbi_domain_register(struct sbi_domain *dom,
|
||||
* Add a memory region to the root domain
|
||||
* @param reg pointer to the memory region to be added
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return SBI_EALREADY if memory region conflicts with existing
|
||||
* @return SBI_EINVAL otherwise
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg);
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
#define SBI_ECALL_VERSION_MAJOR 1
|
||||
#define SBI_ECALL_VERSION_MINOR 0
|
||||
#define SBI_ECALL_VERSION_MAJOR 0
|
||||
#define SBI_ECALL_VERSION_MINOR 3
|
||||
#define SBI_OPENSBI_IMPID 1
|
||||
|
||||
struct sbi_trap_regs;
|
||||
@@ -39,7 +39,7 @@ extern struct sbi_ecall_extension ecall_ipi;
|
||||
extern struct sbi_ecall_extension ecall_vendor;
|
||||
extern struct sbi_ecall_extension ecall_hsm;
|
||||
extern struct sbi_ecall_extension ecall_srst;
|
||||
extern struct sbi_ecall_extension ecall_pmu;
|
||||
extern struct sbi_ecall_extension ecall_susp;
|
||||
|
||||
u16 sbi_ecall_version_major(void);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#define SBI_EXT_RFENCE 0x52464E43
|
||||
#define SBI_EXT_HSM 0x48534D
|
||||
#define SBI_EXT_SRST 0x53525354
|
||||
#define SBI_EXT_PMU 0x504D55
|
||||
#define SBI_EXT_SUSP 0x53555350
|
||||
|
||||
/* SBI function IDs for BASE extension*/
|
||||
#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
|
||||
@@ -38,6 +38,9 @@
|
||||
#define SBI_EXT_BASE_GET_MVENDORID 0x4
|
||||
#define SBI_EXT_BASE_GET_MARCHID 0x5
|
||||
#define SBI_EXT_BASE_GET_MIMPID 0x6
|
||||
#define SBI_EXT_BASE_RESET_C906L 0x7
|
||||
#define SBI_EXT_BASE_RST_C906L 0x8
|
||||
#define SBI_EXT_BASE_UNRST_C906L 0x9
|
||||
|
||||
/* SBI function IDs for TIME extension*/
|
||||
#define SBI_EXT_TIME_SET_TIMER 0x0
|
||||
@@ -92,139 +95,6 @@
|
||||
#define SBI_SRST_RESET_REASON_NONE 0x0
|
||||
#define SBI_SRST_RESET_REASON_SYSFAIL 0x1
|
||||
|
||||
/* SBI function IDs for PMU extension */
|
||||
#define SBI_EXT_PMU_NUM_COUNTERS 0x0
|
||||
#define SBI_EXT_PMU_COUNTER_GET_INFO 0x1
|
||||
#define SBI_EXT_PMU_COUNTER_CFG_MATCH 0x2
|
||||
#define SBI_EXT_PMU_COUNTER_START 0x3
|
||||
#define SBI_EXT_PMU_COUNTER_STOP 0x4
|
||||
#define SBI_EXT_PMU_COUNTER_FW_READ 0x5
|
||||
|
||||
/** General pmu event codes specified in SBI PMU extension */
|
||||
enum sbi_pmu_hw_generic_events_t {
|
||||
SBI_PMU_HW_NO_EVENT = 0,
|
||||
SBI_PMU_HW_CPU_CYCLES = 1,
|
||||
SBI_PMU_HW_INSTRUCTIONS = 2,
|
||||
SBI_PMU_HW_CACHE_REFERENCES = 3,
|
||||
SBI_PMU_HW_CACHE_MISSES = 4,
|
||||
SBI_PMU_HW_BRANCH_INSTRUCTIONS = 5,
|
||||
SBI_PMU_HW_BRANCH_MISSES = 6,
|
||||
SBI_PMU_HW_BUS_CYCLES = 7,
|
||||
SBI_PMU_HW_STALLED_CYCLES_FRONTEND = 8,
|
||||
SBI_PMU_HW_STALLED_CYCLES_BACKEND = 9,
|
||||
SBI_PMU_HW_REF_CPU_CYCLES = 10,
|
||||
|
||||
SBI_PMU_HW_GENERAL_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* Generalized hardware cache events:
|
||||
*
|
||||
* { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
|
||||
* { read, write, prefetch } x
|
||||
* { accesses, misses }
|
||||
*/
|
||||
enum sbi_pmu_hw_cache_id {
|
||||
SBI_PMU_HW_CACHE_L1D = 0,
|
||||
SBI_PMU_HW_CACHE_L1I = 1,
|
||||
SBI_PMU_HW_CACHE_LL = 2,
|
||||
SBI_PMU_HW_CACHE_DTLB = 3,
|
||||
SBI_PMU_HW_CACHE_ITLB = 4,
|
||||
SBI_PMU_HW_CACHE_BPU = 5,
|
||||
SBI_PMU_HW_CACHE_NODE = 6,
|
||||
|
||||
SBI_PMU_HW_CACHE_MAX,
|
||||
};
|
||||
|
||||
enum sbi_pmu_hw_cache_op_id {
|
||||
SBI_PMU_HW_CACHE_OP_READ = 0,
|
||||
SBI_PMU_HW_CACHE_OP_WRITE = 1,
|
||||
SBI_PMU_HW_CACHE_OP_PREFETCH = 2,
|
||||
|
||||
SBI_PMU_HW_CACHE_OP_MAX,
|
||||
};
|
||||
|
||||
enum sbi_pmu_hw_cache_op_result_id {
|
||||
SBI_PMU_HW_CACHE_RESULT_ACCESS = 0,
|
||||
SBI_PMU_HW_CACHE_RESULT_MISS = 1,
|
||||
|
||||
SBI_PMU_HW_CACHE_RESULT_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* Special "firmware" events provided by the OpenSBI, even if the hardware
|
||||
* does not support performance events. These events are encoded as a raw
|
||||
* event type in Linux kernel perf framework.
|
||||
*/
|
||||
enum sbi_pmu_fw_event_code_id {
|
||||
SBI_PMU_FW_MISALIGNED_LOAD = 0,
|
||||
SBI_PMU_FW_MISALIGNED_STORE = 1,
|
||||
SBI_PMU_FW_ACCESS_LOAD = 2,
|
||||
SBI_PMU_FW_ACCESS_STORE = 3,
|
||||
SBI_PMU_FW_ILLEGAL_INSN = 4,
|
||||
SBI_PMU_FW_SET_TIMER = 5,
|
||||
SBI_PMU_FW_IPI_SENT = 6,
|
||||
SBI_PMU_FW_IPI_RECVD = 7,
|
||||
SBI_PMU_FW_FENCE_I_SENT = 8,
|
||||
SBI_PMU_FW_FENCE_I_RECVD = 9,
|
||||
SBI_PMU_FW_SFENCE_VMA_SENT = 10,
|
||||
SBI_PMU_FW_SFENCE_VMA_RCVD = 11,
|
||||
SBI_PMU_FW_SFENCE_VMA_ASID_SENT = 12,
|
||||
SBI_PMU_FW_SFENCE_VMA_ASID_RCVD = 13,
|
||||
|
||||
SBI_PMU_FW_HFENCE_GVMA_SENT = 14,
|
||||
SBI_PMU_FW_HFENCE_GVMA_RCVD = 15,
|
||||
SBI_PMU_FW_HFENCE_GVMA_VMID_SENT = 16,
|
||||
SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD = 17,
|
||||
|
||||
SBI_PMU_FW_HFENCE_VVMA_SENT = 18,
|
||||
SBI_PMU_FW_HFENCE_VVMA_RCVD = 19,
|
||||
SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20,
|
||||
SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21,
|
||||
SBI_PMU_FW_MAX,
|
||||
};
|
||||
|
||||
/** SBI PMU event idx type */
|
||||
enum sbi_pmu_event_type_id {
|
||||
SBI_PMU_EVENT_TYPE_HW = 0x0,
|
||||
SBI_PMU_EVENT_TYPE_HW_CACHE = 0x1,
|
||||
SBI_PMU_EVENT_TYPE_HW_RAW = 0x2,
|
||||
SBI_PMU_EVENT_TYPE_FW = 0xf,
|
||||
SBI_PMU_EVENT_TYPE_MAX,
|
||||
};
|
||||
|
||||
/** SBI PMU counter type */
|
||||
enum sbi_pmu_ctr_type {
|
||||
SBI_PMU_CTR_TYPE_HW = 0,
|
||||
SBI_PMU_CTR_TYPE_FW,
|
||||
};
|
||||
|
||||
/* Helper macros to decode event idx */
|
||||
#define SBI_PMU_EVENT_IDX_OFFSET 20
|
||||
#define SBI_PMU_EVENT_IDX_MASK 0xFFFFF
|
||||
#define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF
|
||||
#define SBI_PMU_EVENT_IDX_TYPE_MASK 0xF0000
|
||||
#define SBI_PMU_EVENT_RAW_IDX 0x20000
|
||||
|
||||
#define SBI_PMU_EVENT_IDX_INVALID 0xFFFFFFFF
|
||||
|
||||
/* Flags defined for config matching function */
|
||||
#define SBI_PMU_CFG_FLAG_SKIP_MATCH (1 << 0)
|
||||
#define SBI_PMU_CFG_FLAG_CLEAR_VALUE (1 << 1)
|
||||
#define SBI_PMU_CFG_FLAG_AUTO_START (1 << 2)
|
||||
#define SBI_PMU_CFG_FLAG_SET_VUINH (1 << 3)
|
||||
#define SBI_PMU_CFG_FLAG_SET_VSINH (1 << 4)
|
||||
#define SBI_PMU_CFG_FLAG_SET_UINH (1 << 5)
|
||||
#define SBI_PMU_CFG_FLAG_SET_SINH (1 << 6)
|
||||
#define SBI_PMU_CFG_FLAG_SET_MINH (1 << 7)
|
||||
|
||||
/* Flags defined for counter start function */
|
||||
#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
|
||||
|
||||
/* Flags defined for counter stop function */
|
||||
#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
|
||||
|
||||
/* SBI base specification related macros */
|
||||
#define SBI_SPEC_VERSION_MAJOR_OFFSET 24
|
||||
#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
|
||||
#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
|
||||
@@ -241,10 +111,15 @@ enum sbi_pmu_ctr_type {
|
||||
#define SBI_ERR_DENIED -4
|
||||
#define SBI_ERR_INVALID_ADDRESS -5
|
||||
#define SBI_ERR_ALREADY_AVAILABLE -6
|
||||
#define SBI_ERR_ALREADY_STARTED -7
|
||||
#define SBI_ERR_ALREADY_STOPPED -8
|
||||
|
||||
#define SBI_LAST_ERR SBI_ERR_ALREADY_STOPPED
|
||||
/* SBI function IDs for SUSP extension */
|
||||
#define SBI_EXT_SUSP_SUSPEND 0x0
|
||||
|
||||
#define SBI_SUSP_SLEEP_TYPE_SUSPEND 0x0
|
||||
#define SBI_SUSP_SLEEP_TYPE_LAST SBI_SUSP_SLEEP_TYPE_SUSPEND
|
||||
#define SBI_SUSP_PLATFORM_SLEEP_START 0x80000000
|
||||
|
||||
#define SBI_LAST_ERR SBI_ERR_ALREADY_AVAILABLE
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#define SBI_EDENIED SBI_ERR_DENIED
|
||||
#define SBI_EINVALID_ADDR SBI_ERR_INVALID_ADDRESS
|
||||
#define SBI_EALREADY SBI_ERR_ALREADY_AVAILABLE
|
||||
#define SBI_EALREADY_STARTED SBI_ERR_ALREADY_STARTED
|
||||
#define SBI_EALREADY_STOPPED SBI_ERR_ALREADY_STOPPED
|
||||
|
||||
#define SBI_ENODEV -1000
|
||||
#define SBI_ENOSYS -1001
|
||||
|
||||
@@ -33,8 +33,8 @@ int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data);
|
||||
int sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data);
|
||||
void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries,
|
||||
u16 entry_size);
|
||||
int sbi_fifo_is_empty(struct sbi_fifo *fifo);
|
||||
int sbi_fifo_is_full(struct sbi_fifo *fifo);
|
||||
bool sbi_fifo_is_empty(struct sbi_fifo *fifo);
|
||||
bool sbi_fifo_is_full(struct sbi_fifo *fifo);
|
||||
int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
|
||||
int (*fptr)(void *in, void *data));
|
||||
u16 sbi_fifo_avail(struct sbi_fifo *fifo);
|
||||
|
||||
@@ -12,33 +12,17 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
/** Possible privileged specification versions of a hart */
|
||||
enum sbi_hart_priv_versions {
|
||||
/** Unknown privileged specification */
|
||||
SBI_HART_PRIV_VER_UNKNOWN = 0,
|
||||
/** Privileged specification v1.10 */
|
||||
SBI_HART_PRIV_VER_1_10 = 1,
|
||||
/** Privileged specification v1.11 */
|
||||
SBI_HART_PRIV_VER_1_11 = 2,
|
||||
/** Privileged specification v1.12 */
|
||||
SBI_HART_PRIV_VER_1_12 = 3,
|
||||
};
|
||||
/** Possible feature flags of a hart */
|
||||
enum sbi_hart_features {
|
||||
/** Hart has S-mode counter enable */
|
||||
SBI_HART_HAS_SCOUNTEREN = (1 << 0),
|
||||
/** Hart has M-mode counter enable */
|
||||
SBI_HART_HAS_MCOUNTEREN = (1 << 1),
|
||||
/** HART has timer csr implementation in hardware */
|
||||
SBI_HART_HAS_TIME = (1 << 2),
|
||||
|
||||
/** Possible ISA extensions of a hart */
|
||||
enum sbi_hart_extensions {
|
||||
/** Hart has Sscofpmt extension */
|
||||
SBI_HART_EXT_SSCOFPMF = 0,
|
||||
/** HART has HW time CSR (extension name not available) */
|
||||
SBI_HART_EXT_TIME,
|
||||
/** HART has AIA CSRs (extension name not available) */
|
||||
SBI_HART_EXT_AIA,
|
||||
/** HART has Smstateen CSR **/
|
||||
SBI_HART_EXT_SMSTATEEN,
|
||||
/** HART has Sstc extension */
|
||||
SBI_HART_EXT_SSTC,
|
||||
|
||||
/** Maximum index of Hart extension */
|
||||
SBI_HART_EXT_MAX,
|
||||
/** Last index of Hart features*/
|
||||
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_TIME,
|
||||
};
|
||||
|
||||
struct sbi_scratch;
|
||||
@@ -58,18 +42,10 @@ void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
|
||||
unsigned int sbi_hart_pmp_count(struct sbi_scratch *scratch);
|
||||
unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch);
|
||||
unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch);
|
||||
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch);
|
||||
int sbi_hart_pmp_configure(struct sbi_scratch *scratch);
|
||||
int sbi_hart_priv_version(struct sbi_scratch *scratch);
|
||||
void sbi_hart_get_priv_version_str(struct sbi_scratch *scratch,
|
||||
char *version_str, int nvstr);
|
||||
void sbi_hart_update_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable);
|
||||
bool sbi_hart_has_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext);
|
||||
void sbi_hart_get_extensions_str(struct sbi_scratch *scratch,
|
||||
char *extension_str, int nestr);
|
||||
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature);
|
||||
void sbi_hart_get_features_str(struct sbi_scratch *scratch,
|
||||
char *features_str, int nfstr);
|
||||
|
||||
void __attribute__((noreturn)) sbi_hart_hang(void);
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@
|
||||
#define __SBI_FENCE_H__
|
||||
|
||||
/** Invalidate Stage2 TLBs for given VMID and guest physical address */
|
||||
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa_divby_4,
|
||||
unsigned long vmid);
|
||||
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa, unsigned long vmid);
|
||||
|
||||
/** Invalidate Stage2 TLBs for given VMID */
|
||||
void __sbi_hfence_gvma_vmid(unsigned long vmid);
|
||||
|
||||
/** Invalidate Stage2 TLBs for given guest physical address */
|
||||
void __sbi_hfence_gvma_gpa(unsigned long gpa_divby_4);
|
||||
void __sbi_hfence_gvma_gpa(unsigned long gpa);
|
||||
|
||||
/** Invalidate all possible Stage2 TLBs */
|
||||
void __sbi_hfence_gvma_all(void);
|
||||
|
||||
@@ -34,17 +34,9 @@ struct sbi_hsm_device {
|
||||
* the hart resumes normal execution.
|
||||
*
|
||||
* For successful non-retentive suspend, the hart will resume from
|
||||
* the warm boot entry point.
|
||||
* specified resume address
|
||||
*/
|
||||
int (*hart_suspend)(u32 suspend_type);
|
||||
|
||||
/**
|
||||
* Perform platform-specific actions to resume from a suspended state.
|
||||
*
|
||||
* This includes restoring any platform state that was lost during
|
||||
* non-retentive suspend.
|
||||
*/
|
||||
void (*hart_resume)(void);
|
||||
int (*hart_suspend)(u32 suspend_type, ulong raddr);
|
||||
};
|
||||
|
||||
struct sbi_domain;
|
||||
@@ -65,9 +57,13 @@ void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch);
|
||||
void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch);
|
||||
int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
ulong raddr, ulong rmode, ulong priv);
|
||||
int sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate,
|
||||
long newstate);
|
||||
int __sbi_hsm_hart_get_state(u32 hartid);
|
||||
int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid);
|
||||
int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom,
|
||||
ulong hbase, ulong *out_hmask);
|
||||
void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch);
|
||||
void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <apatel@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#ifndef __SBI_IRQCHIP_H__
|
||||
#define __SBI_IRQCHIP_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
struct sbi_scratch;
|
||||
struct sbi_trap_regs;
|
||||
|
||||
/**
|
||||
* Set external interrupt handling function
|
||||
*
|
||||
* This function is called by OpenSBI platform code to set a handler for
|
||||
* external interrupts
|
||||
*
|
||||
* @param fn function pointer for handling external irqs
|
||||
*/
|
||||
void sbi_irqchip_set_irqfn(int (*fn)(struct sbi_trap_regs *regs));
|
||||
|
||||
/**
|
||||
* Process external interrupts
|
||||
*
|
||||
* This function is called by sbi_trap_handler() to handle external
|
||||
* interrupts.
|
||||
*
|
||||
* @param regs pointer for trap registers
|
||||
*/
|
||||
int sbi_irqchip_process(struct sbi_trap_regs *regs);
|
||||
|
||||
/** Initialize interrupt controllers */
|
||||
int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
/** Exit interrupt controllers */
|
||||
void sbi_irqchip_exit(struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
||||
@@ -43,17 +43,6 @@ static inline void __sbi_list_add(struct sbi_dlist *new,
|
||||
next->prev = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the list is empty or not.
|
||||
* @param head List head
|
||||
*
|
||||
* Retruns TRUE if list is empty, FALSE otherwise.
|
||||
*/
|
||||
static inline bool sbi_list_empty(struct sbi_dlist *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the new node after the given head.
|
||||
* @param new New node that needs to be added to list.
|
||||
|
||||
@@ -64,9 +64,6 @@ enum sbi_platform_features {
|
||||
|
||||
/** Platform functions */
|
||||
struct sbi_platform_operations {
|
||||
/* Platform nascent initialization */
|
||||
int (*nascent_init)(void);
|
||||
|
||||
/** Platform early initialization */
|
||||
int (*early_init)(bool cold_boot);
|
||||
/** Platform final initialization */
|
||||
@@ -89,18 +86,9 @@ struct sbi_platform_operations {
|
||||
*/
|
||||
int (*misa_get_xlen)(void);
|
||||
|
||||
/** Initialize (or populate) HART extensions for the platform */
|
||||
int (*extensions_init)(void);
|
||||
|
||||
/** Initialize (or populate) domains for the platform */
|
||||
int (*domains_init)(void);
|
||||
|
||||
/** Initialize hw performance counters */
|
||||
int (*pmu_init)(void);
|
||||
|
||||
/** Get platform specific mhpmevent value */
|
||||
uint64_t (*pmu_xlate_to_mhpmevent)(uint32_t event_idx, uint64_t data);
|
||||
|
||||
/** Initialize the platform console */
|
||||
int (*console_init)(void);
|
||||
|
||||
@@ -178,56 +166,6 @@ struct sbi_platform {
|
||||
const u32 *hart_index2id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent modification of struct sbi_platform from affecting
|
||||
* SBI_PLATFORM_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, opensbi_version)
|
||||
== SBI_PLATFORM_OPENSBI_VERSION_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_OPENSBI_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, platform_version)
|
||||
== SBI_PLATFORM_VERSION_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, name)
|
||||
== SBI_PLATFORM_NAME_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_NAME_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, features)
|
||||
== SBI_PLATFORM_FEATURES_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_FEATURES_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_count)
|
||||
== SBI_PLATFORM_HART_COUNT_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_COUNT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_stack_size)
|
||||
== SBI_PLATFORM_HART_STACK_SIZE_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_STACK_SIZE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, platform_ops_addr)
|
||||
== SBI_PLATFORM_OPS_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_OPS_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, firmware_context)
|
||||
== SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_index2id)
|
||||
== SBI_PLATFORM_HART_INDEX2ID_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_INDEX2ID_OFFSET");
|
||||
|
||||
/** Get pointer to sbi_platform for sbi_scratch pointer */
|
||||
#define sbi_platform_ptr(__s) \
|
||||
((const struct sbi_platform *)((__s)->platform_addr))
|
||||
@@ -355,23 +293,6 @@ static inline bool sbi_platform_hart_invalid(const struct sbi_platform *plat,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nascent (very early) initialization for current HART
|
||||
*
|
||||
* NOTE: This function can be used to do very early initialization of
|
||||
* platform specific per-HART CSRs and devices.
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_nascent_init(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->nascent_init)
|
||||
return sbi_platform_ops(plat)->nascent_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initialization for current HART
|
||||
*
|
||||
@@ -456,21 +377,6 @@ static inline int sbi_platform_misa_xlen(const struct sbi_platform *plat)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (or populate) HART extensions for the platform
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_extensions_init(
|
||||
const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->extensions_init)
|
||||
return sbi_platform_ops(plat)->extensions_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (or populate) domains for the platform
|
||||
*
|
||||
@@ -485,39 +391,6 @@ static inline int sbi_platform_domains_init(const struct sbi_platform *plat)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup hw PMU events for the platform
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_pmu_init(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->pmu_init)
|
||||
return sbi_platform_ops(plat)->pmu_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to be written in mhpmeventx for event_idx
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param event_idx ID of the PMU event
|
||||
* @param data Additional configuration data passed from supervisor software
|
||||
*
|
||||
* @return expected value by the platform or 0 if platform doesn't know about
|
||||
* the event
|
||||
*/
|
||||
static inline uint64_t sbi_platform_pmu_xlate_to_mhpmevent(const struct sbi_platform *plat,
|
||||
uint32_t event_idx, uint64_t data)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->pmu_xlate_to_mhpmevent)
|
||||
return sbi_platform_ops(plat)->pmu_xlate_to_mhpmevent(event_idx,
|
||||
data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the platform console
|
||||
*
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Atish Patra <atish.patra@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __SBI_PMU_H__
|
||||
#define __SBI_PMU_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
|
||||
/* Event related macros */
|
||||
/* Maximum number of hardware events that can mapped by OpenSBI */
|
||||
#define SBI_PMU_HW_EVENT_MAX 256
|
||||
|
||||
/* Maximum number of firmware events that can mapped by OpenSBI */
|
||||
#define SBI_PMU_FW_EVENT_MAX 32
|
||||
|
||||
/* Counter related macros */
|
||||
#define SBI_PMU_FW_CTR_MAX 16
|
||||
#define SBI_PMU_HW_CTR_MAX 32
|
||||
#define SBI_PMU_CTR_MAX (SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX)
|
||||
#define SBI_PMU_FIXED_CTR_MASK 0x07
|
||||
|
||||
/** Initialize PMU */
|
||||
int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
/** Reset PMU during hart exit */
|
||||
void sbi_pmu_exit(struct sbi_scratch *scratch);
|
||||
|
||||
/**
|
||||
* Add the hardware event to counter mapping information. This should be called
|
||||
* from the platform code to update the mapping table.
|
||||
* @param eidx_start Start of the event idx range for supported counters
|
||||
* @param eidx_end End of the event idx range for supported counters
|
||||
* @param cmap A bitmap representing counters supporting the event range
|
||||
* @return 0 on success, error otherwise.
|
||||
*/
|
||||
int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32 eidx_end, u32 cmap);
|
||||
|
||||
/**
|
||||
* Add the raw hardware event selector and supported counter information. This
|
||||
* should be called from the platform code to update the mapping table.
|
||||
* @param info a pointer to the hardware event info
|
||||
* @return 0 on success, error otherwise.
|
||||
*/
|
||||
|
||||
int sbi_pmu_add_raw_event_counter_map(uint64_t select, uint64_t select_mask, u32 cmap);
|
||||
|
||||
int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval);
|
||||
|
||||
int sbi_pmu_ctr_stop(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
unsigned long flag);
|
||||
|
||||
int sbi_pmu_ctr_start(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
unsigned long flags, uint64_t ival);
|
||||
|
||||
int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info);
|
||||
|
||||
unsigned long sbi_pmu_num_ctr(void);
|
||||
|
||||
int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
unsigned long flags, unsigned long event_idx,
|
||||
uint64_t event_data);
|
||||
|
||||
int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id);
|
||||
|
||||
#endif
|
||||
@@ -73,66 +73,6 @@ struct sbi_scratch {
|
||||
unsigned long options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent modification of struct sbi_scratch from affecting
|
||||
* SBI_SCRATCH_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, fw_start)
|
||||
== SBI_SCRATCH_FW_START_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_FW_START_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, fw_size)
|
||||
== SBI_SCRATCH_FW_SIZE_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_FW_SIZE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_arg1)
|
||||
== SBI_SCRATCH_NEXT_ARG1_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_ARG1_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_addr)
|
||||
== SBI_SCRATCH_NEXT_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_mode)
|
||||
== SBI_SCRATCH_NEXT_MODE_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_MODE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, warmboot_addr)
|
||||
== SBI_SCRATCH_WARMBOOT_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_WARMBOOT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, platform_addr)
|
||||
== SBI_SCRATCH_PLATFORM_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_PLATFORM_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, hartid_to_scratch)
|
||||
== SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, trap_exit)
|
||||
== SBI_SCRATCH_TRAP_EXIT_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_TRAP_EXIT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, tmp0)
|
||||
== SBI_SCRATCH_TMP0_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_TMP0_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, options)
|
||||
== SBI_SCRATCH_OPTIONS_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_OPTIONS_OFFSET");
|
||||
|
||||
/** Possible options for OpenSBI library */
|
||||
enum sbi_scratch_options {
|
||||
/** Disable prints during boot */
|
||||
@@ -158,17 +98,17 @@ int sbi_scratch_init(struct sbi_scratch *scratch);
|
||||
* @return zero on failure and non-zero (>= SBI_SCRATCH_EXTRA_SPACE_OFFSET)
|
||||
* on success
|
||||
*/
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size);
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner);
|
||||
|
||||
/** Free-up extra space in sbi_scratch */
|
||||
void sbi_scratch_free_offset(unsigned long offset);
|
||||
|
||||
/** Get pointer from offset in sbi_scratch */
|
||||
#define sbi_scratch_offset_ptr(scratch, offset) (void *)((char *)(scratch) + (offset))
|
||||
#define sbi_scratch_offset_ptr(scratch, offset) ((void *)scratch + (offset))
|
||||
|
||||
/** Get pointer from offset in sbi_scratch for current HART */
|
||||
#define sbi_scratch_thishart_offset_ptr(offset) \
|
||||
(void *)((char *)sbi_scratch_thishart_ptr() + (offset))
|
||||
((void *)sbi_scratch_thishart_ptr() + (offset))
|
||||
|
||||
/** HART id to scratch table */
|
||||
extern struct sbi_scratch *hartid_to_scratch_table[];
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define __SBI_SYSTEM_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
/** System reset hardware device */
|
||||
struct sbi_system_reset_device {
|
||||
@@ -23,24 +22,40 @@ struct sbi_system_reset_device {
|
||||
|
||||
/** Reset the system */
|
||||
void (*system_reset)(u32 reset_type, u32 reset_reason);
|
||||
|
||||
/** List */
|
||||
struct sbi_dlist node;
|
||||
};
|
||||
|
||||
static inline struct sbi_system_reset_device *to_system_reset_device(
|
||||
struct sbi_dlist *node)
|
||||
{
|
||||
return container_of(node, struct sbi_system_reset_device, node);
|
||||
}
|
||||
const struct sbi_system_reset_device *sbi_system_reset_get_device(void);
|
||||
|
||||
const struct sbi_system_reset_device *sbi_system_reset_get_device(
|
||||
u32 reset_type, u32 reset_reason);
|
||||
|
||||
void sbi_system_reset_add_device(struct sbi_system_reset_device *dev);
|
||||
void sbi_system_reset_set_device(const struct sbi_system_reset_device *dev);
|
||||
|
||||
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason);
|
||||
|
||||
void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason);
|
||||
|
||||
/** System suspend device */
|
||||
struct sbi_system_suspend_device {
|
||||
/** Name of the system suspend device */
|
||||
char name[32];
|
||||
|
||||
/* Check whether sleep type is supported by the device */
|
||||
int (*system_suspend_check)(u32 sleep_type);
|
||||
|
||||
/**
|
||||
* Suspend the system
|
||||
*
|
||||
* @sleep_type: The sleep type identifier passed to the SBI call.
|
||||
* @mmode_resume_addr:
|
||||
* This is the same as sbi_scratch.warmboot_addr. Some platforms
|
||||
* may not be able to return from system_suspend(), so they will
|
||||
* jump directly to this address instead. Platforms which can
|
||||
* return from system_suspend() may ignore this parameter.
|
||||
*/
|
||||
int (*system_suspend)(u32 sleep_type, unsigned long mmode_resume_addr);
|
||||
};
|
||||
|
||||
const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void);
|
||||
void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev);
|
||||
bool sbi_system_suspend_supported(u32 sleep_type);
|
||||
int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,9 +17,6 @@ struct sbi_timer_device {
|
||||
/** Name of the timer operations */
|
||||
char name[32];
|
||||
|
||||
/** Frequency of timer in HZ */
|
||||
unsigned long timer_freq;
|
||||
|
||||
/** Get free-running timer value */
|
||||
u64 (*timer_value)(void);
|
||||
|
||||
@@ -32,22 +29,6 @@ struct sbi_timer_device {
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
/** Generic delay loop of desired granularity */
|
||||
void sbi_timer_delay_loop(ulong units, u64 unit_freq,
|
||||
void (*delay_fn)(void *), void *opaque);
|
||||
|
||||
/** Provide delay in terms of milliseconds */
|
||||
static inline void sbi_timer_mdelay(ulong msecs)
|
||||
{
|
||||
sbi_timer_delay_loop(msecs, 1000, NULL, NULL);
|
||||
}
|
||||
|
||||
/** Provide delay in terms of microseconds */
|
||||
static inline void sbi_timer_udelay(ulong usecs)
|
||||
{
|
||||
sbi_timer_delay_loop(usecs, 1000000, NULL, NULL);
|
||||
}
|
||||
|
||||
/** Get timer value for current HART */
|
||||
u64 sbi_timer_value(void);
|
||||
|
||||
|
||||
@@ -68,14 +68,8 @@ typedef unsigned long physical_size_t;
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(...) 0
|
||||
#endif
|
||||
|
||||
#undef offsetof
|
||||
#if __has_builtin(__builtin_offsetof)
|
||||
#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE,MEMBER)
|
||||
#elif defined(__compiler_offsetof)
|
||||
#ifdef __compiler_offsetof
|
||||
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER)
|
||||
#else
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#ifndef __SBI_VERSION_H__
|
||||
#define __SBI_VERSION_H__
|
||||
|
||||
#define OPENSBI_VERSION_MAJOR 1
|
||||
#define OPENSBI_VERSION_MINOR 1
|
||||
#define OPENSBI_VERSION_MAJOR 0
|
||||
#define OPENSBI_VERSION_MINOR 9
|
||||
|
||||
/**
|
||||
* OpenSBI 32-bit version with:
|
||||
|
||||
@@ -21,30 +21,6 @@
|
||||
*/
|
||||
void fdt_cpu_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the APLIC nodes in the device tree
|
||||
*
|
||||
* This routine disables APLIC nodes which are not accessible to the next
|
||||
* booting stage based on currently assigned domain.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
* @param fdt: device tree blob
|
||||
*/
|
||||
void fdt_aplic_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the IMSIC nodes in the device tree
|
||||
*
|
||||
* This routine disables IMSIC nodes which are not accessible to the next
|
||||
* booting stage based on currently assigned domain.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
* @param fdt: device tree blob
|
||||
*/
|
||||
void fdt_imsic_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the PLIC node in the device tree
|
||||
*
|
||||
@@ -88,9 +64,8 @@ int fdt_reserved_memory_nomap_fixup(void *fdt);
|
||||
* General device tree fix-up
|
||||
*
|
||||
* This routine do all required device tree fix-ups for a typical platform.
|
||||
* It fixes up the PLIC node, IMSIC nodes, APLIC nodes, and the reserved
|
||||
* memory node in the device tree by calling the corresponding helper
|
||||
* routines to accomplish the task.
|
||||
* It fixes up the PLIC node and the reserved memory node in the device tree
|
||||
* by calling the corresponding helper routines to accomplish the task.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
|
||||
@@ -11,18 +11,10 @@
|
||||
#define __FDT_HELPER_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
|
||||
struct fdt_match {
|
||||
const char *compatible;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
#define FDT_MAX_PHANDLE_ARGS 16
|
||||
struct fdt_phandle_args {
|
||||
int node_offset;
|
||||
int args_count;
|
||||
u32 args[FDT_MAX_PHANDLE_ARGS];
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct platform_uart_data {
|
||||
@@ -31,7 +23,6 @@ struct platform_uart_data {
|
||||
unsigned long baud;
|
||||
unsigned long reg_shift;
|
||||
unsigned long reg_io_width;
|
||||
unsigned long reg_offset;
|
||||
};
|
||||
|
||||
const struct fdt_match *fdt_match_node(void *fdt, int nodeoff,
|
||||
@@ -41,23 +32,12 @@ int fdt_find_match(void *fdt, int startoff,
|
||||
const struct fdt_match *match_table,
|
||||
const struct fdt_match **out_match);
|
||||
|
||||
int fdt_parse_phandle_with_args(void *fdt, int nodeoff,
|
||||
const char *prop, const char *cells_prop,
|
||||
int index, struct fdt_phandle_args *out_args);
|
||||
|
||||
int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
uint64_t *addr, uint64_t *size);
|
||||
|
||||
bool fdt_node_is_enabled(void *fdt, int nodeoff);
|
||||
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
|
||||
unsigned long *size);
|
||||
|
||||
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid);
|
||||
|
||||
int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid);
|
||||
|
||||
int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
|
||||
|
||||
int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart);
|
||||
int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
|
||||
|
||||
int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart);
|
||||
@@ -71,36 +51,18 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
|
||||
const char *compatible);
|
||||
|
||||
int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart);
|
||||
|
||||
struct aplic_data;
|
||||
|
||||
int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic);
|
||||
|
||||
struct imsic_data;
|
||||
|
||||
bool fdt_check_imsic_mlevel(void *fdt);
|
||||
|
||||
int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic);
|
||||
|
||||
struct plic_data;
|
||||
|
||||
int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic);
|
||||
|
||||
int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat);
|
||||
|
||||
int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
unsigned long *out_addr1, unsigned long *out_size1,
|
||||
unsigned long *out_addr2, unsigned long *out_size2,
|
||||
u32 *out_first_hartid, u32 *out_hart_count);
|
||||
struct clint_data;
|
||||
|
||||
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
struct clint_data *clint);
|
||||
|
||||
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
|
||||
const char *compatible);
|
||||
|
||||
static inline void *fdt_get_address(void)
|
||||
{
|
||||
return sbi_scratch_thishart_arg1_ptr();
|
||||
}
|
||||
|
||||
#endif /* __FDT_HELPER_H__ */
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* fdt_pmu.c - Flat Device Tree PMU helper routines
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Atish Patra <atish.patra@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __FDT_PMU_H__
|
||||
#define __FDT_PMU_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
/**
|
||||
* Fix up the PMU node in the device tree
|
||||
*
|
||||
* This routine:
|
||||
* 1. Disables opensbi specific properties from the DT
|
||||
*
|
||||
* It is recommended that platform support call this function in
|
||||
* their final_init() platform operation.
|
||||
*
|
||||
* @param fdt device tree blob
|
||||
*/
|
||||
void fdt_pmu_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Setup PMU data from device tree
|
||||
*
|
||||
* @param fdt device tree blob
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int fdt_pmu_setup(void *fdt);
|
||||
|
||||
/**
|
||||
* Get the mhpmevent select value read from DT for a given event
|
||||
* @param event_idx Event ID of the given event
|
||||
*
|
||||
* @return The select value read from DT or 0 if given index was not found
|
||||
*/
|
||||
uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __FDT_GPIO_H__
|
||||
#define __FDT_GPIO_H__
|
||||
|
||||
#include <sbi_utils/gpio/gpio.h>
|
||||
|
||||
struct fdt_phandle_args;
|
||||
|
||||
/** FDT based GPIO driver */
|
||||
struct fdt_gpio {
|
||||
const struct fdt_match *match_table;
|
||||
int (*xlate)(struct gpio_chip *chip,
|
||||
const struct fdt_phandle_args *pargs,
|
||||
struct gpio_pin *out_pin);
|
||||
int (*init)(void *fdt, int nodeoff, u32 phandle,
|
||||
const struct fdt_match *match);
|
||||
};
|
||||
|
||||
/** Get a GPIO pin using "gpios" DT property of client DT node */
|
||||
int fdt_gpio_pin_get(void *fdt, int nodeoff, int index,
|
||||
struct gpio_pin *out_pin);
|
||||
|
||||
/** Simple xlate function to convert two GPIO FDT cells into GPIO pin */
|
||||
int fdt_gpio_simple_xlate(struct gpio_chip *chip,
|
||||
const struct fdt_phandle_args *pargs,
|
||||
struct gpio_pin *out_pin);
|
||||
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __GPIO_H__
|
||||
#define __GPIO_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
#define GPIO_LINE_DIRECTION_IN 1
|
||||
#define GPIO_LINE_DIRECTION_OUT 0
|
||||
|
||||
/** Representation of a GPIO pin */
|
||||
struct gpio_pin {
|
||||
/** Pointer to the GPIO chip */
|
||||
struct gpio_chip *chip;
|
||||
/** Identification of GPIO pin within GPIO chip */
|
||||
unsigned int offset;
|
||||
/**
|
||||
* Additional configuration flags of the GPIO pin desired
|
||||
* by GPIO clients.
|
||||
*
|
||||
* NOTE: GPIO chip can have custom configuration flags.
|
||||
*/
|
||||
unsigned int flags;
|
||||
#define GPIO_FLAG_ACTIVE_LOW 0x1
|
||||
#define GPIO_FLAG_SINGLE_ENDED 0x2
|
||||
#define GPIO_FLAG_OPEN_DRAIN 0x4
|
||||
#define GPIO_FLAG_TRANSITORY 0x8
|
||||
#define GPIO_FLAG_PULL_UP 0x10
|
||||
#define GPIO_FLAG_PULL_DOWN 0x20
|
||||
};
|
||||
|
||||
/** Representation of a GPIO chip */
|
||||
struct gpio_chip {
|
||||
/** Pointer to GPIO driver owning this GPIO chip */
|
||||
void *driver;
|
||||
/** Uniquie ID of the GPIO chip assigned by the driver */
|
||||
unsigned int id;
|
||||
/** Number of GPIOs supported by the GPIO chip */
|
||||
unsigned int ngpio;
|
||||
/**
|
||||
* Get current direction of GPIO pin
|
||||
*
|
||||
* @return 0=output, 1=input, or negative error
|
||||
*/
|
||||
int (*get_direction)(struct gpio_pin *gp);
|
||||
/**
|
||||
* Set input direction of GPIO pin
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int (*direction_input)(struct gpio_pin *gp);
|
||||
/**
|
||||
* Set output direction of GPIO pin with given output value
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int (*direction_output)(struct gpio_pin *gp, int value);
|
||||
/**
|
||||
* Get current value of GPIO pin
|
||||
*
|
||||
* @return 0=low, 1=high, or negative error
|
||||
*/
|
||||
int (*get)(struct gpio_pin *gp);
|
||||
/** Set output value for GPIO pin */
|
||||
void (*set)(struct gpio_pin *gp, int value);
|
||||
/** List */
|
||||
struct sbi_dlist node;
|
||||
};
|
||||
|
||||
static inline struct gpio_chip *to_gpio_chip(struct sbi_dlist *node)
|
||||
{
|
||||
return container_of(node, struct gpio_chip, node);
|
||||
}
|
||||
|
||||
/** Find a registered GPIO chip */
|
||||
struct gpio_chip *gpio_chip_find(unsigned int id);
|
||||
|
||||
/** Register GPIO chip */
|
||||
int gpio_chip_add(struct gpio_chip *gc);
|
||||
|
||||
/** Un-register GPIO chip */
|
||||
void gpio_chip_remove(struct gpio_chip *gc);
|
||||
|
||||
/** Get current direction of GPIO pin */
|
||||
int gpio_get_direction(struct gpio_pin *gp);
|
||||
|
||||
/** Set input direction of GPIO pin */
|
||||
int gpio_direction_input(struct gpio_pin *gp);
|
||||
|
||||
/** Set output direction of GPIO pin */
|
||||
int gpio_direction_output(struct gpio_pin *gp, int value);
|
||||
|
||||
/** Get current value of GPIO pin */
|
||||
int gpio_get(struct gpio_pin *gp);
|
||||
|
||||
/** Set output value of GPIO pin */
|
||||
int gpio_set(struct gpio_pin *gp, int value);
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 YADRO
|
||||
*
|
||||
* Authors:
|
||||
* Nikita Shubin <n.shubin@yadro.com>
|
||||
*/
|
||||
|
||||
#ifndef __FDT_I2C_H__
|
||||
#define __FDT_I2C_H__
|
||||
|
||||
#include <sbi_utils/i2c/i2c.h>
|
||||
|
||||
/** FDT based I2C adapter driver */
|
||||
struct fdt_i2c_adapter {
|
||||
const struct fdt_match *match_table;
|
||||
int (*init)(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match);
|
||||
};
|
||||
|
||||
/** Get I2C adapter identified by nodeoff */
|
||||
int fdt_i2c_adapter_get(void *fdt, int nodeoff,
|
||||
struct i2c_adapter **out_adapter);
|
||||
|
||||
#endif
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 YADRO
|
||||
*
|
||||
* Authors:
|
||||
* Nikita Shubin <n.shubin@yadro.com>
|
||||
*/
|
||||
|
||||
#ifndef __I2C_H__
|
||||
#define __I2C_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
/** Representation of a I2C adapter */
|
||||
struct i2c_adapter {
|
||||
/** Pointer to I2C driver owning this I2C adapter */
|
||||
void *driver;
|
||||
|
||||
/** Unique ID of the I2C adapter assigned by the driver */
|
||||
int id;
|
||||
|
||||
/**
|
||||
* Send buffer to given address, register
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len);
|
||||
|
||||
/**
|
||||
* Read buffer from given address, register
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len);
|
||||
|
||||
/** List */
|
||||
struct sbi_dlist node;
|
||||
};
|
||||
|
||||
static inline struct i2c_adapter *to_i2c_adapter(struct sbi_dlist *node)
|
||||
{
|
||||
return container_of(node, struct i2c_adapter, node);
|
||||
}
|
||||
|
||||
/** Find a registered I2C adapter */
|
||||
struct i2c_adapter *i2c_adapter_find(int id);
|
||||
|
||||
/** Register I2C adapter */
|
||||
int i2c_adapter_add(struct i2c_adapter *ia);
|
||||
|
||||
/** Un-register I2C adapter */
|
||||
void i2c_adapter_remove(struct i2c_adapter *ia);
|
||||
|
||||
/** Send to device on I2C adapter bus */
|
||||
int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len);
|
||||
|
||||
/** Read from device on I2C adapter bus */
|
||||
int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len);
|
||||
|
||||
static inline int i2c_adapter_reg_write(struct i2c_adapter *ia, uint8_t addr,
|
||||
uint8_t reg, uint8_t val)
|
||||
{
|
||||
return i2c_adapter_write(ia, addr, reg, &val, 1);
|
||||
}
|
||||
|
||||
static inline int i2c_adapter_reg_read(struct i2c_adapter *ia, uint8_t addr,
|
||||
uint8_t reg, uint8_t *val)
|
||||
{
|
||||
uint8_t buf;
|
||||
int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __IPI_ACLINT_MSWI_H__
|
||||
#define __IPI_ACLINT_MSWI_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define ACLINT_MSWI_ALIGN 0x1000
|
||||
#define ACLINT_MSWI_SIZE 0x4000
|
||||
#define ACLINT_MSWI_MAX_HARTS 4095
|
||||
|
||||
#define CLINT_MSWI_OFFSET 0x0000
|
||||
|
||||
struct aclint_mswi_data {
|
||||
/* Public details */
|
||||
unsigned long addr;
|
||||
unsigned long size;
|
||||
u32 first_hartid;
|
||||
u32 hart_count;
|
||||
};
|
||||
|
||||
int aclint_mswi_warm_init(void);
|
||||
|
||||
int aclint_mswi_cold_init(struct aclint_mswi_data *mswi);
|
||||
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_APLIC_H__
|
||||
#define __IRQCHIP_APLIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define APLIC_MAX_DELEGATE 16
|
||||
|
||||
struct aplic_msicfg_data {
|
||||
unsigned long lhxs;
|
||||
unsigned long lhxw;
|
||||
unsigned long hhxs;
|
||||
unsigned long hhxw;
|
||||
unsigned long base_addr;
|
||||
};
|
||||
|
||||
struct aplic_delegate_data {
|
||||
u32 first_irq;
|
||||
u32 last_irq;
|
||||
u32 child_index;
|
||||
};
|
||||
|
||||
struct aplic_data {
|
||||
unsigned long addr;
|
||||
unsigned long size;
|
||||
unsigned long num_idc;
|
||||
unsigned long num_source;
|
||||
bool targets_mmode;
|
||||
bool has_msicfg_mmode;
|
||||
struct aplic_msicfg_data msicfg_mmode;
|
||||
bool has_msicfg_smode;
|
||||
struct aplic_msicfg_data msicfg_smode;
|
||||
struct aplic_delegate_data delegate[APLIC_MAX_DELEGATE];
|
||||
};
|
||||
|
||||
int aplic_cold_irqchip_init(struct aplic_data *aplic);
|
||||
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Samuel Holland <samuel@sholland.org>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_FDT_IRQCHIP_PLIC_H__
|
||||
#define __IRQCHIP_FDT_IRQCHIP_PLIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
void fdt_plic_priority_save(u8 *priority);
|
||||
|
||||
void fdt_plic_priority_restore(const u8 *priority);
|
||||
|
||||
void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold);
|
||||
|
||||
void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold);
|
||||
|
||||
void thead_plic_restore(void);
|
||||
|
||||
#endif
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_IMSIC_H__
|
||||
#define __IRQCHIP_IMSIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define IMSIC_MMIO_PAGE_SHIFT 12
|
||||
#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT)
|
||||
|
||||
#define IMSIC_MAX_REGS 16
|
||||
|
||||
struct imsic_regs {
|
||||
unsigned long addr;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
struct imsic_data {
|
||||
bool targets_mmode;
|
||||
u32 guest_index_bits;
|
||||
u32 hart_index_bits;
|
||||
u32 group_index_bits;
|
||||
u32 group_index_shift;
|
||||
unsigned long num_ids;
|
||||
struct imsic_regs regs[IMSIC_MAX_REGS];
|
||||
};
|
||||
|
||||
int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
|
||||
|
||||
struct imsic_data *imsic_get_data(u32 hartid);
|
||||
|
||||
int imsic_get_target_file(u32 hartid);
|
||||
|
||||
void imsic_local_irqchip_init(void);
|
||||
|
||||
int imsic_warm_irqchip_init(void);
|
||||
|
||||
int imsic_data_check(struct imsic_data *imsic);
|
||||
|
||||
int imsic_cold_irqchip_init(struct imsic_data *imsic);
|
||||
|
||||
#endif
|
||||
@@ -17,23 +17,13 @@ struct plic_data {
|
||||
unsigned long num_src;
|
||||
};
|
||||
|
||||
/* So far, priorities on all consumers of these functions fit in 8 bits. */
|
||||
void plic_priority_save(const struct plic_data *plic, u8 *priority);
|
||||
|
||||
void plic_priority_restore(const struct plic_data *plic, const u8 *priority);
|
||||
|
||||
void plic_context_save(const struct plic_data *plic, int context_id,
|
||||
u32 *enable, u32 *threshold);
|
||||
|
||||
void plic_context_restore(const struct plic_data *plic, int context_id,
|
||||
const u32 *enable, u32 threshold);
|
||||
|
||||
int plic_context_init(const struct plic_data *plic, int context_id,
|
||||
bool enable, u32 threshold);
|
||||
|
||||
int plic_warm_irqchip_init(const struct plic_data *plic,
|
||||
int plic_warm_irqchip_init(struct plic_data *plic,
|
||||
int m_cntx_id, int s_cntx_id);
|
||||
|
||||
int plic_cold_irqchip_init(const struct plic_data *plic);
|
||||
int plic_cold_irqchip_init(struct plic_data *plic);
|
||||
|
||||
void plic_set_thresh(struct plic_data *plic, u32 cntxid, u32 val);
|
||||
|
||||
void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,16 +17,6 @@ struct fdt_reset {
|
||||
int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
|
||||
};
|
||||
|
||||
/**
|
||||
* fdt_reset_driver_init() - initialize reset driver based on the device-tree
|
||||
*/
|
||||
int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
|
||||
|
||||
/**
|
||||
* fdt_reset_init() - initialize reset drivers based on the device-tree
|
||||
*
|
||||
* This function shall be invoked in final init.
|
||||
*/
|
||||
void fdt_reset_init(void);
|
||||
int fdt_reset_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Cobham Gaisler AB.
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Cederman <cederman@gaisler.com>
|
||||
*/
|
||||
|
||||
#ifndef __SERIAL_GAISLER_APBUART_H__
|
||||
#define __SERIAL_GAISLER_APBUART_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Gabriel Somlo
|
||||
*
|
||||
* Authors:
|
||||
* Gabriel Somlo <gsomlo@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __SERIAL_LITEX_UART_H__
|
||||
#define __SERIAL_LITEX_UART_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int litex_uart_init(unsigned long base);
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,6 @@
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
|
||||
u32 reg_width, u32 reg_offset);
|
||||
u32 reg_width);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Alistair Francis <alistair.francis@wdc.com>
|
||||
*/
|
||||
#ifndef __SERIAL_XLNX_UARTLITE_H__
|
||||
#define __SERIAL_XLNX_UARTLITE_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int xlnx_uartlite_init(unsigned long base);
|
||||
|
||||
#endif
|
||||
41
include/sbi_utils/sys/clint.h
Normal file
41
include/sbi_utils/sys/clint.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __SYS_CLINT_H__
|
||||
#define __SYS_CLINT_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
struct clint_data {
|
||||
/* Public details */
|
||||
unsigned long addr;
|
||||
u32 first_hartid;
|
||||
u32 hart_count;
|
||||
bool has_64bit_mmio;
|
||||
/* Private details (initialized and used by CLINT library)*/
|
||||
u32 *ipi;
|
||||
struct clint_data *time_delta_reference;
|
||||
unsigned long time_delta_computed;
|
||||
u64 time_delta;
|
||||
u64 *time_val;
|
||||
u64 *time_cmp;
|
||||
u64 (*time_rd)(volatile u64 *addr);
|
||||
void (*time_wr)(u64 value, volatile u64 *addr);
|
||||
};
|
||||
|
||||
int clint_warm_ipi_init(void);
|
||||
|
||||
int clint_cold_ipi_init(struct clint_data *clint);
|
||||
|
||||
int clint_warm_timer_init(void);
|
||||
|
||||
int clint_cold_timer_init(struct clint_data *clint,
|
||||
struct clint_data *reference);
|
||||
|
||||
#endif
|
||||
@@ -10,12 +10,8 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int htif_serial_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr);
|
||||
int htif_serial_init(void);
|
||||
|
||||
int htif_system_reset_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr);
|
||||
int htif_system_reset_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __TIMER_ACLINT_MTIMER_H__
|
||||
#define __TIMER_ACLINT_MTIMER_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define ACLINT_MTIMER_ALIGN 0x8
|
||||
#define ACLINT_MTIMER_MAX_HARTS 4095
|
||||
|
||||
#define ACLINT_DEFAULT_MTIME_OFFSET 0x7ff8
|
||||
#define ACLINT_DEFAULT_MTIME_SIZE 0x8
|
||||
#define ACLINT_DEFAULT_MTIMECMP_OFFSET 0x0000
|
||||
#define ACLINT_DEFAULT_MTIMECMP_SIZE 0x7ff8
|
||||
|
||||
#define CLINT_MTIMER_OFFSET 0x4000
|
||||
|
||||
struct aclint_mtimer_data {
|
||||
/* Public details */
|
||||
unsigned long mtime_freq;
|
||||
unsigned long mtime_addr;
|
||||
unsigned long mtime_size;
|
||||
unsigned long mtimecmp_addr;
|
||||
unsigned long mtimecmp_size;
|
||||
u32 first_hartid;
|
||||
u32 hart_count;
|
||||
bool has_64bit_mmio;
|
||||
bool has_shared_mtime;
|
||||
/* Private details (initialized and used by ACLINT MTIMER library) */
|
||||
struct aclint_mtimer_data *time_delta_reference;
|
||||
unsigned long time_delta_computed;
|
||||
u64 (*time_rd)(volatile u64 *addr);
|
||||
void (*time_wr)(bool timecmp, u64 value, volatile u64 *addr);
|
||||
};
|
||||
|
||||
void aclint_mtimer_sync(struct aclint_mtimer_data *mt);
|
||||
|
||||
void aclint_mtimer_set_reference(struct aclint_mtimer_data *mt,
|
||||
struct aclint_mtimer_data *ref);
|
||||
|
||||
int aclint_mtimer_warm_init(void);
|
||||
|
||||
int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt,
|
||||
struct aclint_mtimer_data *reference);
|
||||
|
||||
#endif
|
||||
@@ -20,9 +20,9 @@ libsbi-objs-y += sbi_ecall.o
|
||||
libsbi-objs-y += sbi_ecall_base.o
|
||||
libsbi-objs-y += sbi_ecall_hsm.o
|
||||
libsbi-objs-y += sbi_ecall_legacy.o
|
||||
libsbi-objs-y += sbi_ecall_pmu.o
|
||||
libsbi-objs-y += sbi_ecall_replace.o
|
||||
libsbi-objs-y += sbi_ecall_vendor.o
|
||||
libsbi-objs-y += sbi_ecall_susp.o
|
||||
libsbi-objs-y += sbi_emulate_csr.o
|
||||
libsbi-objs-y += sbi_fifo.o
|
||||
libsbi-objs-y += sbi_hart.o
|
||||
@@ -32,10 +32,8 @@ libsbi-objs-y += sbi_hsm.o
|
||||
libsbi-objs-y += sbi_illegal_insn.o
|
||||
libsbi-objs-y += sbi_init.o
|
||||
libsbi-objs-y += sbi_ipi.o
|
||||
libsbi-objs-y += sbi_irqchip.o
|
||||
libsbi-objs-y += sbi_misaligned_ldst.o
|
||||
libsbi-objs-y += sbi_platform.o
|
||||
libsbi-objs-y += sbi_pmu.o
|
||||
libsbi-objs-y += sbi_scratch.o
|
||||
libsbi-objs-y += sbi_string.o
|
||||
libsbi-objs-y += sbi_system.o
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
/* determine CPU extension, return non-zero support */
|
||||
int misa_extension_imp(char ext)
|
||||
@@ -53,7 +52,7 @@ int misa_xlen(void)
|
||||
void misa_string(int xlen, char *out, unsigned int out_sz)
|
||||
{
|
||||
unsigned int i, pos = 0;
|
||||
const char valid_isa_order[] = "iemafdqclbjtpvnhkorwxyzg";
|
||||
const char valid_isa_order[] = "iemafdqclbjtpvnsuhkorwxyzg";
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
@@ -76,8 +75,6 @@ void misa_string(int xlen, char *out, unsigned int out_sz)
|
||||
out[pos++] = '8';
|
||||
break;
|
||||
default:
|
||||
sbi_panic("%s: Unknown misa.MXL encoding %d",
|
||||
__func__, xlen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -121,36 +118,7 @@ unsigned long csr_read_num(int csr_num)
|
||||
switch (csr_num) {
|
||||
switchcase_csr_read_16(CSR_PMPCFG0, ret)
|
||||
switchcase_csr_read_64(CSR_PMPADDR0, ret)
|
||||
switchcase_csr_read(CSR_MCYCLE, ret)
|
||||
switchcase_csr_read(CSR_MINSTRET, ret)
|
||||
switchcase_csr_read(CSR_MHPMCOUNTER3, ret)
|
||||
switchcase_csr_read_4(CSR_MHPMCOUNTER4, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMCOUNTER8, ret)
|
||||
switchcase_csr_read_16(CSR_MHPMCOUNTER16, ret)
|
||||
switchcase_csr_read(CSR_MCOUNTINHIBIT, ret)
|
||||
switchcase_csr_read(CSR_MHPMEVENT3, ret)
|
||||
switchcase_csr_read_4(CSR_MHPMEVENT4, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMEVENT8, ret)
|
||||
switchcase_csr_read_16(CSR_MHPMEVENT16, ret)
|
||||
#if __riscv_xlen == 32
|
||||
switchcase_csr_read(CSR_MCYCLEH, ret)
|
||||
switchcase_csr_read(CSR_MINSTRETH, ret)
|
||||
switchcase_csr_read(CSR_MHPMCOUNTER3H, ret)
|
||||
switchcase_csr_read_4(CSR_MHPMCOUNTER4H, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMCOUNTER8H, ret)
|
||||
switchcase_csr_read_16(CSR_MHPMCOUNTER16H, ret)
|
||||
/**
|
||||
* The CSR range MHPMEVENT[3-16]H are available only if sscofpmf
|
||||
* extension is present. The caller must ensure that.
|
||||
*/
|
||||
switchcase_csr_read(CSR_MHPMEVENT3H, ret)
|
||||
switchcase_csr_read_4(CSR_MHPMEVENT4H, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMEVENT8H, ret)
|
||||
switchcase_csr_read_16(CSR_MHPMEVENT16H, ret)
|
||||
#endif
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown CSR %#x", __func__, csr_num);
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -193,32 +161,7 @@ void csr_write_num(int csr_num, unsigned long val)
|
||||
switch (csr_num) {
|
||||
switchcase_csr_write_16(CSR_PMPCFG0, val)
|
||||
switchcase_csr_write_64(CSR_PMPADDR0, val)
|
||||
switchcase_csr_write(CSR_MCYCLE, val)
|
||||
switchcase_csr_write(CSR_MINSTRET, val)
|
||||
switchcase_csr_write(CSR_MHPMCOUNTER3, val)
|
||||
switchcase_csr_write_4(CSR_MHPMCOUNTER4, val)
|
||||
switchcase_csr_write_8(CSR_MHPMCOUNTER8, val)
|
||||
switchcase_csr_write_16(CSR_MHPMCOUNTER16, val)
|
||||
#if __riscv_xlen == 32
|
||||
switchcase_csr_write(CSR_MCYCLEH, val)
|
||||
switchcase_csr_write(CSR_MINSTRETH, val)
|
||||
switchcase_csr_write(CSR_MHPMCOUNTER3H, val)
|
||||
switchcase_csr_write_4(CSR_MHPMCOUNTER4H, val)
|
||||
switchcase_csr_write_8(CSR_MHPMCOUNTER8H, val)
|
||||
switchcase_csr_write_16(CSR_MHPMCOUNTER16H, val)
|
||||
switchcase_csr_write(CSR_MHPMEVENT3H, val)
|
||||
switchcase_csr_write_4(CSR_MHPMEVENT4H, val)
|
||||
switchcase_csr_write_8(CSR_MHPMEVENT8H, val)
|
||||
switchcase_csr_write_16(CSR_MHPMEVENT16H, val)
|
||||
#endif
|
||||
switchcase_csr_write(CSR_MCOUNTINHIBIT, val)
|
||||
switchcase_csr_write(CSR_MHPMEVENT3, val)
|
||||
switchcase_csr_write_4(CSR_MHPMEVENT4, val)
|
||||
switchcase_csr_write_8(CSR_MHPMEVENT8, val)
|
||||
switchcase_csr_write_16(CSR_MHPMEVENT16, val)
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown CSR %#x", __func__, csr_num);
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -235,9 +178,6 @@ static unsigned long ctz(unsigned long x)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
|
||||
if (x == 0)
|
||||
return 8 * sizeof(x);
|
||||
|
||||
while (!(x & 1UL)) {
|
||||
ret++;
|
||||
x = x >> 1;
|
||||
@@ -265,12 +205,14 @@ int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
# error "Unexpected __riscv_xlen"
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/* encode PMP config */
|
||||
prot &= ~PMP_A;
|
||||
prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
|
||||
cfgmask = ~(0xffUL << pmpcfg_shift);
|
||||
pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask);
|
||||
@@ -316,9 +258,12 @@ int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
# error "Unexpected __riscv_xlen"
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/* decode PMP config */
|
||||
cfgmask = (0xffUL << pmpcfg_shift);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/riscv_locks.h>
|
||||
|
||||
static inline bool spin_lock_unlocked(spinlock_t lock)
|
||||
static inline int spin_lock_unlocked(spinlock_t lock)
|
||||
{
|
||||
return lock.owner == lock.next;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ bool spin_lock_check(spinlock_t *lock)
|
||||
return !spin_lock_unlocked(*lock);
|
||||
}
|
||||
|
||||
bool spin_trylock(spinlock_t *lock)
|
||||
int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
unsigned long inc = 1u << TICKET_SHIFT;
|
||||
unsigned long mask = 0xffffu << TICKET_SHIFT;
|
||||
@@ -42,7 +42,7 @@ bool spin_trylock(spinlock_t *lock)
|
||||
: "r"(inc), "r"(mask), "I"(TICKET_SHIFT)
|
||||
: "memory");
|
||||
|
||||
return l0 == 0;
|
||||
return !l0;
|
||||
}
|
||||
|
||||
void spin_lock(spinlock_t *lock)
|
||||
|
||||
@@ -39,7 +39,7 @@ unsigned long find_first_bit(const unsigned long *addr,
|
||||
if (tmp == 0UL) /* Are any bits set? */
|
||||
return result + size; /* Nope. */
|
||||
found:
|
||||
return result + sbi_ffs(tmp);
|
||||
return result + __ffs(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ unsigned long find_first_zero_bit(const unsigned long *addr,
|
||||
if (tmp == ~0UL) /* Are any bits zero? */
|
||||
return result + size; /* Nope. */
|
||||
found:
|
||||
return result + sbi_ffz(tmp);
|
||||
return result + ffz(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,7 @@ unsigned long find_last_bit(const unsigned long *addr,
|
||||
tmp = addr[--words];
|
||||
if (tmp) {
|
||||
found:
|
||||
return words * BITS_PER_LONG + sbi_fls(tmp);
|
||||
return words * BITS_PER_LONG + __fls(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ found_first:
|
||||
if (tmp == 0UL) /* Are any bits set? */
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + sbi_ffs(tmp);
|
||||
return result + __ffs(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,5 +196,5 @@ found_first:
|
||||
if (tmp == ~0UL) /* Are any bits zero? */
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + sbi_ffz(tmp);
|
||||
return result + ffz(tmp);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <sbi/riscv_locks.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
|
||||
@@ -388,29 +387,13 @@ int sbi_dprintf(const char *format, ...)
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
va_start(args, format);
|
||||
if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS) {
|
||||
spin_lock(&console_out_lock);
|
||||
if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS)
|
||||
retval = print(NULL, NULL, format, args);
|
||||
spin_unlock(&console_out_lock);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void sbi_panic(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
spin_lock(&console_out_lock);
|
||||
va_start(args, format);
|
||||
print(NULL, NULL, format, args);
|
||||
va_end(args);
|
||||
spin_unlock(&console_out_lock);
|
||||
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
const struct sbi_console_device *sbi_console_get_device(void)
|
||||
{
|
||||
return console_dev;
|
||||
|
||||
@@ -34,6 +34,7 @@ struct sbi_domain root = {
|
||||
.possible_harts = &root_hmask,
|
||||
.regions = root_memregs,
|
||||
.system_reset_allowed = TRUE,
|
||||
.system_suspend_allowed = TRUE,
|
||||
};
|
||||
|
||||
bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid)
|
||||
@@ -159,7 +160,7 @@ static bool is_region_subset(const struct sbi_domain_memregion *regA,
|
||||
ulong regA_start = regA->base;
|
||||
ulong regA_end = regA->base + (BIT(regA->order) - 1);
|
||||
ulong regB_start = regB->base;
|
||||
ulong regB_end = regB->base + (BIT(regB->order) - 1);
|
||||
ulong regB_end = regB->base + (BIT(regA->order) - 1);
|
||||
|
||||
if ((regB_start <= regA_start) &&
|
||||
(regA_start < regB_end) &&
|
||||
@@ -295,7 +296,7 @@ static int sanitize_domain(const struct sbi_platform *plat,
|
||||
/* Check next address and next mode*/
|
||||
if (!sbi_domain_check_addr(dom, dom->next_addr, dom->next_mode,
|
||||
SBI_DOMAIN_EXECUTE)) {
|
||||
sbi_printf("%s: %s next booting stage address 0x%lx can't "
|
||||
sbi_printf("%s: %s next booting stage addres 0x%lx can't "
|
||||
"execute\n", __func__, dom->name, dom->next_addr);
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
@@ -328,7 +329,11 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
rend = (reg->order < __riscv_xlen) ?
|
||||
rstart + ((1UL << reg->order) - 1) : -1UL;
|
||||
|
||||
sbi_printf("Domain%d Region%02d %s: 0x%" PRILX "-0x%" PRILX " ",
|
||||
#if __riscv_xlen == 32
|
||||
sbi_printf("Domain%d Region%02d %s: 0x%08lx-0x%08lx ",
|
||||
#else
|
||||
sbi_printf("Domain%d Region%02d %s: 0x%016lx-0x%016lx ",
|
||||
#endif
|
||||
dom->index, i, suffix, rstart, rend);
|
||||
|
||||
k = 0;
|
||||
@@ -347,10 +352,18 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
i++;
|
||||
}
|
||||
|
||||
sbi_printf("Domain%d Next Address%s: 0x%" PRILX "\n",
|
||||
#if __riscv_xlen == 32
|
||||
sbi_printf("Domain%d Next Address%s: 0x%08lx\n",
|
||||
#else
|
||||
sbi_printf("Domain%d Next Address%s: 0x%016lx\n",
|
||||
#endif
|
||||
dom->index, suffix, dom->next_addr);
|
||||
|
||||
sbi_printf("Domain%d Next Arg1 %s: 0x%" PRILX "\n",
|
||||
#if __riscv_xlen == 32
|
||||
sbi_printf("Domain%d Next Arg1 %s: 0x%08lx\n",
|
||||
#else
|
||||
sbi_printf("Domain%d Next Arg1 %s: 0x%016lx\n",
|
||||
#endif
|
||||
dom->index, suffix, dom->next_arg1);
|
||||
|
||||
sbi_printf("Domain%d Next Mode %s: ", dom->index, suffix);
|
||||
@@ -371,6 +384,8 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
|
||||
sbi_printf("Domain%d SysReset %s: %s\n",
|
||||
dom->index, suffix, (dom->system_reset_allowed) ? "yes" : "no");
|
||||
sbi_printf("Domain%d SysSuspend %s: %s\n",
|
||||
dom->index, suffix, (dom->system_suspend_allowed) ? "yes" : "no");
|
||||
}
|
||||
|
||||
void sbi_domain_dump_all(const char *suffix)
|
||||
@@ -471,12 +486,8 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg)
|
||||
|
||||
/* Check for conflicts */
|
||||
sbi_domain_for_each_memregion(&root, nreg) {
|
||||
if (is_region_conflict(reg, nreg)) {
|
||||
sbi_printf("%s: is_region_conflict check failed"
|
||||
" 0x%lx conflicts existing 0x%lx\n", __func__,
|
||||
reg->base, nreg->base);
|
||||
return SBI_EALREADY;
|
||||
}
|
||||
if (is_region_conflict(reg, nreg))
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
/* Append the memregion to root memregions */
|
||||
@@ -503,8 +514,7 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg)
|
||||
if (!nreg1->order)
|
||||
continue;
|
||||
|
||||
if (!(nreg->base & (BIT(nreg->order + 1) - 1)) &&
|
||||
(nreg->base + BIT(nreg->order)) == nreg1->base &&
|
||||
if ((nreg->base + BIT(nreg->order)) == nreg1->base &&
|
||||
nreg->order == nreg1->order &&
|
||||
nreg->flags == nreg1->flags) {
|
||||
nreg->order++;
|
||||
|
||||
@@ -162,15 +162,15 @@ int sbi_ecall_init(void)
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sbi_ecall_register_extension(&ecall_srst);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sbi_ecall_register_extension(&ecall_pmu);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sbi_ecall_register_extension(&ecall_legacy);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sbi_ecall_register_extension(&ecall_vendor);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sbi_ecall_register_extension(&ecall_susp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_version.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
|
||||
static int sbi_ecall_base_probe(unsigned long extid, unsigned long *out_val)
|
||||
{
|
||||
@@ -32,6 +33,41 @@ static int sbi_ecall_base_probe(unsigned long extid, unsigned long *out_val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SEC_SYS_BASE (0x020B0000)
|
||||
static int sbi_ecall_base_rst_c906l(void)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
value = readl((void *)0x3003024);
|
||||
writel((value & (~(1 << 6))), (void *)0x3003024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_ecall_base_unrst_c906l(const unsigned long address)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
value = readl((void *)SEC_SYS_BASE + 0x04);
|
||||
writel((value | (1 << 13)), (void *)SEC_SYS_BASE + 0x04);
|
||||
writel(address, (void *)SEC_SYS_BASE + 0x20);
|
||||
writel((address >> 32), (void *)SEC_SYS_BASE + 0x24);
|
||||
|
||||
value = readl((void *)0x3003024);
|
||||
writel((value | (1 << 6)), (void *)0x3003024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_ecall_base_reset_c906l(const unsigned long address)
|
||||
{
|
||||
sbi_ecall_base_rst_c906l();
|
||||
|
||||
sbi_ecall_base_unrst_c906l(address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_ecall_base_handler(unsigned long extid, unsigned long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_val,
|
||||
@@ -65,6 +101,16 @@ static int sbi_ecall_base_handler(unsigned long extid, unsigned long funcid,
|
||||
case SBI_EXT_BASE_PROBE_EXT:
|
||||
ret = sbi_ecall_base_probe(regs->a0, out_val);
|
||||
break;
|
||||
case SBI_EXT_BASE_RESET_C906L:
|
||||
ret = sbi_ecall_base_reset_c906l(regs->a0);
|
||||
*out_val = regs->a0;
|
||||
break;
|
||||
case SBI_EXT_BASE_RST_C906L:
|
||||
ret = sbi_ecall_base_rst_c906l();
|
||||
break;
|
||||
case SBI_EXT_BASE_UNRST_C906L:
|
||||
ret = sbi_ecall_base_unrst_c906l(regs->a0);
|
||||
break;
|
||||
default:
|
||||
ret = SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Atish Patra <atish.patra@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_ecall.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_version.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
|
||||
static int sbi_ecall_pmu_handler(unsigned long extid, unsigned long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_val,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
int ret = 0;
|
||||
uint64_t temp;
|
||||
|
||||
switch (funcid) {
|
||||
case SBI_EXT_PMU_NUM_COUNTERS:
|
||||
ret = sbi_pmu_num_ctr();
|
||||
if (ret >= 0) {
|
||||
*out_val = ret;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_GET_INFO:
|
||||
ret = sbi_pmu_ctr_get_info(regs->a0, out_val);
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_CFG_MATCH:
|
||||
#if __riscv_xlen == 32
|
||||
temp = ((uint64_t)regs->a5 << 32) | regs->a4;
|
||||
#else
|
||||
temp = regs->a4;
|
||||
#endif
|
||||
ret = sbi_pmu_ctr_cfg_match(regs->a0, regs->a1, regs->a2,
|
||||
regs->a3, temp);
|
||||
if (ret >= 0) {
|
||||
*out_val = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_FW_READ:
|
||||
ret = sbi_pmu_ctr_read(regs->a0, out_val);
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_START:
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
temp = ((uint64_t)regs->a4 << 32) | regs->a3;
|
||||
#else
|
||||
temp = regs->a3;
|
||||
#endif
|
||||
ret = sbi_pmu_ctr_start(regs->a0, regs->a1, regs->a2, temp);
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_STOP:
|
||||
ret = sbi_pmu_ctr_stop(regs->a0, regs->a1, regs->a2);
|
||||
break;
|
||||
default:
|
||||
ret = SBI_ENOTSUPP;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sbi_ecall_pmu_probe(unsigned long extid, unsigned long *out_val)
|
||||
{
|
||||
/* PMU extension is always enabled */
|
||||
*out_val = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sbi_ecall_extension ecall_pmu = {
|
||||
.extid_start = SBI_EXT_PMU,
|
||||
.extid_end = SBI_EXT_PMU,
|
||||
.handle = sbi_ecall_pmu_handler,
|
||||
.probe = sbi_ecall_pmu_probe,
|
||||
};
|
||||
@@ -54,8 +54,8 @@ static int sbi_ecall_rfence_handler(unsigned long extid, unsigned long funcid,
|
||||
struct sbi_tlb_info tlb_info;
|
||||
u32 source_hart = current_hartid();
|
||||
|
||||
if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID &&
|
||||
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA)
|
||||
if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA &&
|
||||
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID)
|
||||
if (!misa_extension('H'))
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
@@ -151,7 +151,7 @@ static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid,
|
||||
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
||||
break;
|
||||
default:
|
||||
return SBI_EINVAL;
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
switch (regs->a1) {
|
||||
@@ -159,7 +159,7 @@ static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid,
|
||||
case SBI_SRST_RESET_REASON_SYSFAIL:
|
||||
break;
|
||||
default:
|
||||
return SBI_EINVAL;
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
if (sbi_system_reset_supported(regs->a0, regs->a1))
|
||||
|
||||
60
lib/sbi/sbi_ecall_susp.c
Normal file
60
lib/sbi/sbi_ecall_susp.c
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
#include <sbi/sbi_ecall.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
|
||||
static int sbi_ecall_susp_handler(unsigned long extid, unsigned long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_val,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
int ret = SBI_ENOTSUPP;
|
||||
|
||||
if (funcid == SBI_EXT_SUSP_SUSPEND)
|
||||
ret = sbi_system_suspend(regs->a0, regs->a1, regs->a2);
|
||||
|
||||
if (ret >= 0) {
|
||||
*out_val = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool susp_available(void)
|
||||
{
|
||||
u32 type;
|
||||
|
||||
/*
|
||||
* At least one suspend type should be supported by the
|
||||
* platform for the SBI SUSP extension to be usable.
|
||||
*/
|
||||
for (type = 0; type <= SBI_SUSP_SLEEP_TYPE_LAST; type++) {
|
||||
if (sbi_system_suspend_supported(type))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sbi_ecall_extension ecall_susp;
|
||||
|
||||
static int sbi_ecall_susp_register_extensions(unsigned long extid, unsigned long *out_val)
|
||||
{
|
||||
if (!susp_available()) {
|
||||
*out_val = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_val = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sbi_ecall_extension ecall_susp = {
|
||||
.extid_start = SBI_EXT_SUSP,
|
||||
.extid_end = SBI_EXT_SUSP,
|
||||
.probe = sbi_ecall_susp_register_extensions,
|
||||
.handle = sbi_ecall_susp_handler,
|
||||
};
|
||||
@@ -24,7 +24,7 @@ static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
if (prev_mode <= PRV_S) {
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) {
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN)) {
|
||||
cen &= csr_read(CSR_MCOUNTEREN);
|
||||
if (virt)
|
||||
cen &= csr_read(CSR_HCOUNTEREN);
|
||||
@@ -33,7 +33,7 @@ static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
|
||||
}
|
||||
}
|
||||
if (prev_mode == PRV_U) {
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
|
||||
cen &= csr_read(CSR_SCOUNTEREN);
|
||||
else
|
||||
cen = 0;
|
||||
|
||||
@@ -43,13 +43,10 @@ u16 sbi_fifo_avail(struct sbi_fifo *fifo)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sbi_fifo_is_full(struct sbi_fifo *fifo)
|
||||
bool sbi_fifo_is_full(struct sbi_fifo *fifo)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (!fifo)
|
||||
return SBI_EINVAL;
|
||||
|
||||
spin_lock(&fifo->qlock);
|
||||
ret = __sbi_fifo_is_full(fifo);
|
||||
spin_unlock(&fifo->qlock);
|
||||
@@ -66,7 +63,7 @@ static inline void __sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data)
|
||||
if (head >= fifo->num_entries)
|
||||
head = head - fifo->num_entries;
|
||||
|
||||
sbi_memcpy((char *)fifo->queue + head * fifo->entry_size, data, fifo->entry_size);
|
||||
sbi_memcpy(fifo->queue + head * fifo->entry_size, data, fifo->entry_size);
|
||||
|
||||
fifo->avail++;
|
||||
}
|
||||
@@ -78,13 +75,10 @@ static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
return (fifo->avail == 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
int sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
bool sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (!fifo)
|
||||
return SBI_EINVAL;
|
||||
|
||||
spin_lock(&fifo->qlock);
|
||||
ret = __sbi_fifo_is_empty(fifo);
|
||||
spin_unlock(&fifo->qlock);
|
||||
@@ -124,7 +118,7 @@ bool sbi_fifo_reset(struct sbi_fifo *fifo)
|
||||
int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
|
||||
int (*fptr)(void *in, void *data))
|
||||
{
|
||||
int i, index;
|
||||
int i, index = 0;
|
||||
int ret = SBI_FIFO_UNCHANGED;
|
||||
void *entry;
|
||||
|
||||
@@ -141,8 +135,8 @@ int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
|
||||
for (i = 0; i < fifo->avail; i++) {
|
||||
index = fifo->tail + i;
|
||||
if (index >= fifo->num_entries)
|
||||
index -= fifo->num_entries;
|
||||
entry = (char *)fifo->queue + (u32)index * fifo->entry_size;
|
||||
index = index - fifo->num_entries;
|
||||
entry = (void *)fifo->queue + (u32)index * fifo->entry_size;
|
||||
ret = fptr(in, entry);
|
||||
|
||||
if (ret == SBI_FIFO_SKIP || ret == SBI_FIFO_UPDATED) {
|
||||
@@ -184,7 +178,7 @@ int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data)
|
||||
return SBI_ENOENT;
|
||||
}
|
||||
|
||||
sbi_memcpy(data, (char *)fifo->queue + (u32)fifo->tail * fifo->entry_size,
|
||||
sbi_memcpy(data, fifo->queue + (u32)fifo->tail * fifo->entry_size,
|
||||
fifo->entry_size);
|
||||
|
||||
fifo->avail--;
|
||||
|
||||
@@ -28,24 +28,17 @@ extern void __sbi_expected_trap_hext(void);
|
||||
void (*sbi_hart_expected_trap)(void) = &__sbi_expected_trap;
|
||||
|
||||
struct hart_features {
|
||||
bool detected;
|
||||
int priv_version;
|
||||
unsigned long extensions;
|
||||
unsigned long features;
|
||||
unsigned int pmp_count;
|
||||
unsigned int pmp_addr_bits;
|
||||
unsigned long pmp_gran;
|
||||
unsigned int mhpm_count;
|
||||
unsigned int mhpm_bits;
|
||||
};
|
||||
static unsigned long hart_features_offset;
|
||||
|
||||
static void mstatus_init(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long menvcfg_val, mstatus_val = 0;
|
||||
int cidx;
|
||||
unsigned int num_mhpm = sbi_hart_mhpm_count(scratch);
|
||||
uint64_t mhpmevent_init_val = 0;
|
||||
uint64_t mstateen_val;
|
||||
unsigned long mstatus_val = 0;
|
||||
|
||||
/* Enable FPU */
|
||||
if (misa_extension('D') || misa_extension('F'))
|
||||
@@ -57,117 +50,13 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
|
||||
csr_write(CSR_MSTATUS, mstatus_val);
|
||||
|
||||
/* Disable user mode usage of all perf counters except default ones (CY, TM, IR) */
|
||||
/* Enable user/supervisor use of perf counters */
|
||||
if (misa_extension('S') &&
|
||||
sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_SCOUNTEREN, 7);
|
||||
|
||||
/**
|
||||
* OpenSBI doesn't use any PMU counters in M-mode.
|
||||
* Supervisor mode usage for all counters are enabled by default
|
||||
* But counters will not run until mcountinhibit is set.
|
||||
*/
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
|
||||
csr_write(CSR_SCOUNTEREN, -1);
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN))
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
|
||||
/* All programmable counters will start running at runtime after S-mode request */
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
/**
|
||||
* The mhpmeventn[h] CSR should be initialized with interrupt disabled
|
||||
* and inhibited running in M-mode during init.
|
||||
* To keep it simple, only contiguous mhpmcounters are supported as a
|
||||
* platform with discontiguous mhpmcounters may not make much sense.
|
||||
*/
|
||||
mhpmevent_init_val |= (MHPMEVENT_OF | MHPMEVENT_MINH);
|
||||
for (cidx = 0; cidx < num_mhpm; cidx++) {
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + cidx, mhpmevent_init_val & 0xFFFFFFFF);
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + cidx,
|
||||
mhpmevent_init_val >> BITS_PER_LONG);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + cidx, mhpmevent_init_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMSTATEEN)) {
|
||||
mstateen_val = csr_read(CSR_MSTATEEN0);
|
||||
#if __riscv_xlen == 32
|
||||
mstateen_val |= ((uint64_t)csr_read(CSR_MSTATEEN0H)) << 32;
|
||||
#endif
|
||||
mstateen_val |= SMSTATEEN_STATEN;
|
||||
mstateen_val |= SMSTATEEN0_HSENVCFG;
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_AIA))
|
||||
mstateen_val |= (SMSTATEEN0_AIA | SMSTATEEN0_SVSLCT |
|
||||
SMSTATEEN0_IMSIC);
|
||||
else
|
||||
mstateen_val &= ~(SMSTATEEN0_AIA | SMSTATEEN0_SVSLCT |
|
||||
SMSTATEEN0_IMSIC);
|
||||
csr_write(CSR_MSTATEEN0, mstateen_val);
|
||||
#if __riscv_xlen == 32
|
||||
csr_write(CSR_MSTATEEN0H, mstateen_val >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12) {
|
||||
menvcfg_val = csr_read(CSR_MENVCFG);
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBZE == 1
|
||||
*
|
||||
* If Zicboz extension is not available then writes to
|
||||
* menvcfg.CBZE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBZE;
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBCFE == 1
|
||||
*
|
||||
* If Zicbom extension is not available then writes to
|
||||
* menvcfg.CBCFE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBCFE;
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBIE == 3
|
||||
*
|
||||
* If Zicbom extension is not available then writes to
|
||||
* menvcfg.CBIE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBIE_INV << ENVCFG_CBIE_SHIFT;
|
||||
|
||||
/*
|
||||
* Set menvcfg.PBMTE == 1 for RV64 or RV128
|
||||
*
|
||||
* If Svpbmt extension is not available then menvcfg.PBMTE
|
||||
* will be read-only zero.
|
||||
*/
|
||||
#if __riscv_xlen > 32
|
||||
menvcfg_val |= ENVCFG_PBMTE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The spec doesn't explicitly describe the reset value of menvcfg.
|
||||
* Enable access to stimecmp if sstc extension is present in the
|
||||
* hardware.
|
||||
*/
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSTC)) {
|
||||
#if __riscv_xlen == 32
|
||||
unsigned long menvcfgh_val;
|
||||
menvcfgh_val = csr_read(CSR_MENVCFGH);
|
||||
menvcfgh_val |= ENVCFGH_STCE;
|
||||
csr_write(CSR_MENVCFGH, menvcfgh_val);
|
||||
#else
|
||||
menvcfg_val |= ENVCFG_STCE;
|
||||
#endif
|
||||
}
|
||||
|
||||
csr_write(CSR_MENVCFG, menvcfg_val);
|
||||
}
|
||||
|
||||
/* Disable all interrupts */
|
||||
csr_write(CSR_MIE, 0);
|
||||
|
||||
@@ -208,9 +97,6 @@ static int delegate_traps(struct sbi_scratch *scratch)
|
||||
|
||||
/* Send M-mode interrupts and most exceptions to S-mode */
|
||||
interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
interrupts |= MIP_LCOFIP;
|
||||
|
||||
exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
|
||||
(1U << CAUSE_USER_ECALL);
|
||||
if (sbi_platform_has_mfaults_delegation(plat))
|
||||
@@ -246,10 +132,17 @@ void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
|
||||
/* No delegation possible as mideleg does not exist*/
|
||||
return;
|
||||
|
||||
sbi_printf("%sMIDELEG%s: 0x%" PRILX "\n",
|
||||
#if __riscv_xlen == 32
|
||||
sbi_printf("%sMIDELEG%s: 0x%08lx\n",
|
||||
prefix, suffix, csr_read(CSR_MIDELEG));
|
||||
sbi_printf("%sMEDELEG%s: 0x%" PRILX "\n",
|
||||
sbi_printf("%sMEDELEG%s: 0x%08lx\n",
|
||||
prefix, suffix, csr_read(CSR_MEDELEG));
|
||||
#else
|
||||
sbi_printf("%sMIDELEG%s: 0x%016lx\n",
|
||||
prefix, suffix, csr_read(CSR_MIDELEG));
|
||||
sbi_printf("%sMEDELEG%s: 0x%016lx\n",
|
||||
prefix, suffix, csr_read(CSR_MEDELEG));
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int sbi_hart_mhpm_count(struct sbi_scratch *scratch)
|
||||
@@ -284,14 +177,6 @@ unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch)
|
||||
return hfeatures->pmp_addr_bits;
|
||||
}
|
||||
|
||||
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
return hfeatures->mhpm_bits;
|
||||
}
|
||||
|
||||
int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_domain_memregion *reg;
|
||||
@@ -326,7 +211,7 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
|
||||
pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
|
||||
else {
|
||||
sbi_printf("Can not configure pmp for domain %s", dom->name);
|
||||
sbi_printf(" because memory region address %lx or size %lx is not in range\n",
|
||||
sbi_printf("because memory region address %lx or size %lx is not in range\n",
|
||||
reg->base, reg->order);
|
||||
}
|
||||
}
|
||||
@@ -334,156 +219,97 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_hart_priv_version(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
return hfeatures->priv_version;
|
||||
}
|
||||
|
||||
void sbi_hart_get_priv_version_str(struct sbi_scratch *scratch,
|
||||
char *version_str, int nvstr)
|
||||
{
|
||||
char *temp;
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
switch (hfeatures->priv_version) {
|
||||
case SBI_HART_PRIV_VER_1_10:
|
||||
temp = "v1.10";
|
||||
break;
|
||||
case SBI_HART_PRIV_VER_1_11:
|
||||
temp = "v1.11";
|
||||
break;
|
||||
case SBI_HART_PRIV_VER_1_12:
|
||||
temp = "v1.12";
|
||||
break;
|
||||
default:
|
||||
temp = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
sbi_snprintf(version_str, nvstr, "%s", temp);
|
||||
}
|
||||
|
||||
static inline void __sbi_hart_update_extension(
|
||||
struct hart_features *hfeatures,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable)
|
||||
{
|
||||
if (enable)
|
||||
hfeatures->extensions |= BIT(ext);
|
||||
else
|
||||
hfeatures->extensions &= ~BIT(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable a particular hart extension
|
||||
* Check whether a particular hart feature is available
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param ext the extension number to check
|
||||
* @param enable new state of hart extension
|
||||
* @param feature the feature to check
|
||||
* @returns true (feature available) or false (feature not available)
|
||||
*/
|
||||
void sbi_hart_update_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable)
|
||||
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
__sbi_hart_update_extension(hfeatures, ext, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a particular hart extension is available
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param ext the extension number to check
|
||||
* @returns true (available) or false (not available)
|
||||
*/
|
||||
bool sbi_hart_has_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
if (hfeatures->extensions & BIT(ext))
|
||||
if (hfeatures->features & feature)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline char *sbi_hart_extension_id2string(int ext)
|
||||
static unsigned long hart_get_features(struct sbi_scratch *scratch)
|
||||
{
|
||||
char *estr = NULL;
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
switch (ext) {
|
||||
case SBI_HART_EXT_SSCOFPMF:
|
||||
estr = "sscofpmf";
|
||||
return hfeatures->features;
|
||||
}
|
||||
|
||||
static inline char *sbi_hart_feature_id2string(unsigned long feature)
|
||||
{
|
||||
char *fstr = NULL;
|
||||
|
||||
if (!feature)
|
||||
return NULL;
|
||||
|
||||
switch (feature) {
|
||||
case SBI_HART_HAS_SCOUNTEREN:
|
||||
fstr = "scounteren";
|
||||
break;
|
||||
case SBI_HART_EXT_TIME:
|
||||
estr = "time";
|
||||
case SBI_HART_HAS_MCOUNTEREN:
|
||||
fstr = "mcounteren";
|
||||
break;
|
||||
case SBI_HART_EXT_AIA:
|
||||
estr = "aia";
|
||||
break;
|
||||
case SBI_HART_EXT_SSTC:
|
||||
estr = "sstc";
|
||||
break;
|
||||
case SBI_HART_EXT_SMSTATEEN:
|
||||
estr = "smstateen";
|
||||
case SBI_HART_HAS_TIME:
|
||||
fstr = "time";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return estr;
|
||||
return fstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hart extensions in string format
|
||||
* Get the hart features in string format
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param extensions_str pointer to a char array where the extensions string
|
||||
* will be updated
|
||||
* @param nestr length of the features_str. The feature string will be
|
||||
* truncated if nestr is not long enough.
|
||||
* @param features_str pointer to a char array where the features string will be
|
||||
* updated
|
||||
* @param nfstr length of the features_str. The feature string will be truncated
|
||||
* if nfstr is not long enough.
|
||||
*/
|
||||
void sbi_hart_get_extensions_str(struct sbi_scratch *scratch,
|
||||
char *extensions_str, int nestr)
|
||||
void sbi_hart_get_features_str(struct sbi_scratch *scratch,
|
||||
char *features_str, int nfstr)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
int offset = 0, ext = 0;
|
||||
unsigned long features, feat = 1UL;
|
||||
char *temp;
|
||||
int offset = 0;
|
||||
|
||||
if (!extensions_str || nestr <= 0)
|
||||
if (!features_str || nfstr <= 0)
|
||||
return;
|
||||
sbi_memset(extensions_str, 0, nestr);
|
||||
sbi_memset(features_str, 0, nfstr);
|
||||
|
||||
if (!hfeatures->extensions)
|
||||
features = hart_get_features(scratch);
|
||||
if (!features)
|
||||
goto done;
|
||||
|
||||
do {
|
||||
if (hfeatures->extensions & BIT(ext)) {
|
||||
temp = sbi_hart_extension_id2string(ext);
|
||||
if (features & feat) {
|
||||
temp = sbi_hart_feature_id2string(feat);
|
||||
if (temp) {
|
||||
sbi_snprintf(extensions_str + offset,
|
||||
nestr - offset,
|
||||
sbi_snprintf(features_str + offset, nfstr,
|
||||
"%s,", temp);
|
||||
offset = offset + sbi_strlen(temp) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
ext++;
|
||||
} while (ext < SBI_HART_EXT_MAX);
|
||||
feat = feat << 1;
|
||||
} while (feat <= SBI_HART_HAS_LAST_FEATURE);
|
||||
|
||||
done:
|
||||
if (offset)
|
||||
extensions_str[offset - 1] = '\0';
|
||||
features_str[offset - 1] = '\0';
|
||||
else
|
||||
sbi_strncpy(extensions_str, "none", nestr);
|
||||
sbi_strncpy(features_str, "none", nfstr);
|
||||
}
|
||||
|
||||
static unsigned long hart_pmp_get_allowed_addr(void)
|
||||
@@ -491,11 +317,7 @@ static unsigned long hart_pmp_get_allowed_addr(void)
|
||||
unsigned long val = 0;
|
||||
struct sbi_trap_info trap = {0};
|
||||
|
||||
csr_write_allowed(CSR_PMPCFG0, (ulong)&trap, 0);
|
||||
if (trap.cause)
|
||||
return 0;
|
||||
|
||||
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK);
|
||||
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK); \
|
||||
if (!trap.cause) {
|
||||
val = csr_read_allowed(CSR_PMPADDR0, (ulong)&trap);
|
||||
if (trap.cause)
|
||||
@@ -505,63 +327,27 @@ static unsigned long hart_pmp_get_allowed_addr(void)
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hart_pmu_get_allowed_bits(void)
|
||||
{
|
||||
unsigned long val = ~(0UL);
|
||||
struct sbi_trap_info trap = {0};
|
||||
int num_bits = 0;
|
||||
|
||||
/**
|
||||
* It is assumed that platforms will implement same number of bits for
|
||||
* all the performance counters including mcycle/minstret.
|
||||
*/
|
||||
csr_write_allowed(CSR_MHPMCOUNTER3, (ulong)&trap, val);
|
||||
if (!trap.cause) {
|
||||
val = csr_read_allowed(CSR_MHPMCOUNTER3, (ulong)&trap);
|
||||
if (trap.cause)
|
||||
return 0;
|
||||
}
|
||||
num_bits = sbi_fls(val) + 1;
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_allowed(CSR_MHPMCOUNTER3H, (ulong)&trap, val);
|
||||
if (!trap.cause) {
|
||||
val = csr_read_allowed(CSR_MHPMCOUNTER3H, (ulong)&trap);
|
||||
if (trap.cause)
|
||||
return num_bits;
|
||||
}
|
||||
num_bits += sbi_fls(val) + 1;
|
||||
|
||||
#endif
|
||||
|
||||
return num_bits;
|
||||
}
|
||||
|
||||
static int hart_detect_features(struct sbi_scratch *scratch)
|
||||
static void hart_detect_features(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_trap_info trap = {0};
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
unsigned long val, oldval;
|
||||
int rc;
|
||||
struct hart_features *hfeatures;
|
||||
unsigned long val;
|
||||
|
||||
/* If hart features already detected then do nothing */
|
||||
if (hfeatures->detected)
|
||||
return 0;
|
||||
|
||||
/* Clear hart features */
|
||||
hfeatures->extensions = 0;
|
||||
/* Reset hart features */
|
||||
hfeatures = sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
hfeatures->features = 0;
|
||||
hfeatures->pmp_count = 0;
|
||||
hfeatures->mhpm_count = 0;
|
||||
|
||||
#define __check_csr(__csr, __rdonly, __wrval, __field, __skip) \
|
||||
oldval = csr_read_allowed(__csr, (ulong)&trap); \
|
||||
val = csr_read_allowed(__csr, (ulong)&trap); \
|
||||
if (!trap.cause) { \
|
||||
if (__rdonly) { \
|
||||
(hfeatures->__field)++; \
|
||||
} else { \
|
||||
csr_write_allowed(__csr, (ulong)&trap, __wrval);\
|
||||
if (!trap.cause) { \
|
||||
if (csr_swap(__csr, oldval) == __wrval) \
|
||||
if (csr_swap(__csr, val) == __wrval) \
|
||||
(hfeatures->__field)++; \
|
||||
else \
|
||||
goto __skip; \
|
||||
@@ -597,8 +383,8 @@ static int hart_detect_features(struct sbi_scratch *scratch)
|
||||
*/
|
||||
val = hart_pmp_get_allowed_addr();
|
||||
if (val) {
|
||||
hfeatures->pmp_gran = 1 << (sbi_ffs(val) + 2);
|
||||
hfeatures->pmp_addr_bits = sbi_fls(val) + 1;
|
||||
hfeatures->pmp_gran = 1 << (__ffs(val) + 2);
|
||||
hfeatures->pmp_addr_bits = __fls(val) + 1;
|
||||
/* Detect number of PMP regions. At least PMPADDR0 should be implemented*/
|
||||
__check_csr_64(CSR_PMPADDR0, 0, val, pmp_count, __pmp_skip);
|
||||
}
|
||||
@@ -606,17 +392,9 @@ __pmp_skip:
|
||||
|
||||
/* Detect number of MHPM counters */
|
||||
__check_csr(CSR_MHPMCOUNTER3, 0, 1UL, mhpm_count, __mhpm_skip);
|
||||
hfeatures->mhpm_bits = hart_pmu_get_allowed_bits();
|
||||
|
||||
__check_csr_4(CSR_MHPMCOUNTER4, 0, 1UL, mhpm_count, __mhpm_skip);
|
||||
__check_csr_8(CSR_MHPMCOUNTER8, 0, 1UL, mhpm_count, __mhpm_skip);
|
||||
__check_csr_16(CSR_MHPMCOUNTER16, 0, 1UL, mhpm_count, __mhpm_skip);
|
||||
|
||||
/**
|
||||
* No need to check for MHPMCOUNTERH for RV32 as they are expected to be
|
||||
* implemented if MHPMCOUNTER is implemented.
|
||||
*/
|
||||
|
||||
__mhpm_skip:
|
||||
|
||||
#undef __check_csr_64
|
||||
@@ -627,69 +405,26 @@ __mhpm_skip:
|
||||
#undef __check_csr_2
|
||||
#undef __check_csr
|
||||
|
||||
/* Detect if hart supports Priv v1.10 */
|
||||
val = csr_read_allowed(CSR_MCOUNTEREN, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_10;
|
||||
|
||||
/* Detect if hart supports Priv v1.11 */
|
||||
val = csr_read_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap);
|
||||
if (!trap.cause &&
|
||||
(hfeatures->priv_version >= SBI_HART_PRIV_VER_1_10))
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_11;
|
||||
|
||||
/* Detect if hart supports Priv v1.12 */
|
||||
csr_read_allowed(CSR_MENVCFG, (unsigned long)&trap);
|
||||
if (!trap.cause &&
|
||||
(hfeatures->priv_version >= SBI_HART_PRIV_VER_1_11))
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_12;
|
||||
|
||||
/* Counter overflow/filtering is not useful without mcounter/inhibit */
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
/* Detect if hart supports sscofpmf */
|
||||
csr_read_allowed(CSR_SCOUNTOVF, (unsigned long)&trap);
|
||||
/* Detect if hart supports SCOUNTEREN feature */
|
||||
val = csr_read_allowed(CSR_SCOUNTEREN, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_SCOUNTEREN, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SSCOFPMF, true);
|
||||
hfeatures->features |= SBI_HART_HAS_SCOUNTEREN;
|
||||
}
|
||||
|
||||
/* Detect if hart supports MCOUNTEREN feature */
|
||||
val = csr_read_allowed(CSR_MCOUNTEREN, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_MCOUNTEREN, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_MCOUNTEREN;
|
||||
}
|
||||
|
||||
/* Detect if hart supports time CSR */
|
||||
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_TIME, true);
|
||||
|
||||
/* Detect if hart has AIA local interrupt CSRs */
|
||||
csr_read_allowed(CSR_MTOPI, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_AIA, true);
|
||||
|
||||
/* Detect if hart supports stimecmp CSR(Sstc extension) */
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
csr_read_allowed(CSR_STIMECMP, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SSTC, true);
|
||||
}
|
||||
|
||||
/* Detect if hart supports mstateen CSRs */
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
val = csr_read_allowed(CSR_MSTATEEN0, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SMSTATEEN, true);
|
||||
}
|
||||
|
||||
/* Let platform populate extensions */
|
||||
rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr());
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Mark hart feature detection done */
|
||||
hfeatures->detected = true;
|
||||
|
||||
return 0;
|
||||
hfeatures->features |= SBI_HART_HAS_TIME;
|
||||
}
|
||||
|
||||
int sbi_hart_reinit(struct sbi_scratch *scratch)
|
||||
@@ -711,21 +446,18 @@ int sbi_hart_reinit(struct sbi_scratch *scratch)
|
||||
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (cold_boot) {
|
||||
if (misa_extension('H'))
|
||||
sbi_hart_expected_trap = &__sbi_expected_trap_hext;
|
||||
|
||||
hart_features_offset = sbi_scratch_alloc_offset(
|
||||
sizeof(struct hart_features));
|
||||
sizeof(struct hart_features),
|
||||
"HART_FEATURES");
|
||||
if (!hart_features_offset)
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
|
||||
rc = hart_detect_features(scratch);
|
||||
if (rc)
|
||||
return rc;
|
||||
hart_detect_features(scratch);
|
||||
|
||||
return sbi_hart_reinit(scratch);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
.global __sbi_hfence_gvma_vmid_gpa
|
||||
__sbi_hfence_gvma_vmid_gpa:
|
||||
/*
|
||||
* rs1 = a0 (GPA >> 2)
|
||||
* rs1 = a0 (GPA)
|
||||
* rs2 = a1 (VMID)
|
||||
* HFENCE.GVMA a0, a1
|
||||
* 0110001 01011 01010 000 00000 1110011
|
||||
@@ -51,7 +51,7 @@ __sbi_hfence_gvma_vmid:
|
||||
.global __sbi_hfence_gvma_gpa
|
||||
__sbi_hfence_gvma_gpa:
|
||||
/*
|
||||
* rs1 = a0 (GPA >> 2)
|
||||
* rs1 = a0 (GPA)
|
||||
* rs2 = zero
|
||||
* HFENCE.GVMA a0
|
||||
* 0110001 00000 01010 000 00000 1110011
|
||||
|
||||
@@ -36,8 +36,25 @@ struct sbi_hsm_data {
|
||||
unsigned long saved_mie;
|
||||
unsigned long saved_mip;
|
||||
};
|
||||
#define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \
|
||||
({ \
|
||||
long state = atomic_cmpxchg(&(hdata)->state, oldstate, newstate); \
|
||||
if (state != (oldstate)) \
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%lu]\n", \
|
||||
__func__, state); \
|
||||
state == (oldstate); \
|
||||
})
|
||||
|
||||
static inline int __sbi_hsm_hart_get_state(u32 hartid)
|
||||
int sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate,
|
||||
long newstate)
|
||||
{
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
|
||||
return __sbi_hsm_hart_change_state(hdata, oldstate, newstate);
|
||||
}
|
||||
|
||||
int __sbi_hsm_hart_get_state(u32 hartid)
|
||||
{
|
||||
struct sbi_hsm_data *hdata;
|
||||
struct sbi_scratch *scratch;
|
||||
@@ -113,8 +130,8 @@ static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Save MIE CSR */
|
||||
saved_mie = csr_read(CSR_MIE);
|
||||
|
||||
/* Set MSIE and MEIE bits to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
|
||||
/* Set MSIE bit to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
|
||||
/* Wait for hart_add call*/
|
||||
while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) {
|
||||
@@ -171,19 +188,13 @@ static int hsm_device_hart_stop(void)
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
static int hsm_device_hart_suspend(u32 suspend_type)
|
||||
static int hsm_device_hart_suspend(u32 suspend_type, ulong raddr)
|
||||
{
|
||||
if (hsm_dev && hsm_dev->hart_suspend)
|
||||
return hsm_dev->hart_suspend(suspend_type);
|
||||
return hsm_dev->hart_suspend(suspend_type, raddr);
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
static void hsm_device_hart_resume(void)
|
||||
{
|
||||
if (hsm_dev && hsm_dev->hart_resume)
|
||||
hsm_dev->hart_resume();
|
||||
}
|
||||
|
||||
int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
{
|
||||
u32 i;
|
||||
@@ -191,7 +202,8 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
struct sbi_hsm_data *hdata;
|
||||
|
||||
if (cold_boot) {
|
||||
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata));
|
||||
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata),
|
||||
"HART_DATA");
|
||||
if (!hart_data_offset)
|
||||
return SBI_ENOMEM;
|
||||
|
||||
@@ -319,7 +331,7 @@ int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sbi_hsm_suspend_default(struct sbi_scratch *scratch)
|
||||
static int __sbi_hsm_suspend_ret_default(struct sbi_scratch *scratch)
|
||||
{
|
||||
/* Wait for interrupt */
|
||||
wfi();
|
||||
@@ -327,7 +339,7 @@ static int __sbi_hsm_suspend_default(struct sbi_scratch *scratch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch)
|
||||
void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
@@ -359,6 +371,29 @@ static void __sbi_hsm_suspend_non_ret_restore(struct sbi_scratch *scratch)
|
||||
csr_write(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP)));
|
||||
}
|
||||
|
||||
static int __sbi_hsm_suspend_non_ret_default(struct sbi_scratch *scratch,
|
||||
ulong raddr)
|
||||
{
|
||||
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
|
||||
|
||||
/*
|
||||
* Save some of the M-mode CSRs which should be restored after
|
||||
* resuming from suspend state
|
||||
*/
|
||||
__sbi_hsm_suspend_non_ret_save(scratch);
|
||||
|
||||
/* Wait for interrupt */
|
||||
wfi();
|
||||
|
||||
/*
|
||||
* Directly jump to warm reboot to simulate resume from a
|
||||
* non-retentive suspend.
|
||||
*/
|
||||
jump_warmboot();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch)
|
||||
{
|
||||
int oldstate;
|
||||
@@ -373,8 +408,6 @@ void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch)
|
||||
__func__, oldstate);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
hsm_device_hart_resume();
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch)
|
||||
@@ -448,36 +481,18 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
/* Save the suspend type */
|
||||
hdata->suspend_type = suspend_type;
|
||||
|
||||
/*
|
||||
* Save context which will be restored after resuming from
|
||||
* non-retentive suspend.
|
||||
*/
|
||||
if (suspend_type & SBI_HSM_SUSP_NON_RET_BIT)
|
||||
__sbi_hsm_suspend_non_ret_save(scratch);
|
||||
|
||||
/* Try platform specific suspend */
|
||||
ret = hsm_device_hart_suspend(suspend_type);
|
||||
ret = hsm_device_hart_suspend(suspend_type, scratch->warmboot_addr);
|
||||
if (ret == SBI_ENOTSUPP) {
|
||||
/* Try generic implementation of default suspend types */
|
||||
if (suspend_type == SBI_HSM_SUSPEND_RET_DEFAULT ||
|
||||
suspend_type == SBI_HSM_SUSPEND_NON_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_default(scratch);
|
||||
if (suspend_type == SBI_HSM_SUSPEND_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_ret_default(scratch);
|
||||
} else if (suspend_type == SBI_HSM_SUSPEND_NON_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_non_ret_default(scratch,
|
||||
scratch->warmboot_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The platform may have coordinated a retentive suspend, or it may
|
||||
* have exited early from a non-retentive suspend. Either way, the
|
||||
* caller is not expecting a successful return, so jump to the warm
|
||||
* boot entry point to simulate resume from a non-retentive suspend.
|
||||
*/
|
||||
if (ret == 0 && (suspend_type & SBI_HSM_SUSP_NON_RET_BIT)) {
|
||||
void (*jump_warmboot)(void) =
|
||||
(void (*)(void))scratch->warmboot_addr;
|
||||
|
||||
jump_warmboot();
|
||||
}
|
||||
|
||||
fail_restore_state:
|
||||
/*
|
||||
* We might have successfully resumed from retentive suspend
|
||||
|
||||
@@ -8,16 +8,13 @@
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_emulate_csr.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_illegal_insn.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_unpriv.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
typedef int (*illegal_insn_func)(ulong insn, struct sbi_trap_regs *regs);
|
||||
|
||||
@@ -34,31 +31,13 @@ static int truly_illegal_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
return sbi_trap_redirect(regs, &trap);
|
||||
}
|
||||
|
||||
static int misc_mem_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
{
|
||||
/* Errata workaround: emulate `fence.tso` as `fence rw, rw`. */
|
||||
if ((insn & INSN_MASK_FENCE_TSO) == INSN_MATCH_FENCE_TSO) {
|
||||
smp_mb();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return truly_illegal_insn(insn, regs);
|
||||
}
|
||||
|
||||
static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
{
|
||||
int do_write, rs1_num = (insn >> 15) & 0x1f;
|
||||
ulong rs1_val = GET_RS1(insn, regs);
|
||||
int csr_num = (u32)insn >> 20;
|
||||
ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
ulong csr_val, new_csr_val;
|
||||
|
||||
if (prev_mode == PRV_M) {
|
||||
sbi_printf("%s: Failed to access CSR %#x from M-mode",
|
||||
__func__, csr_num);
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
|
||||
/* TODO: Ensure that we got CSR read/write instruction */
|
||||
|
||||
if (sbi_emulate_csr_read(csr_num, regs, &csr_val))
|
||||
@@ -100,11 +79,11 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const illegal_insn_func illegal_insn_table[32] = {
|
||||
static illegal_insn_func illegal_insn_table[32] = {
|
||||
truly_illegal_insn, /* 0 */
|
||||
truly_illegal_insn, /* 1 */
|
||||
truly_illegal_insn, /* 2 */
|
||||
misc_mem_opcode_insn, /* 3 */
|
||||
truly_illegal_insn, /* 3 */
|
||||
truly_illegal_insn, /* 4 */
|
||||
truly_illegal_insn, /* 5 */
|
||||
truly_illegal_insn, /* 6 */
|
||||
@@ -150,7 +129,6 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
|
||||
* instruction trap.
|
||||
*/
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_ILLEGAL_INSN);
|
||||
if (unlikely((insn & 3) != 3)) {
|
||||
insn = sbi_get_insn(regs->mepc, &uptrap);
|
||||
if (uptrap.cause) {
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
@@ -49,14 +47,6 @@ static void sbi_boot_print_banner(struct sbi_scratch *scratch)
|
||||
OPENSBI_VERSION_MINOR);
|
||||
#endif
|
||||
|
||||
#ifdef OPENSBI_BUILD_TIME_STAMP
|
||||
sbi_printf("Build time: %s\n", OPENSBI_BUILD_TIME_STAMP);
|
||||
#endif
|
||||
|
||||
#ifdef OPENSBI_BUILD_COMPILER_VERSION
|
||||
sbi_printf("Build compiler: %s\n", OPENSBI_BUILD_COMPILER_VERSION);
|
||||
#endif
|
||||
|
||||
sbi_printf(BANNER);
|
||||
}
|
||||
|
||||
@@ -68,6 +58,7 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
|
||||
const struct sbi_timer_device *tdev;
|
||||
const struct sbi_console_device *cdev;
|
||||
const struct sbi_system_reset_device *srdev;
|
||||
const struct sbi_system_suspend_device *susp_dev;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS)
|
||||
@@ -84,21 +75,20 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
|
||||
sbi_printf("Platform IPI Device : %s\n",
|
||||
(idev) ? idev->name : "---");
|
||||
tdev = sbi_timer_get_device();
|
||||
sbi_printf("Platform Timer Device : %s @ %luHz\n",
|
||||
(tdev) ? tdev->name : "---",
|
||||
(tdev) ? tdev->timer_freq : 0);
|
||||
sbi_printf("Platform Timer Device : %s\n",
|
||||
(tdev) ? tdev->name : "---");
|
||||
cdev = sbi_console_get_device();
|
||||
sbi_printf("Platform Console Device : %s\n",
|
||||
(cdev) ? cdev->name : "---");
|
||||
hdev = sbi_hsm_get_device();
|
||||
sbi_printf("Platform HSM Device : %s\n",
|
||||
(hdev) ? hdev->name : "---");
|
||||
srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_COLD_REBOOT, 0);
|
||||
sbi_printf("Platform Reboot Device : %s\n",
|
||||
(srdev) ? srdev->name : "---");
|
||||
srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_SHUTDOWN, 0);
|
||||
sbi_printf("Platform Shutdown Device : %s\n",
|
||||
srdev = sbi_system_reset_get_device();
|
||||
sbi_printf("Platform SysReset Device : %s\n",
|
||||
(srdev) ? srdev->name : "---");
|
||||
susp_dev = sbi_system_suspend_get_device();
|
||||
sbi_printf("Platform Suspend Device : %s\n",
|
||||
(susp_dev) ? susp_dev->name : "---");
|
||||
|
||||
/* Firmware details */
|
||||
sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start);
|
||||
@@ -139,12 +129,10 @@ static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Boot HART details */
|
||||
sbi_printf("Boot HART ID : %u\n", hartid);
|
||||
sbi_printf("Boot HART Domain : %s\n", dom->name);
|
||||
sbi_hart_get_priv_version_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART Priv Version : %s\n", str);
|
||||
misa_string(xlen, str, sizeof(str));
|
||||
sbi_printf("Boot HART Base ISA : %s\n", str);
|
||||
sbi_hart_get_extensions_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART ISA Extensions : %s\n", str);
|
||||
sbi_printf("Boot HART ISA : %s\n", str);
|
||||
sbi_hart_get_features_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART Features : %s\n", str);
|
||||
sbi_printf("Boot HART PMP Count : %d\n",
|
||||
sbi_hart_pmp_count(scratch));
|
||||
sbi_printf("Boot HART PMP Granularity : %lu\n",
|
||||
@@ -153,6 +141,8 @@ static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_hart_pmp_addrbits(scratch));
|
||||
sbi_printf("Boot HART MHPM Count : %d\n",
|
||||
sbi_hart_mhpm_count(scratch));
|
||||
sbi_printf("Boot HART MHPM Count : %d\n",
|
||||
sbi_hart_mhpm_count(scratch));
|
||||
sbi_hart_delegation_dump(scratch, "Boot HART ", " ");
|
||||
}
|
||||
|
||||
@@ -168,8 +158,8 @@ static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Save MIE CSR */
|
||||
saved_mie = csr_read(CSR_MIE);
|
||||
|
||||
/* Set MSIE and MEIE bits to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
|
||||
/* Set MSIE bit to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
|
||||
/* Acquire coldboot lock */
|
||||
spin_lock(&coldboot_lock);
|
||||
@@ -185,7 +175,7 @@ static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
do {
|
||||
wfi();
|
||||
cmip = csr_read(CSR_MIP);
|
||||
} while (!(cmip & (MIP_MSIP | MIP_MEIP)));
|
||||
} while (!(cmip & MIP_MSIP));
|
||||
};
|
||||
|
||||
/* Acquire coldboot lock */
|
||||
@@ -219,7 +209,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
|
||||
spin_lock(&coldboot_lock);
|
||||
|
||||
/* Send an IPI to all HARTs waiting for coldboot */
|
||||
for (u32 i = 0; i <= sbi_scratch_last_hartid(); i++) {
|
||||
for (int i = 0; i <= sbi_scratch_last_hartid(); i++) {
|
||||
if ((i != hartid) &&
|
||||
sbi_hartmask_test_hart(i, &coldboot_wait_hmask))
|
||||
sbi_ipi_raw_send(i);
|
||||
@@ -235,7 +225,9 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
{
|
||||
int rc;
|
||||
unsigned long *init_count;
|
||||
#ifndef CONFIG_OPENSBI_FASTBOOT
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
#endif
|
||||
|
||||
/* Note: This has to be first thing in coldboot init sequence */
|
||||
rc = sbi_scratch_init(scratch);
|
||||
@@ -247,7 +239,8 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__);
|
||||
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,
|
||||
"INIT_COUNT");
|
||||
if (!init_count_offset)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -255,27 +248,28 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
#ifndef CONFIG_OPENSBI_FASTBOOT
|
||||
rc = sbi_platform_early_init(plat, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
#endif
|
||||
|
||||
rc = sbi_hart_init(scratch, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
#ifndef CONFIG_OPENSBI_FASTBOOT
|
||||
rc = sbi_console_init(scratch);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_pmu_init(scratch, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
#endif
|
||||
|
||||
sbi_boot_print_banner(scratch);
|
||||
|
||||
rc = sbi_irqchip_init(scratch, TRUE);
|
||||
#ifndef CONFIG_OPENSBI_FASTBOOT
|
||||
rc = sbi_platform_irqchip_init(plat, TRUE);
|
||||
if (rc) {
|
||||
sbi_printf("%s: irqchip init failed (error %d)\n",
|
||||
sbi_printf("%s: platform irqchip init failed (error %d)\n",
|
||||
__func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
@@ -285,6 +279,7 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_printf("%s: ipi init failed (error %d)\n", __func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = sbi_tlb_init(scratch, TRUE);
|
||||
if (rc) {
|
||||
@@ -304,6 +299,8 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
sbi_boot_print_general(scratch);
|
||||
|
||||
/*
|
||||
* Note: Finalize domains after HSM initialization so that we
|
||||
* can startup non-root domains.
|
||||
@@ -317,6 +314,8 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
sbi_boot_print_domains(scratch);
|
||||
|
||||
rc = sbi_hart_pmp_configure(scratch);
|
||||
if (rc) {
|
||||
sbi_printf("%s: PMP configure failed (error %d)\n",
|
||||
@@ -328,16 +327,14 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
* Note: Platform final initialization should be last so that
|
||||
* it sees correct domain assignment and PMP configuration.
|
||||
*/
|
||||
#ifndef CONFIG_OPENSBI_FASTBOOT
|
||||
rc = sbi_platform_final_init(plat, TRUE);
|
||||
if (rc) {
|
||||
sbi_printf("%s: platform final init failed (error %d)\n",
|
||||
__func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
sbi_boot_print_general(scratch);
|
||||
|
||||
sbi_boot_print_domains(scratch);
|
||||
#endif
|
||||
|
||||
sbi_boot_print_hart(scratch, hartid);
|
||||
|
||||
@@ -372,11 +369,7 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_pmu_init(scratch, FALSE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_irqchip_init(scratch, FALSE);
|
||||
rc = sbi_platform_irqchip_init(plat, FALSE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -497,14 +490,6 @@ void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0)
|
||||
coldboot = TRUE;
|
||||
|
||||
/*
|
||||
* Do platform specific nascent (very early) initialization so
|
||||
* that platform can initialize platform specific per-HART CSRs
|
||||
* or per-HART devices.
|
||||
*/
|
||||
if (sbi_platform_nascent_init(plat))
|
||||
sbi_hart_hang();
|
||||
|
||||
if (coldboot)
|
||||
init_coldboot(scratch, hartid);
|
||||
else
|
||||
@@ -547,13 +532,11 @@ void __noreturn sbi_exit(struct sbi_scratch *scratch)
|
||||
|
||||
sbi_platform_early_exit(plat);
|
||||
|
||||
sbi_pmu_exit(scratch);
|
||||
|
||||
sbi_timer_exit(scratch);
|
||||
|
||||
sbi_ipi_exit(scratch);
|
||||
|
||||
sbi_irqchip_exit(scratch);
|
||||
sbi_platform_irqchip_exit(plat);
|
||||
|
||||
sbi_platform_final_exit(plat);
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <sbi/sbi_init.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_tlb.h>
|
||||
|
||||
struct sbi_ipi_data {
|
||||
unsigned long ipi_type;
|
||||
@@ -67,8 +64,6 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
|
||||
if (ipi_dev && ipi_dev->ipi_send)
|
||||
ipi_dev->ipi_send(remote_hartid);
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_SENT);
|
||||
|
||||
if (ipi_ops->sync)
|
||||
ipi_ops->sync(scratch);
|
||||
|
||||
@@ -188,7 +183,6 @@ void sbi_ipi_process(void)
|
||||
sbi_scratch_offset_ptr(scratch, ipi_data_off);
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_RECVD);
|
||||
if (ipi_dev && ipi_dev->ipi_clear)
|
||||
ipi_dev->ipi_clear(hartid);
|
||||
|
||||
@@ -233,7 +227,8 @@ int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
struct sbi_ipi_data *ipi_data;
|
||||
|
||||
if (cold_boot) {
|
||||
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data));
|
||||
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data),
|
||||
"IPI_DATA");
|
||||
if (!ipi_data_off)
|
||||
return SBI_ENOMEM;
|
||||
ret = sbi_ipi_event_create(&ipi_smode_ops);
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <apatel@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
|
||||
static int default_irqfn(struct sbi_trap_regs *regs)
|
||||
{
|
||||
return SBI_ENODEV;
|
||||
}
|
||||
|
||||
static int (*ext_irqfn)(struct sbi_trap_regs *regs) = default_irqfn;
|
||||
|
||||
void sbi_irqchip_set_irqfn(int (*fn)(struct sbi_trap_regs *regs))
|
||||
{
|
||||
if (fn)
|
||||
ext_irqfn = fn;
|
||||
}
|
||||
|
||||
int sbi_irqchip_process(struct sbi_trap_regs *regs)
|
||||
{
|
||||
return ext_irqfn(regs);
|
||||
}
|
||||
|
||||
int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
int rc;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
rc = sbi_platform_irqchip_init(plat, cold_boot);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (ext_irqfn != default_irqfn)
|
||||
csr_set(CSR_MIE, MIP_MEIP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_irqchip_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (ext_irqfn != default_irqfn)
|
||||
csr_clear(CSR_MIE, MIP_MEIP);
|
||||
|
||||
sbi_platform_irqchip_exit(plat);
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <sbi/riscv_fp.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_misaligned_ldst.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_unpriv.h>
|
||||
|
||||
@@ -22,18 +21,6 @@ union reg_data {
|
||||
u64 data_u64;
|
||||
};
|
||||
|
||||
static ulong sbi_misaligned_tinst_fixup(ulong orig_tinst, ulong new_tinst,
|
||||
ulong addr_offset)
|
||||
{
|
||||
if (new_tinst == INSN_PSEUDO_VS_LOAD ||
|
||||
new_tinst == INSN_PSEUDO_VS_STORE)
|
||||
return new_tinst;
|
||||
else if (orig_tinst == 0)
|
||||
return 0UL;
|
||||
else
|
||||
return orig_tinst | (addr_offset << SH_RS1);
|
||||
}
|
||||
|
||||
int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
struct sbi_trap_regs *regs)
|
||||
{
|
||||
@@ -42,8 +29,6 @@ int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
struct sbi_trap_info uptrap;
|
||||
int i, fp = 0, shift = 0, len = 0;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_MISALIGNED_LOAD);
|
||||
|
||||
if (tinst & 0x1) {
|
||||
/*
|
||||
* Bit[0] == 1 implies trapped instruction value is
|
||||
@@ -138,8 +123,6 @@ int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
&uptrap);
|
||||
if (uptrap.cause) {
|
||||
uptrap.epc = regs->mepc;
|
||||
uptrap.tinst = sbi_misaligned_tinst_fixup(
|
||||
tinst, uptrap.tinst, i);
|
||||
return sbi_trap_redirect(regs, &uptrap);
|
||||
}
|
||||
}
|
||||
@@ -166,8 +149,6 @@ int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
struct sbi_trap_info uptrap;
|
||||
int i, len = 0;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_MISALIGNED_STORE);
|
||||
|
||||
if (tinst & 0x1) {
|
||||
/*
|
||||
* Bit[0] == 1 implies trapped instruction value is
|
||||
@@ -252,8 +233,6 @@ int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
&uptrap);
|
||||
if (uptrap.cause) {
|
||||
uptrap.epc = regs->mepc;
|
||||
uptrap.tinst = sbi_misaligned_tinst_fixup(
|
||||
tinst, uptrap.tinst, i);
|
||||
return sbi_trap_redirect(regs, &uptrap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ static inline char *sbi_platform_feature_id2string(unsigned long feature)
|
||||
|
||||
switch (feature) {
|
||||
case SBI_PLATFORM_HAS_MFAULTS_DELEGATION:
|
||||
fstr = "medeleg";
|
||||
fstr = "mfdeleg";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -48,18 +48,9 @@ void sbi_platform_get_features_str(const struct sbi_platform *plat,
|
||||
if (features & feat) {
|
||||
temp = sbi_platform_feature_id2string(feat);
|
||||
if (temp) {
|
||||
int len = sbi_snprintf(features_str + offset,
|
||||
nfstr - offset,
|
||||
"%s,", temp);
|
||||
if (len < 0)
|
||||
break;
|
||||
|
||||
if (offset + len >= nfstr) {
|
||||
/* No more space for features */
|
||||
offset = nfstr;
|
||||
break;
|
||||
} else
|
||||
offset = offset + len;
|
||||
sbi_snprintf(features_str + offset, nfstr,
|
||||
"%s,", temp);
|
||||
offset = offset + sbi_strlen(temp) + 1;
|
||||
}
|
||||
}
|
||||
feat = feat << 1;
|
||||
|
||||
@@ -1,766 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Atish Patra <atish.patra@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
|
||||
/** Information about hardware counters */
|
||||
struct sbi_pmu_hw_event {
|
||||
uint32_t counters;
|
||||
uint32_t start_idx;
|
||||
uint32_t end_idx;
|
||||
/* Event selector value used only for raw events. The event select value
|
||||
* can be a even id or a selector value for set of events encoded in few
|
||||
* bits. In case latter, the bits used for encoding of the events should
|
||||
* be zeroed out in the select value.
|
||||
*/
|
||||
uint64_t select;
|
||||
/**
|
||||
* The select_mask indicates which bits are encoded for the event(s).
|
||||
*/
|
||||
uint64_t select_mask;
|
||||
};
|
||||
|
||||
/** Representation of a firmware event */
|
||||
struct sbi_pmu_fw_event {
|
||||
/* Event associated with the particular counter */
|
||||
uint32_t event_idx;
|
||||
|
||||
/* Current value of the counter */
|
||||
unsigned long curr_count;
|
||||
|
||||
/* A flag indicating pmu event monitoring is started */
|
||||
bool bStarted;
|
||||
};
|
||||
|
||||
/* Information about PMU counters as per SBI specification */
|
||||
union sbi_pmu_ctr_info {
|
||||
unsigned long value;
|
||||
struct {
|
||||
unsigned long csr:12;
|
||||
unsigned long width:6;
|
||||
#if __riscv_xlen == 32
|
||||
unsigned long reserved:13;
|
||||
#else
|
||||
unsigned long reserved:45;
|
||||
#endif
|
||||
unsigned long type:1;
|
||||
};
|
||||
};
|
||||
|
||||
/* Mapping between event range and possible counters */
|
||||
static struct sbi_pmu_hw_event hw_event_map[SBI_PMU_HW_EVENT_MAX] = {0};
|
||||
|
||||
/* counter to enabled event mapping */
|
||||
static uint32_t active_events[SBI_HARTMASK_MAX_BITS][SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX];
|
||||
|
||||
/* Contains all the information about firmwares events */
|
||||
static struct sbi_pmu_fw_event fw_event_map[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_EVENT_MAX] = {0};
|
||||
|
||||
/* Maximum number of hardware events available */
|
||||
static uint32_t num_hw_events;
|
||||
/* Maximum number of hardware counters available */
|
||||
static uint32_t num_hw_ctrs;
|
||||
|
||||
/* Maximum number of counters available */
|
||||
static uint32_t total_ctrs;
|
||||
|
||||
/* Helper macros to retrieve event idx and code type */
|
||||
#define get_cidx_type(x) ((x & SBI_PMU_EVENT_IDX_TYPE_MASK) >> 16)
|
||||
#define get_cidx_code(x) (x & SBI_PMU_EVENT_IDX_CODE_MASK)
|
||||
|
||||
/**
|
||||
* Perform a sanity check on event & counter mappings with event range overlap check
|
||||
* @param evtA Pointer to the existing hw event structure
|
||||
* @param evtB Pointer to the new hw event structure
|
||||
*
|
||||
* Return FALSE if the range doesn't overlap, TRUE otherwise
|
||||
*/
|
||||
static bool pmu_event_range_overlap(struct sbi_pmu_hw_event *evtA,
|
||||
struct sbi_pmu_hw_event *evtB)
|
||||
{
|
||||
/* check if the range of events overlap with a previous entry */
|
||||
if (((evtA->end_idx < evtB->start_idx) && (evtA->end_idx < evtB->end_idx)) ||
|
||||
((evtA->start_idx > evtB->start_idx) && (evtA->start_idx > evtB->end_idx)))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
|
||||
uint64_t select_val, uint64_t select_mask)
|
||||
{
|
||||
if ((evt->select == select_val) && (evt->select_mask == select_mask))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
|
||||
{
|
||||
uint32_t event_idx_val;
|
||||
uint32_t event_idx_type;
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
event_idx_val = active_events[hartid][cidx];
|
||||
|
||||
if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
|
||||
return SBI_EINVAL;
|
||||
|
||||
event_idx_type = get_cidx_type(event_idx_val);
|
||||
if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
|
||||
return SBI_EINVAL;
|
||||
|
||||
*event_idx_code = get_cidx_code(event_idx_val);
|
||||
|
||||
return event_idx_type;
|
||||
}
|
||||
|
||||
static int pmu_ctr_read_fw(uint32_t cidx, unsigned long *cval,
|
||||
uint32_t fw_evt_code)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
struct sbi_pmu_fw_event fevent;
|
||||
|
||||
fevent = fw_event_map[hartid][fw_evt_code];
|
||||
*cval = fevent.curr_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add a hardware counter read for completeness for future purpose */
|
||||
static int pmu_ctr_read_hw(uint32_t cidx, uint64_t *cval)
|
||||
{
|
||||
/* Check for invalid hw counter read requests */
|
||||
if (unlikely(cidx == 1))
|
||||
return SBI_EINVAL;
|
||||
#if __riscv_xlen == 32
|
||||
uint32_t temp, temph = 0;
|
||||
|
||||
temp = csr_read_num(CSR_MCYCLE + cidx);
|
||||
temph = csr_read_num(CSR_MCYCLEH + cidx);
|
||||
*cval = ((uint64_t)temph << 32) | temp;
|
||||
#else
|
||||
*cval = csr_read_num(CSR_MCYCLE + cidx);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval)
|
||||
{
|
||||
int event_idx_type;
|
||||
uint32_t event_code;
|
||||
uint64_t cval64;
|
||||
|
||||
event_idx_type = pmu_ctr_validate(cidx, &event_code);
|
||||
if (event_idx_type < 0)
|
||||
return SBI_EINVAL;
|
||||
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
|
||||
pmu_ctr_read_fw(cidx, cval, event_code);
|
||||
else
|
||||
pmu_ctr_read_hw(cidx, &cval64);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap,
|
||||
uint64_t select, uint64_t select_mask)
|
||||
{
|
||||
int i = 0;
|
||||
bool is_overlap;
|
||||
struct sbi_pmu_hw_event *event = &hw_event_map[num_hw_events];
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
int hw_ctr_avail = sbi_hart_mhpm_count(scratch);
|
||||
uint32_t ctr_avail_mask = ((uint32_t)(~0) >> (32 - (hw_ctr_avail + 3)));
|
||||
|
||||
/* The first two counters are reserved by priv spec */
|
||||
if (eidx_start > SBI_PMU_HW_INSTRUCTIONS && (cmap & SBI_PMU_FIXED_CTR_MASK))
|
||||
return SBI_EDENIED;
|
||||
|
||||
if (num_hw_events >= SBI_PMU_HW_EVENT_MAX - 1) {
|
||||
sbi_printf("Can not handle more than %d perf events\n",
|
||||
SBI_PMU_HW_EVENT_MAX);
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
|
||||
event->start_idx = eidx_start;
|
||||
event->end_idx = eidx_end;
|
||||
|
||||
/* Sanity check */
|
||||
for (i = 0; i < num_hw_events; i++) {
|
||||
if (eidx_start == SBI_PMU_EVENT_RAW_IDX)
|
||||
/* All raw events have same event idx. Just do sanity check on select */
|
||||
is_overlap = pmu_event_select_overlap(&hw_event_map[i],
|
||||
select, select_mask);
|
||||
else
|
||||
is_overlap = pmu_event_range_overlap(&hw_event_map[i], event);
|
||||
if (is_overlap)
|
||||
goto reset_event;
|
||||
}
|
||||
|
||||
event->select_mask = select_mask;
|
||||
/* Map the only the counters that are available in the hardware */
|
||||
event->counters = cmap & ctr_avail_mask;
|
||||
event->select = select;
|
||||
num_hw_events++;
|
||||
|
||||
return 0;
|
||||
|
||||
reset_event:
|
||||
event->start_idx = 0;
|
||||
event->end_idx = 0;
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical counter ids are assigned to hardware counters are assigned consecutively.
|
||||
* E.g. counter0 must count MCYCLE where counter2 must count minstret. Similarly,
|
||||
* counterX will mhpmcounterX.
|
||||
*/
|
||||
int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32 eidx_end, u32 cmap)
|
||||
{
|
||||
if ((eidx_start > eidx_end) || eidx_start == SBI_PMU_EVENT_RAW_IDX ||
|
||||
eidx_end == SBI_PMU_EVENT_RAW_IDX)
|
||||
return SBI_EINVAL;
|
||||
|
||||
return pmu_add_hw_event_map(eidx_start, eidx_end, cmap, 0, 0);
|
||||
}
|
||||
|
||||
int sbi_pmu_add_raw_event_counter_map(uint64_t select, uint64_t select_mask, u32 cmap)
|
||||
{
|
||||
return pmu_add_hw_event_map(SBI_PMU_EVENT_RAW_IDX,
|
||||
SBI_PMU_EVENT_RAW_IDX, cmap, select, select_mask);
|
||||
}
|
||||
|
||||
static int pmu_ctr_enable_irq_hw(int ctr_idx)
|
||||
{
|
||||
unsigned long mhpmevent_csr;
|
||||
unsigned long mhpmevent_curr;
|
||||
unsigned long mip_val;
|
||||
unsigned long of_mask;
|
||||
|
||||
if (ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
|
||||
return SBI_EFAIL;
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
mhpmevent_csr = CSR_MHPMEVENT3H + ctr_idx - 3;
|
||||
of_mask = (uint32_t)~MHPMEVENTH_OF;
|
||||
#else
|
||||
mhpmevent_csr = CSR_MHPMEVENT3 + ctr_idx - 3;
|
||||
of_mask = ~MHPMEVENT_OF;
|
||||
#endif
|
||||
|
||||
mhpmevent_curr = csr_read_num(mhpmevent_csr);
|
||||
mip_val = csr_read(CSR_MIP);
|
||||
/**
|
||||
* Clear out the OF bit so that next interrupt can be enabled.
|
||||
* This should be done only when the corresponding overflow interrupt
|
||||
* bit is cleared. That indicates that software has already handled the
|
||||
* previous interrupts or the hardware yet to set an overflow interrupt.
|
||||
* Otherwise, there will be race conditions where we may clear the bit
|
||||
* the software is yet to handle the interrupt.
|
||||
*/
|
||||
if (!(mip_val & MIP_LCOFIP)) {
|
||||
mhpmevent_curr &= of_mask;
|
||||
csr_write_num(mhpmevent_csr, mhpmevent_curr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmu_ctr_write_hw(uint32_t cidx, uint64_t ival)
|
||||
{
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MCYCLE + cidx, 0);
|
||||
csr_write_num(CSR_MCYCLE + cidx, ival & 0xFFFFFFFF);
|
||||
csr_write_num(CSR_MCYCLEH + cidx, ival >> BITS_PER_LONG);
|
||||
#else
|
||||
csr_write_num(CSR_MCYCLE + cidx, ival);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
|
||||
{
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
unsigned long mctr_inhbt;
|
||||
|
||||
/* Make sure the counter index lies within the range and is not TM bit */
|
||||
if (cidx >= num_hw_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11)
|
||||
goto skip_inhibit_update;
|
||||
|
||||
/*
|
||||
* Some of the hardware may not support mcountinhibit but perf stat
|
||||
* still can work if supervisor mode programs the initial value.
|
||||
*/
|
||||
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
|
||||
if (!__test_bit(cidx, &mctr_inhbt))
|
||||
return SBI_EALREADY_STARTED;
|
||||
|
||||
__clear_bit(cidx, &mctr_inhbt);
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
pmu_ctr_enable_irq_hw(cidx);
|
||||
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
|
||||
|
||||
skip_inhibit_update:
|
||||
if (ival_update)
|
||||
pmu_ctr_write_hw(cidx, ival);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_ctr_start_fw(uint32_t cidx, uint32_t fw_evt_code,
|
||||
uint64_t ival, bool ival_update)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
struct sbi_pmu_fw_event *fevent;
|
||||
|
||||
fevent = &fw_event_map[hartid][fw_evt_code];
|
||||
if (ival_update)
|
||||
fevent->curr_count = ival;
|
||||
fevent->bStarted = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
|
||||
unsigned long flags, uint64_t ival)
|
||||
{
|
||||
int event_idx_type;
|
||||
uint32_t event_code;
|
||||
unsigned long ctr_mask = cmask << cbase;
|
||||
int ret = SBI_EINVAL;
|
||||
bool bUpdate = FALSE;
|
||||
|
||||
if (sbi_fls(ctr_mask) >= total_ctrs)
|
||||
return ret;
|
||||
|
||||
if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
|
||||
bUpdate = TRUE;
|
||||
|
||||
for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
|
||||
event_idx_type = pmu_ctr_validate(cbase, &event_code);
|
||||
if (event_idx_type < 0)
|
||||
/* Continue the start operation for other counters */
|
||||
continue;
|
||||
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
|
||||
ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
|
||||
else
|
||||
ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmu_ctr_stop_hw(uint32_t cidx)
|
||||
{
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
unsigned long mctr_inhbt;
|
||||
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11)
|
||||
return 0;
|
||||
|
||||
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
|
||||
|
||||
/* Make sure the counter index lies within the range and is not TM bit */
|
||||
if (cidx >= num_hw_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (!__test_bit(cidx, &mctr_inhbt)) {
|
||||
__set_bit(cidx, &mctr_inhbt);
|
||||
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
|
||||
return 0;
|
||||
} else
|
||||
return SBI_EALREADY_STOPPED;
|
||||
}
|
||||
|
||||
static int pmu_ctr_stop_fw(uint32_t cidx, uint32_t fw_evt_code)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
fw_event_map[hartid][fw_evt_code].bStarted = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_reset_hw_mhpmevent(int ctr_idx)
|
||||
{
|
||||
if (ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
|
||||
return SBI_EFAIL;
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(),
|
||||
SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, 0);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
|
||||
unsigned long flag)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
int ret = SBI_EINVAL;
|
||||
int event_idx_type;
|
||||
uint32_t event_code;
|
||||
unsigned long ctr_mask = cmask << cbase;
|
||||
|
||||
if (sbi_fls(ctr_mask) >= total_ctrs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
|
||||
event_idx_type = pmu_ctr_validate(cbase, &event_code);
|
||||
if (event_idx_type < 0)
|
||||
/* Continue the stop operation for other counters */
|
||||
continue;
|
||||
|
||||
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
|
||||
ret = pmu_ctr_stop_fw(cbase, event_code);
|
||||
else
|
||||
ret = pmu_ctr_stop_hw(cbase);
|
||||
|
||||
if (flag & SBI_PMU_STOP_FLAG_RESET) {
|
||||
active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
|
||||
pmu_reset_hw_mhpmevent(cbase);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pmu_update_inhibit_flags(unsigned long flags, uint64_t *mhpmevent_val)
|
||||
{
|
||||
if (flags & SBI_PMU_CFG_FLAG_SET_VUINH)
|
||||
*mhpmevent_val |= MHPMEVENT_VUINH;
|
||||
if (flags & SBI_PMU_CFG_FLAG_SET_VSINH)
|
||||
*mhpmevent_val |= MHPMEVENT_VSINH;
|
||||
if (flags & SBI_PMU_CFG_FLAG_SET_UINH)
|
||||
*mhpmevent_val |= MHPMEVENT_UINH;
|
||||
if (flags & SBI_PMU_CFG_FLAG_SET_SINH)
|
||||
*mhpmevent_val |= MHPMEVENT_SINH;
|
||||
}
|
||||
|
||||
static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx,
|
||||
unsigned long flags, unsigned long eindex,
|
||||
uint64_t data)
|
||||
{
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
uint64_t mhpmevent_val;
|
||||
|
||||
/* Get the final mhpmevent value to be written from platform */
|
||||
mhpmevent_val = sbi_platform_pmu_xlate_to_mhpmevent(plat, eindex, data);
|
||||
|
||||
if (!mhpmevent_val || ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
|
||||
return SBI_EFAIL;
|
||||
|
||||
/**
|
||||
* Always set the OVF bit(disable interrupts) and inhibit counting of
|
||||
* events in M-mode. The OVF bit should be enabled during the start call.
|
||||
*/
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) |
|
||||
MHPMEVENT_MINH | MHPMEVENT_OF;
|
||||
|
||||
/* Update the inhibit flags based on inhibit flags received from supervisor */
|
||||
pmu_update_inhibit_flags(flags, &mhpmevent_val);
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val & 0xFFFFFFFF);
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3,
|
||||
mhpmevent_val >> BITS_PER_LONG);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_ctr_find_fixed_fw(unsigned long evt_idx_code)
|
||||
{
|
||||
/* Non-programmables counters are enabled always. No need to do lookup */
|
||||
if (evt_idx_code == SBI_PMU_HW_CPU_CYCLES)
|
||||
return 0;
|
||||
else if (evt_idx_code == SBI_PMU_HW_INSTRUCTIONS)
|
||||
return 2;
|
||||
else
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned long flags,
|
||||
unsigned long event_idx, uint64_t data)
|
||||
{
|
||||
unsigned long ctr_mask;
|
||||
int i, ret = 0, fixed_ctr, ctr_idx = SBI_ENOTSUPP;
|
||||
struct sbi_pmu_hw_event *temp;
|
||||
unsigned long mctr_inhbt = 0;
|
||||
u32 hartid = current_hartid();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
if (cbase >= num_hw_ctrs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/**
|
||||
* If Sscof is present try to find the programmable counter for
|
||||
* cycle/instret as well.
|
||||
*/
|
||||
fixed_ctr = pmu_ctr_find_fixed_fw(event_idx);
|
||||
if (fixed_ctr >= 0 &&
|
||||
!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
return fixed_ctr;
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
|
||||
for (i = 0; i < num_hw_events; i++) {
|
||||
temp = &hw_event_map[i];
|
||||
if ((temp->start_idx > event_idx && event_idx < temp->end_idx) ||
|
||||
(temp->start_idx < event_idx && event_idx > temp->end_idx))
|
||||
continue;
|
||||
|
||||
/* For raw events, event data is used as the select value */
|
||||
if (event_idx == SBI_PMU_EVENT_RAW_IDX) {
|
||||
uint64_t select_mask = temp->select_mask;
|
||||
|
||||
/* The non-event map bits of data should match the selector */
|
||||
if (temp->select != (data & select_mask))
|
||||
continue;
|
||||
}
|
||||
/* Fixed counters should not be part of the search */
|
||||
ctr_mask = temp->counters & (cmask << cbase) &
|
||||
(~SBI_PMU_FIXED_CTR_MASK);
|
||||
for_each_set_bit_from(cbase, &ctr_mask, SBI_PMU_HW_CTR_MAX) {
|
||||
/**
|
||||
* Some of the platform may not support mcountinhibit.
|
||||
* Checking the active_events is enough for them
|
||||
*/
|
||||
if (active_events[hartid][cbase] != SBI_PMU_EVENT_IDX_INVALID)
|
||||
continue;
|
||||
/* If mcountinhibit is supported, the bit must be enabled */
|
||||
if ((sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) &&
|
||||
!__test_bit(cbase, &mctr_inhbt))
|
||||
continue;
|
||||
/* We found a valid counter that is not started yet */
|
||||
ctr_idx = cbase;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctr_idx == SBI_ENOTSUPP) {
|
||||
/**
|
||||
* We can't find any programmable counters for cycle/instret.
|
||||
* Return the fixed counter as they are mandatory anyways.
|
||||
*/
|
||||
if (fixed_ctr >= 0)
|
||||
return fixed_ctr;
|
||||
else
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data);
|
||||
|
||||
if (!ret)
|
||||
ret = ctr_idx;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Any firmware counter can map to any firmware event.
|
||||
* Thus, select the first available fw counter after sanity
|
||||
* check.
|
||||
*/
|
||||
static int pmu_ctr_find_fw(unsigned long cbase, unsigned long cmask, u32 hartid)
|
||||
{
|
||||
int i = 0;
|
||||
int fw_base;
|
||||
unsigned long ctr_mask = cmask << cbase;
|
||||
|
||||
if (cbase < num_hw_ctrs)
|
||||
fw_base = num_hw_ctrs;
|
||||
else
|
||||
fw_base = cbase;
|
||||
|
||||
for (i = fw_base; i < total_ctrs; i++)
|
||||
if ((active_events[hartid][i] == SBI_PMU_EVENT_IDX_INVALID) &&
|
||||
((1UL << i) & ctr_mask))
|
||||
return i;
|
||||
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
unsigned long flags, unsigned long event_idx,
|
||||
uint64_t event_data)
|
||||
{
|
||||
int ctr_idx = SBI_ENOTSUPP;
|
||||
u32 hartid = current_hartid();
|
||||
int event_type = get_cidx_type(event_idx);
|
||||
struct sbi_pmu_fw_event *fevent;
|
||||
uint32_t fw_evt_code;
|
||||
unsigned long tmp = cidx_mask << cidx_base;
|
||||
|
||||
/* Do a basic sanity check of counter base & mask */
|
||||
if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
|
||||
/* The caller wants to skip the match because it already knows the
|
||||
* counter idx for the given event. Verify that the counter idx
|
||||
* is still valid.
|
||||
*/
|
||||
if (active_events[hartid][cidx_base] == SBI_PMU_EVENT_IDX_INVALID)
|
||||
return SBI_EINVAL;
|
||||
ctr_idx = cidx_base;
|
||||
goto skip_match;
|
||||
}
|
||||
|
||||
if (event_type == SBI_PMU_EVENT_TYPE_FW) {
|
||||
/* Any firmware counter can be used track any firmware event */
|
||||
ctr_idx = pmu_ctr_find_fw(cidx_base, cidx_mask, hartid);
|
||||
} else {
|
||||
ctr_idx = pmu_ctr_find_hw(cidx_base, cidx_mask, flags, event_idx,
|
||||
event_data);
|
||||
}
|
||||
|
||||
if (ctr_idx < 0)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
active_events[hartid][ctr_idx] = event_idx;
|
||||
skip_match:
|
||||
if (event_type == SBI_PMU_EVENT_TYPE_HW) {
|
||||
if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
|
||||
pmu_ctr_write_hw(ctr_idx, 0);
|
||||
if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
|
||||
pmu_ctr_start_hw(ctr_idx, 0, false);
|
||||
} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
|
||||
fw_evt_code = get_cidx_code(event_idx);
|
||||
fevent = &fw_event_map[hartid][fw_evt_code];
|
||||
if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
|
||||
fevent->curr_count = 0;
|
||||
if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
|
||||
fevent->bStarted = TRUE;
|
||||
}
|
||||
|
||||
return ctr_idx;
|
||||
}
|
||||
|
||||
inline int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
struct sbi_pmu_fw_event *fevent;
|
||||
|
||||
if (unlikely(fw_id >= SBI_PMU_FW_MAX))
|
||||
return SBI_EINVAL;
|
||||
|
||||
fevent = &fw_event_map[hartid][fw_id];
|
||||
|
||||
/* PMU counters will be only enabled during performance debugging */
|
||||
if (unlikely(fevent->bStarted))
|
||||
fevent->curr_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long sbi_pmu_num_ctr(void)
|
||||
{
|
||||
return (num_hw_ctrs + SBI_PMU_FW_CTR_MAX);
|
||||
}
|
||||
|
||||
int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
|
||||
{
|
||||
union sbi_pmu_ctr_info cinfo = {0};
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
/* Sanity check. Counter1 is not mapped at all */
|
||||
if (cidx >= total_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* We have 31 HW counters with 31 being the last index(MHPMCOUNTER31) */
|
||||
if (cidx < num_hw_ctrs) {
|
||||
cinfo.type = SBI_PMU_CTR_TYPE_HW;
|
||||
cinfo.csr = CSR_CYCLE + cidx;
|
||||
/* mcycle & minstret are always 64 bit */
|
||||
if (cidx == 0 || cidx == 2)
|
||||
cinfo.width = 63;
|
||||
else
|
||||
cinfo.width = sbi_hart_mhpm_bits(scratch) - 1;
|
||||
} else {
|
||||
/* it's a firmware counter */
|
||||
cinfo.type = SBI_PMU_CTR_TYPE_FW;
|
||||
/* Firmware counters are XLEN bits wide */
|
||||
cinfo.width = BITS_PER_LONG - 1;
|
||||
}
|
||||
|
||||
*ctr_info = cinfo.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmu_reset_event_map(u32 hartid)
|
||||
{
|
||||
int j;
|
||||
|
||||
/* Initialize the counter to event mapping table */
|
||||
for (j = 3; j < total_ctrs; j++)
|
||||
active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
|
||||
for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
|
||||
sbi_memset(&fw_event_map[hartid][j], 0,
|
||||
sizeof(struct sbi_pmu_fw_event));
|
||||
}
|
||||
|
||||
void sbi_pmu_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
pmu_reset_event_map(hartid);
|
||||
}
|
||||
|
||||
int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
const struct sbi_platform *plat;
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
if (cold_boot) {
|
||||
plat = sbi_platform_ptr(scratch);
|
||||
/* Initialize hw pmu events */
|
||||
sbi_platform_pmu_init(plat);
|
||||
|
||||
/* mcycle & minstret is available always */
|
||||
num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 3;
|
||||
total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX;
|
||||
}
|
||||
|
||||
pmu_reset_event_map(hartid);
|
||||
|
||||
/* First three counters are fixed by the priv spec and we enable it by default */
|
||||
active_events[hartid][0] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET |
|
||||
SBI_PMU_HW_CPU_CYCLES;
|
||||
active_events[hartid][1] = SBI_PMU_EVENT_IDX_INVALID;
|
||||
active_events[hartid][2] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET |
|
||||
SBI_PMU_HW_INSTRUCTIONS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
|
||||
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS - 1;
|
||||
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS;
|
||||
struct sbi_scratch *hartid_to_scratch_table[SBI_HARTMASK_MAX_BITS] = { 0 };
|
||||
|
||||
static spinlock_t extra_lock = SPIN_LOCK_INITIALIZER;
|
||||
@@ -40,7 +40,7 @@ int sbi_scratch_init(struct sbi_scratch *scratch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size)
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner)
|
||||
{
|
||||
u32 i;
|
||||
void *ptr;
|
||||
@@ -74,7 +74,7 @@ done:
|
||||
spin_unlock(&extra_lock);
|
||||
|
||||
if (ret) {
|
||||
for (i = 0; i <= sbi_scratch_last_hartid(); i++) {
|
||||
for (i = 0; i < sbi_scratch_last_hartid(); i++) {
|
||||
rscratch = sbi_hartid_to_scratch(i);
|
||||
if (!rscratch)
|
||||
continue;
|
||||
|
||||
@@ -33,10 +33,6 @@ int sbi_strncmp(const char *a, const char *b, size_t count)
|
||||
for (; count > 0 && *a == *b && *a != '\0'; a++, b++, count--)
|
||||
;
|
||||
|
||||
/* No difference till the end */
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
@@ -149,8 +145,8 @@ void *sbi_memmove(void *dest, const void *src, size_t count)
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
temp1 = (char *)dest + count - 1;
|
||||
temp2 = (char *)src + count - 1;
|
||||
temp1 = dest + count - 1;
|
||||
temp2 = src + count - 1;
|
||||
|
||||
while (count > 0) {
|
||||
*temp1-- = *temp2--;
|
||||
|
||||
@@ -18,50 +18,31 @@
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_init.h>
|
||||
|
||||
static SBI_LIST_HEAD(reset_devices_list);
|
||||
static const struct sbi_system_reset_device *reset_dev = NULL;
|
||||
|
||||
const struct sbi_system_reset_device *sbi_system_reset_get_device(
|
||||
u32 reset_type, u32 reset_reason)
|
||||
const struct sbi_system_reset_device *sbi_system_reset_get_device(void)
|
||||
{
|
||||
struct sbi_system_reset_device *reset_dev = NULL;
|
||||
struct sbi_dlist *pos;
|
||||
/** lowest priority - any non zero is our candidate */
|
||||
int priority = 0;
|
||||
|
||||
/* Check each reset device registered for supported reset type */
|
||||
sbi_list_for_each(pos, &(reset_devices_list)) {
|
||||
struct sbi_system_reset_device *dev =
|
||||
to_system_reset_device(pos);
|
||||
if (dev->system_reset_check) {
|
||||
int status = dev->system_reset_check(reset_type,
|
||||
reset_reason);
|
||||
/** reset_type not supported */
|
||||
if (status == 0)
|
||||
continue;
|
||||
|
||||
if (status > priority) {
|
||||
reset_dev = dev;
|
||||
priority = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reset_dev;
|
||||
}
|
||||
|
||||
void sbi_system_reset_add_device(struct sbi_system_reset_device *dev)
|
||||
void sbi_system_reset_set_device(const struct sbi_system_reset_device *dev)
|
||||
{
|
||||
if (!dev || !dev->system_reset_check)
|
||||
if (!dev || reset_dev)
|
||||
return;
|
||||
|
||||
sbi_list_add(&(dev->node), &(reset_devices_list));
|
||||
reset_dev = dev;
|
||||
}
|
||||
|
||||
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason)
|
||||
{
|
||||
return !!sbi_system_reset_get_device(reset_type, reset_reason);
|
||||
if (reset_dev && reset_dev->system_reset_check &&
|
||||
reset_dev->system_reset_check(reset_type, reset_reason))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
|
||||
{
|
||||
ulong hbase = 0, hmask;
|
||||
@@ -82,13 +63,97 @@ void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
|
||||
sbi_hsm_hart_stop(scratch, FALSE);
|
||||
|
||||
/* Platform specific reset if domain allowed system reset */
|
||||
if (dom->system_reset_allowed) {
|
||||
const struct sbi_system_reset_device *dev =
|
||||
sbi_system_reset_get_device(reset_type, reset_reason);
|
||||
if (dev)
|
||||
dev->system_reset(reset_type, reset_reason);
|
||||
}
|
||||
if (dom->system_reset_allowed &&
|
||||
reset_dev && reset_dev->system_reset)
|
||||
reset_dev->system_reset(reset_type, reset_reason);
|
||||
|
||||
writew(0x5555, (void *)0x100000); // qemu poweroff
|
||||
|
||||
/* If platform specific reset did not work then do sbi_exit() */
|
||||
sbi_exit(scratch);
|
||||
}
|
||||
|
||||
static const struct sbi_system_suspend_device *suspend_dev;
|
||||
|
||||
const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void)
|
||||
{
|
||||
return suspend_dev;
|
||||
}
|
||||
|
||||
void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev)
|
||||
{
|
||||
if (!dev || suspend_dev)
|
||||
return;
|
||||
|
||||
suspend_dev = dev;
|
||||
}
|
||||
|
||||
bool sbi_system_suspend_supported(u32 sleep_type)
|
||||
{
|
||||
return suspend_dev && suspend_dev->system_suspend_check &&
|
||||
suspend_dev->system_suspend_check(sleep_type) == 0;
|
||||
}
|
||||
|
||||
int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque)
|
||||
{
|
||||
const struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
|
||||
//unsigned int hartid = current_hartid();
|
||||
unsigned long prev_mode;
|
||||
//unsigned long i, j;
|
||||
int ret;
|
||||
|
||||
if (!dom || !dom->system_suspend_allowed)
|
||||
return SBI_EFAIL;
|
||||
|
||||
if (!suspend_dev || !suspend_dev->system_suspend ||
|
||||
!suspend_dev->system_suspend_check)
|
||||
return SBI_EFAIL;
|
||||
|
||||
ret = suspend_dev->system_suspend_check(sleep_type);
|
||||
if (ret != SBI_OK)
|
||||
return ret;
|
||||
|
||||
prev_mode = (csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
if (prev_mode != PRV_S && prev_mode != PRV_U)
|
||||
return SBI_EFAIL;
|
||||
|
||||
//FIX ME: hartindex to hart
|
||||
//sbi_hartmask_for_each_hartindex(j, &dom->assigned_harts) {
|
||||
// //i = sbi_hartindex_to_hartid(j);
|
||||
// if (i == hartid)
|
||||
// continue;
|
||||
// if (__sbi_hsm_hart_get_state(i) != SBI_HSM_STATE_STOPPED)
|
||||
// return SBI_ERR_DENIED;
|
||||
//}
|
||||
|
||||
if (!sbi_domain_check_addr(dom, resume_addr, prev_mode,
|
||||
SBI_DOMAIN_EXECUTE))
|
||||
return SBI_EINVALID_ADDR;
|
||||
|
||||
if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_SUSPENDED))
|
||||
return SBI_EFAIL;
|
||||
|
||||
/* Prepare for resume */
|
||||
scratch->next_mode = prev_mode;
|
||||
scratch->next_addr = resume_addr;
|
||||
scratch->next_arg1 = opaque;
|
||||
|
||||
__sbi_hsm_suspend_non_ret_save(scratch);
|
||||
|
||||
/* Suspend */
|
||||
ret = suspend_dev->system_suspend(sleep_type, scratch->warmboot_addr);
|
||||
if (ret != SBI_OK) {
|
||||
if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_STARTED))
|
||||
sbi_hart_hang();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Resume */
|
||||
jump_warmboot();
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -8,13 +8,10 @@
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
|
||||
@@ -44,36 +41,9 @@ static u64 get_ticks(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void nop_delay_fn(void *opaque)
|
||||
static u64 get_platform_ticks(void)
|
||||
{
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
void sbi_timer_delay_loop(ulong units, u64 unit_freq,
|
||||
void (*delay_fn)(void *), void *opaque)
|
||||
{
|
||||
u64 start_val, delta;
|
||||
|
||||
/* Do nothing if we don't have timer device */
|
||||
if (!timer_dev || !get_time_val) {
|
||||
sbi_printf("%s: called without timer device\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save starting timer value */
|
||||
start_val = get_time_val();
|
||||
|
||||
/* Compute desired timer value delta */
|
||||
delta = ((u64)timer_dev->timer_freq * (u64)units);
|
||||
delta = delta / unit_freq;
|
||||
|
||||
/* Use NOP delay function if delay function not available */
|
||||
if (!delay_fn)
|
||||
delay_fn = nop_delay_fn;
|
||||
|
||||
/* Busy loop until desired timer value delta reached */
|
||||
while ((get_time_val() - start_val) < delta)
|
||||
delay_fn(opaque);
|
||||
return timer_dev->timer_value();
|
||||
}
|
||||
|
||||
u64 sbi_timer_value(void)
|
||||
@@ -118,36 +88,16 @@ void sbi_timer_set_delta_upper(ulong delta_upper)
|
||||
|
||||
void sbi_timer_event_start(u64 next_event)
|
||||
{
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SET_TIMER);
|
||||
|
||||
/**
|
||||
* Update the stimecmp directly if available. This allows
|
||||
* the older software to leverage sstc extension on newer hardware.
|
||||
*/
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC)) {
|
||||
#if __riscv_xlen == 32
|
||||
csr_write(CSR_STIMECMP, next_event & 0xFFFFFFFF);
|
||||
csr_write(CSR_STIMECMPH, next_event >> 32);
|
||||
#else
|
||||
csr_write(CSR_STIMECMP, next_event);
|
||||
#endif
|
||||
} else if (timer_dev && timer_dev->timer_event_start) {
|
||||
if (timer_dev && timer_dev->timer_event_start)
|
||||
timer_dev->timer_event_start(next_event);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
}
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
csr_set(CSR_MIE, MIP_MTIP);
|
||||
}
|
||||
|
||||
void sbi_timer_process(void)
|
||||
{
|
||||
csr_clear(CSR_MIE, MIP_MTIP);
|
||||
/*
|
||||
* If sstc extension is available, supervisor can receive the timer
|
||||
* directly without M-mode come in between. This function should
|
||||
* only invoked if M-mode programs the timer for its own purpose.
|
||||
*/
|
||||
if (!sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC))
|
||||
csr_set(CSR_MIP, MIP_STIP);
|
||||
csr_set(CSR_MIP, MIP_STIP);
|
||||
}
|
||||
|
||||
const struct sbi_timer_device *sbi_timer_get_device(void)
|
||||
@@ -162,7 +112,7 @@ void sbi_timer_set_device(const struct sbi_timer_device *dev)
|
||||
|
||||
timer_dev = dev;
|
||||
if (!get_time_val && timer_dev->timer_value)
|
||||
get_time_val = timer_dev->timer_value;
|
||||
get_time_val = get_platform_ticks;
|
||||
}
|
||||
|
||||
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
@@ -171,11 +121,12 @@ int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (cold_boot) {
|
||||
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta));
|
||||
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta),
|
||||
"TIME_DELTA");
|
||||
if (!time_delta_off)
|
||||
return SBI_ENOMEM;
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_TIME))
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_TIME))
|
||||
get_time_val = get_ticks;
|
||||
} else {
|
||||
if (!time_delta_off)
|
||||
|
||||
@@ -21,14 +21,13 @@
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
|
||||
static unsigned long tlb_sync_off;
|
||||
static unsigned long tlb_fifo_off;
|
||||
static unsigned long tlb_fifo_mem_off;
|
||||
static unsigned long tlb_range_flush_limit;
|
||||
|
||||
static void tlb_flush_all(void)
|
||||
static void sbi_tlb_flush_all(void)
|
||||
{
|
||||
__asm__ __volatile("sfence.vma");
|
||||
}
|
||||
@@ -40,8 +39,6 @@ void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo)
|
||||
unsigned long vmid = tinfo->vmid;
|
||||
unsigned long i, hgatp;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_RCVD);
|
||||
|
||||
hgatp = csr_swap(CSR_HGATP,
|
||||
(vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
|
||||
|
||||
@@ -64,15 +61,13 @@ void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo)
|
||||
unsigned long size = tinfo->size;
|
||||
unsigned long i;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD);
|
||||
|
||||
if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
|
||||
__sbi_hfence_gvma_all();
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i += PAGE_SIZE) {
|
||||
__sbi_hfence_gvma_gpa((start + i) >> 2);
|
||||
__sbi_hfence_gvma_gpa(start+i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +77,8 @@ void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo)
|
||||
unsigned long size = tinfo->size;
|
||||
unsigned long i;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD);
|
||||
|
||||
if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
|
||||
tlb_flush_all();
|
||||
sbi_tlb_flush_all();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,8 +98,6 @@ void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo)
|
||||
unsigned long vmid = tinfo->vmid;
|
||||
unsigned long i, hgatp;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD);
|
||||
|
||||
hgatp = csr_swap(CSR_HGATP,
|
||||
(vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
|
||||
|
||||
@@ -135,8 +126,6 @@ void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo)
|
||||
unsigned long vmid = tinfo->vmid;
|
||||
unsigned long i;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD);
|
||||
|
||||
if (start == 0 && size == 0) {
|
||||
__sbi_hfence_gvma_all();
|
||||
return;
|
||||
@@ -148,7 +137,7 @@ void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo)
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i += PAGE_SIZE) {
|
||||
__sbi_hfence_gvma_vmid_gpa((start + i) >> 2, vmid);
|
||||
__sbi_hfence_gvma_vmid_gpa(start + i, vmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,10 +148,8 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
|
||||
unsigned long asid = tinfo->asid;
|
||||
unsigned long i;
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD);
|
||||
|
||||
if (start == 0 && size == 0) {
|
||||
tlb_flush_all();
|
||||
sbi_tlb_flush_all();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,33 +172,10 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
|
||||
|
||||
void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo)
|
||||
{
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD);
|
||||
|
||||
__asm__ __volatile("fence.i");
|
||||
}
|
||||
|
||||
static void tlb_pmu_incr_fw_ctr(struct sbi_tlb_info *data)
|
||||
{
|
||||
if (unlikely(!data))
|
||||
return;
|
||||
|
||||
if (data->local_fn == sbi_tlb_local_fence_i)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_sfence_vma)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_sfence_vma_asid)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_hfence_gvma)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_hfence_gvma_vmid)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_hfence_vvma)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_SENT);
|
||||
else if (data->local_fn == sbi_tlb_local_hfence_vvma_asid)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_ASID_SENT);
|
||||
}
|
||||
|
||||
static void tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
{
|
||||
u32 rhartid;
|
||||
struct sbi_scratch *rscratch = NULL;
|
||||
@@ -229,15 +193,15 @@ static void tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
}
|
||||
}
|
||||
|
||||
static void tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
{
|
||||
struct sbi_tlb_info tinfo;
|
||||
unsigned int deq_count = 0;
|
||||
u32 deq_count = 0;
|
||||
struct sbi_fifo *tlb_fifo =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
|
||||
|
||||
while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) {
|
||||
tlb_entry_process(&tinfo);
|
||||
sbi_tlb_entry_process(&tinfo);
|
||||
deq_count++;
|
||||
if (deq_count > count)
|
||||
break;
|
||||
@@ -245,17 +209,17 @@ static void tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void tlb_process(struct sbi_scratch *scratch)
|
||||
static void sbi_tlb_process(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_tlb_info tinfo;
|
||||
struct sbi_fifo *tlb_fifo =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
|
||||
|
||||
while (!sbi_fifo_dequeue(tlb_fifo, &tinfo))
|
||||
tlb_entry_process(&tinfo);
|
||||
sbi_tlb_entry_process(&tinfo);
|
||||
}
|
||||
|
||||
static void tlb_sync(struct sbi_scratch *scratch)
|
||||
static void sbi_tlb_sync(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long *tlb_sync =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_sync_off);
|
||||
@@ -265,13 +229,13 @@ static void tlb_sync(struct sbi_scratch *scratch)
|
||||
* While we are waiting for remote hart to set the sync,
|
||||
* consume fifo requests to avoid deadlock.
|
||||
*/
|
||||
tlb_process_count(scratch, 1);
|
||||
sbi_tlb_process_count(scratch, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int tlb_range_check(struct sbi_tlb_info *curr,
|
||||
static inline int __sbi_tlb_range_check(struct sbi_tlb_info *curr,
|
||||
struct sbi_tlb_info *next)
|
||||
{
|
||||
unsigned long curr_end;
|
||||
@@ -314,7 +278,7 @@ static inline int tlb_range_check(struct sbi_tlb_info *curr,
|
||||
* before continuing the while loop. This method is preferred over wfi/ipi because
|
||||
* of MMIO cost involved in later method.
|
||||
*/
|
||||
static int tlb_update_cb(void *in, void *data)
|
||||
static int sbi_tlb_update_cb(void *in, void *data)
|
||||
{
|
||||
struct sbi_tlb_info *curr;
|
||||
struct sbi_tlb_info *next;
|
||||
@@ -329,16 +293,16 @@ static int tlb_update_cb(void *in, void *data)
|
||||
if (next->local_fn == sbi_tlb_local_sfence_vma_asid &&
|
||||
curr->local_fn == sbi_tlb_local_sfence_vma_asid) {
|
||||
if (next->asid == curr->asid)
|
||||
ret = tlb_range_check(curr, next);
|
||||
ret = __sbi_tlb_range_check(curr, next);
|
||||
} else if (next->local_fn == sbi_tlb_local_sfence_vma &&
|
||||
curr->local_fn == sbi_tlb_local_sfence_vma) {
|
||||
ret = tlb_range_check(curr, next);
|
||||
ret = __sbi_tlb_range_check(curr, next);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tlb_update(struct sbi_scratch *scratch,
|
||||
static int sbi_tlb_update(struct sbi_scratch *scratch,
|
||||
struct sbi_scratch *remote_scratch,
|
||||
u32 remote_hartid, void *data)
|
||||
{
|
||||
@@ -368,7 +332,7 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
|
||||
tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off);
|
||||
|
||||
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, tlb_update_cb);
|
||||
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, sbi_tlb_update_cb);
|
||||
if (ret != SBI_FIFO_UNCHANGED) {
|
||||
return 1;
|
||||
}
|
||||
@@ -382,7 +346,7 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
* TODO: Introduce a wait/wakeup event mechanism to handle
|
||||
* this properly.
|
||||
*/
|
||||
tlb_process_count(scratch, 1);
|
||||
sbi_tlb_process_count(scratch, 1);
|
||||
sbi_dprintf("hart%d: hart%d tlb fifo full\n",
|
||||
curr_hartid, remote_hartid);
|
||||
}
|
||||
@@ -392,9 +356,9 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
|
||||
static struct sbi_ipi_event_ops tlb_ops = {
|
||||
.name = "IPI_TLB",
|
||||
.update = tlb_update,
|
||||
.sync = tlb_sync,
|
||||
.process = tlb_process,
|
||||
.update = sbi_tlb_update,
|
||||
.sync = sbi_tlb_sync,
|
||||
.process = sbi_tlb_process,
|
||||
};
|
||||
|
||||
static u32 tlb_event = SBI_IPI_EVENT_MAX;
|
||||
@@ -404,8 +368,6 @@ int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo)
|
||||
if (!tinfo->local_fn)
|
||||
return SBI_EINVAL;
|
||||
|
||||
tlb_pmu_incr_fw_ctr(tinfo);
|
||||
|
||||
return sbi_ipi_send_many(hmask, hbase, tlb_event, tinfo);
|
||||
}
|
||||
|
||||
@@ -418,16 +380,19 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (cold_boot) {
|
||||
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync));
|
||||
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync),
|
||||
"IPI_TLB_SYNC");
|
||||
if (!tlb_sync_off)
|
||||
return SBI_ENOMEM;
|
||||
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q));
|
||||
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q),
|
||||
"IPI_TLB_FIFO");
|
||||
if (!tlb_fifo_off) {
|
||||
sbi_scratch_free_offset(tlb_sync_off);
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
tlb_fifo_mem_off = sbi_scratch_alloc_offset(
|
||||
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE);
|
||||
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE,
|
||||
"IPI_TLB_FIFO_MEM");
|
||||
if (!tlb_fifo_mem_off) {
|
||||
sbi_scratch_free_offset(tlb_fifo_off);
|
||||
sbi_scratch_free_offset(tlb_sync_off);
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_illegal_insn.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_misaligned_ldst.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
@@ -99,14 +97,17 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
if (prev_mode != PRV_S && prev_mode != PRV_U)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/* If exceptions came from VS/VU-mode, redirect to VS-mode if
|
||||
* delegated in hedeleg
|
||||
*/
|
||||
/* For certain exceptions from VS/VU-mode we redirect to VS-mode */
|
||||
if (misa_extension('H') && prev_virt) {
|
||||
if ((trap->cause < __riscv_xlen) &&
|
||||
(csr_read(CSR_HEDELEG) & BIT(trap->cause))) {
|
||||
switch (trap->cause) {
|
||||
case CAUSE_FETCH_PAGE_FAULT:
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
case CAUSE_STORE_PAGE_FAULT:
|
||||
next_virt = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
/* Update MSTATUS MPV bits */
|
||||
@@ -193,52 +194,6 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_trap_nonaia_irq(struct sbi_trap_regs *regs, ulong mcause)
|
||||
{
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
case IRQ_M_EXT:
|
||||
return sbi_irqchip_process(regs);
|
||||
default:
|
||||
return SBI_ENOENT;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_trap_aia_irq(struct sbi_trap_regs *regs, ulong mcause)
|
||||
{
|
||||
int rc;
|
||||
unsigned long mtopi;
|
||||
|
||||
while ((mtopi = csr_read(CSR_MTOPI))) {
|
||||
mtopi = mtopi >> TOPI_IID_SHIFT;
|
||||
switch (mtopi) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
case IRQ_M_EXT:
|
||||
rc = sbi_irqchip_process(regs);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
default:
|
||||
return SBI_ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle trap/interrupt
|
||||
*
|
||||
@@ -269,15 +224,18 @@ struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs)
|
||||
}
|
||||
|
||||
if (mcause & (1UL << (__riscv_xlen - 1))) {
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(),
|
||||
SBI_HART_EXT_AIA))
|
||||
rc = sbi_trap_aia_irq(regs, mcause);
|
||||
else
|
||||
rc = sbi_trap_nonaia_irq(regs, mcause);
|
||||
if (rc) {
|
||||
msg = "unhandled local interrupt";
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
default:
|
||||
msg = "unhandled external interrupt";
|
||||
goto trap_error;
|
||||
}
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
|
||||
@@ -299,11 +257,6 @@ struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs)
|
||||
rc = sbi_ecall_handler(regs);
|
||||
msg = "ecall handler failed";
|
||||
break;
|
||||
case CAUSE_LOAD_ACCESS:
|
||||
case CAUSE_STORE_ACCESS:
|
||||
sbi_pmu_ctr_incr_fw(mcause == CAUSE_LOAD_ACCESS ?
|
||||
SBI_PMU_FW_ACCESS_LOAD : SBI_PMU_FW_ACCESS_STORE);
|
||||
/* fallthrough */
|
||||
default:
|
||||
/* If the trap came from S or U mode, redirect it there */
|
||||
trap.epc = regs->mepc;
|
||||
|
||||
@@ -83,7 +83,7 @@ DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld)
|
||||
#elif __riscv_xlen == 32
|
||||
#else
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw)
|
||||
|
||||
@@ -112,8 +112,6 @@ void sbi_store_u64(u64 *addr, u64 val,
|
||||
if (trap->cause)
|
||||
return;
|
||||
}
|
||||
#else
|
||||
# error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
|
||||
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
||||
@@ -149,17 +147,15 @@ ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
||||
switch (trap->cause) {
|
||||
case CAUSE_LOAD_ACCESS:
|
||||
trap->cause = CAUSE_FETCH_ACCESS;
|
||||
trap->tinst = 0UL;
|
||||
trap->tval = mepc;
|
||||
break;
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
trap->cause = CAUSE_FETCH_PAGE_FAULT;
|
||||
trap->tinst = 0UL;
|
||||
trap->tval = mepc;
|
||||
break;
|
||||
case CAUSE_LOAD_GUEST_PAGE_FAULT:
|
||||
trap->cause = CAUSE_FETCH_GUEST_PAGE_FAULT;
|
||||
if (trap->tinst != INSN_PSEUDO_VS_LOAD &&
|
||||
trap->tinst != INSN_PSEUDO_VS_STORE)
|
||||
trap->tinst = 0UL;
|
||||
trap->tval = mepc;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
@@ -96,7 +95,7 @@ static int __fixup_find_domain_offset(void *fdt, int doff, void *p)
|
||||
{
|
||||
struct __fixup_find_domain_offset_info *fdo = p;
|
||||
|
||||
if (!strncmp(fdo->name, fdt_get_name(fdt, doff, NULL), strlen(fdo->name)))
|
||||
if (!sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL)))
|
||||
*fdo->doffset = doff;
|
||||
|
||||
return 0;
|
||||
@@ -165,9 +164,6 @@ void fdt_domain_fixup(void *fdt)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, doffset))
|
||||
continue;
|
||||
|
||||
fdt_nop_property(fdt, doffset, "opensbi-domain");
|
||||
}
|
||||
|
||||
@@ -291,7 +287,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
regions = &fdt_regions[fdt_domains_count][0];
|
||||
|
||||
/* Read DT node name */
|
||||
strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
|
||||
sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
|
||||
sizeof(dom->name));
|
||||
dom->name[sizeof(dom->name) - 1] = '\0';
|
||||
|
||||
@@ -311,16 +307,13 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
sbi_hartmask_set_hart(val32, mask);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup memregions from DT */
|
||||
val32 = 0;
|
||||
memset(regions, 0,
|
||||
sbi_memset(regions, 0,
|
||||
sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 1));
|
||||
dom->regions = regions;
|
||||
err = fdt_iterate_each_memregion(fdt, domain_offset, &val32,
|
||||
@@ -344,7 +337,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
continue;
|
||||
if (FDT_DOMAIN_REGION_MAX_COUNT <= val32)
|
||||
return SBI_EINVAL;
|
||||
memcpy(®ions[val32++], reg, sizeof(*reg));
|
||||
sbi_memcpy(®ions[val32++], reg, sizeof(*reg));
|
||||
}
|
||||
|
||||
/* Read "boot-hart" DT property */
|
||||
@@ -353,7 +346,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (val && len >= 4) {
|
||||
cpu_offset = fdt_node_offset_by_phandle(fdt,
|
||||
fdt32_to_cpu(*val));
|
||||
if (cpu_offset >= 0 && fdt_node_is_enabled(fdt, cpu_offset))
|
||||
if (cpu_offset >= 0)
|
||||
fdt_parse_hart_id(fdt, cpu_offset, &val32);
|
||||
} else {
|
||||
if (domain_offset == *cold_domain_offset)
|
||||
@@ -405,6 +398,13 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
else
|
||||
dom->system_reset_allowed = FALSE;
|
||||
|
||||
/* Read "system-suspend-allowed" DT property */
|
||||
if (fdt_get_property(fdt, domain_offset,
|
||||
"system-suspend-allowed", NULL))
|
||||
dom->system_suspend_allowed = TRUE;
|
||||
else
|
||||
dom->system_suspend_allowed = FALSE;
|
||||
|
||||
/* Find /cpus DT node */
|
||||
cpus_offset = fdt_path_offset(fdt, "/cpus");
|
||||
if (cpus_offset < 0)
|
||||
@@ -420,9 +420,6 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (SBI_HARTMASK_MAX_BITS <= val32)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
|
||||
if (!val || len < 4)
|
||||
return SBI_EINVAL;
|
||||
@@ -469,9 +466,6 @@ int fdt_domains_populate(void *fdt)
|
||||
if (hartid != cold_hartid)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
|
||||
if (val && len >= 4)
|
||||
cold_domain_offset = fdt_node_offset_by_phandle(fdt,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi_utils/fdt/fdt_fixup.h>
|
||||
#include <sbi_utils/fdt/fdt_pmu.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
void fdt_cpu_fixup(void *fdt)
|
||||
@@ -38,9 +37,6 @@ void fdt_cpu_fixup(void *fdt)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Disable a HART DT node if one of the following is true:
|
||||
* 1. The HART is not assigned to the current domain
|
||||
@@ -55,43 +51,6 @@ void fdt_cpu_fixup(void *fdt)
|
||||
}
|
||||
}
|
||||
|
||||
static void fdt_domain_based_fixup_one(void *fdt, int nodeoff)
|
||||
{
|
||||
int rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return;
|
||||
|
||||
if (!sbi_domain_check_addr(dom, reg_addr, dom->next_mode,
|
||||
SBI_DOMAIN_READ | SBI_DOMAIN_WRITE)) {
|
||||
rc = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 32);
|
||||
if (rc < 0)
|
||||
return;
|
||||
fdt_setprop_string(fdt, nodeoff, "status", "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void fdt_aplic_fixup(void *fdt)
|
||||
{
|
||||
int noff = 0;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,aplic")) >= 0)
|
||||
fdt_domain_based_fixup_one(fdt, noff);
|
||||
}
|
||||
|
||||
void fdt_imsic_fixup(void *fdt)
|
||||
{
|
||||
int noff = 0;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,imsics")) >= 0)
|
||||
fdt_domain_based_fixup_one(fdt, noff);
|
||||
}
|
||||
|
||||
void fdt_plic_fixup(void *fdt)
|
||||
{
|
||||
u32 *cells;
|
||||
@@ -301,12 +260,9 @@ int fdt_reserved_memory_nomap_fixup(void *fdt)
|
||||
|
||||
void fdt_fixups(void *fdt)
|
||||
{
|
||||
fdt_aplic_fixup(fdt);
|
||||
|
||||
fdt_imsic_fixup(fdt);
|
||||
|
||||
fdt_plic_fixup(fdt);
|
||||
|
||||
fdt_reserved_memory_fixup(fdt);
|
||||
fdt_pmu_fixup(fdt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,23 +13,19 @@
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/aplic.h>
|
||||
#include <sbi_utils/irqchip/imsic.h>
|
||||
#include <sbi_utils/irqchip/plic.h>
|
||||
#include <sbi_utils/sys/clint.h>
|
||||
|
||||
#define DEFAULT_UART_FREQ 0
|
||||
#define DEFAULT_UART_BAUD 115200
|
||||
#define DEFAULT_UART_REG_SHIFT 0
|
||||
#define DEFAULT_UART_REG_IO_WIDTH 1
|
||||
#define DEFAULT_UART_REG_OFFSET 0
|
||||
|
||||
#define DEFAULT_SIFIVE_UART_FREQ 0
|
||||
#define DEFAULT_SIFIVE_UART_BAUD 115200
|
||||
#define DEFAULT_SIFIVE_UART_REG_SHIFT 0
|
||||
#define DEFAULT_SIFIVE_UART_REG_IO_WIDTH 4
|
||||
|
||||
#define DEFAULT_GAISLER_UART_REG_IO_WIDTH 4
|
||||
|
||||
#define DEFAULT_SHAKTI_UART_FREQ 50000000
|
||||
#define DEFAULT_SHAKTI_UART_BAUD 115200
|
||||
|
||||
@@ -75,55 +71,8 @@ int fdt_find_match(void *fdt, int startoff,
|
||||
return SBI_ENODEV;
|
||||
}
|
||||
|
||||
int fdt_parse_phandle_with_args(void *fdt, int nodeoff,
|
||||
const char *prop, const char *cells_prop,
|
||||
int index, struct fdt_phandle_args *out_args)
|
||||
{
|
||||
u32 i, pcells;
|
||||
int len, pnodeoff;
|
||||
const fdt32_t *list, *list_end, *val;
|
||||
|
||||
if (!fdt || (nodeoff < 0) || !prop || !cells_prop || !out_args)
|
||||
return SBI_EINVAL;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoff, prop, &len);
|
||||
if (!list)
|
||||
return SBI_ENOENT;
|
||||
list_end = list + (len / sizeof(*list));
|
||||
|
||||
while (list < list_end) {
|
||||
pnodeoff = fdt_node_offset_by_phandle(fdt,
|
||||
fdt32_to_cpu(*list));
|
||||
if (pnodeoff < 0)
|
||||
return pnodeoff;
|
||||
list++;
|
||||
|
||||
val = fdt_getprop(fdt, pnodeoff, cells_prop, &len);
|
||||
if (!val)
|
||||
return SBI_ENOENT;
|
||||
pcells = fdt32_to_cpu(*val);
|
||||
if (FDT_MAX_PHANDLE_ARGS < pcells)
|
||||
return SBI_EINVAL;
|
||||
if (list + pcells > list_end)
|
||||
return SBI_ENOENT;
|
||||
|
||||
if (index > 0) {
|
||||
list += pcells;
|
||||
index--;
|
||||
} else {
|
||||
out_args->node_offset = pnodeoff;
|
||||
out_args->args_count = pcells;
|
||||
for (i = 0; i < pcells; i++)
|
||||
out_args->args[i] = fdt32_to_cpu(list[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SBI_ENOENT;
|
||||
}
|
||||
|
||||
static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
|
||||
uint64_t *addr)
|
||||
unsigned long *addr)
|
||||
{
|
||||
int i, rlen;
|
||||
int cell_addr, cell_size;
|
||||
@@ -160,17 +109,14 @@ static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
uint64_t *addr, uint64_t *size)
|
||||
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
|
||||
unsigned long *size)
|
||||
{
|
||||
int parent, len, i, rc;
|
||||
int cell_addr, cell_size;
|
||||
const fdt32_t *prop_addr, *prop_size;
|
||||
uint64_t temp = 0;
|
||||
|
||||
if (!fdt || node < 0 || index < 0)
|
||||
return SBI_EINVAL;
|
||||
|
||||
parent = fdt_parent_offset(fdt, node);
|
||||
if (parent < 0)
|
||||
return parent;
|
||||
@@ -185,11 +131,6 @@ int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
prop_addr = fdt_getprop(fdt, node, "reg", &len);
|
||||
if (!prop_addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
if ((len / sizeof(u32)) <= (index * (cell_addr + cell_size)))
|
||||
return SBI_EINVAL;
|
||||
|
||||
prop_addr = prop_addr + (index * (cell_addr + cell_size));
|
||||
prop_size = prop_addr + cell_addr;
|
||||
|
||||
if (addr) {
|
||||
@@ -216,24 +157,6 @@ int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fdt_node_is_enabled(void *fdt, int nodeoff)
|
||||
{
|
||||
int len;
|
||||
const void *prop;
|
||||
|
||||
prop = fdt_getprop(fdt, nodeoff, "status", &len);
|
||||
if (!prop)
|
||||
return true;
|
||||
|
||||
if (!strncmp(prop, "okay", strlen("okay")))
|
||||
return true;
|
||||
|
||||
if (!strncmp(prop, "ok", strlen("ok")))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid)
|
||||
{
|
||||
int len;
|
||||
@@ -262,7 +185,7 @@ int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid)
|
||||
int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid)
|
||||
{
|
||||
u32 hartid;
|
||||
int err, cpu_offset, cpus_offset;
|
||||
@@ -283,9 +206,6 @@ int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
if (hartid > *max_hartid)
|
||||
*max_hartid = hartid;
|
||||
}
|
||||
@@ -293,78 +213,17 @@ int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
int len, cpus_offset;
|
||||
|
||||
if (!fdt || !freq)
|
||||
return SBI_EINVAL;
|
||||
|
||||
cpus_offset = fdt_path_offset(fdt, "/cpus");
|
||||
if (cpus_offset < 0)
|
||||
return cpus_offset;
|
||||
|
||||
val = fdt_getprop(fdt, cpus_offset, "timebase-frequency", &len);
|
||||
if (len > 0 && val)
|
||||
*freq = fdt32_to_cpu(*val);
|
||||
else
|
||||
return SBI_ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart)
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
|
||||
/**
|
||||
* UART address is mandatory. clock-frequency and current-speed
|
||||
* may not be present. Don't return error.
|
||||
*/
|
||||
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
|
||||
if (len > 0 && val)
|
||||
uart->freq = fdt32_to_cpu(*val);
|
||||
else
|
||||
uart->freq = DEFAULT_UART_FREQ;
|
||||
|
||||
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
|
||||
if (len > 0 && val)
|
||||
uart->baud = fdt32_to_cpu(*val);
|
||||
else
|
||||
uart->baud = DEFAULT_UART_BAUD;
|
||||
|
||||
/* For Gaisler APBUART, the reg-shift and reg-io-width are fixed .*/
|
||||
uart->reg_shift = DEFAULT_UART_REG_SHIFT;
|
||||
uart->reg_io_width = DEFAULT_GAISLER_UART_REG_IO_WIDTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart)
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
unsigned long reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
@@ -393,13 +252,12 @@ int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
unsigned long reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
@@ -432,13 +290,12 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
unsigned long reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
@@ -471,12 +328,6 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
else
|
||||
uart->reg_io_width = DEFAULT_UART_REG_IO_WIDTH;
|
||||
|
||||
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-offset", &len);
|
||||
if (len > 0 && val)
|
||||
uart->reg_offset = fdt32_to_cpu(*val);
|
||||
else
|
||||
uart->reg_offset = DEFAULT_UART_REG_OFFSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -495,295 +346,16 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
|
||||
return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
|
||||
}
|
||||
|
||||
int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart)
|
||||
{
|
||||
int rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic)
|
||||
{
|
||||
bool child_found;
|
||||
const fdt32_t *val;
|
||||
const fdt32_t *del;
|
||||
struct imsic_data imsic;
|
||||
int i, j, d, dcnt, len, noff, rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
struct aplic_delegate_data *deleg;
|
||||
|
||||
if (nodeoff < 0 || !aplic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
memset(aplic, 0, sizeof(*aplic));
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
aplic->addr = reg_addr;
|
||||
aplic->size = reg_size;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,num-sources", &len);
|
||||
if (len > 0)
|
||||
aplic->num_source = fdt32_to_cpu(*val);
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
|
||||
aplic->targets_mmode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aplic->num_idc = len / 2;
|
||||
goto aplic_msi_parent_done;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "msi-parent", &len);
|
||||
if (val && len >= sizeof(fdt32_t)) {
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
rc = fdt_parse_imsic_node(fdt, noff, &imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = imsic_data_check(&imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
aplic->targets_mmode = imsic.targets_mmode;
|
||||
|
||||
if (imsic.targets_mmode) {
|
||||
aplic->has_msicfg_mmode = true;
|
||||
aplic->msicfg_mmode.lhxs = imsic.guest_index_bits;
|
||||
aplic->msicfg_mmode.lhxw = imsic.hart_index_bits;
|
||||
aplic->msicfg_mmode.hhxw = imsic.group_index_bits;
|
||||
aplic->msicfg_mmode.hhxs = imsic.group_index_shift;
|
||||
if (aplic->msicfg_mmode.hhxs <
|
||||
(2 * IMSIC_MMIO_PAGE_SHIFT))
|
||||
return SBI_EINVAL;
|
||||
aplic->msicfg_mmode.hhxs -= 24;
|
||||
aplic->msicfg_mmode.base_addr = imsic.regs[0].addr;
|
||||
} else {
|
||||
goto aplic_msi_parent_done;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,children", &len);
|
||||
if (!val || len < sizeof(fdt32_t))
|
||||
goto aplic_msi_parent_done;
|
||||
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
val = fdt_getprop(fdt, noff, "msi-parent", &len);
|
||||
if (!val || len < sizeof(fdt32_t))
|
||||
goto aplic_msi_parent_done;
|
||||
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
rc = fdt_parse_imsic_node(fdt, noff, &imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = imsic_data_check(&imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!imsic.targets_mmode) {
|
||||
aplic->has_msicfg_smode = true;
|
||||
aplic->msicfg_smode.lhxs = imsic.guest_index_bits;
|
||||
aplic->msicfg_smode.lhxw = imsic.hart_index_bits;
|
||||
aplic->msicfg_smode.hhxw = imsic.group_index_bits;
|
||||
aplic->msicfg_smode.hhxs = imsic.group_index_shift;
|
||||
if (aplic->msicfg_smode.hhxs <
|
||||
(2 * IMSIC_MMIO_PAGE_SHIFT))
|
||||
return SBI_EINVAL;
|
||||
aplic->msicfg_smode.hhxs -= 24;
|
||||
aplic->msicfg_smode.base_addr = imsic.regs[0].addr;
|
||||
}
|
||||
}
|
||||
aplic_msi_parent_done:
|
||||
|
||||
for (d = 0; d < APLIC_MAX_DELEGATE; d++) {
|
||||
deleg = &aplic->delegate[d];
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
}
|
||||
|
||||
del = fdt_getprop(fdt, nodeoff, "riscv,delegate", &len);
|
||||
if (!del || len < (3 * sizeof(fdt32_t)))
|
||||
goto skip_delegate_parse;
|
||||
d = 0;
|
||||
dcnt = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < dcnt; i += 3) {
|
||||
if (d >= APLIC_MAX_DELEGATE)
|
||||
break;
|
||||
deleg = &aplic->delegate[d];
|
||||
|
||||
deleg->first_irq = fdt32_to_cpu(del[i + 1]);
|
||||
deleg->last_irq = fdt32_to_cpu(del[i + 2]);
|
||||
deleg->child_index = 0;
|
||||
|
||||
child_found = false;
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,children", &len);
|
||||
if (!val || len < sizeof(fdt32_t)) {
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
continue;
|
||||
}
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (j = 0; j < len; j++) {
|
||||
if (del[i] != val[j])
|
||||
continue;
|
||||
deleg->child_index = j;
|
||||
child_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_found) {
|
||||
d++;
|
||||
} else {
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
}
|
||||
}
|
||||
skip_delegate_parse:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fdt_check_imsic_mlevel(void *fdt)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
int i, len, noff = 0;
|
||||
|
||||
if (!fdt)
|
||||
return false;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,imsics")) >= 0) {
|
||||
val = fdt_getprop(fdt, noff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
struct imsic_regs *regs;
|
||||
uint64_t reg_addr, reg_size;
|
||||
int i, rc, len, nr_parent_irqs;
|
||||
|
||||
if (nodeoff < 0 || !imsic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
imsic->targets_mmode = false;
|
||||
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
nr_parent_irqs = len / 2;
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
|
||||
imsic->targets_mmode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
return SBI_EINVAL;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,guest-index-bits", &len);
|
||||
if (val && len > 0)
|
||||
imsic->guest_index_bits = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->guest_index_bits = 0;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,hart-index-bits", &len);
|
||||
if (val && len > 0) {
|
||||
imsic->hart_index_bits = fdt32_to_cpu(*val);
|
||||
} else {
|
||||
imsic->hart_index_bits = sbi_fls(nr_parent_irqs);
|
||||
if ((1UL << imsic->hart_index_bits) < nr_parent_irqs)
|
||||
imsic->hart_index_bits++;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,group-index-bits", &len);
|
||||
if (val && len > 0)
|
||||
imsic->group_index_bits = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->group_index_bits = 0;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,group-index-shift", &len);
|
||||
if (val && len > 0)
|
||||
imsic->group_index_shift = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->group_index_shift = 2 * IMSIC_MMIO_PAGE_SHIFT;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,num-ids", &len);
|
||||
if (val && len > 0)
|
||||
imsic->num_ids = fdt32_to_cpu(*val);
|
||||
else
|
||||
return SBI_EINVAL;
|
||||
|
||||
for (i = 0; i < IMSIC_MAX_REGS; i++) {
|
||||
regs = &imsic->regs[i];
|
||||
regs->addr = 0;
|
||||
regs->size = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < (IMSIC_MAX_REGS - 1); i++) {
|
||||
regs = &imsic->regs[i];
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, i,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
break;
|
||||
regs->addr = reg_addr;
|
||||
regs->size = reg_size;
|
||||
};
|
||||
if (!imsic->regs[0].size)
|
||||
return SBI_EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
unsigned long reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !plic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
plic->addr = reg_addr;
|
||||
@@ -809,51 +381,34 @@ int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat)
|
||||
return fdt_parse_plic_node(fdt, nodeoffset, plic);
|
||||
}
|
||||
|
||||
int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
unsigned long *out_addr1, unsigned long *out_size1,
|
||||
unsigned long *out_addr2, unsigned long *out_size2,
|
||||
u32 *out_first_hartid, u32 *out_hart_count)
|
||||
int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
struct clint_data *clint)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
uint64_t reg_addr, reg_size;
|
||||
unsigned long reg_addr, reg_size;
|
||||
int i, rc, count, cpu_offset, cpu_intc_offset;
|
||||
u32 phandle, hwirq, hartid, first_hartid, last_hartid, hart_count;
|
||||
u32 phandle, hwirq, hartid, first_hartid, last_hartid;
|
||||
u32 match_hwirq = (for_timer) ? IRQ_M_TIMER : IRQ_M_SOFT;
|
||||
|
||||
if (nodeoffset < 0 || !fdt ||
|
||||
!out_addr1 || !out_size1 ||
|
||||
!out_first_hartid || !out_hart_count)
|
||||
return SBI_EINVAL;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_size)
|
||||
if (nodeoffset < 0 || !clint || !fdt)
|
||||
return SBI_ENODEV;
|
||||
*out_addr1 = reg_addr;
|
||||
*out_size1 = reg_size;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 1,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_size)
|
||||
reg_addr = reg_size = 0;
|
||||
if (out_addr2)
|
||||
*out_addr2 = reg_addr;
|
||||
if (out_size2)
|
||||
*out_size2 = reg_size;
|
||||
|
||||
*out_first_hartid = 0;
|
||||
*out_hart_count = 0;
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
clint->addr = reg_addr;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count);
|
||||
if (!val || count < sizeof(fdt32_t))
|
||||
return 0;
|
||||
return SBI_EINVAL;
|
||||
count = count / sizeof(fdt32_t);
|
||||
|
||||
first_hartid = -1U;
|
||||
hart_count = last_hartid = 0;
|
||||
for (i = 0; i < (count / 2); i++) {
|
||||
phandle = fdt32_to_cpu(val[2 * i]);
|
||||
hwirq = fdt32_to_cpu(val[(2 * i) + 1]);
|
||||
last_hartid = 0;
|
||||
clint->hart_count = 0;
|
||||
for (i = 0; i < count; i += 2) {
|
||||
phandle = fdt32_to_cpu(val[i]);
|
||||
hwirq = fdt32_to_cpu(val[i + 1]);
|
||||
|
||||
cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
if (cpu_intc_offset < 0)
|
||||
@@ -875,20 +430,26 @@ int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
first_hartid = hartid;
|
||||
if (hartid > last_hartid)
|
||||
last_hartid = hartid;
|
||||
hart_count++;
|
||||
clint->hart_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((last_hartid >= first_hartid) && first_hartid != -1U) {
|
||||
*out_first_hartid = first_hartid;
|
||||
count = last_hartid - first_hartid + 1;
|
||||
*out_hart_count = (hart_count < count) ? hart_count : count;
|
||||
}
|
||||
if ((last_hartid < first_hartid) || first_hartid == -1U)
|
||||
return SBI_ENODEV;
|
||||
|
||||
clint->first_hartid = first_hartid;
|
||||
count = last_hartid - first_hartid + 1;
|
||||
if (clint->hart_count < count)
|
||||
clint->hart_count = count;
|
||||
|
||||
clint->has_64bit_mmio = TRUE;
|
||||
if (fdt_getprop(fdt, nodeoffset, "clint,has-no-64bit-mmio", &count))
|
||||
clint->has_64bit_mmio = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
|
||||
const char *compatible)
|
||||
{
|
||||
int nodeoffset, rc;
|
||||
@@ -897,7 +458,7 @@ int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
if (nodeoffset < 0)
|
||||
return nodeoffset;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0, addr, NULL);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, addr, NULL);
|
||||
if (rc < 0 || !addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* fdt_pmu.c - Flat Device Tree PMU helper routines
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Atish Patra <atish.patra@wdc.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
#define FDT_PMU_HW_EVENT_MAX (SBI_PMU_HW_EVENT_MAX * 2)
|
||||
|
||||
struct fdt_pmu_hw_event_select {
|
||||
uint32_t eidx;
|
||||
uint64_t select;
|
||||
};
|
||||
|
||||
static struct fdt_pmu_hw_event_select fdt_pmu_evt_select[FDT_PMU_HW_EVENT_MAX] = {0};
|
||||
static uint32_t hw_event_count;
|
||||
|
||||
uint64_t fdt_pmu_get_select_value(uint32_t event_idx)
|
||||
{
|
||||
int i;
|
||||
struct fdt_pmu_hw_event_select *event;
|
||||
|
||||
for (i = 0; i < SBI_PMU_HW_EVENT_MAX; i++) {
|
||||
event = &fdt_pmu_evt_select[i];
|
||||
if (event->eidx == event_idx)
|
||||
return event->select;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_pmu_fixup(void *fdt)
|
||||
{
|
||||
int pmu_offset;
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
if (!fdt)
|
||||
return SBI_EINVAL;
|
||||
|
||||
pmu_offset = fdt_node_offset_by_compatible(fdt, -1, "riscv,pmu");
|
||||
if (pmu_offset < 0)
|
||||
return SBI_EFAIL;
|
||||
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmcounters");
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmevent");
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,raw-event-to-mhpmcounters");
|
||||
if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
fdt_delprop(fdt, pmu_offset, "interrupts-extended");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_pmu_setup(void *fdt)
|
||||
{
|
||||
int i, pmu_offset, len, result;
|
||||
const u32 *event_val;
|
||||
const u32 *event_ctr_map;
|
||||
struct fdt_pmu_hw_event_select *event;
|
||||
uint64_t raw_selector, select_mask;
|
||||
u32 event_idx_start, event_idx_end, ctr_map;
|
||||
|
||||
if (!fdt)
|
||||
return SBI_EINVAL;
|
||||
|
||||
pmu_offset = fdt_node_offset_by_compatible(fdt, -1, "riscv,pmu");
|
||||
if (pmu_offset < 0)
|
||||
return SBI_EFAIL;
|
||||
|
||||
event_ctr_map = fdt_getprop(fdt, pmu_offset, "riscv,event-to-mhpmcounters", &len);
|
||||
if (!event_ctr_map || len < 8)
|
||||
return SBI_EFAIL;
|
||||
len = len / (sizeof(u32) * 3);
|
||||
for (i = 0; i < len; i++) {
|
||||
event_idx_start = fdt32_to_cpu(event_ctr_map[3 * i]);
|
||||
event_idx_end = fdt32_to_cpu(event_ctr_map[3 * i + 1]);
|
||||
ctr_map = fdt32_to_cpu(event_ctr_map[3 * i + 2]);
|
||||
sbi_pmu_add_hw_event_counter_map(event_idx_start, event_idx_end, ctr_map);
|
||||
}
|
||||
|
||||
event_val = fdt_getprop(fdt, pmu_offset, "riscv,event-to-mhpmevent", &len);
|
||||
if (!event_val || len < 8)
|
||||
return SBI_EFAIL;
|
||||
len = len / (sizeof(u32) * 3);
|
||||
for (i = 0; i < len; i++) {
|
||||
event = &fdt_pmu_evt_select[hw_event_count];
|
||||
event->eidx = fdt32_to_cpu(event_val[3 * i]);
|
||||
event->select = fdt32_to_cpu(event_val[3 * i + 1]);
|
||||
event->select = (event->select << 32) | fdt32_to_cpu(event_val[3 * i + 2]);
|
||||
hw_event_count++;
|
||||
}
|
||||
|
||||
event_val = fdt_getprop(fdt, pmu_offset, "riscv,raw-event-to-mhpmcounters", &len);
|
||||
if (!event_val || len < 20)
|
||||
return SBI_EFAIL;
|
||||
len = len / (sizeof(u32) * 5);
|
||||
for (i = 0; i < len; i++) {
|
||||
raw_selector = fdt32_to_cpu(event_val[5 * i]);
|
||||
raw_selector = (raw_selector << 32) | fdt32_to_cpu(event_val[5 * i + 1]);
|
||||
select_mask = fdt32_to_cpu(event_val[5 * i + 2]);
|
||||
select_mask = (select_mask << 32) | fdt32_to_cpu(event_val[5 * i + 3]);
|
||||
ctr_map = fdt32_to_cpu(event_val[5 * i + 4]);
|
||||
result = sbi_pmu_add_raw_event_counter_map(raw_selector, select_mask, ctr_map);
|
||||
if (!result)
|
||||
hw_event_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,6 +5,5 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += fdt/fdt_domain.o
|
||||
libsbiutils-objs-y += fdt/fdt_pmu.o
|
||||
libsbiutils-objs-y += fdt/fdt_helper.o
|
||||
libsbiutils-objs-y += fdt/fdt_fixup.o
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
|
||||
/* List of FDT gpio drivers generated at compile time */
|
||||
extern struct fdt_gpio *fdt_gpio_drivers[];
|
||||
extern unsigned long fdt_gpio_drivers_size;
|
||||
|
||||
static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (!chip)
|
||||
return NULL;
|
||||
|
||||
for (pos = 0; pos < fdt_gpio_drivers_size; pos++) {
|
||||
if (chip->driver == fdt_gpio_drivers[pos])
|
||||
return fdt_gpio_drivers[pos];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int fdt_gpio_init(void *fdt, u32 phandle)
|
||||
{
|
||||
int pos, nodeoff, rc;
|
||||
struct fdt_gpio *drv;
|
||||
const struct fdt_match *match;
|
||||
|
||||
/* Find node offset */
|
||||
nodeoff = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
if (nodeoff < 0)
|
||||
return nodeoff;
|
||||
|
||||
/* Check "gpio-controller" property */
|
||||
if (!fdt_getprop(fdt, nodeoff, "gpio-controller", &rc))
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Try all GPIO drivers one-by-one */
|
||||
for (pos = 0; pos < fdt_gpio_drivers_size; pos++) {
|
||||
drv = fdt_gpio_drivers[pos];
|
||||
|
||||
match = fdt_match_node(fdt, nodeoff, drv->match_table);
|
||||
if (match && drv->init) {
|
||||
rc = drv->init(fdt, nodeoff, phandle, match);
|
||||
if (rc == SBI_ENODEV)
|
||||
continue;
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SBI_ENOSYS;
|
||||
}
|
||||
|
||||
static int fdt_gpio_chip_find(void *fdt, u32 phandle,
|
||||
struct gpio_chip **out_chip)
|
||||
{
|
||||
int rc;
|
||||
struct gpio_chip *chip = gpio_chip_find(phandle);
|
||||
|
||||
if (!chip) {
|
||||
/* GPIO chip not found so initialize matching driver */
|
||||
rc = fdt_gpio_init(fdt, phandle);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Try to find GPIO chip again */
|
||||
chip = gpio_chip_find(phandle);
|
||||
if (!chip)
|
||||
return SBI_ENOSYS;
|
||||
}
|
||||
|
||||
if (out_chip)
|
||||
*out_chip = chip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_gpio_pin_get(void *fdt, int nodeoff, int index,
|
||||
struct gpio_pin *out_pin)
|
||||
{
|
||||
int rc;
|
||||
u32 phandle;
|
||||
struct fdt_gpio *drv;
|
||||
struct gpio_chip *chip = NULL;
|
||||
struct fdt_phandle_args pargs;
|
||||
|
||||
if (!fdt || (nodeoff < 0) || (index < 0) || !out_pin)
|
||||
return SBI_EINVAL;
|
||||
|
||||
pargs.node_offset = pargs.args_count = 0;
|
||||
rc = fdt_parse_phandle_with_args(fdt, nodeoff,
|
||||
"gpios", "#gpio-cells",
|
||||
index, &pargs);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
phandle = fdt_get_phandle(fdt, pargs.node_offset);
|
||||
rc = fdt_gpio_chip_find(fdt, phandle, &chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
drv = fdt_gpio_driver(chip);
|
||||
if (!drv || !drv->xlate)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return drv->xlate(chip, &pargs, out_pin);
|
||||
}
|
||||
|
||||
int fdt_gpio_simple_xlate(struct gpio_chip *chip,
|
||||
const struct fdt_phandle_args *pargs,
|
||||
struct gpio_pin *out_pin)
|
||||
{
|
||||
if ((pargs->args_count < 2) || (chip->ngpio <= pargs->args[0]))
|
||||
return SBI_EINVAL;
|
||||
|
||||
out_pin->chip = chip;
|
||||
out_pin->offset = pargs->args[0];
|
||||
out_pin->flags = pargs->args[1];
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
HEADER: sbi_utils/gpio/fdt_gpio.h
|
||||
TYPE: struct fdt_gpio
|
||||
NAME: fdt_gpio_drivers
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 SiFive
|
||||
*
|
||||
* Authors:
|
||||
* Green Wan <green.wan@sifive.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
|
||||
#define SIFIVE_GPIO_CHIP_MAX 2
|
||||
|
||||
#define SIFIVE_GPIO_PINS_MIN 1
|
||||
#define SIFIVE_GPIO_PINS_MAX 32
|
||||
#define SIFIVE_GPIO_PINS_DEF 16
|
||||
|
||||
#define SIFIVE_GPIO_OUTEN 0x8
|
||||
#define SIFIVE_GPIO_OUTVAL 0xc
|
||||
#define SIFIVE_GPIO_BIT(b) (1U << (b))
|
||||
|
||||
struct sifive_gpio_chip {
|
||||
unsigned long addr;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
static unsigned int sifive_gpio_chip_count;
|
||||
static struct sifive_gpio_chip sifive_gpio_chip_array[SIFIVE_GPIO_CHIP_MAX];
|
||||
|
||||
static int sifive_gpio_direction_output(struct gpio_pin *gp, int value)
|
||||
{
|
||||
unsigned int v;
|
||||
struct sifive_gpio_chip *chip =
|
||||
container_of(gp->chip, struct sifive_gpio_chip, chip);
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTEN));
|
||||
v |= SIFIVE_GPIO_BIT(gp->offset);
|
||||
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTEN));
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
|
||||
if (!value)
|
||||
v &= ~SIFIVE_GPIO_BIT(gp->offset);
|
||||
else
|
||||
v |= SIFIVE_GPIO_BIT(gp->offset);
|
||||
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_gpio_set(struct gpio_pin *gp, int value)
|
||||
{
|
||||
unsigned int v;
|
||||
struct sifive_gpio_chip *chip =
|
||||
container_of(gp->chip, struct sifive_gpio_chip, chip);
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
|
||||
if (!value)
|
||||
v &= ~SIFIVE_GPIO_BIT(gp->offset);
|
||||
else
|
||||
v |= SIFIVE_GPIO_BIT(gp->offset);
|
||||
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
|
||||
}
|
||||
|
||||
extern struct fdt_gpio fdt_gpio_sifive;
|
||||
|
||||
static int sifive_gpio_init(void *fdt, int nodeoff, u32 phandle,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct sifive_gpio_chip *chip;
|
||||
uint64_t addr;
|
||||
|
||||
if (SIFIVE_GPIO_CHIP_MAX <= sifive_gpio_chip_count)
|
||||
return SBI_ENOSPC;
|
||||
chip = &sifive_gpio_chip_array[sifive_gpio_chip_count];
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
chip->addr = addr;
|
||||
chip->chip.driver = &fdt_gpio_sifive;
|
||||
chip->chip.id = phandle;
|
||||
chip->chip.ngpio = SIFIVE_GPIO_PINS_DEF;
|
||||
chip->chip.direction_output = sifive_gpio_direction_output;
|
||||
chip->chip.set = sifive_gpio_set;
|
||||
rc = gpio_chip_add(&chip->chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sifive_gpio_chip_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match sifive_gpio_match[] = {
|
||||
{ .compatible = "sifive,gpio0" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_gpio fdt_gpio_sifive = {
|
||||
.match_table = sifive_gpio_match,
|
||||
.xlate = fdt_gpio_simple_xlate,
|
||||
.init = sifive_gpio_init,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user