Compare commits
151 Commits
cv18xx-v4.
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f91ee9c9 | ||
|
|
d30bde36d5 | ||
|
|
2082153fc9 | ||
|
|
f270359810 | ||
|
|
d249d6544c | ||
|
|
69d7e53613 | ||
|
|
460041c816 | ||
|
|
66fbcc03df | ||
|
|
2428987cc0 | ||
|
|
14faee6916 | ||
|
|
7e77706966 | ||
|
|
7a22c780df | ||
|
|
22d556d268 | ||
|
|
52af6e4b52 | ||
|
|
d335a178d8 | ||
|
|
f3744967c6 | ||
|
|
13a1158d24 | ||
|
|
6ca6bcafb7 | ||
|
|
2c964a2e15 | ||
|
|
1d462e0397 | ||
|
|
94eba23376 | ||
|
|
31fe5a7a3d | ||
|
|
b8845e4204 | ||
|
|
15906a3984 | ||
|
|
b28f07005c | ||
|
|
b628cfd6a0 | ||
|
|
1e147324f0 | ||
|
|
0c304b6619 | ||
|
|
2363f950bc | ||
|
|
730f01bb41 | ||
|
|
9134c3643e | ||
|
|
867c65360d | ||
|
|
7084ad9f42 | ||
|
|
a74daf2cb9 | ||
|
|
be245acfff | ||
|
|
57f094e67e | ||
|
|
2fe2f55d50 | ||
|
|
0979ffda12 | ||
|
|
013ba4ef3d | ||
|
|
c891acca17 | ||
|
|
723aa88ff4 | ||
|
|
309e8bdf85 | ||
|
|
78c2b19218 | ||
|
|
422eda499c | ||
|
|
67cbbcb100 | ||
|
|
c38973e087 | ||
|
|
9283d503bd | ||
|
|
516161c46f | ||
|
|
754d51192b | ||
|
|
fa59dd317a | ||
|
|
9d0ab35ab4 | ||
|
|
6355155f51 | ||
|
|
12e7af9ab7 | ||
|
|
72154f4708 | ||
|
|
51113fe2a5 | ||
|
|
12753d2256 | ||
|
|
3477f08b08 | ||
|
|
395ff7eede | ||
|
|
0274a96004 | ||
|
|
bd355213bf | ||
|
|
1718b1642e | ||
|
|
c262306533 | ||
|
|
bd316e2c9b | ||
|
|
b1d3e91e9a | ||
|
|
ee274377b2 | ||
|
|
33eac764f2 | ||
|
|
7aa6c9aa96 | ||
|
|
b35f7826b0 | ||
|
|
f3a0eb8583 | ||
|
|
7a3a0cce4d | ||
|
|
e0d1b9db8a | ||
|
|
47a47654e8 | ||
|
|
d244f3dbd6 | ||
|
|
e928472e67 | ||
|
|
ce03c88ee1 | ||
|
|
217d5e4880 | ||
|
|
74db0acbe5 | ||
|
|
989039117f | ||
|
|
81eb7088b6 | ||
|
|
2c74dc3c47 | ||
|
|
a4555e5698 | ||
|
|
8a1475b5a1 | ||
|
|
f3a8f603a7 | ||
|
|
7210e90785 | ||
|
|
e3d6919d10 | ||
|
|
4c3df2ab96 | ||
|
|
c14f1fe0df | ||
|
|
36b8effe4a | ||
|
|
e931f387b2 | ||
|
|
17729d44da | ||
|
|
2942777425 | ||
|
|
09ad811ec4 | ||
|
|
a3d328ae33 | ||
|
|
397afe5ba1 | ||
|
|
7f1be8a624 | ||
|
|
a76ac4449b | ||
|
|
b88b3661d4 | ||
|
|
dcb756b01a | ||
|
|
abfce9b25c | ||
|
|
dafaa0f54b | ||
|
|
14c7f71c0d | ||
|
|
0e12aa8dee | ||
|
|
ec1b8bb763 | ||
|
|
764a17d852 | ||
|
|
37f9b0f2f2 | ||
|
|
ae72ec0915 | ||
|
|
13d40f21d5 | ||
|
|
e7cc7a3ab2 | ||
|
|
49966db306 | ||
|
|
fd9116bd46 | ||
|
|
41ae63cd0a | ||
|
|
9c9b4ad24b | ||
|
|
0829f2bc28 | ||
|
|
d3a96cc469 | ||
|
|
fde28fadc2 | ||
|
|
3e8b31aca9 | ||
|
|
17e23b678d | ||
|
|
197e08941b | ||
|
|
b7f2cd268b | ||
|
|
a731c7e369 | ||
|
|
03d6bb51ba | ||
|
|
56fc5f7618 | ||
|
|
bd5d2089b8 | ||
|
|
5a049fe1d6 | ||
|
|
4519e29c51 | ||
|
|
11c345f14a | ||
|
|
99017946f3 | ||
|
|
cdcf907b19 | ||
|
|
eb90e0a16c | ||
|
|
79f9b4220f | ||
|
|
360ab88569 | ||
|
|
1da3d80b5b | ||
|
|
434198e3be | ||
|
|
de446ccf18 | ||
|
|
b32fac4b65 | ||
|
|
a03ea2e2b1 | ||
|
|
f30b18944e | ||
|
|
66c4fca532 | ||
|
|
d9ba6536d3 | ||
|
|
54d7def6c2 | ||
|
|
b2dbbc0577 | ||
|
|
fe92347b9f | ||
|
|
ee7c2b27ea | ||
|
|
c9ef2bc7e4 | ||
|
|
6139ab272b | ||
|
|
e822b7504d | ||
|
|
f90c4c2e02 | ||
|
|
26998f3d11 | ||
|
|
d4177e7217 | ||
|
|
552f53f360 | ||
|
|
117fb6dcb1 |
122
Makefile
122
Makefile
@@ -76,26 +76,54 @@ 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
|
||||
|
||||
# Guess the compillers xlen
|
||||
OPENSBI_CC_XLEN := $(shell TMP=`$(CC) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP})
|
||||
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)
|
||||
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
|
||||
@@ -106,6 +134,44 @@ 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)
|
||||
|
||||
# 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)
|
||||
@@ -194,20 +260,32 @@ else
|
||||
endif
|
||||
|
||||
# Setup compilation commands flags
|
||||
GENFLAGS = -I$(platform_src_dir)/include
|
||||
ifeq ($(CC_IS_CLANG),y)
|
||||
GENFLAGS += $(CLANG_TARGET)
|
||||
GENFLAGS += -Wno-unused-command-line-argument
|
||||
endif
|
||||
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-strict-aliasing -O2
|
||||
CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
CFLAGS += -mno-save-restore -mstrict-align
|
||||
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
|
||||
endif
|
||||
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
|
||||
@@ -218,22 +296,36 @@ CPPFLAGS += $(platform-cppflags-y)
|
||||
CPPFLAGS += $(firmware-cppflags-y)
|
||||
|
||||
ASFLAGS = -g -Wall -nostdlib
|
||||
ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
ASFLAGS += -mno-save-restore -mstrict-align
|
||||
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 += -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 += -Wl,--build-id=none -N -static-libgcc -lgcc
|
||||
ELFFLAGS += $(USE_LD_FLAG)
|
||||
ELFFLAGS += -Wl,--build-id=none -Wl,-N
|
||||
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
|
||||
@@ -338,6 +430,11 @@ $(build_dir)/%.dep: $(src_dir)/%.c
|
||||
$(build_dir)/%.o: $(src_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,$@,$<)
|
||||
|
||||
@@ -491,3 +588,6 @@ 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,8 +96,21 @@ Required Toolchain
|
||||
------------------
|
||||
|
||||
OpenSBI can be compiled natively or cross-compiled on a x86 host. For
|
||||
cross-compilation, you can build your own toolchain or just download
|
||||
a prebuilt one from the [Bootlin toolchain repository].
|
||||
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*).
|
||||
|
||||
Please note that only a 64-bit version of the toolchain is available in
|
||||
the Bootlin toolchain repository for now.
|
||||
@@ -111,7 +124,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-unknown-elf-* if the gcc executable used is *riscv64-unknown-elf-gcc*.
|
||||
*riscv64-linux-gnu-* if the gcc executable used is *riscv64-linux-gnu-gcc*.
|
||||
|
||||
To build *libsbi.a* simply execute:
|
||||
```
|
||||
@@ -187,21 +200,83 @@ 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-unknown-elf-*, 64-bit OpenSBI images will be generated. If building
|
||||
to *riscv64-linux-gnu-*, 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-unknown-elf-*.
|
||||
pre-configured to generate 32-bit RISC-V codes, like *riscv32-linux-gnu-*.
|
||||
|
||||
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-unknown-elf-
|
||||
export CROSS_COMPILE=riscv64-linux-gnu-
|
||||
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
|
||||
-----------------------
|
||||
|
||||
@@ -284,3 +359,4 @@ 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
|
||||
|
||||
@@ -9,6 +9,13 @@ 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*)
|
||||
@@ -62,6 +69,12 @@ 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,8 +6,9 @@ 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 it's address to *FW_DYNAMIC*
|
||||
via *a2* register of RISC-V CPU.
|
||||
*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.
|
||||
|
||||
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 fundamentalAHB/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 fundamental AHB/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,11 +46,13 @@ RISC-V Platforms Using Generic Platform
|
||||
---------------------------------------
|
||||
|
||||
* **QEMU RISC-V Virt Machine** (*[qemu_virt.md]*)
|
||||
* **Spike** (*[spike.md]*)
|
||||
* **Shakti C-class SoC Platform** (*[shakti_cclass.md]*)
|
||||
* **SiFive HiFive Unleashed** (*[sifive_fu540.md]*)
|
||||
* **Spike** (*[spike.md]*)
|
||||
* **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*)
|
||||
|
||||
[qemu_virt.md]: qemu_virt.md
|
||||
[spike.md]: spike.md
|
||||
[shakti_cclass.md]: shakti_cclass.md
|
||||
[sifive_fu540.md]: sifive_fu540.md
|
||||
[spike.md]: spike.md
|
||||
[thead-c9xx.md]: thead-c9xx.md
|
||||
|
||||
@@ -147,3 +147,27 @@ 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=sifive/fu540* parameter to the top level `make` command.
|
||||
*PLATFORM=generic* 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=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
|
||||
make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
|
||||
or
|
||||
(For Linux v5.2 or higher)
|
||||
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>
|
||||
make PLATFORM=generic 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=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin
|
||||
make PLATFORM=generic 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/sifive/fu540/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024
|
||||
dd if=build/platform/generic/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=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
|
||||
make PLATFORM=generic 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/sifive/fu540/firmware/fw_payload.bin
|
||||
-bios build/platform/generic/firmware/fw_payload.bin
|
||||
```
|
||||
or
|
||||
```
|
||||
qemu-system-riscv64 -M sifive_u -m 256M -nographic \
|
||||
-bios build/platform/sifive/fu540/firmware/fw_jump.bin \
|
||||
-bios build/platform/generic/firmware/fw_jump.bin \
|
||||
-kernel <uboot_build_dir>/u-boot.bin
|
||||
```
|
||||
|
||||
|
||||
@@ -43,7 +43,18 @@ make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Im
|
||||
|
||||
Run:
|
||||
```
|
||||
spike --initrd <path_to_cpio_ramdisk> build/platform/generic/firmware/fw_payload.elf
|
||||
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
|
||||
```
|
||||
|
||||
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 FW_PIC=y /usr/bin/make
|
||||
CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic /usr/bin/make
|
||||
```
|
||||
|
||||
The *T-HEAD C9xx* DTB provided to OpenSBI generic firmwares will usually have
|
||||
@@ -51,11 +51,6 @@ 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 = "riscv,clint0";
|
||||
interrupts-extended = <
|
||||
@@ -67,7 +62,8 @@ DTS Example1: (Single core, eg: Allwinner D1 - c906)
|
||||
|
||||
intc: interrupt-controller@10000000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "riscv,plic0";
|
||||
compatible = "allwinner,sun20i-d1-plic",
|
||||
"thead,c900-plic";
|
||||
interrupt-controller;
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 0xffffffff &cpu0_intc 9
|
||||
@@ -150,7 +146,6 @@ 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>;
|
||||
@@ -173,7 +168,7 @@ DTS Example2: (Multi cores with soc reset-regs)
|
||||
|
||||
intc: interrupt-controller@ffd8000000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "riscv,plic0";
|
||||
compatible = "thead,c900-plic";
|
||||
interrupt-controller;
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 0xffffffff &cpu0_intc 9
|
||||
@@ -194,7 +189,6 @@ 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 *riskv-pk* boot loader (BBL).
|
||||
for the legacy *riscv-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.
|
||||
|
||||
87
docs/pmu_support.md
Normal file
87
docs/pmu_support.md
Normal file
@@ -0,0 +1,87 @@
|
||||
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, which depends on
|
||||
how the platform encodes the monitor events. Currently, only the following three
|
||||
encoding methods are supported, encoding each event as a number, using a bitmap
|
||||
to encode monitor events, and mixing the previous two methods. The information
|
||||
is encoded in a table format where each row represent the specific raw event(s).
|
||||
The first column represents a 64-bit selector value which can indicate an
|
||||
monitor event ID (encoded by a number) or an event set (encoded by a bitmap).
|
||||
In case of the latter, the lower bits used to encode a set of events should be
|
||||
set to zero. The second column is a 64-bit selector mask where any bits used
|
||||
for event encoding will be cleared. If a platform directly encodes each raw PMU
|
||||
event as a unique ID, the value of select_mask will be 0xffffffff_ffffffff.
|
||||
The third column represent a bitmap of all the MHPMCOUNTERx that can be used for
|
||||
monitoring the specified event(s).
|
||||
|
||||
*Note:* A platform may choose to provide the mapping between event & counters
|
||||
via platform hooks rather than the device tree.
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
pmu {
|
||||
compatible = "riscv,pmu";
|
||||
interrupts = <0x100>;
|
||||
interrupt-parent = <&plic>
|
||||
riscv,event-to-mhpmevent = <0x0000B 0x0000 0x0001>,
|
||||
riscv,event-to-mhpmcounters = <0x00001 0x00001 0x00000001>,
|
||||
<0x00002 0x00002 0x00000004>,
|
||||
<0x00003 0x0000A 0x00000ff8>,
|
||||
<0x10000 0x10033 0x000ff000>,
|
||||
riscv,raw-event-to-mhpmcounters = <0x0000 0x0002 0xffffffff 0xffffffff 0x00000f8>,
|
||||
<0xffffffff 0xfffffff0 0xffffffff 0xfffffff0 0x00000ff0>,
|
||||
};
|
||||
|
||||
```
|
||||
@@ -65,14 +65,14 @@ _try_lottery:
|
||||
|
||||
/* Save load address */
|
||||
lla t0, _load_start
|
||||
lla t1, _start
|
||||
lla t1, _fw_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 _start */
|
||||
/* t1 shall has the address of _fw_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, _start
|
||||
lla t0, _fw_start
|
||||
lla t1, _link_start
|
||||
REG_L t1, 0(t1)
|
||||
beq t0, t1, _wait_for_boot_hart
|
||||
@@ -278,6 +278,14 @@ _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
|
||||
@@ -287,10 +295,7 @@ _scratch_init:
|
||||
/* Initialize scratch space */
|
||||
/* Store fw_start and fw_size in scratch space */
|
||||
lla a4, _fw_start
|
||||
lla a5, _fw_end
|
||||
mul t0, s7, s8
|
||||
add a5, a5, t0
|
||||
sub a5, a5, a4
|
||||
sub a5, t3, 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 */
|
||||
@@ -344,8 +349,7 @@ _scratch_init:
|
||||
* previous booting stage.
|
||||
*/
|
||||
beqz a1, _fdt_reloc_done
|
||||
/* Mask values in a3 and a4 */
|
||||
li a3, ~(__SIZEOF_POINTER__ - 1)
|
||||
/* Mask values in a4 */
|
||||
li a4, 0xff
|
||||
/* t1 = destination FDT start address */
|
||||
MOV_3R s0, a0, s1, a1, s2, a2
|
||||
@@ -354,10 +358,8 @@ _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)
|
||||
@@ -501,6 +503,7 @@ _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:
|
||||
@@ -553,12 +556,39 @@ _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, 0x2
|
||||
li a1, FW_DYNAMIC_INFO_VERSION_2
|
||||
blt a0, a1, 2f
|
||||
REG_L a0, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2)
|
||||
ret
|
||||
@@ -57,14 +57,6 @@ 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)
|
||||
@@ -77,7 +69,7 @@ fw_save_info:
|
||||
REG_S a3, (a4)
|
||||
|
||||
/* Save version == 0x2 fields */
|
||||
li a4, 0x2
|
||||
li a4, FW_DYNAMIC_INFO_VERSION_2
|
||||
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 := y
|
||||
FW_PIC := $(OPENSBI_LD_PIE)
|
||||
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
|
||||
firmware-ldflags-y += -Wl,--no-dynamic-linker -Wl,-pie
|
||||
endif
|
||||
|
||||
ifdef FW_TEXT_START
|
||||
|
||||
@@ -9,24 +9,25 @@
|
||||
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
|
||||
#define SBI_ECALL(__num, __a0, __a1, __a2) \
|
||||
#define SBI_ECALL(__eid, __fid, __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 a7 asm("a7") = (unsigned long)(__num); \
|
||||
register unsigned long a6 asm("a6") = (unsigned long)(__fid); \
|
||||
register unsigned long a7 asm("a7") = (unsigned long)(__eid); \
|
||||
asm volatile("ecall" \
|
||||
: "+r"(a0) \
|
||||
: "r"(a1), "r"(a2), "r"(a7) \
|
||||
: "r"(a1), "r"(a2), "r"(a6), "r"(a7) \
|
||||
: "memory"); \
|
||||
a0; \
|
||||
})
|
||||
|
||||
#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_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_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, (c))
|
||||
#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, (c))
|
||||
|
||||
static inline void sbi_ecall_console_puts(const char *str)
|
||||
{
|
||||
|
||||
@@ -15,23 +15,24 @@
|
||||
/* clang-format off */
|
||||
|
||||
/** Offset of magic member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_LONG__)
|
||||
/** Offset of version member in fw_dynamic_info */
|
||||
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_LONG__)
|
||||
/** Offset of next_addr member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_LONG__)
|
||||
/** Offset of next_mode member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_LONG__)
|
||||
/** Offset of options member in fw_dynamic_info (version >= 1) */
|
||||
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_LONG__)
|
||||
/** Offset of boot_hart member in fw_dynamic_info (version >= 2) */
|
||||
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_POINTER__)
|
||||
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_LONG__)
|
||||
|
||||
/** 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_MAX 0x2
|
||||
#define FW_DYNAMIC_INFO_VERSION_2 0x2
|
||||
#define FW_DYNAMIC_INFO_VERSION_MAX FW_DYNAMIC_INFO_VERSION_2
|
||||
|
||||
/** Possible next mode values */
|
||||
#define FW_DYNAMIC_INFO_NEXT_MODE_U 0x0
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
#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)
|
||||
@@ -97,6 +98,7 @@
|
||||
#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
|
||||
@@ -171,6 +173,31 @@
|
||||
#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT
|
||||
#endif
|
||||
|
||||
#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 (_UL(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_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)
|
||||
|
||||
/* ===== User-level CSRs ===== */
|
||||
|
||||
/* User Trap Setup (N-extension) */
|
||||
@@ -516,6 +543,40 @@
|
||||
#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
|
||||
|
||||
@@ -42,15 +42,28 @@
|
||||
: "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"); \
|
||||
sizeof(ulong) == 4 ? *(int64_t *)value : (int64_t)value; \
|
||||
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)
|
||||
|
||||
int spin_lock_check(spinlock_t *lock);
|
||||
bool spin_lock_check(spinlock_t *lock);
|
||||
|
||||
int spin_trylock(spinlock_t *lock);
|
||||
bool spin_trylock(spinlock_t *lock);
|
||||
|
||||
void spin_lock(spinlock_t *lock);
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ 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);
|
||||
@@ -51,4 +53,9 @@ 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
|
||||
|
||||
@@ -39,6 +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;
|
||||
|
||||
u16 sbi_ecall_version_major(void);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define SBI_EXT_RFENCE 0x52464E43
|
||||
#define SBI_EXT_HSM 0x48534D
|
||||
#define SBI_EXT_SRST 0x53525354
|
||||
#define SBI_EXT_PMU 0x504D55
|
||||
|
||||
/* SBI function IDs for BASE extension*/
|
||||
#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
|
||||
@@ -91,6 +92,139 @@
|
||||
#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
|
||||
@@ -107,8 +241,10 @@
|
||||
#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_AVAILABLE
|
||||
#define SBI_LAST_ERR SBI_ERR_ALREADY_STOPPED
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#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);
|
||||
bool sbi_fifo_is_empty(struct sbi_fifo *fifo);
|
||||
bool sbi_fifo_is_full(struct sbi_fifo *fifo);
|
||||
int sbi_fifo_is_empty(struct sbi_fifo *fifo);
|
||||
int 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);
|
||||
|
||||
@@ -18,8 +18,12 @@ enum sbi_hart_features {
|
||||
SBI_HART_HAS_SCOUNTEREN = (1 << 0),
|
||||
/** Hart has M-mode counter enable */
|
||||
SBI_HART_HAS_MCOUNTEREN = (1 << 1),
|
||||
/** Hart has counter inhibit CSR */
|
||||
SBI_HART_HAS_MCOUNTINHIBIT = (1 << 2),
|
||||
/** Hart has sscofpmf extension */
|
||||
SBI_HART_HAS_SSCOFPMF = (1 << 3),
|
||||
/** HART has timer csr implementation in hardware */
|
||||
SBI_HART_HAS_TIME = (1 << 2),
|
||||
SBI_HART_HAS_TIME = (1 << 4),
|
||||
|
||||
/** Last index of Hart features*/
|
||||
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_TIME,
|
||||
@@ -42,6 +46,7 @@ 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);
|
||||
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature);
|
||||
void sbi_hart_get_features_str(struct sbi_scratch *scratch,
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
#define __SBI_FENCE_H__
|
||||
|
||||
/** Invalidate Stage2 TLBs for given VMID and guest physical address */
|
||||
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa, unsigned long vmid);
|
||||
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa_divby_4,
|
||||
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);
|
||||
void __sbi_hfence_gvma_gpa(unsigned long gpa_divby_4);
|
||||
|
||||
/** Invalidate all possible Stage2 TLBs */
|
||||
void __sbi_hfence_gvma_all(void);
|
||||
|
||||
@@ -43,6 +43,17 @@ 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.
|
||||
|
||||
@@ -89,6 +89,12 @@ struct sbi_platform_operations {
|
||||
/** 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);
|
||||
|
||||
@@ -391,6 +397,39 @@ 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
|
||||
*
|
||||
|
||||
74
include/sbi/sbi_pmu.h
Normal file
74
include/sbi/sbi_pmu.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 64
|
||||
|
||||
/* 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
|
||||
@@ -98,7 +98,7 @@ 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, const char *owner);
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size);
|
||||
|
||||
/** Free-up extra space in sbi_scratch */
|
||||
void sbi_scratch_free_offset(unsigned long offset);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define __SBI_SYSTEM_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
/** System reset hardware device */
|
||||
struct sbi_system_reset_device {
|
||||
@@ -22,11 +23,21 @@ struct sbi_system_reset_device {
|
||||
|
||||
/** Reset the system */
|
||||
void (*system_reset)(u32 reset_type, u32 reset_reason);
|
||||
|
||||
/** List */
|
||||
struct sbi_dlist node;
|
||||
};
|
||||
|
||||
const struct sbi_system_reset_device *sbi_system_reset_get_device(void);
|
||||
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);
|
||||
}
|
||||
|
||||
void sbi_system_reset_set_device(const struct sbi_system_reset_device *dev);
|
||||
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);
|
||||
|
||||
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason);
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ 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);
|
||||
|
||||
@@ -29,6 +32,22 @@ 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,8 +68,14 @@ 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
|
||||
#ifdef __compiler_offsetof
|
||||
#if __has_builtin(__builtin_offsetof)
|
||||
#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE,MEMBER)
|
||||
#elif defined(__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 0
|
||||
#define OPENSBI_VERSION_MINOR 9
|
||||
#define OPENSBI_VERSION_MAJOR 1
|
||||
#define OPENSBI_VERSION_MINOR 0
|
||||
|
||||
/**
|
||||
* OpenSBI 32-bit version with:
|
||||
|
||||
@@ -11,12 +11,20 @@
|
||||
#define __FDT_HELPER_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
|
||||
struct fdt_match {
|
||||
const char *compatible;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define FDT_MAX_PHANDLE_ARGS 16
|
||||
struct fdt_phandle_args {
|
||||
int node_offset;
|
||||
int args_count;
|
||||
u32 args[FDT_MAX_PHANDLE_ARGS];
|
||||
};
|
||||
|
||||
struct platform_uart_data {
|
||||
unsigned long addr;
|
||||
unsigned long freq;
|
||||
@@ -32,13 +40,22 @@ int fdt_find_match(void *fdt, int startoff,
|
||||
const struct fdt_match *match_table,
|
||||
const struct fdt_match **out_match);
|
||||
|
||||
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
|
||||
unsigned long *size);
|
||||
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);
|
||||
|
||||
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid);
|
||||
|
||||
int fdt_parse_max_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_shakti_uart_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart);
|
||||
|
||||
@@ -57,12 +74,17 @@ 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);
|
||||
|
||||
struct clint_data;
|
||||
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);
|
||||
|
||||
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
|
||||
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
const char *compatible);
|
||||
|
||||
static inline void *fdt_get_address(void)
|
||||
{
|
||||
return sbi_scratch_thishart_arg1_ptr();
|
||||
}
|
||||
|
||||
#endif /* __FDT_HELPER_H__ */
|
||||
|
||||
46
include/sbi_utils/fdt/fdt_pmu.h
Normal file
46
include/sbi_utils/fdt/fdt_pmu.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
34
include/sbi_utils/gpio/fdt_gpio.h
Normal file
34
include/sbi_utils/gpio/fdt_gpio.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/** 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
|
||||
107
include/sbi_utils/gpio/gpio.h
Normal file
107
include/sbi_utils/gpio/gpio.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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
|
||||
26
include/sbi_utils/i2c/fdt_i2c.h
Normal file
26
include/sbi_utils/i2c/fdt_i2c.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
85
include/sbi_utils/i2c/i2c.h
Normal file
85
include/sbi_utils/i2c/i2c.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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
|
||||
33
include/sbi_utils/ipi/aclint_mswi.h
Normal file
33
include/sbi_utils/ipi/aclint_mswi.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -17,6 +17,16 @@ struct fdt_reset {
|
||||
int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
|
||||
};
|
||||
|
||||
int fdt_reset_init(void);
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#endif
|
||||
|
||||
17
include/sbi_utils/serial/gaisler-uart.h
Normal file
17
include/sbi_utils/serial/gaisler-uart.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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
|
||||
17
include/sbi_utils/serial/litex-uart.h
Normal file
17
include/sbi_utils/serial/litex-uart.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
53
include/sbi_utils/timer/aclint_mtimer.h
Normal file
53
include/sbi_utils/timer/aclint_mtimer.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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,6 +20,7 @@ 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_emulate_csr.o
|
||||
@@ -33,6 +34,7 @@ libsbi-objs-y += sbi_init.o
|
||||
libsbi-objs-y += sbi_ipi.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,6 +11,7 @@
|
||||
#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)
|
||||
@@ -75,6 +76,8 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +121,32 @@ 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)
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -161,7 +189,32 @@ 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;
|
||||
};
|
||||
|
||||
@@ -178,6 +231,9 @@ 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;
|
||||
@@ -205,14 +261,12 @@ 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
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
return SBI_ENOTSUPP;
|
||||
#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);
|
||||
@@ -258,12 +312,9 @@ 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
|
||||
pmpcfg_csr = -1;
|
||||
pmpcfg_shift = -1;
|
||||
return SBI_ENOTSUPP;
|
||||
#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 int spin_lock_unlocked(spinlock_t lock)
|
||||
static inline bool 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);
|
||||
}
|
||||
|
||||
int spin_trylock(spinlock_t *lock)
|
||||
bool spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
unsigned long inc = 1u << TICKET_SHIFT;
|
||||
unsigned long mask = 0xffffu << TICKET_SHIFT;
|
||||
@@ -42,7 +42,7 @@ int spin_trylock(spinlock_t *lock)
|
||||
: "r"(inc), "r"(mask), "I"(TICKET_SHIFT)
|
||||
: "memory");
|
||||
|
||||
return !l0;
|
||||
return l0 == 0;
|
||||
}
|
||||
|
||||
void spin_lock(spinlock_t *lock)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#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>
|
||||
|
||||
@@ -387,13 +388,29 @@ 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)
|
||||
if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS) {
|
||||
spin_lock(&console_out_lock);
|
||||
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;
|
||||
|
||||
@@ -295,7 +295,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 addres 0x%lx can't "
|
||||
sbi_printf("%s: %s next booting stage address 0x%lx can't "
|
||||
"execute\n", __func__, dom->name, dom->next_addr);
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
@@ -328,11 +328,7 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
rend = (reg->order < __riscv_xlen) ?
|
||||
rstart + ((1UL << reg->order) - 1) : -1UL;
|
||||
|
||||
#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
|
||||
sbi_printf("Domain%d Region%02d %s: 0x%" PRILX "-0x%" PRILX " ",
|
||||
dom->index, i, suffix, rstart, rend);
|
||||
|
||||
k = 0;
|
||||
@@ -351,18 +347,10 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
i++;
|
||||
}
|
||||
|
||||
#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
|
||||
sbi_printf("Domain%d Next Address%s: 0x%" PRILX "\n",
|
||||
dom->index, suffix, dom->next_addr);
|
||||
|
||||
#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
|
||||
sbi_printf("Domain%d Next Arg1 %s: 0x%" PRILX "\n",
|
||||
dom->index, suffix, dom->next_arg1);
|
||||
|
||||
sbi_printf("Domain%d Next Mode %s: ", dom->index, suffix);
|
||||
@@ -511,7 +499,8 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg)
|
||||
if (!nreg1->order)
|
||||
continue;
|
||||
|
||||
if ((nreg->base + BIT(nreg->order)) == nreg1->base &&
|
||||
if (!(nreg->base & (BIT(nreg->order + 1) - 1)) &&
|
||||
(nreg->base + BIT(nreg->order)) == nreg1->base &&
|
||||
nreg->order == nreg1->order &&
|
||||
nreg->flags == nreg1->flags) {
|
||||
nreg->order++;
|
||||
|
||||
@@ -162,6 +162,9 @@ 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);
|
||||
|
||||
87
lib/sbi/sbi_ecall_pmu.c
Normal file
87
lib/sbi/sbi_ecall_pmu.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 &&
|
||||
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID)
|
||||
if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID &&
|
||||
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA)
|
||||
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_ENOTSUPP;
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
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_ENOTSUPP;
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
if (sbi_system_reset_supported(regs->a0, regs->a1))
|
||||
|
||||
@@ -43,10 +43,13 @@ u16 sbi_fifo_avail(struct sbi_fifo *fifo)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool sbi_fifo_is_full(struct sbi_fifo *fifo)
|
||||
int 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);
|
||||
@@ -75,10 +78,13 @@ static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
return (fifo->avail == 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
bool sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
int 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);
|
||||
@@ -118,7 +124,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 = 0;
|
||||
int i, index;
|
||||
int ret = SBI_FIFO_UNCHANGED;
|
||||
void *entry;
|
||||
|
||||
@@ -135,7 +141,7 @@ 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 = index - fifo->num_entries;
|
||||
index -= fifo->num_entries;
|
||||
entry = (void *)fifo->queue + (u32)index * fifo->entry_size;
|
||||
ret = fptr(in, entry);
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct hart_features {
|
||||
unsigned int pmp_addr_bits;
|
||||
unsigned long pmp_gran;
|
||||
unsigned int mhpm_count;
|
||||
unsigned int mhpm_bits;
|
||||
};
|
||||
static unsigned long hart_features_offset;
|
||||
|
||||
@@ -50,13 +51,23 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
|
||||
csr_write(CSR_MSTATUS, mstatus_val);
|
||||
|
||||
/* Enable user/supervisor use of perf counters */
|
||||
/* Disable user mode usage of all perf counters except default ones (CY, TM, IR) */
|
||||
if (misa_extension('S') &&
|
||||
sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
|
||||
csr_write(CSR_SCOUNTEREN, -1);
|
||||
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_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_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
/* Disable all interrupts */
|
||||
csr_write(CSR_MIE, 0);
|
||||
|
||||
@@ -97,6 +108,9 @@ 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_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
interrupts |= MIP_LCOFIP;
|
||||
|
||||
exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
|
||||
(1U << CAUSE_USER_ECALL);
|
||||
if (sbi_platform_has_mfaults_delegation(plat))
|
||||
@@ -132,17 +146,10 @@ void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
|
||||
/* No delegation possible as mideleg does not exist*/
|
||||
return;
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
sbi_printf("%sMIDELEG%s: 0x%08lx\n",
|
||||
sbi_printf("%sMIDELEG%s: 0x%" PRILX "\n",
|
||||
prefix, suffix, csr_read(CSR_MIDELEG));
|
||||
sbi_printf("%sMEDELEG%s: 0x%08lx\n",
|
||||
sbi_printf("%sMEDELEG%s: 0x%" PRILX "\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)
|
||||
@@ -177,6 +184,14 @@ 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;
|
||||
@@ -211,7 +226,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);
|
||||
}
|
||||
}
|
||||
@@ -259,6 +274,12 @@ static inline char *sbi_hart_feature_id2string(unsigned long feature)
|
||||
case SBI_HART_HAS_MCOUNTEREN:
|
||||
fstr = "mcounteren";
|
||||
break;
|
||||
case SBI_HART_HAS_MCOUNTINHIBIT:
|
||||
fstr = "mcountinhibit";
|
||||
break;
|
||||
case SBI_HART_HAS_SSCOFPMF:
|
||||
fstr = "sscofpmf";
|
||||
break;
|
||||
case SBI_HART_HAS_TIME:
|
||||
fstr = "time";
|
||||
break;
|
||||
@@ -317,7 +338,11 @@ static unsigned long hart_pmp_get_allowed_addr(void)
|
||||
unsigned long val = 0;
|
||||
struct sbi_trap_info trap = {0};
|
||||
|
||||
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK); \
|
||||
csr_write_allowed(CSR_PMPCFG0, (ulong)&trap, 0);
|
||||
if (trap.cause)
|
||||
return 0;
|
||||
|
||||
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK);
|
||||
if (!trap.cause) {
|
||||
val = csr_read_allowed(CSR_PMPADDR0, (ulong)&trap);
|
||||
if (trap.cause)
|
||||
@@ -327,6 +352,37 @@ 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 = __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 += __fls(val) + 1;
|
||||
|
||||
#endif
|
||||
|
||||
return num_bits;
|
||||
}
|
||||
|
||||
static void hart_detect_features(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_trap_info trap = {0};
|
||||
@@ -392,9 +448,17 @@ __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
|
||||
@@ -421,6 +485,23 @@ __mhpm_skip:
|
||||
hfeatures->features |= SBI_HART_HAS_MCOUNTEREN;
|
||||
}
|
||||
|
||||
/* Detect if hart supports MCOUNTINHIBIT feature */
|
||||
val = csr_read_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_MCOUNTINHIBIT;
|
||||
}
|
||||
|
||||
/* Counter overflow/filtering is not useful without mcounter/inhibit */
|
||||
if (hfeatures->features & SBI_HART_HAS_MCOUNTINHIBIT &&
|
||||
hfeatures->features & SBI_HART_HAS_MCOUNTEREN) {
|
||||
/* Detect if hart supports sscofpmf */
|
||||
csr_read_allowed(CSR_SCOUNTOVF, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_SSCOFPMF;
|
||||
}
|
||||
|
||||
/* Detect if hart supports time CSR */
|
||||
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
@@ -451,8 +532,7 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
sbi_hart_expected_trap = &__sbi_expected_trap_hext;
|
||||
|
||||
hart_features_offset = sbi_scratch_alloc_offset(
|
||||
sizeof(struct hart_features),
|
||||
"HART_FEATURES");
|
||||
sizeof(struct hart_features));
|
||||
if (!hart_features_offset)
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
.global __sbi_hfence_gvma_vmid_gpa
|
||||
__sbi_hfence_gvma_vmid_gpa:
|
||||
/*
|
||||
* rs1 = a0 (GPA)
|
||||
* rs1 = a0 (GPA >> 2)
|
||||
* 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)
|
||||
* rs1 = a0 (GPA >> 2)
|
||||
* rs2 = zero
|
||||
* HFENCE.GVMA a0
|
||||
* 0110001 00000 01010 000 00000 1110011
|
||||
|
||||
@@ -185,8 +185,7 @@ 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");
|
||||
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata));
|
||||
if (!hart_data_offset)
|
||||
return SBI_ENOMEM;
|
||||
|
||||
@@ -359,12 +358,6 @@ static int __sbi_hsm_suspend_non_ret_default(struct sbi_scratch *scratch,
|
||||
{
|
||||
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();
|
||||
|
||||
@@ -464,6 +457,13 @@ 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, scratch->warmboot_addr);
|
||||
if (ret == SBI_ENOTSUPP) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#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>
|
||||
|
||||
@@ -129,6 +130,7 @@ 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) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_ipi.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>
|
||||
@@ -47,6 +48,14 @@ 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);
|
||||
}
|
||||
|
||||
@@ -74,16 +83,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\n",
|
||||
(tdev) ? tdev->name : "---");
|
||||
sbi_printf("Platform Timer Device : %s @ %luHz\n",
|
||||
(tdev) ? tdev->name : "---",
|
||||
(tdev) ? tdev->timer_freq : 0);
|
||||
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_printf("Platform SysReset Device : %s\n",
|
||||
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) ? srdev->name : "---");
|
||||
|
||||
/* Firmware details */
|
||||
@@ -137,8 +150,6 @@ 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 ", " ");
|
||||
}
|
||||
|
||||
@@ -205,7 +216,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 (int i = 0; i <= sbi_scratch_last_hartid(); i++) {
|
||||
for (u32 i = 0; i <= sbi_scratch_last_hartid(); i++) {
|
||||
if ((i != hartid) &&
|
||||
sbi_hartmask_test_hart(i, &coldboot_wait_hmask))
|
||||
sbi_ipi_raw_send(i);
|
||||
@@ -233,8 +244,7 @@ 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");
|
||||
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__);
|
||||
if (!init_count_offset)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -254,6 +264,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_pmu_init(scratch, TRUE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
sbi_boot_print_banner(scratch);
|
||||
|
||||
rc = sbi_platform_irqchip_init(plat, TRUE);
|
||||
@@ -287,8 +301,6 @@ 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.
|
||||
@@ -302,8 +314,6 @@ 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",
|
||||
@@ -322,6 +332,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
sbi_boot_print_general(scratch);
|
||||
|
||||
sbi_boot_print_domains(scratch);
|
||||
|
||||
sbi_boot_print_hart(scratch, hartid);
|
||||
|
||||
wake_coldboot_harts(scratch, hartid);
|
||||
@@ -355,6 +369,10 @@ 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_platform_irqchip_init(plat, FALSE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
@@ -518,6 +536,8 @@ 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);
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#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;
|
||||
@@ -64,6 +67,8 @@ 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);
|
||||
|
||||
@@ -183,6 +188,7 @@ 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);
|
||||
|
||||
@@ -227,8 +233,7 @@ 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");
|
||||
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data));
|
||||
if (!ipi_data_off)
|
||||
return SBI_ENOMEM;
|
||||
ret = sbi_ipi_event_create(&ipi_smode_ops);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#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>
|
||||
|
||||
@@ -29,6 +30,8 @@ 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
|
||||
@@ -149,6 +152,8 @@ 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
|
||||
|
||||
@@ -20,7 +20,7 @@ static inline char *sbi_platform_feature_id2string(unsigned long feature)
|
||||
|
||||
switch (feature) {
|
||||
case SBI_PLATFORM_HAS_MFAULTS_DELEGATION:
|
||||
fstr = "mfdeleg";
|
||||
fstr = "medeleg";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -48,9 +48,18 @@ void sbi_platform_get_features_str(const struct sbi_platform *plat,
|
||||
if (features & feat) {
|
||||
temp = sbi_platform_feature_id2string(feat);
|
||||
if (temp) {
|
||||
sbi_snprintf(features_str + offset, nfstr,
|
||||
"%s,", temp);
|
||||
offset = offset + sbi_strlen(temp) + 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
feat = feat << 1;
|
||||
|
||||
753
lib/sbi/sbi_pmu.c
Normal file
753
lib/sbi/sbi_pmu.c
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
* 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];
|
||||
|
||||
/* 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;
|
||||
event->counters = cmap;
|
||||
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 = ~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_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
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_feature(scratch, SBI_HART_HAS_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 (__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_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
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);
|
||||
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 (__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 clear the OVF bit and inhibit countin of events in M-mode */
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) | MHPMEVENT_MINH;
|
||||
|
||||
/* 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);
|
||||
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_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
return fixed_ctr;
|
||||
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
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_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT)) &&
|
||||
!__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 + 1;
|
||||
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 (__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_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
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) + 2;
|
||||
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;
|
||||
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS - 1;
|
||||
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, const char *owner)
|
||||
unsigned long sbi_scratch_alloc_offset(unsigned long size)
|
||||
{
|
||||
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,6 +33,10 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,28 +18,48 @@
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_init.h>
|
||||
|
||||
static const struct sbi_system_reset_device *reset_dev = NULL;
|
||||
static SBI_LIST_HEAD(reset_devices_list);
|
||||
|
||||
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)
|
||||
{
|
||||
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_set_device(const struct sbi_system_reset_device *dev)
|
||||
void sbi_system_reset_add_device(struct sbi_system_reset_device *dev)
|
||||
{
|
||||
if (!dev || reset_dev)
|
||||
if (!dev || !dev->system_reset_check)
|
||||
return;
|
||||
|
||||
reset_dev = dev;
|
||||
sbi_list_add(&(dev->node), &(reset_devices_list));
|
||||
}
|
||||
|
||||
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason)
|
||||
{
|
||||
if (reset_dev && reset_dev->system_reset_check &&
|
||||
reset_dev->system_reset_check(reset_type, reset_reason))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
return !!sbi_system_reset_get_device(reset_type, reset_reason);
|
||||
}
|
||||
|
||||
void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
|
||||
@@ -62,9 +82,12 @@ 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 &&
|
||||
reset_dev && reset_dev->system_reset)
|
||||
reset_dev->system_reset(reset_type, reset_reason);
|
||||
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 platform specific reset did not work then do sbi_exit() */
|
||||
sbi_exit(scratch);
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
@@ -46,6 +49,38 @@ static u64 get_platform_ticks(void)
|
||||
return timer_dev->timer_value();
|
||||
}
|
||||
|
||||
static void nop_delay_fn(void *opaque)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
u64 sbi_timer_value(void)
|
||||
{
|
||||
if (get_time_val)
|
||||
@@ -88,6 +123,7 @@ 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);
|
||||
if (timer_dev && timer_dev->timer_event_start)
|
||||
timer_dev->timer_event_start(next_event);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
@@ -121,8 +157,7 @@ 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");
|
||||
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta));
|
||||
if (!time_delta_off)
|
||||
return SBI_ENOMEM;
|
||||
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
#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 sbi_tlb_flush_all(void)
|
||||
static void tlb_flush_all(void)
|
||||
{
|
||||
__asm__ __volatile("sfence.vma");
|
||||
}
|
||||
@@ -39,6 +40,8 @@ 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);
|
||||
|
||||
@@ -61,13 +64,15 @@ 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);
|
||||
__sbi_hfence_gvma_gpa((start + i) >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +82,10 @@ 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)) {
|
||||
sbi_tlb_flush_all();
|
||||
tlb_flush_all();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,6 +105,8 @@ 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);
|
||||
|
||||
@@ -126,6 +135,8 @@ 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;
|
||||
@@ -137,7 +148,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, vmid);
|
||||
__sbi_hfence_gvma_vmid_gpa((start + i) >> 2, vmid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +159,10 @@ 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) {
|
||||
sbi_tlb_flush_all();
|
||||
tlb_flush_all();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,10 +185,33 @@ 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 sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
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)
|
||||
{
|
||||
u32 rhartid;
|
||||
struct sbi_scratch *rscratch = NULL;
|
||||
@@ -193,15 +229,15 @@ static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
}
|
||||
}
|
||||
|
||||
static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
static void tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
{
|
||||
struct sbi_tlb_info tinfo;
|
||||
u32 deq_count = 0;
|
||||
unsigned int deq_count = 0;
|
||||
struct sbi_fifo *tlb_fifo =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
|
||||
|
||||
while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) {
|
||||
sbi_tlb_entry_process(&tinfo);
|
||||
tlb_entry_process(&tinfo);
|
||||
deq_count++;
|
||||
if (deq_count > count)
|
||||
break;
|
||||
@@ -209,17 +245,17 @@ static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void sbi_tlb_process(struct sbi_scratch *scratch)
|
||||
static void 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))
|
||||
sbi_tlb_entry_process(&tinfo);
|
||||
tlb_entry_process(&tinfo);
|
||||
}
|
||||
|
||||
static void sbi_tlb_sync(struct sbi_scratch *scratch)
|
||||
static void tlb_sync(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long *tlb_sync =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_sync_off);
|
||||
@@ -229,13 +265,13 @@ static void sbi_tlb_sync(struct sbi_scratch *scratch)
|
||||
* While we are waiting for remote hart to set the sync,
|
||||
* consume fifo requests to avoid deadlock.
|
||||
*/
|
||||
sbi_tlb_process_count(scratch, 1);
|
||||
tlb_process_count(scratch, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int __sbi_tlb_range_check(struct sbi_tlb_info *curr,
|
||||
static inline int tlb_range_check(struct sbi_tlb_info *curr,
|
||||
struct sbi_tlb_info *next)
|
||||
{
|
||||
unsigned long curr_end;
|
||||
@@ -278,7 +314,7 @@ static inline int __sbi_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 sbi_tlb_update_cb(void *in, void *data)
|
||||
static int tlb_update_cb(void *in, void *data)
|
||||
{
|
||||
struct sbi_tlb_info *curr;
|
||||
struct sbi_tlb_info *next;
|
||||
@@ -293,16 +329,16 @@ static int sbi_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 = __sbi_tlb_range_check(curr, next);
|
||||
ret = tlb_range_check(curr, next);
|
||||
} else if (next->local_fn == sbi_tlb_local_sfence_vma &&
|
||||
curr->local_fn == sbi_tlb_local_sfence_vma) {
|
||||
ret = __sbi_tlb_range_check(curr, next);
|
||||
ret = tlb_range_check(curr, next);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sbi_tlb_update(struct sbi_scratch *scratch,
|
||||
static int tlb_update(struct sbi_scratch *scratch,
|
||||
struct sbi_scratch *remote_scratch,
|
||||
u32 remote_hartid, void *data)
|
||||
{
|
||||
@@ -332,7 +368,7 @@ static int sbi_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, sbi_tlb_update_cb);
|
||||
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, tlb_update_cb);
|
||||
if (ret != SBI_FIFO_UNCHANGED) {
|
||||
return 1;
|
||||
}
|
||||
@@ -346,7 +382,7 @@ static int sbi_tlb_update(struct sbi_scratch *scratch,
|
||||
* TODO: Introduce a wait/wakeup event mechanism to handle
|
||||
* this properly.
|
||||
*/
|
||||
sbi_tlb_process_count(scratch, 1);
|
||||
tlb_process_count(scratch, 1);
|
||||
sbi_dprintf("hart%d: hart%d tlb fifo full\n",
|
||||
curr_hartid, remote_hartid);
|
||||
}
|
||||
@@ -356,9 +392,9 @@ static int sbi_tlb_update(struct sbi_scratch *scratch,
|
||||
|
||||
static struct sbi_ipi_event_ops tlb_ops = {
|
||||
.name = "IPI_TLB",
|
||||
.update = sbi_tlb_update,
|
||||
.sync = sbi_tlb_sync,
|
||||
.process = sbi_tlb_process,
|
||||
.update = tlb_update,
|
||||
.sync = tlb_sync,
|
||||
.process = tlb_process,
|
||||
};
|
||||
|
||||
static u32 tlb_event = SBI_IPI_EVENT_MAX;
|
||||
@@ -368,6 +404,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -380,19 +418,16 @@ 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),
|
||||
"IPI_TLB_SYNC");
|
||||
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync));
|
||||
if (!tlb_sync_off)
|
||||
return SBI_ENOMEM;
|
||||
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q),
|
||||
"IPI_TLB_FIFO");
|
||||
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q));
|
||||
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,
|
||||
"IPI_TLB_FIFO_MEM");
|
||||
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE);
|
||||
if (!tlb_fifo_mem_off) {
|
||||
sbi_scratch_free_offset(tlb_fifo_off);
|
||||
sbi_scratch_free_offset(tlb_sync_off);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <sbi/sbi_illegal_insn.h>
|
||||
#include <sbi/sbi_ipi.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>
|
||||
@@ -257,6 +258,11 @@ 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;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
@@ -95,7 +96,7 @@ static int __fixup_find_domain_offset(void *fdt, int doff, void *p)
|
||||
{
|
||||
struct __fixup_find_domain_offset_info *fdo = p;
|
||||
|
||||
if (!sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL)))
|
||||
if (!strncmp(fdo->name, fdt_get_name(fdt, doff, NULL), strlen(fdo->name)))
|
||||
*fdo->doffset = doff;
|
||||
|
||||
return 0;
|
||||
@@ -287,7 +288,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
regions = &fdt_regions[fdt_domains_count][0];
|
||||
|
||||
/* Read DT node name */
|
||||
sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
|
||||
strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
|
||||
sizeof(dom->name));
|
||||
dom->name[sizeof(dom->name) - 1] = '\0';
|
||||
|
||||
@@ -313,7 +314,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
|
||||
/* Setup memregions from DT */
|
||||
val32 = 0;
|
||||
sbi_memset(regions, 0,
|
||||
memset(regions, 0,
|
||||
sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 1));
|
||||
dom->regions = regions;
|
||||
err = fdt_iterate_each_memregion(fdt, domain_offset, &val32,
|
||||
@@ -337,7 +338,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
continue;
|
||||
if (FDT_DOMAIN_REGION_MAX_COUNT <= val32)
|
||||
return SBI_EINVAL;
|
||||
sbi_memcpy(®ions[val32++], reg, sizeof(*reg));
|
||||
memcpy(®ions[val32++], reg, sizeof(*reg));
|
||||
}
|
||||
|
||||
/* Read "boot-hart" DT property */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#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)
|
||||
@@ -263,6 +264,7 @@ void fdt_fixups(void *fdt)
|
||||
fdt_plic_fixup(fdt);
|
||||
|
||||
fdt_reserved_memory_fixup(fdt);
|
||||
fdt_pmu_fixup(fdt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/plic.h>
|
||||
#include <sbi_utils/sys/clint.h>
|
||||
|
||||
#define DEFAULT_UART_FREQ 0
|
||||
#define DEFAULT_UART_BAUD 115200
|
||||
@@ -26,6 +25,8 @@
|
||||
#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
|
||||
|
||||
@@ -71,8 +72,55 @@ 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,
|
||||
unsigned long *addr)
|
||||
uint64_t *addr)
|
||||
{
|
||||
int i, rlen;
|
||||
int cell_addr, cell_size;
|
||||
@@ -109,14 +157,17 @@ static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
|
||||
unsigned long *size)
|
||||
int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
uint64_t *addr, uint64_t *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;
|
||||
@@ -131,6 +182,11 @@ int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
|
||||
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) {
|
||||
@@ -213,17 +269,78 @@ int fdt_parse_max_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;
|
||||
unsigned long reg_addr, reg_size;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
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;
|
||||
@@ -252,12 +369,13 @@ int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
unsigned long reg_addr, reg_size;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
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;
|
||||
@@ -290,12 +408,13 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
unsigned long reg_addr, reg_size;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
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;
|
||||
@@ -350,12 +469,13 @@ int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
|
||||
{
|
||||
int len, rc;
|
||||
const fdt32_t *val;
|
||||
unsigned long reg_addr, reg_size;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !plic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
plic->addr = reg_addr;
|
||||
@@ -381,34 +501,51 @@ int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat)
|
||||
return fdt_parse_plic_node(fdt, nodeoffset, plic);
|
||||
}
|
||||
|
||||
int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
struct clint_data *clint)
|
||||
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)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
unsigned long reg_addr, reg_size;
|
||||
uint64_t reg_addr, reg_size;
|
||||
int i, rc, count, cpu_offset, cpu_intc_offset;
|
||||
u32 phandle, hwirq, hartid, first_hartid, last_hartid;
|
||||
u32 phandle, hwirq, hartid, first_hartid, last_hartid, hart_count;
|
||||
u32 match_hwirq = (for_timer) ? IRQ_M_TIMER : IRQ_M_SOFT;
|
||||
|
||||
if (nodeoffset < 0 || !clint || !fdt)
|
||||
return SBI_ENODEV;
|
||||
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, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
clint->addr = reg_addr;
|
||||
*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;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count);
|
||||
if (!val || count < sizeof(fdt32_t))
|
||||
return SBI_EINVAL;
|
||||
return 0;
|
||||
count = count / sizeof(fdt32_t);
|
||||
|
||||
first_hartid = -1U;
|
||||
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]);
|
||||
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]);
|
||||
|
||||
cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
if (cpu_intc_offset < 0)
|
||||
@@ -430,26 +567,20 @@ int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
|
||||
first_hartid = hartid;
|
||||
if (hartid > last_hartid)
|
||||
last_hartid = hartid;
|
||||
clint->hart_count++;
|
||||
hart_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;
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
|
||||
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
const char *compatible)
|
||||
{
|
||||
int nodeoffset, rc;
|
||||
@@ -458,7 +589,7 @@ int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
|
||||
if (nodeoffset < 0)
|
||||
return nodeoffset;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, addr, NULL);
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0, addr, NULL);
|
||||
if (rc < 0 || !addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
|
||||
117
lib/utils/fdt/fdt_pmu.c
Normal file
117
lib/utils/fdt/fdt_pmu.c
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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_feature(scratch, SBI_HART_HAS_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,5 +5,6 @@
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
135
lib/utils/gpio/fdt_gpio.c
Normal file
135
lib/utils/gpio/fdt_gpio.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
extern struct fdt_gpio fdt_gpio_sifive;
|
||||
|
||||
static struct fdt_gpio *gpio_drivers[] = {
|
||||
&fdt_gpio_sifive
|
||||
};
|
||||
|
||||
static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (!chip)
|
||||
return NULL;
|
||||
|
||||
for (pos = 0; pos < array_size(gpio_drivers); pos++) {
|
||||
if (chip->driver == gpio_drivers[pos])
|
||||
return 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 < array_size(gpio_drivers); pos++) {
|
||||
drv = 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;
|
||||
}
|
||||
107
lib/utils/gpio/fdt_gpio_sifive.c
Normal file
107
lib/utils/gpio/fdt_gpio_sifive.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
98
lib/utils/gpio/gpio.c
Normal file
98
lib/utils/gpio/gpio.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/gpio/gpio.h>
|
||||
|
||||
static SBI_LIST_HEAD(gpio_chip_list);
|
||||
|
||||
struct gpio_chip *gpio_chip_find(unsigned int id)
|
||||
{
|
||||
struct sbi_dlist *pos;
|
||||
|
||||
sbi_list_for_each(pos, &(gpio_chip_list)) {
|
||||
struct gpio_chip *chip = to_gpio_chip(pos);
|
||||
|
||||
if (chip->id == id)
|
||||
return chip;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int gpio_chip_add(struct gpio_chip *gc)
|
||||
{
|
||||
if (!gc)
|
||||
return SBI_EINVAL;
|
||||
if (gpio_chip_find(gc->id))
|
||||
return SBI_EALREADY;
|
||||
|
||||
sbi_list_add(&(gc->node), &(gpio_chip_list));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_chip_remove(struct gpio_chip *gc)
|
||||
{
|
||||
if (!gc)
|
||||
return;
|
||||
|
||||
sbi_list_del(&(gc->node));
|
||||
}
|
||||
|
||||
int gpio_get_direction(struct gpio_pin *gp)
|
||||
{
|
||||
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
|
||||
return SBI_EINVAL;
|
||||
if (!gp->chip->get_direction)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return gp->chip->get_direction(gp);
|
||||
}
|
||||
|
||||
int gpio_direction_input(struct gpio_pin *gp)
|
||||
{
|
||||
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
|
||||
return SBI_EINVAL;
|
||||
if (!gp->chip->direction_input)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return gp->chip->direction_input(gp);
|
||||
}
|
||||
|
||||
int gpio_direction_output(struct gpio_pin *gp, int value)
|
||||
{
|
||||
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
|
||||
return SBI_EINVAL;
|
||||
if (!gp->chip->direction_output)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return gp->chip->direction_output(gp, value);
|
||||
}
|
||||
|
||||
int gpio_get(struct gpio_pin *gp)
|
||||
{
|
||||
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
|
||||
return SBI_EINVAL;
|
||||
if (!gp->chip->get)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return gp->chip->get(gp);
|
||||
}
|
||||
|
||||
int gpio_set(struct gpio_pin *gp, int value)
|
||||
{
|
||||
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
|
||||
return SBI_EINVAL;
|
||||
if (!gp->chip->set)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
gp->chip->set(gp, value);
|
||||
return 0;
|
||||
}
|
||||
12
lib/utils/gpio/objects.mk
Normal file
12
lib/utils/gpio/objects.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
#
|
||||
# Authors:
|
||||
# Anup Patel <anup.patel@wdc.com>
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += gpio/fdt_gpio.o
|
||||
libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
|
||||
libsbiutils-objs-y += gpio/gpio.o
|
||||
90
lib/utils/i2c/fdt_i2c.c
Normal file
90
lib/utils/i2c/fdt_i2c.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 YADRO
|
||||
*
|
||||
* Authors:
|
||||
* Nikita Shubin <n.shubin@yadro.com>
|
||||
*
|
||||
* derivate: lib/utils/gpio/fdt_gpio.c
|
||||
* 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/i2c/fdt_i2c.h>
|
||||
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
|
||||
|
||||
static struct fdt_i2c_adapter *i2c_adapter_drivers[] = {
|
||||
&fdt_i2c_adapter_sifive
|
||||
};
|
||||
|
||||
static int fdt_i2c_adapter_init(void *fdt, int nodeoff)
|
||||
{
|
||||
int pos, rc;
|
||||
struct fdt_i2c_adapter *drv;
|
||||
const struct fdt_match *match;
|
||||
|
||||
/* Try all I2C drivers one-by-one */
|
||||
for (pos = 0; pos < array_size(i2c_adapter_drivers); pos++) {
|
||||
drv = i2c_adapter_drivers[pos];
|
||||
match = fdt_match_node(fdt, nodeoff, drv->match_table);
|
||||
if (match && drv->init) {
|
||||
rc = drv->init(fdt, nodeoff, match);
|
||||
if (rc == SBI_ENODEV)
|
||||
continue;
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SBI_ENOSYS;
|
||||
}
|
||||
|
||||
static int fdt_i2c_adapter_find(void *fdt, int nodeoff,
|
||||
struct i2c_adapter **out_adapter)
|
||||
{
|
||||
int rc;
|
||||
struct i2c_adapter *adapter = i2c_adapter_find(nodeoff);
|
||||
|
||||
if (!adapter) {
|
||||
/* I2C adapter not found so initialize matching driver */
|
||||
rc = fdt_i2c_adapter_init(fdt, nodeoff);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Try to find I2C adapter again */
|
||||
adapter = i2c_adapter_find(nodeoff);
|
||||
if (!adapter)
|
||||
return SBI_ENOSYS;
|
||||
}
|
||||
|
||||
if (out_adapter)
|
||||
*out_adapter = adapter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_i2c_adapter_get(void *fdt, int nodeoff,
|
||||
struct i2c_adapter **out_adapter)
|
||||
{
|
||||
int rc;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
if (!fdt || (nodeoff < 0) || !out_adapter)
|
||||
return SBI_EINVAL;
|
||||
|
||||
rc = fdt_i2c_adapter_find(fdt, nodeoff, &adapter);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*out_adapter = adapter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
277
lib/utils/i2c/fdt_i2c_sifive.c
Normal file
277
lib/utils/i2c/fdt_i2c_sifive.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 YADRO
|
||||
*
|
||||
* Authors:
|
||||
* Nikita Shubin <n.shubin@yadro.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/i2c/fdt_i2c.h>
|
||||
|
||||
#define SIFIVE_I2C_ADAPTER_MAX 2
|
||||
|
||||
#define SIFIVE_I2C_PRELO 0x00
|
||||
#define SIFIVE_I2C_PREHI 0x04
|
||||
#define SIFIVE_I2C_CTR 0x08
|
||||
#define SIFIVE_I2C_TXR 0x00c
|
||||
#define SIFIVE_I2C_RXR SIFIVE_I2C_TXR
|
||||
#define SIFIVE_I2C_CR 0x010
|
||||
#define SIFIVE_I2C_SR SIFIVE_I2C_CR
|
||||
|
||||
#define SIFIVE_I2C_CTR_IEN (1 << 6)
|
||||
#define SIFIVE_I2C_CTR_EN (1 << 7)
|
||||
|
||||
#define SIFIVE_I2C_CMD_IACK (1 << 0)
|
||||
#define SIFIVE_I2C_CMD_ACK (1 << 3)
|
||||
#define SIFIVE_I2C_CMD_WR (1 << 4)
|
||||
#define SIFIVE_I2C_CMD_RD (1 << 5)
|
||||
#define SIFIVE_I2C_CMD_STO (1 << 6)
|
||||
#define SIFIVE_I2C_CMD_STA (1 << 7)
|
||||
|
||||
#define SIFIVE_I2C_STATUS_IF (1 << 0)
|
||||
#define SIFIVE_I2C_STATUS_TIP (1 << 1)
|
||||
#define SIFIVE_I2C_STATUS_AL (1 << 5)
|
||||
#define SIFIVE_I2C_STATUS_BUSY (1 << 6)
|
||||
#define SIFIVE_I2C_STATUS_RXACK (1 << 7)
|
||||
|
||||
#define SIFIVE_I2C_WRITE_BIT (0 << 0)
|
||||
#define SIFIVE_I2C_READ_BIT (1 << 0)
|
||||
|
||||
struct sifive_i2c_adapter {
|
||||
unsigned long addr;
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
static unsigned int sifive_i2c_adapter_count;
|
||||
static struct sifive_i2c_adapter
|
||||
sifive_i2c_adapter_array[SIFIVE_I2C_ADAPTER_MAX];
|
||||
|
||||
extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
|
||||
|
||||
static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap,
|
||||
uint8_t reg, uint8_t value)
|
||||
{
|
||||
writel(value, (volatile void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static inline uint8_t sifive_i2c_getreg(struct sifive_i2c_adapter *adap,
|
||||
uint8_t reg)
|
||||
{
|
||||
return readl((volatile void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static int sifive_i2c_adapter_rxack(struct sifive_i2c_adapter *adap)
|
||||
{
|
||||
uint8_t val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
|
||||
|
||||
if (val & SIFIVE_I2C_STATUS_RXACK)
|
||||
return SBI_EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_i2c_adapter_poll(struct sifive_i2c_adapter *adap,
|
||||
uint32_t mask)
|
||||
{
|
||||
unsigned int timeout = 1; // [msec]
|
||||
int count = 0;
|
||||
uint8_t val;
|
||||
|
||||
sbi_timer_udelay(80); // worst case if bus speed is 100 kHz
|
||||
|
||||
do {
|
||||
val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
|
||||
if (!(val & mask))
|
||||
return 0;
|
||||
|
||||
sbi_timer_udelay(1);
|
||||
count += 1;
|
||||
if (count == (timeout * 1000))
|
||||
return SBI_ETIMEDOUT;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
#define sifive_i2c_adapter_poll_tip(adap) \
|
||||
sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_TIP)
|
||||
#define sifive_i2c_adapter_poll_busy(adap) \
|
||||
sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_BUSY)
|
||||
|
||||
static int sifive_i2c_adapter_start(struct sifive_i2c_adapter *adap,
|
||||
uint8_t addr, uint8_t bit)
|
||||
{
|
||||
uint8_t val = (addr << 1) | bit;
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, val);
|
||||
val = SIFIVE_I2C_CMD_STA | SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK;
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, val);
|
||||
|
||||
return sifive_i2c_adapter_poll_tip(adap);
|
||||
}
|
||||
|
||||
static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr,
|
||||
uint8_t reg, uint8_t *buffer, int len)
|
||||
{
|
||||
struct sifive_i2c_adapter *adap =
|
||||
container_of(ia, struct sifive_i2c_adapter, adapter);
|
||||
int rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set register address */
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
rc = sifive_i2c_adapter_poll_tip(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set value */
|
||||
while (len) {
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
rc = sifive_i2c_adapter_poll_tip(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
/* poll BUSY instead of ACK*/
|
||||
rc = sifive_i2c_adapter_poll_busy(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr,
|
||||
uint8_t reg, uint8_t *buffer, int len)
|
||||
{
|
||||
struct sifive_i2c_adapter *adap =
|
||||
container_of(ia, struct sifive_i2c_adapter, adapter);
|
||||
int rc;
|
||||
|
||||
rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
rc = sifive_i2c_adapter_poll_tip(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* setting addr with high 0 bit */
|
||||
rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_READ_BIT);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sifive_i2c_adapter_rxack(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
while (len) {
|
||||
if (len == 1)
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
|
||||
SIFIVE_I2C_CMD_ACK |
|
||||
SIFIVE_I2C_CMD_RD |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
else
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
|
||||
SIFIVE_I2C_CMD_RD |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
rc = sifive_i2c_adapter_poll_tip(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
|
||||
SIFIVE_I2C_CMD_IACK);
|
||||
rc = sifive_i2c_adapter_poll_busy(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_i2c_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct sifive_i2c_adapter *adapter;
|
||||
uint64_t addr;
|
||||
|
||||
if (sifive_i2c_adapter_count >= SIFIVE_I2C_ADAPTER_MAX)
|
||||
return SBI_ENOSPC;
|
||||
|
||||
adapter = &sifive_i2c_adapter_array[sifive_i2c_adapter_count];
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
adapter->addr = addr;
|
||||
adapter->adapter.driver = &fdt_i2c_adapter_sifive;
|
||||
adapter->adapter.id = nodeoff;
|
||||
adapter->adapter.write = sifive_i2c_adapter_write;
|
||||
adapter->adapter.read = sifive_i2c_adapter_read;
|
||||
rc = i2c_adapter_add(&adapter->adapter);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sifive_i2c_adapter_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match sifive_i2c_match[] = {
|
||||
{ .compatible = "sifive,i2c0" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_i2c_adapter fdt_i2c_adapter_sifive = {
|
||||
.match_table = sifive_i2c_match,
|
||||
.init = sifive_i2c_init,
|
||||
};
|
||||
75
lib/utils/i2c/i2c.c
Normal file
75
lib/utils/i2c/i2c.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 YADRO
|
||||
*
|
||||
* Authors:
|
||||
* Nikita Shubin <n.shubin@yadro.com>
|
||||
*
|
||||
* derivate: lib/utils/gpio/gpio.c
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/i2c/i2c.h>
|
||||
|
||||
static SBI_LIST_HEAD(i2c_adapters_list);
|
||||
|
||||
struct i2c_adapter *i2c_adapter_find(int id)
|
||||
{
|
||||
struct sbi_dlist *pos;
|
||||
|
||||
sbi_list_for_each(pos, &(i2c_adapters_list)) {
|
||||
struct i2c_adapter *adap = to_i2c_adapter(pos);
|
||||
|
||||
if (adap->id == id)
|
||||
return adap;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i2c_adapter_add(struct i2c_adapter *ia)
|
||||
{
|
||||
if (!ia)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (i2c_adapter_find(ia->id))
|
||||
return SBI_EALREADY;
|
||||
|
||||
sbi_list_add(&(ia->node), &(i2c_adapters_list));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_adapter_remove(struct i2c_adapter *ia)
|
||||
{
|
||||
if (!ia)
|
||||
return;
|
||||
|
||||
sbi_list_del(&(ia->node));
|
||||
}
|
||||
|
||||
int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len)
|
||||
{
|
||||
if (!ia)
|
||||
return SBI_EINVAL;
|
||||
if (!ia->write)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return ia->write(ia, addr, reg, buffer, len);
|
||||
}
|
||||
|
||||
|
||||
int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
|
||||
uint8_t *buffer, int len)
|
||||
{
|
||||
if (!ia)
|
||||
return SBI_EINVAL;
|
||||
if (!ia->read)
|
||||
return SBI_ENOSYS;
|
||||
|
||||
return ia->read(ia, addr, reg, buffer, len);
|
||||
}
|
||||
12
lib/utils/i2c/objects.mk
Normal file
12
lib/utils/i2c/objects.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2021 YADRO
|
||||
#
|
||||
# Authors:
|
||||
# Nikita Shubin <n.shubin@yadro.com>
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += i2c/i2c.o
|
||||
libsbiutils-objs-y += i2c/fdt_i2c.o
|
||||
libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
|
||||
100
lib/utils/ipi/aclint_mswi.c
Normal file
100
lib/utils/ipi/aclint_mswi.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_atomic.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi_utils/ipi/aclint_mswi.h>
|
||||
|
||||
static struct aclint_mswi_data *mswi_hartid2data[SBI_HARTMASK_MAX_BITS];
|
||||
|
||||
static void mswi_ipi_send(u32 target_hart)
|
||||
{
|
||||
u32 *msip;
|
||||
struct aclint_mswi_data *mswi;
|
||||
|
||||
if (SBI_HARTMASK_MAX_BITS <= target_hart)
|
||||
return;
|
||||
mswi = mswi_hartid2data[target_hart];
|
||||
if (!mswi)
|
||||
return;
|
||||
|
||||
/* Set ACLINT IPI */
|
||||
msip = (void *)mswi->addr;
|
||||
writel(1, &msip[target_hart - mswi->first_hartid]);
|
||||
}
|
||||
|
||||
static void mswi_ipi_clear(u32 target_hart)
|
||||
{
|
||||
u32 *msip;
|
||||
struct aclint_mswi_data *mswi;
|
||||
|
||||
if (SBI_HARTMASK_MAX_BITS <= target_hart)
|
||||
return;
|
||||
mswi = mswi_hartid2data[target_hart];
|
||||
if (!mswi)
|
||||
return;
|
||||
|
||||
/* Clear ACLINT IPI */
|
||||
msip = (void *)mswi->addr;
|
||||
writel(0, &msip[target_hart - mswi->first_hartid]);
|
||||
}
|
||||
|
||||
static struct sbi_ipi_device aclint_mswi = {
|
||||
.name = "aclint-mswi",
|
||||
.ipi_send = mswi_ipi_send,
|
||||
.ipi_clear = mswi_ipi_clear
|
||||
};
|
||||
|
||||
int aclint_mswi_warm_init(void)
|
||||
{
|
||||
/* Clear IPI for current HART */
|
||||
mswi_ipi_clear(current_hartid());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
|
||||
{
|
||||
u32 i;
|
||||
int rc;
|
||||
unsigned long pos, region_size;
|
||||
struct sbi_domain_memregion reg;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!mswi || (mswi->addr & (ACLINT_MSWI_ALIGN - 1)) ||
|
||||
(mswi->size < ACLINT_MSWI_SIZE) ||
|
||||
(mswi->first_hartid >= SBI_HARTMASK_MAX_BITS) ||
|
||||
(mswi->hart_count > ACLINT_MSWI_MAX_HARTS))
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Update MSWI hartid table */
|
||||
for (i = 0; i < mswi->hart_count; i++)
|
||||
mswi_hartid2data[mswi->first_hartid + i] = mswi;
|
||||
|
||||
/* Add MSWI regions to the root domain */
|
||||
for (pos = 0; pos < mswi->size; pos += ACLINT_MSWI_ALIGN) {
|
||||
region_size = ((mswi->size - pos) < ACLINT_MSWI_ALIGN) ?
|
||||
(mswi->size - pos) : ACLINT_MSWI_ALIGN;
|
||||
sbi_domain_memregion_init(mswi->addr + pos, region_size,
|
||||
SBI_DOMAIN_MEMREGION_MMIO, ®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
sbi_ipi_set_device(&aclint_mswi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,14 +7,15 @@
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/ipi/fdt_ipi.h>
|
||||
|
||||
extern struct fdt_ipi fdt_ipi_clint;
|
||||
extern struct fdt_ipi fdt_ipi_mswi;
|
||||
|
||||
static struct fdt_ipi *ipi_drivers[] = {
|
||||
&fdt_ipi_clint
|
||||
&fdt_ipi_mswi
|
||||
};
|
||||
|
||||
static struct fdt_ipi dummy = {
|
||||
@@ -44,7 +45,7 @@ static int fdt_ipi_cold_init(void)
|
||||
int pos, noff, rc;
|
||||
struct fdt_ipi *drv;
|
||||
const struct fdt_match *match;
|
||||
void *fdt = sbi_scratch_thishart_arg1_ptr();
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(ipi_drivers); pos++) {
|
||||
drv = ipi_drivers[pos];
|
||||
@@ -54,6 +55,8 @@ static int fdt_ipi_cold_init(void)
|
||||
drv->match_table, &match)) >= 0) {
|
||||
if (drv->cold_init) {
|
||||
rc = drv->cold_init(fdt, noff, match);
|
||||
if (rc == SBI_ENODEV)
|
||||
continue;
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/ipi/fdt_ipi.h>
|
||||
#include <sbi_utils/sys/clint.h>
|
||||
|
||||
#define CLINT_IPI_MAX_NR 16
|
||||
|
||||
static unsigned long clint_ipi_count = 0;
|
||||
static struct clint_data clint_ipi[CLINT_IPI_MAX_NR];
|
||||
|
||||
static int ipi_clint_cold_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct clint_data *ci;
|
||||
|
||||
if (CLINT_IPI_MAX_NR <= clint_ipi_count)
|
||||
return SBI_ENOSPC;
|
||||
ci = &clint_ipi[clint_ipi_count++];
|
||||
|
||||
rc = fdt_parse_clint_node(fdt, nodeoff, FALSE, ci);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return clint_cold_ipi_init(ci);
|
||||
}
|
||||
|
||||
static const struct fdt_match ipi_clint_match[] = {
|
||||
{ .compatible = "riscv,clint0" },
|
||||
{ .compatible = "sifive,clint0" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_ipi fdt_ipi_clint = {
|
||||
.match_table = ipi_clint_match,
|
||||
.cold_init = ipi_clint_cold_init,
|
||||
.warm_init = clint_warm_ipi_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
68
lib/utils/ipi/fdt_ipi_mswi.c
Normal file
68
lib/utils/ipi/fdt_ipi_mswi.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/ipi/fdt_ipi.h>
|
||||
#include <sbi_utils/ipi/aclint_mswi.h>
|
||||
|
||||
#define MSWI_MAX_NR 16
|
||||
|
||||
static unsigned long mswi_count = 0;
|
||||
static struct aclint_mswi_data mswi[MSWI_MAX_NR];
|
||||
|
||||
static int ipi_mswi_cold_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
unsigned long offset;
|
||||
struct aclint_mswi_data *ms;
|
||||
|
||||
if (MSWI_MAX_NR <= mswi_count)
|
||||
return SBI_ENOSPC;
|
||||
ms = &mswi[mswi_count];
|
||||
|
||||
rc = fdt_parse_aclint_node(fdt, nodeoff, false,
|
||||
&ms->addr, &ms->size, NULL, NULL,
|
||||
&ms->first_hartid, &ms->hart_count);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (match->data) {
|
||||
/* Adjust MSWI address and size for CLINT device */
|
||||
offset = *((unsigned long *)match->data);
|
||||
ms->addr += offset;
|
||||
if ((ms->size - offset) < ACLINT_MSWI_SIZE)
|
||||
return SBI_EINVAL;
|
||||
ms->size = ACLINT_MSWI_SIZE;
|
||||
}
|
||||
|
||||
rc = aclint_mswi_cold_init(ms);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mswi_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clint_offset = CLINT_MSWI_OFFSET;
|
||||
|
||||
static const struct fdt_match ipi_mswi_match[] = {
|
||||
{ .compatible = "riscv,clint0", .data = &clint_offset },
|
||||
{ .compatible = "sifive,clint0", .data = &clint_offset },
|
||||
{ .compatible = "riscv,aclint-mswi" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_ipi fdt_ipi_mswi = {
|
||||
.match_table = ipi_mswi_match,
|
||||
.cold_init = ipi_mswi_cold_init,
|
||||
.warm_init = aclint_mswi_warm_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
@@ -7,5 +7,6 @@
|
||||
# Anup Patel <anup.patel@wdc.com>
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += ipi/aclint_mswi.o
|
||||
libsbiutils-objs-y += ipi/fdt_ipi.o
|
||||
libsbiutils-objs-y += ipi/fdt_ipi_clint.o
|
||||
libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip.h>
|
||||
@@ -37,7 +38,7 @@ static int fdt_irqchip_cold_init(void)
|
||||
int pos, noff, rc;
|
||||
struct fdt_irqchip *drv;
|
||||
const struct fdt_match *match;
|
||||
void *fdt = sbi_scratch_thishart_arg1_ptr();
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(irqchip_drivers); pos++) {
|
||||
drv = irqchip_drivers[pos];
|
||||
@@ -47,6 +48,8 @@ static int fdt_irqchip_cold_init(void)
|
||||
drv->match_table, &match)) >= 0) {
|
||||
if (drv->cold_init) {
|
||||
rc = drv->cold_init(fdt, noff, match);
|
||||
if (rc == SBI_ENODEV)
|
||||
continue;
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
@@ -91,6 +92,11 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (match->data) {
|
||||
void (*plic_plat_init)(struct plic_data *) = match->data;
|
||||
plic_plat_init(pd);
|
||||
}
|
||||
|
||||
rc = plic_cold_irqchip_init(pd);
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -106,9 +112,18 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff,
|
||||
return irqchip_plic_update_hartid_table(fdt, nodeoff, pd);
|
||||
}
|
||||
|
||||
#define THEAD_PLIC_CTRL_REG 0x1ffffc
|
||||
|
||||
static void thead_plic_plat_init(struct plic_data *pd)
|
||||
{
|
||||
writel_relaxed(BIT(0), (void *)pd->addr + THEAD_PLIC_CTRL_REG);
|
||||
}
|
||||
|
||||
static const struct fdt_match irqchip_plic_match[] = {
|
||||
{ .compatible = "riscv,plic0" },
|
||||
{ .compatible = "sifive,plic-1.0.0" },
|
||||
{ .compatible = "thead,c900-plic",
|
||||
.data = thead_plic_plat_init },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ int32_t fdt_ro_probe_(const void *fdt)
|
||||
if (can_assume(VALID_DTB))
|
||||
return totalsize;
|
||||
|
||||
/* The device tree must be at an 8-byte aligned address */
|
||||
if ((uintptr_t)fdt & 7)
|
||||
return -FDT_ERR_ALIGNMENT;
|
||||
|
||||
if (fdt_magic(fdt) == FDT_MAGIC) {
|
||||
/* Complete tree */
|
||||
if (!can_assume(LATEST)) {
|
||||
@@ -86,6 +90,10 @@ int fdt_check_header(const void *fdt)
|
||||
{
|
||||
size_t hdrsize;
|
||||
|
||||
/* The device tree must be at an 8-byte aligned address */
|
||||
if ((uintptr_t)fdt & 7)
|
||||
return -FDT_ERR_ALIGNMENT;
|
||||
|
||||
if (fdt_magic(fdt) != FDT_MAGIC)
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
if (!can_assume(LATEST)) {
|
||||
@@ -134,16 +142,20 @@ int fdt_check_header(const void *fdt)
|
||||
|
||||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
|
||||
{
|
||||
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
unsigned int uoffset = offset;
|
||||
unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
|
||||
if (offset < 0)
|
||||
return NULL;
|
||||
|
||||
if (!can_assume(VALID_INPUT))
|
||||
if ((absoffset < offset)
|
||||
if ((absoffset < uoffset)
|
||||
|| ((absoffset + len) < absoffset)
|
||||
|| (absoffset + len) > fdt_totalsize(fdt))
|
||||
return NULL;
|
||||
|
||||
if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
|
||||
if (((offset + len) < offset)
|
||||
if (((uoffset + len) < uoffset)
|
||||
|| ((offset + len) > fdt_size_dt_struct(fdt)))
|
||||
return NULL;
|
||||
|
||||
@@ -206,10 +218,11 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||
|
||||
int fdt_check_node_offset_(const void *fdt, int offset)
|
||||
{
|
||||
if (can_assume(VALID_INPUT))
|
||||
return offset;
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
|
||||
if (!can_assume(VALID_INPUT)
|
||||
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
@@ -217,8 +230,11 @@ int fdt_check_node_offset_(const void *fdt, int offset)
|
||||
|
||||
int fdt_check_prop_offset_(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
|
||||
if (!can_assume(VALID_INPUT)
|
||||
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
@@ -306,9 +322,12 @@ const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
|
||||
|
||||
int fdt_move(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
if (!can_assume(VALID_INPUT) && bufsize < 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
FDT_RO_PROBE(fdt);
|
||||
|
||||
if (fdt_totalsize(fdt) > bufsize)
|
||||
if (fdt_totalsize(fdt) > (unsigned int)bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memmove(buf, fdt, fdt_totalsize(fdt));
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct fdt_header {
|
||||
fdt32_t magic; /* magic word FDT_MAGIC */
|
||||
|
||||
@@ -19,9 +19,12 @@ int fdt_check_full(const void *fdt, size_t bufsize)
|
||||
unsigned int depth = 0;
|
||||
const void *prop;
|
||||
const char *propname;
|
||||
bool expect_end = false;
|
||||
|
||||
if (bufsize < FDT_V1_SIZE)
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
if (bufsize < fdt_header_size(fdt))
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
err = fdt_check_header(fdt);
|
||||
if (err != 0)
|
||||
return err;
|
||||
@@ -39,6 +42,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
|
||||
if (nextoffset < 0)
|
||||
return nextoffset;
|
||||
|
||||
/* If we see two root nodes, something is wrong */
|
||||
if (expect_end && tag != FDT_END)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
switch (tag) {
|
||||
case FDT_NOP:
|
||||
break;
|
||||
@@ -52,12 +59,24 @@ int fdt_check_full(const void *fdt, size_t bufsize)
|
||||
depth++;
|
||||
if (depth > INT_MAX)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
/* The root node must have an empty name */
|
||||
if (depth == 1) {
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
name = fdt_get_name(fdt, offset, &len);
|
||||
if (*name || len)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
}
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
if (depth == 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
expect_end = true;
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
|
||||
@@ -241,6 +241,7 @@ static int overlay_update_local_node_references(void *fdto,
|
||||
|
||||
if (fixup_len % sizeof(uint32_t))
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
fixup_len /= sizeof(uint32_t);
|
||||
|
||||
tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
|
||||
if (!tree_val) {
|
||||
@@ -250,7 +251,7 @@ static int overlay_update_local_node_references(void *fdto,
|
||||
return tree_len;
|
||||
}
|
||||
|
||||
for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
|
||||
for (i = 0; i < fixup_len; i++) {
|
||||
fdt32_t adj_val;
|
||||
uint32_t poffset;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
|
||||
|
||||
err = -FDT_ERR_BADOFFSET;
|
||||
absoffset = stroffset + fdt_off_dt_strings(fdt);
|
||||
if (absoffset >= totalsize)
|
||||
if (absoffset >= (unsigned)totalsize)
|
||||
goto fail;
|
||||
len = totalsize - absoffset;
|
||||
|
||||
@@ -61,17 +61,19 @@ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
|
||||
if (stroffset < 0)
|
||||
goto fail;
|
||||
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
|
||||
if (stroffset >= fdt_size_dt_strings(fdt))
|
||||
if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
|
||||
goto fail;
|
||||
if ((fdt_size_dt_strings(fdt) - stroffset) < len)
|
||||
len = fdt_size_dt_strings(fdt) - stroffset;
|
||||
}
|
||||
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
|
||||
if ((stroffset >= 0)
|
||||
|| (stroffset < -fdt_size_dt_strings(fdt)))
|
||||
unsigned int sw_stroffset = -stroffset;
|
||||
|
||||
if ((stroffset >= 0) ||
|
||||
(sw_stroffset > fdt_size_dt_strings(fdt)))
|
||||
goto fail;
|
||||
if ((-stroffset) < len)
|
||||
len = -stroffset;
|
||||
if (sw_stroffset < len)
|
||||
len = sw_stroffset;
|
||||
} else {
|
||||
err = -FDT_ERR_INTERNAL;
|
||||
goto fail;
|
||||
@@ -157,8 +159,8 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
|
||||
|
||||
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
|
||||
{
|
||||
int offset = n * sizeof(struct fdt_reserve_entry);
|
||||
int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
|
||||
unsigned int offset = n * sizeof(struct fdt_reserve_entry);
|
||||
unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
|
||||
|
||||
if (!can_assume(VALID_INPUT)) {
|
||||
if (absoffset < fdt_off_mem_rsvmap(fdt))
|
||||
@@ -179,8 +181,8 @@ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
|
||||
if (!can_assume(VALID_INPUT) && !re)
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
*address = fdt64_ld(&re->address);
|
||||
*size = fdt64_ld(&re->size);
|
||||
*address = fdt64_ld_(&re->address);
|
||||
*size = fdt64_ld_(&re->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,7 +192,7 @@ int fdt_num_mem_rsv(const void *fdt)
|
||||
const struct fdt_reserve_entry *re;
|
||||
|
||||
for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
|
||||
if (fdt64_ld(&re->size) == 0)
|
||||
if (fdt64_ld_(&re->size) == 0)
|
||||
return i;
|
||||
}
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
@@ -368,7 +370,7 @@ static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
|
||||
prop = fdt_offset_ptr_(fdt, offset);
|
||||
|
||||
if (lenp)
|
||||
*lenp = fdt32_ld(&prop->len);
|
||||
*lenp = fdt32_ld_(&prop->len);
|
||||
|
||||
return prop;
|
||||
}
|
||||
@@ -406,7 +408,7 @@ static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
|
||||
offset = -FDT_ERR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
|
||||
if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
|
||||
name, namelen)) {
|
||||
if (poffset)
|
||||
*poffset = offset;
|
||||
@@ -459,7 +461,7 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
||||
|
||||
/* Handle realignment */
|
||||
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
|
||||
(poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
|
||||
(poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
|
||||
return prop->data + 4;
|
||||
return prop->data;
|
||||
}
|
||||
@@ -477,7 +479,7 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
||||
int namelen;
|
||||
|
||||
if (!can_assume(VALID_INPUT)) {
|
||||
name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
|
||||
name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
|
||||
&namelen);
|
||||
if (!name) {
|
||||
if (lenp)
|
||||
@@ -486,13 +488,13 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
||||
}
|
||||
*namep = name;
|
||||
} else {
|
||||
*namep = fdt_string(fdt, fdt32_ld(&prop->nameoff));
|
||||
*namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle realignment */
|
||||
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
|
||||
(offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
|
||||
(offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
|
||||
return prop->data + 4;
|
||||
return prop->data;
|
||||
}
|
||||
@@ -517,7 +519,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fdt32_ld(php);
|
||||
return fdt32_ld_(php);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias_namelen(const void *fdt,
|
||||
@@ -680,7 +682,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if ((phandle == 0) || (phandle == -1))
|
||||
if ((phandle == 0) || (phandle == ~0U))
|
||||
return -FDT_ERR_BADPHANDLE;
|
||||
|
||||
FDT_RO_PROBE(fdt);
|
||||
|
||||
@@ -46,7 +46,7 @@ static int fdt_rw_probe_(void *fdt)
|
||||
return err_; \
|
||||
}
|
||||
|
||||
static inline int fdt_data_size_(void *fdt)
|
||||
static inline unsigned int fdt_data_size_(void *fdt)
|
||||
{
|
||||
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
||||
}
|
||||
@@ -54,15 +54,16 @@ static inline int fdt_data_size_(void *fdt)
|
||||
static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
|
||||
{
|
||||
char *p = splicepoint;
|
||||
char *end = (char *)fdt + fdt_data_size_(fdt);
|
||||
unsigned int dsize = fdt_data_size_(fdt);
|
||||
size_t soff = p - (char *)fdt;
|
||||
|
||||
if (((p + oldlen) < p) || ((p + oldlen) > end))
|
||||
if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
|
||||
if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
|
||||
if (dsize - oldlen + newlen > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
memmove(p + newlen, p + oldlen, end - p - oldlen);
|
||||
memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -348,7 +349,10 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
||||
return offset;
|
||||
|
||||
/* Try to place the new node after the parent's properties */
|
||||
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
|
||||
tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
|
||||
/* the fdt_subnode_offset_namelen() should ensure this never hits */
|
||||
if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
|
||||
return -FDT_ERR_INTERNAL;
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
@@ -390,7 +394,9 @@ int fdt_del_node(void *fdt, int nodeoffset)
|
||||
}
|
||||
|
||||
static void fdt_packblocks_(const char *old, char *new,
|
||||
int mem_rsv_size, int struct_size)
|
||||
int mem_rsv_size,
|
||||
int struct_size,
|
||||
int strings_size)
|
||||
{
|
||||
int mem_rsv_off, struct_off, strings_off;
|
||||
|
||||
@@ -405,8 +411,7 @@ static void fdt_packblocks_(const char *old, char *new,
|
||||
fdt_set_off_dt_struct(new, struct_off);
|
||||
fdt_set_size_dt_struct(new, struct_size);
|
||||
|
||||
memmove(new + strings_off, old + fdt_off_dt_strings(old),
|
||||
fdt_size_dt_strings(old));
|
||||
memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
|
||||
fdt_set_off_dt_strings(new, strings_off);
|
||||
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
|
||||
}
|
||||
@@ -427,15 +432,17 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
|
||||
|
||||
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
|
||||
struct_size = fdt_size_dt_struct(fdt);
|
||||
} else {
|
||||
} else if (fdt_version(fdt) == 16) {
|
||||
struct_size = 0;
|
||||
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
|
||||
;
|
||||
if (struct_size < 0)
|
||||
return struct_size;
|
||||
} else {
|
||||
return -FDT_ERR_BADVERSION;
|
||||
}
|
||||
|
||||
if (can_assume(LIBFDT_ORDER) |
|
||||
if (can_assume(LIBFDT_ORDER) ||
|
||||
!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
|
||||
/* no further work necessary */
|
||||
err = fdt_move(fdt, buf, bufsize);
|
||||
@@ -464,7 +471,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
}
|
||||
|
||||
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size);
|
||||
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
|
||||
fdt_size_dt_strings(fdt));
|
||||
memmove(buf, tmp, newsize);
|
||||
|
||||
fdt_set_magic(buf, FDT_MAGIC);
|
||||
@@ -484,7 +492,8 @@ int fdt_pack(void *fdt)
|
||||
|
||||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
||||
* sizeof(struct fdt_reserve_entry);
|
||||
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
|
||||
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
|
||||
fdt_size_dt_strings(fdt));
|
||||
fdt_set_totalsize(fdt, fdt_data_size_(fdt));
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -40,7 +40,7 @@ static struct fdt_errtabent fdt_errtable[] = {
|
||||
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
|
||||
FDT_ERRTABENT(FDT_ERR_BADFLAGS),
|
||||
};
|
||||
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
|
||||
#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
|
||||
|
||||
const char *fdt_strerror(int errval)
|
||||
{
|
||||
@@ -48,7 +48,7 @@ const char *fdt_strerror(int errval)
|
||||
return "<valid offset/length>";
|
||||
else if (errval == 0)
|
||||
return "<no error>";
|
||||
else if (errval > -FDT_ERRTABSIZE) {
|
||||
else if (-errval < FDT_ERRTABSIZE) {
|
||||
const char *s = fdt_errtable[-errval].str;
|
||||
|
||||
if (s)
|
||||
|
||||
@@ -32,7 +32,7 @@ static int fdt_sw_probe_(void *fdt)
|
||||
/* 'memrsv' state: Initial state after fdt_create()
|
||||
*
|
||||
* Allowed functions:
|
||||
* fdt_add_reservmap_entry()
|
||||
* fdt_add_reservemap_entry()
|
||||
* fdt_finish_reservemap() [moves to 'struct' state]
|
||||
*/
|
||||
static int fdt_sw_probe_memrsv_(void *fdt)
|
||||
@@ -93,8 +93,8 @@ static inline uint32_t sw_flags(void *fdt)
|
||||
|
||||
static void *fdt_grab_space_(void *fdt, size_t len)
|
||||
{
|
||||
int offset = fdt_size_dt_struct(fdt);
|
||||
int spaceleft;
|
||||
unsigned int offset = fdt_size_dt_struct(fdt);
|
||||
unsigned int spaceleft;
|
||||
|
||||
spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
|
||||
- fdt_size_dt_strings(fdt);
|
||||
@@ -108,8 +108,8 @@ static void *fdt_grab_space_(void *fdt, size_t len)
|
||||
|
||||
int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
|
||||
{
|
||||
const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
|
||||
sizeof(struct fdt_reserve_entry));
|
||||
const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
|
||||
sizeof(struct fdt_reserve_entry));
|
||||
void *fdt = buf;
|
||||
|
||||
if (bufsize < hdrsize)
|
||||
@@ -152,6 +152,9 @@ int fdt_resize(void *fdt, void *buf, int bufsize)
|
||||
|
||||
FDT_SW_PROBE(fdt);
|
||||
|
||||
if (bufsize < 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
tailsize = fdt_size_dt_strings(fdt);
|
||||
|
||||
@@ -159,7 +162,7 @@ int fdt_resize(void *fdt, void *buf, int bufsize)
|
||||
headsize + tailsize > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_INTERNAL;
|
||||
|
||||
if ((headsize + tailsize) > bufsize)
|
||||
if ((headsize + tailsize) > (unsigned)bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
|
||||
@@ -247,18 +250,18 @@ int fdt_end_node(void *fdt)
|
||||
static int fdt_add_string_(void *fdt, const char *s)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_totalsize(fdt);
|
||||
int strtabsize = fdt_size_dt_strings(fdt);
|
||||
int len = strlen(s) + 1;
|
||||
int struct_top, offset;
|
||||
unsigned int strtabsize = fdt_size_dt_strings(fdt);
|
||||
unsigned int len = strlen(s) + 1;
|
||||
unsigned int struct_top, offset;
|
||||
|
||||
offset = -strtabsize - len;
|
||||
offset = strtabsize + len;
|
||||
struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
if (fdt_totalsize(fdt) + offset < struct_top)
|
||||
if (fdt_totalsize(fdt) - offset < struct_top)
|
||||
return 0; /* no more room :( */
|
||||
|
||||
memcpy(strtab + offset, s, len);
|
||||
memcpy(strtab - offset, s, len);
|
||||
fdt_set_size_dt_strings(fdt, strtabsize + len);
|
||||
return offset;
|
||||
return -offset;
|
||||
}
|
||||
|
||||
/* Must only be used to roll back in case of error */
|
||||
@@ -374,7 +377,7 @@ int fdt_finish(void *fdt)
|
||||
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
|
||||
|
||||
/* And fix up fields that were keeping intermediate state. */
|
||||
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
|
||||
fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
|
||||
fdt_set_magic(fdt, FDT_MAGIC);
|
||||
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user